fix(waveform): 4x-oversampled min/max envelope, columnMinMax homed in peaks, panel routed through shared drawWaveform — one gap-free algorithm on all surfaces

This commit is contained in:
2026-07-27 19:09:50 -04:00
parent 20308c842e
commit b3c9fad9ba
11 changed files with 240 additions and 203 deletions
+20 -7
View File
@@ -121,10 +121,12 @@ std::string sampleLabel(const std::vector<SampleChoice>& samples, const std::str
return "?";
}
// The bin count a card's thumbnail is computed at: the card thumbnail width, so one bin
// per horizontal pixel.
// The bin count a card's thumbnail is computed at: kWaveformOversample bins per drawn
// thumbnail pixel column (FA3 anti-alias) — drawWaveform collapses them per column via
// peaks::columnMinMax. thumbnailFor clamps the request to the decoded frame count.
int thumbBins(const BrowserLayout& layout) {
return (std::max)(1, cardThumbnailRect(layout, 0).width());
return (std::max)(1, kWaveformOversample *
waveformColumnCount(toKitBox(cardThumbnailRect(layout, 0))));
}
#endif
} // namespace
@@ -570,8 +572,12 @@ const Envelope& ReaSamplerEditor::thumbnailFor(const std::string& sampleId, int
const std::vector<AudioSample>& mono = monoPcmFor(sampleId);
Envelope env;
if (!mono.empty()) {
env = computeEnvelope(mono, 1, mono.size(),
static_cast<std::size_t>((std::max)(1, binCount)));
// Clamp bins to the frame count: computeEnvelope pads binCount > frameCount with
// trailing empty {0,0} bins, which would render a very short sample as a comb of
// spikes over flat gaps.
const std::size_t bins =
(std::min)(static_cast<std::size_t>((std::max)(1, binCount)), mono.size());
env = computeEnvelope(mono, 1, mono.size(), bins);
}
auto ins = thumbCache_.emplace(key, std::move(env));
return ins.first->second;
@@ -1005,8 +1011,15 @@ void ReaSamplerEditor::paintSample(LICE_IBitmap* bmp, int w, int h) {
const Rect waveArea = bands.hero;
fillSurface(bmp, toKitBox(waveArea), Role::BgBase, InteractionState::Rest);
if (frames > 0 && waveArea.width() > 0) {
const int bins = (std::max)(1, waveArea.width());
const Envelope env = computeEnvelope(pcm, 1, pcm.size(), static_cast<std::size_t>(bins));
// FA3 anti-alias: request kWaveformOversample bins per drawn pixel column (clamped
// to the frame count) — drawWaveform collapses them per column via
// peaks::columnMinMax into a gap-free true min/max envelope.
const std::int64_t wantBins =
static_cast<std::int64_t>((std::max)(1, waveformColumnCount(toKitBox(waveArea)))) *
kWaveformOversample;
const std::size_t bins =
static_cast<std::size_t>(wantBins < frames ? wantBins : frames);
const Envelope env = computeEnvelope(pcm, 1, pcm.size(), bins);
drawEnvelope(bmp, waveArea, env);
const SetupMarkers m = pickedMarkers(frames);