Play decoded audio buffer with AudioWorklet

672 views Asked by At

I want to play the AudioBuffer that I have gotten from AudioContext.decodeAudioData() with AudioWorklet. I'm currently able to play decoded audio buffer with AudioBufferSourceNode but as you know this method will execute the task on the main thread which is not what I want, the thing I want is to play audio in the background which seems it's only possible to do with workers. but workers can't access the Web Audio Api. so the only way is AudioWorklet

setup worklet :

var audioContext = new AudioContext()
await audioContext.audioWorklet.addModule("./playing-audio-processor.js");
PlayingAudioProcessor= new AudioWorkletNode(
    audioContext,
    "playing-audio-processor"
);
PlayingAudioProcessor.connect(audioContext.destination);
audioContext.resume();

decoding and sending it to the worklet (I'm sure that the passed audioBuffer does not have any problem and can be easily played with AudioBufferSourceNode)

let ctx = new AudioContext();
ctx.decodeAudioData(new Uint8Array(audioData).buffer, (audioBuffer) => {
    //set `audioData` of worklet to a float32array
    myAudioWorklet.port.postMessage(audioBuffer.getChannelData(0))
})

the length of passed audio data array (audioBuffer.getChannelData(0)) is 960 which is greater than the length of outputs[0][0] so I splitted it (actully it doesn't seem to be a good idea and I think this is why I have not expected audio output)

class PlayingAudioProcessor extends AudioWorkletProcessor {
    audioData = []
    constructor() {
        super();
        //set listener to receive audio data
        this.port.onmessage = (data) => {
            this.audioData = data.data
        }
    }

    process(inputs, outputs, parameters) {
        //playing each 128 floats of 960 floats
        for (let i = 0; i < this.audioData.length / 128; i++) {
                for (let b = 0; b < 128; b++) {
                    if ((i * 128) + b <= this.audioData.length) {
                        outputs[0][0][b] = this.audioData[(i * 128) + b];
                    }
                }
            }
        return true;
    }

}

registerProcessor("playing-audio-processor", PlayingAudioProcessor);

the problem is now that the audio result is nothing but a meaningless noisy sound that depends on the loudness of the input data.

I really need to solve this, please put anything that might be helpful for me. thank you.

2

There are 2 answers

2
chrisguttandin On BEST ANSWER

It looks like you're writing all the samples within a single process() call. You would instead need to write only 128 samples per process() call to achieve the desired result.

The first invocation would need to write sample 1 to 128 out of your AudioBuffer, the second invocation would need to write sample 129 to 256, and so on...

0
Elizabeth Hudnott On

AudioBufferSourceNode doesn't play audio on the main thread. None of the AudioNode objects do that (except for ScriptProcessorNode, which is deprecated). All audio processing and playback for the Web Audio API is performed inside a separate web audio thread. Only the parts of the audio nodes that send control messages run on the main thread. By which I mean that a brief message gets sent between threads when you call a method like start() or setValueAtTime(), etc.

https://www.w3.org/TR/webaudio/#control-thread-and-rendering-thread