Cut core/ui and core/audio comment bloat ~60% (comments only, zero code change)

This commit is contained in:
2026-07-29 20:49:02 -04:00
parent 1f24c4b095
commit 3d3415f943
29 changed files with 527 additions and 1225 deletions
+13 -28
View File
@@ -5,14 +5,11 @@
#include <cmath>
#include <cstdint>
// peaks implementation.
// peaks — pure implementation. See peaks.h.
//
// One linear pass per channel. The frame->bin partition is computed with integer
// arithmetic so it is exact for any frameCount / binCount pairing: bin b owns the
// half-open frame span [b*frameCount/binCount, (b+1)*frameCount/binCount). That
// span formula distributes the remainder deterministically (earlier bins get the
// extra frames) with no rounding drift and no dropped tail — the last bin's end is
// always exactly frameCount.
// One linear pass per channel. Frame->bin partition uses integer arithmetic so it's exact for
// any frameCount/binCount pairing: bin b owns [b*frameCount/binCount, (b+1)*frameCount/binCount)
// — earlier bins absorb the remainder, no rounding drift, no dropped tail.
namespace reasampler::audio {
@@ -22,11 +19,10 @@ Envelope computeEnvelope(const std::vector<AudioSample>& interleaved,
std::size_t binCount) {
Envelope envelope(channelCount);
if (channelCount == 0) {
return envelope; // no channels -> no envelopes
return envelope;
}
// Never read past what the buffer actually holds, even if the caller's
// frameCount overstates the buffer (defensive: no OOB on a short buffer).
// Never read past what the buffer actually holds, even if frameCount overstates it.
const std::size_t availableFrames = interleaved.size() / channelCount;
const std::size_t frames = std::min(frameCount, availableFrames);
@@ -35,14 +31,10 @@ Envelope computeEnvelope(const std::vector<AudioSample>& interleaved,
bins.assign(binCount, MinMax{}); // empty/degenerate bins default to {0,0}
for (std::size_t b = 0; b < binCount; ++b) {
// Half-open frame span for this bin: [b*frames/binCount, (b+1)*frames/binCount).
// Guard against size_t overflow in b*frames and (b+1)*frames: binCount is
// caller-controlled and unbounded, so when b >= SIZE_MAX/frames either
// multiplication could wrap. Any such bin is unreachable in practice
// (allocating that many MinMax entries would OOM first), but we guard
// explicitly to eliminate UB.
// Guard b*frames / (b+1)*frames overflow: binCount is caller-controlled and
// unbounded. Unreachable in practice (would OOM first) but guarded to avoid UB.
if (frames > 0 && b >= SIZE_MAX / frames) {
continue; // b*frames or (b+1)*frames would overflow; span is empty
continue;
}
const std::size_t begin = (b * frames) / binCount;
const std::size_t end = ((b + 1) * frames) / binCount;
@@ -69,21 +61,17 @@ MinMax columnMinMax(const ChannelEnvelope& bins, int columnCount, int col) {
const int nbins = static_cast<int>(bins.size());
if (columnCount <= 0 || nbins == 0) return MinMax{};
// Clamp col to [0, columnCount-1].
if (col < 0) col = 0;
if (col >= columnCount) col = columnCount - 1;
// Half-open bin range for this column, mirroring computeEnvelope's exact partition.
// 64-bit products: col*nbins can exceed int range for a large oversampled envelope
// (same overflow discipline as computeEnvelope's frame-span arithmetic above).
// Half-open bin range for this column, mirroring computeEnvelope's partition. 64-bit
// products: col*nbins can exceed int range for a large oversampled envelope.
const std::int64_t begin64 = (static_cast<std::int64_t>(col) * nbins) / columnCount;
const std::int64_t end64 =
(static_cast<std::int64_t>(col) + 1) * nbins / columnCount;
// col <= columnCount-1 guarantees begin64 <= (columnCount-1)*nbins/columnCount < nbins.
const int colBinBegin = static_cast<int>(begin64);
// When the column spans no full bin (more columns than bins), use the enclosing bin
// so no column is left empty.
// When the column spans no full bin (more columns than bins), use the enclosing bin.
const int scanEnd = (end64 > begin64) ? static_cast<int>(end64) : colBinBegin + 1;
const int clampedEnd = (scanEnd <= nbins) ? scanEnd : nbins;
@@ -102,14 +90,11 @@ std::size_t lastFrameAboveThreshold(const std::vector<AudioSample>& interleaved,
AudioSample linearThreshold) {
if (channelCount == 0) return kNoFrameAboveThreshold;
// Clamp to what the buffer actually holds — a caller frameCount that overstates
// the buffer must never read past the end (mirror of computeEnvelope's guard).
const std::size_t availableFrames = interleaved.size() / channelCount;
const std::size_t frames = std::min(frameCount, availableFrames);
if (frames == 0) return kNoFrameAboveThreshold;
// Scan backward: the first frame (from the end) whose loudest channel exceeds the
// threshold is the last audible frame. `f` runs frames..1 so `f-1` never wraps.
// Scan backward; `f` runs frames..1 so `f-1` never wraps.
for (std::size_t f = frames; f > 0; --f) {
const std::size_t frame = f - 1;
const std::size_t base = frame * channelCount;
+42 -76
View File
@@ -1,32 +1,20 @@
#pragma once
// peaks — waveform min/max envelope (thumbnail) computation from raw interleaved
// PCM. We compute our own thumbnails from the captured file rather than depending
// on REAPER's peak API: we own the file format, so this is simpler, testable, and
// dependency-free. A future bank panel (M5) calls this at whatever bin resolution
// the panel width dictates and draws one min/max envelope per channel.
//
// PURE MODULE (CLAUDE.md §load-bearing split): NO REAPER types, NO SWELL, NO
// vendor/ includes. Standard library only. Builds and unit-tests without REAPER.
// peaks — waveform min/max envelope (thumbnail) computation from raw interleaved PCM. We compute
// our own thumbnails rather than depending on REAPER's peak API: we own the file format, so this
// is simpler, testable, and dependency-free.
#include <cstddef>
#include <vector>
namespace reasampler::audio {
// 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.
// REAPER's native audio buffer format (interleaved 32-bit float), consumed directly with no
// lossy conversion. Named AudioSample rather than Sample to avoid colliding with bank_model's
// metadata struct of the same short name.
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.
// One bin's extremes across the samples that fell in it. min <= max always; an empty bin
// (more bins than frames) is {0, 0}.
struct MinMax {
AudioSample min = 0.0f;
AudioSample max = 0.0f;
@@ -37,83 +25,61 @@ struct MinMax {
// One channel's envelope: exactly `binCount` bins, in time order.
using ChannelEnvelope = std::vector<MinMax>;
// Per-channel envelopes: outer index is channel (channelCount entries, order
// preserved — never mixed or folded), inner is that channel's bins.
// Per-channel envelopes: outer index is channel (channelCount entries, order preserved — never
// mixed or folded), inner is that channel's bins.
using Envelope = std::vector<ChannelEnvelope>;
// Computes a per-channel min/max envelope from interleaved PCM.
//
// interleaved frame-interleaved samples: [f0c0, f0c1, ..., f1c0, f1c1, ...].
// Size must be >= frameCount * channelCount; extra is ignored.
// channelCount channels per frame (the stride). Each channel is enveloped
// INDEPENDENTLY — no averaging, no stereo fold (precision
// invariant: channel count preserved).
// interleaved frame-interleaved samples: [f0c0, f0c1, ..., f1c0, f1c1, ...]. Size must be
// >= frameCount * channelCount; extra is ignored.
// channelCount channels per frame (the stride). Each channel is enveloped INDEPENDENTLY — no
// averaging, no stereo fold (channel count is preserved end to end).
// frameCount frames (samples-per-channel) to consider.
// binCount requested bins per channel. Honored exactly for any frameCount.
//
// Frame->bin partition: frames are split into `binCount` contiguous spans as
// evenly as possible; when frameCount does not divide evenly, the remainder is
// spread one-frame-per-bin across the earliest bins (ceil/floor split), so the
// tail is never dropped and no bin reads out of bounds. When binCount > frameCount
// the trailing empty bins are {0, 0}.
// Frame->bin partition: frames split into `binCount` contiguous spans as evenly as possible;
// when frameCount doesn't divide evenly, the remainder spreads one-frame-per-bin across the
// earliest bins, so the tail is never dropped and no bin reads out of bounds.
//
// Defined behavior for degenerate input (no UB, no throw):
// binCount == 0 -> per channel: an empty bin vector.
// channelCount == 0 -> an empty envelope (no channels).
// frameCount == 0 -> per channel: binCount bins, all {0, 0}.
// Degenerate input (no UB, no throw): binCount == 0 -> empty bin vector per channel;
// channelCount == 0 -> empty envelope; frameCount == 0 -> binCount bins, all {0, 0}.
Envelope computeEnvelope(const std::vector<AudioSample>& interleaved,
std::size_t channelCount,
std::size_t frameCount,
std::size_t binCount);
// The merged min/max for display column `col` (0-based, of `columnCount` total columns)
// of a pre-computed per-bin ChannelEnvelope: the true extremes of every bin that projects
// to that column. This is the display-side collapse of an envelope computed at HIGHER
// resolution than the drawn width (oversampled bins -> per-pixel-column min/max), so a
// steep transient whose adjacent bins hold disjoint spans (e.g. {0.9,1.0} then
// {-1.0,-0.9}) renders as one gap-free vertical span instead of two separated dots.
// Merged min/max for display column `col` (0-based, of `columnCount` total) of a pre-computed
// ChannelEnvelope the true extremes of every bin projecting to that column. This is the
// display-side collapse when the envelope was computed at a higher resolution than the drawn
// width, so a steep transient split across adjacent bins (e.g. {0.9,1.0} then {-1.0,-0.9})
// renders as one gap-free span instead of two separated dots.
//
// Bin->column mapping mirrors computeEnvelope's half-open partition:
// column col owns bins [col*nbins/columnCount, (col+1)*nbins/columnCount).
// When that range is empty (more columns than bins), the enclosing bin
// (col*nbins/columnCount) fills the column — so no column is left empty and no bin is
// ever dropped. columnCount <= 0 or bins.empty() returns {0, 0}; `col` is clamped to
// [0, columnCount-1]. Pure.
// Bin->column mapping mirrors computeEnvelope's half-open partition: column col owns bins
// [col*nbins/columnCount, (col+1)*nbins/columnCount). When that range is empty (more columns
// than bins), the enclosing bin fills the column instead. columnCount <= 0 or bins.empty()
// returns {0, 0}; col is clamped to [0, columnCount-1].
MinMax columnMinMax(const ChannelEnvelope& bins, int columnCount, int col);
// Sentinel returned by lastFrameAboveThreshold when NO frame in the scanned range
// peaks above the threshold (pure silence at that level). SIZE_MAX is unambiguous:
// no valid frame index can equal it (a real index is < frameCount <= SIZE_MAX for
// any allocatable buffer), so the caller tests `== kNoFrameAboveThreshold` cleanly.
// Sentinel for "no frame in the scanned range peaked above threshold". SIZE_MAX is unambiguous
// since no real frame index can reach it.
inline constexpr std::size_t kNoFrameAboveThreshold =
static_cast<std::size_t>(-1);
// Scans interleaved PCM BACKWARD for the last frame whose per-frame peak (the max
// absolute value across all channels of that frame — NO stereo fold, just the
// loudest channel that frame) exceeds `linearThreshold`, returning that frame index.
// Returns kNoFrameAboveThreshold if no frame exceeds it (or on degenerate input).
// Scans interleaved PCM BACKWARD for the last frame whose per-frame peak (max |sample| across
// all channels of that frame — no stereo fold) exceeds `linearThreshold`. Returns
// kNoFrameAboveThreshold if no frame exceeds it (or on degenerate input).
//
// This is the boundary primitive behind the realtime tail's decay-scan trim
// (docs/product/capture-tail.md §The realtime path): the recorded tail window is
// scanned back from the end for the last frame still above -72 dB, and the file is
// truncated one frame past it. Deliberately a separate primitive from
// computeEnvelope — that answers "the min/max envelope over bins" (a thumbnail),
// this answers "the last frame above a level" (a boundary). Bending the bin-oriented
// envelope to a frame-exact boundary question is a worse fit (spec §option a).
// This is the boundary primitive behind the realtime tail's decay-scan trim (see
// docs/product/capture-tail.md): the recorded tail is scanned back from the end for the last
// frame still above -72 dB, and the file truncated one frame past it. Deliberately separate from
// computeEnvelope — that answers "the min/max envelope over bins" (a thumbnail), this answers
// "the last frame above a level" (a boundary); bending a bin-oriented envelope to a frame-exact
// question is a worse fit.
//
// interleaved frame-interleaved samples: [f0c0, f0c1, ..., f1c0, f1c1, ...].
// Must hold >= frameCount * channelCount; extra is ignored, and a
// short buffer is clamped to what it actually holds (no OOB read).
// channelCount channels per frame (the stride). The per-frame test is the max
// |sample| over these channels — the frame is "above" if its
// loudest channel is above the threshold.
// frameCount frames to consider (the scan starts at the last of these).
// linearThreshold the comparison level as a LINEAR amplitude ratio (e.g. the
// -72 dB ratio from render_settings::autoTrimEndRatio), NOT dB.
// A frame counts as above when its peak is STRICTLY > this.
//
// Pure, stdlib-only, unit-tested (a synthetic decaying ramp, silence, all-above,
// and degenerate inputs) so the trim boundary math is locked outside the DAW.
// linearThreshold a LINEAR amplitude ratio (e.g. the -72 dB ratio from
// render_settings::autoTrimEndRatio), NOT dB. A frame counts as above when
// its peak is STRICTLY greater than this.
std::size_t lastFrameAboveThreshold(const std::vector<AudioSample>& interleaved,
std::size_t channelCount,
std::size_t frameCount,