ここから本文です

テキスト→音声変換 「Text To Speech API」の使い方

@IT 4/14(金) 9:10配信

※本稿は2017年3月10日の情報を元に作成しています。この記事内で使用している画面やコグニティブサービスの仕様は変更になっている場合があります。

作作成したBing Speech APIのリソース

 本連載「認識系API活用入門」では、マイクロソフトのコグニティブサービスのAPIを用いて、「現在のコグニティブサービスでどのようなことができるのか」「どのようにして利用できるのか」「どの程度の精度なのか」を検証していきます。連載第1回の「Deep Learningの恩恵を手軽に活用できるコグニティブサービスとは」では、コグニティブサービスとは何かの概要とAPIを使うための準備の仕方を説明しました。

 今回はText To Speech APIを試します。

●Text To Speech APIとは

 Text To Speech APIは、テキストと言語をAPIに渡すと、その言語でテキストを発話した音声データを返してくれます。また、女性の声か男性の声かを選択できます。ちなみに日本語の女性の声は「Ayumi」、男性の声は「Ichiro」という名前だそうです。

○使用できる言語

 Text To Speech APIで使用できる言語についての最新の対応状況は下記URLで確認できます。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/overview

○活用シナリオ

 SiriやCortanaのようにユーザーと音声で対話させたいアプリケーションを作る場合、このAPIを使って、発話させたい言葉をテキストで送ることによって、アプリケーションにより自然な会話を行わせることができます。

○ドキュメント

 Text To Speech APIのドキュメントは下記URLにあります。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/API-Reference-REST/BingVoiceOutput



○利用申し込み

 Text To Speech APIを使用するにはマイクロソフトのAzureサービス上で利用開始の手続きを行う必要があります。連載第1回でサービスの申し込みの詳細な手順は紹介しているので割愛しますが、Speech To Text APIは「Bing Speech API」の1つなので、「API Type」は「Bing Speech API」を指定します。「Account Name」は下の例ではAPI名に合わせて「BingSpeechAPI」としています。「Pricing Tier(価格レベル)」は無料で使用できる「F0 Free」を指定します。


 作成ボタンをクリックすると、デプロイが開始されます。デプロイは1分もかからずに完了するかと思います。デプロイが完了したら「すべてのリソース」から先ほど作成した Bing Speech API をクリックします。

 「Resource Management」の下の「Keys」をクリックすると、Bing Speech APIにアクセスするために必要なキー(Ocp-Apim-Subscription-Key)を取得することができます。この「Key1」の値をOcp-Apim-Subscription-Keyとしてプログラム内で使用しますので、コピーしておいてください。また、このキーの値は外部に知られることのないように大切に扱ってください。


●作成するアプリケーションについて

 今回作成するアプリケーションの画面は以下のようになります。発話させたい言葉を入力するテキストボックスと、その言語、声の性別、音声ファイルの形式を指定してボタンをクリックすると、音声ファイルを取得して再生する、というアプリケーションです。


○プロジェクトの作成

 Visual Studioを起動し、プロジェクト名を「TextToSpeechAPI」として新規プロジェクトを作成します。C#のWPFアプリケーションを作成します。

○画面の作成

 MainWindow.xamlは以下の通りです(※画面部分のソースコードの解説は本連載の趣旨から外れるので割愛します)。

<Window x:Class="TextToSpeechAPI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextToSpeechAPI"
mc:Ignorable="d"
Title="Text To Speech API" Height="350" Width="525">
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="発話させたい言葉を入力してください" />
<TextBox Name="tboxSpeechText" Width="500" Height="80" TextWrapping="Wrap" />
<ComboBox Name="cmbLanguage" Width="120" Margin="0,10,0,0">
<ComboBoxItem Content="en-US" />
<ComboBoxItem Content="ja-JP" IsSelected="True" />
</ComboBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0">
<TextBlock Text="声の性別" Width="70"/>
<RadioButton Name="rdoFemale" Content="女性の声" GroupName="rdoGroupGender" IsChecked="True" />
<RadioButton Name="rdoMale" Content="男性の声" GroupName="rdoGroupGender" Margin="30,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0">
<TextBlock Text="音声ファイル形式" Width="100"/>
<RadioButton Name="rdoWAV" Content="WAV" GroupName="rdoGroupAudioFormat" IsChecked="True" />
<RadioButton Name="rdoMP3" Content="MP3" GroupName="rdoGroupAudioFormat" Margin="20,0,0,0"/>
</StackPanel>

<Button Name="btnCallTextToSpeech" Content="G O" Click="btnCallTextToSpeech_Click" Width="200" Height="50" Margin="0,20,0,0"/>
<TextBlock Name="tblkMessage" TextWrapping="Wrap"/>
<MediaElement Name="mePlayWav" />
</StackPanel>
</Grid>
</Window>



○処理の作成

 MainWindow.xaml.csで実際にプログラムから呼び出すところを作成します。プログラムの流れとしては非常にシンプルで、以下のようになります。


