Я использую API Web Audio для создания простого анализатора спектра с использованием компьютерного микрофона в качестве входного сигнала. Основные функциональные возможности моей текущей реализации прекрасно работают, используя частоту дискретизации по умолчанию (обычно 48 кГц, но может быть 44,1 кГц в зависимости от браузера).
Для некоторых приложений я хотел бы использовать более низкую частоту дискретизации (~ 8 кГц) для БПФ.
Похоже, что API веб-аудио добавляет поддержку для настройки частоты дискретизации, доступной только в FireFox (https://developer.mozilla.org/en-US/docs/Web/API/AudioContextOptions/sampleRate).
Добавление частоты дискретизации к конструктору контекста:
// create AudioContext object named 'audioCtx'
var audioCtx = new (AudioContext || webkitAudioContext)({sampleRate: 8000,});
console.log(audioCtx.sampleRate)
Консоль выдает "8000" (в FireFox), поэтому, похоже, она работает до этого момента.
С помощью выпадающего меню микрофон включается пользователем. Это функция, обслуживающая это выключение:
var microphone;
function getMicInputState()
{
let selectedValue = document.getElementById("micOffOn").value;
if (selectedValue === "on") {
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
microphone = audioCtx.createMediaStreamSource(stream);
microphone.connect(analyserNode);
})
.catch(err => { alert("Microphone is required."); });
} else {
microphone.disconnect();
}
}
В FireFox с помощью раскрывающегося списка для активации микрофона отображается всплывающее окно с запросом на доступ к микрофону (как обычно ожидалось). После нажатия на микрофон, консоль отобразит: "Подключение AudioNodes из AudioContexts с другой частотой дискретизации в настоящее время не поддерживается". Дисплей анализатора спектра остается пустым.
Любые идеи, как преодолеть эту ошибку? Если мы сможем справиться с этим, какие-либо рекомендации о том, как указать sampleRate, когда частота выборки звуковой карты пользователя неизвестна?
Одним из способов преодоления этого является передача аудиопакетов, захваченных с микрофона, в узел анализатора с помощью узла процессора скриптов, который перебирает проходящие через него аудиопакеты.
Краткий обзор узла скриптового процессора
Вот псевдокод:
Когда происходит событие onaudioprocess:
4.1) Извлечение аудиоданных из входного буфера
4.2) Повторная выборка аудиоданных
4.3) Поместите повторно отобранные данные в выходной буфер
Следующий фрагмент кода реализует вышеуказанный псевдокод:
var microphone;
// *** 1) create a script processor node
var scriptProcessorNode = audioCtx.createScriptProcessor(4096, 1, 1);
function getMicInputState()
{
let selectedValue = document.getElementById("micOffOn").value;
if (selectedValue === "on") {
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
microphone = audioCtx.createMediaStreamSource(stream);
// *** 2) connect live media source to analyserNode via script processor node
microphone.connect(scriptProcessorNode);
scriptProcessorNode.connect(analyserNode);
})
.catch(err => { alert("Microphone is required."); });
} else {
microphone.disconnect();
}
}
// *** 3) Whenever an audio packet passes through script processor node, resample it
scriptProcessorNode.onaudioprocess = function(event){
var inputBuffer = event.inputBuffer;
var outputBuffer = event.outputBuffer;
for(var channel = 0; channel < outputBuffer.numberOfChannels; channel++){
var inputData = inputBuffer.getChannelData(channel);
var outputData = outputBuffer.getChannelData(channel);
// *** 3.1) Resample inputData
var fromSampleRate = audioCtx.sampleRate;
var toSampleRate = 8000;
var resampledAudio = downsample(inputData, fromSampleRate, toSampleRate);
// *** 3.2) make output equal to the resampled audio
for (var sample = 0; sample < outputData.length; sample++) {
outputData[sample] = resampledAudio[sample];
}
}
}
function downsample(buffer, fromSampleRate, toSampleRate) {
// buffer is a Float32Array
var sampleRateRatio = Math.round(fromSampleRate / toSampleRate);
var newLength = Math.round(buffer.length / sampleRateRatio);
var result = new Float32Array(newLength);
var offsetResult = 0;
var offsetBuffer = 0;
while (offsetResult < result.length) {
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
var accum = 0, count = 0;
for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
accum += buffer[i];
count++;
}
result[offsetResult] = accum / count;
offsetResult++;
offsetBuffer = nextOffsetBuffer;
}
return result;
}
for (var sample = 0; sample < outputbuffer.length; sample++) {
, должен иметь outputBuffer
. Проблема, с которой я analyserNode.getByteFrequencyData(dataArray);
сейчас, заключается в том, что я использую analyserNode.getByteFrequencyData(dataArray);
, Я хотел бы сохранить это Uint8Array, для производительности. Есть ли способ, которым ваш код / буфер может быть изменен, чтобы изменить с Float32Array на Uint8Array?