Add server-side waveform loudness profiling on track upload

ILoudnessAlgorithm strategy (RmsLoudnessAlgorithm first impl), WaveformProfileService
stores quantized byte[] sidecar in new MediaFileVault (profiles vault), wired into
UnifiedTrackService.UploadAsync; failure is logged and swallowed. WaveformProfileDto
and WaveformProfileOptions in shared projects.
This commit is contained in:
daniel-c-harvey
2026-06-05 16:38:02 -04:00
parent eed99df0dd
commit fa57861dbf
16 changed files with 544 additions and 10 deletions
@@ -0,0 +1,23 @@
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);
}