javascript scriptNode.onaudioprocess已弃用,是否有替代方案?

xdyibdwo  于 2023-01-07  发布在  Java
关注(0)|答案(2)|浏览(438)

我想在说话的时候得到音频缓冲区,我用这个方法来检测它,但是我收到消息,这个方法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);
  }
};
cuxqih21

cuxqih211#

使用MediaStream Recording APIMediaDevices.getUserMedia()方法,您可以从麦克风流式传输音频,并将其流式传输到录音机中,然后每当ondataavailable事件在录音机上触发时,录音机可以通过WebSockets发送Blob对象。
下面的函数创建一个流并将其传递给MediaRecorder示例。该示例将录制您的麦克风音频并能够将其发送到您的WebSocket。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();
  });
}());
pnwntuvh

pnwntuvh2#

答案应该是使用Audio Worklets,使我们能够创建 * 自定义音频处理节点 *,可以像常规AudioNode一样实现。
Web音频API的AudioWorkletNode接口表示用户定义的AudioNode的基类,它可以与其他节点一起连接到音频路由图。它具有关联的AudioWorkletProcessor,该处理器在Web音频呈现线程中执行实际的音频处理。
它通过扩展AudioWorkletProcessor类并提供强制的process方法来工作,process方法公开在静态parameterDescriptors getter中设置的inputsoutputsparameters
在这里你可以插入与onaudioprocess回调相同的逻辑,但是你必须做一些修改才能正常工作。
使用worklet的一个陷阱是,您已经将此脚本作为一个文件从worklet界面中包含进来。这意味着任何依赖项,如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',
    }]
  }

  #socket = null;
  #isRecording = false;

  constructor() {
    super();
  }

  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;
    }
  }
});

现在我们要做的就是在脚本中包含worklet并创建一个节点示例,我们可以使用BaseAudioContext.audioWorklet属性中的addModule方法来完成。

  • 重要提示:* 添加模块在安全(HTTPS)上下文中有效。

成功添加模块后,使用AudioWorkletNode构造函数创建新节点,分配WebSocket示例,设置记录标志,然后就可以开始了。

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

(async () => {
  try {
    // Register the worklet.
    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);
  } catch (error) {
    console.error(error);
  }
})();

相关问题