diff --git a/ql-application/threadx/interface/custom_app/mp3_player.c b/ql-application/threadx/interface/custom_app/mp3_player.c index d11092a..42826be 100644 --- a/ql-application/threadx/interface/custom_app/mp3_player.c +++ b/ql-application/threadx/interface/custom_app/mp3_player.c @@ -24,7 +24,8 @@ static Mp3PlayConfigInfo mp3_configInfo; static unsigned char mp3_player_status = 0; QFILE *local_mp3_file_fd=NULL; -static int mp3_player_end_frame = 0; +static int mp3_player_end_frame = 0; +static int mp3_player_last_result = 0; int current_sample_rate = 0; static audio_track_handle track_hd; @@ -278,9 +279,10 @@ u32 local_mp3_data_fetch(const char *uri, uint8_t *buffer, u32 length) return read_bytes; } -size_t url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length) -{ +int url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length) +{ int resp_status = -1; + int result = -1; uint32_t nw = length; uint32_t page_pos = 0; uint32_t content_length = 0; @@ -353,7 +355,10 @@ size_t url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length) total_length += page_pos; } - printf("end read:%d, %d\n", total_length, session->content_length); + printf("end read:%d, %d\n", total_length, session->content_length); + if(total_length == session->content_length){ + result = 0; + } __exit: @@ -363,13 +368,13 @@ __exit: session = NULL; } - if(total_length=0){ aofei_mp3_decoder->read_ptr = mp3_buff; @@ -457,6 +483,7 @@ void aofei_mp3_player(char *file_name) app_codec_pa_close(); } +__exit: if(aofei_mp3_decoder!=NULL){ mp3_decoder_delete(aofei_mp3_decoder); aofei_mp3_decoder = NULL; @@ -477,10 +504,17 @@ void aofei_mp3_player(char *file_name) mp3_buff = NULL; } - total_length = 0; - mp3_player_end_frame = 0; - mp3_player_status = 0; -} + total_length = 0; + mp3_player_end_frame = 0; + mp3_player_status = 0; + mp3_player_last_result = ret; +} + +/* 播放调用是同步的,队列线程可在返回后读取本次结果决定是否使用本地兜底。 */ +int aofei_mp3_player_get_result(void) +{ + return mp3_player_last_result; +} diff --git a/ql-application/threadx/interface/custom_app/ws_talkingq.c b/ql-application/threadx/interface/custom_app/ws_talkingq.c index a1f0e4f..6ee5b17 100644 --- a/ql-application/threadx/interface/custom_app/ws_talkingq.c +++ b/ql-application/threadx/interface/custom_app/ws_talkingq.c @@ -30,8 +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 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; @@ -65,17 +66,22 @@ 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 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); int audio_packet_type = 0; -typedef struct ice_work_item_s -{ - int type; //0-tts_out 1-session_continue 2-session_end 3-error +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; @@ -198,16 +204,12 @@ static void remove_underscores(char *str) { *dst = '\0'; // 添加字符串结束符 } -/* - * 将后台下发的任意角色对话超时音频 URL 改为 banban 角色的 HTTP 地址, - * 并在 tts_error00~tts_error09 中随机选择一条。 - */ -static int randomize_tts_error_url(char *url, size_t url_size) -{ - static uint32_t random_sequence = 0; - static unsigned int last_random_index = 10; - char *file_name; - 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; @@ -215,14 +217,6 @@ static int randomize_tts_error_url(char *url, size_t url_size) 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; - } - /* * 某些设备上硬件随机接口可能持续返回 0,因此混合伪随机数、系统 tick * 和递增序列;若仍与上一次相同,则向后偏移,避免连续重复播放。 @@ -244,9 +238,32 @@ static int randomize_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; -} + 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) { @@ -383,7 +400,8 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i if(tts_playing==2) return; - add_file_to_play_player_queue(url_buff, 0); + /* 对话超时提示使用 type=3,云端播放失败时由播放线程切换到本地兜底。 */ + add_file_to_play_player_queue(url_buff, is_tts_error_url == 1 ? 3 : 0); } } @@ -967,9 +985,10 @@ int app_play_ota_tips_file(void) return player_mq_couter > 0 ? 0 : -1; } -void app_player_thread(void *arvg) -{ - ice_work_item *player_item=NULL; +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); @@ -979,12 +998,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); - app_set_app_operation(1); - - if(player_mq_couter>0) player_mq_couter--; - 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){ @@ -1191,9 +1227,19 @@ static void check_connect_thread_entry(void *parameter) printf("tts_error_set_speech1\n"); vad_set_speech_timeout_flag(1); - app_play_tips_file(9); - //talkingq_ws_send_text("REQUEST_PROMPT_SOUND:tts_error"); - //改为播放本地音频 听不清 请再说一遍 + /* + * 后台没有返回任何音频 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