2026年3月16日の開発日記
前日にVOICEVOXとGoogle Cloud TTSでナレーション機能を組み上げたのを受けて、今日は音声の品質・管理・配信の3つを一気に底上げした。朝にElevenLabs v3のAPIを叩き始め、夜にはステレオパン付きのナレーションが本番環境から流れるところまで持っていった。
今日やったこと
1. ElevenLabs v3 TTS導入と音声サービス比較
日本語TTSを4サービス(OpenAI TTS、Google Cloud TTS、ElevenLabs v3、VOICEVOX)で比較した。ElevenLabs v3が最も自然な日本語を生成すると判断し、APIスクリプトを組んでテスト音声を生成。Audio Tagsで文中に[gentle]や[serious]を挿入して感情を揺らすテストも実施した。聞き比べページ(/admin/voice-compare)を立てて3サービスを横並びで再生できるようにし、最終的にChapter 04全59行をElevenLabsで本番生成した。
主な成果:
- 4サービスの比較調査とElevenLabs v3の選定
- Audio Tags感情制御(文中挿入パターン)のテスト
- 聞き比べページ(4カラム構成)の構築
- Chapter 04の全59行をElevenLabsで本番生成(3,458文字消費)
詳細: ElevenLabs v3で日本語TTS - サービス比較から本番生成まで
2. 音声ファイル管理基盤の整備
152個の音声ファイルをpublic/audio/からaudio-assets/に移行し、.gitignoreで除外した。dev server middleware(dev-audio.ts)を作成してローカル開発でaudio-assets/から配信し、useAudioUrl composableで本番(R2 CDN)と開発(ローカル)のURL解決を一元化した。Cloudflare R2にElevenLabs音声とVOICEVOX音声をアップロードし、DEPLOY.mdにデプロイ手順を文書化した。
主な成果:
audio-assets/への一元移行(gitignore済み)- dev middleware +
useAudioUrlcomposable でURL解決統一 - R2アップロード(
--remoteフラグ忘れの教訓も記録) DEPLOY.md作成
詳細: 音声ファイル管理基盤の整備 - Git除外・R2配信・dev middleware構築
3. ナレーションコンテンツ改善とNarrationViewer拡張
Chapter 01+04の計196行のナレーションを通しレビューし、未解説の専門用語やスライドのずれ、scene.idの重複など8件の問題を修正した。旧プロジェクトから移行時に書き換わっていたメッセージラインを原文に忠実に復元。NarrationViewerにはmessageLine表示、ステレオパン(ユイ=左、カイ先生=右)、デバッグパネル(Dキー)を追加した。Playwright+ffmpegによる動画レンダリングも試行し、NarrationViewerのロジックを純粋関数に抽出してVitest 49テストを新規作成した。
主な成果:
- 196行の通しレビューで8件修正
- messageLine原文復元(旧プロジェクトとの整合性)
- ステレオパン実装とデバッグパネル追加
- Playwright+ffmpegで動画レンダリングのパイプライン構築
- 純粋関数抽出 + 49テスト新規作成(全292テスト合格)
- 命名規則移行計画(Section > Chapter > Topic)のマークダウン作成
詳細: ナレーションコンテンツ改善とNarrationViewer拡張
今日の試行錯誤
| # | テーマ | 試したこと | 結果 | 気づき |
|---|---|---|---|---|
| 1 | ElevenLabs API権限 | TTS権限のみでキー発行 | 音声生成OK、使用量確認NG | 最小権限で始めるのは正解だが段階的追加が必要 |
| 2 | 〃 | user_read権限を追加 | 使用量OK、ボイス検索NG | 権限は機能単位で分かれている |
| 3 | 〃 | voices_readを追加(保存ボタン押し忘れ) | 反映されず | UIの保存ボタン見落とし注意 |
| 4 | 〃 | 保存を確認して再実行 | 全権限通った | 計4回で全機能アクセス可能に |
| 5 | R2アップロード | wrangler r2 object putで59ファイル | CDNで全404 | デフォルトはローカルエミュレータに書き込む |
| 6 | 〃 | --remoteフラグ付きで再アップロード | 200 OK | DEPLOY.mdに「2回ハマった」と明記して再発防止 |
| 7 | 動画レンダリング | Playwrightでスクリーンショット撮影 | Playwright未インストール | プロジェクトのnode_modulesにない |
| 8 | 〃 | インストール後に再実行 | ffmpegにlibx264なし+音声パス取得失敗 | デコーダーのみビルド。audio.srcは再生しないと空 |
| 9 | 〃 | 音声パスをデータファイルから直接取得 | 動画生成OK、全セリフ2秒固定 | ffprobeがなくfallback値が使われた |
| 10 | 〃 | WAVヘッダーから音声長を直接計算 | 正確な長さで動画完成 | ffprobe不要でWAVヘッダーパースが軽量 |
| 11 | TTS二重管理 | Pythonスクリプトにセリフをハードコード | ts修正のたびにPyも手動更新が必要 | Source of Truthが2箇所は破綻する |
| 12 | 〃 | ts→JSON変換ステップを追加 | PyがJSONを読む一本化フロー完成 | 言語をまたいでもパイプラインで繋げば一元管理可能 |
| 13 | 音声ファイル配置 | public/audio/に配置 | gitignoreでデプロイに含まれない | リポジトリも肥大化する |
| 14 | 〃 | audio-assets/に移行+dev middleware | ローカルOK、本番はR2配信 | composableで環境切り替えが正解 |
| 15 | ナレーション移行 | AI任せで旧→新プロジェクトに移行 | メッセージラインがほぼ全面書き換えられていた | AIは原文を勝手に「改善」する。忠実移行を明示指示すべき |
| 16 | 〃 | 旧プロジェクトの原文を忠実に全復元 | Chapters 1-4の整合性回復 | 補足はナレーション層で行う方針に |
今日の学び
- ElevenLabs v3のAudio Tagsは文中挿入で感情の切り替えが効く。GCP TTSのSSMLより表現力が高い
wrangler r2 object putはデフォルトでローカルエミュレータに書き込む。本番R2には--remoteフラグが必須(2回ハマった)- TTS生成スクリプトのSource of Truthは1箇所に。ts→JSON変換パイプラインで二重管理を排除
- WAVヘッダーを直接パースすればffprobe不要で音声長が取れる
- ロジックを純粋関数に抽出するとVue依存なしでテストが書ける
明日やること
- 命名規則移行計画(Section > Chapter > Topic)の実装に着手
- ElevenLabsの残クレジットでChapter 01の音声も差し替え検討
- 動画レンダリングスクリプトの改善(既存音声ファイルを使う方式に統一)
- ナレーションレビューで指摘した残りの用語解説追加