From 54022ed6cc4761239f8c0749796531c667dcdc27 Mon Sep 17 00:00:00 2001 From: clement Date: Wed, 17 Jun 2026 23:47:08 +0200 Subject: [PATCH] fix(plip): VAD calib for quiet voice + ring stop Recalibrate the capture VAD to the quiet handset voice: onset 1.4%->0.7% FS (was never triggering -> 'no sustained voice'), silence 0.6%->0.34% FS and end-of-speech window 600->900 ms so a whole sentence is held instead of cut mid-phrase. Also force slic_ring_stop() on incoming pickup so the physical bell always stops when answered (phone.c/slic.c s_ringing could desync, leaving the bell ringing through the call). --- plip_voice/main/audio.c | 21 ++++++++------------- plip_voice/main/conversation.c | 8 +++++++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/plip_voice/main/audio.c b/plip_voice/main/audio.c index 2e97d4c..62cbb9b 100644 --- a/plip_voice/main/audio.c +++ b/plip_voice/main/audio.c @@ -599,19 +599,14 @@ int audio_capture_wav(uint8_t *out, size_t out_max, int max_ms, int silence_ms) const int max_frames = (max_ms / 20); const int silence_frames = (silence_ms / 20); - /* VAD thresholds (raw S16). Tuned for the SLIC handset mic at +24 dB PGA: - * speech peaks ~5 % FS / RMS ~1-2 %, noise floor ~0.6 %. Onset must sit - * between (catch quiet speech), silence ABOVE the noise floor (so the turn - * ends when the caller stops instead of running to max_ms). */ - /* Measured on the SLIC handset (DC-blocked): speech body sits ~1 % FS RMS - * with peaks ~18 %, noise floor ~0.3 %. The previous silence threshold - * (1.2 %) was ABOVE the speech body, so the recorder classified the - * caller's own voice as silence and cut the turn after ~0.6 s → empty STT. - * Onset must clear noise (3-frame sustained confirm also guards it); silence - * must sit between the noise floor and the speech body so the turn ends only - * on a real pause. */ - const int32_t vad_onset_rms_sq = 450 * 450; /* ~1.4% FS² — speech onset above noise */ - const int32_t vad_silence_rms_sq = 200 * 200; /* ~0.6% FS² — below speech body, above noise floor */ + /* Calibrated to the QUIET handset voice (DC-blocked: body 0.16-0.8 % FS RMS, + * loud syllables ~2-4 %, noise floor ~0.3 %). The old onset (1.4 %) NEVER + * triggered → "no sustained voice"; the old silence (0.6 %) sat ABOVE the + * body → cut mid-sentence. Now: onset catches a real syllable just above the + * floor (3-frame confirm guards clicks); silence sits just above the floor so + * the turn ends only on a true pause, holding the whole sentence. */ + const int32_t vad_onset_rms_sq = 230 * 230; /* ~0.70% FS² — quiet-voice onset */ + const int32_t vad_silence_rms_sq = 110 * 110; /* ~0.34% FS² — just above noise floor */ int silent_frames = 0; int total_frames = 0; diff --git a/plip_voice/main/conversation.c b/plip_voice/main/conversation.c index b6ba1b8..144f18c 100644 --- a/plip_voice/main/conversation.c +++ b/plip_voice/main/conversation.c @@ -47,7 +47,9 @@ #define CAPTURE_MAX_IRAM (128 * 1024) #define CAPTURE_MAX_MS_PSRAM 8000 #define CAPTURE_MAX_MS_IRAM 4000 -#define CAPTURE_SILENCE_MS 600 /* end-of-speech VAD: shorter = snappier reply (was 800) */ +#define CAPTURE_SILENCE_MS 900 /* end-of-speech: needs to ride through brief + * mid-sentence pauses so a whole phrase is + * captured (quiet handset voice). 900 ms. */ #define REPLY_POLL_MS 200 /* interval for checking hook during playback */ #define REPLY_PLAYBACK_EXTRA_MS 500 /* safety margin added to computed WAV duration */ #define BETWEEN_TURNS_MS 300 /* short pause between capture rounds */ @@ -124,6 +126,10 @@ static void enter_incoming_greet(void) { s_incoming_armed = false; phone_ring_stop(); + slic_ring_stop(); /* belt-and-suspenders: kill the physical bell directly in + * case phone.c's s_ringing flag desynced from slic.c's + * (otherwise phone_ring_stop early-returns and the bell + * keeps ringing through the answered call). */ tones_stop(); dialer_reset(); s_offhook = true; -- 2.52.0