会計学習コンテンツのナレーション機能構築
スライドとSVG図解で構成していた学習コンテンツに、音声ナレーションを載せた。テキストを目で追うだけだった教材が、声に合わせてスライドが切り替わる「見て聴く」体験に変わった。朝にVOICEVOXで最初の音声を生成し、夜にはCloudflare R2から配信される状態まで持っていった。
VOICEVOXで音声WAV生成
最初の音声合成にはVOICEVOXのローカルAPIを使った。プロローグから第3話までの138台詞 + 第4話の43台詞、合計181台詞分のWAVファイルを生成した。再生時間は約27分。
台詞テキストをAPIに投げてWAVを受け取るスクリプトを回すだけで音声ファイルが揃う。手軽さは申し分ないが、一部の漢字で読み間違いが発生した。「石」を「こく」と読んでしまう問題は、台詞テキスト側で「いし」とひらがなに置換して回避した。
NarrationViewer.vueの作成
音声に合わせてスライドが自動で進行するビューアコンポーネントを作った。
主な機能:
- 音声自動再生: 台詞ごとにWAVを順次再生し、終わったら次のスライドへ遷移
- 倍速再生: 1x / 1.25x / 1.5x / 1.8x / 2x の5段階。AudioのplaybackRateを切り替える
- シーン一覧ドロワー: ハンバーガーメニューから開く。チャプター番号を表示し、本の目次のように構造化した
- シーン切替トランジション: 映画のフェードイン・フェードアウトでシーン間をつなぐ
SVG画像と台詞のマッピングも調整した。シーン7で3つの表が登場するが、台詞との紐づけがずれていたので修正。どの台詞でどの表を見せるかを1つずつ確認して合わせた。
ランディングページの3カード構成
コンテンツへの入口となるランディングページを作成した。同じ教材を3つの視聴モードで提供する構成にした:
- カラム版 -- Miller Columns形式で自由にスライドを行き来する
- フォーカス版 -- ナビゲーションを消して全幅でスライドだけ見せる
- ナレーション版 -- 音声付きで自動進行する
カード型のUIで3つを並べ、それぞれの特徴が一目で伝わるようにした。
MillerViewerのカラムヘッダー名変更
Miller Columnsの各カラムに表示するヘッダー名を見直した。「カテゴリー」を「セクション」、「中カテ」を「チャプター」に変更し、トピック名も追加。学習コンテンツの階層構造と用語を揃えた。
Google Cloud TTS(Chirp 3 HD)への移行
VOICEVOXの音声は素早く量産できたが、キャラクターの声質が教材のトーンと合わない箇所があった。Google Cloud TTSのChirp 3 HDモデルに切り替えて、より自然な読み上げを目指した。
ボイスの選定に時間をかけた。複数の声を試聴して、役割ごとに割り当てた:
| 役割 | ボイス名 | 選定理由 |
|---|---|---|
| ナレーター | Leda | 落ち着いたトーンで説明に向く |
| 生徒役 | Zephyr | 明るく、質問する場面に合う |
| 先生役 | Algieba | 穏やかで信頼感がある |
第1話の全55行をGCP音声に差し替えた。VOICEVOXとGCPの音声が混在する過渡期を経て、第1話はGCPに統一できた。
GCP ADC認証方式への移行
当初はサービスアカウントのキーファイル(JSONファイル)でGCP認証していたが、ADC(Application Default Credentials)方式に切り替えた。gcloud auth application-default login でローカル環境の認証を済ませ、キーファイルを削除した。秘密鍵ファイルがリポジトリ周辺から消えて、管理がすっきりした。
Cloudflare R2への音声ファイル移行
210ファイルの音声データをローカルからCloudflare R2に移した。
手順:
- R2バケットを作成
- wranglerで210ファイルをアップロード
- カスタムドメインを設定してCDN配信を有効化
- NarrationViewer側の音声URLをR2ドメインに書き換え
- CSP(Content Security Policy)の
media-srcとconnect-srcにR2ドメインを追加
ローカルR2とリモートR2の転送で詰まった。wrangler r2 object put がデフォルトでローカルのR2エミュレータに書き込んでいた。--remote フラグを付け忘れていたのが原因で、フラグを追加したら本番のR2バケットにファイルが入った。
リファクタリング
一通り動くようになった段階で、3つのレビューエージェントを並列に起動してコードレビューを回した。コンポーネントの責務分離、composableの切り出し、型定義の整理など、指摘を受けた箇所を修正した。
振り返り
朝の時点では「スライドに音声を付ける」という1つのタスクだったが、音声合成エンジンの選定、ファイル配信のインフラ、認証方式の見直しと、レイヤーをまたいで手を動かす一日になった。VOICEVOXで素早くプロトタイプを作り、GCP TTSで品質を上げ、R2で配信基盤を整える。この順番で進めたことで、各段階で動くものを確認しながら前に進めた。
181台詞・約27分の音声付き教材が、ブラウザで再生ボタンを押すだけで動く。テキストだけだった教材が、声と映像の教材に変わった手応えがある。