mavsttd: silence gate — don't feed non-speech to whisper
Whisper hallucinates subtitle-credit boilerplate ("Редактор субтитров …") on
silence/room-noise, which then got stored as tap:voice facts. Gate before the
model: drop clips shorter than -min-ms (default 300) or below -silence-rms
(default 0.01 normalized RMS). Both are flags — the mic floor is hardware
specific. Returns empty transcript (same as whisper's no-segments path), so
nothing downstream changes.
gateReason is pure and unit-tested (silence/short/quiet → dropped, loud+long →
passes). ponytail: energy gate, not a real VAD; upgrade to WebRTC VAD or
whisper no_speech_prob if too blunt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// sine builds `ms` milliseconds of a 16 kHz tone at the given amplitude, so we
|
||||
// can exercise the energy gate without real audio or whisper.
|
||||
func sine(ms int, amp float64) []float32 {
|
||||
n := whisperSampleRate * ms / 1000
|
||||
out := make([]float32, n)
|
||||
for i := range out {
|
||||
out[i] = float32(amp * math.Sin(2*math.Pi*440*float64(i)/whisperSampleRate))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestGateReason(t *testing.T) {
|
||||
const (
|
||||
minMs = 300
|
||||
minRMS = 0.01
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
samples []float32
|
||||
gated bool // true ⇒ expect a non-empty reason (dropped)
|
||||
}{
|
||||
{"empty", nil, true},
|
||||
{"too short", sine(100, 0.5), true}, // 100ms < 300ms
|
||||
{"long but silent", make([]float32, whisperSampleRate), true}, // 1s of zeros
|
||||
{"long but near-silent", sine(500, 0.005), true}, // rms ~0.0035 < floor
|
||||
{"real speech-ish", sine(500, 0.3), false}, // loud enough, long enough
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
reason := gateReason(tc.samples, whisperSampleRate, minMs, minRMS)
|
||||
if (reason != "") != tc.gated {
|
||||
t.Fatalf("gateReason=%q, want gated=%v", reason, tc.gated)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user