0

我想对麦克风输入进行一些实时处理(包括下采样),最重要的是我想保存输入样本。

在将代码部署到生产环境后,我注意到我收到的一些录音有问题,有些录音比其他录音要多得多。故障是指记录包含零值样本的随机周期。该问题发生在 Android 设备上。

幸运的是,我得到了一台能够始终如一地重现该问题的设备(OnePlus 6)。在将 AudioWorklet 剥离到最低限度后,我仍然可以观察到故障。这就是我的测试 AudioWorklet 类在去掉通信部分后的样子。

class TestWorklet extends AudioWorkletProcessor {
    constructor(options) {
        super(options);

        // Allocate the buffer once in the beginning
        this.recordingBuffer = new Float32Array(sampleRate * 20);  // store max 20 sec audio
        this.recordingBufferOffset = 0;
    }

    process(inputs, outputs) {
        const input = inputs[0];
        const output = outputs[0];

        // Copy samples to the recording buffer
        if (this.recordingBufferOffset < this.recordingBuffer.length - input[0].length) {
            this.recordingBuffer.set(input[0], this.recordingBufferOffset);
            this.recordingBufferOffset += input[0].length;
        }

        // Copy input to the output
        for (let channel = 0; channel < input.length; ++channel) {
            output[channel].set(input[channel]);
        }

        return true;
    }
}

registerProcessor('TestWorklet', TestWorklet);

为了完整起见,这就是我连接 AudioWorklet 的方式

import TestWorklet from './testWorklet';

start = async () => {
    let stream;
    let audioCtx;
    if (navigator.mediaDevices) {
        try {
            stream = await navigator.mediaDevices.getUserMedia({
                audio: true,
                video: false
            });
        } catch (e) { }
    }

    if (!stream) {
        return false;
    }

    try {
        audioCtx = new AudioContext();
    } catch (e) {
        return false;
    }

    await audioCtx.audioWorklet.addModule(TestWorklet);
    const audioSource = audioCtx.createMediaStreamSource(stream);

    const worklet = new AudioWorkletNode(audioCtx, "TestWorklet", {
        channelCount: 1,
        channelCountMode: "explicit",
        channelInterpretation: "discrete"
    });

    audioSource.connect(worklet);

    await audioCtx.resume();
    return true;
}

有趣的是,在功能较弱的 Android 设备上,我没有遇到这些零故障。至少我没有注意到。

有没有人有这个问题的经验?也许我正在使用次优参数初始化 Worklet?

同时,我正在尝试使用已弃用的替代实现ScriptProcessorNode来查看它是否表现更好。

4

2 回答 2

0

AudioWorkletProcessor 运行在由声卡定时的音频线程上,process() 方法回调也是如此。它意味着每次准备好处理 128 个样本时都会调用它。根据我的直接观察,您希望尽可能地减慢速度。

您可以尝试将 AudioworletProcessor 仅用作这 128 个样本块的导出器,使用 AudioworkletNode.port() 方法与主脚本对话并“导出”来自输入(麦克风)的数组缓冲区:

class CustomMicProcessor extends AudioWorkletProcessor {
    process (inputs, outputs, parameters) {
    const input = inputs[0];
    const output = outputs[0];

    if (input[0]) 
      this.port.postMessage(input[0]);
      
    return true;
    }
}

另一方面,您将有一个消息侦听器:

customMicProcessor.port.onmessage = (samples) => { FillMicBuffer (samples.data)};
                      

然后你对你的数据做任何你需要做的事情,但在主线程上而不是在音频线程上。

我在 voip Web 应用程序中这样做(下采样记录 + 编码然后网络传输),并且可以从我的 Android 设备完美地记录和发送数据。

于 2021-07-04T20:37:55.610 回答
0

我认为您在 Chrome 中遇到了一个已在此处报告的错误。如果我没记错的话,它是在有一个不产生任何可听声音的 AudioContext 时触发的。在这种情况下,Chrome 应用了优化,这意味着它从内部时钟运行上下文,而不是使用实际的物理音频设备。

如果以上所有情况都属实,您可能会通过向音频输出发送一个微小的信号来保持上下文从该设备运行而逃脱。

const constantSourceNode = new ConstantSourceNode(audioContext);

// 0.0001 is an arbitrary value
// maybe it needs to be bigger
// maybe a smaller value works as well
constantSourceNode.offset.value = 0.0001;

constantSourceNode.connect(audioContext.destination);
constantSourceNode.start();
于 2021-03-23T21:29:29.923 回答