Divide the sum loss before smoothing it, not after - #76
Conversation
The Virtual DSP and Compare sum-loss curves smoothed the channel and sum magnitudes and then subtracted them. That order does not commute: a fractional-octave window straddling a steep crossover skirt pulls that channel up toward its own passband — several dB on a 36 dB/octave slope, more under the psychoacoustic cubic mean — while the flat sum barely moves, so the magnitude sum inflates faster than the complex sum and the curve draws a dip at every corner. A measured 70 Hz junction losing a true 0.2 dB read as 1.6 dB. Build the loss out of the unsmoothed pair (both widths now come from one gate and one FFT) and smooth the finished ratio with a plain mean of decibels — the psychoacoustic width is kept, its magnitude weighting dropped, since a ratio is not a level. The per-junction avg/dip read-outs consume that same curve instead of re-deriving it, so the label and the trace cannot disagree. Same fix for the Compare view's Complex Sum Loss and its overlay slot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Complex Sum Loss overlay slot smoothed twice: the pipeline baked the FR plot's width into the ratio, then the slot smoothed the result again with its own. Switching the main plot's smoothing moved a slot set to Off, and 1/6 on both meant two 1/6 passes. The slot's pass also had to run with the magnitude weighting disabled — correct for a ratio, but that switch turns the whole psychoacoustic mode off inside the overlay smoother, collapsing its 1/3-octave bass window to a fixed 1/6 and its Gaussian to a cosine, so the overlay drew a different bass than the Compare curve it mirrors. Hand the slot's width down to TryBuildComplexSumLossCurve instead (the Compare view keeps passing its own, as its default) and drop the second pass: the ratio is smoothed once, by SmoothRatioLevels, at the width whose control the user just moved. The complex SUM overlay is a magnitude curve and keeps inheriting the plot's pipeline as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both findings confirmed against the code and fixed in 0748705. 1. Double smoothing / inherited plot width — real. 2. Psychoacoustic width collapsing to a fixed 1/6 — real. Fixed exactly as suggested: Tests added, both as requested:
Full solution suite green: 2081 tests. |
The Virtual DSP "Sum loss" curve drew dips at 75 Hz and 170 Hz in the
psychoacoustic mode that the unsmoothed curve does not contain. Both sat on
crossover corners (70 Hz sub/bass, 180 Hz bass/mid), and the 75 Hz one is
entirely manufactured: the same session measures −0.19 dB of true loss there.
Why
The loss is
20log10(|ΣH|) − 20log10(Σ|H|), and both operands were display-smoothed before the subtraction. That does not commute. A fractional-octave
window straddling a steep skirt pulls the rolling-off channel up toward its own
passband — several dB on a 36 dB/octave slope, and more again under the
psychoacoustic mode's peak-weighted cubic mean — while the flat sum barely
moves. Measured on the same session, at the 70 Hz corner the bass channel reads
−21.6 dB unsmoothed and −16.4 dB smoothed (+5.2 dB) against +1.1 dB for the sum,
so
Σ|H|inflates faster than|ΣH|and the difference is drawn as a dip.The plain 1/N modes hide it in the bass only because their window clamps to the
two-bin minimum down there and effectively does nothing; the psychoacoustic mode
is 1/3 octave below 100 Hz, which is where the artifact appears.
What changed
SumLossCurvetakes the smoothing width and applies it to the finished ratio;its operands must now be unsmoothed, and the doc says why.
DataHelper.SmoothRatioLevels: a plain arithmetic mean of decibels on thedisplay grid (box for a fixed width, Gaussian for the psychoacoustic one). The
psychoacoustic mode keeps its frequency-dependent WIDTH and loses only the
cubic magnitude weighting, which biases a ratio. Non-finite points stay gaps
and are excluded from their neighbours' means.
AverageSumLossDb/MinimumSumLossDbread a curve that was already builtinstead of deriving their own, so the read-out and the drawn trace are the
same numbers by construction, smoothing included.
GetGatedPrimarySpectrumPairreturns the display and unsmoothed curves fromone gate and one FFT (the same instance when smoothing is off), so the honest
order costs one extra resample per curve, not a second gated FFT.
Complex Sum Lossand in theoverlay slot that stores it.
Effect on the reported session (psychoacoustic mode)
Per-junction read-outs: sub/bass avg −0.70 → −0.24, dip −1.63 → −0.67;
bass/mid avg −0.83 → −0.46, dip −2.19 → −0.74. The real 170 Hz cancellation
survives, widened and shallowed by the smoothing as a narrow notch should be;
the invented corner dips are gone. The smoothing selector no longer moves the
measured loss by half a dB, so it can no longer disagree with Auto delay's own
prediction, which is computed on a fixed internal grid.
Tests
SumLossCurve_SmoothsTheRatio_NotTheOperands— an ideal complementary 70 Hz /48 dB/octave crossover, in phase, whose sum is flat 0 dB: the honest answer is
0 dB at every frequency. The shipped order returns it; the operand-first order
invents a −0.87 dB dip at the corner, so the test fails on the old code.
SmoothRatioLevels_AveragesDecibelsWithoutTheMagnitudeBias— a −6 dB stepsmooths toward the arithmetic middle (a power mean would read ≈ −2.0 dB), and
gaps neither spread nor fill.
Verified end to end against the reporting session's own measurements through a
scratchpad harness that replays the panel's pipeline (chain application, shared
anchor, 5/50/20 ms fixed gate) on the four left-side channels.
🤖 Generated with Claude Code