AI音声認識をUnityで
UnityでAI音声認識
はじめに
このチュートリアルでは、Hugging Face Unity APIを使用してUnityゲームに最先端の音声認識を実装する方法を案内します。この機能は、コマンドの実行、NPCへの話しかけ、アクセシビリティの向上、音声をテキストに変換する必要がある他の機能など、さまざまな用途で使用することができます。
自分自身でUnityで音声認識を試してみるには、itch.ioでライブデモをチェックしてください。
前提条件
このチュートリアルでは、Unityの基本的な知識があることを前提としています。また、Hugging Face Unity APIをインストールしている必要があります。APIの設定手順については、以前のブログ記事を参照してください。
- ファルコンはHugging Faceのエコシステムに着陸しました
- Hugging Face Hubへ、fastText をようこそお迎えください
- ギャラリー、図書館、アーカイブ、博物館向けのHugging Face Hub
手順
1. シーンの設定
このチュートリアルでは、プレイヤーが録音を開始および停止でき、その結果がテキストに変換される非常にシンプルなシーンを設定します。
まず、Unityプロジェクトを作成し、次の4つのUI要素を持つキャンバスを作成します。
- 開始ボタン:録音を開始します。
- 停止ボタン:録音を停止します。
- テキスト(TextMeshPro):音声認識の結果が表示される場所です。
2. スクリプトの設定
SpeechRecognitionTest
という名前のスクリプトを作成し、空のGameObjectにアタッチします。
スクリプト内で、UIコンポーネントへの参照を定義します。
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text;
インスペクターでそれらを割り当てます。
次に、Start()
メソッドを使用して開始ボタンと停止ボタンのリスナーを設定します。
private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
}
この時点で、スクリプトは次のようになります:
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text;
private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
}
private void StartRecording() {
}
private void StopRecording() {
}
}
3. マイクの入力を録音する
さて、マイクの入力を録音し、WAV形式でエンコードしましょう。まず、メンバ変数を定義します:
private AudioClip clip;
private byte[] bytes;
private bool recording;
次に、StartRecording()
内で、Microphone.Start()
メソッドを使用して録音を開始します:
private void StartRecording() {
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
}
これにより、44100 Hzで最大10秒間のオーディオが録音されます。
録音が最大長さの10秒に達した場合、録音を自動的に停止する必要があります。そのために、Update()
メソッドに以下のコードを記述します:
private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
}
次に、StopRecording()
内で、録音を切り詰めてWAV形式でエンコードします:
private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
}
最後に、Hugging Face APIにオーディオデータを準備するために、EncodeAsWAV()
メソッドを実装する必要があります:
private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2);
foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}
フルスクリプトは以下のようになるはずです:
using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text;
private AudioClip clip;
private byte[] bytes;
private bool recording;
private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
}
private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
}
private void StartRecording() {
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
}
private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
}
private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2);
foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}
}
このコードが正しく動作しているかテストするために、StopRecording()
メソッドの最後に以下の行を追加してください:
File.WriteAllBytes(Application.dataPath + "/test.wav", bytes);
これで、Start
ボタンをクリックし、マイクに話しかけ、Stop
ボタンをクリックすると、録音したオーディオが Unity Assets フォルダに test.wav
というファイル名で保存されるはずです。
4.音声認識
次に、Hugging Face Unity API を使用してエンコードされたオーディオ上で音声認識を実行したいと思います。それには、SendRecording()
メソッドを作成します:
using HuggingFace.API;
private void SendRecording() {
HuggingFaceAPI.AutomaticSpeechRecognition(bytes, response => {
text.color = Color.white;
text.text = response;
}, error => {
text.color = Color.red;
text.text = error;
});
}
これにより、エンコードされたオーディオが API に送信され、成功した場合は白で応答が表示され、それ以外の場合は赤でエラーメッセージが表示されます。
StopRecording()
メソッドの最後で SendRecording()
を呼び出すことを忘れないでください:
private void StopRecording() {
/* 他のコード */
SendRecording();
}
5.最終的な修正
最後に、ボタンのインタラクティビティとステータスメッセージを使用して、このデモの UX を改善しましょう。
録音が開始/停止可能な場合、つまり録音の準備ができている場合にのみ、Start ボタンと Stop ボタンをインタラクティブにする必要があります。
次に、録音中または API の応答を待っている間に、レスポンステキストをシンプルなステータスメッセージに設定します。
完成したスクリプトは以下のようになります:
using System.IO;
using HuggingFace.API;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text;
private AudioClip clip;
private byte[] bytes;
private bool recording;
private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
stopButton.interactable = false;
}
private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
}
private void StartRecording() {
text.color = Color.white;
text.text = "録音中...";
startButton.interactable = false;
stopButton.interactable = true;
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
}
private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
SendRecording();
}
private void SendRecording() {
text.color = Color.yellow;
text.text = "送信中...";
stopButton.interactable = false;
HuggingFaceAPI.AutomaticSpeechRecognition(bytes, response => {
text.color = Color.white;
text.text = response;
startButton.interactable = true;
}, error => {
text.color = Color.red;
text.text = error;
startButton.interactable = true;
});
}
private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2);
foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}
}
おめでとうございます!Unityで最新の音声認識を使用できるようになりました!
質問がある場合やHugging Face for Gamesの利用に更に関わりたい場合は、Hugging Face Discordに参加してください!
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles