增加打断接口,开启新对话将自动打断上一次对话 2440/1
authorhcj <1224667075@qq.com>
Mon, 29 Sep 2025 08:20:54 +0000 (16:20 +0800)
committerhcj <1224667075@qq.com>
Mon, 29 Sep 2025 08:20:54 +0000 (16:20 +0800)
code/sdk/middleware/services/media/foundation/sample/wrapper/audio_manager_c_wrapper.cpp
code/sdk/middleware/services/media/foundation/sample/wrapper/audio_manager_c_wrapper.h
code/sdk/zhihui/ai/media/ai_media_service_ext.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index 6c00d37..397deea 100644 (file)
@@ -77,6 +77,7 @@ public:
 
     void OnInterrupt(int32_t type, int32_t hint) override
     {
+        printf("OnInterrupt xxxxxxxxxxxxxxxxxxxxxxxxxx\r\n");
         ALOGI("type:%d hint:%d", type, hint);
     }
 };
@@ -276,6 +277,15 @@ int32_t AudioStreamOutInit(AudioRendererConfig renderConfig)
     ALOGD("success");
     return AUDIO_SUCCESS;
 }
+// int32_t GetTimestamp(struct timespec &timeStamp) const;
+
+int32_t AudioStreamGetTimestamp(struct timespec *timeStamp)
+{
+    g_streamOut->GetTimestamp(*timeStamp);
+    return AUDIO_SUCCESS;
+}
+
+
 
 int32_t AudioStreamOutSetMute(bool mute)
 {
index 9a70782..0b7935a 100644 (file)
@@ -9,6 +9,7 @@
 #define AUDIO_MANAGER_C_WRAPPER_H
 
 #include "audio_base_type.h"
+#include <time.h> 
 
 #ifdef __cplusplus
 extern "C" {
@@ -62,6 +63,8 @@ int32_t AudioStreamOutDeinit(void);
 
 int32_t AudioStreamOutFlush(void);
 
+int32_t AudioStreamGetTimestamp(struct timespec *timeStamp);
+
 /**
  * @brief Sets audio effect algorithm parameters.
  *
index dfda417..03fa4e3 100644 (file)
@@ -1051,13 +1051,15 @@ static int32_t RunAIVoice(AIVoiceContext *context)
             
             if (status == osOK) {
                 // 成功接收到队列数据
-                MEDIA_INFO_LOG("RunAIVoice: received queue data: size=%u, timestamp=%u", msg.size, msg.timestamp);
+                // MEDIA_INFO_LOG("RunAIVoice: received queue data: size=%u, timestamp=%u", msg.size, msg.timestamp);
                 
                 // 处理接收到的音频数据
                 if (msg.data != nullptr && msg.size > 0) {
                     // 写入音频数据到输出流
                     AI_WriteAudioData(msg.data,msg.size);
-                    
+                    // struct timespec ts;
+                    // AudioStreamGetTimestamp(&ts);
+                    // printf("AudioStreamGetTimestamp %d\r\n",ts.tv_sec);
                     // 释放消息数据内存
                     free(msg.data);
                 }
@@ -1071,15 +1073,15 @@ static int32_t RunAIVoice(AIVoiceContext *context)
                 if (AI_HasFileCacheData()) {
                     AIVoiceQueueMessage cachedMsg;
                     if (AI_ReadFromFileCache(cachedMsg) == 0) {
-                        MEDIA_INFO_LOG("RunAIVoice: restoring cached data to queue: size=%u, timestamp=%u", 
-                                     cachedMsg.size, cachedMsg.timestamp);
+                        // MEDIA_INFO_LOG("RunAIVoice: restoring cached data to queue: size=%u, timestamp=%u", 
+                        //              cachedMsg.size, cachedMsg.timestamp);
                         
                         // 将缓存的数据重新写入队列
                         if (cachedMsg.data != nullptr && cachedMsg.size > 0) {
                             osStatus_t queueStatus = osMessageQueuePut(context->inputQueue, &cachedMsg, 0, 0);
                             if (queueStatus == osOK) {
                                 context->queueMsgCount++;
-                                MEDIA_INFO_LOG("Successfully restored %u bytes from cache to queue", cachedMsg.size);
+                                // MEDIA_INFO_LOG("Successfully restored %u bytes from cache to queue", cachedMsg.size);
                             } else {
                                 MEDIA_ERR_LOG("Failed to restore cached data to queue: %d", queueStatus);
                                 // 如果队列写入失败,释放内存
@@ -1573,9 +1575,23 @@ int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size)
         MEDIA_ERR_LOG("Invalid audio data parameters");
         return -1;
     }
+    
+    const int32_t MAX_RETRY_COUNT = 5;  // 最大重试次数
+    int32_t retry_count = 0;
+    
     AI_WRITE:
     if(g_aiVoiceContext->interruptFlag)
-    return -1;
+    {
+        g_aiVoiceContext->interruptFlag=false;
+        return -1;
+    }
+    
+    // 检查重试次数
+    if (retry_count >= MAX_RETRY_COUNT) {
+        MEDIA_ERR_LOG("Failed to write audio data after %d retries, giving up", MAX_RETRY_COUNT);
+        return -1;
+    }
+    
     // 获取全局互斥锁(自动初始化)
     MediaMutexHandle mutex = GetGlobalMutex();
     if (mutex == nullptr) {
@@ -1596,8 +1612,8 @@ int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size)
     int32_t ret = AudioStreamOutStreamWrite(data, size);
     usleep(50*size);
     if (ret != 0) {
-        // MEDIA_ERR_LOG("Failed to write audio data to output stream, ret=%d", ret);
-        // return -1;
+        retry_count++;
+        MEDIA_ERR_LOG("Failed to write audio data to output stream, ret=%d, retry %d/%d", ret, retry_count, MAX_RETRY_COUNT);
         usleep(1000*500);
         goto AI_WRITE;
     }
@@ -1910,11 +1926,11 @@ int32_t AI_ForceInterrupt(void)
     AI_ClearFileCache();
     
     // 立即暂停和清空输出流
-    PauseAudioStreams();
+    // PauseAudioStreams();
     ClearAudioStreams();
     
     // 立即恢复输出流
-    ResetAudioStreams();
+    // ResetAudioStreams();
     
     MEDIA_INFO_LOG("AI voice force interrupt completed");
     
index e7ab7d3..66aade4 100644 (file)
@@ -867,6 +867,35 @@ static uint32_t ln_ws_send_random_story_at(uint8_t *para, uint32_t paraLen, char
     return ERRCODE_SUCC;
 }
 
+/* AT^LN_WS_SEND_VOICE_CHAT_INTERRUPT - 发送语音聊天打断请求 */
+static uint32_t ln_ws_send_voice_chat_interrupt_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    
+    const char *reason = "用户主动打断"; // 默认原因
+    
+    // 解析参数
+    if (para != NULL && paraLen > 0) {
+        uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+        uint32_t argc = 0;
+        
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) == VAD_SUCCESS && argc >= 1) {
+            reason = (const char *)argv[0];
+        }
+    }
+    
+    printf("[LN][WS_SEND_VOICE_CHAT_INTERRUPT] Sending voice chat interrupt with reason: %s\n", reason);
+    
+    int ret = ln_ws_send_voice_chat_interrupt(reason);
+    if (ret != 0)
+    {
+        printf("[LN][WS_SEND_VOICE_CHAT_INTERRUPT] failed: %d\n", ret);
+        return ERRCODE_FAIL;
+    }
+    printf("[LN][WS_SEND_VOICE_CHAT_INTERRUPT] success\n");
+    return ERRCODE_SUCC;
+}
+
 /* AT^LN_WS_SLEEP */
 static uint32_t ln_ws_sleep_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
