feat(waveform): generalize high-res compute to every track (Direction B)

Per-track high-res datum keyed by EntryKey in the renamed track-waveforms vault; computed at upload for all tracks, regenerable per-track via CMS, with a re-runnable backfill. Mix read path repointed so it keeps working.
This commit is contained in:
daniel-c-harvey
2026-06-17 10:18:44 -04:00
parent ad94354632
commit accf20ba57
16 changed files with 612 additions and 155 deletions
@@ -102,9 +102,11 @@ public class UnifiedReleaseService
/// <summary>
/// Fetch the Mix's track audio from the vault, compute a high-res waveform datum at a constant time
/// resolution (≈333 samples/sec derived from the track's duration; see
/// <see cref="MixWaveformResolution"/>), store it in the MixWaveforms vault under the track's
/// <see cref="WaveformResolution"/>), store it in the TrackWaveforms vault under the track's
/// EntryKey, then point the release's Mix satellite at that same key. The datum key equals the
/// track's EntryKey — the Mix is single-track.
/// track's EntryKey — the Mix is single-track. Under the per-track model (phase-12 §5) this is the
/// same datum every track now carries; the Mix satellite link is kept so the existing public
/// <c>GET api/release/{entryKey}/mix/waveform</c> read path keeps resolving until 12.B2 rewires it.
/// </summary>
public async Task<Result> TriggerMixWaveformAsync(long releaseId, CancellationToken ct)
{
@@ -148,10 +150,10 @@ public class UnifiedReleaseService
}
// Duration-derived, constant-time-resolution capture (≈333 samples/sec) so long mixes are not
// under-sampled by a fixed bucket count — see MixWaveformResolution / spec §F.
var bucketCount = MixWaveformResolution.BucketCountForDuration(audio.Duration);
var computed = await _waveformProfileService.ComputeAndStoreAsync(
audio.Buffer, entryKey, bucketCount, VaultConstants.MixWaveforms);
// under-sampled by a fixed bucket count — see WaveformResolution / spec §F. Same per-track
// high-res datum every track now carries (phase-12 §5).
var computed = await _waveformProfileService.ComputeAndStoreHighResAsync(
audio.Buffer, entryKey, audio.Duration);
if (!computed)
{
_logger.LogError("TriggerMixWaveform: waveform computation/storage failed for {EntryKey}", entryKey);