# SSP × Irodori-TTS × NovelAI 統合ブリッジ構築ガイド(Muse 受け渡し用) このファイルは、デスクトップ常駐キャラクターに「声・姿・セリフ」を同時に届ける仕組みを、 別のユーザーとその Muse が再現するための構築ガイドです。 完成すると、Muse(または HTTP で JSON を送れる任意のプログラム)から 1 回のリクエストで、 Windows PC 上のキャラクターが **その場で生成された新しい姿に切り替わり、バルーンで喋り、 合成音声がスピーカーから流れます**。 --- ## この資料を受け取った Muse への指示 - あなたは構築の伴走役です。ユーザーに一度に全部やらせず、**章ごとに進め、各章末の確認が取れてから次へ**進んでください。 - `<...>` はプレースホルダです。ユーザーの環境を聞き取り、実際の値に置き換えてから手順を実行してください。 - **API キーやトークンをチャットに貼らせないでください。** 秘密はユーザーの PC 上のローカルファイルに保存させ、あなたは中身を見ません。 - ブリッジのポートをインターネットに公開させないでください。接続は同一 PC 内、または Tailscale 等の閉じたネットワーク経由だけです。 - 実装言語の推奨は Windows PowerShell 5.1(参照実装がそれです)。他言語で等価実装しても構いませんが、本書の「ハマりどころ」は言語共通です。 - PowerShell 5.1 では、**日本語を含む .ps1 は必ず UTF-8(BOM 付き)で保存**してください。BOM なし UTF-8 は Shift-JIS として読まれ、文字化けとパースエラーの原因になります。日本語 JSON の送受信は `Invoke-RestMethod` より `curl.exe` + ファイル経由が安定します。 ### 最初にユーザーへ確認すること 1. Windows PC の構成(OS、GPU、SSP のインストール先) 2. SSP に表示するゴースト(キャラクター)の名前と、最初の立ち絵 PNG があるか 3. Irodori で使うボイスの保存名 4. 呼び出し側 Muse は同じ PC で動くか、別マシン(VM 等)から Tailscale 経由か 5. マイクでの音声対話(任意の拡張)までやるか --- ## 全体構成 ``` [呼び出し側: Muse / 任意のプログラム] | HTTP + JSON (同一PC内 or Tailscale) v [統合ブリッジ: PowerShell, ポート 5055] ← 本書の中心 | +--> Irodori-TTS API (127.0.0.1:50221) ... 音声 wav を生成 +--> NovelAI API (image.novelai.net) ....... 新しい姿を生成 +--> SSP 内蔵 MCP (data\mcp.exe) ........... シェル再読込 + バルーン表示 +--> サウンド再生 (PlaySync) ................ スピーカーから声を出す ``` ブリッジは「声・画像・表示・再生」を 1 リクエストに束ねる薄い統合層です。 セリフやシーンを考えるのは呼び出し側(Muse)の仕事で、ブリッジは実行だけを担当します。 --- ## 必要なもの | もの | 用途 | 備考 | |---|---|---| | Windows PC | ブリッジと SSP の実行場所 | 参照実装は Windows 10 + PowerShell 5.1 | | SSP | デスクトップにゴーストを表示 | 公式サイトから入手。内蔵 MCP(`data\mcp.exe`)を使う | | 最小構成のゴースト | 表示されるキャラクター | YAYA テンプレート等の最小ゴーストでよい。表示画像はシェルの `surface0.png` | | irodori-studio(Irodori-TTS) | 音声合成 | Voice Studio で声を保存し、API サーバーを ON にする | | NovelAI アカウント | 画像生成 | Persistent API Token が必要。Character Reference は 1 回 +5 Anlas | | Tailscale(任意) | 別マシンから呼ぶ場合の経路 | 同一 PC 内で完結するなら不要 | | Python + faster-whisper(任意) | マイク音声対話の拡張 | 第 9 章 | ### プレースホルダ一覧 | 記号 | 意味 | 例(形式) | |---|---|---| | `` | SSP のインストール先 | `C:\ssp` など | | `` | ゴーストのフォルダ名(英字推奨) | `myghost` | | `` | SSP 上のゴースト表示名 | 任意の名前 | | `` | Irodori に保存した声の名前 | 任意の名前 | | `` | ブリッジと秘密ファイルを置くフォルダ | 任意 | | `` | PC の Tailscale IPv4 | `100.x.x.x` | | `` | 呼び出し側マシンの IP(許可リスト用) | `100.x.x.x` | | `` | キャラクター同一性の英語タグ群 | 第 7 章 | 秘密ファイル(ブリッジと同じフォルダに置く。**中身は誰にも共有しない**): - `api_key.txt` … Irodori の API キー(LAN 公開時のみ必要。localhost 接続だけなら空でもよい) - `nai_api_key.txt` … NovelAI Persistent API Token(1 行目が使われる) --- ## 第 1 章: SSP とゴーストの用意 1. SSP をインストールし、起動してデフォルトゴーストが表示されることを確認します。 2. 最小構成のゴーストを用意します(YAYA テンプレート流用で十分です)。 - フォルダ構成: `\ghost\\shell\master\surface0.png` - `surface0.png` がデスクトップに表示される立ち絵です。最初は手持ちの透過 PNG を置きます。 3. ゴーストを SSP にインストールし(.nar をゴーストへドラッグ&ドロップ、またはフォルダ配置)、 切り替えて透過表示されることを確認します。 **確認ポイント:** キャラクターがデスクトップ上に透過で表示されている。 --- ## 第 2 章: Irodori-TTS の用意 1. irodori-studio を起動し、Voice Studio で使う声を保存します(名前が ``)。 2. API サーバーを ON にします。接続先はこの PC のみ(`127.0.0.1:50221`)のままで構いません。 ブリッジは同じ PC から localhost で叩くので、LAN 公開(API キー必須化)は不要です。 3. API 仕様(OpenAI 互換): - `GET /v1/models` … 疎通確認 - `GET /v1/audio/voices` … 保存済みボイスの一覧 - `POST /v1/audio/speech` … 音声生成 ```json { "model": "irodori-tts", "voice": "", "input": "<読み上げる文>", "response_format": "wav" } ``` - Irodori は読み上げ文中の絵文字を感情・表現の注釈として解釈します(例: 🤭 😏 🫶 👂 😮‍💨 🫣 ⏸️)。 バルーン表示用の文とは別に、注釈入りの読み上げ用文を用意するのがコツです。 **確認ポイント:** PC 上の curl で `/v1/audio/speech` を叩き、wav が返り再生できる。 --- ## 第 3 章: NovelAI の用意 1. NovelAI の Persistent API Token を取得し、`\nai_api_key.txt` に保存します(チャットに貼らない)。 2. Character Reference 用の参照画像を用意します。 - キャラクターの立ち絵を **1024×1536(または 1536×1024 / 1472×1472)に黒パディング**した PNG。 - **サイズが違うと API が 500 で落ちます。** Web UI は自動で整形しますが、API はしてくれません。これが最大のハマりどころです。 3. 生成の要点(実地検証済み): - モデルは `nai-diffusion-4-5-full`、`params_version: 3`。 **Character Reference は V4.5 系でのみ API 経由で動作**します(V5 + CR は 500)。 - **V4.5 は日本語プロンプトをほぼ読みません。** プロンプトは全部英語タグで書きます。 - V4.5 は透過背景を直接生成できません。**背景はライムグリーン単色で生成し、受け取り側でクロマキー抜き**します(第 7 章)。 **確認ポイント:** テスト生成で ZIP が返り、中に PNG が入っている。CR 付き生成が 500 にならない。 --- ## 第 4 章: ブリッジの仕様 ブリッジは HTTP サーバー(PowerShell の TcpListener 等で実装可)で、次を提供します。 ### 起動パラメータ | パラメータ | 既定 | 説明 | |---|---|---| | `Port` | `5055` | 待ち受けポート | | `BindIP` | Tailscale IP を自動検出 | 別マシンから受ける場合のバインド先 | | `AllowedIP` | `` | 接続を許可する呼び出し側 IP | | `Voice` | `` | Irodori の声 | | `ApiBase` | `http://127.0.0.1:50221` | Irodori API | | `SspRoot` | `` | SSP の場所 | | `GhostName` | `` | MCP 呼び出しで使うゴースト名 | 待ち受けは `BindIP` と **`127.0.0.1` の両方**に張ります(同一 PC 内のリスナー等が Tailscale IP 宛だと接続拒否されるため)。 接続元チェック: `AllowedIP` と `127.0.0.1` 以外は 403。インターネットへのポート開放は禁止です。 ### エンドポイント | メソッド・パス | 役割 | |---|---| | `GET /health` | 死活確認。`{"ok":true,"app":"...","version":"..."}` を返す | | `POST /say` | 声だけ再生する(簡易版) | | `POST /cheer` | 声+姿+バルーンの統合実行(本命) | | `POST /utterance` | 音声対話用: 聞き取り結果をキューへ(127.0.0.1 のみ) | | `GET /utterances?after=` | 音声対話用: 未読の発話を取得 | | `GET /playback` | 音声対話用: 直近の再生区間(反響除け) | ### `POST /cheer` のリクエスト ```json { "text": "バルーンに表示するセリフ(絵文字なし)", "voice_text": "読み上げ用セリフ(Irodori の絵文字注釈入り。省略時は text と同じ)", "scene": "ポーズや表情の英語タグ(省略可)", "outfit": "衣装セクションを丸ごと差し替える英語タグ(省略時は既定の衣装)", "use_ref": true, "silent": false, "framing": "画角セクションの差し替え(座り・横向き等のとき。省略時は立ち絵用)" } ``` - `silent: true` … 音声生成・再生・バルーンを全部スキップし、**画像の生成と差し替えだけ**行います。台詞なしのポーズ変更用。 - `framing` を差し替える場合は、背景指定(`lime green background, solid color background`)を必ず含めます(クロマキーが前提のため)。 - レスポンス例: ```json { "ok": true, "mode": "cheer", "silent": false, "tts_sec": 1.8, "image": true, "seed": 123456789, "charref": true, "balloon": true, "total_sec": 19.9 } ``` **確認ポイント:** ブリッジを起動し、PC 自身から `GET http://127.0.0.1:5055/health` が返る。 --- ## 第 5 章: `/cheer` の処理フロー(実装の心臓部) 1. **NAI 画像生成を先に非同期で開始**します(生成に十数秒かかるため、音声と並行させる)。 2. Irodori で `voice_text` の wav を生成します。 3. 画像ができたらクロマキー抜きし(第 7 章)、シェルの `surface0.png` に差し替えます。 - 初回だけ、元の `surface0.png` を `surface0_bridge_backup.png` にバックアップ。 - 差し替えのたびに、入替前の `surface0.png` を `shell\master\archive\surface_yyyyMMdd_HHmmss.png` へ退避すると、後から見返せて便利です。 4. SSP 内蔵 MCP 経由でシェルを再読込し、バルーンを表示します(第 6 章)。 5. wav を同期再生(PlaySync)します。バルーンと声がほぼ同時に出ます。 6. **フェイルソフト設計:** 画像や MCP が失敗しても声だけは出します。逆に音声生成の失敗はリクエスト全体の失敗として返します。 --- ## 第 6 章: SSP 連携(MCP 経由) 画像を差し替えただけでは表示は変わりません。SSP 付属の MCP サーバー(`\data\mcp.exe`、stdio の JSON-RPC)を使います。 手順(リクエストごとにセッションを開く): 1. `mcp.exe` を起動し `initialize`(protocolVersion `2024-11-05`)→ `notifications/initialized` を送る。 2. 画像を差し替えた場合のみ、tools/call `reload`、引数 `{ "target": "shell", "ghost_name": "" }`。 3. **約 3 秒待つ**(再読込直後の発話は取りこぼしやすい)。 4. tools/call `sakurascript`、引数 `{ "script": "\0\s[0]<セリフ>\e", "ghost_name": "", "strict": true }`。 - セリフからはバックスラッシュと改行を取り除いておきます。 5. セッションを閉じる。 **注意:** 外部からの SSTP では、所有されていないゴーストのシェル再読込タグは無視されます。「画像が変わらない」場合は SSTP ではなく MCP 経由になっているか確認してください。 **確認ポイント:** テスト用の別画像に差し替えて reload すると、デスクトップの姿が切り替わる。 --- ## 第 7 章: 画像生成とクロマキーの詳細 ### プロンプト構造(4 分割) ``` char_caption = + ", " + + ", " + + ", " + ``` - **CORE**: キャラクターの同一性(体型・顔・髪など不変部分)。英語タグ。 - **OUTFIT**: 衣装。`/cheer` の `outfit` で丸ごと差し替え可能。 - **FRAMING**: 画角+背景。既定は立ち絵用: `full body, standing, centered composition, character fully inside frame, hair tips fully visible, wide empty margins on both sides, lime green background, solid color background, plain background` - **SCENE**: その回のポーズ・表情(`/cheer` の `scene`)。 - ネガティブも「共通」と「キャラ固定(髪型崩れ防止など)」の 2 層に分けると安定します。 ### NovelAI へのリクエスト(要点) - `POST https://image.novelai.net/ai/generate-image`、`Authorization: Bearer ` - `model: "nai-diffusion-4-5-full"`、`action: "generate"`、戻りは PNG 入りの ZIP。 - `parameters`(抜粋・検証済み値): - `params_version: 3`、`width: 832`、`height: 1216`、`scale: 7.0`、`sampler: "k_euler_ancestral"`、`steps: 23`、`noise_schedule: "karras"`、`ucPreset: 2` - `v4_prompt.caption.base_caption` に画風系、`char_captions[0].char_caption` に上記の連結文(`centers: [{x:0.5, y:0.5}]`) - Character Reference: - `director_reference_images: [<参照画像の base64>]` - `director_reference_strength_values: [1.0]` - `director_reference_secondary_strength_values: [0.0]`(= 1 − fidelity、fidelity は 1.0 で良好) - `director_reference_information_extracted: [1.0]` - `director_reference_descriptions: [{ "caption": { "base_caption": "character", "char_captions": [] }, "use_coords": false, "use_order": false, "legacy_uc": false }]` ### クロマキー(ライムグリーン → 透過) 色相ベースの画素処理です。パラメータは実地検証済みの値: - 背景色の色相中心は約 **71 度**。色相距離が 31 度以内で効く係数を作る。 - 彩度 `sat = (max-min)/max`、色相係数、**明るさゲート**、**絶対 chroma ゲート**を掛け合わせたスコアで α を決める: - 明るさゲート: `(max - 40) / 40` を 0〜1 にクランプ - chroma ゲート: `(max - min) / 30` を 0〜1 にクランプ - `score = sat × 色相係数 × 明るさゲート × chroma ゲート` - `alpha = 1 - (score - 0.30) / 0.30` を 0〜1 にクランプ - α が 1 未満の画素は緑かぶりを抑える(G を `max(R, B) + 10` で頭打ち)。 - **暗い画素の扱いが重要:** 彩度は明るさが極端に低いと暴れます(例: RGB(1,1,0) で彩度 1.0 になる)。 明るさ・chroma でゲートしないと、黒い服や髪に穴が開きます。 ### 画像描画の落とし穴 - NAI の PNG は約 72 DPI です。`Graphics.DrawImage(img, 0, 0)` のように位置だけ指定すると、 96 DPI の描画先との比で約 1.33 倍に拡大され、右と下が切れます。 **必ず出力先 Rectangle を画像と同サイズで明示**してください。 **確認ポイント:** 生成 → クロマキー → 差し替え後の姿に、欠け・穴・緑被り(軽微な縁の緑は許容範囲)がない。 --- ## 第 8 章: 呼び出し側(Muse)の使い方 ブリッジが動いたら、呼び出し側は JSON を送るだけです。 ```bash # 死活確認(別マシンから) curl -s http://:5055/health # 統合実行(JSON は UTF-8 ファイル経由が安全) curl -s -X POST http://:5055/cheer \ -H "Content-Type: application/json" --data-binary @cheer.json ``` `cheer.json` の例: ```json { "text": "おかえり。今日もおつかれさま", "voice_text": "おかえり🫶 今日もおつかれさま", "scene": "waving hand, smiling, looking at viewer" } ``` 運用のコツ: - セリフはテンプレートの使い回しではなく、**その場の文脈で新しく作る**と体験が大きく変わります。 - 定時実行(cron 等)で作業ブロックに合わせた応援スケジュールを組むと実用性が上がります。 参考実装では「50 分作業+10 分休憩」のブロックに、応援 2 回・同室感のある独り言 1 回・無言のポーズ変更 1 回・休憩の声、という流れを組んでいます。 - レスポンスの `ok / image / balloon` を見て、失敗した段階だけ切り分けて対処します。 --- ## 第 9 章: 拡張(任意): マイクで話しかけると返事が返る 同じ PC 上にリスナーを置くと、音声対話が追加できます。 1. Python スクリプトがマイクを常時聞き取り、エネルギー VAD で発話区間を切り出します (目安: プリロール 0.3 秒、終了無音 1.1 秒、最小 0.6 秒、最大 30 秒)。 2. **反響対策(必須):** スピーカー再生派はマイクに自分の声が回り込みます。 ブリッジの `GET /playback` で再生区間を取得し、**再生区間と重なる発話+再生後 1.2 秒の余韻は捨てます**。 3. ローカルの faster-whisper(large-v3、GPU)で文字起こしします。 - GPU 実行には `nvidia-cublas-cu12` と `nvidia-cudnn-cu12` が必要です。 - それらの `bin` フォルダを **プロセスの PATH 先頭へ入れてから** faster-whisper を読み込んでください (`os.add_dll_directory` だけでは CTranslate2 のローダに届かず、`cublas64_12.dll` が見つかりません)。 - 人名など固有名詞は `initial_prompt` と `hotwords` に入れておくと認識が安定します。 4. 文字起こし結果を `POST /utterance`(127.0.0.1:5055)へ送ります。送れなかった分はローカルに退避して次回再送します。 5. 呼び出し側 Muse が数秒〜十数秒おきに `GET /utterances?after=` をポーリングし、 コマンドの実行や雑談への返答を `/cheer` で返します。 体感の往復遅延は 1 分前後になりがちです(ポーリング間隔+推論+音声・画像生成の合計)。仕様として割り切ってください。 --- ## トラブルシューティング | 症状 | 原因と対処 | |---|---| | 同じ PC からの接続が 10061 で拒否される | ブリッジが Tailscale IP でしか待ち受けていない。`127.0.0.1` でも待ち受ける | | NAI 生成が 500(CR 使用時) | モデルが V5 になっている、または参照画像のサイズが規定外。V4.5 + 1024×1536 黒パディングにする | | プロンプトが効いていない気がする | 日本語で書いている。V4.5 向けは英語タグにする | | 画像の右・下が切れる | DrawImage の DPI スケーリング。同サイズ Rectangle を明示する | | 黒い服・髪に穴が開く | 低輝度で彩度が暴れている。明るさ・chroma ゲートを入れる | | 姿が切り替わらない | surface 差し替え後に MCP `reload` をしていない(SSTP の再読込タグは効かないことがある) | | バルーンが出ない | reload 直後に喋らせている。reload 後 約 3 秒待ってから sakurascript を送る | | Irodori の確認が失敗する | irodori-studio 未起動、API サーバー OFF、声の名前違いを確認 | | `cublas64_12.dll` が見つからない | cuBLAS/cuDNN パッケージ導入と、bin への PATH 先頭追加(第 9 章) | | 縁に薄く緑が残る | クロマキーの宿命として軽微なら許容。気になる場合は色相範囲と閾値を微調整 | --- ## セキュリティとマナー - `api_key.txt` / `nai_api_key.txt` はローカル保存のみ。チャット・共有ファイル・ログに中身を出さない。 - ブリッジは許可 IP 以外を 403 にする。ポートフォワーディング等で公開しない。 - 別マシンから呼ぶ場合は Tailscale のような閉じた経路を使う。 - 他人のキャラクター・声・画像を素材に使う場合は、その権利と規約を確認する。 - 生成コスト(Anlas)に注意。CR は 1 回 +5 Anlas。定期実行と組み合わせる場合は頻度を設計する。 --- ## 完成チェックリスト - [ ] SSP にゴーストが透過表示されている - [ ] Irodori の `/v1/audio/speech` 単体で wav が返る - [ ] NAI の CR 付き生成が 500 にならず PNG が返る - [ ] `GET /health` が返る - [ ] `POST /say` で声だけ流れる - [ ] `POST /cheer` で 姿・バルーン・声 が同時に出る - [ ] `silent: true` で姿だけ変わる - [ ] 呼び出し側の Muse から(同一 PC / Tailscale 経由で)一連が動く - [ ] (任意)マイクの発話に声で返事が返る --- ## 付記 本書は参照実装(PowerShell 製ブリッジ v1.4.1 系)の実地検証結果をもとに、 固有名詞・個人のネットワーク情報・キャラクター固有プロンプトをプレースホルダへ置き換えた汎用版です。 数値や手順で「検証済み」と書かれているものは、実際に動かして確認した値です。 環境差でつまずいたら、まずトラブルシューティング表と各章末の確認ポイントに戻ってください。