До этого мы говорили только об обработке и синтезе аудио, но это только половина всех возможностей, которые предоставляет Web Audio API. Другая половина — анализ аудио — позволяет понять, какими свойствами обладает проигрываемый звук. Классический пример использования такого функционала — это визуализация звука, но существует также очень много применений анализа, которые выходят за границы этой книги, включая в себя определение высоты звука, темпа и распознавание речи.
Это очень важная тема для разработчиков игр и интерактивных приложений по двум причинам. Во-первых, хороший визуальный анализатор может использоваться как отладочный инструмент (в дополнение к нашим ушам и хорошо настроенной измерительной аппаратуре) для тонкой настройки звука. Во-вторых, визуализация критична для игр и музыкальных приложений, таких как Guitar Hero или музыкального ПО типа GarageBand.
Основное средство для анализа звука в Web Audio API — это узлы AnalyserNode. Эти узлы не меняют сам
звук и могут быть добавлены в любом месте аудиографа. Будучи подключёнными к аудиографу, они могут использоваться
для анализа звуковой волны как во временном, так и в частотном спектре.
Полученные результаты основаны на FFT-анализе буфера определённого размера. У нас есть несколько способов
повлиять на выходные данные узла AnalyserNode:
fftSizeЭтот параметр задаёт размер буфера, который используется для анализа. Он должен быть равен степени двойки. Более высокие значения этого параметра позволят проводить более детальный анализ, но могут привести к некоторому снижению производительности.
frequencyBinCountЭто значение только для чтения, задаётся автоматически как fftSize/2.
smoothingTimeConstantЭто значение должно находиться в диапазоне от 0 до 1. При значении 1
используется большое окно скользящего среднего, и результаты получаются максимально сглаженными. При значении
0 скользящее среднее не применяется, и результаты изменяются быстро и резко.
Вот как выглядит базовая настройка узла анализатора, который можно подключить к интересующей нас части аудиографа:
// Предположим, что узел A соединён с узлом B
var analyser = context.createAnalyser();
A.connect(analyser);
analyser.connect(B);
Далее мы можем получить массив значений частотного или временного спектра:
var freqDomain = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatFrequencyData(freqDomain);
freqDomain в этом примере — это массив 32-битных чисел с плавающей точкой, относящихся к частотному
спектру. Эти числа нормализованы и находятся в диапазоне от нуля до единицы. Индексы выходных данных могут быть
линейны сопоставлены между нулём и частотой Найквиста, которая определяется как половина частоты
дискретизации (значение доступно в Web Audio API через context.sampleRate). Следующий фрагмент кода
сопоставляет частоту с подходящим сегментом в массиве частот:
function getFrequencyValue(frequency) {
var nyquist = context.sampleRate / 2;
var index = Math.round(frequency / nyquist * freqDomain.length);
return freqDomain[index];
}
К примеру, если мы попытаемся проанализировать синусоидальную волну частотой 1000 Hz, мы можем
ожидать, что вызов getFrequencyValue(1000) вернёт пиковое значение на графике, как показано на Рисунке 5-1.
Частотный спектр также доступен в виде 8-битных беззнаковых целых чисел через вызов
getByteFrequencyData(). Значения этих чисел масштабируются таким образом, чтобы соответствовать
диапазону между значениями minDecibels и maxDecibels (в dBFS) на узле
анализатора, поэтому эти параметры можно настраивать для масштабирования выходных данных по желанию.
requestAnimationFrameЕсли мы хотим построить визуализацию формы звуковой волны, мы должны периодически опрашивать анализатор,
обрабатывать результаты и отображать их на странице. Мы можем сделать это, установив JavaScript-таймеры через
setInterval или setTimeout, но есть более подходящий способ: использовать
requestAnimationFrame. Он позволяет браузеру встроить вашу кастомную функцию отрисовки внутрь цикла
отрисовки браузера, который отлично оптимизирован для подобного использования. Вместо того, чтобы заставлять эту
функцию
отрисовки запускаться в конкретные промежутки времени и конкурировать с другими браузерными событиями, мы
можем просто запланировать события отрисовки в очереди событий и браузер выполнит их автоматически, как только
сможет.
Так как requestAnimationFrame всё ещё экспериментальный API, мы должны использовать версии с
префиксами для каждого браузера, и предоставить фолбэк для браузеров, которые его не поддерживают в виде
setTimeout:
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
Теперь мы можем использовать requestAnimationFrame для того, чтобы опрашивать анализатор для
получения детальной информации о состоянии аудиопотока.
Объединив всё вместе, мы можем настроить цикл рендера, который, как и раньше, будет опрашивать анализатор и
отображать его текущий частотный анализ в массив freqDomain:
var freqDomain = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqDomain);
for (var i = 0; i < analyser.frequencyBinCount; i++) {
var value = freqDomain[i];
var percent = value / 256;
var height = HEIGHT * percent;
var offset = HEIGHT - height - 1;
var barWidth = WIDTH/analyser.frequencyBinCount;
var hue = i/analyser.frequencyBinCount * 360;
drawContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
drawContext.fillRect(i * barWidth, offset, barWidth, height);
}
Мы можем сделать похожую вещь и для временного спектра:
var timeDomain = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteTimeDomainData(timeDomain);
for (var i = 0; i < analyser.frequencyBinCount; i++) {
var value = timeDomain[i];
var percent = value / 256;
var height = HEIGHT * percent;
var offset = HEIGHT - height - 1;
var barWidth = WIDTH/analyser.frequencyBinCount;
drawContext.fillStyle = 'black';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}
Этот код нарисует график временного спектра поверх цветного графика частотного спектра, используя HTML5 Canvas API. Результат отрисовки канваса изображен на Рисунке 5-2 и будет меняться со временем.
Наш подход к визуализации упускает множество важных данных. Для визуализации музыки этого достаточно, однако, если мы хотим провести комплексный анализ всего аудиобуфера, следует обратиться к другим методам.