refactor(12.A): rename Mix* visualizer engine to Waveform* abstraction
This commit is contained in:
@@ -29,7 +29,7 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
// error banner.
|
||||
//
|
||||
// _miniDock is the minimized FAB container. We observe it in minimized state so
|
||||
// --player-height stays non-zero (the FAB's actual height) and the MixWaveformVisualizer
|
||||
// --player-height stays non-zero (the FAB's actual height) and the WaveformVisualizer
|
||||
// clips to the top of the FAB rather than extending to the viewport bottom (fix §1).
|
||||
// The player-spacer's .minimized class uses a hardcoded 60px and ignores the var,
|
||||
// so publishing the FAB height here does not regress the spacer.
|
||||
@@ -125,7 +125,7 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
// The Fixed embed is already in normal flow — no spacer/clip needed.
|
||||
// For the docked player: we observe in BOTH expanded and minimized states
|
||||
// so --player-height always reflects the live height of whichever element
|
||||
// is visible. This keeps the MixWaveformVisualizer clipped to the top of
|
||||
// is visible. This keeps the WaveformVisualizer clipped to the top of
|
||||
// the footer in both states (fix §1).
|
||||
// expanded → observe _playerRoot (full player bar, reflows across breakpoints)
|
||||
// minimized → observe _miniDock (floating FAB container, ~56–60px)
|
||||
|
||||
+4
-4
@@ -1,16 +1,16 @@
|
||||
@namespace DeepDrftPublic.Client.Controls
|
||||
|
||||
@* Full-page scrolling Mix waveform background (Phase 9, 8.K). A windowed slice of the mix's loudness
|
||||
@* Full-page scrolling waveform background (Phase 9, 8.K). A windowed slice of the track's loudness
|
||||
datum scrolls bottom-to-top, coupled to playback; a zoom slider controls the visible time-span (and
|
||||
so the apparent scroll speed, Guitar-Hero style). Strictly read-only: it self-fetches its datum from
|
||||
ReleaseEntryKey, takes playback as one-way input only, and never seeks or writes back. The rAF loop and all
|
||||
scroll/zoom/compositing math live in the MixVisualizer.ts interop module; this component is a thin
|
||||
scroll/zoom/compositing math live in the WaveformVisualizer.ts interop module; this component is a thin
|
||||
bridge that feeds it datum + playback + zoom + theme. Deliberately NOT the player-bar peak-bar idiom. *@
|
||||
|
||||
<div class="mix-waveform-bg">
|
||||
<canvas @ref="_canvas" class="mix-waveform-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
@* The viewing controls (resolution + the three Wave 2 controls) live in MixVisualizerControls,
|
||||
@* The viewing controls (resolution + the three Wave 2 controls) live in WaveformVisualizerControls,
|
||||
rendered in the mix-detail foreground row below the back button — NOT here. This component is now a
|
||||
pure backdrop bridge; it pushes uniforms in response to the shared MixVisualizerControlState. *@
|
||||
pure backdrop bridge; it pushes uniforms in response to the shared WaveformVisualizerControlState. *@
|
||||
+12
-12
@@ -8,10 +8,10 @@ using Microsoft.JSInterop;
|
||||
namespace DeepDrftPublic.Client.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Full-page scrolling Mix waveform background. Standalone and reusable: give it a
|
||||
/// Full-page scrolling waveform background. Standalone and reusable: give it a
|
||||
/// <see cref="ReleaseEntryKey"/> and it fetches its own loudness datum. The rendering itself — a windowed,
|
||||
/// bottom-to-top, playback-coupled scroll with a glassy theme-aware gradient — lives in the
|
||||
/// MixVisualizer.ts interop module; this component is the bridge that feeds it datum, playback
|
||||
/// WaveformVisualizer.ts interop module; this component is the bridge that feeds it datum, playback
|
||||
/// position, zoom, and theme, and owns the module lifecycle.
|
||||
///
|
||||
/// Strictly read-only (spec §D): no seek, no two-way write-back. <see cref="PlaybackPosition"/> is a
|
||||
@@ -20,12 +20,12 @@ namespace DeepDrftPublic.Client.Controls;
|
||||
/// <see cref="PlaybackPosition"/> parameter is the composability fallback for hosts that have no
|
||||
/// player cascade (e.g. an embed) and want to drive position themselves.
|
||||
/// </summary>
|
||||
public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
public partial class WaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
[Inject] public required IReleaseDataService ReleaseData { get; set; }
|
||||
[Inject] public required IJSRuntime JS { get; set; }
|
||||
[Inject] public required MixVisualizerControlState ControlState { get; set; }
|
||||
[Inject] public required ILogger<MixWaveformVisualizer> Logger { get; set; }
|
||||
[Inject] public required WaveformVisualizerControlState ControlState { get; set; }
|
||||
[Inject] public required ILogger<WaveformVisualizer> Logger { get; set; }
|
||||
|
||||
// Live playback + the mix duration come from the cascaded streaming player when present. The
|
||||
// cascade is IsFixed, so we subscribe to its multicast StateChanged side-channel to learn about
|
||||
@@ -54,12 +54,12 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
/// </summary>
|
||||
[Parameter] public double PlaybackPosition { get; set; }
|
||||
|
||||
// Bridge-level diagnostics. Mirrors the JS-side DEBUG flag in MixVisualizer.ts: when true the
|
||||
// Bridge-level diagnostics. Mirrors the JS-side DEBUG flag in WaveformVisualizer.ts: when true the
|
||||
// datum-fetch / subscription / playback-coupling seams log to the browser console (prefixed
|
||||
// `[MixVisualizer]`, same as the JS logs so the two interleave into one timeline). These pinpoint
|
||||
// `[WaveformVisualizer]`, same as the JS logs so the two interleave into one timeline). These pinpoint
|
||||
// which upstream link is broken when the ribbon stays blank — set true temporarily to diagnose.
|
||||
private static readonly bool Debug = false;
|
||||
private const string Tag = "[MixVisualizer]";
|
||||
private const string Tag = "[WaveformVisualizer]";
|
||||
|
||||
private static void DebugLog(string message)
|
||||
{
|
||||
@@ -164,12 +164,12 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
try
|
||||
{
|
||||
_module = await JS.InvokeAsync<IJSObjectReference>(
|
||||
"import", "./js/visualizer/MixVisualizer.js");
|
||||
"import", "./js/visualizer/WaveformVisualizer.js");
|
||||
_handle = await _module.InvokeAsync<IJSObjectReference>("create", _canvas);
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "MixWaveformVisualizer: failed to load the visualizer module; rendering a plain backdrop.");
|
||||
Logger.LogWarning(ex, "WaveformVisualizer: failed to load the visualizer module; rendering a plain backdrop.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
/// and to re-push when the controls bar signals a change. Each value is its own dedicated dial:
|
||||
/// <list type="bullet">
|
||||
/// <item>scroll speed [0,1] is mapped onto the useful zoom band via
|
||||
/// <see cref="MixZoomMapping.ScrollKnobToSeconds"/> and pushed through <c>setScrollSpeed</c>
|
||||
/// <see cref="WaveformZoomMapping.ScrollKnobToSeconds"/> and pushed through <c>setScrollSpeed</c>
|
||||
/// (higher speed → tighter window → faster scroll);</item>
|
||||
/// <item>gradient rotation speed → <c>setGradientRotationSpeed</c> (live OKLab anchor rotation);</item>
|
||||
/// <item>gravity / heat / collision strength → their dedicated lava-physics dials;</item>
|
||||
@@ -218,7 +218,7 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
if (_handle is null) return;
|
||||
// Scroll speed is a normalized [0,1] axis; map it onto the useful zoom band (Phase 10 retune —
|
||||
// the knob's full travel now covers the 60%–100% zoom range, dropping the dead slow/wide end).
|
||||
var visibleSeconds = MixZoomMapping.ScrollKnobToSeconds(ControlState.ScrollSpeed);
|
||||
var visibleSeconds = WaveformZoomMapping.ScrollKnobToSeconds(ControlState.ScrollSpeed);
|
||||
await _handle.InvokeVoidAsync("setScrollSpeed", visibleSeconds);
|
||||
await _handle.InvokeVoidAsync("setGradientRotationSpeed", ControlState.GradientRotationSpeed);
|
||||
await _handle.InvokeVoidAsync("setLavaGravity", ControlState.LavaGravity);
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
@namespace DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inject MixVisualizerControlState ControlState
|
||||
@inject WaveformVisualizerControlState ControlState
|
||||
|
||||
@* The Mix visualizer controls. EIGHT continuous RadialKnobs — scroll speed, gradient rotation speed,
|
||||
@* The waveform visualizer controls. EIGHT continuous RadialKnobs — scroll speed, gradient rotation speed,
|
||||
lava gravity, lava heat, fluid amount, fluid viscosity, collision strength, waveform width — each its
|
||||
own dedicated control with a Material-icon caption. The single "bubbles" knob is split into
|
||||
fluid-amount + fluid-viscosity (Phase 10 §5).
|
||||
@@ -14,8 +14,8 @@
|
||||
convention) — the knobs are simply not rendered when hidden, while a min-height container holds the
|
||||
layout. No collapse animation, no glass surface, no CSS visibility-hiding of populated knobs.
|
||||
|
||||
It owns NO JS interop: it mutates the shared, session-scoped MixVisualizerControlState and raises its
|
||||
Changed event. The backdrop bridge (MixWaveformVisualizer) subscribes to that event and pushes the
|
||||
It owns NO JS interop: it mutates the shared, session-scoped WaveformVisualizerControlState and raises its
|
||||
Changed event. The backdrop bridge (WaveformVisualizer) subscribes to that event and pushes the
|
||||
affected dial to the WebGL module. That keeps the JS module handle single-owned by the bridge and
|
||||
this component purely presentational. None of these is a seek surface (read-only contract §D).
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
namespace DeepDrftPublic.Client.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Pure mapping between the Mix visualizer's zoom slider position [0, 1] and the visible time-span in
|
||||
/// Pure mapping between the waveform visualizer's zoom slider position [0, 1] and the visible time-span in
|
||||
/// seconds. The span range is wide (0.333 s … 30 s, ~90×), so the mapping is logarithmic — equal
|
||||
/// slider travel changes the span by an equal *ratio*, which feels even to the hand. Slider
|
||||
/// orientation: fraction 0 = most zoomed-out (longest span), fraction 1 = most zoomed-in (the
|
||||
/// 0.333 s quarter-note-@-180-BPM anchor). Extracted from the component so the math is unit-testable.
|
||||
/// </summary>
|
||||
public static class MixZoomMapping
|
||||
public static class WaveformZoomMapping
|
||||
{
|
||||
/// <summary>Shortest span (max zoom): one quarter note at 180 BPM = 60/180 s. Hard anchor.</summary>
|
||||
public const double MinVisibleSeconds = 60.0 / 180.0;
|
||||
@@ -1,6 +1,6 @@
|
||||
.deepdrft-footer {
|
||||
/* position:relative + z-index:1 creates a stacking context that paints above the
|
||||
MixWaveformVisualizer backdrop (z-index:0), keeping footer text fully legible. */
|
||||
WaveformVisualizer backdrop (z-index:0), keeping footer text fully legible. */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: var(--deepdrft-white);
|
||||
|
||||
@@ -36,7 +36,7 @@ else
|
||||
@* Full-page waveform sits behind the scaffold content. The scaffold's container is positioned
|
||||
above it via the mix-detail-foreground stacking context. TrackId lets the visualizer couple to
|
||||
playback only when the player is on this mix's track. *@
|
||||
<MixWaveformVisualizer ReleaseEntryKey="@release.EntryKey" TrackId="@ViewModel.Track?.Id" />
|
||||
<WaveformVisualizer ReleaseEntryKey="@release.EntryKey" TrackId="@ViewModel.Track?.Id" />
|
||||
|
||||
<div class="mix-detail-foreground">
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="mix-detail-container">
|
||||
@@ -57,9 +57,9 @@ else
|
||||
Phase 10 §4: the control is ALWAYS rendered; the lava-lamp toggle feeds its Visible
|
||||
parameter, and the control itself @if-gates the knobs while holding the container's
|
||||
reserved height — so content below never pops on toggle. The band mutates the shared
|
||||
MixVisualizerControlState; the backdrop bridge pushes the dials. A knob drag does not
|
||||
WaveformVisualizerControlState; the backdrop bridge pushes the dials. A knob drag does not
|
||||
toggle it — the lamp's click does. *@
|
||||
<MixVisualizerControls Visible="@_controlsExpanded" />
|
||||
<WaveformVisualizerControls Visible="@_controlsExpanded" />
|
||||
</TopContent>
|
||||
<TopRightAction>
|
||||
@* Lava-lamp button top-right, across from the back link. Toggles the knob band below the
|
||||
@@ -126,8 +126,8 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
// Lava-lamp knob-band visibility. Pure presentation over MixVisualizerControlState — gates whether
|
||||
// the seven-knob MixVisualizerControls is rendered into the TopContent band; toggling it touches no
|
||||
// Lava-lamp knob-band visibility. Pure presentation over WaveformVisualizerControlState — gates whether
|
||||
// the seven-knob WaveformVisualizerControls is rendered into the TopContent band; toggling it touches no
|
||||
// control value or bridge push. The lava-lamp button's filled/outline glyph is driven off this flag.
|
||||
private bool _controlsExpanded;
|
||||
|
||||
|
||||
+14
-14
@@ -1,7 +1,7 @@
|
||||
namespace DeepDrftPublic.Client.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the Mix visualizer's eight continuous-control positions for the lifetime of the WASM app
|
||||
/// Holds the waveform visualizer's eight continuous-control positions for the lifetime of the WASM app
|
||||
/// instance. Scoped in DI, so it lives across SPA navigations within one listening session — open a
|
||||
/// second mix and the knobs keep where you left them — but a fresh page load (F5) constructs a new
|
||||
/// instance, resetting to defaults. That matches the spec's "persist within session, reset on fresh
|
||||
@@ -11,27 +11,27 @@ namespace DeepDrftPublic.Client.Services;
|
||||
/// parameters: this is a plain scoped value holder, so widening it (the Phase 10 split of the single
|
||||
/// density knob into fluid-amount + fluid-viscosity) adds fields + defaults only and never forces a
|
||||
/// consumer constructor to grow. Each C#-side default mirrors a TS-side tuning anchor in
|
||||
/// MixVisualizer.ts; keep the two in sync, as the <c>DefaultVisibleSeconds</c> /
|
||||
/// WaveformVisualizer.ts; keep the two in sync, as the <c>DefaultVisibleSeconds</c> /
|
||||
/// <c>DEFAULT_VISIBLE_SECONDS</c> pair does.
|
||||
///
|
||||
/// <para>
|
||||
/// <see cref="Changed"/> is the decoupling seam between the controls bar and the visualizer bridge.
|
||||
/// The controls component (a sibling of the backdrop in the page tree) only mutates this shared state
|
||||
/// and raises <see cref="Changed"/>; the bridge component (<c>MixWaveformVisualizer</c>) subscribes
|
||||
/// and raises <see cref="Changed"/>; the bridge component (<c>WaveformVisualizer</c>) subscribes
|
||||
/// and pushes the affected uniform/dial to the JS module. This keeps the JS module handle single-owned
|
||||
/// by the bridge — no handle sharing, no service-locator, no cross-component interop.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class MixVisualizerControlState
|
||||
public sealed class WaveformVisualizerControlState
|
||||
{
|
||||
// ── The eight control defaults (Phase 10). Each mirrors a DEFAULT_* anchor in
|
||||
// MixVisualizer.ts; keep the two in sync, as the default-sync discipline requires.
|
||||
// WaveformVisualizer.ts; keep the two in sync, as the default-sync discipline requires.
|
||||
// Feel-anchors only — Daniel tunes on screen; the ~20% gravity / ~100% heat pair is his stated
|
||||
// sweet spot (§4c).
|
||||
|
||||
/// <summary>
|
||||
/// Default scroll-speed dial. Normalized [0,1] → mapped C#-side to a visible time-span in seconds
|
||||
/// via <see cref="MixZoomMapping"/>, then sent to MixVisualizer.ts as a seconds value via
|
||||
/// via <see cref="WaveformZoomMapping"/>, then sent to WaveformVisualizer.ts as a seconds value via
|
||||
/// <c>setScrollSpeed</c>. The TS-side anchor is <c>DEFAULT_VISIBLE_SECONDS</c>. Opens mid-range
|
||||
/// (0 = slow/wide window, 1 = fast/tight window).
|
||||
/// </summary>
|
||||
@@ -39,26 +39,26 @@ public sealed class MixVisualizerControlState
|
||||
|
||||
/// <summary>
|
||||
/// Default gradient-rotation-speed dial. Mirrors <c>DEFAULT_GRADIENT_ROTATION_SPEED</c> in
|
||||
/// MixVisualizer.ts. Normalized [0,1] → slow→fast anchor-rotation; drives the live OKLab
|
||||
/// WaveformVisualizer.ts. Normalized [0,1] → slow→fast anchor-rotation; drives the live OKLab
|
||||
/// three-colour gradient. 0.45 opens with a clearly-visible ~7 s colour cycle (Phase 10 §3.2).
|
||||
/// </summary>
|
||||
public const double DefaultGradientRotationSpeed = 0.45;
|
||||
|
||||
/// <summary>
|
||||
/// Default lava-gravity dial. Mirrors <c>DEFAULT_LAVA_GRAVITY</c> in MixVisualizer.ts. Normalized
|
||||
/// Default lava-gravity dial. Mirrors <c>DEFAULT_LAVA_GRAVITY</c> in WaveformVisualizer.ts. Normalized
|
||||
/// [0,1]; 0 = near-weightless float, 1 = wax falls + settles fast. Tuned to Daniel's ~20% sweet spot.
|
||||
/// </summary>
|
||||
public const double DefaultLavaGravity = 0.2;
|
||||
|
||||
/// <summary>
|
||||
/// Default lava-heat dial. Mirrors <c>DEFAULT_LAVA_HEAT</c> in MixVisualizer.ts. Normalized [0,1];
|
||||
/// Default lava-heat dial. Mirrors <c>DEFAULT_LAVA_HEAT</c> in WaveformVisualizer.ts. Normalized [0,1];
|
||||
/// 0 = wax rests at the bottom (collision-only), 1 = many small turbulent rising bubbles. Tuned to
|
||||
/// Daniel's ~100% sweet spot.
|
||||
/// </summary>
|
||||
public const double DefaultLavaHeat = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Default fluid-amount dial. Mirrors <c>DEFAULT_FLUID_AMOUNT</c> in MixVisualizer.ts. The first
|
||||
/// Default fluid-amount dial. Mirrors <c>DEFAULT_FLUID_AMOUNT</c> in WaveformVisualizer.ts. The first
|
||||
/// half of the Phase 10 "bubbles" split. Normalized [0,1]; 0 = few small blobs, 1 = many larger
|
||||
/// blobs (more wax in the container — blob count + per-blob volume).
|
||||
/// </summary>
|
||||
@@ -66,26 +66,26 @@ public sealed class MixVisualizerControlState
|
||||
|
||||
/// <summary>
|
||||
/// Default fluid-viscosity / cohesion dial. Mirrors <c>DEFAULT_FLUID_VISCOSITY</c> in
|
||||
/// MixVisualizer.ts. The second half of the Phase 10 "bubbles" split. Normalized [0,1]; 1 = high
|
||||
/// WaveformVisualizer.ts. The second half of the Phase 10 "bubbles" split. Normalized [0,1]; 1 = high
|
||||
/// cohesion (crisp spheres that snap back), 0 = low cohesion (deforms freely, stays gooey/merged).
|
||||
/// </summary>
|
||||
public const double DefaultFluidViscosity = 0.6;
|
||||
|
||||
/// <summary>
|
||||
/// Default collision-strength dial. Mirrors <c>DEFAULT_COLLISION_STRENGTH</c> in MixVisualizer.ts.
|
||||
/// Default collision-strength dial. Mirrors <c>DEFAULT_COLLISION_STRENGTH</c> in WaveformVisualizer.ts.
|
||||
/// Normalized [0,1]; 0 = soft mush, 1 = hard elastic up-and-out throw.
|
||||
/// </summary>
|
||||
public const double DefaultCollisionStrength = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// Default waveform-width dial. Mirrors <c>DEFAULT_WAVEFORM_WIDTH</c> in MixVisualizer.ts.
|
||||
/// Default waveform-width dial. Mirrors <c>DEFAULT_WAVEFORM_WIDTH</c> in WaveformVisualizer.ts.
|
||||
/// Normalized [0,1], mapped onto the useful 10%–95% ribbon-extent band (Phase 10 §3.7); 0.5 opens
|
||||
/// mid-band. Narrowing clears room for the lava.
|
||||
/// </summary>
|
||||
public const double DefaultWaveformWidth = 0.5;
|
||||
|
||||
/// <summary>Apparent bottom-to-top scroll rate, normalized [0,1]. Bridge maps it to a visible
|
||||
/// time-span via <see cref="MixZoomMapping"/>; the standalone resolution/zoom control is gone.</summary>
|
||||
/// time-span via <see cref="WaveformZoomMapping"/>; the standalone resolution/zoom control is gone.</summary>
|
||||
public double ScrollSpeed { get; set; } = DefaultScrollSpeed;
|
||||
|
||||
/// <summary>Gradient anchor-rotation rate, normalized [0,1]. Drives the live OKLab gradient.</summary>
|
||||
@@ -26,9 +26,9 @@ public static class Startup
|
||||
services.AddScoped<ReleaseDetailViewModel>();
|
||||
services.AddScoped<CutDetailViewModel>();
|
||||
|
||||
// Mix visualizer controls — scoped so the four slider positions persist across navigation
|
||||
// within a session and reset on a fresh page load (see MixVisualizerControlState).
|
||||
services.AddScoped<MixVisualizerControlState>();
|
||||
// Waveform visualizer controls — scoped so the eight slider positions persist across navigation
|
||||
// within a session and reset on a fresh page load (see WaveformVisualizerControlState).
|
||||
services.AddScoped<WaveformVisualizerControlState>();
|
||||
}
|
||||
|
||||
public static void ConfigureApiHttpClient(IServiceCollection services, string baseAddress)
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* MixVisualizer — the scrolling Mix waveform background (Phase 10 + Lava Reframe).
|
||||
* WaveformVisualizer — the scrolling waveform background (Phase 10 + Lava Reframe).
|
||||
*
|
||||
* What this renders: a *windowed* slice of a mix's loudness profile, scrolling
|
||||
* bottom-to-top, coupled to playback position. New audio enters at the bottom,
|
||||
@@ -60,7 +60,7 @@ export const MAX_VISIBLE_SECONDS = 30;
|
||||
export const DEFAULT_VISIBLE_SECONDS = 10;
|
||||
|
||||
// ── Control tuning anchors. These mirror the C#-side defaults in ──────────────────
|
||||
// MixVisualizerControlState.cs — keep the two in sync, exactly as the
|
||||
// WaveformVisualizerControlState.ts — keep the two in sync, exactly as the
|
||||
// DEFAULT_VISIBLE_SECONDS / DefaultVisibleSeconds pair is kept in sync. All seven are
|
||||
// normalized [0,1] (scroll speed is mapped to a visible time-span on the C# side before it
|
||||
// reaches setScrollSpeed; it arrives here already in seconds).
|
||||
@@ -359,7 +359,7 @@ const PLAYHEAD_CORRECTION_SNAP_SECONDS = 0.0005;
|
||||
// ── Diagnostics ──────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Set true to surface the init/draw/datum/playback seams to the browser console
|
||||
// (all prefixed `[MixVisualizer]`). The error/warn paths fire regardless of this
|
||||
// (all prefixed `[WaveformVisualizer]`). The error/warn paths fire regardless of this
|
||||
// flag — they only trigger on the abnormal path. The verbose `log` paths (datum
|
||||
// received/uploaded, first-draw dimensions, GL error after first draw) are gated
|
||||
// here so they can be silenced once the renderer is confirmed healthy. Leave it on
|
||||
@@ -367,7 +367,7 @@ const PLAYHEAD_CORRECTION_SNAP_SECONDS = 0.0005;
|
||||
// NOTE: defaults to false; set true temporarily to surface verbose seams in-browser.
|
||||
const DEBUG = false;
|
||||
|
||||
const TAG = '[MixVisualizer]';
|
||||
const TAG = '[WaveformVisualizer]';
|
||||
function debugLog(...args: unknown[]): void {
|
||||
if (DEBUG) console.log(TAG, ...args);
|
||||
}
|
||||
@@ -507,7 +507,7 @@ interface Playback {
|
||||
pushWallClockMs: number;
|
||||
}
|
||||
|
||||
export interface MixVisualizerHandle {
|
||||
export interface WaveformVisualizerHandle {
|
||||
setDatum(samplesBase64: string, durationSeconds: number): void;
|
||||
setPlayback(positionSeconds: number, isPlaying: boolean): void;
|
||||
/** Visible time-span in seconds — the scroll-speed control, mapped from [0,1] on the C# side. */
|
||||
@@ -1028,13 +1028,13 @@ void main() {
|
||||
/** Compile one shader stage, throwing with the info log on failure. */
|
||||
function compileShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader {
|
||||
const shader = gl.createShader(type);
|
||||
if (!shader) throw new Error('MixVisualizer: gl.createShader returned null.');
|
||||
if (!shader) throw new Error('WaveformVisualizer: gl.createShader returned null.');
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
const log = gl.getShaderInfoLog(shader);
|
||||
gl.deleteShader(shader);
|
||||
throw new Error(`MixVisualizer: shader compile failed: ${log}`);
|
||||
throw new Error(`WaveformVisualizer: shader compile failed: ${log}`);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
@@ -1044,7 +1044,7 @@ function linkProgram(gl: WebGL2RenderingContext): WebGLProgram {
|
||||
const vert = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);
|
||||
const frag = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);
|
||||
const program = gl.createProgram();
|
||||
if (!program) throw new Error('MixVisualizer: gl.createProgram returned null.');
|
||||
if (!program) throw new Error('WaveformVisualizer: gl.createProgram returned null.');
|
||||
gl.attachShader(program, vert);
|
||||
gl.attachShader(program, frag);
|
||||
gl.linkProgram(program);
|
||||
@@ -1054,13 +1054,13 @@ function linkProgram(gl: WebGL2RenderingContext): WebGLProgram {
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
const log = gl.getProgramInfoLog(program);
|
||||
gl.deleteProgram(program);
|
||||
throw new Error(`MixVisualizer: program link failed: ${log}`);
|
||||
throw new Error(`WaveformVisualizer: program link failed: ${log}`);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
/** The no-op handle returned when WebGL2 is unavailable or setup fails. */
|
||||
function noopHandle(): MixVisualizerHandle {
|
||||
function noopHandle(): WaveformVisualizerHandle {
|
||||
return {
|
||||
setDatum() {},
|
||||
setPlayback() {},
|
||||
@@ -1077,7 +1077,7 @@ function noopHandle(): MixVisualizerHandle {
|
||||
};
|
||||
}
|
||||
|
||||
export function create(canvas: HTMLCanvasElement): MixVisualizerHandle {
|
||||
export function create(canvas: HTMLCanvasElement): WaveformVisualizerHandle {
|
||||
// premultipliedAlpha so the translucent ribbon composites correctly over the
|
||||
// page; antialias off (the soft-edge smoothstep handles AA in-shader, and MSAA
|
||||
// would cost fill rate we don't need for a backdrop).
|
||||
Reference in New Issue
Block a user