非同步語音通知(notify)

notify 訊息允許雲端在裝置空閒時播放一條單向語音通知。它不會開啟對話音訊通道,也不會開啟麥克風上行。要開始對話,仍然需要使用者明確的喚醒動作。

訊息格式

雲端透過裝置當前協議的控制連線傳送下面的 JSON。對於 MQTT 裝置,就是現有的 MQTT 控制主題:

{
  "type": "notify",
  "audio_url": "https://cdn.example.com/audio/task-complete.ogg",
  "subtitles": [
    {
      "start_ms": 0,
      "text": "Your task is complete."
    },
    {
      "start_ms": 1800,
      "text": "The result has been saved."
    }
  ]
}

audio_url 為必填,http:// 和 https:// 均可接受。HTTP 便於區域網開發,生產環境可以在生成 URL 時強制使用 HTTPS。

subtitles 為可選。每個條目包含媒體起始時間(毫秒)和要顯示的文字。裝置按 start_ms 排序,僅在播放進度越過新的字幕條目時更新顯示。

該訊息沒有確認、通知 ID、狀態、型別或過期欄位。投遞是盡力而為的,且只對線上裝置生效。

音訊響應

audio_url 必須返回成功的 2xx 響應,內容為單流、單聲道的 Ogg Opus 檔案。支援帶 Content-Length 或分塊傳輸編碼的響應。裝置不會跟隨重定向。

檔案按增量方式讀取。裝置不會按完整響應長度分配記憶體,也不會在播放前下載完整檔案。通知流式播放使用現有的有界 HTTP 響應佇列、2 KB 的 Ogg 邏輯包緩衝、共享的 20 包 Opus 解碼佇列以及現有的兩幀 PCM 播放佇列。在韌體預設的 60 ms 包時長下,解碼佇列相當於 1.2 秒。這些緩衝提供 TCP 反壓,使該功能可以在沒有 PSRAM 的裝置上使用。

在使用獨立 LiteAudioEngine WakeNet 的裝置上,通知播放期間會釋放 WakeNet 資源,裝置回到 Idle 時再重新建立。基於 AFE 的裝置保持原有的本地喚醒行為。

Opus 包時長從 Opus TOC 位元組讀取,而不是由 MQTT 訊息提供。韌體解碼器支援的 5 ms 至 120 ms 整數包時長均可接受。當前實現會拒絕立體聲流、檢測到的 Ogg 或 Opus 結構錯誤、不完整的流、超長的邏輯包以及 2.5 ms 的包。

裝置行為

裝置僅在 Idle 狀態下接受 notify,隨後執行以下動作:

  1. 進入內部的 Notifying 狀態,並將開發板切換到效能模式。
  2. 關閉常規語音處理和麥克風上行。
  3. 清除之前的播放內容,並將內建提示音加入佇列。
  4. 在後臺任務中發起一次 HTTP GET。
  5. 增量解複用 Ogg 包,並直接送入現有的 Opus 解碼佇列。
  6. 按照到達音訊輸出任務的 Opus 包的媒體位置顯示字幕。
  7. 僅在 HTTP 流成功結束且所有排隊音訊播放完畢後才回到 Idle。

提示音在 HTTP 任務啟動之前就已入隊,因此遠端音訊不會早於它播放。HTTP 連線建立仍會與提示音的實際播放重疊。

如果在遠端音訊開始播放後、HTTP 流結束前,所有播放佇列都被清空,裝置會記錄日誌 Notification playback underrun #<count> at <position> ms。從提示音到流的切換以及正常播放結束,都不會被報告為欠載。

裝置不會開啟 UDP 音訊通道、傳送 start-listening,也不會自動進入 Listening。基於 AFE 的裝置在播放期間可以繼續本地喚醒詞檢測。沒有播放回聲消除的裝置保持現有的說話模式喚醒行為;硬體喚醒控制仍然可以取消通知。

喚醒動作會取消 HTTP 生產者、清除已排隊的通知音訊,並繼續走正常的喚醒流程。網路中斷、HTTP 錯誤和無效音訊同樣會取消播放並讓裝置回到 Idle。裝置忙碌時收到的第二條通知會被忽略。

xz-mqtt 轉發

xz-mqtt 透過其現有的 Redis RPC 方法轉發該訊息:

{
  "method": "forward",
  "clientId": "device-client-id",
  "params": {
    "type": "notify",
    "audio_url": "https://cdn.example.com/audio/task-complete.ogg",
    "subtitles": [
      {
        "start_ms": 0,
        "text": "Your task is complete."
      }
    ]
  }
}

RPC 結果包含 MQTT 傳送操作返回的布林值:

{
  "success": true
}

該結果表示 xz-mqtt 已把釋出訊息寫入當前線上的裝置連線,並不確認裝置已收到或已播放。xz-mqtt 不會建立 UDP 會話、不會啟動對話橋接、不會代理 Ogg 檔案,也不會為離線裝置保留通知。

來源:78/xiaozhi-esp32 的 docs/notify.md