@@ -1847,6 +1876,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_WS_START_AUDIO_CHAT", ln_ws_start_audio_chat_at, (char *)"开始音频聊天: AT^LN_WS_START_AUDIO_CHAT[=0/1]"},
     {(char *)"AT^LN_WS_END_AUDIO_CHAT", ln_ws_end_audio_chat_at, (char *)"结束音频聊天: AT^LN_WS_END_AUDIO_CHAT"},
     {(char *)"AT^LN_WS_SEND_RANDOM_STORY", ln_ws_send_random_story_at, (char *)"发送随机故事请求: AT^LN_WS_SEND_RANDOM_STORY[=storyTheme]"},
+    {(char *)"AT^LN_WS_SEND_VOICE_CHAT_INTERRUPT", ln_ws_send_voice_chat_interrupt_at, (char *)"发送语音聊天打断请求: AT^LN_WS_SEND_VOICE_CHAT_INTERRUPT[=reason]"},
     
     /* 音频播放 */
     {(char *)"AT^LN_STORY_PLAY_AT", ln_audio_play_at, (char *)"播放音频: AT^LN_STORY_PLAY_AT"},
index 2323e1c..0036dfb 100644 (file)
@@ -353,6 +353,7 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
 int ln_ws_send_random_story_request(const char *story_theme);
 int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type);
 int ln_ws_end_audio_chat(void);