1. アクセスキーを使用してTokenを取得する
2. Tokenを使用して、Text To Speech APIに発話させたいテキストを渡し、音声ファイルを取得する

 前回で紹介したTranslator APIと違って、サービスが対応している言語の一覧を取得するAPIがないようなので、今回はプログラム内にリストを持つことにします(プログラムを簡便にするために、今回は英語と日本語のみとします)。

 まずアクセスキーを使用してTokenを取得する処理ですが、Tokenの取得については、Translator APIのときとまったく同じです。Tokenは10分間有効なので、前回取得時から10分以上経過していたら再取得するよう、取得した時間を変数に取っておきます。

// ----------------------------------------------
// Tokenを取得する処理
// ----------------------------------------------
string GetAccessToken()
{
try
{
//取得したBing Speech API の Key
string subscriptionKey = "xxxxxxxxxxxxxxxxxxxxxxxx";

WebRequest request = WebRequest.Create("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");

//リクエスト設定
request.Method = "POST"; // メソッドはPOST
request.Headers.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
request.ContentLength = 0;

// リクエストを投げる
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();

// レスポンスを受け取る
StreamReader streamReader = new StreamReader(responseStream);

// Tokenを取得した時間を取得
tokenRefreshedTime = DateTime.Now;

// レスポンス(Token)を返す
return streamReader.ReadToEnd();
}
catch (WebException webException)
{
Stream responseStream = webException.Response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
tblkMessage.Text = streamReader.ReadToEnd();
return null;
}
catch (Exception exception)
{
tblkMessage.Text = exception.Message;
return null;
}
}


 続いて、テキストを渡して音声ファイルを取得する処理です。APIのリクエストメソッドはPOSTになります。Text To Speech APIでは、リクエストのHTTP Header に以下の値を含めるように記載されています。


Content-Type / application/ssml+xml / The input content type
X-Microsoft-OutputFormat / 1) ssml-16khz-16bit-mono-tts, 2) raw-16khz-16bit-mono-pcm, 3) audio-16khz-16kbps-mono-siren, 4) riff-16khz-16kbps-mono-siren 5) riff-16khz-16bit-mono-pcm 6) audio-16khz-128kbitrate-mono-mp3, 7) audio-16khz-64kbitrate-mono-mp3, 8) audio-16khz-32kbitrate-mono-mp3 / The output audio format
X-Search-AppId / A GUID (hex only, no dashes) / An ID that uniquely identifies the client application. This can be Store ID for Apps. If one is not available, the ID may be user generated per-application.
X-Search-ClientID / A GUID (hex only, no dashes) / An ID that uniquely identifies application instance per-installation.
User-Agent / Application name / Application name is required and must be less than 255 characters.



 出力される音声ファイルのフォーマット(X-Microsoft-OutputFormat)については8パターンもあって「どれを使えばよいのか」悩ましいところですが、WAVなら「riff-16khz-16bit-mono-pcm」、MP3なら「audio-16khz-128kbitrate-mono-mp3」でいいかと思います(個人的には、MP3の32kbitrateと64kbitrateは、やや音が悪い印象です。ただし、通信量を減らしたいときには有効だと思います)。

 クライアントアプリケーションを一意に識別するIDとして「X-Search-AppId」を設定する必要があります。また、その下のレベルでインストールごとのアプリケーションインスタンスを一意に識別する「X-Search-ClientID」なるものまで必要です。ここが少々分かりにくいと思いますが、実際には適当な固定値でもリクエストのたびに新たなIDを生成する形でも問題ないようです。

 ただし、「Hex only, no dashes」と書いてありますので、「16進数のみ使用可能でダッシュ(-)は使えない」ことに注意してください。今回のサンプルプログラムではリクエストごとに新たなIDを生成しています。

 リクエストヘッダの他に、パラメーターも必要になります。パラメーターに含める内容は、下記3つです。

・発話の言語
・男性の声か女性の声か
・発話するテキスト

 パラメーターはSpeech Synthesis Markup Language(SSML)で記述するようになっています。サンプルプログラムでは、下記のようなSSMLを作成しています。

<speak version='1.0' xml:lang='en-US'><voice xml:lang='en-US' xml:gender='Female' name='Microsoft Server Speech Text to Speech Voice (en-US, ZiraRUS)'>Microsoft Bing Voice Output API</voice></speak>


 発話の言語および男性の声、女性の声として具体的にどのような値を設定するかは、下記URLの「Supported Locales and Voice Fonts」で確認できます。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/API-Reference-REST/BingVoiceOutput

 今回のサンプルプログラムでは英語と日本語のみにしていますので、以下のようになります。

