#ifndef _APPLICATION_H_ #define _APPLICATION_H_ #include #include #include #include #include #include #include #include #include #include #include #include "protocol.h" #include "websocket_protocol.h" #include "ota.h" #include "background_task.h" #include "audio/simple_pipeline.h" // #include "ble_service.h" // BLE JSON Service 暂不使用 #if CONFIG_USE_WAKE_WORD_DETECT #include "wake_word_detect.h" #elif CONFIG_USE_CUSTOM_WAKE_WORD #include "custom_wake_word.h" #endif #if CONFIG_USE_AUDIO_PROCESSOR #include "audio_processor.h" #endif #define SCHEDULE_EVENT (1 << 0) #define AUDIO_INPUT_READY_EVENT (1 << 1) #define AUDIO_OUTPUT_READY_EVENT (1 << 2) // 未知状态、启动中、WiFi配网模式、空闲待命、连接服务器、语音监听中、语音播报中、固件升级中、设备激活中、致命错误 enum DeviceState { kDeviceStateUnknown, kDeviceStateStarting, kDeviceStateWifiConfiguring, kDeviceStateIdle, kDeviceStateConnecting, kDeviceStateListening, kDeviceStateSpeaking, kDeviceStateDialog, kDeviceStateUpgrading, kDeviceStateActivating, kDeviceStateFatalError }; // OPUS音频帧时长(60ms) #define OPUS_FRAME_DURATION_MS 60 // 应用程序主类(单例模式) class Application { public: static Application& GetInstance() { static Application instance; return instance; } // 删除拷贝构造函数和赋值运算符 Application(const Application&) = delete; Application& operator=(const Application&) = delete; void Start(); // 启动应用程序 DeviceState GetDeviceState() const { return device_state_; } // 获取当前状态 bool IsVoiceDetected() const { return voice_detected_; } // 语音检测状态 void Schedule(std::function callback); // 任务调度 void SetDeviceState(DeviceState state); // 状态变更 void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = "");// 警报管理 状态、消息、情感、声音 void DismissAlert();// 关闭警报 void AbortSpeaking(AbortReason reason);// 打断语音播报 void AbortHttpsPlayback(const char* reason);// 中止HTTPS音频播放并清空DMA void SendStoryRequest(); // 通过HTTPS故事API请求并播放故事 void SendMusicRequest(); // 通过HTTPS音乐API请求并播放音乐 void HttpsPlaybackFromUrl(const std::string& url); // 通过HTTPS下载JSON并播放音频(故事/歌曲等) void ToggleChatState();// 切换聊天状态 void ToggleListeningState();// 切换监听状态 void StartListening();// 开始监听 void StopListening();// 停止监听 void SendTextMessage(const std::string& text);// 发送文本消息 void UpdateIotStates();// 更新IOT设备状态 void Reboot();// 系统重启 void WakeWordInvoke(const std::string& wake_word);// 唤醒词回调 void PlaySound(const std::string_view& sound);// 播放声音 void WaitForAudioPlayback();// 等待音频播报完成 bool IsAudioQueueEmpty(); // 检查音频队列是否为空 void ClearAudioQueue(); // 清空音频播放队列 bool CanEnterSleepMode();// 检查是否可以进入睡眠模式 // Phase 6 移植:RTC 软退出 / 唤醒 void EnterIdleHibernate(); // 进入空闲休眠(真退房 + 待命音 + 状态保留) void WakeFromHibernate(); // 从休眠唤醒(BOOT 触发,重连 RTC) bool IsHibernating() const { return hibernating_.load(); } void StopAudioProcessor();// 停止音频处理器 void ResetDecoder();// 重置解码器状态(用于修复音频播放问题) bool IsSafeToOperate(); // 🔧 检查当前是否可以安全执行操作 void AbortSpeakingAndReturnToIdle(); // 🔴 专门处理从说话状态到空闲状态的切换 void AbortSpeakingAndReturnToListening(); // 🔵 专门处理从说话状态到聆听状态的切换 void PauseAudioPlayback(); // ⏸️ 暂停音频播放 void ResumeAudioPlayback(); // ▶️ 恢复音频播放 void SuppressNextIdleSound(); // 🔇 抑制下一个空闲状态的声音播放 void SetLowBatteryTransition(bool value); bool IsLowBatteryTransition() const; void InitializeWebsocketProtocol(); // 🌐 初始化WebSocket协议(RTC连接成功后调用) // void SendTextViaWebsocket(const std::string& text);// 🌐 通过WebSocket发送文本消息 // 姿态传感器接口 bool IsImuSensorAvailable(); // 检查IMU传感器是否可用 bool GetImuData(float* acc_x, float* acc_y, float* acc_z, float* gyro_x, float* gyro_y, float* gyro_z, float* temperature); // 获取IMU传感器数据 void OnMotionDetected(); // 运动检测事件处理 bool IsAudioPaused() const { return audio_paused_; } // 检查音频是否暂停 bool ShouldSkipDialogIdleSession() const { return skip_dialog_idle_session_; }// 是否跳过对话待机会话 void ClearDialogIdleSkipSession();// 清除对话待机会话标志位 bool IsDialogUploadEnabled() const { return dialog_upload_enabled_; }// 是否启用对话上传 void SetDialogUploadEnabled(bool enabled);// 设置对话上传状态 // // BLE JSON 命令处理(暂不使用) // void HandleBleJsonCommand(const std::string& cmd, int msg_id, cJSON* data, BleJsonService& service); private: void HttpsApiPlayback(const char* api_url, const char* tag, const char* task_name);// HTTPS API音频播放通用实现 Application();// 构造函数 ~Application();// 析构函数 // 配置使用唤醒词检测 #if CONFIG_USE_WAKE_WORD_DETECT WakeWordDetect wake_word_detect_; #elif CONFIG_USE_CUSTOM_WAKE_WORD CustomWakeWord wake_word_detect_; #endif // 音频处理器 #if CONFIG_USE_AUDIO_PROCESSOR AudioProcessor audio_processor_; #endif Ota ota_; std::mutex mutex_; std::list> main_tasks_; std::unique_ptr protocol_; std::unique_ptr websocket_protocol_; // 🌐 WebSocket协议实例(RTC连接后初始化) EventGroupHandle_t event_group_ = nullptr; esp_timer_handle_t clock_timer_handle_ = nullptr; volatile DeviceState device_state_ = kDeviceStateUnknown; std::atomic is_aborting_{false}; // 🔧 原子标志:防止重复中止操作 std::atomic last_safe_operation_; // 🔧 最后安全操作时间戳 std::atomic is_switching_to_listening_{false}; // 🔵 标志:正在主动切换到聆听状态 std::atomic is_low_battery_transition_{false}; // Phase 6 移植:空闲休眠状态 std::atomic hibernating_{false}; // 是否处于空闲休眠状态 int idle_cycles_ = 0; // 累计休眠次数(NVS 持久化) static constexpr int IDLE_CYCLES_REBOOT_THRESHOLD = 50; // 累计 50 次触发硬重启清碎片 void SaveIdleCyclesToNvs(); void LoadIdleCyclesFromNvs(); void ResetIdleCyclesNvs(); ListeningMode listening_mode_ = kListeningModeAutoStop; #if CONFIG_USE_REALTIME_CHAT bool realtime_chat_enabled_ = true; #else bool realtime_chat_enabled_ = false; #endif std::atomic ws_downlink_enabled_{true};// 🌐 WebSocket下行通道是否启用 std::atomic opus_playback_active_{false};// 🌐 Opus解码播放活跃标志(WS/HTTPS共用) std::atomic https_playback_active_{false};// 🌐 HTTPS音频播放进行中标志 std::atomic https_playback_abort_{false};// 🌐 HTTPS音频播放中止标志 bool aborted_ = false; bool voice_detected_ = false; bool audio_paused_ = false; // 音频暂停状态标志 float current_speaker_volume_ = 0.0f; // 当前扬声器音量,用于语音打断判断 bool first_idle_location_checked_ = false;// 是否首次查询城市天气 bool send_pcm_uplink_ = true; // 是否发送PCM音频数据到服务器,由SDK内部转码为G711A bool send_g711a_uplink_ = false;// 是否直接发送G711A音频数据到服务器 std::chrono::time_point last_audio_input_time_; std::chrono::time_point last_audible_output_time_; // 最后一次有声音输出的时间点 bool skip_dialog_idle_session_; // 是否跳过对话待机会话标志 bool dialog_upload_enabled_ = true; // 对话上传状态标志 bool dialog_watchdog_running_; // 对话看门狗运行标志 int dialog_watchdog_last_logged_; // 对话看门狗上次记录的日志时间 TaskHandle_t dialog_watchdog_task_handle_; // 对话看门狗任务句柄 int clock_ticks_; TaskHandle_t main_loop_task_handle_; TaskHandle_t check_new_version_task_handle_; // Audio encode / decode TaskHandle_t audio_loop_task_handle_; BackgroundTask* background_task_; std::chrono::steady_clock::time_point last_output_time_; std::list> audio_decode_queue_; std::unique_ptr opus_encoder_; std::unique_ptr opus_decoder_; OpusResampler input_resampler_;// 输入音频采样器 OpusResampler reference_resampler_;// 参考音频采样器 OpusResampler output_resampler_;// 输出音频采样器 OpusResampler uplink_resampler_;// 上传音频采样器 player_pipeline_handle_t player_pipeline_ = nullptr; recorder_pipeline_handle_t recorder_pipeline_ = nullptr; // 路径 D'' AEC: esp_aec.h 底层同步 API + 软件 loopback ref // codec 保持 baseline 1ch 16-bit (MIC1|MIC2 ES7210 内部混合 mono) // DAC 输出 PCM 同步复制到 ref_ring_buf, ReadAudio 调 aec_process(mic, delayed_ref) → clean void *aec_handle_ = nullptr; // aec_handle_t* (避免暴露 esp_aec.h 类型) int aec_chunk_size_ = 0; // aec_get_chunksize 返回 (16k 通常 512 samples = 32ms) int16_t *ref_ring_buf_ = nullptr; // PSRAM 上分配 ~200ms ref ring buffer int ref_ring_capacity_ = 0; // ring buf 容量 (samples) int ref_ring_write_idx_ = 0; // 写指针 (OnAudioOutput 推进) int ref_ring_filled_ = 0; // 已写入样本累计 (用于判断是否足够延迟补偿) int aec_ref_delay_samples_ = 800; // 延迟补偿 samples (默认 50ms @16kHz, 后续调优) // ⚠️ portMUX (spinlock) 会禁用本核中断, 与 WiFi 协议栈 pm_coex_set_reconnect_policy 冲突 // 实测引发 IllegalInstruction panic。改用 FreeRTOS mutex (不禁中断, 仅 task 间互斥) SemaphoreHandle_t ref_ring_mutex_ = nullptr; void InitAec(); void DeinitAec(); // 把 DAC 输出 PCM (16kHz mono 16-bit) 推入 ref ring buffer void AppendRefSamples(const int16_t *pcm, int samples); // 从 ref ring buffer 取 delayed ref (mic 同步用) void GetDelayedRef(int16_t *ref_out, int samples); // 对单 chunk_size mic PCM 调 aec_process, 输出 clean PCM // 累积不足 chunk_size 时直接 passthrough void ApplyAEC(std::vector& mic_inout); void MainLoop();// 主事件循环 void OnAudioInput();// 音频输入回调 void OnAudioOutput();// 音频输出回调 void ReadAudio(std::vector& data, int sample_rate, int samples);// 读取音频数据 void SetDecodeSampleRate(int sample_rate, int frame_duration);// 设置解码采样率 void CheckNewVersion();// 检查新固件版本 void ShowActivationCode();// 显示激活码 void OnClockTimer();// 时钟定时器回调 void SetListeningMode(ListeningMode mode);// 设置监听模式 void AudioLoop();// 音频处理循环 bool suppress_next_idle_sound_ = false;// 标志:是否抑制下一个空闲状态的声音播放 void StartDialogWatchdog();// 启动对话看门狗 void StopDialogWatchdog(); // 停止对话看门狗 const char* DeviceStateToString(DeviceState state); // 状态枚举转字符串 }; #endif // _APPLICATION_H_