From fbd26a9c08c228293f845b3af1b22456aaa1a037 Mon Sep 17 00:00:00 2001 From: hcj <1224667075@qq.com> Date: Sat, 25 Oct 2025 17:08:12 +0800 Subject: [PATCH] =?utf8?q?=E5=A2=9E=E5=8A=A0vad=20=E9=9B=B7=E9=B8=9F?= =?utf8?q?=E5=AF=B9=E8=AF=9D=EF=BC=8C=E4=BB=85=E5=8F=AF=E8=87=AA=E5=8A=A8?= =?utf8?q?=E7=BB=93=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- code/sdk/zhihui/leiniao/imsl_ln/ln_api.h | 1 + .../zhihui/leiniao/imsl_ln/ln_api_websocket.c | 130 +++++++++++++++++- 2 files changed, 127 insertions(+), 4 deletions(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h index 4b8dff687..242f0af52 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h @@ -381,6 +381,7 @@ typedef enum { WS_CMD_AUDIO_PLAYBACK_START, /* 音频播放开始 */ WS_CMD_AUDIO_PLAYBACK_END, /* 音频播放结束 */ WS_CMD_AUDIO_RECORDING_START, /* 开始录音上传 */ + WS_CMD_AUDIO_RECORDING_END, /* 结束录音上传 */ WS_CMD_SENTENCE_PLAYBACK_COMPLETE, /* 单句语音播放完成 */ WS_CMD_MAX /* 最大命令 */ } ws_command_t; diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c index bd1dfb94f..6e84e582a 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -152,6 +152,9 @@ static audio_chat_context_t g_audio_chat_ctx = {0}; // 音频聊天结束标志 static bool g_audio_chat_ending = false; +// 录音上传状态跟踪 +static bool g_recording_upload_started = false; + // 音频数据缓存结构 typedef struct { uint8_t *data; // 音频数据 @@ -176,6 +179,11 @@ static uint32_t g_audio_timeout_ticks = 1500; // 音频超时tick数(默认1.5 static uint32_t g_last_binary_data_tick = 0; // 最后一次接收二进制数据的tick static uint32_t g_audio_playback_start_tick = 0; // 音频播放开始时间tick +// VAD冷却时间相关变量 +static uint32_t g_vad_cooldown_start_tick = 0; // VAD冷却开始时间tick +static bool g_vad_cooldown_active = false; // VAD冷却是否激活 +static uint32_t g_vad_cooldown_duration_ms = 3000; // VAD冷却持续时间(毫秒) + // 音频时长记录循环数组 #define AUDIO_DURATION_BUFFER_SIZE 50 @@ -197,6 +205,8 @@ static void set_chat_id_matched(void); static bool is_chat_id_matched(void); static bool is_active_chat_response(const char *chat_id, const char *response_id); static void ln_ws_audio_write_callback(uint32_t size); +static bool ln_ws_is_vad_cooldown_active(void); +static void ln_ws_start_vad_cooldown(void); @@ -667,6 +677,9 @@ int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type) // 重置对话ID匹配标志 reset_chat_id_match(); + // 重置录音上传状态 + g_recording_upload_started = false; + const char *command_name = (command_type == LN_AUDIO_CHAT_VOICE_CHAT_REQUEST) ? "VoiceChatRequest" : "ChooseStoryRequest"; @@ -690,6 +703,12 @@ int ln_ws_end_audio_chat(void) } LN_WS_DEBUG("Ending audio chat: %s, total messages: %u\n", g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter); + + // 安全检查:如果录音上传状态异常,记录警告 + if (!g_recording_upload_started) { + LN_WS_WARN("Recording upload was not started, but ending audio chat\n"); + } + // 设置结束标志,等待下次AIVoiceDataCallback_ln调用时发送结束消息 g_audio_chat_ending = true; @@ -706,6 +725,12 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use { (void)userData; + // 状态一致性检查:如果音频聊天未激活但录音上传状态为true,重置状态 + if (!g_audio_chat_ctx.is_active && g_recording_upload_started) { + LN_WS_WARN("Audio chat not active but recording upload started, resetting state\n"); + g_recording_upload_started = false; + } + // MEDIA_INFO_LOG("Received audio data: size=%u bytes", size); // ========== VAD 处理已移到 ln_ws_send_audio_data (录音接口层) ========== @@ -719,8 +744,8 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use if (ret != ERRCODE_SUCC) { /* 静默失败,避免过多日志 */ } else { - LN_WS_DEBUG("VAD: Sent %u bytes (format=%d, is_last=%d)\n", - size, format, false); + // LN_WS_DEBUG("VAD: Sent %u bytes (format=%d, is_last=%d)\n", + // size, format, false); } } @@ -729,6 +754,10 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use LN_WS_INFO("VAD: Audio recording ended\n"); } } + else + { + LN_WS_INFO("VAD: Audio g_vad_server_enabled g_vad_server_initialized vad_server_is_running\n"); + } #endif @@ -737,18 +766,45 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use { if (g_audio_chat_ending) { LN_WS_DEBUG("Sending audio chat end message\n"); + + // 触发结束录音上传事件 + // 增加多重判断确保不会重复触发: + // 1. 录音上传状态已开始 + // 2. 音频聊天上下文激活 + // 3. 回调函数已注册 + if (g_recording_upload_started && g_ws_message_callback != NULL) { + LN_WS_INFO("Triggering WS_CMD_AUDIO_RECORDING_END event\n"); + g_ws_message_callback(NULL, WS_CMD_AUDIO_RECORDING_END); + g_recording_upload_started = false; + } + // 发送结束消息 ln_ws_send_audio_data(data, size, true); + // AI_SendAudioDataToQueue(data, size); // 重置聊天上下文和结束标志 g_audio_chat_ctx.is_active = false; g_audio_chat_ending = false; } else { + // 触发开始录音上传事件(仅在第一次发送时触发) + // 增加多重判断确保不会重复触发: + // 1. 录音上传状态未开始 + // 2. 音频聊天上下文激活 + // 3. 有有效的音频数据 + // 4. 回调函数已注册 + if (!g_recording_upload_started && g_ws_message_callback != NULL) { + LN_WS_INFO("Triggering WS_CMD_AUDIO_RECORDING_START event\n"); + g_ws_message_callback(NULL, WS_CMD_AUDIO_RECORDING_START); + g_recording_upload_started = true; + } + // 使用新的音频发送接口 ln_ws_send_audio_data(data, size, false); + // AI_SendAudioDataToQueue(data, size); } } + LOS_Msleep(10); } // "audioBase64": @@ -972,6 +1028,38 @@ int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks) return 0; } +void vad_status_callback_ln_ws(int status) +{ + LN_WS_INFO("Audio vad_status_callback_ln_ws %d\n",status); + if(g_audio_playback_active) + { + // ln_ws_start_vad_cooldown(); + LN_WS_INFO("g_audio_playback_active %d\n",g_audio_playback_active); + return; + } + + // if(status==1) + // { + // // 检查是否在VAD冷却时间内 + // if(ln_ws_is_vad_cooldown_active()) + // { + // LN_WS_INFO("VAD cooldown active, ignoring start request\n"); + // return; + // } + + // ln_ws_start_audio_chat(LN_AUDIO_CHAT_VOICE_CHAT_REQUEST); + // } + // else + if (status==2) + { + ln_ws_end_audio_chat(); + // 开始VAD冷却计时 + ln_ws_start_vad_cooldown(); + } + + +} + void ln_ws_audio_init(void) { // 检查是否已经初始化 @@ -1007,9 +1095,11 @@ void ln_ws_audio_init(void) #if ENABLE_VAD_SERVER // 如果 VAD 服务器已启用,则初始化 VAD 服务器 + ln_ws_enable_vad_server(); + if (g_vad_server_enabled && !g_vad_server_initialized) { LN_WS_INFO("Initializing VAD server...\n"); - errcode_t ret = vad_server_init(NULL); // 使用 NULL 回调,也可以传入自定义回调 + errcode_t ret = vad_server_init(vad_status_callback_ln_ws); // 使用 NULL 回调,也可以传入自定义回调 if (ret == ERRCODE_SUCC) { g_vad_server_initialized = true; LN_WS_INFO("VAD server initialized successfully\n"); @@ -1081,7 +1171,7 @@ int ln_ws_enable_vad_server(void) // 如果音频已经初始化,立即初始化 VAD 服务器 if (g_audio_initialized && !g_vad_server_initialized) { - errcode_t ret = vad_server_init(NULL); + errcode_t ret = vad_server_init(vad_status_callback_ln_ws); if (ret == ERRCODE_SUCC) { g_vad_server_initialized = true; LN_WS_INFO("VAD server initialized successfully\n"); @@ -2852,3 +2942,35 @@ static bool check_playback_completion_by_timeout(void) return false; // 播放未完成 } + +// ===== VAD冷却时间管理函数 ===== + +// 检查VAD冷却时间是否激活 +static bool ln_ws_is_vad_cooldown_active(void) +{ + if (!g_vad_cooldown_active) { + return false; + } + + uint32_t current_tick = osKernelGetTickCount(); + uint32_t elapsed_ms = current_tick - g_vad_cooldown_start_tick; + + // 如果冷却时间已过,重置冷却状态 + if (elapsed_ms >= g_vad_cooldown_duration_ms) { + g_vad_cooldown_active = false; + LN_WS_INFO("VAD cooldown period ended, elapsed: %u ms\n", elapsed_ms); + return false; + } + + LN_WS_DEBUG("VAD cooldown active, remaining: %u ms\n", + g_vad_cooldown_duration_ms - elapsed_ms); + return true; +} + +// 开始VAD冷却计时 +static void ln_ws_start_vad_cooldown(void) +{ + g_vad_cooldown_start_tick = osKernelGetTickCount(); + g_vad_cooldown_active = true; + LN_WS_INFO("VAD cooldown started, duration: %u ms\n", g_vad_cooldown_duration_ms); +} -- 2.17.1