diff --git a/ql-application/threadx/interface/custom_app/vad_det.c b/ql-application/threadx/interface/custom_app/vad_det.c index 486101e..946ede6 100644 --- a/ql-application/threadx/interface/custom_app/vad_det.c +++ b/ql-application/threadx/interface/custom_app/vad_det.c @@ -299,9 +299,47 @@ void vad_leave_message(void) audio_packet_type = 4; vad_leave_message_flag = 1; + /* A previous conversation may still be parked in WAITING_COMMAND. NFC + * leave-message recording must enter IDLE first, otherwise g_triggered is + * left pending forever and its 10-second no-speech timer never starts. */ + set_state(STATE_IDLE); vad_audio_trigger_record(); } +int vad_leave_message_is_active(void) +{ + return vad_leave_message_flag != 0 || audio_packet_type == 4; +} + +/* The conversation key has priority over an unfinished NFC leave-message + * transaction. Clear every local latch before the normal conversation prompt + * is queued so that its recording can never inherit packetType=4. */ +int vad_cancel_leave_message(void) +{ + int was_active = vad_leave_message_is_active(); + + if (!was_active) return 0; + + printf("cancel pending leave message\r\n"); + vad_leave_message_flag = 0; + g_triggered = false; + audio_packet_type = 0; + no_speech_timeout = 0; + speech_detect_count = 0; + vad_preroll_next = 0; + vad_preroll_count = 0; + key_start_leave = 0; + + if (vad_audio_is_recording()) { + set_state(STATE_IDLE); + vad_audio_stop_no_wait_resp(); + } else { + set_state(STATE_IDLE); + } + + return 1; +} + // 主处理函数 - 需要在循环中定期调用 void vad_audio_process(void) { size_t bytes_read = 0; @@ -315,10 +353,9 @@ void vad_audio_process(void) { // 状态机处理 switch (g_state) { - case STATE_IDLE: - no_speech_timeout = 0; - vad_leave_message_flag = 0; - speech_detect_count = 0; // 空闲时清零计数器 + case STATE_IDLE: + no_speech_timeout = 0; + speech_detect_count = 0; // 空闲时清零计数器 vad_tts_playback_finished_status = 0; if (g_triggered) { @@ -448,15 +485,20 @@ void vad_audio_process(void) { buffer_offset = 0; break; - case STATE_WAITING_COMMAND: - if(vad_tts_playback_finished_status == 1) { - printf("finished_status:%d, no_speech_timeout:%d\n", vad_tts_playback_finished_status, no_speech_timeout); - set_state(STATE_IDLE); - ql_rtos_task_sleep_ms(50); - if(no_speech_timeout == 0&&vad_leave_message_flag==0) { - vad_audio_trigger_record(); - } - } + case STATE_WAITING_COMMAND: + if(vad_tts_playback_finished_status == 1) { + int leave_message_finished = vad_leave_message_flag != 0; + printf("finished_status:%d, no_speech_timeout:%d\n", vad_tts_playback_finished_status, no_speech_timeout); + set_state(STATE_IDLE); + ql_rtos_task_sleep_ms(50); + if(no_speech_timeout == 0&&vad_leave_message_flag==0) { + vad_audio_trigger_record(); + } + if(leave_message_finished) { + vad_leave_message_flag = 0; + audio_packet_type = 0; + } + } key_start_leave = 0; break; diff --git a/ql-application/threadx/interface/custom_app/ws_talkingq.c b/ql-application/threadx/interface/custom_app/ws_talkingq.c index 6ee5b17..fb23d05 100644 --- a/ql-application/threadx/interface/custom_app/ws_talkingq.c +++ b/ql-application/threadx/interface/custom_app/ws_talkingq.c @@ -30,9 +30,9 @@ #define PCM_FRAME_LENGTH 1152 -#define CLOUD_TTS_ERROR_URL_FORMAT \ - "http://101.35.224.118:8080/assets/roles/banban/tts_error/tts_error%02u.mp3" -#define LOCAL_TTS_ERROR_PATH "U:/tts_error.mp3" +#define CLOUD_TTS_ERROR_URL_FORMAT \ + "http://101.35.224.118:8080/assets/roles/banban/tts_error/tts_error%02u.mp3" +#define LOCAL_TTS_ERROR_PATH "U:/tts_error.mp3" static int g_sim_ready = 0; @@ -50,14 +50,14 @@ static uint8_t sender_buf[PCM_FRAME_LENGTH]={0}; static int16 mp3_buff[2*PCM_FRAME_LENGTH]; static int16 *ppbuff=mp3_buff; -static int onmessage_status = -1; -volatile int tts_playing = 0; -unsigned char wss_connect_start = 0; -static volatile int player_mq_couter = 0; -/* Set while a barge-in prompt bridges the old and new sessions. The old - * server response can still deliver URLs after its player queue was cleared. */ -static volatile int barge_in_transition = 0; -static unsigned char audio_in_task_cancel = 0; +static int onmessage_status = -1; +volatile int tts_playing = 0; +unsigned char wss_connect_start = 0; +static volatile int player_mq_couter = 0; +/* Set while a barge-in prompt bridges the old and new sessions. The old + * server response can still deliver URLs after its player queue was cleared. */ +static volatile int barge_in_transition = 0; +static volatile unsigned char audio_in_task_cancel = 0; static unsigned char xiaoice_wss_connect_status = 0; static unsigned char wss_connect_counter = 0; @@ -66,22 +66,24 @@ int fota_start = 0; int mqtt_task_run_flag = 0; extern int sub_topic_status; -extern int mqtt_task_run_flag; -static unsigned char shine_init = 0; -extern int fisrt_net_start; -extern u8 power_down_status; -extern int aofei_mp3_player_get_result(void); - -void sendAuthentication(void); -void vad_start_func(void); -void xiaoice_close_all_audio(void); +extern int mqtt_task_run_flag; +static unsigned char shine_init = 0; +extern int fisrt_net_start; +extern u8 power_down_status; +extern int aofei_mp3_player_get_result(void); +extern int vad_leave_message_is_active(void); +extern int vad_cancel_leave_message(void); + +void sendAuthentication(void); +void vad_start_func(void); +void xiaoice_close_all_audio(void); int audio_packet_type = 0; -typedef struct ice_work_item_s -{ - int type; //0-tts_out 1-session_continue 2-session_end 3-云端超时提示(失败时播放本地兜底) +typedef struct ice_work_item_s +{ + int type; //0-tts_out 1-session_continue 2-session_end 3-云端超时提示(失败时播放本地兜底) int error; int del_url; void *data; @@ -117,11 +119,11 @@ static librws_app_t app = &_app; static ql_sem_t ws_connect_sem = NULL; extern int fota_start; -#define OTA_AUDIO_STOP_TIMEOUT_MS 3000 -#define OTA_AUDIO_STOP_POLL_MS 20 - -void xiaoice_clear_player_mq(void); - +#define OTA_AUDIO_STOP_TIMEOUT_MS 3000 +#define OTA_AUDIO_STOP_POLL_MS 20 + +void xiaoice_clear_player_mq(void); + static int tts_play_status = 0;//1播放中 2播放完成 @@ -204,12 +206,12 @@ static void remove_underscores(char *str) { *dst = '\0'; // 添加字符串结束符 } -/* 生成 banban 角色的随机云端对话超时提示音 URL。 */ -static int build_random_tts_error_url(char *url, size_t url_size) -{ - static uint32_t random_sequence = 0; - static unsigned int last_random_index = 10; - uint32_t random_value; +/* 生成 banban 角色的随机云端对话超时提示音 URL。 */ +static int build_random_tts_error_url(char *url, size_t url_size) +{ + static uint32_t random_sequence = 0; + static unsigned int last_random_index = 10; + uint32_t random_value; unsigned int random_index; int written; @@ -238,32 +240,32 @@ static int build_random_tts_error_url(char *url, size_t url_size) return -1; } - printf("random cloud tts error index:%02u, url:%s\n", random_index, url); - return 1; -} - -/* - * 后台下发任意角色的对话超时 URL 时,统一替换成 banban 的随机云端资源。 - * 非对话超时 URL 保持不变。 - */ -static int randomize_tts_error_url(char *url, size_t url_size) -{ - char *file_name; - - if(url == NULL || url_size == 0){ - return 0; - } - - file_name = strrchr(url, '/'); - file_name = (file_name == NULL) ? url : file_name + 1; - - if(strncmp(file_name, "tts_error", strlen("tts_error")) != 0 && - strncmp(file_name, "tts_err", strlen("tts_err")) != 0){ - return 0; - } - - return build_random_tts_error_url(url, url_size); -} + printf("random cloud tts error index:%02u, url:%s\n", random_index, url); + return 1; +} + +/* + * 后台下发任意角色的对话超时 URL 时,统一替换成 banban 的随机云端资源。 + * 非对话超时 URL 保持不变。 + */ +static int randomize_tts_error_url(char *url, size_t url_size) +{ + char *file_name; + + if(url == NULL || url_size == 0){ + return 0; + } + + file_name = strrchr(url, '/'); + file_name = (file_name == NULL) ? url : file_name + 1; + + if(strncmp(file_name, "tts_error", strlen("tts_error")) != 0 && + strncmp(file_name, "tts_err", strlen("tts_err")) != 0){ + return 0; + } + + return build_random_tts_error_url(url, url_size); +} void add_file_to_play_player_queue(char *path, int type) { @@ -312,21 +314,21 @@ void add_file_to_play_player_queue(char *path, int type) //如果录音后 超过10s 还没有收到信息 提示本轮对话结束 -static void onmessage_text(rws_socket socket, const char *text, const unsigned int len) -{ - if (fota_start == 1) { - printf("ignore websocket audio during OTA\r\n"); - return; - } - - /* The new session id is generated only when recording starts. Until - * then, every TTS message on this socket belongs to the interrupted - * session and must not be allowed to refill the cleared player queue. */ - if (barge_in_transition) { - printf("drop stale websocket message during barge-in\r\n"); - return; - } - +static void onmessage_text(rws_socket socket, const char *text, const unsigned int len) +{ + if (fota_start == 1) { + printf("ignore websocket audio during OTA\r\n"); + return; + } + + /* The new session id is generated only when recording starts. Until + * then, every TTS message on this socket belongs to the interrupted + * session and must not be allowed to refill the cleared player queue. */ + if (barge_in_transition) { + printf("drop stale websocket message during barge-in\r\n"); + return; + } + // printf("message(txt), %d(byte): %s ", len, text); char url_buff[128]={0x00}; @@ -351,6 +353,7 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i char *ret_str2 = NULL; size_t url_len = 0; int is_tts_error_url = 0; + int is_message_ok_url = 0; if(ret_str1 == NULL){ ret_str1 = strstr(text, "http://"); } @@ -367,6 +370,14 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i } memcpy(url_buff, ret_str1, url_len); url_buff[url_len] = '\0'; + is_message_ok_url = strstr(url_buff, "message_ok") != NULL; + + /* A cancelled NFC transaction may still have a response in flight. + * Never let that acknowledgement enter a normal conversation. */ + if(is_message_ok_url && !vad_leave_message_is_active()){ + printf("drop stale leave message ack\r\n"); + return; + } /* 两个角色统一使用 banban 的 HTTP 随机超时提示音 */ is_tts_error_url = randomize_tts_error_url(url_buff, sizeof(url_buff)); @@ -391,7 +402,7 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i tts_play_status = 2; } - if(is_tts_error_url == 1 || strstr(url_buff, "message_ok")!=NULL){ + if(is_tts_error_url == 1 || is_message_ok_url){ printf("tts_error or message_ok\n"); vad_set_speech_timeout_flag(1); } @@ -400,8 +411,8 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i if(tts_playing==2) return; - /* 对话超时提示使用 type=3,云端播放失败时由播放线程切换到本地兜底。 */ - add_file_to_play_player_queue(url_buff, is_tts_error_url == 1 ? 3 : 0); + /* 对话超时提示使用 type=3,云端播放失败时由播放线程切换到本地兜底。 */ + add_file_to_play_player_queue(url_buff, is_tts_error_url == 1 ? 3 : 0); } } @@ -772,12 +783,12 @@ void generateSessionId(void) #if 1 -void sendStartPacket(void) { - generateSessionId(); - /* sessionId now identifies the new conversation. Late URLs from the old - * conversation will fail the existing session-id check below. */ - barge_in_transition = 0; - generateDeviceId(); +void sendStartPacket(void) { + generateSessionId(); + /* sessionId now identifies the new conversation. Late URLs from the old + * conversation will fail the existing session-id check below. */ + barge_in_transition = 0; + generateDeviceId(); gSequenceNumber = 0; AudioPacket startPacket = {0}; @@ -894,23 +905,23 @@ void sendRecordingAudioPacket(AudioPacket *packet) { extern const char *mp3_file[]; -int app_play_tips_file(int index) -{ - int file_valid_check = 0; - int access_ret = 0; - - printf("xiaoice_play_index:%d\n", index); - - //正在录音过程中不允许播放 -// if(xiaoice_get_tts_play_status() || get_start_mic_flag()==1) return -1; - if(power_down_status!=1){ - if(vad_audio_is_recording() || tts_playing!=0 || fota_start==1 || power_down_status==2 || get_sleep_mode()==1) { - printf("tips blocked:index=%d vad=%d tts=%d fota=%d power=%d sleep=%d\r\n", - index, vad_audio_is_recording(), tts_playing, fota_start, - power_down_status, get_sleep_mode()); - return -1; - } - } +int app_play_tips_file(int index) +{ + int file_valid_check = 0; + int access_ret = 0; + + printf("xiaoice_play_index:%d\n", index); + + //正在录音过程中不允许播放 +// if(xiaoice_get_tts_play_status() || get_start_mic_flag()==1) return -1; + if(power_down_status!=1){ + if(vad_audio_is_recording() || tts_playing!=0 || fota_start==1 || power_down_status==2 || get_sleep_mode()==1) { + printf("tips blocked:index=%d vad=%d tts=%d fota=%d power=%d sleep=%d\r\n", + index, vad_audio_is_recording(), tts_playing, fota_start, + power_down_status, get_sleep_mode()); + return -1; + } + } char filename_buff[64]={0x00}; @@ -919,18 +930,18 @@ int app_play_tips_file(int index) if(index==0) { file_valid_check = 0; }else{ - access_ret = ql_access(filename_buff, 0); - if(access_ret==0){ - file_valid_check = 1; - }else{ - printf("tips file missing:index=%d path=%s access=%d\r\n", - index, filename_buff, access_ret); - file_valid_check = 2; - } + access_ret = ql_access(filename_buff, 0); + if(access_ret==0){ + file_valid_check = 1; + }else{ + printf("tips file missing:index=%d path=%s access=%d\r\n", + index, filename_buff, access_ret); + file_valid_check = 2; + } } if(file_valid_check!=2){ - if(index==18 || index==19){ + if(index==18 || index==19){ add_file_to_play_player_queue(filename_buff, 2); }else{ add_file_to_play_player_queue(filename_buff, 0); @@ -942,53 +953,53 @@ int app_play_tips_file(int index) return 0; } -int app_play_ota_tips_file(void) -{ - char filename_buff[64] = {0}; - uint32_t timeout; - - /* OTA owns the audio path from this point on. Stop the active conversation - * before queuing the prompt, otherwise recording/TTS rejects or masks it. */ - if (vad_audio_is_recording()) { - vad_audio_stop_record(); - } - - if (get_xiaoice_connected_status() == 2) { - talkingq_ws_disconnect(); - } - - tts_playing = 2; - xiaoice_clear_player_mq(); - - timeout = hal_os_up_time_ms(); - while (player_mq_couter > 0) { - ql_rtos_task_sleep_ms(OTA_AUDIO_STOP_POLL_MS); - if (hal_os_up_time_ms() - timeout > OTA_AUDIO_STOP_TIMEOUT_MS) { - printf("OTA wait previous audio timeout, counter:%d\r\n", - player_mq_couter); - return -3; - } - } - - tts_play_status = 0; - tts_playing = 0; - - /* Normal prompts are blocked while fota_start is set. Queue the dedicated - * OTA prompt directly after the old conversation has fully stopped. */ - snprintf(filename_buff, sizeof(filename_buff), "U:/%s", mp3_file[3]); - printf("xiaoice_play_index:3\n"); - if (ql_access(filename_buff, 0) != 0) { - return -2; - } - - add_file_to_play_player_queue(filename_buff, 0); - return player_mq_couter > 0 ? 0 : -1; -} - -void app_player_thread(void *arvg) -{ - ice_work_item *player_item=NULL; - int play_ret; +int app_play_ota_tips_file(void) +{ + char filename_buff[64] = {0}; + uint32_t timeout; + + /* OTA owns the audio path from this point on. Stop the active conversation + * before queuing the prompt, otherwise recording/TTS rejects or masks it. */ + if (vad_audio_is_recording()) { + vad_audio_stop_record(); + } + + if (get_xiaoice_connected_status() == 2) { + talkingq_ws_disconnect(); + } + + tts_playing = 2; + xiaoice_clear_player_mq(); + + timeout = hal_os_up_time_ms(); + while (player_mq_couter > 0) { + ql_rtos_task_sleep_ms(OTA_AUDIO_STOP_POLL_MS); + if (hal_os_up_time_ms() - timeout > OTA_AUDIO_STOP_TIMEOUT_MS) { + printf("OTA wait previous audio timeout, counter:%d\r\n", + player_mq_couter); + return -3; + } + } + + tts_play_status = 0; + tts_playing = 0; + + /* Normal prompts are blocked while fota_start is set. Queue the dedicated + * OTA prompt directly after the old conversation has fully stopped. */ + snprintf(filename_buff, sizeof(filename_buff), "U:/%s", mp3_file[3]); + printf("xiaoice_play_index:3\n"); + if (ql_access(filename_buff, 0) != 0) { + return -2; + } + + add_file_to_play_player_queue(filename_buff, 0); + return player_mq_couter > 0 ? 0 : -1; +} + +void app_player_thread(void *arvg) +{ + ice_work_item *player_item=NULL; + int play_ret; while(1){ ql_rtos_queue_wait(player_queue, (u8*)(&player_item), sizeof(ice_work_item *), 0xFFFFFFFF); @@ -998,29 +1009,29 @@ void app_player_thread(void *arvg) if(player_item->data){ // printf("xiaoice player! %s\n",player_item->data); - app_set_app_operation(1); - aofei_mp3_player(player_item->data); - play_ret = aofei_mp3_player_get_result(); - app_set_app_operation(1); - - if(player_mq_couter>0) player_mq_couter--; - - /* - * type=3 只用于对话超时提示。云端连接、HTTP、读取或播放器初始化 - * 失败时才排入本地兜底;如果用户已经开始下一轮录音,则丢弃旧提示。 - */ - if(play_ret != 0 && player_item->type == 3 && - !vad_audio_is_recording() && tts_playing_ret() != 2){ - printf("cloud tts error play failed:%d, fallback:%s\r\n", - play_ret, LOCAL_TTS_ERROR_PATH); - if(ql_access(LOCAL_TTS_ERROR_PATH, 0) == 0){ - add_file_to_play_player_queue(LOCAL_TTS_ERROR_PATH, 0); - }else{ - printf("local tts error fallback missing:%s\r\n", LOCAL_TTS_ERROR_PATH); - } - } - - if(player_mq_couter==0){ + app_set_app_operation(1); + aofei_mp3_player(player_item->data); + play_ret = aofei_mp3_player_get_result(); + app_set_app_operation(1); + + if(player_mq_couter>0) player_mq_couter--; + + /* + * type=3 只用于对话超时提示。云端连接、HTTP、读取或播放器初始化 + * 失败时才排入本地兜底;如果用户已经开始下一轮录音,则丢弃旧提示。 + */ + if(play_ret != 0 && player_item->type == 3 && + !vad_audio_is_recording() && tts_playing_ret() != 2){ + printf("cloud tts error play failed:%d, fallback:%s\r\n", + play_ret, LOCAL_TTS_ERROR_PATH); + if(ql_access(LOCAL_TTS_ERROR_PATH, 0) == 0){ + add_file_to_play_player_queue(LOCAL_TTS_ERROR_PATH, 0); + }else{ + printf("local tts error fallback missing:%s\r\n", LOCAL_TTS_ERROR_PATH); + } + } + + if(player_mq_couter==0){ printf("player_mq_couter==0\r\n"); if(tts_play_status==2){ @@ -1060,46 +1071,69 @@ void app_player_thread(void *arvg) int tts_play_interrupt_func(void) { - int playback_interrupted = 0; - + int playback_interrupted = 0; + int leave_message_cancelled = 0; + + /* A short press explicitly starts a normal conversation. Cancel an NFC + * leave-message before the generic "already recording" guard, otherwise a + * pending/recording leave-message can leak packetType=4 into this session. */ + leave_message_cancelled = vad_cancel_leave_message(); + if(leave_message_cancelled){ + uint64_t cancel_start; + + /* Let the sender task discard buffered PCM and its encoder without + * emitting END. Only then close the old leave-message socket. */ + audio_in_task_cancel = 1; + ql_rtos_semaphore_release(sender_sem); + cancel_start = hal_os_up_time_ms(); + while(audio_in_task_cancel && + hal_os_up_time_ms() - cancel_start < OTA_AUDIO_STOP_TIMEOUT_MS){ + ql_rtos_task_sleep_ms(20); + } + if(audio_in_task_cancel){ + printf("wait leave message sender cancel timeout\r\n"); + } + talkingq_ws_disconnect(); + } + //如果在vad状态,则直接返回 if(vad_audio_is_recording()||g_mic_working) return -1; g_mic_working = 1;//等待转为vad状态就清0 或者拾音超时 或者ws没有正常 - - /* Remember whether this key press is interrupting TTS. tts_playing == 2 - * is only a cancellation signal for the old player; it must not leak into - * the next conversation, otherwise prompts and error audio are discarded. */ - playback_interrupted = (tts_playing != 0 || player_mq_couter > 0); - barge_in_transition = playback_interrupted; - xiaoice_close_all_audio(); + + /* Remember whether this key press is interrupting TTS. tts_playing == 2 + * is only a cancellation signal for the old player; it must not leak into + * the next conversation, otherwise prompts and error audio are discarded. */ + playback_interrupted = (tts_playing != 0 || player_mq_couter > 0); + barge_in_transition = playback_interrupted; + xiaoice_close_all_audio(); talkingq_ws_connect(); - if(get_xiaoice_connected_status()!=2) { - /* Let the caller play its local network-error prompt. */ - tts_play_status = 0; - tts_playing = 0; - barge_in_transition = 0; - g_mic_working = 0; - return -2; - } - - if(playback_interrupted){ - printf("barge_in_prompt\n"); - /* The old player has finished consuming the cancellation state. */ - tts_play_status = 0; - tts_playing = 0; - /* Use the same local-prompt flow as ai_active_zh.mp3. Queue type 2 - * starts VAD after playback; fall back to VAD if the file is absent. */ - if(app_play_tips_file(19) != 0){ - vad_start_func(); - } + if(get_xiaoice_connected_status()!=2) { + /* Let the caller play its local network-error prompt. */ + tts_play_status = 0; + tts_playing = 0; + barge_in_transition = 0; + g_mic_working = 0; + return -2; + } + + if(playback_interrupted){ + printf("barge_in_prompt\n"); + /* The old player has finished consuming the cancellation state. */ + tts_play_status = 0; + tts_playing = 0; + /* Use the same local-prompt flow as ai_active_zh.mp3. Queue type 2 + * starts VAD after playback; fall back to VAD if the file is absent. */ + if(app_play_tips_file(19) != 0){ + vad_start_func(); + } }else{ app_play_tips_file(18); } - - return 0; + + return 0; } void stop_player_func(void) @@ -1107,7 +1141,7 @@ void stop_player_func(void) vad_audio_stop_record(); xiaoice_close_all_audio(); - tts_play_status = 0; + tts_play_status = 0; tts_playing = 0; } @@ -1227,19 +1261,19 @@ static void check_connect_thread_entry(void *parameter) printf("tts_error_set_speech1\n"); vad_set_speech_timeout_flag(1); - /* - * 后台没有返回任何音频 URL 时,也先由设备生成随机云端提示音。 - * type=3 会在 HTTP 下载或播放初始化失败时自动切换到本地音频。 - */ - { - char cloud_tts_error_url[128] = {0}; - if(build_random_tts_error_url(cloud_tts_error_url, - sizeof(cloud_tts_error_url)) == 1){ - add_file_to_play_player_queue(cloud_tts_error_url, 3); - }else{ - app_play_tips_file(9); - } - } + /* + * 后台没有返回任何音频 URL 时,也先由设备生成随机云端提示音。 + * type=3 会在 HTTP 下载或播放初始化失败时自动切换到本地音频。 + */ + { + char cloud_tts_error_url[128] = {0}; + if(build_random_tts_error_url(cloud_tts_error_url, + sizeof(cloud_tts_error_url)) == 1){ + add_file_to_play_player_queue(cloud_tts_error_url, 3); + }else{ + app_play_tips_file(9); + } + } } #endif @@ -1258,7 +1292,7 @@ void xiaoice_clear_player_mq(void) free(player_item->data); player_item->data = NULL; } - if(player_mq_couter>0) player_mq_couter--; + if(player_mq_couter>0) player_mq_couter--; free(player_item); player_item = NULL; } @@ -1284,16 +1318,16 @@ void xiaoice_close_all_audio(void) timeout = hal_os_up_time_ms(); - /* The queue counter also includes the item currently owned by the - * player task. Wait for that item to observe tts_playing == 2 and - * finish cleanup before the cancellation state is reused/reset. */ - while(player_mq_couter>0){ + /* The queue counter also includes the item currently owned by the + * player task. Wait for that item to observe tts_playing == 2 and + * finish cleanup before the cancellation state is reused/reset. */ + while(player_mq_couter>0){ ql_rtos_task_sleep_ms(20); - if(hal_os_up_time_ms()-timeout>OTA_AUDIO_STOP_TIMEOUT_MS){ - printf("wait player stop timeout, counter:%d\r\n", - player_mq_couter); - break; - } + if(hal_os_up_time_ms()-timeout>OTA_AUDIO_STOP_TIMEOUT_MS){ + printf("wait player stop timeout, counter:%d\r\n", + player_mq_couter); + break; + } } } #endif @@ -1313,6 +1347,23 @@ void app_sender_thread(void *arvg) ql_rtos_semaphore_wait(sender_sem, 0xFFFFFFFF); sender_loop: + if(audio_in_task_cancel){ + left = rb_get_space_used(ring_sender); + while(left > 0){ + int discard_len = left > PCM_FRAME_LENGTH ? PCM_FRAME_LENGTH : left; + hal_os_rb_read(ring_sender, sender_buf, discard_len, 0); + left = rb_get_space_used(ring_sender); + } + if(shine_init != 0){ + shine_close(sh_shine); + sh_shine = NULL; + shine_init = 0; + } + audio_packet_type = 0; + audio_in_task_cancel = 0; + printf("leave message sender cancelled\r\n"); + continue; + } if(!app_get_data_call_net_status()) continue;