Bapliscaのブログ

イベントやインターンの話など

ボイストラベラー 音声認識や周波数周りの技術選定の話

Baplisca(@sooooouls) です。
先日、技育展で「ボイストラベラー」という音声認識と周波数取得を組合せた世界初のゲームを紹介しました。
内容や感想はこちらの記事で書きました。

baplisca.hatenablog.com

ボイストラベラー  アイコン
ボイストラベラー アイコン


ゲーム内では、音声認識をするパートと周波数取得するパートに別れています。
この2つの機能をどの様に実装したか書いていきます。

はじめに

ゲームを作ってから結構時間が経っており、あいまいなところがあると思うので注意してください。 よって適宜調べながら、補完するのを推奨します。
それと、Unityの基礎的な話はここでは書きません。

技術選定の話

ゲームという性質上、高速な相互作用が必要です。 そこで基本指針としては、ユーザー目線に立った操作性と安定性と低遅延性を心がけました。

音声区間検出

音声認識と周波数取得、両方に共通するのは音声区間検出(VAD)です。 音声区間検出は、実際に取り出したい信号区間のみを検出し、雑音のみの信号区間を除去します。 これにより、音声認識と周波数取得の精度が上がります。 実装では、パワースペクトルとゼロ交差数を用いました。 パワースペクトルで、ある閾値以上の音を有声と判断します。 ゼロ交差数で、ある閾値以上の値を雑音として判断します。
この2つの技術で、音声区間検出を実現しています。 もっと詳しい説明を見つけたので紹介します。

www.ai-shift.co.jp

音声認識

使用技術の変遷は、UnityEngine.Windows.Speech → Julius(モジュールモード)→ Julius(ネイティブプラグイン)です。
UnityEngine.Windows.Speechは、Unityの音声認識APIなので手軽に実装できたので採用しました。しかし、実装してみると認識遅延が発生して、ゲームとしては使えませんでした。
Julius(モジュールモード)は、Juliusを裏で実行し、ソケット通信を行うのですが、頻繁にソケットエラーが起き、安定しませんでした。
Julius(ネイティブプラグイン)は、メインスレッドであるC#側から、C++で書かれたJuliusの処理を呼び出し、音声認識を実現しています。両者の欠点(遅延と安定性)をうまく補えたので採用しました。

周波数取得

初めは、AMDF(Average Magnitude Difference Function)で、自己相関関数を利用して周波数を取得してました。ほぼリアルタイムで周波数は取得できるのですが、精度があまり良くなかったです。例えば音階A4(440Hz)の信号を流し込むと、倍音のA5(880Hz)で判定されるケースがありました。
試行錯誤の末、最終的には、高速フーリエ変換FFT)を用いました。リアルタイムで精度もかなり高いです。

終わりに

「ボイストラベラー」は、現在Windows(exe版)のみを作成してます。UnityはMacAndroid、ブラウザ(WebGL)などのマルチプラットフォームに対応しているので、他プラットフォームでの公開も検討してます。