scriptNode.onaudioprocess устарел, есть альтернатива?

#javascript #audio #buffer #html5-audio #audiocontext

#javascript #Аудио #буфер #html5-аудио #audiocontext

Вопрос:

Я хочу получить аудиобуфер во время разговора, я использовал этот метод для его обнаружения, но я получаю сообщение, что этот метод onaudioprocess устарел и не запускается, есть ли для него какая-либо альтернатива с примером.

 audioContext = new AudioContext({ sampleRate: 16000 });
scriptNode = (audioContext.createScriptProcessor || audioContext.createJavaScriptNode).call(audioContext, 1024, 1, 1);
scriptNode.onaudioprocess = function (audioEvent) {
  if (recording) {
    input = audioEvent.inputBuffer.getChannelData(0);
    // convert float audio data to 16-bit PCM
    var buffer = new ArrayBuffer(input.length * 2);
    var output = new DataView(buffer);
    for (var i = 0, offset = 0; i < input.length; i  , offset  = 2) {
      var s = Math.max(-1, Math.min(1, input[i]));
      output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
    }
    ws.send(buffer);
  }
};
 

Комментарии:

1. Как вы записываете свой звук? Вы собираетесь записывать звук с вашего микрофона и отправлять его в веб-сокет?

2. @EmielZuurbier да, это с моего микрофона на веб-сокет.

3. Не могли бы вы дать некоторые отзывы об ответе ниже?

Ответ №1:

С помощью API MediaStream Recording и MediaDevices.getUserMedia() метода вы можете передавать аудио с вашего микрофона и передавать его на диктофон. Затем рекордер может отправлять Blob объекты через WebSockets всякий ondataavailable раз, когда событие срабатывает на рекордере.

Приведенная ниже функция создает поток и передает его MediaRecorder экземпляру. Этот экземпляр запишет ваш звук с микрофона и сможет отправить его на ваш веб-сокет. Экземпляр MediaRecorder возвращается для управления записью.

 async function streamMicrophoneAudioToSocket(ws) {
  let stream;
  const constraints = { video: false, audio: true };

  try {
    stream = await navigator.mediaDevices.getUserMedia(constraints);
  } catch (error) {
    throw new Error(`
      MediaDevices.getUserMedia() threw an error. 
      Stream did not open.
      ${error.name} - 
      ${error.message}
    `);
  }

  const recorder = new MediaRecorder(stream);

  recorder.addEventListener('dataavailable', ({ data }) => {
    ws.send(data);
  });

  recorder.start();
  return recorder;
});
 

Таким образом, вы также можете остановить запись, если захотите, вызвав stop() метод на диктофоне.

 (async () => {
  const ws = new WebSocket('ws://yoururl.com');
  const recorder = await streamMicrophoneAudioToSocket(ws);

  document.addEventListener('click', event => {
    recorder.stop();
  });
}());
 

Ответ №2:

Примечание: Хотя мой предыдущий ответ помог некоторым людям, он не предоставил альтернативы устаревшему onaudioprocess интерфейсу event и ScriptProcessorNode. Этот ответ должен предоставить альтернативу вопросу OP.

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

Интерфейс AudioWorkletNode API Web Audio представляет базовый класс для определяемого пользователем AudioNode, который может быть подключен к графу маршрутизации звука вместе с другими узлами. Он имеет связанный AudioWorkletProcessor, который выполняет фактическую обработку звука в потоке веб-рендеринга звука.

Он работает путем расширения AudioWorkletProcessor класса и предоставления обязательного process метода. process Метод предоставляет inputs , outputs и parameters устанавливается в получателе статических параметровdescriptors .

Здесь вы можете вставить ту же логику, что и в onaudioprocess обратном вызове. Но вам нужно внести некоторые изменения, чтобы работать должным образом.

Одним из недостатков использования рабочих столов является то, что вы должны включить этот скрипт в виде файла из интерфейса worklets. Это означает, что любые зависимости, такие как ws переменная, необходимо вводить на более позднем этапе. Мы можем расширить класс, чтобы добавить любые значения или зависимости к экземпляру worklet.

Примечание: process необходимо вернуть логическое значение, чтобы браузер знал, следует ли поддерживать аудиоузел в рабочем состоянии или нет.

 registerProcessor('buffer-detector', class extends AudioWorkletProcessor {
  process (inputs, outputs, parameters) {
    if (this.socket === null) {
      return false;
    }

    if (this._isRecording === true) {
      const [input] = inputs;
      const buffer = new ArrayBuffer(input.length * 2);
      const output = new DataView(buffer);

      for (let i = 0, offset = 0; i < input.length; i  , offset  = 2) {
        const s = Math.max(-1, Math.min(1, input[i]));
        output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
      }

      this.socket.send(buffer);
    }

    return true;
  }

  static get parameterDescriptors() {
    return [{
      name: 'Buffer Detector',
    }]
  }

  constructor() {
    super();
    this._socket = null;
    this._isRecording = false;
  }

  get socket() {
    return this._socket;
  }

  set socket(value) {
    if (value instanceof WebSocket) {
      this._socket = value;
    }
  }

  get recording() {
    return this._isRecording;
  }

  set recording(value) {
    if ('boolean' === typeof value) {
      this._isRecording = value;
    }
  }
});
 

Теперь все, что нам нужно сделать, это включить рабочий лист в ваш скрипт и создать экземпляр узла. Мы можем сделать это с addModule помощью метода, который существует в этом BaseAudioContext.audioWorklet свойстве.

Важно: Добавление модуля работает только в защищенных (HTTPS) контекстах.

Когда модуль будет успешно добавлен, создайте новый узел с AudioWorkletNode помощью конструктора. Назначьте экземпляр WebSocket, установите флаг записи, и все готово.

 const ws = new WebSocket('ws://...');
const audioContext = new AudioContext();
const source = new MediaStreamAudioSourceNode(audioContext, {
  mediaStream: stream // Your stream here.
});

// In an async context
await audioContext.audioWorklet.addModule('buffer-detector.js');

// Create our custom node.
const bufferDetectorNode = new AudioWorkletNode(audioContext, 'buffer-detector');

// Assign the socket and the recording state.
bufferDetectorNode.socket = ws;
bufferDetectorNode.recording = true;

// Connect the node.
source.connect(bufferDetectorNode);
 

Комментарии:

1. Привет, @emiel-zuurbier, как настройка bufferDetectorNode.recording передает значение this._isRecording . Я тестировал этот пример, и, похоже, он не работает.

2. @EX0MAK3R Это была опечатка с моей стороны. recording Установщик не был правильно определен в классе. Попробуйте еще раз с измененным кодом выше.