// ----------------------------------------------
// 音声ファイルを取得する処理
// ----------------------------------------------
string GetTextToSpeechAudio(string pAccessToken, string pLanguage, string pGender, string pText, string pAudioFormat)
{
try
{
string url = "https://speech.platform.bing.com/synthesize";
string speakerName = "";
switch (pLanguage)
{
case "en-US":
if (pGender == "Female") speakerName = "ZiraRUS"; else speakerName = "BenjaminRUS";
break;
case "ja-JP":
if (pGender == "Female") speakerName = "Ayumi, Apollo"; else speakerName = "Ichiro, Apollo";
break;
}

// インプットパラメーター(SSML)作成
string requestSSML =
string.Format(
"<speak version='1.0' xml:lang='{0}'><voice xml:lang='{0}' xml:gender='{1}' name='Microsoft Server Speech Text to Speech Voice ({0}, {2})'>{3}</voice></speak>",
pLanguage,
pGender,
speakerName,
pText
);

// リクエスト作成
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/ssml+xml";
if (pAudioFormat == "WAV")
{
request.Headers.Add("X-Microsoft-OutputFormat", "riff-16khz-16bit-mono-pcm");
}
else
{
request.Headers.Add("X-Microsoft-OutputFormat", "audio-16khz-128kbitrate-mono-mp3");
}
request.Headers.Add("X-Search-AppId", (Guid.NewGuid().ToString()).Replace("-", ""));
request.Headers.Add("X-Search-ClientID", (Guid.NewGuid().ToString()).Replace("-", ""));
request.Headers.Add("Authorization", "Bearer " + pAccessToken);
request.UserAgent = "SpeechToTextDemoApp";

// パラメーターをUTF8でエンコード
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(requestSSML);
request.ContentLength = bytes.Length;

// パラメーターをリクエストに追加
System.IO.Stream s = request.GetRequestStream();
s.Write(bytes, 0, bytes.Length);
s.Close();

// リクエスト送信/レスポンス取得
WebResponse webResponse = request.GetResponse();

// レスポンスをストリームに変換
Stream responseStream = webResponse.GetResponseStream();
if (responseStream == null)
{
throw new Exception("responseStreamがNULLです。");
}

// レスポンスの中身を音声ファイルとして保存
string audioFile = "";
if (pAudioFormat == "WAV")
{
audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.wav";
}
else
{
audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.mp3";
}
FileStream fs = new FileStream(audioFile, FileMode.Create, FileAccess.Write);

byte[] readData = new byte[1024];
while(true)
{
// データを読み込む
int readSize = responseStream.Read(readData, 0, readData.Length);
if (readSize == 0)
{
// 全てのデータを読み込んだら抜ける
break;
}
// 書き込む
fs.Write(readData, 0, readSize);
}

fs.Close();
responseStream.Close();

return audioFile;

}
catch (WebException webException)
{
Stream responseStream = webException.Response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
tblkMessage.Text = streamReader.ReadToEnd();
return null;
}
catch (Exception exception)
{
tblkMessage.Text = exception.Message;
return null;
}
}




 前述の処理にコンストラクタやボタンクリック時のイベントを含めると、最終的には「MainWindow.xaml.cs」は、こちらのようになります(※クリックしてダウンロードできます)。

 どうでしょうか。特に難しいところはないのではないかと思います。

●Text To Speech APIの精度をIBM Watsonと4パターンで比較

 実際にText To Speech APIの精度が、どの程度のものなのかを検証してみましょう。ビルドしたExeを起動します。

 特に操作に難しいところはありません。しゃべらせたい言葉を入力し、日本語か英語か、声の性別、音声ファイル形式を選択して「GO」ボタンをクリックします。音声ファイルのダウンロードが終わると自動的に音声ファイルの再生を開始しますので、スピーカーをONにしておいてください。

 それでは、マイクロソフトのコグニティブサービスのWebページの日本語をしゃべらせてみましょう。今回はマイクロソフトとIBM Watsonで比較してみます。

 IBM Watsonはプログラムを作成せずに、下記URLからブラウザを使って検証を行いました。
・https://text-to-speech-demo.mybluemix.net/

※GoogleはAPIサービスとして利用できるText To Speech APIがないようなので、今回は検証の対象には含めていません。ただし、Google翻訳のサイトには入力したテキストを発話する機能がありますので、試してみたい方は、そちらをどうぞ。

 なお現段階では、マイクロソフトのText To Speech APIとIBM WatsonのText To Speech APIでは、対応している言語に違いがあります。マイクロソフトの方が対応している言語は多いようです。またマイクロソフトのText To Speechでは、日本語でも男性の声と女性の声を選択できますが、IBM WatsonのText To Speech APIでは女性の声のみのようです。ただし、将来的に改善される可能性も十分あります。

【検証1】日本語テキストを日本語音声に変換

 まずはマイクロソフトのコグニティブサービスのページから文言を使います。言語はja-JPを指定します。

 こればかりは実際に聞いていただくしかありません。日本語は少したどたどしくイントネーションも普段私たちがしゃべっているのとは異なっています。最先端の音声合成ソフトウェアと比較すると「もう一歩」という感じですが、何をしゃべっているのかは問題なく分かります。男性の声でも女性の声でも流暢さや聞き取りやすさといった点にほとんど差は感じませんでした。

 ちなみに男性のIchiroの声、しゃべり方は、お笑いコンビと女性アナウンサーの3人組が街を歩くモヤモヤな番組のナレーションにとてもよく似ていると評判です(あちらも音声合成ソフトウェアを使っているそうなので、親戚といえば親戚なのかもしれません)。

 Watsonも実際にやってみました。こちらも実際に聞いていただくしかないのですが、やはり日本語はたどたどしく、イントネーションも普段しゃべっているのとは異なっています。マイクロソフトもIBM Watson もほぼ互角という印象です。

