Skip to content

Commit eb1e238

Browse files
committed
refactor(processor): upgrade silence measurement to full candidate metrics
- Replace OutputMeasurements.SilenceSample type from SilenceAnalysis (6 fields) to SilenceCandidateMetrics (20+ fields) - Add OutputMeasurements.SpeechSample field for future speech region tracking - Update MeasureOutputSilenceRegion to return SilenceCandidateMetrics - Adjust report helpers to use new metric types Enables comprehensive silence/speech region comparison across processing passes. Phase 2 will expand filter graph to capture full spectral and loudness measurements. Signed-off-by: Martin Wimpress <martin@wimpress.org>
1 parent 536273c commit eb1e238

2 files changed

Lines changed: 32 additions & 22 deletions

File tree

internal/logging/report.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ func writeSpectralTable(f *os.File, input *processor.AudioMeasurements, filtered
13381338

13391339
// writeNoiseFloorTable outputs a three-column comparison table for noise floor metrics.
13401340
// Columns: Input (Pass 1 NoiseProfile), Filtered (Pass 2 SilenceSample), Final (Pass 4 SilenceSample)
1341-
func writeNoiseFloorTable(f *os.File, inputNoise *processor.NoiseProfile, filteredNoise *processor.SilenceAnalysis, finalNoise *processor.SilenceAnalysis) {
1341+
func writeNoiseFloorTable(f *os.File, inputNoise *processor.NoiseProfile, filteredNoise *processor.SilenceCandidateMetrics, finalNoise *processor.SilenceCandidateMetrics) {
13421342
writeSection(f, "Noise Floor Analysis")
13431343

13441344
// Skip if no input noise profile
@@ -1355,10 +1355,10 @@ func writeNoiseFloorTable(f *os.File, inputNoise *processor.NoiseProfile, filter
13551355
filteredRMS := math.NaN()
13561356
finalRMS := math.NaN()
13571357
if filteredNoise != nil {
1358-
filteredRMS = filteredNoise.NoiseFloor
1358+
filteredRMS = filteredNoise.RMSLevel
13591359
}
13601360
if finalNoise != nil {
1361-
finalRMS = finalNoise.NoiseFloor
1361+
finalRMS = finalNoise.RMSLevel
13621362
}
13631363
table.AddMetricRow("RMS Level", inputRMS, filteredRMS, finalRMS, 1, "dBFS", "")
13641364

@@ -1789,15 +1789,15 @@ func getFinalMeasurements(result *processor.ProcessingResult) *processor.OutputM
17891789
}
17901790

17911791
// getFilteredNoise safely extracts filtered noise profile from the result.
1792-
func getFilteredNoise(result *processor.ProcessingResult) *processor.SilenceAnalysis {
1792+
func getFilteredNoise(result *processor.ProcessingResult) *processor.SilenceCandidateMetrics {
17931793
if result == nil || result.FilteredMeasurements == nil {
17941794
return nil
17951795
}
17961796
return result.FilteredMeasurements.SilenceSample
17971797
}
17981798

17991799
// getFinalNoise safely extracts final noise profile from the result.
1800-
func getFinalNoise(result *processor.ProcessingResult) *processor.SilenceAnalysis {
1800+
func getFinalNoise(result *processor.ProcessingResult) *processor.SilenceCandidateMetrics {
18011801
if result == nil || result.NormResult == nil || result.NormResult.FinalMeasurements == nil {
18021802
return nil
18031803
}

internal/processor/analyzer.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,7 +1648,10 @@ type OutputMeasurements struct {
16481648
LoudnormMeasured bool `json:"loudnorm_measured"` // True if loudnorm measurement was captured
16491649

16501650
// Silence region analysis (same region as Pass 1, for noise reduction comparison)
1651-
SilenceSample *SilenceAnalysis `json:"silence_sample,omitempty"` // Measurements from same silence region
1651+
SilenceSample *SilenceCandidateMetrics `json:"silence_sample,omitempty"` // Measurements from same silence region
1652+
1653+
// Speech region analysis (same region as Pass 1, for processing comparison)
1654+
SpeechSample *SpeechCandidateMetrics `json:"speech_sample,omitempty"` // Measurements from same speech region
16521655
}
16531656

16541657
// outputMetadataAccumulators holds accumulator variables for Pass 2 output measurement extraction.
@@ -3036,15 +3039,14 @@ func scoreSpeechCandidate(m *SpeechCandidateMetrics) float64 {
30363039
}
30373040

30383041
// MeasureOutputSilenceRegion analyses the same silence region in the output file
3039-
// that was measured during Pass 1, enabling direct comparison of noise characteristics.
3040-
// Unlike extractNoiseProfile, this does NOT create a WAV file - it only measures.
3042+
// that was used for noise profiling in Pass 1. This allows comparing how
3043+
// noise characteristics changed after adaptive processing.
30413044
//
3042-
// Parameters:
3043-
// - outputPath: path to the processed audio file
3044-
// - region: the silence region identified during Pass 1 (start time and duration)
3045+
// The region parameter should use the same Start/Duration as the NoiseProfile
3046+
// from Pass 1 analysis. Returns nil if the region cannot be measured.
30453047
//
3046-
// Returns SilenceAnalysis with noise floor, peak level, crest factor, and entropy.
3047-
func MeasureOutputSilenceRegion(outputPath string, region SilenceRegion) (*SilenceAnalysis, error) {
3048+
// Returns full SilenceCandidateMetrics with all amplitude, spectral, and loudness measurements.
3049+
func MeasureOutputSilenceRegion(outputPath string, region SilenceRegion) (*SilenceCandidateMetrics, error) {
30483050
if region.Duration == 0 {
30493051
return nil, fmt.Errorf("invalid silence region: zero duration")
30503052
}
@@ -3164,19 +3166,27 @@ func MeasureOutputSilenceRegion(outputPath string, region SilenceRegion) (*Silen
31643166
crestFactorDB = peakLevel - noiseFloor
31653167
}
31663168

3167-
analysis := &SilenceAnalysis{
3168-
Start: region.Start,
3169-
Duration: region.Duration,
3169+
// Map to SilenceCandidateMetrics with currently measured fields
3170+
// Phase 2 will expand this to capture all 20+ fields via extended filter graph
3171+
metrics := &SilenceCandidateMetrics{
3172+
Region: region,
3173+
3174+
// Amplitude metrics from astats
3175+
RMSLevel: noiseFloor,
31703176
PeakLevel: peakLevel,
31713177
CrestFactor: crestFactorDB,
3172-
Entropy: entropy,
3178+
3179+
// Legacy entropy field from astats
3180+
Entropy: entropy,
3181+
SpectralEntropy: entropy, // Use same value for both fields
3182+
3183+
// Remaining spectral and loudness fields will be populated in Phase 2
3184+
// when filter graph is extended to include aspectralstats and ebur128
31733185
}
31743186

3175-
if noiseFloorFound {
3176-
analysis.NoiseFloor = noiseFloor
3177-
} else {
3178-
analysis.NoiseFloor = -60.0 // Conservative fallback
3187+
if !noiseFloorFound {
3188+
metrics.RMSLevel = -60.0 // Conservative fallback
31793189
}
31803190

3181-
return analysis, nil
3191+
return metrics, nil
31823192
}

0 commit comments

Comments
 (0)