66 lines
3.8 KiB
C#
66 lines
3.8 KiB
C#
namespace DeepDrftContent.Processors;
|
|
|
|
/// <summary>
|
|
/// 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 <c>WaveformProfileService</c>, the stored wire format, or the frontend renderer.
|
|
/// </summary>
|
|
public interface ILoudnessAlgorithm
|
|
{
|
|
/// <summary>
|
|
/// Computes a peak-normalized loudness profile from raw interleaved PCM.
|
|
/// </summary>
|
|
/// <param name="pcmData">Interleaved, little-endian PCM sample bytes (the WAV data chunk).</param>
|
|
/// <param name="channels">Number of interleaved channels; averaged to mono per sample.</param>
|
|
/// <param name="sampleRate">Samples per second (unused by RMS but part of the contract for measures that need it).</param>
|
|
/// <param name="bitsPerSample">Bit depth (8 unsigned, 16/24/32 signed) used to decode samples.</param>
|
|
/// <param name="bucketCount">Number of equal time slices to reduce the signal to.</param>
|
|
/// <returns>
|
|
/// A <c>double[bucketCount]</c>, 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.
|
|
/// </returns>
|
|
double[] Compute(ReadOnlySpan<byte> pcmData, int channels, int sampleRate, int bitsPerSample, int bucketCount);
|
|
|
|
/// <summary>
|
|
/// 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 <c>byte[]</c>. The accumulator's output is
|
|
/// byte-identical to <see cref="Compute"/> for the same total PCM, because <see cref="Compute"/> is
|
|
/// itself defined in terms of one — the single source of truth for the loudness reduction.
|
|
/// </summary>
|
|
/// <param name="pcmByteLength">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="channels">Number of interleaved channels; averaged to mono per frame.</param>
|
|
/// <param name="sampleRate">Samples per second (used for the envelope-smoothing time base).</param>
|
|
/// <param name="bitsPerSample">Bit depth (8 unsigned, 16/24/32 signed) used to decode samples.</param>
|
|
/// <param name="bucketCount">Number of equal time slices to reduce the signal to.</param>
|
|
ILoudnessAccumulator CreateAccumulator(
|
|
long pcmByteLength, int channels, int sampleRate, int bitsPerSample, int bucketCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stateful, single-pass reducer for one loudness profile. Frames are fed via <see cref="Add"/> in
|
|
/// arbitrary (non-frame-aligned) chunks — a partial frame straddling a chunk boundary is carried
|
|
/// internally — and <see cref="Finish"/> emits the peak-normalized <c>double[bucketCount]</c>. Not
|
|
/// thread-safe; feed one stream sequentially. Reusable across the same stream's chunks only, not
|
|
/// across streams.
|
|
/// </summary>
|
|
public interface ILoudnessAccumulator
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
void Add(ReadOnlySpan<byte> pcmChunk);
|
|
|
|
/// <summary>
|
|
/// Finalizes and returns the peak-normalized loudness profile (<c>double[bucketCount]</c>, each in
|
|
/// [0, 1]). All zeros for silence or a degenerate (no-frame) input. Call once, after the last
|
|
/// <see cref="Add"/>.
|
|
/// </summary>
|
|
double[] Finish();
|
|
}
|