Skip to content

Commit 8720554

Browse files
committed
xiaozhi修复stop后tts播放不全问题
1 parent 0d121df commit 8720554

File tree

6 files changed

+190
-59
lines changed

6 files changed

+190
-59
lines changed

projects/Edgi_Talk_M55_XiaoZhi/.settings/Edgi_Talk_M55_XiaoZhi.OpenOCD.Debug.rttlaunch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<stringAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbClientOtherOptions" value=""/>
1515
<stringAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerExecutable" value="${debugger_install_path}/${openocd-infineon_debugger_relative_path}/bin/openocd.exe"/>
1616
<intAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerGdbPortNumber" value="3333"/>
17-
<stringAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerOther" value="-s ../scripts -s ../flm/cypress/cat1d -f interface/kitprog3.cfg -f target/infineon/pse84xgxs2.cfg -c &quot;set QSPI_FLASHLOADER ../flm/cypress/cat1d/PSE84_SMIF.FLM&quot; -c &quot;transport select swd&quot; -c &quot;gdb_port 3332&quot; -c &quot;cat1d.cm55 configure -rtos auto -rtos-wipe-on-reset-halt 1&quot; -c &quot;gdb_breakpoint_override hard&quot; -c &quot;init; reset init; flash write_image erase ${project_loc}/rtthread.hex;&quot; -c &quot;reset init;&quot;"/>
17+
<stringAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerOther" value="-s ../scripts -s ../flm/cypress/cat1d -f interface/kitprog3.cfg -f target/infineon/pse84xgxs2.cfg -c &quot;set QSPI_FLASHLOADER ../flm/cypress/cat1d/PSE84_SMIF.FLM&quot; -c &quot;transport select swd&quot; -c &quot;gdb_port 3332&quot; -c &quot;cat1d.cm55 configure -rtos auto -rtos-wipe-on-reset-halt 1&quot; -c &quot;gdb_breakpoint_override hard&quot; -c &quot;init; reset init; flash write_image erase ${project_loc}/Debug/rtthread.hex;&quot; -c &quot;reset init;&quot;"/>
1818
<stringAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerTclPortNumber" value="6666"/>
1919
<intAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.gdbServerTelnetPortNumber" value="4444"/>
2020
<booleanAttribute key="ilg.gnumcueclipse.debug.gdbjtag.openocd.multicoreMode" value="false"/>

projects/Edgi_Talk_M55_XiaoZhi/applications/xiaozhi/xiaozhi.cpp

Lines changed: 185 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <string.h>
1919

2020
#define DBG_TAG "xz.ws"
21-
#define DBG_LVL DBG_INFO
21+
#define DBG_LVL DBG_LOG
2222
#include <rtdbg.h>
2323

2424
/* Configuration constants */
@@ -29,27 +29,28 @@
2929
#define NETWORK_CHECK_DELAY_MS 500
3030
#define RETRY_DELAY_BASE_MS 1000
3131
#define RETRY_DELAY_INCREMENT_MS 200
32-
#define TTS_STOP_DELAY_MS 10
32+
#define TTS_STOP_DELAY_MS 200
3333
#define BUTTON_DEBOUNCE_MS 20
3434
#define WAKEWORD_INIT_FLAG_RESET 0
35-
#define TTS_SENTENCE_TIMEOUT_MS 8500
35+
#define TTS_SENTENCE_TIMEOUT_MS 6000
3636

3737
/* Global application state */
3838
xiaozhi_app_t g_app =
39-
{
40-
.xiaozhi_tid = RT_NULL,
41-
.client_id = "af7ac552-9991-4b31-b660-683b210ae95f",
42-
.websocket_reconnect_flag = 0,
43-
.iot_initialized = 0,
44-
.last_reconnect_time = 0,
45-
.mac_address_string = {0},
46-
.client_id_string = {0},
47-
.ws = {0},
48-
.state = kDeviceStateUnknown,
49-
.button_event = RT_NULL,
50-
.wakeword_initialized_session = 0,
51-
.multi_turn_conversation_enabled = RT_TRUE,
52-
.tts_sentence_end_timer = RT_NULL
39+
{
40+
.xiaozhi_tid = RT_NULL,
41+
.client_id = "af7ac552-9991-4b31-b660-683b210ae95f",
42+
.websocket_reconnect_flag = 0,
43+
.iot_initialized = 0,
44+
.last_reconnect_time = 0,
45+
.mac_address_string = {0},
46+
.client_id_string = {0},
47+
.ws = {0},
48+
.state = kDeviceStateUnknown,
49+
.button_event = RT_NULL,
50+
.wakeword_initialized_session = 0,
51+
.multi_turn_conversation_enabled = RT_TRUE,
52+
.tts_sentence_end_timer = RT_NULL,
53+
.tts_stop_workqueue = RT_NULL
5354
};
5455

5556
#include "ui/xiaozhi_ui.h"
@@ -134,10 +135,65 @@ void xz_wakeword_detected_callback(const char *wake_word, float confidence)
134135
/* TTS sentence end timeout handler */
135136
static void tts_sentence_end_timeout(void *parameter)
136137
{
137-
LOG_D("TTS sentence end timeout, sending event to restart listening for multi-turn conversation");
138+
uint32_t tick = rt_tick_get();
139+
LOG_D("TTS sentence end timeout at tick=%u, sending event to restart listening for multi-turn conversation", tick);
138140

139141
/* Send event to button thread to handle the restart in non-ISR context */
140-
rt_event_send(g_app.button_event, TIMEOUT_EVENT);
142+
rt_err_t ev_ret = rt_event_send(g_app.button_event, TIMEOUT_EVENT);
143+
if (ev_ret != RT_EOK)
144+
{
145+
LOG_W("rt_event_send returned %d from timer callback", ev_ret);
146+
}
147+
}
148+
149+
/* TTS stop delayed restart work function */
150+
static void tts_stop_restart_listening(struct rt_work *work, void *work_data)
151+
{
152+
LOG_D("TTS stop delayed restart: restarting listening mode");
153+
154+
/* Multi-turn conversation: keep listening without restarting wake word detection */
155+
g_app.state = kDeviceStateListening;
156+
xz_mic(1);
157+
158+
/* Try to send listen start */
159+
if (ws_send_listen_start(&g_app.ws.clnt, g_app.ws.session_id, kListeningModeAutoStop))
160+
{
161+
xiaozhi_ui_chat_status(" 聆听中");
162+
xiaozhi_ui_chat_output("聆听中...");
163+
}
164+
else
165+
{
166+
LOG_W("Listen start failed in TTS stop delayed restart");
167+
/* Reset state if failed */
168+
g_app.state = kDeviceStateIdle;
169+
xz_mic(0);
170+
xiaozhi_ui_chat_status(" 就绪");
171+
xiaozhi_ui_chat_output("就绪");
172+
}
173+
}
174+
175+
/* Static work item for TTS stop restart */
176+
static struct rt_work tts_stop_work;
177+
178+
/* Static timer for TTS stop delay */
179+
static rt_timer_t tts_stop_delay_timer = RT_NULL;
180+
181+
/* TTS stop delay timer callback */
182+
static void tts_stop_delay_timeout(void *parameter)
183+
{
184+
LOG_D("TTS stop delay timeout, submitting work to restart listening");
185+
186+
/* Submit work to restart listening */
187+
if (g_app.tts_stop_workqueue)
188+
{
189+
rt_workqueue_submit_work(g_app.tts_stop_workqueue, &tts_stop_work, 0);
190+
}
191+
else
192+
{
193+
LOG_W("Workqueue not available in timer callback");
194+
/* Fallback */
195+
tts_stop_restart_listening(&tts_stop_work, RT_NULL);
196+
}
141197
}
142198

143199
/* State consistency check function */
@@ -623,8 +679,12 @@ err_t my_wsapp_fn(int code, char *buf, size_t len)
623679
/* Stop TTS sentence end timer if running */
624680
if (g_app.tts_sentence_end_timer)
625681
{
626-
rt_timer_stop(g_app.tts_sentence_end_timer);
627-
LOG_D("Stopped TTS sentence end timer due to disconnection");
682+
rt_err_t stop_ret = rt_timer_stop(g_app.tts_sentence_end_timer);
683+
LOG_D("rt_timer_stop returned %d when stopping timer due to disconnection", stop_ret);
684+
if (stop_ret == RT_EOK)
685+
{
686+
LOG_D("Stopped TTS sentence end timer due to disconnection");
687+
}
628688
}
629689

630690
xiaozhi_ui_chat_status(" 休眠中");
@@ -813,6 +873,59 @@ void xz_ws_audio_init(void)
813873
/* Wake word detection will be initialized after WebSocket connection */
814874
LOG_I("Audio system initialized successfully");
815875

876+
/* Create TTS sentence end timer once here to avoid race conditions
877+
* Timer is one-shot and will be started on sentence_end events */
878+
if (!g_app.tts_sentence_end_timer)
879+
{
880+
g_app.tts_sentence_end_timer = rt_timer_create("tts_end_timer",
881+
tts_sentence_end_timeout,
882+
RT_NULL,
883+
rt_tick_from_millisecond(TTS_SENTENCE_TIMEOUT_MS),
884+
RT_TIMER_FLAG_ONE_SHOT);
885+
if (g_app.tts_sentence_end_timer)
886+
{
887+
LOG_D("Created TTS sentence end timer (%d ms)", TTS_SENTENCE_TIMEOUT_MS);
888+
}
889+
else
890+
{
891+
LOG_E("Failed to create TTS sentence end timer");
892+
}
893+
}
894+
895+
/* Create workqueue for TTS stop delayed restart */
896+
if (!g_app.tts_stop_workqueue)
897+
{
898+
g_app.tts_stop_workqueue = rt_workqueue_create("tts_stop_wq", 2048, RT_THREAD_PRIORITY_MAX - 1);
899+
if (g_app.tts_stop_workqueue)
900+
{
901+
LOG_D("Created TTS stop workqueue");
902+
/* Initialize the work item */
903+
rt_work_init(&tts_stop_work, tts_stop_restart_listening, RT_NULL);
904+
}
905+
else
906+
{
907+
LOG_E("Failed to create TTS stop workqueue");
908+
}
909+
}
910+
911+
/* Create TTS stop delay timer */
912+
if (!tts_stop_delay_timer)
913+
{
914+
tts_stop_delay_timer = rt_timer_create("tts_stop_delay",
915+
tts_stop_delay_timeout,
916+
RT_NULL,
917+
rt_tick_from_millisecond(TTS_STOP_DELAY_MS),
918+
RT_TIMER_FLAG_ONE_SHOT);
919+
if (tts_stop_delay_timer)
920+
{
921+
LOG_D("Created TTS stop delay timer (%d ms)", TTS_STOP_DELAY_MS);
922+
}
923+
else
924+
{
925+
LOG_E("Failed to create TTS stop delay timer");
926+
}
927+
}
928+
816929
init_flag = 0;
817930
}
818931
}
@@ -1042,25 +1155,31 @@ void Message_handle(const uint8_t *data, uint16_t len)
10421155
/* Check if multi-turn conversation is enabled */
10431156
if (g_app.multi_turn_conversation_enabled)
10441157
{
1045-
/* Multi-turn conversation: keep listening without restarting wake word detection */
1046-
LOG_I("Multi-turn conversation enabled, restarting listening mode");
1047-
g_app.state = kDeviceStateListening;
1048-
xz_mic(1);
1049-
1050-
/* Try to send listen start */
1051-
if (ws_send_listen_start(&g_app.ws.clnt, g_app.ws.session_id, kListeningModeAutoStop))
1158+
/* Multi-turn conversation: start delay timer to restart listening after audio finishes */
1159+
if (tts_stop_delay_timer)
10521160
{
1053-
xiaozhi_ui_chat_status(" 聆听中");
1054-
xiaozhi_ui_chat_output("聆听中...");
1161+
LOG_D("Starting TTS stop delay timer (%d ms)", TTS_STOP_DELAY_MS);
1162+
rt_timer_start(tts_stop_delay_timer);
10551163
}
10561164
else
10571165
{
1058-
LOG_W("Listen start failed in TTS stop handler");
1059-
/* Reset state if failed */
1060-
g_app.state = kDeviceStateIdle;
1061-
xz_mic(0);
1062-
xiaozhi_ui_chat_status(" 就绪");
1063-
xiaozhi_ui_chat_output("就绪");
1166+
LOG_W("Delay timer not available, restarting listening immediately");
1167+
/* Fallback: restart immediately */
1168+
g_app.state = kDeviceStateListening;
1169+
xz_mic(1);
1170+
if (ws_send_listen_start(&g_app.ws.clnt, g_app.ws.session_id, kListeningModeAutoStop))
1171+
{
1172+
xiaozhi_ui_chat_status(" 聆听中");
1173+
xiaozhi_ui_chat_output("聆听中...");
1174+
}
1175+
else
1176+
{
1177+
LOG_W("Listen start failed in TTS stop handler");
1178+
g_app.state = kDeviceStateIdle;
1179+
xz_mic(0);
1180+
xiaozhi_ui_chat_status(" 就绪");
1181+
xiaozhi_ui_chat_output("就绪");
1182+
}
10641183
}
10651184
}
10661185
else
@@ -1093,29 +1212,40 @@ void Message_handle(const uint8_t *data, uint16_t len)
10931212
/* For multi-turn conversation, start a timeout timer to restart listening after sentence end */
10941213
if (g_app.multi_turn_conversation_enabled && g_app.state == kDeviceStateSpeaking)
10951214
{
1096-
/* Stop any existing timer first */
1215+
/* Use the pre-created timer: stop then start to reset timeout */
10971216
if (g_app.tts_sentence_end_timer)
10981217
{
1099-
rt_timer_stop(g_app.tts_sentence_end_timer);
1218+
rt_err_t stop_ret = rt_timer_stop(g_app.tts_sentence_end_timer);
1219+
if (stop_ret != RT_EOK)
1220+
{
1221+
LOG_W("rt_timer_stop returned %d when resetting TTS timer", stop_ret);
1222+
}
1223+
rt_err_t start_ret = rt_timer_start(g_app.tts_sentence_end_timer);
1224+
if (start_ret == RT_EOK)
1225+
{
1226+
LOG_D("Started TTS sentence end timer (%d ms), stop_ret=%d start_ret=%d", TTS_SENTENCE_TIMEOUT_MS, stop_ret, start_ret);
1227+
}
1228+
else
1229+
{
1230+
LOG_E("Failed to start existing TTS sentence end timer, start_ret=%d", start_ret);
1231+
}
11001232
}
11011233
else
11021234
{
1103-
/* Create timer if it doesn't exist */
1235+
/* Fallback: try to create and start the timer if it wasn't created earlier */
11041236
g_app.tts_sentence_end_timer = rt_timer_create("tts_end_timer",
1105-
tts_sentence_end_timeout,
1106-
RT_NULL,
1107-
rt_tick_from_millisecond(TTS_SENTENCE_TIMEOUT_MS),
1108-
RT_TIMER_FLAG_ONE_SHOT);
1109-
}
1110-
1111-
if (g_app.tts_sentence_end_timer)
1112-
{
1113-
rt_timer_start(g_app.tts_sentence_end_timer);
1114-
LOG_D("Started TTS sentence end timer (3s timeout)");
1115-
}
1116-
else
1117-
{
1118-
LOG_E("Failed to create TTS sentence end timer");
1237+
tts_sentence_end_timeout,
1238+
RT_NULL,
1239+
rt_tick_from_millisecond(TTS_SENTENCE_TIMEOUT_MS),
1240+
RT_TIMER_FLAG_ONE_SHOT);
1241+
if (g_app.tts_sentence_end_timer && rt_timer_start(g_app.tts_sentence_end_timer) == RT_EOK)
1242+
{
1243+
LOG_D("Created and started fallback TTS sentence end timer (%d ms)", TTS_SENTENCE_TIMEOUT_MS);
1244+
}
1245+
else
1246+
{
1247+
LOG_E("Failed to create/start fallback TTS sentence end timer");
1248+
}
11191249
}
11201250
}
11211251
}
@@ -1265,8 +1395,8 @@ char *get_xiaozhi_ws(void)
12651395
{
12661396
bytes_read = webclient_read(session, buffer + content_pos,
12671397
content_length - content_pos > GET_RESP_BUFSZ
1268-
? GET_RESP_BUFSZ
1269-
: content_length - content_pos);
1398+
? GET_RESP_BUFSZ
1399+
: content_length - content_pos);
12701400
if (bytes_read <= 0)
12711401
{
12721402
break;

projects/Edgi_Talk_M55_XiaoZhi/applications/xiaozhi/xiaozhi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ typedef struct
124124
int wakeword_initialized_session;
125125
rt_bool_t multi_turn_conversation_enabled; /* 多轮对话开关 */
126126
rt_timer_t tts_sentence_end_timer; /* TTS句子结束定时器 */
127+
struct rt_workqueue *tts_stop_workqueue; /* TTS停止工作队列 */
127128
} xiaozhi_app_t;
128129

