From: hcj <1224667075@qq.com> Date: Wed, 29 Oct 2025 10:14:29 +0000 (+0800) Subject: vad自动开启对话,仅安静环境下可用 X-Git-Tag: V1.0.9_20251128_194606_LN~155 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=c5b717ceb3c2394fc2f8098a02736d96b6510886;p=mirrors%2FFD230.git vad自动开启对话,仅安静环境下可用 --- 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 3306f2a34..ad1455ba2 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -177,12 +177,19 @@ static uint32_t g_last_audio_write_tick = 0; // 最后一次音频写入的tick static bool g_audio_playback_active = false; // 音频播放是否活跃 static uint32_t g_audio_timeout_ticks = 1500; // 音频超时tick数(默认1.5秒) static uint32_t g_last_binary_data_tick = 0; // 最后一次接收二进制数据的tick + +// 音频数据最近缓存(环形缓冲区,仅保留最近数据) +#define AUDIO_CACHE_BUFFER_SIZE 1024 // 8KB缓存 +static uint8_t g_audio_cache_buffer[AUDIO_CACHE_BUFFER_SIZE]; +static uint32_t g_audio_cache_pos = 0; // 当前写入位置 +static uint32_t g_audio_cache_total = 0; // 总写入数据量 +static bool g_audio_cache_wrapped = false; // 是否已循环 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冷却持续时间(毫秒) +static uint32_t g_vad_cooldown_duration_ms = 10000; // VAD冷却持续时间(毫秒) // 音频时长记录循环数组 #define AUDIO_DURATION_BUFFER_SIZE 50 @@ -207,6 +214,11 @@ 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); +static void ln_ws_add_to_audio_cache(const uint8_t* data, uint32_t size); +static uint32_t ln_ws_get_audio_cache(const uint8_t** buffer); +static void ln_ws_reset_audio_cache(void); +static uint32_t ln_ws_copy_from_ring_cache(uint32_t start_pos, uint32_t length, uint8_t* output); +static int ln_ws_send_cached_audio_data(void); @@ -679,6 +691,7 @@ int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type) // 重置录音上传状态 g_recording_upload_started = false; + const char *command_name = (command_type == LN_AUDIO_CHAT_VOICE_CHAT_REQUEST) ? "VoiceChatRequest" : "ChooseStoryRequest"; @@ -797,13 +810,23 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use 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_cached_audio_data(); + // ln_ws_reset_audio_cache(); } + // 使用新的音频发送接口 ln_ws_send_audio_data(data, size, false); // AI_SendAudioDataToQueue(data, size); } } + else + { + if (data && size > 0) { + ln_ws_add_to_audio_cache(data, size); + } + } LOS_Msleep(10); } @@ -816,10 +839,10 @@ static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestam // 记录接收数据的时间 g_last_binary_data_tick = osKernelGetTickCount(); - if (!is_chat_id_matched()) { - LN_WS_WARN("Chat ID not matched\n"); - return; - } + // if (!is_chat_id_matched()) { + // LN_WS_WARN("Chat ID not matched\n"); + // return; + // } if (!raw_data || data_len == 0) { LN_WS_ERROR("Invalid binary data parameters\n"); @@ -1012,7 +1035,7 @@ int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks) return ret; } - //ln_ws_audio_init(); + ln_ws_audio_init(); // 注册音频数据写入回调 AI_RegisterWriteAudioDataCallback(ln_ws_audio_write_callback); @@ -1038,23 +1061,26 @@ void vad_status_callback_ln_ws(int status) return; } - // if(status==1) - // { - // // 检查是否在VAD冷却时间内 - // if(ln_ws_is_vad_cooldown_active()) - // { - // LN_WS_INFO("VAD cooldown active, ignoring start request\n"); - // 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 + ln_ws_start_audio_chat(LN_AUDIO_CHAT_VOICE_CHAT_REQUEST); + } + else if (status==2) { ln_ws_end_audio_chat(); + if(g_audio_chat_ctx.is_active) + { + ln_ws_start_vad_cooldown(); + } // 开始VAD冷却计时 - ln_ws_start_vad_cooldown(); } @@ -2974,3 +3000,188 @@ static void ln_ws_start_vad_cooldown(void) g_vad_cooldown_active = true; LN_WS_INFO("VAD cooldown started, duration: %u ms\n", g_vad_cooldown_duration_ms); } + +// ===== 音频缓存管理函数 ===== + +/** + * @brief 添加音频数据到缓存(环形缓冲区,逐步替换,仅保留最近的数据) + * @param data 音频数据指针 + * @param size 音频数据大小 + */ +static void ln_ws_add_to_audio_cache(const uint8_t* data, uint32_t size) +{ + if (!data || size == 0) { + return; + } + + // 如果要写入的数据太大,截取部分数据 + uint32_t write_size = size; + if (write_size > AUDIO_CACHE_BUFFER_SIZE) { + write_size = AUDIO_CACHE_BUFFER_SIZE; + LN_WS_WARN("Audio data too large (%u bytes), truncating to %u bytes\n", size, write_size); + } + + // 写入数据到缓存缓冲区 + for (uint32_t i = 0; i < write_size; i++) { + g_audio_cache_buffer[g_audio_cache_pos] = data[i]; + g_audio_cache_pos = (g_audio_cache_pos + 1) % AUDIO_CACHE_BUFFER_SIZE; + + // 如果到达缓冲区末尾,标记为已循环 + if (g_audio_cache_pos == 0) { + g_audio_cache_wrapped = true; + } + } + + // 累计总写入量,但在达到缓存大小时停止增长 + if (g_audio_cache_total < AUDIO_CACHE_BUFFER_SIZE) { + uint32_t new_total = g_audio_cache_total + write_size; + if (new_total > AUDIO_CACHE_BUFFER_SIZE) { + g_audio_cache_total = AUDIO_CACHE_BUFFER_SIZE; + } else { + g_audio_cache_total = new_total; + } + } + // 如果已经达到最大数量,g_audio_cache_total 保持不变 + + // 调试日志(可选,避免日志过多):当写入位置回到0时打印 + if (g_audio_cache_pos == 0 && g_audio_cache_wrapped) { + LN_WS_DEBUG("Audio cache: total=%u bytes, pos=%u, wrapped=%d\n", + g_audio_cache_total, g_audio_cache_pos, g_audio_cache_wrapped); + } +} + +/** + * @brief 获取音频缓存数据 + * @param buffer 输出参数,返回缓存数据的指针 + * @return 返回缓存的有效数据长度(字节数) + * @note 如果缓存未循环,返回从开始到当前位置的数据长度 + * @note 如果缓存已循环,返回完整的缓存大小 + */ +static uint32_t ln_ws_get_audio_cache(const uint8_t** buffer) +{ + if (!buffer) { + return 0; + } + + *buffer = g_audio_cache_buffer; + + // 如果缓存已循环,返回完整缓冲区大小 + if (g_audio_cache_wrapped) { + return AUDIO_CACHE_BUFFER_SIZE; + } + + // 如果还未循环,返回当前位置 + return g_audio_cache_pos; +} + +/** + * @brief 重置音频缓存 + */ +static void ln_ws_reset_audio_cache(void) +{ + g_audio_cache_pos = 0; + g_audio_cache_total = 0; + g_audio_cache_wrapped = false; + + // 可选:清空缓冲区 + memset(g_audio_cache_buffer, 0, AUDIO_CACHE_BUFFER_SIZE); + + LN_WS_INFO("Audio cache reset\n"); +} + +/** + * @brief 从环形缓存中复制数据到输出缓冲区(考虑循环情况) + * @param start_pos 起始位置 + * @param length 要复制的长度 + * @param output 输出缓冲区 + * @return 实际复制的字节数 + */ +static uint32_t ln_ws_copy_from_ring_cache(uint32_t start_pos, uint32_t length, uint8_t* output) +{ + if (!output || length == 0) { + return 0; + } + + uint32_t copied = 0; + + // 处理环形缓冲区的情况 + while (length > 0) { + // 计算从当前位置到缓冲区末尾的长度 + uint32_t copy_len = AUDIO_CACHE_BUFFER_SIZE - start_pos; + if (copy_len > length) { + copy_len = length; + } + + // 复制数据 + memcpy(output + copied, g_audio_cache_buffer + start_pos, copy_len); + copied += copy_len; + length -= copy_len; + start_pos = (start_pos + copy_len) % AUDIO_CACHE_BUFFER_SIZE; + } + + return copied; +} + +/** + * @brief 获取缓存的音频数据并通过 ln_ws_send_audio_data 发送,发送完后清空缓存 + * @return 成功返回0,失败返回-1 + * @note 考虑环形缓冲区,进行数据分割发送 + */ +static int ln_ws_send_cached_audio_data(void) +{ + // 如果缓存为空,直接返回 + if (g_audio_cache_pos == 0 && !g_audio_cache_wrapped) { + LN_WS_DEBUG("No cached audio data to send\n"); + return 0; + } + + uint32_t total_size = g_audio_cache_wrapped ? AUDIO_CACHE_BUFFER_SIZE : g_audio_cache_pos; + uint32_t start_pos = g_audio_cache_wrapped ? g_audio_cache_pos : 0; + + LN_WS_INFO("Sending cached audio data: %u bytes (wrapped=%d, start_pos=%u)\n", + total_size, g_audio_cache_wrapped, start_pos); + + #define MAX_SEND_CHUNK_SIZE 512 // 每次最多发送4KB + + int ret = 0; + uint32_t remaining = total_size; + uint32_t sent_offset = 0; + uint8_t send_buffer[MAX_SEND_CHUNK_SIZE]; + + while (remaining > 0 && ret == 0) { + // 计算本次发送的数据长度 + uint32_t chunk_size = (remaining > MAX_SEND_CHUNK_SIZE) ? MAX_SEND_CHUNK_SIZE : remaining; + + // 从环形缓存中复制数据到发送缓冲区 + uint32_t copy_start = (start_pos + sent_offset) % AUDIO_CACHE_BUFFER_SIZE; + uint32_t actual_copied = ln_ws_copy_from_ring_cache(copy_start, chunk_size, send_buffer); + + if (actual_copied != chunk_size) { + LN_WS_ERROR("Failed to copy data from cache, expected %u, got %u\n", chunk_size, actual_copied); + ret = -1; + break; + } + + // 判断是否是最后一块 + bool is_last = (remaining <= chunk_size); + + // 发送数据 + LN_WS_DEBUG("Sending chunk: size=%u, remaining=%u, is_last=%d\n", + chunk_size, remaining - chunk_size, is_last); + + ret = ln_ws_send_audio_data(send_buffer, chunk_size, false); + + sent_offset += chunk_size; + remaining -= chunk_size; + } + + if (ret == 0) { + LN_WS_INFO("Cached audio data sent successfully, clearing cache\n"); + // 发送成功,清空缓存 + ln_ws_reset_audio_cache(); + } else { + LN_WS_ERROR("Failed to send cached audio data, ret=%d\n", ret); + } + + return ret; +}