Не каждый пытается создать караоке-бокс в браузере, но если вы обнаружили, что делаете его, и вынуждены поддерживать все браузеры и все устройства (т.е. вы не можете использовать рабочие файлы) ИЛИ вы просто любите Интернет аудио, эта статья может быть для вас!

Правильным термином для этого эффекта, похожего на «караоке», будет реверберация! Итак, все, что нам нужно сделать, это реализовать реверберацию, верно?

Неправильный. Многое нужно для получения правильного эффекта, самый большой вклад здесь будет вашим ядром реверберации/импульсным файлом!

Давайте начнем с самого начала и расшифруем это —

Как и в большинстве аудиоприложений, мы начинаем с создания контекста, который позже будет использоваться для создания наших аудиоузлов.

const audioCtx = new window.AudioContext();

Теперь веб-аудио действительно делает нашу жизнь намного проще, чем мы думаем, перед написанием этой статьи я думал о выпуске библиотеки npm для создания караоке-подобной реверберации, но установка настолько проста, что нет смысла добавлять зависимость к ваш проект.

Конвольверный узел!

Из официальных документов —

Интерфейс ConvolverNode представляет собой AudioNode, который выполняет Linear Convolution для заданного AudioBuffer, часто используемого для достижения эффекта реверберации. ConvolverNode всегда имеет ровно один вход и один выход.

Ответ лежит прямо здесь, этот узел выполняет операцию линейной свертки между вашим звуковым буфером и…. импульсное ядро!

Для тех, кто изучал DSP (цифровую обработку сигналов), они знают, как, используя различные формы импульсных ядер, мы можем в некотором роде «формировать» нашу выходную матрицу!

Доступно множество фильтров (основные файлы .wav), но выбор правильного имеет самое большое значение!

Все сводится к задержке и сложности. Если ваш фильтр длится 10 секунд, вам нужно сохранить аудиобуфер за последние 10 секунд, чтобы иметь возможность вычислить текущий выходной аудиосэмпл с практически нулевой задержкой (игнорируя время, необходимое для вычислений здесь), просто выполнив:

где 𝑙 — длина импульсной характеристики ℎ, а 𝑥[0] всегда считается текущей выборкой.

Хотя это дает вам в основном нулевую задержку, это сводится к производительности наивной свертки, которая может быть слишком большой, особенно если импульсная характеристика очень длинная.

Вернуться к коду —

После некоторых проб и ошибок для моих целей я обнаружил, что это импульсное ядро, предоставленное в библиотеке tunajs, работает лучше всего — YMMV. Вы можете использовать тунец для генерации реверберации, но в этой статье мы будем придерживаться чистой реализации на основе веб-аудио!

Теперь мы создаем основной узел реверберации и добавляем ядро ​​импульсного отклика!

//We are assuming you have a media stream obtained via getUserMedia
const inputAudioStream = audioCtx.createMediaStreamSource(stream)
const reverbNode = audioCtx.createConvolver();
const response = await fetch("impulse_rev.wav");
const arraybuffer = await response.arrayBuffer();
reverbNode.buffer = await audioCtx.decodeAudioData(arraybuffer);
inputAudioStream.connect(reverbNode);
reverbNode.connect(audioCtx.destination);

Это делает наш звуковой график довольно простым,

И это почти все! Вы можете сделать эту систему более изящной, добавив узел усиления на задний фронт и немного уменьшив усиление, чтобы звук не звучал слишком сильно (это может произойти, если у вас нет статических фильтров для шумоподавления). можете проверить мои предыдущие статьи, чтобы увидеть, как это сделать!)