vad自动开启对话,仅安静环境下可用
authorhcj <1224667075@qq.com>
Wed, 29 Oct 2025 10:14:29 +0000 (18:14 +0800)
committerhcj <1224667075@qq.com>
Wed, 29 Oct 2025 10:14:29 +0000 (18:14 +0800)
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index 3306f2a..ad1455b 100644 (file)
@@ -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;
+}