From e84cb67500530f2baa027be0305951fe2d5d1139 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 21 Feb 2026 02:08:55 -0500 Subject: [PATCH] Noise suppression tweaks. --- noise/suppression_test.go | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 noise/suppression_test.go diff --git a/noise/suppression_test.go b/noise/suppression_test.go new file mode 100644 index 0000000..f787547 --- /dev/null +++ b/noise/suppression_test.go @@ -0,0 +1,103 @@ +package noise + +import ( + "math" + "testing" +) + +func TestSuppressorDisabledBypassesSamples(t *testing.T) { + suppressor := NewSuppressor() + samples := []int16{100, -200, 300, -400, 500} + original := append([]int16(nil), samples...) + + suppressor.ProcessSamples(samples) + + for i := range samples { + if samples[i] != original[i] { + t.Fatalf("expected sample %d to remain unchanged, got %d want %d", i, samples[i], original[i]) + } + } +} + +func TestSuppressorAttenuatesLowLevelNoise(t *testing.T) { + suppressor := NewSuppressor() + suppressor.SetEnabled(true) + suppressor.SetThreshold(0.08) + + input := makeSineFrame(600, 700) + originalRMS := frameRMS(input) + processed := append([]int16(nil), input...) + suppressor.ProcessSamples(processed) + processedRMS := frameRMS(processed) + + if processedRMS >= originalRMS*0.8 { + t.Fatalf("expected low-level noise attenuation, got RMS %.2f from %.2f", processedRMS, originalRMS) + } +} + +func TestSuppressorPreservesSpeechLikeSignal(t *testing.T) { + suppressor := NewSuppressor() + suppressor.SetEnabled(true) + suppressor.SetThreshold(0.08) + + voice := makeSineFrame(1000, 9000) + originalRMS := frameRMS(voice) + processed := append([]int16(nil), voice...) + suppressor.ProcessSamples(processed) + processedRMS := frameRMS(processed) + + if processedRMS <= originalRMS*0.6 { + t.Fatalf("expected speech-like signal to be mostly preserved, got RMS %.2f from %.2f", processedRMS, originalRMS) + } +} + +func TestHigherThresholdAppliesStrongerSuppression(t *testing.T) { + lowSuppressor := NewSuppressor() + lowSuppressor.SetEnabled(true) + lowSuppressor.SetThreshold(0.02) + + highSuppressor := NewSuppressor() + highSuppressor.SetEnabled(true) + highSuppressor.SetThreshold(0.20) + + noiseFrame := makeSineFrame(500, 700) + lowRMS := runFrameWarmup(lowSuppressor, noiseFrame, 8) + highRMS := runFrameWarmup(highSuppressor, noiseFrame, 8) + + if highRMS >= lowRMS*0.8 { + t.Fatalf("expected stronger suppression at higher threshold, got low %.2f high %.2f", lowRMS, highRMS) + } +} + +func runFrameWarmup(suppressor *Suppressor, frame []int16, repeats int) float64 { + var processed []int16 + for i := 0; i < repeats; i++ { + processed = append([]int16(nil), frame...) + suppressor.ProcessSamples(processed) + } + return frameRMS(processed) +} + +func makeSineFrame(frequency float64, amplitude float64) []int16 { + const sampleRate = 48000.0 + const frameSize = 480 + + frame := make([]int16, frameSize) + for i := 0; i < frameSize; i++ { + value := math.Sin((2.0 * math.Pi * frequency * float64(i)) / sampleRate) + frame[i] = int16(value * amplitude) + } + return frame +} + +func frameRMS(samples []int16) float64 { + if len(samples) == 0 { + return 0.0 + } + var sumSquares float64 + for _, sample := range samples { + normalized := float64(sample) / 32768.0 + sumSquares += normalized * normalized + } + return math.Sqrt(sumSquares / float64(len(samples))) +}