Интерфейс сонификации движения и положения
Демо: https://ami.stranno.su
Видео: https://youtu.be/H1ryDYgeoOs
Note: В Firefox баг — на дефолтных настройках проблемы со звуком. Рекомендуется поставить значение атаки минимум на 0.1 для исправления. В целом Firefox не рекомендуется из-за сниженной частоты обновления параметров движения
Система синтезирует звук на основе данных с датчиков движения смартфона: скорость определяет громкость, положение определяет частоту. Другими словами, это музыкальный инструмент (синтезатор), где вместо клавиш или струн используются жесты рукой.
Предусмотрен т.н. локальный режим, когда звук генерирует смартфон и распределённый режим, когда смартфон передаёт данные о движении на компьютер и уже компьютер генерирует звук.
Так как звук с устройства можно вывести в любую акустическую систему (колонки/комбик/наушники/bluetooth-колонки), AMI можно использовать как полноценный музыкальный инструмент.
Система имеет минимальное количество внутренних настроек, оставляя на ваше усмотрение возможность обработки звука (это могут быть как педали/примочки, так и разнообразные DAW вроде Ableton, Cubase, FL Studio и т.д.).
Самый простой вариант — зайти со смартфона на https://ami.stranno.su. Смартфон попросит доступ к датчикам — его необходимо разрешить. После этого он сразу начнёт при лёгком встряхивании генерировать звук из встроенного динамика. Здесь лучше либо подключить наушники, либо подключиться к bluetooth-колонке, либо кабелем миниджек-миниджек (или с помощью переходника на джек) соединить с колонками/усилителем/комбиком. При таком варианте есть следующие недостатки:
- вы связаны кабелем
- у смартфона есть ощутимая задержка
- вы не видите что играете (генерируемую ноту/частоту)
- не очень удобно изменять настройки
Все эти недостатки решаются распределённым режимом работы. Для этого:
- на смартфоне переключить стратегию синтеза на распределённый режим
- зайти дополнительно с компьютера на https://ami.stranno.su. На компьютере автоматически включится специальный режим приёмника данных. При этом отобразится строка "Подключено (1)" (цифра может быть больше, если кто-то ещё зашёл на сайт вместе с вами)
- теперь смартфон передаёт данные о движении на компьютер. Здесь и смартфон, и компьютер начинают синтезировать звук. Смартфон и компьютер будут синтезировать звук с разной задержкой, из-за чего при включённом звуке на обоих устройствах можно услышать что-то вроде эха. Здесь можно убавить громкость на смартфоне до нуля, а компьютер подключить к вашей акустической системе.
Некоторые возможные схемы работы:
- смартфон → встроенный динамик
- смартфон → наушники
- смартфон → bluetooth-колонка
- смартфон → педали/примочки → колонки/комбик
- смартфон → компьютер → DAW на компьютере → педали/примочки → колонки/комбик
Вы также можете загрузить в систему пресеты (необходимо скачать как файл и воспользоваться кнопкой Import в системе):
Note: использование https://ami.stranno.su — демонстрационный вариант. Главный его недостаток это синхронизация между всеми пользователями; ваш звук и ваши настройки могут перебить случайные пользователи. Плюс, так как информация о движении идёт через интернет (как минимум до Франкфурта, где располагается сервер, и обратно), будет наблюдаться дополнительная задержка (20-100мс, в зависимости от качества связи). Для решения всех этих недостатков рекомендуется развернуть систему локально (смотри раздел Рекомендуемое использование (запуск на локальном компьютере)).
- Запуск
- Теория и термины
- User guide
- Управление системой
- Генерируемая частота
- Нота
- Отсечка
- Влияние скорости на громкость
- Затухание (Release)
- Количество осцилляторов
- Задержка
- Рабочая полусфера
- Диапазон частот
- Диапазон нот
- Режим генерации частот
- Темперированный режим
- Движение по осям α/β/γ
- Положение смартфона по оси γ
- Наличие движения
- Максимальное значение
- Режим приёмника данных включён
- Режим источника данных включён
- Подключение к серверу
- Связь с сервером установлена
- Связь с сервером потеряна
- Подключено (х)
- Ожидание подключений
- Режим экономии вычислительных ресурсов
- Тип отсечки
- Таймаут датчика
- Атака
- Громкость
- Затухание до значения
- Фильтр
- LFO
- Компрессор
- Сброс осцилляторов
- Errors
- Fullscreen-mode
- Генерация звука
- MIDI
- Хранение состояния (настроек) интерфейса (Import / Export)
- Управление системой
- Tech guide
- Secure context
В локальном режиме:
- Зайти на https://ami.stranno.su со смартфона
Note: в локальном режиме задержка синтеза звука может быть довольно ощутима в связи с тем, что вычислительный ресурс у смартфона довольно ограничен по сравнению с даже самым средним ноутбуком.
В распределённом режиме:
- Зайти на https://ami.stranno.su с компьютера
- Зайти на https://ami.stranno.su со смартфона
(в любом порядке)
Note: в распределённом режиме синтез звука становится общим для всех кто зашёл в данный момент на сайт, а настройки синхронизироются между всеми пользователями. То есть, если на сайт зашло несколько человек одновременно и кто-то поменял настройки синтеза, они поменяются у всех участников; звуки, генерируемые одним участником, воспроизведутся на всех устройстах всех посетителей.
Рекомендуемое использование — это запуск собственного экземпляра AMI у себя на компьютере и работа в распределённом режиме.
Note: смартфон и компьютер должны быть подключены к одной wifi-сети. Либо можно запустить виртуальный роутер на ноутбуке (с помощью стороннего сервиса а-ля Connectify) и подключить смартфон к ноутбуку. Самый идеальный вариант это запуск точки доступа на ноутбуке, чтобы нивелировать задержку роутера. На Windows можно воспользоваться функцией "Мобильный хот-спот".
Note: время задержки в таком варианте запуска самое минимальное из возможных.
Windows:
- Скачать архив
- Распаковать
- Кликнуть на
run.bat
- Должен автоматически открыться браузер по адресу
192.168.X.X
MacOS:
- Скачать архив
- Распаковать
- Перенести папку в Documents*
cmd
+Space
- Ввести "terminal.app", запустить Терминал
- В терминале ввести
cd
и перетащить мышкой папку "audio-motion-interface" из Finder в терминал. Терминал автоматически вставит путь до папки. Получится что-то вроде:cd /User/Имя пользователя/Documents/audio-motion-interface
НажатьEnter
- Ввести
chmod -R 755 app
и нажатьEnter
- Кликнуть файл run.command правой кнопкой мыши, там "открыть с помощью Терминал"
- Дать разрешение на исполнение файла. В дальнейшем можно будет запускать AMI просто кликая на run.command
- Должен автоматически открыться браузер по адресу
192.168.X.X
*В целом можно перенести в любую папку, но тогда надо отредактировать файл run.command любым текстовым редактором и исправить пути до Node.js и до index.js
Цель обоих установок такова: AMI использует Node.js для запуска экземпляра. В папках для скачивания ниже уже лежит Node.js. Нужно с её помощью открыть файл index.js. Это удобно делать скриптом. В MacOS для этого необходимо дополнительно сделать скрипт исполняемым через chmod -R 755 app
.
Возможно есть более простые способы установки. Буду рад вашим предложениям.
На Linux установка будет аналогична MacOS, только необходимо будет скачать бинарные файлы Node.js под Linux и положить в папку /app/node. Если Node.js уже установлена глобально, то нужно просто ею запустить файл index.js (node index
).
Node: так как для шифрования трафика, которого требует API, используется самоподписанный сертификат, браузеры будут выдавать плашку о недействительном (недоверенном) сертификате. Это нормально для работы в пределах локальной сети. Подробнее в разделе Secure context.
Надо сказать, что самым эффективным вариантом использования является распределённый режим на локальном компьютере. По сути, смартфон здесь используется исключительно как интерфейс передачи данных с датчиков, а компьютер используется как интерфейс управления этими данными.
Попав на компьютер, эти данные можно обработать любым удобным способом. Например, можно звук передать в DAW (Ableton, Cubase, FL Studio) через Virtual Audio Cabel (VAC) и обработать там, пустив на вход VAC звуки операционной системы (так как браузер отдаёт звук туда), а выход VAC подключить к DAW. Тогда звук можно снимать либо с миниджека компьютера, либо с внешнего-аудиоинтерфейса, а оттуда обрабатывать дальше.
Если у вас нет внешнего аудиоинтерфейса (звуковой карты), вы будете наблюдать задержку обработки звука (по крайней мере на Windows). Чтобы этого избежать, рекомендуется использовать ASIO.
Также есть возможность свой локальный экземпляр AMI сделать доступным из интернета без необходимости разворачивания на удалённом сервере. Для этого необходимо свой локальный экземпляр AMI расшарить в интернет с помощью туннелирования, например, с сервисом ngrok (это бесплатно):
ngrok http https://localhost
На компьютере открываем https://localhost
На смартфоне сгенеренную ngrok ссылку на туннель.
При желании доработать или переработать код, нужно запустить необходимое окружение разработки.
Первый запуск:
git clone https://github.com/MaxAlyokhin/audio-motion-interface.git
- Открываем папку в терминале
npm i
nodemon index
(или простоnode index
)- Открываем второй терминал
cd client
npm i
gulp
Последующие запуски:
- В первом терминале:
nodemon index
(или простоnode index
) - Во втором терминале:
cd client
gulp
Первый терминал это бэкенд, второй терминал это фронтенд.
Также необходимо убрать автоматический запуск браузера по перезапуску сервера. Для этого надо закомментировать эту строку в index.js:
server.listen(443, '0.0.0.0', function () {
console.log(`${getDate()} Audio-motion interface is up and running`)
lookup(hostname, options, function (err, ips, fam) {
ips.forEach(ip => {
if (ip.address.indexOf('192.168') === 0) {
address = ip.address
console.log(`${getDate()} Opening https://${address} in default browser`)
// open(`https://${address}`) Вот эту
console.log(`${getDate()} Close terminal for exit from AMI`)
} else {
address = 'ami.stranno.su'
}
})
})
})
Note: в целях разработки лучше глобально установить Nodemon. Тогда за перезапуск при изменениях кода в бэкенде будет отвечать он, а за изменения в коде фронтенда будет отвечать Gulp
В репозитории уже содержатся приватный и публичный ключи для запуска https-сервера. Подробнее смотри ниже в разделе Secure context.
Словом "синтезатор" обозначают электронный музыкальный инструмент, часто клавишный, генерирующий звук с помощью преобразований электричества принципиальной схемой (аналоговый), либо с помощью математических вычислений микропроцессора (цифровой). Важной особенностью здесь является то, что такие инструменты не акустические (в отличие от классических гитар, скрипок или духовых), они скорее предоставляют систему управления электрическим током, подаваемым в акустическую систему (под акустической системой здесь понимается любое воспроизводяшее звук устройство: колонки, комбик, наушники, встроенный динамик). Более того, это также касается устройств, имеющих в своём составе датчик. Например, электрогитара, тоже не акустический инструмент: центральным его элементом является звукосниматель, а струны это просто способ формирования сигнала. Также, любой микрофон является датчиком, и голос здесь также является просто способом формирования сигнала для микрофона для его дальнейшего перевода в электричество и управления им. Все подобные устройства представляют собой инструменты формирования сигнала (данных), а акустические системы представляют собой инструменты сонификации сигнала, то есть перевода не-звуковых процессов (вроде изменения напряжения тока) в звук. Любой процесс записи звука есть процесс, обратный сонификации. Кливиши на синтезаторе или струны на электрогитаре это просто часть системы управления электрическим током, перекочевавшая в своём привычном для музыкантов формате в эти инструменты.
Интерфейс это переводчик с языка на язык (в широком смысле этого слова). Вся современная музыка представляет собой набор способов перевода акустического сигнала в электрический сигнал и обратно, либо создание электрического сигнала с нуля и дальнейшее манипулирование им.
Интерфейс это способ доступа. Любой, даже акустический музыкальный инструмент, представляет собой интерфейс для доступа к определённому набору звуков, формируемых этим инструментом.
Любой датчик это интерфейс. Смартфон сейчас почти всегда имеет на борту акселерометр и гироскоп, датчик движения и положения соответственно. Мы можем на их основе построить систему синтеза и управления звуком, переводя данные с датчиков в сигналы, обрабатывая и переводя в звук. То есть здесь система не только и не столько синтезирует звук, сколько производит перевод и интерпретацию процессов изменения движения и положения, поэтому здесь слово "синтезатор" отражало бы только часть системы.
Данный репозиторий представляет собой конкретную реализацию такой системы.
Способ получения данных с датчиков и определения места, где они будут переведены в звук.
Устройство, которое получает данные о движении и синтезирует на их основе звук.
Источник данных и точка синтеза находятся на смартфоне (совмещены).
Источник данных находится на смартфоне, а точка синтеза находится на удалённой машине (разнесены). Такой режим в целом позволяет любые сколько угодно сложные комбинации с несколькими источниками данных (смартфонами) и несколькими точками синтеза, находящимися сколь угодно удалённо друг от друга (при трансляции данных через интернет) и подключёнными к разным акустическим системам.
Набор виртуальных устройств осциллятор → фильтр → LFO → огибающая.
JS-событие, генерируемое примерно каждые 16мс (в Chrome и Chromium-подобных браузерах, в Firefox раз в 100мс) смартфоном, содержащее параметры движения. События возникают даже в состоянии покоя — в этом случае параметры движения нулевые.
Минимальная скорость движения, при которой заводится система.
Набор событий движения от превышения отсечки до значения ниже отсечки. Каждому жесту соответствует своя связка. Важно отметить, что слово "жест" здесь не имеет ничего общего с жестами по сенсорному экрану или с жестами мыши, как это происходит в других более привычных нам интерфейсах; здесь "жест" это скорее всего жест рукой в пространстве, либо, в общем смысле, процесс движения смартфона в пространстве, порождающий набор событий движения от превышения отсечки до значения ниже отсечки.
Граф — это абстракция, соединяющая узлы связями. Например, граф для электрогитары может быть примерно таким:
гитара → педаль 1 → педаль 2 → педаль 3 → комбик
AMI состоит из виртуальных устройств (узлов), которые определённым образом соединены. Общий граф выглядит примерно так:
[осциллятор → фильтр → LFO → огибающая] → компрессор
Устройства в квадратных скобках (связка) генерируются по каждому жесту и подсоединяются к компрессору. По окончанию работы связки она удаляется и отключается от компрессора.
Человеческая рука при вращении кисти имеет понятные ограничения: левую руку удобно вращать от положения ладонью вверх до положения ладонью вниз по часовой стрелке, а правую руку удобно вращать от положения ладонью вверх до положения ладонью вниз против часовой стрелки.
Смартфон можно вращать вдоль своей оси на 360 градусов. Но в данной системе 360 градусов поделены пополам: когда смартфон лежит на столе экраном вверх это 180 градусов (ладонь вверх), а когда на экране (ладонь вниз) это 0. От 0 до 180 можно пройти двумя путями: вращая смартфон против часовой стрелки и по часовой стрелке. Чтобы сделать систему эргономичной, мы можем разделить 360 градусов на две полусферы, где правая полусфера удобна для левши, а левая полусфера удобна для правши.
*На рисунке в центре условно показан смартфон экраном вниз
Экземпляр AMI это один сервер и подключённые к нему клиенты (браузеры компьютеров и смартфонов). Например, https://ami.stranno.su это один экземпляр AMI; все подключающиеся к нему клиенты становятся частью экземпляра.
Под состоянием подразумевается вся совокупность настроек системы, непосредственно влияющих на характер звучания (то есть, кроме настроек пользовательского интерфейса, таких как язык или тёмная/светлая тема).
Жесты, пропускаясь через состояние, превращаются в звук. Поэтому состояние определяет звучание.
На уровне экземпляра состояние едино для всех клиентов и синхронизируется между всеми клиентами. Клиенты имеют равный доступ к изменению состояния.
Данные о всех жестах на уровне экземпляра передаются всем клиентам.
Таким образом экземпляр есть единая сущность. Сервер выступает коммутатором между клиентами, объединяя между всеми жесты и состояние.
Подробнее о работе состояния в главе Хранение состояния (настроек) интерфейса (Import / Export)
На синтез звука влияет два параметра — положение и скорость движения.
Положение (угол наклона по полусфере) определяет частоту, отображаемую в поле Генерируемая частота, а также ниже в поле Нота попадание этой частоты в ближайшую ноту.
Скорость движения влияет на два фактора:
- включает систему, когда скорость движения превышает Отсечку, определённую в соответствующем поле
- влияет на громкость при включённом параметре Влияние скорости на громкость
При определении отсечки на 0, всё время будет работать один осциллятор.
Соответственно, система включается при превышении отсечки, создаёт связку и генерирует звук. При замедлении до значения ниже отсечки, система планирует затухание звука (если поле Затухание (Release) не равно 0).
В поле Количество осцилляторов отображаются все связки, звучащие на данный момент.
Например, при хаотичном встряхивании рукой в течение какого-то времени, отсечка будет несколько раз превышена случайным образом, а значит будет сгенерировано несколько связок, которые будут плавно затухать и их звук наложится друг на друга. Количество осцилляторов лучше не доводить, по текущим наблюдениям, до значений выше 120 штук, так как почти наверняка вычислительная мощь устройства на этом закончится и звук начнёт заикаться, либо вовсе пропадёт.
По субъективным наблюдениям оптимальная отсечка может быть в пределах от 3 до 7 (по умолчанию 1), тогда можно исключить случайные движения.
Поле Задержка отображает задержку от события движения до синтеза звука. На смартфоне она равна программной задержке (AudioContext.outputLatency
). На десктопе она равна программной задержке + времени передачи объекта движения от смартфона к десктопу. В интерфейсе отображается в формате задержка устройства + задержка сети
.
По текущим наблюдениям наименьшая задержка наблюдается на устройствах Apple (это касается и IPhone и настольных устройств, порядка 8мс). Например, на смартфоне Huawei Honor 10 задержка устройства 80мс, на ноутбуке Huawei Matebook задержка устройства 40мс; при этом при локальном запуске системы через wi-fi роутер задержка сети выходит 4-5мс. Значит, конкретно для этих устройств, полная задержка смартфона = 80мс, полная задержка ноутбука 40 + 5 = 45мс. То есть, выходит парадоксальная ситуация, что на ноутбуке звук возникает раньше, чем на смартфоне.
Самый эффективный вариант это использовать ноутбук от Apple и развернуть систему локально, тогда задержка будет порядка 5 + 8 = 13мс.
Так как частоты распределены по полусфере, предусмотрено поле Рабочая полусфера, позволяющее переключать систему под левшу или правшу.
Полусфера содержит 1800 делений (180 градусов * десятые доли), по которым распределяются значения, указанные в поле Диапазон частот. Значения распределяются непрерывно и экспоненциально, то есть на высоких значениях на каждый градус приходится больше герц, что позволяет учесть особенности нашего слуха и сделать распределение частот по полусфере равномерным.
Есть возможность перераспределить по полусфере ноты в рамках 12-ступенного равномерно темперированного строя с помощью поля Режим генерации частот, выбрав Темперированный режим. Тогда поле диапазон частот автоматически заменится на Диапазон нот. Выбрав небольшой диапазон, можно довольно точно и уверенно попадать в нужные ноты.
Поле Движение по осям α/β/γ показывает скорости движения по трём координатам в пространстве.
Поле Положение смартфона по оси γ показывает угол наклона в полусфере. Этот угол наклона определяет частоту синтезируемого звука.
Поле Наличие движения показывает true
, когда отсечка превышена и система генерирует звук в текущей связке. В положении false
система находится в режиме ожидания превышения отсечки и не генерирует звуки.
Поле Максимальное значение показывает максимальную скорость движения за всё время сессии (то есть от момента открытия вкладки, до текущего момента).
Режим приёмника данных включён означает, что компьютер готов принимать данные от внешних смартфонов.
Режим источника данных включён означает, что смартфон транслирует свои данные о движении на удалённый компьютер.
Подключение к вебсокет-серверу — в этом статусе система пытается установить websocket-соединение с сервером, через который данные будут транслироваться между смартфоном и компьютером.
Связь с вебсокет-сервером установлена — есть возможность передавать данные между устройствами.
Связь с вебсокет-сервером потеряна — что-то произошло на сервере и он больше не отвечает, либо устройство отключилось от интернета и потеряло связь.
Подключено (х) — количество подключённых устройств, помимо данного компьютера (это поле отображается только с десктопа).
Ожидание подключений — ни одно устройство не подключено помимо этого компьютера (это поле отображается только с десктопа).
Режим экономии вычислительных ресурсов — события движения, а также пересчёт выходных значений синтезируемого звука, запускают очень быстрое обновление данных в интерфейсе. Это обновление довольно затратная операция. Для экономии ресурсов устройства, особенно если вы на каком-то этапе слышите щелчки или артефакты звука, можно включить этот режим, но он отключит все обновления данных в интерфейсе и ориентироваться нужно будет только на слух.
Тип отсечки — при "полном" типе звук генерируется от точки превышения отсечки до точки ниже отсечки. При типе "Пик" звук генерируется только в точке наивысшей скорости движения. Пояснение: каждый жест в конце своего движения так или иначе замедляется. Это приводит к тому, что, во-первых, сложнее управлять отдельными звуками, а во-вторых, в режиме влияния скорости на громкость середина звука может быть громкой, а конец очень тихим. Мы можем поймать замедление движения и интерпретировать его как конец жеста. На практике это позволяет добиться более чётких отдельных контролируемых звуков.
Таймаут датчика — как и отсечка датчика, этот параметр позволяет лучше контролировать движение и избавиться от случайных звуков. Он выставляет паузу после окончания предыдущего жеста, нивелируя случайные превышения отсечки при замедлении скорости движения.
Атака — время плавного нарастания громкости до значения в поле громкость.
Громкость — целевое значение громкости, до которого нарастает атака и от которого начинается затухание.
Затухание до значения — значение громкости, до которого затухает звук. По умолчанию 0,0001
, минимальное значение (ноль поставить нельзя по математическим причинам, так как затухание идёт экспоненциально). Если поставить это значение больше громкости, то звук будет нарастать и резко обрываться, что создаст своего рода эффект вывернутого наизнанку звука.
Фильтр — lowpass-фильтр, срезает верхние частоты, начиная от частоты, указанной в соответствующем поле. Q-фактор определяет "мощность" подавления частот, широту влияния на частоты (из всех возможных фильтров был выбран именно lowpass, так как он смягчает звенящие высокие частоты, что очень уместно для такой системы. Если есть потребность в более изощрённой фильтрации или вообще в полноценном эквалайзере, то необходимо возпользоваться внешними решениями, будь то DAW или какие-то отдельные устройства).
LFO — осциллятор, управляющий ручкой громкости основного осциллятора. Амплитуда опереляет глубину изменения громкости, а частота скорость изменения громкости.
Компрессор — по умолчанию его влияние сведено к минимуму. Если хочется играть множество осцилляторов сразу, а скатываться в грубый нойз нет желания, можно поставить, к примеру, поле Release на 0.25
, а поле Threshold на -50
.
Сброс осцилляторов — выключает и удаляет все работающие связки. Помогает в случае, если от обилия осцилляторов звук начал выдавать артефакты, а также если были выставлены очень большие значения затухания и не хочется ждать пока звук затихнет.
Errors — это поле возможно будет убрано в дальнейшем. Сюда будут вставляться все ошибки, возникающие в процессе работы, что поможет отладить систему.
Fullscreen-mode — на смартфоне, напротив надписи Audio-motion interface, будет значок перехода в полноэкранный режим (устройства Apple его, по всей видимости, не поддерживают). Этот режим рекомендуется, так как в нём отключаются стандартные браузерные жесты "Назад" (при свайпе с левого края вправо) и "Обновить" (при свайпе с верхнего края вниз), что позволит более уверенно держать смартфон в руке не боясь что-то нажать.
Генерация звука — индикатор, показывающий генерируется ли в данный момент звук.
AMI позволяет генерировать MIDI-сообщения. Для включения MIDI необходимо включить MIDI-режим. Автоматически выбирается первый попавшийся порт из доступных и его первый канал. При превышении отсечки посылается сигнал noteOn, далее в режиме Пик сигнал noteOff посылается через время, указанное в поле Длительность; в режиме Полный, noteOff посылается только по окончании движения + через время в поле Длительность; при этом во время движения при влиянии скорости на громкость меняется давление на канал (то есть скорость во время движения равноценна изменению силы нажатия на клавишу). В режиме Пик скорость увеличивает Velocity. Кнопка Сброс MIDI посылает сигнал allSoundOff.
MIDI-сообщения могут посылаться:
- в соседние вкладки и окна браузеров, если они слушают MIDI (например, в веб-аналог DX7)
- в DAW и прочие приложения, где есть виртуальные синтезаторы (то есть AMI может управлять, например, синтезатором в Ableton)
- во внешние устройства, поддерживающие MIDI и подключённые к компьютеру
Для передачи MIDI-сообщений в DAW на устройствах Windows можно воспользоваться loopMIDI.
Note: После любых манипуляций в MIDI-портами (подключение/отключение/переподключение) необходимо полностью перезапустить браузер, закрыв все окна браузера если их несколько
Note: MIDI-сообщения генерируются только на десктопе. Смартфон в этом режиме только посылает данные о движении и положении
При первом запуске приложения устанавливается состояние по-умолчанию.
В дальнейшем, любые изменения настроек сразу же записываются на компьютер, поэтому при перезапуске ваши наработки автоматически восстанавливаются.
Последний подключившийся компьютер имеет приоритет и его настройки автоматически принимаются для всего экземпляра.
Но у вас может возникнуть ситуация, когда понадобится хранить сразу несколько экземпляров настроек. Также, при полной очистке браузера, при смене браузера, либо при запуске приложения с другого домена (например на новом адресе 192.168.X.X
), настройки будут утеряны. В качестве решения в приложении предусмотрены кнопки Import / Export для сохранения и загрузки настроек с внешнего файла в формате .json
.
Стек: HTML, Sass, JS, Web Audio API, Device Motion API, Device Orientation API, Socket.io, Express, Gulp.
AMI это по сути небольшое "fullstack" веб-приложение с https-сервером на Express и websocket-сервером на Socket.io, раздающее несложный фронтенд на нативном JS с использованием Web Audio API (WAAPI), Device Motion API (DMAPI) и Device Orientation API (DOAPI). Мы собираем данные с DMAPI/DOAPI, приводим их в порядок и отправляем сразу в WAAPI в локальном режиме и в websocket в распределённом режиме (а на удалённой машине эти данные принимаются по websocket и там уже отдаются в WAAPI).
index.js — точка входа в приложение. Запускает Express и Socket.io, раздаёт фронтенд из /client/dist. Фронтенд собирает Gulp в папке /client из /client/src и готовое кладёт в /client/dist. JS собирается Webpack, Sass компилируется в CSS, HTML собирается из шаблонов, в <head>
инжектируется собранный JS и CSS. BrowserSync запускает development-сервер на отдельном порту, но на нём не будет работать бэкенд (но зато работает live-reload), поэтому лучше открывать адрес без порта (https://localhost
).
Код приложения во многих местах прокомментирован, поэтому нюансы работы можно изучить непосредственно в коде.
Грубая схема движения данных по функциям после инициализации:
С помощью библиотеки current-device определяется тип устройства — mobile или desktop, который инициализирует соответствующий режим. На мобильном устройстве у каждого события движения проверяется скорость движения (максимальная из трёх координат) и сравнивается с отсечкой, если превысили, то создаём связку. Если после этого оказались ниже отсечки, то планируем удаление связки. Все элементы связки пушатся в массивы, а затем из них удаляются.
В приложении используется MVC-подобная архитектура (имеется в виду MVC на клиенте), где в модели вместо БД используется мутируемый стейт (состояние), а под View будет подразумеваться не только GUI, но и данные с гироскопа и акселерометра, а также синтезируемый звук. Важнейшие части бизнес-логики находятся в audio.js
, motion.js
и orientation.js
. Стейт и почти всё управление им происходит в settings.js
. Объект settings
содержит в себе полностью всё состояние приложения. Изменение этого объекта происходит через методы объекта mutations
. Связь представления (его UI-части) со стейтом происходит в функции settingsInit()
. Этот объект записывается в localStorage
. Каждая его мутация вызывает обновление localStorage
. Объект settings
, за исключением свойства settings.ui
, полностью определяет звучание системы. Кнопки Import / Export позволяют манипулировать состоянием как внешним JSON-файлом.
Вполне возможно построить систему со множеством стейтов и управлять ими через нечто вроде "мастер-устройства", микширующего "дорожки" от разных устройств, но в данном случае принято решение, что каждый экземпляр AMI это отдельный музыкальный инструмент со своим характерным в данный момент звучанием.
В связи с тем, что Motion/Orientation API требуют secure context (то есть шифрование трафика), приходится поднимать https-сервер. Для этого с помощью OpenSSL были сгенерированы публичный и приватный ключи, с помощью которых шифруется трафик между компьютером и смартфоном (самоподписанный сертификат). Практической пользы от такого шифрования особо нет, так как данные движутся в пределах вашей локальной сети (да и при утечке в интернет данные о вращении вашего смартфона особого вреда не принесут), но secure context требуется всеми браузерами для передачи данных о движении и положении на внешние устройства.
При необходимости вы можете сгенерировать свой самоподписанный сертификат (подробнее можно подсмотреть здесь).
При публикации кода в вебе скорее всего перед Express-сервером у вас будет ещё какая-то инфраструктура (nginx + панель управления сервером и т.д.), соответственно необходимость в https-сервере скорее всего отпадёт и можно будет поменять index.js так:
const fs = require('fs')
const os = require('os')
const http = require('http') // Поменяли пакет с https на http
const express = require('express')
const { Server } = require('socket.io')
const { lookup } = require('dns')
const open = require('open')
const hostname = os.hostname()
const app = express()
const server = http.createServer(app) // Убрали загрузку сертификатов