diff --git a/ql-application/threadx/interface/custom_app/hal_key.c b/ql-application/threadx/interface/custom_app/hal_key.c index 0441fda..8eb650d 100644 --- a/ql-application/threadx/interface/custom_app/hal_key.c +++ b/ql-application/threadx/interface/custom_app/hal_key.c @@ -187,6 +187,9 @@ static void key_thread_entry(void *parameter) if(ql_rtos_get_systicks() - key_press_mic_time > KEY_LONG_PRESS_3S && power_down_status==0){ nfc_tx_click_count = 0; power_down_status = 1; + /* Shutdown audio must own the codec. Stop VAD and any + * interrupted TTS before queueing the power-off prompt. */ + stop_player_func(); app_play_tips_file(10); power_down_status = 2; //ql_rtos_task_sleep_s(3); diff --git a/ql-application/threadx/interface/custom_app/http_downfile.c b/ql-application/threadx/interface/custom_app/http_downfile.c index 2be48a8..9c1819a 100644 --- a/ql-application/threadx/interface/custom_app/http_downfile.c +++ b/ql-application/threadx/interface/custom_app/http_downfile.c @@ -34,6 +34,7 @@ const char *mp3_file[] ={ "volume_up_zh.mp3",//16 "volume_down_zh.mp3",//17 "ai_active_zh.mp3",//18 + "barge_in_cute.mp3",//19 对话打断提示音 }; //http://banban.api.talkingq.com:8080/assets/roles/banban/tts_error.mp3 @@ -183,11 +184,13 @@ void app_file_check_download(void) char filename_buff[64]={0x00}; int ret = 0, cnt = 0; - for(index=1;index<19;index++){ - - if(index==4||index==5||index==6||index==7||index==8){ - continue; - } + for(index=1;index<20;index++){ + + /* barge_in_cute.mp3 is shipped in customer_fs and is not hosted by the + * legacy prompt download service. */ + if(index==4||index==5||index==6||index==7||index==8||index==19){ + continue; + } if(strlen(mp3_file[index])>0){ sprintf(filename_buff, "U:/%s", mp3_file[index]); @@ -203,14 +206,16 @@ down_loop: } } -void app_file_check_del(void) +void app_file_check_del(void) { int index = 1; char filename_buff[64]={0x00}; - for(index=1;index<19;index++){ + for(index=1;index<20;index++){ - if(index==4||index==5||index==6||index==7||index==8){ + /* Keep the bundled barge-in prompt across app-only FOTA. Unlike the + * server-managed prompts, it cannot be restored by the downloader. */ + if(index==4||index==5||index==6||index==7||index==8||index==19){ continue; } diff --git a/ql-application/threadx/interface/custom_app/ws_talkingq.c b/ql-application/threadx/interface/custom_app/ws_talkingq.c index f0bc864..a1f0e4f 100644 --- a/ql-application/threadx/interface/custom_app/ws_talkingq.c +++ b/ql-application/threadx/interface/custom_app/ws_talkingq.c @@ -49,11 +49,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 int onmessage_status = -1; +volatile int tts_playing = 0; +unsigned char wss_connect_start = 0; static volatile int player_mq_couter = 0; -static unsigned char audio_in_task_cancel = 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 unsigned char xiaoice_wss_connect_status = 0; static unsigned char wss_connect_counter = 0; @@ -299,6 +302,14 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i 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}; @@ -743,9 +754,12 @@ void generateSessionId(void) #if 1 -void sendStartPacket(void) { - generateSessionId(); - 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}; @@ -862,17 +876,23 @@ void sendRecordingAudioPacket(AudioPacket *packet) { extern const char *mp3_file[]; -int app_play_tips_file(int index) -{ - int file_valid_check = 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) 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}; @@ -881,15 +901,18 @@ int app_play_tips_file(int index) if(index==0) { file_valid_check = 0; }else{ - if(ql_access(filename_buff, 0)==0){ - file_valid_check = 1; - }else{ - 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){ + if(index==18 || index==19){ add_file_to_play_player_queue(filename_buff, 2); }else{ add_file_to_play_player_queue(filename_buff, 0); @@ -1001,26 +1024,46 @@ void app_player_thread(void *arvg) int tts_play_interrupt_func(void) { + int playback_interrupted = 0; + //如果在vad状态,则直接返回 if(vad_audio_is_recording()||g_mic_working) return -1; g_mic_working = 1;//等待转为vad状态就清0 或者拾音超时 或者ws没有正常 - - 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) { - g_mic_working = 0; - return -2; - } - - if(tts_playing==2){ - printf("vad_start_func0\n"); - 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; } void stop_player_func(void) @@ -1028,6 +1071,7 @@ void stop_player_func(void) vad_audio_stop_record(); xiaoice_close_all_audio(); + tts_play_status = 0; tts_playing = 0; } @@ -1194,9 +1238,16 @@ void xiaoice_close_all_audio(void) timeout = hal_os_up_time_ms(); - while(tts_play_status&&tts_playing!=2){ + /* 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>10000) 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 diff --git a/ql-config/config/EG800AKCN_11LG/customer_fs.bin b/ql-config/config/EG800AKCN_11LG/customer_fs.bin index 78c7470..5066bf7 100644 --- a/ql-config/config/EG800AKCN_11LG/customer_fs.bin +++ b/ql-config/config/EG800AKCN_11LG/customer_fs.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08eba5e80158dc312326f81f1ffef9fc8af648f084e2d8d1bc623b58d275c67a +oid sha256:1c498e97811cc8e4f4732936cc26cd42fb37c9c9f4795d2cab9135dca2636c16 size 262144