API Web Audio предоставляет программистам JavaScript легкий доступ к обработке и синтезу звука. В этой статье мы расскажем о пользовательских осцилляторах, малоизвестной функции Web Audio API, которая позволяет легко преобразовать Фурье в работу для синтеза отличительных звуковых эффектов в браузере.
Веб Аудио Осцилляторы
API-интерфейс Web Audio позволяет составлять график звуковых элементов для создания звука. Осциллятор является одним из таких элементов — источником звука, который генерирует чистый звуковой сигнал. Вы можете установить его частоту и тип, который может быть синусоидальным, квадратным, пилообразным или треугольным, но, как мы скоро увидим, существует также мощный пользовательский тип.
Во-первых, давайте попробуем стандартный генератор. Мы просто устанавливаем частоту 440 Гц, которую музыканты распознают как ноту A4, и мы включаем селектор типа, чтобы вы могли услышать разницу между синусоидальной, квадратной, пилообразной и треугольной формами волны.
Пользовательские осцилляторы позволяют вам определять ваши собственные формы сигналов вместо этих встроенных типов, но с изюминкой: они используют преобразования Фурье для генерации этого сигнала. Это делает их идеально подходящими для реалистичной генерации звука.
Преобразование Фурье по примеру
Преобразование Фурье — это математический инструмент, используемый стандартами сжатия звука, такими как MP3, среди многих других приложений. Обратное преобразование Фурье разлагает сигнал на составляющие его частоты, подобно тому, как человеческое ухо обрабатывает вибрации для восприятия отдельных тонов.
На высоком уровне преобразования Фурье используют тот факт, что сложный сигнал может быть разложен на дискретные синусоидальные кривые инкрементальных частот. Он работает с использованием таблиц коэффициентов, каждая из которых применяется к кратному основной частоты. Чем больше таблицы, тем ближе приближение. Заинтригованный? Страницу Википедии стоит посмотреть, и она включает в себя анимацию, чтобы помочь визуализировать разложение сигнала на дискретные синусоиды.
Но вместо того, чтобы углубляться в теорию, давайте воплотим это в жизнь, разобрав простой непрерывный звук: воздушный гудок.
Синтез Рога
Для этой статьи мы будем использовать эту запись полицейской сирены и гудка. Здесь представлен спектрограф звукового сигнала, созданный с помощью аудиоредактора с открытым исходным кодом Audacity.
Это ясно показывает количество линий различной интенсивности, все разнесенные с равными интервалами. Если мы посмотрим ближе, этот интервал составляет около 160 Гц.
Преобразования Фурье работают с основной частотой (назовем ее f ) и ее обертонами, кратными f. Если в качестве основного f мы выберем 160 Гц, линия с частотой 320 Гц (2 xf) будет нашим первым обертоном, линия с частотой 480 Гц (3 xf) — нашим вторым обертоном и так далее.
Поскольку спектрограф показывает, что все линии имеют кратные f, массив интенсивностей, наблюдаемых при каждом кратном f, достаточен для представления достойной имитации записанного звука.
Документация по API Web Audio для createPeriodicWave , которая создает пользовательский сигнал из коэффициентов Фурье, сообщает нам следующее:
The real parameter represents an array of cosine terms (traditionally the A terms). In audio terminology, the first element (index 0) is the DC-offset of the periodic waveform and is usually set to zero. The second element (index 1) represents the fundamental frequency. The third element represents the first overtone, and so on.
Существует также параметр imag
Итак, давайте создадим массив этих коэффициентов (оценивая их как 0,4, 0,4, 1, 1, 1, 0,3, 0,7, 0,6, 0,5, 0,9, 0,8 на основе яркости линий на спектрографе, начиная снизу). Затем мы создаем собственный генератор из этой таблицы и синтезируем полученный звук.
var audioContext = new AudioContext();
var osc = audioContext.createOscillator();
var real = new Float32Array([0,0.4,0.4,1,1,1,0.3,0.7,0.6,0.5,0.9,0.8]);
var imag = new Float32Array(real.length);
var hornTable = audioContext.createPeriodicWave(real, imag);
osc = audioContext.createOscillator();
osc.setPeriodicWave(hornTable);
osc.frequency.value = 160;
osc.connect(audioContext.destination);
osc.start(0);
Не совсем успокаивающий звук, но поразительно близко к записанному звуку. Конечно, звуковой синтез выходит далеко за рамки одного спектра — в частности, огибающие являются не менее важным аспектом тембра.
От данных сигнала к таблицам Фурье
Необычно создавать коэффициенты Фурье вручную, как мы только что сделали (и немногие звуки так просты, как наш звук рога, который состоит только из гармонических частичек, то есть кратных f). Как правило, таблицы Фурье вычисляются путем подачи данных реального сигнала в алгоритм обратного БПФ (быстрого преобразования Фурье).
Вы можете найти коэффициенты Фурье для выбора звуков в репозитории Chromium , среди которых звучит орган, играемый ниже:
Библиотека с открытым исходным кодом dsp.js позволяет вам вычислять такие коэффициенты Фурье из ваших собственных образцов данных. Теперь мы продемонстрируем это для получения определенной формы волны.
Низкочастотный генератор: полицейский сигнал сирены
Сирена полиции США колеблется между низкой и высокой частотой. Мы можем добиться этого, используя API-интерфейс Web Audio, подключив два генератора. Первый (низкочастотный генератор, или LFO ) модулирует частоту второго, который сам производит звуковые звуковые волны.
Чтобы разобрать реальную вещь, как и прежде, мы берем спектрограф звука полицейской сирены из той же записи.
Вместо горизонтальных линий мы теперь видим форму волны в форме акульего плавника, представляющую ритмическую тональную модуляцию сирены. Стандартные генераторы поддерживают только синусоидальные, квадратные, пилообразные и треугольные формы сигналов, поэтому мы не можем полагаться на них для имитации этой конкретной формы волны. Но мы можем создать собственный генератор еще раз.
Во-первых, нам нужен массив значений, представляющих желаемую кривую. Следующая функция создает такие значения, которые мы помещаем в массив с именем sharkFinValues.
Далее мы используем dsp.js для вычисления коэффициентов Фурье по данным этого сигнала. Мы получаем массивы real и imag, которые затем используем для инициализации LFO.
var ft = new DFT(sharkFinValues.length);
ft.forward(sharkFinValues);
var lfoTable = audioContext.createPeriodicWave(ft.real, ft.imag);
Наконец, мы создаем второй генератор и подключаем LFO к его частоте через узел усиления, чтобы усилить выход LFO. Наш спектрограф показывает, что форма волны длится около 380 мс, поэтому мы устанавливаем частоту LFO равной 1 / 0,380. Это также показывает нам, что основной тон сирены колеблется от низкого уровня около 750 Гц до высокого уровня около 1650 Гц (медиана 1200 Гц — 450 Гц), поэтому мы устанавливаем частоту генератора на 1200 и усиление LFO на 450.
Теперь мы можем запустить оба генератора, чтобы услышать нашу полицейскую сирену.
osc = audioContext.createOscillator();
osc.frequency.value = 1200;
lfo = audioContext.createOscillator();
lfo.setPeriodicWave(lfoTable);
lfo.frequency.value = 1/0.380;
lfoGain = audioContext.createGain();
lfoGain.gain.value = 450;
lfo.connect(lfoGain);
lfoGain.connect(osc.frequency);
osc.connect(audioContext.destination);
osc.start(0);
lfo.start(0);
Для большей реалистичности мы могли бы также применить пользовательскую форму волны ко второму генератору, как мы показали с помощью звукового сигнала.
Вывод
Благодаря использованию преобразований Фурье пользовательские осцилляторы предоставляют разработчикам Web Audio простой способ синтезировать сложные тоны и полностью автоматизировать настраиваемые эффекты, такие как форма волны сирены, которую мы продемонстрировали.
Синтез звука гораздо более гибкий, чем работа с аудио-образцами. Например, этот эффект сирены легко построить, чтобы добавить больше эффектов, как я сделал, чтобы добавить доплеровский сдвиг в этом мобильном приложении .
Спецификация «Могу ли я использовать» показывает, что Web Audio API пользуется широкой поддержкой браузера, за исключением IE. Не все браузеры соответствуют последнему стандарту W3C, но имеется Monkey Patch , помогающий писать кросс-браузерный код.
Android L добавит поддержку Web Audio API в WebView, что iOS делает с версии 6. Сейчас самое время начать экспериментировать!