API веб-аудио: зачем сочинять, если можно кодить?
Опубликовано: 2022-03-11Самый первый черновик API для веб-аудио появился в W3C в 2011 году. Хотя аудио на веб-страницах поддерживается уже давно, до недавнего времени не было надлежащего способа генерировать аудио из веб-браузера. Я лично отношу это к Google Chrome, потому что в интересах Google браузер стал становиться самой важной частью компьютера. Вы, наверное, помните, что мир веб-браузера не сильно изменился, пока не появился Google Chrome. Если бы вы использовали звук на веб-странице в это время, это было бы плохим дизайнерским решением. Но с тех пор, как появилась идея веб-экспериментов, веб-аудио снова обрело смысл. Веб-браузеры в настоящее время являются еще одним инструментом художественного самовыражения, и видео и аудио в веб-браузере играют в нем жизненно важную роль.
Web Audio API может быть довольно сложно использовать для некоторых целей, так как он все еще находится в стадии разработки, но уже существует ряд библиотек JavaScript, которые упрощают работу. В этом случае я собираюсь показать вам, как начать работу с API веб-аудио, используя библиотеку под названием Tone.js. Благодаря этому вы сможете удовлетворить большинство звуковых потребностей вашего браузера, изучив только основы.
Привет API веб-аудио
Начиная
Мы начнем без использования библиотеки. В нашем первом эксперименте мы создадим три синусоидальные волны. Поскольку это будет простой пример, мы создадим всего один файл с именем hello.html, чистый HTML-файл с небольшим количеством разметки.
<!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>
Ядром API веб-аудио является аудиоконтекст. Аудиоконтекст — это объект, который будет содержать все, что связано с веб-аудио. Не рекомендуется иметь более одного аудиоконтекста в одном проекте. Мы начнем с создания аудиоконтекста, следуя рекомендациям, данным в документации Mozilla Web Audio API.
var audioCtx = new (window.AudioContext || window.webkitAudioContext);
Создание осциллятора
С созданным аудиоконтекстом у вас уже есть аудиокомпонент: audioCtx.destination. Это похоже на ваш динамик. Чтобы сделать звук, вы должны подключить его к audioCtx.destination. Теперь, чтобы произвести звук, давайте создадим осциллятор:
var sine = audioCtx.createOscillator();
Отлично, но недостаточно. Его также нужно запустить и подключить к нашему audioCtx.destination:
sine.start(); sine.connect(audioCtx.destination);
С этими четырьмя строками у вас будет довольно раздражающая веб-страница, которая воспроизводит синусоидальный звук, но теперь вы понимаете, как модули могут соединяться друг с другом. В следующем скрипте будет три синусоидальных тона, подключенных к выходу, каждый с другим тоном. Код очень понятен:
//create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);
Осцилляторы не ограничиваются синусоидальными волнами, но также могут быть треугольными, пилообразными, квадратными и произвольной формы, как указано в MDN.
Исправление логики веб-аудио
Далее мы добавим модуль усиления в наш оркестр компонентов веб-аудио. Этот модуль позволяет нам изменять амплитуду наших звуков. Это похоже на ручку громкости. Мы уже использовали функцию подключения для подключения генератора к аудиовыходу. Мы можем использовать ту же функцию подключения для подключения любого аудиокомпонента. Если вы используете Firefox и посмотрите на консоль веб-аудио, вы увидите следующее:
Если мы хотим изменить громкость, наш патч должен выглядеть так:
Это означает, что генераторы больше не подключены к аудиоприемнику, а вместо этого подключены к модулю усиления, и этот модуль усиления подключен к адресату. Хорошо всегда представлять, что вы делаете это с гитарными педалями и кабелями. Код будет выглядеть так:
var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;
Вы можете найти решение на https://github.com/autotel/simple-webaudioapi/.
GainNode — это самый простой блок эффектов, но есть также задержка, конвольвер, биквадратный фильтр, стереопаннер, формирователь волны и многие другие. Вы можете получить новые эффекты из таких библиотек, как Tone.js.
Хранение одного из этих звуковых патчей в отдельных объектах позволит вам повторно использовать их по мере необходимости и создавать более сложные оркестровки с меньшим количеством кода. Это может стать темой для будущего поста.
Делаем вещи проще с Tone.js
Теперь, когда мы кратко рассмотрели, как работают ванильные модули веб-аудио, давайте взглянем на потрясающую среду веб-аудио: Tone.js. С помощью этого (и NexusUI для компонентов пользовательского интерфейса) мы можем очень легко создавать более интересные синтезаторы и звуки. Чтобы попробовать, давайте создадим сэмплер и применим к нему некоторые пользовательские интерактивные эффекты, а затем мы добавим несколько простых элементов управления для этого примера.
Сэмплер Tone.js
Мы можем начать с создания простой структуры проекта:
simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html
Наши библиотеки JavaScript будут находиться в каталоге js . Для этой демонстрации мы можем использовать файл hit4.wav NoiseCollector, который можно загрузить с Freesound.org.

