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:
+13
-13
@@ -27,8 +27,8 @@ constexpr double kPi = 3.14159265358979323846;
|
||||
|
||||
// A full-scale sine over `frames` frames, mono, `cycles` complete periods so every
|
||||
// bin sees both a near-peak and a near-trough.
|
||||
static std::vector<Sample> monoSine(std::size_t frames, double cycles, float amp) {
|
||||
std::vector<Sample> buf(frames);
|
||||
static std::vector<AudioSample> monoSine(std::size_t frames, double cycles, float amp) {
|
||||
std::vector<AudioSample> buf(frames);
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
const double phase = 2.0 * kPi * cycles * (double)i / (double)frames;
|
||||
buf[i] = amp * (float)std::sin(phase);
|
||||
@@ -60,7 +60,7 @@ static void testSineEnvelope() {
|
||||
static void testRampMonotonic() {
|
||||
const std::size_t frames = 10000;
|
||||
const std::size_t bins = 50;
|
||||
std::vector<Sample> buf(frames);
|
||||
std::vector<AudioSample> buf(frames);
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
buf[i] = (float)i / (float)(frames - 1); // 0.0 .. 1.0
|
||||
}
|
||||
@@ -97,14 +97,14 @@ static void testDcAndSilence() {
|
||||
const std::size_t frames = 1000;
|
||||
const std::size_t bins = 16;
|
||||
|
||||
std::vector<Sample> silence(frames, 0.0f);
|
||||
std::vector<AudioSample> silence(frames, 0.0f);
|
||||
Envelope se = computeEnvelope(silence, 1, frames, bins);
|
||||
for (const MinMax& mm : se[0]) {
|
||||
CHECK(mm.min == 0.0f);
|
||||
CHECK(mm.max == 0.0f);
|
||||
}
|
||||
|
||||
std::vector<Sample> dc(frames, 0.5f);
|
||||
std::vector<AudioSample> dc(frames, 0.5f);
|
||||
Envelope de = computeEnvelope(dc, 1, frames, bins);
|
||||
for (const MinMax& mm : de[0]) {
|
||||
CHECK(mm.min == 0.5f);
|
||||
@@ -121,7 +121,7 @@ static void testMultiChannelNoFold() {
|
||||
|
||||
// Interleave: [ch0, ch1] per frame; ch0 = sine, ch1 = 0.
|
||||
auto sine = monoSine(frames, /*cycles=*/64.0, amp);
|
||||
std::vector<Sample> buf(frames * 2);
|
||||
std::vector<AudioSample> buf(frames * 2);
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
buf[i * 2 + 0] = sine[i];
|
||||
buf[i * 2 + 1] = 0.0f;
|
||||
@@ -147,7 +147,7 @@ static void testMultiChannelNoFold() {
|
||||
static void testChannelsNotAveraged() {
|
||||
const std::size_t frames = 100;
|
||||
const std::size_t bins = 4;
|
||||
std::vector<Sample> buf(frames * 2);
|
||||
std::vector<AudioSample> buf(frames * 2);
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
buf[i * 2 + 0] = 1.0f;
|
||||
buf[i * 2 + 1] = -1.0f;
|
||||
@@ -164,7 +164,7 @@ static void testChannelsNotAveraged() {
|
||||
static void testShortBuffer() {
|
||||
const std::size_t frames = 3;
|
||||
const std::size_t bins = 8;
|
||||
std::vector<Sample> buf = {0.25f, -0.5f, 0.75f};
|
||||
std::vector<AudioSample> buf = {0.25f, -0.5f, 0.75f};
|
||||
|
||||
Envelope env = computeEnvelope(buf, 1, frames, bins);
|
||||
CHECK(env[0].size() == bins);
|
||||
@@ -190,7 +190,7 @@ static void testShortBuffer() {
|
||||
static void testNonDivisibleRemainderBin() {
|
||||
const std::size_t frames = 10;
|
||||
const std::size_t bins = 3;
|
||||
std::vector<Sample> buf(frames);
|
||||
std::vector<AudioSample> buf(frames);
|
||||
for (std::size_t i = 0; i < frames; ++i) buf[i] = (float)i; // 0..9
|
||||
|
||||
Envelope env = computeEnvelope(buf, 1, frames, bins);
|
||||
@@ -205,7 +205,7 @@ static void testNonDivisibleRemainderBin() {
|
||||
|
||||
// binCount == 1: the whole buffer collapses to a single min/max.
|
||||
static void testSingleBinWholeBuffer() {
|
||||
std::vector<Sample> buf = {-0.3f, 0.8f, -0.9f, 0.1f, 0.4f};
|
||||
std::vector<AudioSample> buf = {-0.3f, 0.8f, -0.9f, 0.1f, 0.4f};
|
||||
Envelope env = computeEnvelope(buf, 1, buf.size(), 1);
|
||||
CHECK(env[0].size() == 1);
|
||||
CHECK(env[0][0].min == -0.9f);
|
||||
@@ -214,7 +214,7 @@ static void testSingleBinWholeBuffer() {
|
||||
|
||||
// Degenerate inputs: defined behavior, no UB, no throw.
|
||||
static void testDegenerateInputs() {
|
||||
std::vector<Sample> buf = {0.1f, 0.2f, 0.3f, 0.4f};
|
||||
std::vector<AudioSample> buf = {0.1f, 0.2f, 0.3f, 0.4f};
|
||||
|
||||
// Zero frames -> binCount bins, all {0,0}.
|
||||
Envelope zf = computeEnvelope(buf, 1, 0, 4);
|
||||
@@ -238,7 +238,7 @@ static void testDegenerateInputs() {
|
||||
CHECK(over[0][1].min == 0.3f && over[0][1].max == 0.4f);
|
||||
|
||||
// Empty buffer, non-zero request -> all-zero bins, no crash.
|
||||
std::vector<Sample> empty;
|
||||
std::vector<AudioSample> empty;
|
||||
Envelope eb = computeEnvelope(empty, 2, 10, 3);
|
||||
CHECK(eb.size() == 2);
|
||||
for (const auto& chenv : eb) {
|
||||
@@ -257,7 +257,7 @@ static void testLargeBinCountOverflowGuard() {
|
||||
// same loop iteration path that would UB for pathological binCount near SIZE_MAX.
|
||||
const std::size_t frames = 4;
|
||||
const std::size_t binCount = 9;
|
||||
std::vector<Sample> buf = {0.1f, 0.2f, 0.3f, 0.4f};
|
||||
std::vector<AudioSample> buf = {0.1f, 0.2f, 0.3f, 0.4f};
|
||||
|
||||
Envelope env = computeEnvelope(buf, 1, frames, binCount);
|
||||
CHECK(env.size() == 1);
|
||||
|
||||
Reference in New Issue
Block a user