Example : Simple Vu Meter

Specifications :

This example demonstrates how to communicate real-time data from the processor to the host. It requires using the postMessage method.

Vu-meter class :

We will create a simple canvas with a gradient rule for the style. Nothing particular in this code.

// vu-meter.js
class VuMeter {

    constructor(canvas, height, width) {
        this.canvas = canvas;
        this.canvas.height = height;
        this.canvas.width = width;
        this.ctx = this.canvas.getContext("2d")

        let gradient = this.ctx.createLinearGradient(0,0, width, height);
        gradient.addColorStop(0, "#08ff00");
        gradient.addColorStop(0.33, "#fffb00");
        gradient.addColorStop(0.66, "#ff7300");
        gradient.addColorStop(1, "#ff0000");

        this.ctx.fillStyle = gradient;
        this.ctx.clearRect(0,0,width,height);
    }

    update(value) {
        value = Math.min(value, 1);
        this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height);
        this.ctx.fillRect(0, 0, value*this.canvas.width, this.canvas.height);
    }
}

Sending data from the processor :

We want to minimize the call to the update function of the vu-meter. That's why we will not send a message after each call of the process method in the processor.

// audio-player-processor.js

const COUNT_BLOCK = 8;

class AudioPlayerProcessor extends AudioWorkletProcessor {

    /* Code of the processor
       ...
    */

    calculateMax(output) {
        for (let i = 0; i < output.length; i++) {
            this.max = Math.max(this.max, output[i]);
        }
        this.blockCount++;
        if (this.blockCount >= COUNT_BLOCK) {
            this.port.postMessage({volume: this.max});
            this.max = 0;
            this.blockCount = 0;
        }
    }

    process(inputs, outputs, parameters) {

        /* Process Audio
           ...
        */

        this.calculateMax(output[0]);
        return true;
    }
}

For each 8 quantum blocks of the render process method, we check the maximum value of the output array. Then we send to the host the max value from all the audio with a postMessage.

Then in the host we will retrieve the value of the max.

Receive volume in host :

// index.js

const vuMeterCanvas = document.getElementById("canvas2");

(async () => {

    const vuMeter = new VuMeter(vuMeterCanvas, 30, 200);

    /* Code of the host
       ...
    */

    node.port.onmessage = ev => {
        let vol = 0;
        let sensitivity = 1.3;
        if (ev.data.volume) {
            vol = ev.data.volume;
        }
        vuMeter.update(Math.abs(vol) * sensitivity);
    }

})();

After creating the vuMeter, we can receive data from the processor using the audio node.

Conclusion :

We've seen how to get information from the processor in the host. We created a simple vu meter, but we can imagine any other implementation where we need to get information about the processor. For instance, making a playhead cursor for the host.