+int ln_ws_send_voice_chat_interrupt(const char *reason);
 const char* ln_ws_get_current_chat_id(void);
 int ln_ws_stop(void);
 // int ln_ws_force_reconnect(void);
index 063a5bd..dab6592 100644 (file)
@@ -228,7 +228,7 @@ int ln_ws_send_ping(void)
     printf("[LN][WS] Sent ping: %s\n", json_string);
     
     // 清理内存
-    free(json_string);
+    cJSON_free(json_string);
     cJSON_Delete(json);
     
     return ret;
@@ -251,9 +251,17 @@ static audio_chat_context_t g_audio_chat_ctx = {0};
 // 音频聊天结束标志
 static bool g_audio_chat_ending = false;
 
+
+// 当前对话匹配标志
+static bool g_chat_id_matched = false;    // 当前对话ID是否匹配
+
 // 函数声明
 static void generate_chat_id(char *chat_id, size_t size);
 static void generate_message_id(char *message_id, size_t size, const char *chat_id, uint32_t counter);
+static void reset_chat_id_match(void);
+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);
 
 // 生成聊天ID
 static void generate_chat_id(char *chat_id, size_t size)
@@ -274,6 +282,41 @@ static void generate_message_id(char *message_id, size_t size, const char *chat_
     snprintf(message_id, size, "%s_msg_%u", chat_id, counter);
 }
 
+
+// 重置对话ID匹配标志
+static void reset_chat_id_match(void)
+{
+    g_chat_id_matched = false;
+    printf("[LN][WS] Reset chat ID match flag\n");
+}
+
+// 设置对话ID匹配标志
+static void set_chat_id_matched(void)
+{
+    g_chat_id_matched = true;
+    printf("[LN][WS] Set chat ID matched\n");
+}
+
+// 检查对话ID是否匹配
+static bool is_chat_id_matched(void)
+{
+    return g_chat_id_matched;
+}
+
+// 检查是否为活跃对话的回复
+static bool is_active_chat_response(const char *chat_id, const char *response_id)
+{
+    if (!chat_id || !response_id) return false;
+    
+    // 检查对话ID是否匹配当前活跃对话
+    if (!g_audio_chat_ctx.is_active || strcmp(g_audio_chat_ctx.chat_id, chat_id) != 0) {
+        return false;
+    }
+    
+    // 检查对话ID是否匹配
+    return is_chat_id_matched();
+}
+
 // 发送音频数据到WebSocket
 int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool finished)
 {
@@ -299,6 +342,8 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
         generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id));
         g_audio_chat_ctx.message_counter = 0;
         g_audio_chat_ctx.is_active = true;
+        // 重置对话ID匹配标志
+        reset_chat_id_match();
         printf("[LN][WS] New chat started: %s\n", g_audio_chat_ctx.chat_id);
     }
     
@@ -392,7 +437,7 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
            g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false");
     
     // 清理内存
-    free(json_string);
+    cJSON_free(json_string);
     free(audio_base64);
     cJSON_Delete(json);
     
@@ -400,6 +445,8 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
     if (finished) {
         g_audio_chat_ctx.is_active = false;
         printf("[LN][WS] Chat ended: %s\n", g_audio_chat_ctx.chat_id);
+        // 重置对话ID匹配标志
+        reset_chat_id_match();
     }
     
     return ret;
