Goでラリーシムのコドライバーを「ずんだもん」にする
EA Sports WRC の標準コドライバーについて
- 日本語話者コドライバーが居ない
- インカム風のこもったような音声で聞き取りにくい
- 「スライトレフト」で「ス」を聞き逃すと右なん?左なん?
- 「ワンレフト、ワンテン」は「1左110m」の意だけど「テン」を聞き逃すとレフトやライトを待ってしまったりして頭の中のコーナーイメージが構築できない
そこで「ずんだもん」
- 特徴的な話者であり、エンジン音や環境音が鳴っていても聞き取りやすい
実現までに検討したこと
Phase1
データ化されていないコドラのペースノートを引っ張りぬき、データからまた音声に生成しなおすわけですが、後者は幸いにVoiceVoxというプロジェクトが最近話題になり、このエンジンを使えばテキストを「すんだもん」に発声させることができそうなのでこちらを採用しました。
実際に試したときの内容はこちら。
あとはテキスト化の戦略をどうするか。
- 標準コドライバー音声を解析してテキスト化
- ゲーム内にはOSDでペースノートアイコンが表示されるのでそれを拾ってテキスト化
の2択かなという結論になったので順番に試していきました。
標準のコドライバーはいくつかの言語がありましたがその中では最もなじみのある「英語」を選択。deepspeechというオープンソースの音声解析ディープラーニングエンジンをを試しました。
これがあまりうまくテキスト化できません、そもそも「音声区間検出(Voice Acitivity Detection: VAD)技術」が必要なんですが、これがエンジン音にまみれたオーディオキャプチャデータだととっても精度の確保が難しい。
ゲームタイトル側のオーディオ設定にはエンジン音は50%までしかさげることができない。
さらに、区間の長さで解析結果が得られるまでの遅延時間のバラつきがあり、これを補正するのも難しい問題でした。
Phase2
戦略の2つ目、OpenCVにて切り出したペースノートアイコンを判読してテキスト化するというのを頑張ってみました。
ここでの奮闘の一部はGoCVというライブラリを使ってGoでOpenCV利用をするネタにまとめました。
おおむね完成しつつあるところまで作りこんだ成果物はこちら。
この戦略の結果をまとめると
- ペースノートアイコンには音声の内容に比べるとだいぶ簡素な情報しかない
- アイコンには「late」や「short/long」「tighten」などの詳細が無いのが厳しい
- これらの詳細は上達には重要な情報だった
- 結局音声を聞き直してペースノートの抜けた情報を埋めるという作業が発生
さらにとある問題が発覚
- テレメトリパケットの「ステージの現在の走行距離」という情報をもとにアイコン表示タイミングを記録
- 再生時もその情報をもとに記録の読み上げをおこなうという方針で進めてた
- しかし、だいぶ出来上がってからバック走行しても走行距離が増えるということが発覚
- ステージの進行ゲージの元データという扱いではなかった
Phase3
というわけで戦略を変えることに。
- これまでの方針がうまくいったとしても結局手修正はなくせない
- ならペースノートエディタにしちゃおう
- 音声とテレメトリ(3D座標)を両方記録
- それにペースノートを付与できるエディタ
- ペースノート保存時に3D座標との紐づけ
- Octreeに座標を読んでそれで近傍からコールさせる
という方針で再開発。
採用したもの
go-wca
GoからWindows Core AudioのAPIを利用できるラッパーライブラリ。
利用方法については別途まとめた。
Wavesurfer.js
音声波形の表示やマーキングを編集するという今回のやりたいことにマッチしたライブラリ
Skeleton + SvelteKit
エディタフロントエンドの実装に利用。
svgo + svg-pan-zoom
マップの表示に利用
Go製のSVGファイル生成エンジン。
svg-pan-zoomはSVG表示をズームしたりパンしたりできるようにするJavaScriptライブラリ
nanoda
GoからVoiceVoxのTTSエンジンを利用するためのラッパーライブラリ。利用方法は以前まとめた。
八分木(Octree)アルゴリズム
3次元空間の点群から近傍を検索するのに八分木が使える。(1次元におけるBinary-Treeの3次元版)
Octree(八文木)構造についての解説はこちら。
仕掛け
3段階ある
1.ログキャプチャー
- WCA経由でPC上の音声出力をキャプチャ
- EA-WRCからはテレメトリパケットが送信(標準UDPで127.0.0.1:20777に向けて)
- テレメトリには車の3D座標が含まれる
- その座標とキャプチャ音声の記録時間をセットでログに残す
- 音声キャプチャの開始はテレメトリパケットの受信をきっかけにする
- 音声キャプチャの終了はテレメトリパケットが来なくなって三秒後
2.ペースノート編集
- Webフロントエンド(SvelteKit+Skeleton)にて音声と地図の表示
- その上にマーキングを書き加える
- マーキングにはテキストを書き込める
- そうすると音声時間軸上にテキストがまばらにあるデータが作成できる
- これを保存するときにあらかじめとっておいた座標ログと突き合わす
- 最も近しい3D座標にテキストが紐づいたペースノートを生成できる
3.ゲーム中のペースノート読み上げ
- ゲームからのStageLengthは全ステージでユニークな値であることを利用
- 特定したペースノートをOctreeに読み込む
- テレメトリにある車の3D座標に一定範囲内にあるログを見つける
- ログの中で一番若いログを取り出す
- それが車との距離が一定距離以下になったら紐づけておいたテキストをTTSで音声合成
もう一つの問題
nanoda経由のTTSエンジン利用中、特定条件でpanicで落ちる問題が発覚。
あらかじめ辞書として作ったものは音素定義をあらかじめCreateAudioQueryというAPIで作成済みで動作開始するんだけど、辞書にある単語だけを扱っているとpanicしないことに気づく。
あらかじめペースノートを読み込むときに未知の単語があればあらかじめCreateAudioQueryを澄ましておくという形にしてみてしばらく使ってみる。
できたもの
使い方
READMEを読むと良いでしょう。
既知の問題
- 小さすぎる区間ができてしまった場合はZoomで広げて操作してください
- 編集ペースノートがまだ保存されていないステージは自動的に記録を残すモードになります
- 記録を残す際に複数回行うと記録ファイルが連番の別ファイルに記録されます(過去の記録を消しません)
- なので、一番有効な記録を選んで後置の番号を取り除く作業が必要です(このあたりのUIはまだ未実装)
- テキストにはbase.jsonにある単語を指定する必要がある(動的単語でTTSやっているとクラッシュする問題がある)
フィクション系ステージの一部でまだステージ長を確認できていないところがある- ステージ距離をキーにログのパスを特定するってやってたんだけど、距離の表現が同じでも値としてミスマッチなレアケースを発見<-こういうのはやっちゃダメだった
TODO
- フィクション系ステージの一部未確認のステージ長を確認する(v0.0.3にてDONE)
- ステージ選択に履歴と作成日時も表示してその選択でペースノートの元記録を選ぶ
- MyDucumentsフォルダの取得方法をWindowsAPI経由に変更する
- base.jsonの保存場所をMyDocuments配下に、名前をdictionary.jsonに変更する
- VoiceVoxが利用するonnxruntime.dllと同名のものがWindows側にある問題をこれで解決
- pacenote.logをパースした時に未知の単語があれば、あらかじめCreateAudioQueryしておく(動的な単語利用でクラッシュする問題の回避)
- ステージ距離キーにしてたところをパケットからくるビットパターンのままキーに変更
まとめ
編集中の様子
- 自動でペースノート作成まではできないけども
- それなりに各自でペースノートづくりができるようになったかな
- 標準コドライバーでレッキー>ペースノート編集ー>ずんだもんコドライバーで走る
- こういうフローで楽しめるようになった
- 記録と再生は自動的にステージを認識してくれるので楽ちん
走行事例
- 走行開始時と終了時に標準コドラがしゃべっちゃう
- 文言はもうちょっと調整するなりしないとコールが間に合ってないところはちょっとある
Discussion
利用法のアイディアとして、提案をもらいました。
「標準のコドラを残しつつ注意喚起のみずんだもんにしゃべってもらう」
というのは編集コストも低いし良い使い方だなぁと思いました!ナイス!