feat(bank_panel): docked bank grid with LICE waveform thumbnails (M5 Wave A)

Docked SWELL window toggled by a new action, drawing the current bank as
per-sample min/max thumbnails (PCM_source + peaks) with an in-memory cache.
Pure grid-layout/cache-key math in bank_grid (tested). Renames peaks::Sample
-> AudioSample to avoid colliding with bank_model::Sample.
This commit is contained in:
2026-07-22 21:02:22 -04:00
parent ec211a5620
commit 71a3b26a47
12 changed files with 936 additions and 33 deletions
+14 -9
View File
@@ -13,18 +13,23 @@
namespace reasampler {
// Canonical in-memory sample type. `float` is REAPER's native audio buffer format
// (its render/PCM_source callbacks hand back interleaved 32-bit float), so peaks
// consumes that directly with no lossy conversion. If a capture ever lands as a
// different depth, the caller converts to float at the boundary — the thumbnail
// core stays single-typed.
using Sample = float;
// Canonical in-memory audio-sample type. `float` is REAPER's native audio buffer
// format (its render/PCM_source callbacks hand back interleaved 32-bit float), so
// peaks consumes that directly with no lossy conversion. If a capture ever lands
// as a different depth, the caller converts to float at the boundary — the
// thumbnail core stays single-typed.
//
// NAMED AudioSample, not `Sample`: `reasampler::Sample` is already bank_model's
// metadata struct. A `using Sample = float` here would collide at namespace scope
// wherever both headers are visible (the bank_panel module includes both). The
// audio-domain name also reads more precisely — this is one PCM sample value.
using AudioSample = float;
// One bin of a channel's envelope: the extremes of every sample that fell in it.
// min <= max always. For an empty bin (more bins than frames), both are 0.
struct MinMax {
Sample min = 0.0f;
Sample max = 0.0f;
AudioSample min = 0.0f;
AudioSample max = 0.0f;
bool operator==(const MinMax& o) const { return min == o.min && max == o.max; }
};
@@ -56,7 +61,7 @@ using Envelope = std::vector<ChannelEnvelope>;
// binCount == 0 -> per channel: an empty bin vector.
// channelCount == 0 -> an empty envelope (no channels).
// frameCount == 0 -> per channel: binCount bins, all {0, 0}.
Envelope computeEnvelope(const std::vector<Sample>& interleaved,
Envelope computeEnvelope(const std::vector<AudioSample>& interleaved,
std::size_t channelCount,
std::size_t frameCount,
std::size_t binCount);