【検証2】日本語の早口言葉テキストを日本語音声に変換

 では続いて早口言葉はどうでしょうか。

→赤巻紙青巻紙黄巻紙。東京特許許可局。新人歌手新春シャンソンショー。

 こちらもやはりイントネーションが違ったり、たどたどしさがあったりという点は変わりません。そして当たり前といえば当たり前ですが、一度も噛みません。何度やっても噛みません。ちゃんとしゃべってくれます。

 Watsonも同様に試してみましたが、やはりこちらも同じような感じでした。イントネーションはともかく、コグニティブサービスもWatsonも今回のサンプルではきちんと間違いなくしゃべってくれました。

 ただし、弱点もあります。まずは「漢字」です。

 例えば、「行った」は「いった」とも読むことができますし、「おこなった」とも読むことができます。マイクロソフトのText To Speech APIもWatsonも「東京に行った」をしゃべらせると「いった」と読んでくれますし、「作業を行った」をしゃべらせると「おこなった」と読んでくれます。どちらのサービスも文脈を解析して「いった」なのか「おこなった」なのかを判断してくれるようです。

 しかし「行った」だけをしゃべらせるとマイクロソフトのText To Speech APIは「おこなった」と読み、IBM Watsonは「いった」と読みました。これを意図する読み方で読ませたいのであれば、漢字ではなくひらがなでテキストを渡すしかないようです。

 2つ目は「記号」です。

 例えば「東京タワーの高さは333mです。」の「m」は人間が見れば「メートル」と読むことが分かりますが、マイクロソフトのText To Speech APIでは「エム」と読んでしまいました(IBM Watsonは「メートル」と読んでくれました)。「東京タワーの高さは333メートルです。」とテキストを渡してあげれば正しく読んでくれます。

 その他、「10kg」の「kg」はどちらも「キログラム」と読んでくれましたが、「10g」の「g」はマイクロソフトのText To Speech APIは「グラム」と読んでくれたのに対しIBM Watson は「ジー」と読んでしまうという結果になりました。このように、誤読を防ぐためにはこちら側がAPIにどのようにテキストを渡すかを考慮しなければならないケースがあります。

【検証3】英語テキストを英語音声に変換

 続いて、英語も試してみましょう。

 Speech To Text APIのドキュメントにある「Introduction」の文章をしゃべらせてみます。

→With the Bing Text to Speech API your application can send HTTP requests to a cloud server, where text is instantly synthesized into human sounding speech, and returned as an audio file. The API can be used in many different contexts to provide real-time text to speech conversion in a variety of different voices and languages.


 筆者はNative English Speakerではないので正確な判断はできませんが、非常に流暢にしゃべっています。日本語のような違和感はほとんど感じません。「The API」の「The」は「ザ」ではなく「ジ」の発音になっていますし、「returned as an audio file」「in a」などのlinkageも不自然な感じはなく普通にしゃべっています。何の前置きもなくこの音声を聞いたら、多くの方は本物の人間がしゃべっていると思うのではないでしょうか。

 Watsonも同様に試してみましたが、やはり非常に流暢にしゃべっています。ほぼ互角という印象です。

【検証4】英語の早口言葉テキストを英語音声に変換

 続いて英語の早口言葉をしゃべらせてみます。

→She sells sea shells by the seashore. Peter Piper picked a peck of pickled peppers. A big black bug bit a big black bear. Vincent vowed vengeance very vehemently.


 当たり前ですが、一度も噛みません。何度やっても噛みません。男性も女性も噛みません。恐ろしいくらい非常に流暢にしゃべります。Watsonもまったく同様で、非常に流暢にしゃべります。やはり互角という印象です。

●次回は今回の逆、Speech To Textを試す

 今回はテキスト→音声変換APIを取り上げましたが、いかがでしたでしょうか。

 日本語については、まだ現時点では人間や最先端の音声合成ソフトウェアのように流暢にしゃべってくれるところまでは達していませんが、漢字や記号の誤読に気を付けさえすれば用途次第では必要十分という印象です。

 英語については、筆者には「もはや人間と変わりない」と思えるほどでした。音声合成ソフトウェアを使おうとすると、それなりにハードルは高いので、手軽に利用できるコグニティブサービスは有効な代替手段になるのではないでしょうか。

 Text To Speech APIもTranslator APIと同様、あっという間に進化を遂げてアナウンサーやナレーションのような仕事はコグニティブサービスが取って代わる時代が来るのではないか、という印象を受けました。さらには、英会話教室の講師もコグニティブサービスが行う時代が来るのかもしれません。

 次回は今回の逆、Speech Recognition API(Speech To Text)を試します。お楽しみに。

