From 8a8e9da6f2965d2f13d141eedd9e710bef1f7c34 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 7 Apr 2026 22:15:50 +0200 Subject: [PATCH] dsp: support dynamic bs412 chunk duration updates --- internal/dsp/bs412.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/internal/dsp/bs412.go b/internal/dsp/bs412.go index 527c709..98bb2ea 100644 --- a/internal/dsp/bs412.go +++ b/internal/dsp/bs412.go @@ -72,6 +72,35 @@ func NewBS412Limiter(thresholdDBr, pilotLevel, rdsInjection, chunkDurationSec fl } } +// UpdateChunkDuration reconfigures the limiter for a new chunk size. +// Call this from GenerateFrame when the actual chunk duration is known +// (computed as samples/sampleRate) to avoid calibration errors if the +// engine's chunk duration differs from the value passed to NewBS412Limiter. +// Safe to call on every chunk; no-ops when duration has not changed. +func (l *BS412Limiter) UpdateChunkDuration(chunkSec float64) { + if chunkSec <= 0 { + return + } + windowSec := 60.0 + newBufLen := int(math.Ceil(windowSec / chunkSec)) + if newBufLen < 10 { + newBufLen = 10 + } + if newBufLen == len(l.powerBuf) { + return // no change + } + // Resize buffer — drop history to avoid stale power readings from the + // old window size distorting the rolling average. + l.powerBuf = make([]float64, newBufLen) + l.bufIdx = 0 + l.bufFull = false + l.powerSum = 0 + attackTC := 2.0 / chunkSec + releaseTC := 5.0 / chunkSec + l.attackCoeff = 1.0 - math.Exp(-1.0/attackTC) + l.releaseCoeff = 1.0 - math.Exp(-1.0/releaseTC) +} + // ProcessChunk measures the audio power of a chunk and returns the // gain factor to apply to the audio composite for BS.412 compliance. // Call once per chunk with the average audio power of that chunk.