fix(plip): VAD calib for quiet voice + ring stop #29

Merged
electron merged 1 commits from fix/plip-vad-and-ring into main 2026-06-17 21:54:17 +00:00
2 changed files with 15 additions and 14 deletions
+8 -13
View File
@@ -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;
+7 -1
View File
@@ -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;