Waveform

by MusicBar, progress bar

The whole song as a streaming-player waveform, colored up to the playhead, with the bars by the cursor breathing to the live music.

21 downloads Download file, 4.2 KB
How it installs, its SHA-256 and its code

Click Apply: MusicBar downloads it, checks its SHA-256 and puts it on the taskbar, or just puts it on if it already has it. Needs MusicBar 1.8 or newer.

SHA-256 17977bfff1141839d36c0045e7c01e7d1d2c6ccf87f089a80df7b1aea81735cc

//! name: Waveform
//! author: MusicBar
//! kind: progress
//! description: The whole song drawn as a streaming-player waveform, lit up to the playhead, with the bars by the cursor breathing to the live spectrum.

// A steady random number for a bar.
float hash1(float n) { return frac(sin(n * 78.233) * 43758.5453); }

// Smooth random wobble: values that drift slowly from bar to bar.
float wobble(float x)
{
    float i = floor(x);
    float f = frac(x);
    f = f * f * (3.0 - 2.0 * f);
    return lerp(hash1(i), hash1(i + 1.0), f);
}

// The loudness of the song at t (0 to 1): intro, verse, chorus, verse, chorus, bridge, last chorus, outro.
float shape(float t)
{
    return 0.3 + 0.2 * smoothstep(0.0, 0.08, t) + 0.35 * smoothstep(0.28, 0.31, t) - 0.3 * smoothstep(0.44, 0.47, t)
         + 0.35 * smoothstep(0.58, 0.61, t) - 0.5 * smoothstep(0.77, 0.79, t) + 0.55 * smoothstep(0.84, 0.86, t)
         - 0.7 * smoothstep(0.94, 1.0, t);
}

void layer(inout float3 acc, inout float cover, float3 rgb, float a)
{
    a = saturate(a);
    acc = rgb * a + acc * (1.0 - a);
    cover = a + cover * (1.0 - a);
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float2 px = float2(uv.x * size.x, rise(uv) * size.y);   // pixels: along the bar, up from the screen edge
    float H = size.y;
    float head = progress * size.x;
    float y0 = 0.34 * H;                                    // the line the bars stand on
    float tall = 0.62 * H;

    // One thin bar per cell; its height is fixed per bar, so the picture never jitters.
    float P = max(3.0, 0.14 * H);
    float i = floor(px.x / P);
    float cx = (i + 0.5) * P;
    float r = max(0.75, 0.29 * P);
    float detail = 0.45 * wobble(i * 0.23) + 0.25 * wobble(i * 0.9 + 17.0) + 0.3 * hash1(i);
    float h = shape(cx / size.x) * (0.3 + 0.75 * detail);

    // The bars by the cursor breathe with the live spectrum: bass at the cursor, treble further out.
    float k = i - floor(head / P);
    float near = exp(-abs(k) / 2.2);
    float s = spectrum(saturate(0.06 + abs(k) * 0.08));
    h *= lerp(1.0, 0.45 + 1.1 * s + 0.3 * beat, near);
    h = clamp(h, 0.06, 1.0);

    // The bar (a capsule above the line) and its short reflection below.
    float top = y0 + max(h * tall, 2.0 * r);
    float dy = px.y - clamp(px.y, y0 + r + 0.5, top - r);
    float d = length(float2(px.x - cx, dy)) - r;
    float bottom = y0 - max(h * tall * 0.42, 2.0 * r);
    float dyR = px.y - clamp(px.y, bottom + r, y0 - r - 0.5);
    float dR = length(float2(px.x - cx, dyR)) - r;
    float bar = smoothstep(0.5, -0.5, d);
    float mirror = smoothstep(0.5, -0.5, dR) * (0.25 + 0.75 * saturate((px.y - bottom) / (y0 - bottom)));

    // Played: the palette gradient up the bar, a slow sheen running through, a halo near the cursor.
    float played = smoothstep(0.5, -0.5, px.x - head);
    float t = saturate((px.y - y0) / tall);
    float3 hot = lerp(colorA.rgb, colorB.rgb, t);
    float sweep = frac(time * 0.11) * (head + 6.0 * H) - 3.0 * H;
    float sheen = exp(-abs(px.x - sweep) / (1.5 * H)) * 0.35;
    hot = lerp(hot, float3(1, 1, 1), sheen) * (0.9 + 0.2 * level);
    float3 cool = float3(0.6, 0.62, 0.68);

    float3 acc = float3(0, 0, 0);
    float cover = 0;
    float halo = exp(-max(d, 0.0) / (0.05 * H + 0.8)) * exp(-abs(px.x - head) / (2.2 * H)) * played;
    layer(acc, cover, colorA.rgb, halo * (0.25 + 0.3 * level));
    layer(acc, cover, lerp(cool, hot, played), mirror * lerp(0.2, 0.42, played));
    layer(acc, cover, lerp(cool, hot, played), bar * lerp(0.5, 1.0, played));

    // The cursor: a thin glowing line with a knob on the base line that swells on the beat.
    float dl = abs(px.x - head);
    float lineMask = step(0.05 * H, px.y) * step(px.y, 0.97 * H);
    layer(acc, cover, colorC.rgb, exp(-dl / (0.06 * H + 1.0)) * 0.35 * lineMask);
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.5), smoothstep(0.9, 0.2, dl) * lineMask * 0.9);
    float2 kp = px - float2(head, y0);
    float rk = 0.12 * H * (1.0 + 0.3 * beat);
    float dk = length(kp) - rk;
    layer(acc, cover, colorA.rgb, exp(-max(dk, 0.0) / (0.08 * H + 2.0 * beat)) * (0.55 + 0.4 * beat));
    layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.75 - 0.3 * smoothstep(-0.6 * rk, 0.0, dk)), smoothstep(0.5, -0.5, dk));

    return paint(acc / max(cover, 0.001), cover);
}