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);
}
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);
// 如果队列写入失败,释放内存
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) {
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;
}
AI_ClearFileCache();
// 立即暂停和清空输出流
- PauseAudioStreams();
+ // PauseAudioStreams();
ClearAudioStreams();
// 立即恢复输出流
- ResetAudioStreams();
+ // ResetAudioStreams();
MEDIA_INFO_LOG("AI voice force interrupt completed");
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)
{
{(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"},
printf("[LN][WS] Sent ping: %s\n", json_string);
// 清理内存
- free(json_string);
+ cJSON_free(json_string);
cJSON_Delete(json);
return ret;
// 音频聊天结束标志
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)
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)
{
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);
}
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);
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;
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;
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";
{
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;
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();
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);
int ret = uapi_ws_send_text(json_string);
// 清理资源
- free(json_string);
+ cJSON_free(json_string);
cJSON_Delete(json);
if (ret == 0) {
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)
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);
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字段
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字段
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字段
}
+// {
+// "deviceId": "DEVICE_001",
+// "timestamp": 1640995204,
+// "messageId": "msg_interrupt_001",
+// "command": "VoiceChatInterrupt",
+// "data": {
+// "chatId": "chat_12345",
+// "reason": "用户主动打断"
+// }
+// }
+
+
// {
// "deviceId": "DEVICE_001",
// "timestamp": 1640995200,