Google Cloud Text to Speechで作る読み上げキャラクターボイスの作り方
こんにちは。丸ダイスです。
先日、 unity1week というオンラインゲームジャムでこんなゲームを作りました。
遊ぶと分かる通り、全てのセリフにボイスがついています。
ゲームデザイン上の理由で可能ならボイスを付けたかったので、開発期間中に検証してコマンド一発で全セリフの音声ファイルを出力する方法を検証しましたので紹介します。
上からやればゲーム中のテキストから読み上げ音声を自動生成する仕組みが大体組めるはず。
Google Cloud Text to Speech
Googleが提供している音声読み上げサービスです。100万語/月 の制限付きですが無料でも使え、HTTPリクエストで音声ファイル出力に対応しています。
デモサイトで試してみるとリクエスト用のjsonも表示でき、使い方のイメージが湧きます。
サービスの利用準備
コマンドラインからクエリを投げるにはアカウントの登録とSDKの初期化が必要です。
ぶっちゃけるとほぼこちらのサイトの通りにやっただけです。
Linux系バッシュを前提に書かれていますが、Windowsのコマンドラインでも可能でした。一部は読み替えが必要です。
・Google Cloud Platformの利用登録~Google Cloud SDK の初期化
上から順にやります。
SDKのインストールについてはWindows用のクイックスタートからインストーラが落とせるのがむしろ楽です。基本コマンドである gcloud もどこからでも実行可能なよう、いい感じにパスも通してくれます。
・認証情報の作成
環境変数の保存でexportは使えないので
export GOOGLE_APPLICATION_CREDENTIALS=<キーファイルのパス>
こうではなく
setx GOOGLE_APPLICATION_CREDENTIALS=<キーファイルのパス>
こうなります。
・Cloud Text-to-Speech API へのリクエスト実行
リクエストで使うcurlコマンドは問題ないですが、 $(gcloud auth application-default print-access-token) ←この形式でアクセストークンの出力をcurlコマンドに埋め込むのはWindowsのコマンドラインではやりづらいです。試すだけなら素直に、
gcloud auth application-default print-access-token
これの結果を$( ) の箇所にコピペして
curl -H "Authorization: Bearer "<アクセストークンをコピペ> \
-H "Content-Type: application/json; charset=utf-8" \
-d @<JSON ファイルのパス> \
https://texttospeech.googleapis.com/v1beta1/text:synthesize > synthesize-output.txt
こうした方が良いでしょう。
解説ページにある通り、.txtの中身の "audioContent"が目的の音声をテキストにエンコードしたもの(意味が分からなくてもOK)です。デコードのためのbase64はWindowsにはないので、
base64 synthesize-output-base64.txt --decode > synthetic-audio.mp3
これは
certutil -f -decode synthesize-output-base64.txt synthetic-audio.mp3
こう書き換えます。
Rubyで自動化
ここまで来れば、一連のコマンド実行をスクリプト化するだけです。私はRubyで書きました。
ExportVoiceFile.rb
require 'fileutils'
require 'optparse'
require 'json'
class ExportVoiceFile
def initialize()
@access_token = `gcloud auth application-default print-access-token`
@access_token = @access_token.gsub("\n", "").gsub("\r","")
end
def execute()
export_file("SampleText.txt")
end
def export_file(filepath)
FileUtils.mkdir_p("Tmp")
FileUtils.mkdir_p("Voice")
lines = File.readlines(filepath)
idx = 0
lines.each do |line|
line = line.gsub("\n", "").gsub("\r", "")
next if line == ""
doc = <<-EOS
{
"audioConfig": {
"audioEncoding": "MP3",
"pitch": "4.4",
"speakingRate": "1.30"
},
"input": {
"text": "#{line}"
},
"voice": {
"languageCode": "ja-JP",
"name": "ja-JP-Wavenet-A"
}
}
EOS
File.open("Tmp/input.json", "wb") do |file|
file.puts doc
end
cmd = "curl -H \"Authorization: Bearer \"#{@access_token} -H \"Content-Type: application/json; charset=utf-8\" -d @\"Tmp/input.json\" https://texttospeech.googleapis.com/v1beta1/text:synthesize > Tmp/output.txt"
result = `#{cmd}`
puts result
json = JSON.parse(File.read("Tmp/output.txt"))
File.open("Tmp/audio.txt", "wb") do |file|
file.puts( json["audioContent"])
end
cmd = "certutil -f -decode \"Tmp/audio.txt\" \"Voice/voice#{idx}.mp3\""
puts cmd
result = `#{cmd}`
puts result
idx += 1
end
end
end
if $0 == __FILE__
begin
ExportVoiceFile.new().execute()
exit 0
rescue => e
puts e.message
puts e.backtrace
exit 1
end
end
このRubyスクリプトの隣にこれ(Sample.txt)
勇者よ
勇者
伝説の
勇者
山の上に
棲みついた
ドラゴン
を置いて、Rubyをインストールしてから
ruby ExportVoiceFile.rb
と実行すれば動作します。Voiceフォルダが出来て、voice0.mp3, voice1.mp3, .... が出力されます。1行ごとに
「リクエスト用のjsonを出力→curlでリクエスト→結果からaudioContentを抽出→base64デコード」
これをしてるだけなので、PythonでもUnityのC#エディタ拡張でもお好みで(なぜエディタ拡張で書かなかったのか)。
Timelineでタイミングを合わせ、発話させる
Timelineから任意の関数を呼び出せるSignal Trackという機能で、画像のように1行ごとにしゃべるイベントをTimelineに打っていきます。
先ほどの音声は Resources/Audio/Voice/Cut1_0/voice0.mp3, .... と配置してあるので、1回シグナルが来るごとに
mVoiceIdx = 0;
..
public void SpeekSignal(){
AudioClip clip = Resources.Load<AudioClip>($"Audio/Voice/Cut1_0/voice{mVoiceIdx}");
mVoiceIdx++;
}
これで音声ファイルを読み込みます。勘のいい方はお気づきの通り、シグナルの打ち方を間違えると無が再生されたり、セリフと話者がズレます。
以上です。ゲームの中身が変わってもキャラクターボイスの自動生成として応用が効きそうですね。
実際に使った際の工夫
・入力ファイルと出力ファイルはそのままUnityプロジェクト内にしてます
・44の全カットシーンをまとめて実行してます
・セリフファイルにタグ行を入れて話者ごとにボイスパターンを変える
加えて、極端なピッチ変更(ドラゴン、偽物)は音量が小さくなったので音量を正規化したりしました。が、大慌てで泥臭くやったので紹介しません・・。
他に使える読み上げ音声サービス
結局、セリフテキストに対してコマンドで音声ファイルが出力出来ればなんでもいいので、好みで他のサービスを使っても良さそうです。
有料で、今回は人物ごとにボイスを変えたかったのでキツそうでした。 キャラクター性が強く出てしまうのも役者の声としてはイマイチかも? アカネちゃんときりたんが好きです。
ボイロゲームジャムとかでは良さそうですね。
Cloud Text to Speechとほぼ同等の機能のようです。ボイスのパターンが少ないと思ってGoogleにしました。
・Microsoft Azure Text to Speech
これもほぼ同等。ボイスパターン的にもGoogleに負けてないかも? やり方紹介ページでWatson vs Google って競合紹介されてて気付かなかった・・。