Documentation/Source example
Example source

samples/vis/Shader - Peak Meter.js

Referenced by:vis.CreateAsync
include('docs/Flags.js');
window.DrawMode = 1;
// JSplitter vis sample: Peak Meter | custom HLSL.
// Source: sample peaks, peak hold and clipping. The shader reproduces the
// built-in renderer defaults: full-height theme-colored bar, 1 px held peak,
// and no clip indicator unless ShowClip is changed from its default false.
// HLSL is compiled off the UI thread; painting starts after CreateAsync resolves.

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;

function hlslColor(argb) {
    argb >>>= 0;
    return [
        ((argb >>> 16) & 0xff) / 255,
        ((argb >>> 8) & 0xff) / 255,
        (argb & 0xff) / 255,
        ((argb >>> 24) & 0xff) / 255
    ].map(value => value.toFixed(8)).join(', ');
}

const peakMeterShader = `
Texture2D<float> LevelTexture : register(t0);
Texture2D<float> PeakTexture  : register(t1);
Texture2D<float> ClipTexture  : register(t2);

cbuffer VisConstants : register(b0)
{
    float2 viewportSize;
    float time;
    float sourceCount;
};

struct VS_OUTPUT
{
    float4 clipSpacePos : SV_POSITION;
    float4 sceneSpacePos : SCENE_POS;
};

static const float4 MeterColor = float4(${hlslColor(textColor)});
static const float4 ClipColor = float4(${hlslColor(clipColor)});
static const float PeakSize = 1.0;
static const float ShowPeak = 1.0;
static const float ShowClip = 0.0;
static const float ClipWidth = 4.0;

float4 Premultiply(float4 color, float coverage)
{
    float alpha = color.a * saturate(coverage);
    return float4(color.rgb * alpha, alpha);
}

float4 CompositeOver(float4 foreground, float4 background)
{
    return foreground + background * (1.0 - foreground.a);
}

float4 main(VS_OUTPUT input) : SV_Target
{
    float2 size = max(viewportSize, float2(1.0, 1.0));
    float2 p = input.sceneSpacePos.xy;
    float levelEnd = saturate(LevelTexture.Load(int3(0, 0, 0)).r) * size.x;
    float holdEnd = saturate(PeakTexture.Load(int3(0, 0, 0)).r) * size.x;
    float clipped = ClipTexture.Load(int3(0, 0, 0)).r;

    float barCoverage = levelEnd > 0.0 && p.x >= 0.0 && p.x < levelEnd ? 1.0 : 0.0;
    float holdCoverage = 0.0;
    if (ShowPeak > 0.5 && holdEnd >= 1.0)
    {
        float markerLeft = max(0.0, holdEnd - PeakSize * 0.5);
        float markerRight = min(size.x, holdEnd + PeakSize * 0.5);
        if (markerLeft < markerRight && p.x >= markerLeft && p.x < markerRight)
            holdCoverage = 1.0;
    }

    float clipCoverage = 0.0;
    if (ShowClip > 0.5 && clipped >= 0.5 && p.x >= max(0.0, size.x - ClipWidth))
        clipCoverage = 1.0;

    float4 barPixel = Premultiply(MeterColor, barCoverage);
    float4 holdPixel = Premultiply(MeterColor, holdCoverage);
    float4 clipPixel = Premultiply(ClipColor, clipCoverage);
    return CompositeOver(clipPixel, CompositeOver(holdPixel, barPixel));
}
`;

const source = {
    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.
};

let meter = null;
vis.CreateAsync(VisSourceType.PeakMeter, source, { shader: peakMeterShader })
    .then(visualization => {
        meter = visualization;
        window.Repaint();
    })
    .catch(error => console.log("Peak Meter shader compilation failed: " + error));

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

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