Documentation/Source example
Example source

samples/vis/JS - Peak Meter.js

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

// JSplitter vis sample: Peak Meter | JavaScript callback.
// Source: sample peaks, peak hold and clipping; presentation below mirrors
// the built-in renderer defaults while keeping the source 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);
const clipColor = 0xffff3030;

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

// Callback data (one channel or mix, all Float32Array(1)):
//   data.levels:  normalized current level (0..1 on the minDb..maxDb scale).
//   data.peaks:   normalized held peak position (0..1 on the same scale).
//   data.clipped: clip indicator, 1 while clipping is detected/held, else 0.
// JSplitter owns and reuses these buffers.
const meter = vis.Create(VisSourceType.PeakMeter, {
    channel: "mix", // Default: "mix"; also accepts a vis.AudioFormat channel name or unsigned PCM index.
    minDb: -60, // Default: -60 dBFS; allowed: -160 <= value < maxDb.
    maxDb: 0, // Default: 0 dBFS; allowed: minDb < value <= +24.
    attackMs: 0, // Default: 0 ms; allowed: >= 0 ms.
    releaseMs: 150, // Default: 150 ms; allowed: >= 0 ms.
    peakHoldMs: 500, // Default: 500 ms; allowed: >= 0 ms.
    peakGravity: 3, // Default: 3; allowed: >= 0.
    clipHoldMs: 1000 // Default: 1000 ms; allowed: integer 0..60000 ms.
}, (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]));
    const clipped = data.clipped[0] !== 0;

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

    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);
        }
    }

    // showClip is false by default, matching the built-in renderer. Enabling it
    // overlays the rightmost clipWidth pixels; no separate track or gap is used.
    if (showClip && clipped)
        gr.FillSolidRect(x + Math.max(0, w - clipWidth), y, Math.min(w, clipWidth), h, clipColor);
});

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);