diff --git a/internal/processor/analyzer.go b/internal/processor/analyzer.go index a7e7b0d..93e7711 100644 --- a/internal/processor/analyzer.go +++ b/internal/processor/analyzer.go @@ -2724,6 +2724,11 @@ const ( crosstalkKurtosisThreshold = 10.0 // Above this + voice centroid = likely crosstalk crosstalkCrestFactorThreshold = 15.0 // Above this + voice centroid = likely crosstalk crosstalkPeakRMSGap = 45.0 // dB - catches severe transient contamination regardless of spectral content + // silenceCrestFactorMax is the maximum acceptable crest factor for silence candidates. + // Crest factor > 25 dB indicates physical transients (bumps, interference) contaminating + // the silence region, making noise floor measurements unreliable. + // Normal room tone: 5-20 dB; contaminated: 25-45 dB. + silenceCrestFactorMax = 25.0 // dB - hard rejection above this // Crest factor penalty thresholds for silence candidates. // Context: These apply to SILENCE CANDIDATES (RMS < -70 dBFS). @@ -2902,6 +2907,19 @@ func scoreSilenceCandidate(m *SilenceCandidateMetrics) float64 { return 0.0 // Reject this candidate } + // Hard rejection: extreme crest factor indicates physical transients (bumps, + // interference) contaminating the silence region. Unlike the crosstalk check + // (45 dB), this catches moderate contamination (25-45 dB range). + if m.CrestFactor > silenceCrestFactorMax { + debugLog("scoreSilenceCandidate: REJECTING candidate at %.3fs - crest factor %.1f dB exceeds %.1f dB threshold", + m.Region.Start.Seconds(), m.CrestFactor, silenceCrestFactorMax) + m.TransientWarning = fmt.Sprintf( + "rejected: crest factor %.1f dB exceeds %.1f dB threshold (transient contamination)", + m.CrestFactor, silenceCrestFactorMax, + ) + return 0.0 + } + // Calculate individual component scores (all normalised to 0-1 range) ampScore := calculateAmplitudeScore(m.RMSLevel) specScore := calculateSpectralScore(m.SpectralCentroid, m.SpectralFlatness, m.SpectralKurtosis) diff --git a/testdata/justfile b/testdata/justfile index dd3e05a..b6facb2 100644 --- a/testdata/justfile +++ b/testdata/justfile @@ -71,7 +71,7 @@ analysis: mark-analysis martin-analysis popey-analysis # Process all presenters for episodes 70, 71, and 72 all-episodes: #!/usr/bin/env bash - for ep in 68 69 70 71 72 73 74; do + for ep in 68 69 70 71 72 73 74 75; do echo "=== Processing Episode $ep ===" for presenter in mark martin popey; do echo "--- $presenter ---"