fix(audio): ignore playback tail before VAD

This commit is contained in:
chenjintao
2026-09-23 17:49:13 +08:00
parent 1f55943c29
commit 82a419f812
@@ -29,6 +29,9 @@
// 在文件顶部添加状态变量 // 在文件顶部添加状态变量
static uint8_t speech_detect_count = 0; // 连续语音帧计数器 static uint8_t speech_detect_count = 0; // 连续语音帧计数器
#define SPEECH_DETECT_THRESHOLD 20 // 连续检测到语音的阈值 #define SPEECH_DETECT_THRESHOLD 20 // 连续检测到语音的阈值
#define PLAYBACK_VAD_GUARD_MS 800 // 屏蔽播放结束后的尾音和音频通路切换噪声
#define VAD_FRAME_BYTES 640 // 20ms @ 16kHz, 16-bit mono
#define VAD_PREROLL_FRAMES 25 // 保留500ms预录音,避免吞掉用户首字
uint8_t no_speech_timeout = 0; uint8_t no_speech_timeout = 0;
extern int g_mic_working; extern int g_mic_working;
@@ -57,6 +60,9 @@ static int64_t no_sound_start_time = 0;
static int64_t cmd_wait_start_time = 0; static int64_t cmd_wait_start_time = 0;
static bool first_sound_detected = false; static bool first_sound_detected = false;
static int64_t recording_start_time = 0; // 记录录音开始时间(首次检测到声音时) static int64_t recording_start_time = 0; // 记录录音开始时间(首次检测到声音时)
static uint8_t vad_preroll[VAD_PREROLL_FRAMES][VAD_FRAME_BYTES];
static size_t vad_preroll_next = 0;
static size_t vad_preroll_count = 0;
static int vad_tts_playback_finished_status = 0; static int vad_tts_playback_finished_status = 0;
// 统计 // 统计
@@ -101,6 +107,9 @@ void set_state(system_state_t new_state) {
no_sound_start_time = now; no_sound_start_time = now;
first_sound_detected = false; first_sound_detected = false;
speech_frame_count = 0; speech_frame_count = 0;
speech_detect_count = 0;
vad_preroll_next = 0;
vad_preroll_count = 0;
g_mic_working = 0; g_mic_working = 0;
break; break;
case STATE_RECORDING_ACTIVE: case STATE_RECORDING_ACTIVE:
@@ -299,8 +308,8 @@ void vad_audio_process(void) {
int64_t now = hal_os_up_time_ms(); int64_t now = hal_os_up_time_ms();
static size_t buffer_offset = 0; static size_t buffer_offset = 0;
uint8_t mic_buf[640]={0}; uint8_t mic_buf[VAD_FRAME_BYTES]={0};
int record_len = 640; int record_len = VAD_FRAME_BYTES;
uint16 frame[320]; uint16 frame[320];
int index = 0, i = 0; int index = 0, i = 0;
@@ -326,15 +335,19 @@ void vad_audio_process(void) {
//每次读取640字节音频数据 //每次读取640字节音频数据
ql_audio_record_read(record_hd, mic_buf, &record_len); ql_audio_record_read(record_hd, mic_buf, &record_len);
//如果检测到声音再去传输 if (g_state == STATE_RECORDING_WAIT_SOUND) {
// if(first_sound_detected){ /* 首次开口确认前不向服务端发送静音。只在本地保留最近500ms,
//实际上每次编码需要输入1152字节的PCM数据 * 确认开口时再回填,防止截掉用户首字。 */
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){ memcpy(vad_preroll[vad_preroll_next], mic_buf, VAD_FRAME_BYTES);
vad_preroll_next = (vad_preroll_next + 1) % VAD_PREROLL_FRAMES;
//quec_usbcdc_write((uint8*)mic_buf, record_len); if (vad_preroll_count < VAD_PREROLL_FRAMES) {
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据 vad_preroll_count++;
}
} else {
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
ql_rtos_semaphore_release(sender_sem);
}
} }
// }
#if 1 #if 1
for(index=0; index<320; index++) for(index=0; index<320; index++)
{ {
@@ -347,6 +360,12 @@ void vad_audio_process(void) {
// 首次声音检测 // 首次声音检测
if (!first_sound_detected) { if (!first_sound_detected) {
/* 播放刚结束时扬声器尾音和PA切换噪声容易被FVAD认为用户开口。 */
if ((now - no_sound_start_time) < PLAYBACK_VAD_GUARD_MS) {
speech_detect_count = 0;
buffer_offset = 0;
break;
}
if (current_vad == VAD_STATE_ACTIVE) { if (current_vad == VAD_STATE_ACTIVE) {
speech_detect_count++; speech_detect_count++;
printf("Speech frame count: %d\r\n", speech_detect_count); printf("Speech frame count: %d\r\n", speech_detect_count);
@@ -363,8 +382,19 @@ void vad_audio_process(void) {
} }
#endif #endif
first_sound_detected = true; first_sound_detected = true;
set_state(STATE_RECORDING_ACTIVE); {
size_t first = (vad_preroll_next + VAD_PREROLL_FRAMES - vad_preroll_count) % VAD_PREROLL_FRAMES;
size_t n;
for (n = 0; n < vad_preroll_count; n++) {
size_t frame_index = (first + n) % VAD_PREROLL_FRAMES;
if (hal_os_rb_write(ring_sender, vad_preroll[frame_index], VAD_FRAME_BYTES, 25) > 0) {
ql_rtos_semaphore_release(sender_sem);
}
}
vad_preroll_count = 0;
}
set_state(STATE_RECORDING_ACTIVE);
printf("First speech confirmed by FVAD (continuous %d frames)", SPEECH_DETECT_THRESHOLD); printf("First speech confirmed by FVAD (continuous %d frames)", SPEECH_DETECT_THRESHOLD);
} }
} else { } else {