Search code examples
web-audio-apichannelmixing

creating an audioWorkletNode with 4 channels?


I am working on a mod player which is an audio file with 4 different tracks (channels) using webaudio/audioWorkletNode.

I got it working correctly using a 2 channel (stereo) audio node:

  • channels (tracks) 0 & 3 are mixed into the left channel
  • channels (tracks) 1 & 2 are mixed into the right channel

The problem is that I'd like to analyse and show a waveform display for each of the tracks (so there should be 4 different analysers).

I had the idea of creating an audioWorkletNode with outputChannelCount set to [4], connect an analyser to each of the node's four channels, and then use a channelMerger to mix it into 2 stereo channels.

So I used the following code, expecting it to create a node with 4 channels:

let node = new AudioWorkletNode(context, 'processor', { outputChannelCount: [4] });

But the outputChannelCount parameter seems to be ignored. No matter what I specify, it's set to 2 channels in the end.

Is there a way to do it another way, or must I handle the analyse myself, using my own analyser?


Solution

  • I finally found a way to mix all four channels and pass each channel to its own analyser by doing that:

    this.context.audioWorklet.addModule(`js/${soundProcessor}`).then(() => 
    {
        this.splitter = this.context.createChannelSplitter(4);
        // Use 4 inputs that will be used to send each track's data to a separate analyser
        // NOTE: what should we do if we support more channels (and different mod formats)?
        this.workletNode = new AudioWorkletNode(this.context, 'mod-processor', {
                outputChannelCount: [1, 1, 1, 1],
                numberOfInputs: 0,
                numberOfOutputs: 4
        });
    
        this.workletNode.port.onmessage = this.handleMessage.bind(this);
        this.postMessage({
            message: 'init',
            mixingRate: this.mixingRate
        });
        this.workletNode.port.start();
    
        // create four analysers and connect each worklet's input to one
        this.analysers = new Array();
    
        for (let i = 0; i < 4; ++i) {
            const analyser = this.context.createAnalyser();
            analyser.fftSize = 256;// Math.pow(2, 11);
            analyser.minDecibels = -90;
            analyser.maxDecibels = -10;
            analyser.smoothingTimeConstant = 0.65;
            this.workletNode.connect(analyser, i, 0);
            this.analysers.push(analyser);
        }
    
        this.merger = this.context.createChannelMerger(4);
    
        // merge the channel 0+3 in left channel, 1+2 in right channel
        this.workletNode.connect(this.merger, 0, 0);
        this.workletNode.connect(this.merger, 1, 1);
        this.workletNode.connect(this.merger, 2, 1);
        this.workletNode.connect(this.merger, 3, 0);
        this.merger.connect(this.context.destination);
    });
    

    I basically create a new node with 4 outputs and use the outputs as a channel. To produce a stereo output I can then use a channel merger. And voila!

    Complete source code of the app can be found here: https://warpdesign.github.io/modplayer-js/