Снижение частоты дискретизации анализатора спектра Web Audio с использованием микрофонного входа - PullRequest
0 голосов
/ 13 октября 2018

Я использую Web Audio API для создания простого анализатора спектра с использованием компьютерного микрофона в качестве входного сигнала.Базовая функциональность моей текущей реализации работает нормально, с использованием частоты дискретизации по умолчанию (обычно 48 кГц, но может быть 44,1 кГц в зависимости от браузера).

В некоторых приложениях я хотел бы использовать более низкую частоту дискретизации (~ 8 кГц) для FFT.

Похоже, что API Web Audio добавляет поддержку для настройки частоты дискретизации, которая в настоящее время доступна только в 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, когда частота дискретизации звуковой карты пользователя неизвестна?

1 Ответ

0 голосов
/ 19 октября 2018

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

Briefобзор узла процессора сценариев

  • Каждый узел процессора сценариев имеет входной буфер и буфер вывода.
  • Когда звук поступает во входной буфер, узел процессора сценариев запускает событие 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;
}
...