音声オーバーレイライフサイクル(macOS)
対象者: macOS アプリコントリビューター。目標: ウェイクワードとプッシュ・トゥ・トークが重なる場合に音声オーバーレイを予測可能に保つ。
現在の意図
- オーバーレイがウェイクワードから既に表示されていて、ユーザーがホットキーを押した場合、ホットキーセッションはリセットする代わりに既存のテキストを採用します。オーバーレイはホットキーが押されている間表示されたままです。ユーザーが解放したとき: トリミングされたテキストがあれば送信、そうでなければ閉じます。
- ウェイクワード単独では沈黙時に自動送信します。プッシュ・トゥ・トークは解放時に即座に送信します。
実装済み(2025年12月9日)
- オーバーレイセッションは、キャプチャごとにトークンを持つようになりました(ウェイクワードまたはプッシュ・トゥ・トーク)。トークンが一致しない場合、partial/final/send/dismiss/level 更新はドロップされ、古いコールバックを回避します。
- プッシュ・トゥ・トークは、表示されているオーバーレイテキストをプレフィックスとして採用します(ウェイクオーバーレイが表示されている間にホットキーを押すと、テキストを保持して新しいスピーチを追加します)。現在のテキストにフォールバックする前に、最終トランスクリプトを最大1.5秒待ちます。
- チャイム/オーバーレイロギングは、カテゴリ voicewake.overlay, voicewake.ptt, voicewake.chime で info レベルで出力されます(セッション開始、partial、final、send、dismiss、チャイム理由)。
次のステップ
- VoiceSessionCoordinator (actor)
- 一度に正確に1つの VoiceSession を所有します。
- API(トークンベース): beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown。
- 古いトークンを持つコールバックをドロップします(古い認識器がオーバーレイを再オープンするのを防ぎます)。
- VoiceSession (model)
- フィールド: token, source (wakeWord|pushToTalk)、コミット/揮発性テキスト、チャイムフラグ、タイマー(自動送信、アイドル)、overlayMode (display|editing|sending)、クールダウン期限。
- オーバーレイバインディング
- VoiceSessionPublisher (ObservableObject) はアクティブなセッションを SwiftUI にミラーリングします。
- VoiceWakeOverlayView はパブリッシャー経由でのみレンダリングします。グローバルシングルトンを直接変更することはありません。
- オーバーレイユーザーアクション(sendNow, dismiss, edit)は、セッショントークンを使用してコーディネーターにコールバックします。
- 統一送信パス
- endCapture 時: トリミングされたテキストが空の場合 → 閉じる。そうでなければ performSend(session:)(送信チャイムを一度再生、転送、閉じる)。
- プッシュ・トゥ・トーク: 遅延なし。ウェイクワード: 自動送信のためのオプションの遅延。
- プッシュ・トゥ・トーク終了後、ウェイク実行時に短いクールダウンを適用して、ウェイクワードが即座に再トリガーしないようにします。
- ロギング
- コーディネーターは、サブシステム bot.molt、カテゴリ voicewake.overlay と voicewake.chime で .info ログを出力します。
- 主要イベント: session_started, adopted_by_push_to_talk, partial, finalized, send, dismiss, cancel, cooldown。
デバッグチェックリスト
-
固定オーバーレイを再現しながらログをストリーミング:
sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact -
アクティブなセッショントークンが1つだけであることを確認。古いコールバックはコーディネーターによってドロップされるはずです。
-
プッシュ・トゥ・トーク解放が常にアクティブなトークンで endCapture を呼び出すことを確認。テキストが空の場合、チャイムや送信なしの dismiss を期待します。
移行ステップ(提案)
- VoiceSessionCoordinator, VoiceSession, VoiceSessionPublisher を追加します。
- VoiceWakeRuntime をリファクタリングして、VoiceWakeOverlayController に直接触れる代わりにセッションを作成/更新/終了します。
- VoicePushToTalk をリファクタリングして既存のセッションを採用し、解放時に endCapture を呼び出します。ランタイムクールダウンを適用します。
- VoiceWakeOverlayController をパブリッシャーに配線します。ランタイム/PTT からの直接呼び出しを削除します。
- セッション採用、クールダウン、および空テキスト閉じのための統合テストを追加します。