※本稿は2017年3月10日の情報を元に作成しています。この記事内で使用している画面やコグニティブサービスの仕様は変更になっている場合があります。

 本連載「認識系API活用入門」では、マイクロソフトのコグニティブサービスのAPIを用いて、「現在のコグニティブサービスでどのようなことができるのか」「どのようにして利用できるのか」「どの程度の精度なのか」を検証していきます。連載第1回の「Deep Learningの恩恵を手軽に活用できるコグニティブサービスとは」では、コグニティブサービスとは何かの概要とAPIを使うための準備の仕方を説明しました。

 今回はText To Speech APIを試します。

●Text To Speech APIとは

 Text To Speech APIは、テキストと言語をAPIに渡すと、その言語でテキストを発話した音声データを返してくれます。また、女性の声か男性の声かを選択できます。ちなみに日本語の女性の声は「Ayumi」、男性の声は「Ichiro」という名前だそうです。

○使用できる言語

 Text To Speech APIで使用できる言語についての最新の対応状況は下記URLで確認できます。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/overview

○活用シナリオ

 SiriやCortanaのようにユーザーと音声で対話させたいアプリケーションを作る場合、このAPIを使って、発話させたい言葉をテキストで送ることによって、アプリケーションにより自然な会話を行わせることができます。

○ドキュメント

 Text To Speech APIのドキュメントは下記URLにあります。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/API-Reference-REST/BingVoiceOutput



○利用申し込み

 Text To Speech APIを使用するにはマイクロソフトのAzureサービス上で利用開始の手続きを行う必要があります。連載第1回でサービスの申し込みの詳細な手順は紹介しているので割愛しますが、Speech To Text APIは「Bing Speech API」の1つなので、「API Type」は「Bing Speech API」を指定します。「Account Name」は下の例ではAPI名に合わせて「BingSpeechAPI」としています。「Pricing Tier(価格レベル)」は無料で使用できる「F0 Free」を指定します。


 作成ボタンをクリックすると、デプロイが開始されます。デプロイは1分もかからずに完了するかと思います。デプロイが完了したら「すべてのリソース」から先ほど作成した Bing Speech API をクリックします。

 「Resource Management」の下の「Keys」をクリックすると、Bing Speech APIにアクセスするために必要なキー(Ocp-Apim-Subscription-Key)を取得することができます。この「Key1」の値をOcp-Apim-Subscription-Keyとしてプログラム内で使用しますので、コピーしておいてください。また、このキーの値は外部に知られることのないように大切に扱ってください。


●作成するアプリケーションについて

 今回作成するアプリケーションの画面は以下のようになります。発話させたい言葉を入力するテキストボックスと、その言語、声の性別、音声ファイルの形式を指定してボタンをクリックすると、音声ファイルを取得して再生する、というアプリケーションです。


○プロジェクトの作成

 Visual Studioを起動し、プロジェクト名を「TextToSpeechAPI」として新規プロジェクトを作成します。C#のWPFアプリケーションを作成します。

○画面の作成

 MainWindow.xamlは以下の通りです(※画面部分のソースコードの解説は本連載の趣旨から外れるので割愛します)。

<Window x:Class="TextToSpeechAPI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextToSpeechAPI"
mc:Ignorable="d"
Title="Text To Speech API" Height="350" Width="525">
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="発話させたい言葉を入力してください" />
<TextBox Name="tboxSpeechText" Width="500" Height="80" TextWrapping="Wrap" />
<ComboBox Name="cmbLanguage" Width="120" Margin="0,10,0,0">
<ComboBoxItem Content="en-US" />
<ComboBoxItem Content="ja-JP" IsSelected="True" />
</ComboBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0">
<TextBlock Text="声の性別" Width="70"/>
<RadioButton Name="rdoFemale" Content="女性の声" GroupName="rdoGroupGender" IsChecked="True" />
<RadioButton Name="rdoMale" Content="男性の声" GroupName="rdoGroupGender" Margin="30,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0">
<TextBlock Text="音声ファイル形式" Width="100"/>
<RadioButton Name="rdoWAV" Content="WAV" GroupName="rdoGroupAudioFormat" IsChecked="True" />
<RadioButton Name="rdoMP3" Content="MP3" GroupName="rdoGroupAudioFormat" Margin="20,0,0,0"/>
</StackPanel>

<Button Name="btnCallTextToSpeech" Content="G O" Click="btnCallTextToSpeech_Click" Width="200" Height="50" Margin="0,20,0,0"/>
<TextBlock Name="tblkMessage" TextWrapping="Wrap"/>
<MediaElement Name="mePlayWav" />
</StackPanel>
</Grid>
</Window>



○処理の作成

 MainWindow.xaml.csで実際にプログラムから呼び出すところを作成します。プログラムの流れとしては非常にシンプルで、以下のようになります。


