Снижение частоты дискретизации анализатора спектра Web Audio с использованием микрофонного входа

1

Я использую 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, когда частота выборки звуковой карты пользователя неизвестна?

  • 0
    Мне любопытно, в чем причина. Вы можете просто отказаться от всего выше 4 кГц на выходе.
Теги:
web-audio
web-audio-api

1 ответ

1

Одним из способов преодоления этого является передача аудиопакетов, захваченных с микрофона, в узел анализатора с помощью узла процессора скриптов, который перебирает проходящие через него аудиопакеты.

Краткий обзор узла скриптового процессора

  • Каждый узел процессора скриптов имеет входной буфер и выходной буфер.
  • Когда звук поступает во входной буфер, узел процессора скрипта запускает событие onaudioprocess.
  • Все, что помещается в выходной буфер узла скриптового процессора, становится его выходом.
  • Для получения подробных спецификаций см. Узел скриптового процессора

Вот псевдокод:

  1. Создайте источник живых медиа, узел процессора процессора и узел анализатора
  2. Подключите источник живого медиафайла к узлу анализатора через узел процессора скриптов
  3. Всякий раз, когда аудиопакет входит в узел процессора скриптов, происходит событие onaudioprocess
  4. Когда происходит событие 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;
}
  • 0
    Большое спасибо за ваше время и внимание!
  • 0
    Большое спасибо за ваше время и внимание! Кстати, for (var sample = 0; sample < outputbuffer.length; sample++) { , должен иметь outputBuffer . Проблема, с которой я analyserNode.getByteFrequencyData(dataArray); сейчас, заключается в том, что я использую analyserNode.getByteFrequencyData(dataArray); , Я хотел бы сохранить это Uint8Array, для производительности. Есть ли способ, которым ваш код / буфер может быть изменен, чтобы изменить с Float32Array на Uint8Array?
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню