Merge pS-fa3-waveform: gap-free per-column min/max waveform render (columnMinMax in peaks, shared across panel + editor)

This commit is contained in:
2026-07-27 19:47:49 -04:00
10 changed files with 268 additions and 68 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: one bin per drawn pixel column — the
// gap-free render comes from peaks::columnMinMax's exact partition, not from extra bins.
// 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
@@ -582,8 +584,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;
@@ -1017,8 +1023,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 gap-free: one bin per drawn pixel column (kWaveformOversample == 1, so this
// multiplies by 1). The gap-free draw comes from peaks::columnMinMax's exact
// partition — extra bins produce no visible change. Clamped to frame count below.
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);