Tone.js предоставляет свои функции через объекты Player. Основная возможность объекта — загрузить семпл и воспроизвести его либо в цикле, либо один раз. Наш первый шаг — создать объект player в переменной «sampler» внутри файла sampler.html:
<!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>
Обратите внимание, что первый параметр конструктора проигрывателя — это имя WAV-файла, а второй — функция обратного вызова. WAV — не единственный поддерживаемый тип файлов, и совместимость больше зависит от веб-браузера, чем от библиотеки. Функция обратного вызова запустится, когда проигрыватель закончит загрузку семпла в свой буфер.
Мы также должны подключить наш сэмплер к выходу. Способ Tone.js сделать это:
sampler.toMaster();
… где сэмплер — это объект Tone.Player после строки 10. Функция toMaster — это сокращение от connect(Tone.Master).
Если вы откроете веб-браузер с открытой консолью разработчика, вы должны увидеть сообщение «загружены образцы», указывающее на то, что проигрыватель был создан правильно. В этот момент вы можете захотеть услышать образец. Для этого нам нужно добавить кнопку на веб-страницу и запрограммировать ее на воспроизведение образца после нажатия. Мы собираемся использовать кнопку NexusUI в теле:
<canvas nx="button"></canvas>
Теперь вы должны увидеть закругленную кнопку, отображаемую в документе. Чтобы запрограммировать его для воспроизведения нашего образца, мы добавляем прослушиватель NexusUI, который выглядит следующим образом:
button1.on('*',function(data) { console.log("button pressed!"); })
Отличительной особенностью NexusUI является то, что он создает глобальную переменную для каждого элемента NexusUI. Вы можете настроить NexusUI так, чтобы он не делал этого, и вместо этого иметь эти переменные только в nx.widgets[], установив для nx.globalWidgets значение false. Здесь мы собираемся создать всего пару элементов, так что мы просто будем придерживаться этого поведения.
Как и в jQuery, мы можем поместить эти события .on, и первым аргументом будет имя события. Здесь мы просто назначаем функцию тому, что делается с кнопкой. Это то, что пишется как «*». Вы можете узнать больше о событиях для каждого элемента в NexusUI API. Чтобы воспроизвести сэмпл вместо записи сообщений при нажатии кнопки, мы должны запустить функцию запуска нашего сэмплера.
nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }
Также обратите внимание, что прослушиватель входит в обратный вызов onload. Элементы NexusUI отрисовываются на холсте, и вы не можете ссылаться на них, пока nx не вызовет функцию onload. Так же, как и с элементами DOM в jQuery.
Событие срабатывает при нажатии кнопки мыши и при отпускании. Если вы хотите, чтобы он запускался только при нажатии, вы должны оценить, равно ли event.press единице.
При этом у вас должна быть кнопка, которая воспроизводит семпл при каждом нажатии. Если вы установите для sampler.retrigger значение true, это позволит вам воспроизводить семпл независимо от того, воспроизводится он или нет. В противном случае вам придется дождаться завершения семпла, чтобы запустить его повторно.
Применение эффектов
С Tone.js мы можем легко создать задержку:
var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();
Первый аргумент — это время задержки, которое можно записать в нотной записи, как показано здесь. Второй уровень — это обработанный уровень, который означает смесь между исходным звуком и звуком, который на него влияет. Для задержек вам обычно не нужен 100% обработанный звук, потому что задержки интересны по отношению к оригинальному звуку, а один только обработанный звук не очень привлекателен, как и то, и другое вместе.
Следующим шагом будет отключение нашего сэмплера от мастера и подключение его к задержке. Настройте строку, где сэмплер подключен к мастеру:
sampler.connect(delay);
Теперь попробуйте кнопку еще раз и увидите разницу.
Далее мы добавим два циферблата в тело нашего документа:
<canvas nx="dial"></canvas> <canvas nx="dial"></canvas>
И мы применяем значения циферблатов к эффекту задержки с помощью NexusUIlistener:
dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })
Параметры, которые вы можете настроить для каждого события, можно найти в документации Tone.js. Для задержки, это здесь. Теперь вы готовы попробовать пример и настроить параметры задержки с помощью циферблатов NexusUI. Этот процесс можно легко выполнить с каждым элементом NexusUI, не ограничиваясь только эффектами. Например, также попробуйте добавить еще один циферблат и добавить его прослушиватель следующим образом:
dial3.on('*',function(data) { sampler.playbackRate=data.value; })
Вы можете найти эти файлы на github.com/autotel/simpleSampler.
Заключение
Когда я прошел через эти API, я начал чувствовать себя ошеломленным всеми возможностями и идеями, которые начали приходить мне в голову. Большая разница между этой реализацией звука и традиционными реализациями цифрового звука заключается не в самом звуке, а в контексте. Никаких новых методов синтеза здесь нет. Скорее инновация заключается в том, что создание аудио и музыки теперь встречается с веб-технологиями.
Я лично занимаюсь созданием электронной музыки, и в этой области всегда был этот парадокс двусмысленности между реальным исполнением музыки и простым нажатием кнопки воспроизведения записанного трека. Если вы действительно хотите создавать живую электронную музыку, вы должны уметь создавать свои собственные исполнительские инструменты или «музыкальных роботов» для живой импровизации. Но если исполнение электронной музыки становится просто настройкой параметров в заранее подготовленных алгоритмах создания музыки, то и аудитория может быть вовлечена в этот процесс. Я работаю над небольшими экспериментами, связанными с интеграцией Интернета и аудио для краудсорсинговой музыки, и, возможно, скоро мы будем посещать вечеринки, на которых музыка исходит от аудитории через их смартфоны. В конце концов, это не так уж отличается от ритмичных джемов, которыми мы могли наслаждаться в пещерные века.
Дальнейшее чтение в блоге Toptal Engineering:
- Учебник по WebAssembly/Rust: идеальная обработка звука
- Учебное пособие по MIDI: создание аудиоприложений на основе браузера, управляемых оборудованием MIDI