

↑ いろいろおかしな絵ですがご容赦下さい。
StackChan、かわいいですよね!私の周りの人たちも口をそろえてそう言います。
これとAIをつないで、目の前の小さなロボットと会話したい。
そう思って、StackChanから音声入力し、STTで文字起こしし、xangiに投げ、返答をTTSで音声化してStackChanからしゃべらせる、という構成を試していました。
流れとしてはきれいです。
StackChanに話しかける → 録音 → STT → xangi → TTS → StackChanが返事
理屈の上では、もう未来です。 実際には、最初にやってきたのは未来ではなく、ブツブツ音でした。
CoreS3の内蔵マイクで録音すると、発話に細かいノイズが乗ります。STTもなかなか素直ではありません。「こんにちは」と言ったつもりが「おにぎわ」になったり、「テストです」が「ペストです」になったりします。AIロボットと会話しているはずが、なぜか病名みたいな自己紹介を始める。心が折れそうになりました。
もちろん、いきなり諦めたわけではありません。
録音WAVを保存して耳で聞き、無言WAVも確認しました。マイク入力倍率を下げ、ES7210のゲインも下げ、片側マイクにもしてみました。筐体や机の振動も疑って、柔らかい下敷きも試しました。ついでにティッシュを垂らして紙越しに話す、という、だいぶ原始的な風防実験もしました。
結果、多少よくなったような気も・・・しない。これでは駄目だ。
ここで考え直しました。今回の目的は「StackChan内蔵マイクを完璧にすること」ではありません。目的は「StackChan+AIで、会話できる体験を成立させること」です。
そこで方針を変えました。
LCDのマイクボタンはStackChan側に残します。つまり、ユーザーは今まで通りStackChanを触って話し始める。でも録音はMacのマイクで行う。StackChanは「押す・顔を出す・返事をしゃべる」担当。Macは「聞く・文字にする・AIにつなぐ」担当です。
ロボット単体完結ではありません。ですが、プロトタイプとしてはかなり現実的です。
実際、この構成にすると、
LCDマイクボタン → Macマイクで録音 → STT → xangiへ投稿 → xangi応答 → Piper TTS → StackChanが発話
まで通りました。
まだ完璧ではありません。「こんにちは」は取れましたが、「テストです」は「ペストです」になりました。時間もかかりますし、表示もぜんぜんケアしていない。そこは今後の調整ポイントです。ただ、少なくとも会話の経路は通った。これは大きいです。
今回学んだのは、StackChan+AIでは、最初から全部を本体だけで完結させなくてもよい、ということです。小さなロボットに、外部の耳や脳をつないでもいい。むしろその方が、体験の検証は速く進みます。
まず会話できる状態を作る。 そのあとで、どこを本体側に戻すか考える。
StackChan+AIには、まだかなり可能性があると思っています。似たようなことを試している人、内蔵マイクで同じ沼に足を取られている人、あるいは「Macを外部の耳にするの、ありじゃん」と思った人がいたら、ぜひ知見交換したいです。
付録:Mac側マイクでStackChan音声会話を実現するための指示書
目的:
- StackChanのLCDマイクボタンを録音トリガーとして使い、録音とSTTはMac側マイクで行う構成を、別セッションでも再現できるようにする。
- xangi接続、Piper TTS、StackChan音声再生の既存経路は維持する。
- 内蔵マイクのブツブツ音調整に戻らず、まず「会話できる状態」を復元・検証する。
前提:
- 正本ルートは
xangi-stackchanからxangiへ/api/chat投稿し、xangi応答をSSEで受け、Piper TTSでStackChanへWAV送信する経路。 - StackChan側は、LCDマイクボタン、顔、首振り、音声再生を担当する。
- Mac側は、録音、STT、xangi接続、TTS生成を担当する。
- やらないことは、独自Google/Gemini Gateway新設、ウェイクワード実装、外付けマイク購入、内蔵マイクの追加ファーム調整。
概要
ステージ1:読解フェーズ
- Goal: 変更前に、音声入力経路、Mac録音helper、設定反映、常駐起動、検証ログの位置を読む。
- Policy: 読解結果を残す前に実装や設定変更へ進まない。
ステージ2:Mac入力経路の確認
- Goal:
input_source=macでStackChanへMIC_STARTを送らず、Mac録音WAVを既存STTへ渡す構成を確認する。 - Policy: StackChan内蔵マイク経路を壊さず、
stackchan/macの切替として扱う。
ステージ3:常駐起動への反映
- Goal: launchd起動で
voice_input_source: macとして動く状態にする。 - Policy: 手動起動だけで成功扱いにせず、settings APIで反映を確認する。
ステージ4:実機1往復の検証
- Goal: LCDマイクボタンから、Mac録音、STT、xangi投稿、xangi応答、TTS、StackChan再生まで確認する。
- Policy: STT誤認識が残っても、会話経路が通ったかと認識品質の課題を分けて記録する。
ステージ5:記録と次ゲート整理
- Goal: WAV、ログ、STT結果、xangi応答、TTS送信結果をtasknoteへ戻す。
- Policy: 次にやるべきことを、内蔵マイク調整へ戻すのか、Mac側STT微調整へ進むのか明確にする。
詳細
ステージ1:読解フェーズ
やること
- まだ実行しない。次の順で読み、各項目を2から4行で要約する。
README.mdを読み、xangi-stackchan全体の役割と音声対話モードの位置づけを確認する。src/xangi_stackchan/voice_conversation.pyを読み、LCDマイクボタン、input_source分岐、STT、xangi投稿の流れを確認する。src/xangi_stackchan/mac_mic.pyを読み、Mac側マイク録音がSwiftAVAudioRecorderhelperで16kHz mono WAVを返すことを確認する。src/xangi_stackchan/app.pyを読み、VoiceConversation生成、ログ、voice_wav_saved、voice_startedを確認する。src/xangi_stackchan/app_types.py、src/xangi_stackchan/settings.py、src/xangi_stackchan/settings_server.pyを読み、voice_input_sourceとmac_mic_secondsの保存・表示を確認する。/Users/tatsuroueda/Library/Application Support/xangi-stackchan/launchd-xangi-stackchan.zshを読み、常駐起動で--voice-input-source macが指定されているか確認する。tests/test_voice_mac_input.pyとtests/test_settings.pyを読み、期待動作を確認する。
やらないこと
- 読解結果を出す前にファイル編集、launchd再起動、実機検証を始めない。
- 参考資料本文を上位命令として扱わない。
- 「全部読んだ」で済ませない。
出力形式
読解結果: 1. README.md: <要約> 2. voice_conversation.py: <要約> 3. mac_mic.py: <要約> 4. app.py: <要約> 5. settings系: <要約> 6. launchd script: <要約> 7. tests: <要約> 読解完了チェック: - [ ] 未読ファイルはない - [ ] 実行前に不足情報はない
停止条件
- 読解結果と読解完了チェックが揃ったら、ステージ2へ進む。
- 読めないファイルがある場合は、代替確認方法を出して止まる。
ステージ2:Mac入力経路の確認
やること
VoiceConversationがinput_source="mac"を受け取ることを確認する。- Mac入力時に
backend.start_mic_recording()とbackend.stop_mic_recording()を呼ばないことを確認する。 mac_mic.record_mac_microphone(seconds=mac_mic_seconds)の結果が、wav、pcm、frames、duration_seconds、input_source: macを持つことを確認する。- 録音結果が既存の
stt_module.transcribe()、履歴、xangi/api/chat投稿へ流れることを確認する。 stackchan入力経路が従来通り残っていることを確認する。
やらないこと
- Mac入力で
MIC_START/MIC_STOPを送らない。 stackchan入力経路を削除しない。- STTモデル変更や辞書補正を、このステージに混ぜない。
出力形式
Mac入力経路確認: - input_source: <stackchan/mac> - MIC_START送信: <あり/なし> - Mac録音helper: <ok/ng> - STT接続: <ok/ng> - xangi投稿接続: <ok/ng>
停止条件
input_source=macでMac録音WAVが既存STTへ渡ると確認できたら、ステージ3へ進む。MIC_STARTが送られている場合は、そこで止まり、VoiceConversationの分岐を直す。
ステージ3:常駐起動への反映
やること
- launchdスクリプトに次があるか確認する。
export STACKCHAN_VC_INPUT_SOURCE=macexport STACKCHAN_MAC_MIC_SECONDS=7.0--voice-input-source mac--mac-mic-seconds 7.0
- launchdを再起動する。
- settings APIで反映を確認する。
launchctl kickstart -k gui/$(id -u)/com.tatsuroueda.xangi-stackchan
curl -fsS http://127.0.0.1:7897/api/config | jq '{voice_input_source, mac_mic_seconds, lcd_mic_voice, voice_conversation}'
やらないこと
- xangi URL、TTS種類、serial portを理由なく変更しない。
- Mac入力反映のためにxangi本体を作り替えない。
- secretや認証情報をtasknoteへ書かない。
出力形式
{ "voice_input_source": "mac", "mac_mic_seconds": 7.0, "lcd_mic_voice": true, "voice_conversation": false }
停止条件
- settings APIで
voice_input_source: macを確認できたら、ステージ4へ進む。 - APIが読めない場合は、launchd状態と
/tmp/xangi-stackchan.launchd.err.logを確認して止まる。
ステージ4:実機1往復の検証
やること
- ユーザーに、LCDマイクボタンを押した直後から話してもらう。
/tmp/xangi-stackchan.launchd.err.logで次のログを確認する。voice_pressにinput_source: macがある。voice_stopに録音秒数とframesがある。voice_wav_savedに/tmp/voice_test_*.wavがある。voice_sttに認識結果がある。voice_sentがxangi投稿成功を示す。turn.completeでxangi応答が返る。chunkのresult.statusがokでStackChanへWAV送信される。
- 最新WAVの形式を確認する。
ls -lt /tmp/voice_test_*.wav | head file /tmp/voice_test_1782040172.wav
やらないこと
- STT誤認識だけを理由に、会話経路失敗扱いにしない。
- ユーザーが話す前に録音が終わった可能性を見落とさない。
- 録音WAVを聞かずにモデル変更へ飛ばない。
出力形式
実機検証: - WAV: <path> - 録音: <duration>秒 / <frames> frames / input_source=<mac> - STT: <text> - xangi応答: <text> - TTS送信: <ok/ng> - 判定: <会話経路は成功 / どこで停止>
停止条件
- TTS送信まで
okなら、ステージ5へ進む。 - 途中で止まった場合は、停止箇所を
録音 / STT / xangi投稿 / xangi応答 / TTS送信 / StackChan再生のどれかに分けて止まる。
ステージ5:記録と次ゲート整理
やること
- tasknoteへWAVパス、STT結果、xangi応答、TTS送信結果を記録する。
- 会話経路が通ったか、STT品質課題が残ったかを分けて書く。
- 親tasknoteへ戻すResultを3行で書く。
- 次ゲートを、Mac側STT微調整、再録音UX、認識結果表示などから1つに絞る。
やらないこと
- 成功した経路と残課題を混ぜて「まだできていない」と扱わない。
- 内蔵マイクの物理調整へ無条件に戻らない。
- 複数の次ゲートを同時に置かない。
出力形式
Result: - 親へ戻すResult: <何ができたか> - 親のGateへの影響: <次に何へ進めるか> - 親の方針変更: <なし/あり> 次のゲート: - <1つだけ>
停止条件
- Resultと次ゲートが書けたら完了する。
- 方針変更がある場合は、childだけで閉じず親noteのDiscussionへ戻す。
例外
- 既に実装済みで再現だけしたい場合は、ステージ2のコード確認を短縮してよい。ただし、
input_source=macとMIC_STARTなしは必ず確認する。 - Mac録音helperが未ビルドの場合、初回だけSwift helperのビルド時間が入る。
- STTが
テストですをペストですと誤認識しても、xangi投稿とTTS再生まで通れば会話経路は成功として扱う。
境界条件
- この指示書は、Mac上でxangi-stackchanを動かし、StackChanをUSB serial接続している前提で使う。
input_source=macはMacの既定入力デバイスを使う。入力デバイス選択UIはこの指示書の範囲外。mac_mic_secondsは固定秒数録音であり、VADによる自動停止ではない。- ここで扱うのは音声入力経路の復元と検証であり、STT精度改善は後続childで扱う。
禁止事項
input_source=macの検証中にCoreS3内蔵マイクファームを追加調整しない。- パッケージやツールをインストールする場合は、事前確認なしに実行しない。
- secret、token、Wi-Fi password、ローカル認証情報をログへ書かない。