fix: restore waveform symmetry about the midline, cut deck_values' link to the bank model, and unit-test the column arithmetic

The waveform column's vertical extents move to pure component_geometry so the shared
primitive stops being untested; PlaySeconds hoists into a header-only play_seconds target.
This commit is contained in:
2026-08-01 09:47:07 -04:00
parent 7f74b11dce
commit ca464397b2
17 changed files with 310 additions and 138 deletions
+27
View File
@@ -97,4 +97,31 @@ int waveformColumnCount(const KitBox& box) {
return w > 0 ? w : 0;
}
WaveformBand waveformBand(int bandTop, int bandHeight) {
WaveformBand b;
b.top = bandTop;
b.height = bandHeight;
b.midY = bandTop + bandHeight / 2;
b.halfSpan = (bandHeight / 2) - 2; // matches drawWaveform's 2px vertical breathing room
return b;
}
WaveformColumnSpan waveformColumnSpan(const WaveformBand& band,
double compressedMax, double compressedMin) {
const int lo = band.top;
const int hi = band.top + band.height - 1;
WaveformColumnSpan s;
s.top = band.midY - static_cast<int>(compressedMax * band.halfSpan);
s.bottom = band.midY - static_cast<int>(compressedMin * band.halfSpan);
s.topF = band.midY - compressedMax * band.halfSpan;
s.bottomF = band.midY - compressedMin * band.halfSpan;
if (s.top < lo) s.top = lo;
if (s.bottom > hi) s.bottom = hi;
if (s.topF < lo) s.topF = lo;
if (s.bottomF > hi) s.bottomF = hi;
return s;
}
} // namespace reasampler::ui
+28 -1
View File
@@ -75,11 +75,38 @@ ListRowBox computeListRow(const KitBox& list, int index, int rowHeight);
// `rowCount` rows). rowCount bounds the hit so blank space past the last row is a clean miss.
int hitTestListRow(int px, int py, const KitBox& list, int rowHeight, int rowCount);
// --- Waveform column count ---------------------------------------------------
// --- Waveform columns --------------------------------------------------------
// Pixel columns drawWaveform renders inside `box` (its fixed 2px side insets), never negative.
// Pass directly as peaks::computeEnvelope's binCount — one bin per column is correct resolution;
// overbinning doesn't improve render quality and wastes memory/CPU.
int waveformColumnCount(const KitBox& box);
// One channel band's shared vertical metrics: the zero line every column mirrors about, and the
// pixel height a full-scale amplitude reaches (inset so a peak keeps the band's breathing room).
struct WaveformBand {
int top = 0;
int height = 0;
int midY = 0;
double halfSpan = 0.0;
};
WaveformBand waveformBand(int bandTop, int bandHeight);
// The vertical extents of one column: the integer edges of the filled span, and the same edges
// at sub-pixel precision for the antialiased outline stroke that joins a column's extremes to
// its neighbour's. Both take the band clamp, so the stroke cannot leave the band the fill is
// confined to. Amplitudes arrive already through the display curve.
struct WaveformColumnSpan {
int top = 0;
int bottom = 0;
double topF = 0.0;
double bottomF = 0.0;
};
// Truncation is applied to the SCALED AMPLITUDE and then mirrored about midY — never to the
// resulting y, which would round the two edges in opposite directions and leave a column with
// |max| == |min| a pixel taller above the zero line than below.
WaveformColumnSpan waveformColumnSpan(const WaveformBand& band,
double compressedMax, double compressedMin);
} // namespace reasampler::ui