1. アクセスキーを使用してTokenを取得する
2. Tokenを使用して、Text To Speech APIに発話させたいテキストを渡し、音声ファイルを取得する

 前回で紹介したTranslator APIと違って、サービスが対応している言語の一覧を取得するAPIがないようなので、今回はプログラム内にリストを持つことにします(プログラムを簡便にするために、今回は英語と日本語のみとします)。

 まずアクセスキーを使用してTokenを取得する処理ですが、Tokenの取得については、Translator APIのときとまったく同じです。Tokenは10分間有効なので、前回取得時から10分以上経過していたら再取得するよう、取得した時間を変数に取っておきます。

// ----------------------------------------------
// Tokenを取得する処理
// ----------------------------------------------
string GetAccessToken()
{
try
{
//取得したBing Speech API の Key
string subscriptionKey = "xxxxxxxxxxxxxxxxxxxxxxxx";

WebRequest request = WebRequest.Create("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");

//リクエスト設定
request.Method = "POST"; // メソッドはPOST
request.Headers.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
request.ContentLength = 0;

// リクエストを投げる
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();

// レスポンスを受け取る
StreamReader streamReader = new StreamReader(responseStream);

// Tokenを取得した時間を取得
tokenRefreshedTime = DateTime.Now;

// レスポンス(Token)を返す
return streamReader.ReadToEnd();
}
catch (WebException webException)
{
Stream responseStream = webException.Response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
tblkMessage.Text = streamReader.ReadToEnd();
return null;
}
catch (Exception exception)
{
tblkMessage.Text = exception.Message;
return null;
}
}


 続いて、テキストを渡して音声ファイルを取得する処理です。APIのリクエストメソッドはPOSTになります。Text To Speech APIでは、リクエストのHTTP Header に以下の値を含めるように記載されています。


Content-Type / application/ssml+xml / The input content type
X-Microsoft-OutputFormat / 1) ssml-16khz-16bit-mono-tts, 2) raw-16khz-16bit-mono-pcm, 3) audio-16khz-16kbps-mono-siren, 4) riff-16khz-16kbps-mono-siren 5) riff-16khz-16bit-mono-pcm 6) audio-16khz-128kbitrate-mono-mp3, 7) audio-16khz-64kbitrate-mono-mp3, 8) audio-16khz-32kbitrate-mono-mp3 / The output audio format
X-Search-AppId / A GUID (hex only, no dashes) / An ID that uniquely identifies the client application. This can be Store ID for Apps. If one is not available, the ID may be user generated per-application.
X-Search-ClientID / A GUID (hex only, no dashes) / An ID that uniquely identifies application instance per-installation.
User-Agent / Application name / Application name is required and must be less than 255 characters.



 出力される音声ファイルのフォーマット(X-Microsoft-OutputFormat)については8パターンもあって「どれを使えばよいのか」悩ましいところですが、WAVなら「riff-16khz-16bit-mono-pcm」、MP3なら「audio-16khz-128kbitrate-mono-mp3」でいいかと思います(個人的には、MP3の32kbitrateと64kbitrateは、やや音が悪い印象です。ただし、通信量を減らしたいときには有効だと思います)。

 クライアントアプリケーションを一意に識別するIDとして「X-Search-AppId」を設定する必要があります。また、その下のレベルでインストールごとのアプリケーションインスタンスを一意に識別する「X-Search-ClientID」なるものまで必要です。ここが少々分かりにくいと思いますが、実際には適当な固定値でもリクエストのたびに新たなIDを生成する形でも問題ないようです。

 ただし、「Hex only, no dashes」と書いてありますので、「16進数のみ使用可能でダッシュ(-)は使えない」ことに注意してください。今回のサンプルプログラムではリクエストごとに新たなIDを生成しています。

 リクエストヘッダの他に、パラメーターも必要になります。パラメーターに含める内容は、下記3つです。

・発話の言語
・男性の声か女性の声か
・発話するテキスト

 パラメーターはSpeech Synthesis Markup Language(SSML)で記述するようになっています。サンプルプログラムでは、下記のようなSSMLを作成しています。

<speak version='1.0' xml:lang='en-US'><voice xml:lang='en-US' xml:gender='Female' name='Microsoft Server Speech Text to Speech Voice (en-US, ZiraRUS)'>Microsoft Bing Voice Output API</voice></speak>


 発話の言語および男性の声、女性の声として具体的にどのような値を設定するかは、下記URLの「Supported Locales and Voice Fonts」で確認できます。

・https://www.microsoft.com/cognitive-services/en-us/Speech-api/documentation/API-Reference-REST/BingVoiceOutput

 今回のサンプルプログラムでは英語と日本語のみにしていますので、以下のようになります。

