From ea74aaaf2eaeb4d9c8ce4d35b4a6694d0579574a Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Wed, 17 Jun 2026 06:33:03 -0400 Subject: [PATCH] fix(mix): reduce waveform smoothing to 15 ms; turn off DEBUG flags --- .../Processors/RmsLoudnessAlgorithm.cs | 17 +++++++++-------- .../Controls/MixWaveformVisualizer.razor.cs | 7 ++----- .../Interop/visualizer/MixVisualizer.ts | 6 ++---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/DeepDrftContent/Processors/RmsLoudnessAlgorithm.cs b/DeepDrftContent/Processors/RmsLoudnessAlgorithm.cs index 8f2a2f3..7a5a8d6 100644 --- a/DeepDrftContent/Processors/RmsLoudnessAlgorithm.cs +++ b/DeepDrftContent/Processors/RmsLoudnessAlgorithm.cs @@ -3,19 +3,20 @@ namespace DeepDrftContent.Processors; /// /// Loudness via root-mean-square amplitude per time bucket. Decodes signed PCM (8-bit unsigned, /// 16/24/32-bit signed little-endian), averages channels to mono, partitions the frames into -/// equal time slices, takes the RMS of each slice, applies a ~50 ms envelope-follower smoothing +/// equal time slices, takes the RMS of each slice, applies a ~15 ms envelope-follower smoothing /// so the contour reads as a smooth curve rather than a spikey polygon, then peak-normalizes so /// the loudest bucket is 1. No external audio dependency — operates directly on the WAV data-chunk bytes. /// public class RmsLoudnessAlgorithm : ILoudnessAlgorithm { /// - /// Envelope-follower time constant, seconds. ~50 ms is the spec's smoothing target (Phase 10 - /// tuning): long enough to round off the per-bucket RMS spikes into a smooth ribbon contour, - /// short enough that real loudness transients (kicks, drops) still read. Applied as a symmetric - /// (forward+backward) one-pole filter so the smoothing introduces no time lag. + /// Envelope-follower time constant, seconds. ~15 ms is the smoothing target (Phase 10 + /// tuning, reduced from 50 ms which was over-smoothed): long enough to round off the + /// per-bucket RMS spikes into a smooth ribbon contour, short enough that real loudness + /// transients (kicks, drops) still read. Applied as a symmetric (forward+backward) one-pole + /// filter so the smoothing introduces no time lag. /// - public const double SmoothingTimeConstantSeconds = 0.05; + public const double SmoothingTimeConstantSeconds = 0.015; public double[] Compute(ReadOnlySpan pcmData, int channels, int sampleRate, int bitsPerSample, int bucketCount) { @@ -81,7 +82,7 @@ public class RmsLoudnessAlgorithm : ILoudnessAlgorithm } } - // Envelope smoothing (~50 ms): round the spikey per-bucket RMS into a smooth contour before + // Envelope smoothing (~15 ms): round the spikey per-bucket RMS into a smooth contour before // peak-normalization, so the rendered ribbon reads as a continuous curve, not faceted polygons. // Each bucket spans (totalSeconds / bucketCount) of audio; the filter coefficient is derived // from that against the time constant so the smoothing is duration-aware, not a fixed window. @@ -117,7 +118,7 @@ public class RmsLoudnessAlgorithm : ILoudnessAlgorithm /// Symmetric one-pole envelope smoothing over the per-bucket loudness, in place. A forward pass /// then a backward pass cancels the single-pole phase lag, so the smoothed contour stays aligned /// with the audio (no rightward time shift). The coefficient a = exp(−bucketSeconds / τ) - /// gives a ~-relative response targeting the ~50 ms time constant: + /// gives a ~-relative response targeting the ~15 ms time constant: /// each bucket blends (1 − a) of itself with a of the running envelope. A near-zero /// or non-finite bucket duration leaves the data untouched (nothing to smooth meaningfully). /// diff --git a/DeepDrftPublic.Client/Controls/MixWaveformVisualizer.razor.cs b/DeepDrftPublic.Client/Controls/MixWaveformVisualizer.razor.cs index a3c542a..dbb4f0f 100644 --- a/DeepDrftPublic.Client/Controls/MixWaveformVisualizer.razor.cs +++ b/DeepDrftPublic.Client/Controls/MixWaveformVisualizer.razor.cs @@ -57,11 +57,8 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable // Bridge-level diagnostics. Mirrors the JS-side DEBUG flag in MixVisualizer.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 - // which upstream link is broken when the ribbon stays blank — set false once confirmed healthy. - // ON for the Phase 10 reframe Wave R4 controls test (matches the JS-side DEBUG in - // MixVisualizer.ts). Daniel evaluates the eight-knob bar + pause behavior in-browser; flip back to - // false at reframe close along with the JS flag. - private static readonly bool Debug = true; + // 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 static void DebugLog(string message) diff --git a/DeepDrftPublic/Interop/visualizer/MixVisualizer.ts b/DeepDrftPublic/Interop/visualizer/MixVisualizer.ts index e235910..c244f9c 100644 --- a/DeepDrftPublic/Interop/visualizer/MixVisualizer.ts +++ b/DeepDrftPublic/Interop/visualizer/MixVisualizer.ts @@ -364,10 +364,8 @@ const PLAYHEAD_CORRECTION_SNAP_SECONDS = 0.0005; // 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 // while the runtime fix is being verified through the browser. -// NOTE: ON for the Phase 10 reframe Wave R4 controls pass. Daniel tests in-browser; the FPS lines -// (which should hold ~60 even while paused, confirming the continuous-loop power cost is acceptable) -// + the seven-dial lava line confirm the controls + pause fix. Flip back to false at reframe close. -const DEBUG = true; +// NOTE: defaults to false; set true temporarily to surface verbose seams in-browser. +const DEBUG = false; const TAG = '[MixVisualizer]'; function debugLog(...args: unknown[]): void {