この記事は フリュー Advent Calendar 2022 の3日目の記事となります。
紹介の前に
2022/11/19 〜 20 に、SPAJAM 2022 の本選が開催されました。
その本選にて、めでたく 優秀賞 を受賞しました!!🎉
残念ながら最優秀賞は逃しましたが、初めての本選参加はとても楽しめました♨️
今回は、その開発の中で使った技術周りの紹介をしたいと思います。
(Unity 初心者なので、専門用語とか間違ってても許してください)
アプリの紹介
- アプリ名: PiCCOM (読み方: ピッコム)
- 使用技術:
- Unity (C#): 2021.3.2f1 (SILICON)
- Google Cloud Speech-to-Text API
- Speech Recognition using Google Cloud Pro
- DeepL API
Google Cloud Speech-to-Text API
概要
Google が提供するAPIで、「音声データをテキストデータに変換する」ことができます。
準備
Google Cloud Console で、 Speech-to-Text API
の利用できるように、サービスの有効化を行います。
次に、APIを利用するための認証情報を登録します。
(誤字ってますね、今更なので気にしない)
使い方
今回は、ハッカソンという時間が限られた状況、且つまだまだ実装しなければならいものがあったので、時間をお金で買う決断をしました。
というのも、本来であれば、諸々を時前実装すべきなのですが、ライブラリ周りを探していたところ Speech Recognition using Google Cloud Pro を見つけました。
なので、今回は実装方法については Speech Recognition using Google Cloud Pro
を使う方法での説明を次項でやりたいと思います。
とはいえ、今回のアプリを使う中で、調べた情報は追記しておこうと思います。
APIを駆使(?)する方法
多言語化対応
多言語化対応というと少し大袈裟ですが「テキストへ変換したい音声データの言語の候補(代替言語)を複数指定する」ことができます。
ただし 最大3つまで という制限はありますが「本来の変換対象の言語」の指定と合わせれば 最大4つ までの指定が可能にはなりそうです。
指定する方法は RecognitionConfig を使い、必要な項目を設定します。
Field | 説明 | |
---|---|---|
languageCode | String (required) | 変換対象とする音声データの言語コード。 (例: "en-US" ) |
alternativeLanguageCodes | String の配列 (optional) | 変換対象とする音声データの代替言語コード。この項目が設定されている場合は、認識結果には languageCode を含め、検出された最も可能性が高い言語での認識が含まれ、認識結果には検出された言語タグが含まれます。 |
言語コード は、共に「BCP-47 に対応した言語タグである」必要があります。
Speech-to-Text API としての サポート状況 は確認して利用してください。
Speech Recognition using Google Cloud Pro
概要
Google Cloud サービスで提供される Speech-to-Text API を利用して、音声をテキストに変換する機能を提供する。
準備
Speech Recognition using Google Cloud Pro
を上記、Unity Asset Store から購入し、該当プロジェクトにインポートします。
インポートしたあとは Speech-to-Text API
を利用するために取得した、認証コードを Api Key
に設定します。
この時の注意点として「GC Recognitionnition
を使いたいスクリプトなどに Component
として追加」すると、1回目の実行はうまくいうものの、2度目以降の実行がうまくいかなくなります。
アプリ起動かビルドタイミングでエラーが発生します。
発生する内容は、メモってません!!
エラーの内容的には「GCSpeechRecognition
の初期化に失敗しました」のようなエラーが出ます。
回避策としては GCSpeechRecognition
の Prefab を Scene
に直接配置して、共通参照できる状態にしてあげることです。
使い方
とりあえず、下のコードを参考にしてもらえばよいかなと思います。
(C#
特有?の改行はしていません!!!)
音声データの録音から、 Speech-to-Text API
へのデータ送信など全てをやってくれるので、かなり実装が楽にできるようになります。
using FrostweepGames.Plugins.GoogleCloud.SpeechRecognition;
public class SpeechToTextText: MonoBehaviour {
[SerializeField]
private Button recordButton = null;
// MEMO: `GCSpeechRecognition` を `Scene` に追加するときは、Prefabを配置すること
private GCSpeechRecognition _speechRecognition;
private void Awake() {
_speechRecognition = GCSpeechRecognition.Instance;
_speechRecognition.FinishedRecordEvent += OnFinishedRecordEvent;
_speechRecognition.RecognizeSuccessEvent += OnRecognizeSuccessEvent;
if (_speechRecognition.HasConnectedMicrophoneDevices()) {
_speechRecognition.SetMicrophoneDevice(_speechRecognition.GetMicrophoneDevices()[0]);
}
if (recordButton != null) {
// ボタンを押している最中は録音する
recordButton.OnPointerDownAsObservable()
.Subscribe(_ => StartRecord())
.AddTo(this);
// ボタンを離した段階で録音を終了する
recordButton.OnPointerUpAsObservable()
.Subscribe(_ => StopRecord())
.AddTo(this);
}
}
private void OnDestroy() {
_speechRecognition.FinishedRecordEvent -= OnFinishedRecordEvent;
_speechRecognition.RecognizeSuccessEvent -= OnRecognizeSuccessEvent;
}
private void StartRecord() {
_speechRecognition.StartRecord(false);
}
private void StopRecord() {
_speechRecognition.StopRecord();
}
private void OnFinishedRecordEvent(AudioClip clip, float[] raw) {
if (clip == null) return;
RecognitionConfig config = RecognitionConfig.GetDefault();
// メインの言語コードを設定
config.languageCode = Enumerators.LanguageCode.en_US.Parse();
// メインの言語コード以外の、代替言語コードを設定
List<string> autoLangCodes = new List<string>() {
Enumerators.LanguageCode.ja_JP.Parse(),
Enumerators.LanguageCode.en_GB.Parse(),
"zh", // `Enumerators.LanguageCode` に定義が無いのでも、 `Speech-to-Text API` 側がサポートしていれば指定は可能
};
config.alternativeLanguageCodes = autoLangCodes.ToArray();
config.audioChannelCount = clip.channels;
GeneralRecognitionRequest recognitionRequest = new GeneralRecognitionRequest() {
audio = new RecognitionAudioContent() {
content = raw.ToBase64()
},
config = config
};
_speechRecognition.Recognize(recognitionRequest);
}
private async void OnRecognizeSuccessEvent(RecognitionResponse recognitionResponse) {
string transcript = "";
foreach (var result in recognitionResponse.results) {
foreach (var alternative in result.alternatives) {
if (recognitionResponse.results[0].alternatives[0] != alternative) {
transcript = alternative.transcript;
}
}
}
}
}
こんな感じです。
実装などについては、以下の記事を参考にさせていただきました。
DeepL API
概要
DeepL SE が提供するAPIで、「テキストデータの翻訳する」ことができます。
ちなみに、読み方は「ディープエル」です。
(実は、ディープルって呼んでたんですよねー・・・)
準備
DeepL API を使うにはアカウント登録が必要です。
現時点ではプラン(開発者向け)は現時点では、2つあります。
- DEEPL API FREE
- DEEPL API Pro
フリープランでも「1か月に500,000文字まで翻訳 (2022/12/03 時点)」可能なので、お試しするなら、フリープランで十分です。
登録が完了したら、マイページを確認します。
マイページの「アカウント」タブを開き、下の方にスクロールすると DeepL APIで使用する認証キー があるので、それをコピーしてください。
注意点
DeepL のアカウントでログインしていると、Web版の DeepL 翻訳 は使えません。
ログアウトしてから使うようにしましょう!!
使い方
こちらも、 下のコードを参考にしてもらえばよいかなと思います。
(C#
特有?の改行はしていません!!!)
public class DeepL {
static string APIKey = "{DeepL APIで使用する認証キー}";
static string BaseURL = "https://api-free.deepl.com";
static string TranslateAPI = BaseURL + "/v2/translate";
public async UniTask<TranslateModel> TranslateAPIRequest(string text, string targetLang) {
try {
HttpClient client = new HttpClient();
// リクエストヘッダーに認証キーを設定
client.DefaultRequestHeaders.Add("Authorization", $"DeepL-Auth-Key {APIKey}");
// DeepL API を利用するテキストデータは `UTF-8` にエンコードする必要がある
byte[] bytesData = System.Text.Encoding.UTF8.GetBytes(text);
Encoding utf8Enc = Encoding.GetEncoding("UTF-8");
string utf8EncodedText = utf8Enc.GetString(bytesData);
// `source_lang` を指定しなければ、自動認識による翻訳をしてくれる
Dictionary<string, string> requestParameters = new Dictionary<string, string>() {
{ "text", utf8EncodedText },
{ "target_lang", targetLang }
};
HttpResponseMessage response;
using (FormUrlEncodedContent content = new FormUrlEncodedContent(requestParameters)) {
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
response = await client.PostAsync(TranslateAPI, content);
}
string contentString = await response.Content.ReadAsStringAsync();
TranslateResult result = JsonConvert.DeserializeObject<TranslateResult>(contentString);
var dic = JsonConvert.DeserializeObject<Dictionary<string, List<Dictionary<string, string>>>>(contentString);
List<Dictionary<string, string>> translations = dic["translations"];
TranslateModel model = result.translations[0];
Debug.Log($"ほんやくこんにゃく({targetLang}): {model.language}, {model.text}");
return model;
} catch (Exception e) {
Debug.Log("Error: \n" + e.Message);
return null;
}
}
}
public class TranslateResult {
[JsonProperty("translations")]
public List<TranslateModel> translations { get; set; }
}
public class TranslateModel {
/// 変換元のテキストデータの言語コード
[JsonProperty("detected_source_language")]
public string language { get; set; }
/// 変換後のテキストデータ
[JsonProperty("text")]
public string text { get; set; }
}
あとは、呼び出すだけですね。
細かいところは省いています。
private DeepL deepL = new DeepL();
// テキストデータを、指定した言語コードに翻訳する
// 言語コードは `DeepL API` 側で対応されているもに限る
// See: https://www.deepl.com/ja/docs-api/translate-text/translate-text/
TranslateModel translated = await deepL.TranslateAPIRequest(transcript, "JA");
translated.language
translated.text
DeepL API
のレスポンスには「翻訳前の言語コード」が含まれているのがとても助かります。
アプリ利用開始タイミングでテキスト入力をしてもらい、その結果をもとに、自動でロケールの変更やUIの変更などにもつかえるかもですねー・・・
さいごに
-
Speech-to-Text API
を使ってみた感想としては、日本語メインで使いましたが「音として」は認識していますが「意図する単語にならない」ことは多々ありましたが、意味は通じそうだなという感じでした- ただし、それを「翻訳」する場合は意味が変わってくるので使い方次第かもしれないです
- 端末にはよりますが、なかなか認識してくれない時もありました(滑舌の問題かもしれませんけど)
- ゆっくりはっきり発音すれば、拾ってくれたので、やっぱり滑舌のせいですね・・・
-
DeepL API
をあえて使ってみましたが、翻訳精度は高かったですね- ちなみに、同じテキストをAPI版とWeb版で試しましたが、同じ結果返ってきました!!(当たり前ですけど、一回は検証しますよね、エンジニアなら)
- 今回のアプリとしては本来は翻訳後のテキストデータを
Google Cloud Natural Language API
を通して、テキスト分析も行いたかったんですが、これについてはまたいつか試したいと思います - あと
Unity(2021.3.2f1)
と古いバージョンで、しかもSILICON
版を使ったのですが、Unity Hub
を通して使う裏技的なものも見つけた(?)ので、別の記事で書こうと思います- Unity 初心者ですので、今回はUI部分はノータッチだったので、勉強していってもよさそうだなとは思いました
参考文献まとめ
Google Cloud Speech-to-Text API
Speech Recognition using Google Cloud [VR\AR\Mobile\Desktop] Pro
【Unity(C#)】Google Cloud Speech-to-TextをUnityで使用できるGoogle Cloud Speech Recognitionの使い方
DeepL API
Google Cloud Natural Language API