129130
extern enum DeviceState g_state;

projects/Edgi_Talk_M55_XiaoZhi/edge-impulse/SConscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ LOCAL_CFLAGS += ' -DARM_MATH_CM55 -DARM_MATH_DSP -DARM_MATH_LOOPUNROLL -DARM_MAT
161161
LOCAL_CXXFLAGS += ' -DARM_MATH_CM55 -DARM_MATH_DSP -DARM_MATH_LOOPUNROLL -DARM_MATH_AUTOVECTORIZE'
162162

163163
# C++ flags
164-
LOCAL_CXXFLAGS += ' -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics'
164+
LOCAL_CXXFLAGS += ' -std=c++17 -fno-rtti -fno-exceptions -fno-threadsafe-statics'
165165
LOCAL_CXXFLAGS += ' -Wno-sign-compare -Wno-strict-aliasing'
166166

167167
# ============= Build target =============

projects/Edgi_Talk_M55_XiaoZhi/packages/opus-1.4/SConscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ PREBUILT_LIB = cwd + '/lib/libopus.a'
1717
USE_PREBUILT = os.path.exists(PREBUILT_LIB)
1818

1919
# Base definitions
20-
CPPDEFINES = ['HAVE_LRINTF', 'HAVE_ALLOCA_H', 'USE_ALLOCA', 'OPUS_BUILD', 'FIXED_POINT', 'DISABLE_FLOAT_API']
20+
CPPDEFINES = ['HAVE_LRINTF', 'HAVE_ALLOCA_H', 'USE_ALLOCA', 'OPUS_BUILD', 'FIXED_POINT=1', 'DISABLE_FLOAT_API']
2121

2222
# ARM Cortex-M55 optimizations (ARMv8.1-M with DSP extensions)
2323
# Enable ARM inline assembly optimizations - M55 supports ARMv5E/ARMv6 DSP instructions

projects/Edgi_Talk_M55_XiaoZhi/rtconfig.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
OBJDUMP = PREFIX + 'objdump'
4444
OBJCPY = PREFIX + 'objcopy'
4545

46-
DEVICE = ' -mcpu=cortex-m55 -mthumb -mfpu=fpv5-d16 --specs=nano.specs -mfloat-abi=hard -ffunction-sections -fdata-sections -nostartfiles'
46+
DEVICE = ' -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 --specs=nano.specs -mfloat-abi=hard -ffunction-sections -fdata-sections -nostartfiles'
4747
CFLAGS = DEVICE + ' -g -Wall -pipe'
4848
AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -Wa,-mimplicit-it=thumb '
4949
LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,Reset_Handler -T board/linker_scripts/link.ld -flto'

0 commit comments

Comments
 (0)