@@ -418,7 +465,9 @@ int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type)
         printf("[LN][WS] Audio chat already active: %s\n", g_audio_chat_ctx.chat_id);
         return 0;
     }
-    // AI_ForceInterrupt();
+    ln_ws_send_voice_chat_interrupt("新对话开始");
+
+    AI_ForceInterrupt();
     
     // 设置命令类型
     g_audio_chat_command_type = command_type;
@@ -427,6 +476,9 @@ int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type)
     g_audio_chat_ctx.message_counter = 0;
     g_audio_chat_ctx.is_active = true;
     
+    // 重置对话ID匹配标志
+    reset_chat_id_match();
+    
     const char *command_name = (command_type == LN_AUDIO_CHAT_VOICE_CHAT_REQUEST) ? 
                               "VoiceChatRequest" : "ChooseStoryRequest";
     
@@ -487,6 +539,11 @@ static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestam
 {
     printf("[AI] Received binary message at %u, length: %d bytes\n", timestamp, data_len);
     
+    if (!is_chat_id_matched()) {
+        printf("[AI] Chat ID not matched\n");
+        return;
+    }
+
     if (!raw_data || data_len == 0) {
         printf("[AI] Invalid binary data parameters\n");
         return;
@@ -669,14 +726,13 @@ int ln_ws_send_random_story_request(const char *story_theme)
         printf("[LN][WS] Cannot send random story request, connection state: %d\n", g_ws_connected);
         return -1;
     }
+    ln_ws_send_voice_chat_interrupt("故事请求开始");
     
     // 生成聊天ID(使用当前对话ID)
-    char chat_id[128];
-    generate_chat_id(chat_id, sizeof(chat_id));
-    
+    generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id));
     // 生成消息ID
     char message_id[128];
-    generate_message_id(message_id, sizeof(message_id), chat_id, 1);
+    generate_message_id(message_id, sizeof(message_id), g_audio_chat_ctx.chat_id, 1);
     
     // 构建JSON消息
     cJSON *json = cJSON_CreateObject();
@@ -699,7 +755,7 @@ int ln_ws_send_random_story_request(const char *story_theme)
         return -1;
     }
     
-    cJSON_AddStringToObject(data, "chatId", chat_id);
+    cJSON_AddStringToObject(data, "chatId", g_audio_chat_ctx.chat_id);
     cJSON_AddStringToObject(data, "storyTheme", story_theme);
     cJSON_AddItemToObject(json, "data", data);
     
@@ -717,7 +773,7 @@ int ln_ws_send_random_story_request(const char *story_theme)
     int ret = uapi_ws_send_text(json_string);
     
     // 清理资源
-    free(json_string);
+    cJSON_free(json_string);
     cJSON_Delete(json);
     
     if (ret == 0) {
@@ -729,6 +785,74 @@ int ln_ws_send_random_story_request(const char *story_theme)
     return ret;
 }
 
+/* 发送语音聊天打断请求 */
+int ln_ws_send_voice_chat_interrupt(const char *reason)
+{
+    if (!reason) {
+        printf("[LN][WS] Invalid reason parameter\n");
+        return -1;
+    }
+    
+    if (g_ws_connected != WS_STATE_CONNECTED) {
+        printf("[LN][WS] Cannot send voice chat interrupt, connection state: %d\n", g_ws_connected);
+        return -1;
+    }
+       
+    // 生成消息ID
+    char message_id[128];
+    generate_message_id(message_id, sizeof(message_id), g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter + 1);
+    
+    // 构建JSON消息
+    cJSON *json = cJSON_CreateObject();
+    if (!json) {
+        printf("[LN][WS] Failed to create JSON object\n");
+        return -1;
+    }
+    
+    // 添加基本字段
+    cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id());
+    cJSON_AddNumberToObject(json, "timestamp", (double)time(NULL));
+    cJSON_AddStringToObject(json, "messageId", message_id);
+    cJSON_AddStringToObject(json, "command", "VoiceChatInterrupt");
+    
+    // 添加data字段
+    cJSON *data = cJSON_CreateObject();
+    if (!data) {
+        printf("[LN][WS] Failed to create data JSON object\n");
+        cJSON_Delete(json);
+        return -1;
+    }
+    
+    cJSON_AddStringToObject(data, "chatId", g_audio_chat_ctx.chat_id);
+    cJSON_AddStringToObject(data, "reason", reason);
+    cJSON_AddItemToObject(json, "data", data);
+    
+    // 转换为字符串
+    char *json_string = cJSON_Print(json);
+    if (!json_string) {
+        printf("[LN][WS] Failed to convert JSON to string\n");
+        cJSON_Delete(json);
+        return -1;
+    }
+    
+    printf("[LN][WS] Sending VoiceChatInterrupt: %s\n", json_string);
+    
+    // 发送消息
+    int ret = uapi_ws_send_text(json_string);
+    
+    // 清理资源
+    cJSON_free(json_string);
+    cJSON_Delete(json);
+    
+    if (ret == 0) {
+        printf("[LN][WS] VoiceChatInterrupt sent successfully\n");
+    } else {
+        printf("[LN][WS] Failed to send VoiceChatInterrupt\n");
+    }
+    
+    return ret;
+}
+
 
 /* 停止:关闭心跳并关闭连接 */
 int ln_ws_stop(void)
