| @@ -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 | // ProcessChunk measures the audio power of a chunk and returns the | ||||
| // gain factor to apply to the audio composite for BS.412 compliance. | // gain factor to apply to the audio composite for BS.412 compliance. | ||||
| // Call once per chunk with the average audio power of that chunk. | // Call once per chunk with the average audio power of that chunk. | ||||