namespace DeepDrftContent.Processors; /// /// Strategy for reducing a stream of PCM samples to a fixed-length, peak-normalized loudness /// envelope. Swappable so the loudness measure (RMS today, LUFS later) can change without /// touching WaveformProfileService, the stored wire format, or the frontend renderer. /// public interface ILoudnessAlgorithm { /// /// Computes a peak-normalized loudness profile from raw interleaved PCM. /// /// Interleaved, little-endian PCM sample bytes (the WAV data chunk). /// Number of interleaved channels; averaged to mono per sample. /// Samples per second (unused by RMS but part of the contract for measures that need it). /// Bit depth (8 unsigned, 16/24/32 signed) used to decode samples. /// Number of equal time slices to reduce the signal to. /// /// A double[bucketCount], each value in [0, 1], peak-normalized so the loudest bucket /// is 1. All zeros when the signal is silent (peak is 0) or no samples are present. /// double[] Compute(ReadOnlySpan pcmData, int channels, int sampleRate, int bitsPerSample, int bucketCount); /// /// Creates a stateful accumulator that reduces the same loudness profile from PCM fed in bounded /// chunks rather than from one contiguous buffer. The streaming waveform path uses this so a long /// track's PCM is never materialized whole in a managed byte[]. The accumulator's output is /// byte-identical to for the same total PCM, because is /// itself defined in terms of one — the single source of truth for the loudness reduction. /// /// /// Total length of the PCM data region in bytes. Required up front because the bucket each frame /// lands in is derived from the frame's position relative to the total frame count. /// /// Number of interleaved channels; averaged to mono per frame. /// Samples per second (used for the envelope-smoothing time base). /// Bit depth (8 unsigned, 16/24/32 signed) used to decode samples. /// Number of equal time slices to reduce the signal to. ILoudnessAccumulator CreateAccumulator( long pcmByteLength, int channels, int sampleRate, int bitsPerSample, int bucketCount); } /// /// Stateful, single-pass reducer for one loudness profile. Frames are fed via in /// arbitrary (non-frame-aligned) chunks — a partial frame straddling a chunk boundary is carried /// internally — and emits the peak-normalized double[bucketCount]. Not /// thread-safe; feed one stream sequentially. Reusable across the same stream's chunks only, not /// across streams. /// public interface ILoudnessAccumulator { /// /// Feeds the next run of PCM bytes (interleaved, little-endian). Need not be frame-aligned; bytes /// that do not complete a frame are retained until the next call. Bytes past the total frame count /// declared at construction are ignored, matching the whole-buffer path's trailing-partial-frame drop. /// void Add(ReadOnlySpan pcmChunk); /// /// Finalizes and returns the peak-normalized loudness profile (double[bucketCount], each in /// [0, 1]). All zeros for silence or a degenerate (no-frame) input. Call once, after the last /// . /// double[] Finish(); }