javascriptservice-workeraudio-streamingweb-audio-apiaudio-worklet

Play decoded audio buffer with AudioWorklet


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.


Solution

  • 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...