m_bootTime(0),
m_lastCat1RestartTime(0),
m_storyModeTimeoutTimer(nullptr),
- m_storyAudioPlaybackTimeoutTimer(nullptr),
- m_stressAudioLoopTimer(nullptr),
- m_networkConnectingReason(NetworkConnectingReason::NONE),
+ m_storyAudioPlaybackTimeoutTimer(nullptr),
+ m_stressAudioLoopTimer(nullptr),
+ m_speakingTtsWaitTimer(nullptr),
+ m_networkConnectingReason(NetworkConnectingReason::NONE),
m_networkConnectingTimeoutCount(0)
{
// 初始化时加载提醒数据
ThunderBirdModel::~ThunderBirdModel()
{
+ StopSpeakingTtsWaitTimer();
// 停止所有定时器
StopTimeDisplayTimer();
StopOtaCountdown();
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Count up timer stopped");
}
+
+ static void SpeakingTtsWaitTimerCallback(uint8_t data)
+ {
+ (void)data;
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind([]() {
+ ThunderBirdModel::GetInstance()->HandleSpeakingTtsWaitExpired();
+ }));
+ }
+
+ void ThunderBirdModel::StartSpeakingTtsWaitTimer(void)
+ {
+ if (m_stressTestDialogueActive) {
+ return;
+ }
+ ThunderBirdPresenter *pre = ThunderBirdPresenter::GetInstance();
+ if (pre == nullptr || !pre->isDialogueing) {
+ return;
+ }
+ StopSpeakingTtsWaitTimer();
+ m_speakingTtsWaitTimer =
+ osTimerNew((osTimerFunc_t)SpeakingTtsWaitTimerCallback, osTimerOnce, nullptr, nullptr);
+ if (m_speakingTtsWaitTimer == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StartSpeakingTtsWaitTimer: osTimerNew failed");
+ return;
+ }
+ static constexpr uint32_t kSpeakingTtsWaitMs = 12000;
+ osStatus_t ret = osTimerStart(m_speakingTtsWaitTimer, kSpeakingTtsWaitMs);
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StartSpeakingTtsWaitTimer: osTimerStart failed ret=%d", (int)ret);
+ (void)osTimerDelete(m_speakingTtsWaitTimer);
+ m_speakingTtsWaitTimer = nullptr;
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StartSpeakingTtsWaitTimer: %ums 内须收到服务端文本", (unsigned)kSpeakingTtsWaitMs);
+ }
+
+ void ThunderBirdModel::StopSpeakingTtsWaitTimer(void)
+ {
+ if (m_speakingTtsWaitTimer != nullptr) {
+ (void)osTimerStop(m_speakingTtsWaitTimer);
+ (void)osTimerDelete(m_speakingTtsWaitTimer);
+ m_speakingTtsWaitTimer = nullptr;
+ }
+ }
+
+ void ThunderBirdModel::HandleSpeakingTtsWaitExpired(void)
+ {
+ StopSpeakingTtsWaitTimer();
+ ThunderBirdMainView *view = ThunderBirdMainView::GetInstance();
+ if (view == nullptr) {
+ return;
+ }
+ if (view->GetCurrentView() != THUNDERBIRD_DIALOGUEVIEW ||
+ view->GetCurrentSubView() != ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING) {
+ return;
+ }
+ ThunderBirdPresenter *pre = ThunderBirdPresenter::GetInstance();
+ if (pre == nullptr || !pre->isDialogueing) {
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "HandleSpeakingTtsWaitExpired: 8s 内未收到文本,模拟语音键进入聆听");
+ pre->SimulateVoiceKeySpeakingToListening();
+ }
// ---------------------- 仅当当前在提醒界面时启动 3 秒退出定时器(供 LN_HTTP_SERVICE_CMD_AUDIO_STOP 使用,避免哄睡结束误触) ----------------------
void ThunderBirdModel::StartCountUpTimerForDialogueExitIfInSpeaking(void)
// 直接推送到对话界面
if(ThunderBirdDialogueView::GetInstance() && currentSubView == ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING)
{
+ if (!m_stressTestDialogueActive) {
+ StopSpeakingTtsWaitTimer();
+ }
ThunderBirdDialogueView::GetInstance()->UpdateDialogueText(textId);
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "第一个文本直接显示在对话界面: %s", textId);
}
currentSubView == ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING)) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleBindAndUnbind: 从对话模式跳转绑定成功,先退出对话模式");
StopCountUpTimer();
+ StopSpeakingTtsWaitTimer();
ThunderBirdPresenter::GetInstance()->SetDialogueing(false);
ln_ws_end_audio_chat();//结束对话
AI_ForceInterrupt();
currentSubView == ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING)) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleBindAndUnbind: 从对话模式跳转解绑警告/绑定成功,先退出对话模式");
StopCountUpTimer();
+ StopSpeakingTtsWaitTimer();
ThunderBirdPresenter::GetInstance()->SetDialogueing(false);
ln_ws_end_audio_chat();//结束对话
AI_ForceInterrupt();
if (currentSubView == ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
"ThunderBirdModel::HandleAudioRecordingStart: speaking 中收到录音开始,交由 VAD 流程处理,只切换到聆听界面");
+ StopSpeakingTtsWaitTimer();
auto* mainView = ThunderBirdMainView::GetInstance();
if (mainView && mainView->listenIcon) {
mainView->listenIcon->SetVisible(true);
if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::GetInstance()->listenIcon) {
ThunderBirdMainView::GetInstance()->listenIcon->SetVisible(false);
}
+ StartSpeakingTtsWaitTimer();
}
else if (currentView == THUNDERBIRD_LULLABYVIEW &&
currentSubView == ThunderBirdLullabyView::THUNDERBIRD_LULLABYVIEW_3 ) {
ThunderBirdModel::GetInstance()->StopTimeDisplayTimer();
ThunderBirdModel::GetInstance()->StopOtaCountdown();
ThunderBirdModel::GetInstance()->StopCountUpTimer();
+ ThunderBirdModel::GetInstance()->StopSpeakingTtsWaitTimer();
//ThunderBirdModel::GetInstance()->StopReminderHideTimer();
// 停止录音
// ==================== 辅助函数 ====================
+ void ThunderBirdPresenter::SimulateVoiceKeySpeakingToListening(void)
+ {
+ if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::IsSwitchingVideo()) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SimulateVoiceKeySpeakingToListening: 等待视频切换完成");
+ ThunderBirdMainView::WaitForVideoSwitchComplete(2000);
+ }
+ ThunderBirdModel::GetInstance()->StopCountUpTimer();
+ ThunderBirdModel::GetInstance()->StopSpeakingTtsWaitTimer();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SimulateVoiceKeySpeakingToListening: 与语音键一致,SPEAKING→LISTENING");
+ AI_ForceInterrupt();
+ ln_ws_end_audio_chat();
+ ln_ws_send_voice_chat_interrupt("新对话开始");
+ auto *dialogueView = ThunderBirdDialogueView::GetInstance();
+ if (dialogueView) {
+ dialogueView->IsReconnect = true;
+ }
+ auto *mainView = ThunderBirdMainView::GetInstance();
+ if (mainView && mainView->listenIcon) {
+ mainView->listenIcon->SetVisible(true);
+ }
+ ln_ws_set_vad_recognition_enabled(true);
+ if (mainView) {
+ const int16_t cv = mainView->GetCurrentView();
+ mainView->ChangeView(cv, THUNDERBIRD_DIALOGUEVIEW,
+ ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_LISTENING);
+ }
+ }
+
/**
* @brief 处理对话功能
*/
{
// 在对话界面进行按键操作时,取消退出对话的定时器(用户有操作,说明要继续对话)
ThunderBirdModel::GetInstance()->StopCountUpTimer();
+ ThunderBirdModel::GetInstance()->StopSpeakingTtsWaitTimer();
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "HandleDialogueViewNavigation: 取消退出对话定时器");
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "HandleDialogueFunction: currentView=%d, currentSubView=%d", currentView, currentSubView);
mainView->listenIcon->SetVisible(false);
}
ThunderBirdMainView::GetInstance()->ChangeView(currentView, THUNDERBIRD_DIALOGUEVIEW, ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING);
+ ThunderBirdModel::GetInstance()->StartSpeakingTtsWaitTimer();
} else if(currentView == THUNDERBIRD_DIALOGUEVIEW && currentSubView == ThunderBirdDialogueView::THUNDERBIRD_DIALOGUEVIEW_SPEAKING) {
// 检查 WebSocket 连接状态,如果正在重连,等待重连完成
// int wsState = ln_ws_get_connection_state();
// 先停止定时器,避免在退出过程中继续触发
ThunderBirdModel::GetInstance()->StopCountUpTimer();
+ ThunderBirdModel::GetInstance()->StopSpeakingTtsWaitTimer();
ln_ws_end_audio_chat();//结束对话