// ----------------------------------------------
// 音声ファイルを取得する処理
// ----------------------------------------------
string GetTextToSpeechAudio(string pAccessToken, string pLanguage, string pGender, string pText, string pAudioFormat)
{
try
{
string url = "https://speech.platform.bing.com/synthesize";
string speakerName = "";
switch (pLanguage)
{
case "en-US":
if (pGender == "Female") speakerName = "ZiraRUS"; else speakerName = "BenjaminRUS";
break;
case "ja-JP":
if (pGender == "Female") speakerName = "Ayumi, Apollo"; else speakerName = "Ichiro, Apollo";
break;
}

// インプットパラメーター(SSML)作成
string requestSSML =
string.Format(
"<speak version='1.0' xml:lang='{0}'><voice xml:lang='{0}' xml:gender='{1}' name='Microsoft Server Speech Text to Speech Voice ({0}, {2})'>{3}</voice></speak>",
pLanguage,
pGender,
speakerName,
pText
);

// リクエスト作成
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/ssml+xml";
if (pAudioFormat == "WAV")
{
request.Headers.Add("X-Microsoft-OutputFormat", "riff-16khz-16bit-mono-pcm");
}
else
{
request.Headers.Add("X-Microsoft-OutputFormat", "audio-16khz-128kbitrate-mono-mp3");
}
request.Headers.Add("X-Search-AppId", (Guid.NewGuid().ToString()).Replace("-", ""));
request.Headers.Add("X-Search-ClientID", (Guid.NewGuid().ToString()).Replace("-", ""));
request.Headers.Add("Authorization", "Bearer " + pAccessToken);
request.UserAgent = "SpeechToTextDemoApp";

// パラメーターをUTF8でエンコード
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(requestSSML);
request.ContentLength = bytes.Length;

// パラメーターをリクエストに追加
System.IO.Stream s = request.GetRequestStream();
s.Write(bytes, 0, bytes.Length);
s.Close();

// リクエスト送信/レスポンス取得
WebResponse webResponse = request.GetResponse();

// レスポンスをストリームに変換
Stream responseStream = webResponse.GetResponseStream();
if (responseStream == null)
{
throw new Exception("responseStreamがNULLです。");
}

// レスポンスの中身を音声ファイルとして保存
string audioFile = "";
if (pAudioFormat == "WAV")
{
audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.wav";
}
else
{
audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.mp3";
}
FileStream fs = new FileStream(audioFile, FileMode.Create, FileAccess.Write);

byte[] readData = new byte[1024];
while(true)
{
// データを読み込む
int readSize = responseStream.Read(readData, 0, readData.Length);
if (readSize == 0)
{
// 全てのデータを読み込んだら抜ける
break;
}
// 書き込む
fs.Write(readData, 0, readSize);
}

fs.Close();
responseStream.Close();

return audioFile;

}
catch (WebException webException)
{
Stream responseStream = webException.Response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
tblkMessage.Text = streamReader.ReadToEnd();
return null;
}
catch (Exception exception)
{
tblkMessage.Text = exception.Message;
return null;
}
}




 前述の処理にコンストラクタやボタンクリック時のイベントを含めると、最終的には「MainWindow.xaml.cs」は、こちらのようになります(※クリックしてダウンロードできます)。

 どうでしょうか。特に難しいところはないのではないかと思います。

●Text To Speech APIの精度をIBM Watsonと4パターンで比較

 実際にText To Speech APIの精度が、どの程度のものなのかを検証してみましょう。ビルドしたExeを起動します。

 特に操作に難しいところはありません。しゃべらせたい言葉を入力し、日本語か英語か、声の性別、音声ファイル形式を選択して「GO」ボタンをクリックします。音声ファイルのダウンロードが終わると自動的に音声ファイルの再生を開始しますので、スピーカーをONにしておいてください。

 それでは、マイクロソフトのコグニティブサービスのWebページの日本語をしゃべらせてみましょう。今回はマイクロソフトとIBM Watsonで比較してみます。

 IBM Watsonはプログラムを作成せずに、下記URLからブラウザを使って検証を行いました。
・https://text-to-speech-demo.mybluemix.net/

※GoogleはAPIサービスとして利用できるText To Speech APIがないようなので、今回は検証の対象には含めていません。ただし、Google翻訳のサイトには入力したテキストを発話する機能がありますので、試してみたい方は、そちらをどうぞ。

 なお現段階では、マイクロソフトのText To Speech APIとIBM WatsonのText To Speech APIでは、対応している言語に違いがあります。マイクロソフトの方が対応している言語は多いようです。またマイクロソフトのText To Speechでは、日本語でも男性の声と女性の声を選択できますが、IBM WatsonのText To Speech APIでは女性の声のみのようです。ただし、将来的に改善される可能性も十分あります。

【検証1】日本語テキストを日本語音声に変換

 まずはマイクロソフトのコグニティブサービスのページから文言を使います。言語はja-JPを指定します。

 こればかりは実際に聞いていただくしかありません。日本語は少したどたどしくイントネーションも普段私たちがしゃべっているのとは異なっています。最先端の音声合成ソフトウェアと比較すると「もう一歩」という感じですが、何をしゃべっているのかは問題なく分かります。男性の声でも女性の声でも流暢さや聞き取りやすさといった点にほとんど差は感じませんでした。

 ちなみに男性のIchiroの声、しゃべり方は、お笑いコンビと女性アナウンサーの3人組が街を歩くモヤモヤな番組のナレーションにとてもよく似ていると評判です(あちらも音声合成ソフトウェアを使っているそうなので、親戚といえば親戚なのかもしれません)。

 Watsonも実際にやってみました。こちらも実際に聞いていただくしかないのですが、やはり日本語はたどたどしく、イントネーションも普段しゃべっているのとは異なっています。マイクロソフトもIBM Watson もほぼ互角という印象です。

