Documentation/Source example
Example source

samples/vis/JS - VU Meter.js

Referenced by:vis.Create
include('docs/Flags.js');

// JSplitter vis sample: VU Meter | JavaScript callback.
// Source: VU detector and ballistic response; presentation below mirrors
// the built-in renderer defaults while keeping the held-peak data accessible.
vis.UpdateIntervalMs = 16; // Default: 16 ms; allowed: unsigned integer 1..4294967295 ms.

const textColor = window.InstanceType === 1
    ? window.GetColourDUI(0) : window.GetColourCUI(0);
const backgroundColor = window.InstanceType === 1
    ? window.GetColourDUI(1) : window.GetColourCUI(1);

// Built-in renderer defaults reproduced by this callback.
const showPeak = false;
const peakSize = 1;

// Callback data (one channel or mix, both Float32Array(1)):
//   data.levels: normalized VU reading (0..1 over minVu..maxVu).
//   data.peaks:  normalized held peak position (0..1 on the same scale).
// JSplitter owns and reuses both buffers.
const meter = vis.Create(VisSourceType.VUMeter, {
    channel: "mix", // Default: "mix"; also accepts a vis.AudioFormat channel name or unsigned PCM index.
    referenceDbfs: -18, // Default: -18 dBFS for 0 VU; allowed: -96..0 dBFS.
    minVu: -20, // Default: -20 VU; allowed: -160 <= value < maxVu.
    maxVu: 3, // Default: +3 VU; allowed: minVu < value <= +24.
    attackMs: 300, // Default: 300 ms; allowed: >= 0 ms.
    releaseMs: 300, // Default: 300 ms; allowed: >= 0 ms.
    peakHoldMs: 500, // Default: 500 ms; allowed: >= 0 ms.
    peakGravity: 3 // Default: 3; allowed: >= 0.
}, (gr, x, y, w, h, data) => {
    const level = Math.max(0, Math.min(1, data.levels[0]));
    const peak = Math.max(0, Math.min(1, data.peaks[0]));

    // Continuous built-in default: one full-height bar over the complete width.
    gr.FillSolidRect(x, y, level * w, h, textColor);

    // VU's built-in default is showPeak=false. Keep the code here so the
    // callback demonstrates the held-peak buffer without changing the default look.
    if (showPeak) {
        const holdEnd = peak * w;
        if (holdEnd >= 1) {
            const markerLeft = Math.max(0, holdEnd - peakSize * 0.5);
            const markerRight = Math.min(w, holdEnd + peakSize * 0.5);
            if (markerLeft < markerRight)
                gr.FillSolidRect(x + markerLeft, y, markerRight - markerLeft, h, textColor);
        }
    }
});

function on_paint(gr) {
    const w = window.Width, h = window.Height;
    gr.FillSolidRect(0, 0, w, h, backgroundColor);
    if (w > 0 && h > 0) meter.Draw(gr, 0, 0, w, h);
}

setInterval(() => window.Repaint(), 33);