音声オーバーレイのライフサイクル(macOS)
対象読者:macOS アプリのコントリビューター。目標:ウェイクワードとプッシュトゥトークが重なったときでも、音声オーバーレイが予測可能に動作するようにする。動作
- ウェイクワードによってオーバーレイがすでに表示されている状態でユーザーがホットキーを押すと、ホットキーセッションはテキストをリセットせず、既存のテキストを引き継ぐ。ホットキーを押している間、オーバーレイは表示されたままになる。キーを離した時点で、前後の空白を除去したテキストがあれば送信し、なければ閉じる。
- ウェイクワードのみの場合は、引き続き無音を検出すると自動送信する。プッシュトゥトークの場合は、キーを離すと即座に送信する。
実装
VoiceSessionCoordinator(apps/macos/Sources/OpenClaw/VoiceSessionCoordinator.swift)は、アクティブな音声セッションを一元管理する。これはアクターではなく、@MainActor @Observableシングルトンである。API:startSession、updatePartial、finalize、sendNow、dismiss、updateLevel、snapshot。各セッションはUUIDトークンを保持し、古いトークンまたは一致しないトークンを使用した呼び出しは破棄される。VoiceWakeOverlayController(VoiceWakeOverlayController+Session.swift)はオーバーレイを描画し、ユーザー操作(requestSend、dismiss)をセッショントークン経由でコーディネーターに転送する。セッション状態自体を管理することはない。- プッシュトゥトーク(
VoicePushToTalk.begin())は、表示中のオーバーレイにあるテキストを(VoiceSessionCoordinator.shared.snapshot()経由で)adoptedPrefixとして引き継ぐ。そのため、ウェイクオーバーレイの表示中にホットキーを押してもテキストが維持され、新しい発話が追加される。キーを離すと、最終文字起こしを最大 1.5s 待ってから、現在のテキストを使用する。 dismissの際、オーバーレイはVoiceSessionCoordinator.overlayDidDismissを呼び出し、それによってVoiceWakeRuntime.refresh(state:)がトリガーされる。このため、X による手動での閉じる操作、空テキストによる終了、送信後の終了のいずれの場合も、ウェイクワードの待ち受けが再開される。- 統一された送信経路:前後の空白を除去したテキストが空なら閉じる。それ以外の場合は、
sendNowが送信チャイムを一度再生し、VoiceWakeForwarder経由で転送してから閉じる。
ログ
音声サブシステムはai.openclaw であり、各コンポーネントはそれぞれのカテゴリにログを記録する。
デバッグチェックリスト
-
オーバーレイが消えずに残る問題を再現しながら、ログをストリーミングする。
- アクティブなセッショントークンが 1 つだけであることを確認する。古いコールバックはコーディネーターによって破棄される。
-
プッシュトゥトークのキーを離した際に、アクティブなトークンを指定して必ず
end()が呼び出されることを確認する。テキストが空の場合は、チャイムの再生や送信を行わずに閉じることが期待される。