32.【音声生成AI設計】FSMに最小の音声を接続する|自然さを捨てた壊れない第一声
tags: [“音声生成AI”, “FSM”, “TTS”, “設計思想”, “生成AI”]
🔊【音声生成AI設計】FSMに最小の音声を接続する
―― 🧪 自然さを捨てた壊れない第一声
前回(31)で、音声AIの正体は FSM(有限状態機械) だと分解した。
今回はそのFSMに、最小限の音声出力を接続する。
⚠️ ここでの目的は「自然に喋らせる」ことではない。
🎯 壊れずに音が出て、必ず戻ってこれること。
🎯 本記事のゴール
- FSMの Speaking 状態にのみ音声を出す
- 発話終了を 明示的イベントとして扱う
- LLMは使わない(まだ)
- 「それっぽさ」より 制御の健全性を優先
🧩 全体構造(最小)
🧩 FSM
├─ 🎤 音声入力(今回はダミー)
├─ 🔊 TTS(最小)
└─ ⏱ タイマ/完了イベント
👉 音声はFSMの従属物。
勝手に喋らせない。
📦 FSMの関与ポイント(再確認)
- 🧠 Thinking:今回は使わない
- 🔊 Speaking:ここだけで音を出す
- 💤 Idle:必ず戻る
FSMが言わない限り、
音声は 出ない/続かない/再生されない。
🔊 最小TTSの考え方
ここでは「品質」を一切追わない。
- 単一フレーズ
- 単一音量
- 単一話速
- 感情なし
例:
「システム、起動しました。」
これで十分。
🧪 擬似コード(FSM × 最小音声)
state = "Idle"
def on_event(event):
global state
if state == "Idle" and event == "start":
state = "Speaking"
play_tts("システム、起動しました。")
elif state == "Speaking" and event == "tts_finished":
state = "Idle"
🔑 重要なのは:
play_tts()が 状態を変えない- 音声終了は
tts_finishedという 明示イベント - Speaking が終わったら 必ず Idle に戻る
⏱ 発話終了は「感覚」で決めない
❌ よくある失敗
- 音が止んだから終わり
- たぶん喋り終わった
⭕ 正解
- TTSエンジンから 完了イベントを受け取る
- それを FSMに通知する
👉 FSMが遷移を決める。
✋ 割り込みを“まだ”入れない理由
ここで割り込みを入れると:
- 状態数が増える
- 遷移が爆発する
- 音が出たのか分からなくなる
今回は あえて単純化する。
- Speaking 中は割り込み不可
- 音が終わったら Idle
割り込みは 次回(33)。
🚫 この段階でやってはいけないこと
- ❌ LLMをつなぐ
- ❌ 音声をストリーミング再生
- ❌ 自然さ・感情・抑揚の調整
- ❌ 会話っぽくする
👉 それらは FSMが完成してから。
🧠 この段階で得られるもの
- 音が 必ず出る
- 無限に喋らない
- 無音フリーズしない
- どこで止まったか分かる
🎉 これだけで、
世の「音声AIデモ」の大半より 堅牢。
📌 まとめ
- 🔊 音は Speaking 状態でのみ出す
- 🧩 FSMがすべてを決める
- ⏱ 発話終了はイベントで扱う
- 🚫 自然さは捨てる
- 🏗 壊れない最小構成が最優先
🔊 最小デモ
FSMが許可したときにのみ音声が出る、
最小構成の音声デモを用意しました。
▶ デモ
https://samizo-aitl.github.io/qiita-articles/demos/audio-fsm-minimal/
本デモは、
- Speaking 状態でのみ音声が出る
- 発話終了は明示イベントで検出する
- 無限発話・無音フリーズが起きない
ことを確認するためのものです。
🔜 次の記事(33)
- ✋ Speaking 中に人が喋ったらどうする?
- 🔄 割り込み FSM の追加
- 🧪 音声AIが一番壊れる瞬間を扱う
次は 地獄回。
割り込みをやる。
(GitHub上のMarkdownを正本とし、Qiitaには抜粋・調整して投稿)