【検証2】日本語の早口言葉テキストを日本語音声に変換

 では続いて早口言葉はどうでしょうか。

→赤巻紙青巻紙黄巻紙。東京特許許可局。新人歌手新春シャンソンショー。

 こちらもやはりイントネーションが違ったり、たどたどしさがあったりという点は変わりません。そして当たり前といえば当たり前ですが、一度も噛みません。何度やっても噛みません。ちゃんとしゃべってくれます。

 Watsonも同様に試してみましたが、やはりこちらも同じような感じでした。イントネーションはともかく、コグニティブサービスもWatsonも今回のサンプルではきちんと間違いなくしゃべってくれました。

 ただし、弱点もあります。まずは「漢字」です。

 例えば、「行った」は「いった」とも読むことができますし、「おこなった」とも読むことができます。マイクロソフトのText To Speech APIもWatsonも「東京に行った」をしゃべらせると「いった」と読んでくれますし、「作業を行った」をしゃべらせると「おこなった」と読んでくれます。どちらのサービスも文脈を解析して「いった」なのか「おこなった」なのかを判断してくれるようです。

 しかし「行った」だけをしゃべらせるとマイクロソフトのText To Speech APIは「おこなった」と読み、IBM Watsonは「いった」と読みました。これを意図する読み方で読ませたいのであれば、漢字ではなくひらがなでテキストを渡すしかないようです。

 2つ目は「記号」です。

 例えば「東京タワーの高さは333mです。」の「m」は人間が見れば「メートル」と読むことが分かりますが、マイクロソフトのText To Speech APIでは「エム」と読んでしまいました(IBM Watsonは「メートル」と読んでくれました)。「東京タワーの高さは333メートルです。」とテキストを渡してあげれば正しく読んでくれます。

 その他、「10kg」の「kg」はどちらも「キログラム」と読んでくれましたが、「10g」の「g」はマイクロソフトのText To Speech APIは「グラム」と読んでくれたのに対しIBM Watson は「ジー」と読んでしまうという結果になりました。このように、誤読を防ぐためにはこちら側がAPIにどのようにテキストを渡すかを考慮しなければならないケースがあります。

【検証3】英語テキストを英語音声に変換

 続いて、英語も試してみましょう。

 Speech To Text APIのドキュメントにある「Introduction」の文章をしゃべらせてみます。

→With the Bing Text to Speech API your application can send HTTP requests to a cloud server, where text is instantly synthesized into human sounding speech, and returned as an audio file. The API can be used in many different contexts to provide real-time text to speech conversion in a variety of different voices and languages.


 筆者はNative English Speakerではないので正確な判断はできませんが、非常に流暢にしゃべっています。日本語のような違和感はほとんど感じません。「The API」の「The」は「ザ」ではなく「ジ」の発音になっていますし、「returned as an audio file」「in a」などのlinkageも不自然な感じはなく普通にしゃべっています。何の前置きもなくこの音声を聞いたら、多くの方は本物の人間がしゃべっていると思うのではないでしょうか。

 Watsonも同様に試してみましたが、やはり非常に流暢にしゃべっています。ほぼ互角という印象です。

【検証4】英語の早口言葉テキストを英語音声に変換

 続いて英語の早口言葉をしゃべらせてみます。

→She sells sea shells by the seashore. Peter Piper picked a peck of pickled peppers. A big black bug bit a big black bear. Vincent vowed vengeance very vehemently.


 当たり前ですが、一度も噛みません。何度やっても噛みません。男性も女性も噛みません。恐ろしいくらい非常に流暢にしゃべります。Watsonもまったく同様で、非常に流暢にしゃべります。やはり互角という印象です。

●次回は今回の逆、Speech To Textを試す

 今回はテキスト→音声変換APIを取り上げましたが、いかがでしたでしょうか。

 日本語については、まだ現時点では人間や最先端の音声合成ソフトウェアのように流暢にしゃべってくれるところまでは達していませんが、漢字や記号の誤読に気を付けさえすれば用途次第では必要十分という印象です。

 英語については、筆者には「もはや人間と変わりない」と思えるほどでした。音声合成ソフトウェアを使おうとすると、それなりにハードルは高いので、手軽に利用できるコグニティブサービスは有効な代替手段になるのではないでしょうか。

 Text To Speech APIもTranslator APIと同様、あっという間に進化を遂げてアナウンサーやナレーションのような仕事はコグニティブサービスが取って代わる時代が来るのではないか、という印象を受けました。さらには、英会話教室の講師もコグニティブサービスが行う時代が来るのかもしれません。

 次回は今回の逆、Speech Recognition API(Speech To Text)を試します。お楽しみに。

[岩本禎史,株式会社クロスキャット]

最終更新:4/14(金) 9:10

@IT