@@ -920,6 +1044,7 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
     if (message_id && cJSON_IsString(message_id) && message_id->valuestring) {
         strncpy(data_msg->message_id, message_id->valuestring, sizeof(data_msg->message_id) - 1);
         data_msg->message_id[sizeof(data_msg->message_id) - 1] = '\0';
+        
     } else {
         printf("[LN][WS] Missing or invalid messageId in message\n");
         cJSON_Delete(root);
@@ -958,6 +1083,15 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
             if (chat_id && cJSON_IsString(chat_id) && chat_id->valuestring) {
                 strncpy(data_msg->chat_id, chat_id->valuestring, sizeof(data_msg->chat_id) - 1);
                 data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0';
+                
+                // 检查对话ID是否匹配当前活跃对话
+                if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) {
+                    set_chat_id_matched();
+                } else {
+                    printf("[LN][WS] VoiceChatResponse from non-active chat %s, ignoring\n", data_msg->chat_id);
+                    cJSON_Delete(root);
+                    return -1; // 返回错误,不处理此消息
+                }
             }
             
             // 解析data.data字段
@@ -998,6 +1132,15 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
             if (chat_id && cJSON_IsString(chat_id) && chat_id->valuestring) {
                 strncpy(data_msg->chat_id, chat_id->valuestring, sizeof(data_msg->chat_id) - 1);
                 data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0';
+                
+                // 检查对话ID是否匹配当前活跃对话
+                if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) {
+                    set_chat_id_matched();
+                } else {
+                    printf("[LN][WS] ChooseStoryResponse from non-active chat %s, ignoring\n", data_msg->chat_id);
+                    cJSON_Delete(root);
+                    return -1; // 返回错误,不处理此消息
+                }
             }
             
             // 解析data.data字段
@@ -1038,6 +1181,15 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
             if (chat_id && cJSON_IsString(chat_id) && chat_id->valuestring) {
                 strncpy(data_msg->chat_id, chat_id->valuestring, sizeof(data_msg->chat_id) - 1);
                 data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0';
+                
+                // 检查对话ID是否匹配当前活跃对话
+                if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) {
+                    set_chat_id_matched();
+                } else {
+                    printf("[LN][WS] RandomStoryResponse from non-active chat %s, ignoring\n", data_msg->chat_id);
+                    cJSON_Delete(root);
+                    return -1; // 返回错误,不处理此消息
+                }
             }
             
             // 解析data.data字段
@@ -1521,6 +1673,18 @@ static void ln_ws_process_incremental_base64_data(const char *new_data, size_t n
 }
 
 
+// {
+//   "deviceId": "DEVICE_001",
+//   "timestamp": 1640995204,
+//   "messageId": "msg_interrupt_001",
+//   "command": "VoiceChatInterrupt",
+//   "data": {
+//     "chatId": "chat_12345",
+//     "reason": "用户主动打断"
+//   }
+// }
+
+
 // {
 //   "deviceId": "DEVICE_001",
 //   "timestamp": 1640995200,