Windows 8.1用のストア・アプリで追加された「テキスト・データの読み上げ機能」の使い方を解説。発声のピッチやレートの調整や、使用上の注意点を説明する。
powered by Insider.NET
Windows 8.1(以降、Win 8.1)用のWindowsストア・アプリ(以降、Win 8.1アプリ)からは、新しく音声合成(別名「text-to-speech(TTS)」)機能を利用してテキスト・データの読み上げができるようになった。Windowsに複数の「声」がインストールされていれば、「声」を切り替えることもできる。また、「SSML」で読み上げるデータを与えることにより、発声のピッチやレートなどの調整も可能だ。本稿では、テキスト・データを読み上げる方法と注意点を解説する。本稿のサンプルは「Windows Store app samples:MetroTips #57(Windows 8.1版)」からダウンロードできる。
Win 8.1アプリを開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿ではOracle VM VirtualBox上で64bit版Windows 8.1 Pro Preview(日本語版)とVisual Studio Express 2013 Preview for Windows(日本語版)を使用してプログラミングしている。これらを準備する方法や注意事項は、「WinRT/Metro TIPS: Win8用のソース・コードをWin8.1用に変換するには?[Win 8.1]」の記事をご参照いただきたい。また、本稿のソース・コードは、64bit版Windows 8.1 Pro(日本語版の製品版)とVisual Studio Express 2013 for Windows(日本語版の製品版)*1を使って動作を確認している。
*1マイクロソフト公式ダウンロード・センターの「Microsoft Visual Studio Express 2013 for Windows」から無償で入手できる。
Windowsの音声合成機能はWindows XPから始まり、.NET Frameworkでは3.0(Windows Vista時代)以降でこの機能が利用可能となり、そしてWin 8.1ではWindowsストア・アプリ用のAPIが用意された。それがWindows.Media.SpeechSynthesis名前空間である。
音声合成エンジン(「声」と呼ぶことにする)*2の機能へのアクセスを提供するSpeechSynthesizerクラスや、「声」の情報を提供するVoiceInformationクラスなどがあり、それらを利用してテキストを読み上げさせる。
*2「声」は言語ごとに分かれている。日本語版のWin 8.1では、英語1声/日本語1声(合わせて2声)だけが既定でインストールされているようだ。ほかの「声」を追加インストールする方法は、本稿末尾に掲載しておく。
SpeechSynthesizerオブジェクトを作り、それにプレーン・テキストを与えて音声合成ストリームを生成させ、そのストリームを再生すればよい。
まず、最後に再生するためのMediaElementコントロール(Windows.UI.Xaml.Controls名前空間)を画面に配置する(次のコード)。
……省略……
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
……省略……
</Grid.RowDefinitions>
<MediaElement x:Name="mediaElement1" Volume="100" />
……省略……
そうしたら、コードビハインドで次のようなコードを書けばよい。
string text = ……省略……; // 読み上げるテキスト
// 合成した音声を受け取るストリーム
Windows.Media.SpeechSynthesis.SpeechSynthesisStream stream;
// SpeechSynthesizerオブジェクトを作る
using (var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer())
{
// プレーン・テキストから合成音声ストリームを生成する
stream = await synth.SynthesizeTextToStreamAsync(text);
}
// ストリームをMediaElementコントロールに渡して、再生させる
this.mediaElement1.SetSource(stream, stream.ContentType);
this.mediaElement1.Play();
Dim text As String = ……省略…… ' 読み上げるテキスト
' 合成した音声を受け取るストリーム
Dim stream As Windows.Media.SpeechSynthesis.SpeechSynthesisStream
' SpeechSynthesizerオブジェクトを作る
Using synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer()
' プレーン・テキストから合成音声ストリームを生成する
stream = Await synth.SynthesizeTextToStreamAsync(text)
End Using
' ストリームをMediaElementコントロールに渡して、再生させる
Me.mediaElement1.SetSource(stream, stream.ContentType)
Me.mediaElement1.Play()
これだけでプレーン・テキストの読み上げができる。読み上げる「声」は既定のもので、Win 8.1日本語版では「Microsoft Haruka Desktop」という女性の声だ。
Win 8.1には複数の「声」がインストールされていることがある。筆者の環境ではデフォルトで「Microsoft Haruka Desktop」(日本語/女性)と「Microsoft Zira Desktop」(米国英語/女性)の2つが入っていた。
Windowsストア・アプリから利用できる「声」の一覧を得るには、SpeechSynthesizerクラスのAllVoicesプロパティを参照すればよい。これはVoiceInformationオブジェクトのコレクションであり、ここに入っている「声」だけが読み上げに利用できる。
VoiceInformationクラスの主なプロパティを次の表に載せておく。いずれも、読み出しのみで、変更できない。
プロパティ | 説明 |
---|---|
DisplayName | 表示名。 「Microsoft Haruka Desktop」や「Microsoft Zira Desktop」など |
Gender | 声の性別 |
Id | その「声」が登録されているレジストリのパス*3 |
Language | 言語。 「ja-JP」や「en-US」など |
VoiceInformationクラスの主なプロパティ |
なお、サード・パーティ製の音声合成エンジンは、インストールされていてもWindowsストア・アプリからは利用できないことがあるようだ。注意してほしい。
*3音声合成エンジンが登録されているレジストリは、「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens」の下にある。
SpeechSynthesizerオブジェクトを作った後で、そのVoiceプロパティにVoiceInformationオブジェクトをセットすればよい。
ただし、前述したようにVoiceInformationオブジェクトはSpeechSynthesizerクラスのAllVoicesプロパティから取得しなければならない。例えば「Microsoft Zira Desktop」(米国英語/女性)を使ってプレーン・テキストを読み上げるコードは次のようになる。
string text = ……省略……; // 読み上げるテキスト
// VoiceInformation「Zira」を取得
Windows.Media.SpeechSynthesis.VoiceInformation zira
= Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices
.FirstOrDefault(v => v.DisplayName.Contains("Zira"));
// 合成した音声を受け取るストリーム
Windows.Media.SpeechSynthesis.SpeechSynthesisStream stream;
// SpeechSynthesizerオブジェクトを作る
using (var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer())
{
// 「Zira」を指定
synth.Voice = zira;
// プレーン・テキストから音声合成ストリームを生成する
stream = await synth.SynthesizeTextToStreamAsync(text);
}
// ストリームをMediaElementコントロールに渡して、再生させる
this.mediaElement1.SetSource(stream, stream.ContentType);
this.mediaElement1.Play();
Dim text As String = ……省略…… ' 読み上げるテキスト
' VoiceInformation「Zira」を取得
Dim zira As Windows.Media.SpeechSynthesis.VoiceInformation _
= Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices _
.FirstOrDefault(Function(v) v.DisplayName.Contains("Zira"))
' 合成した音声を受け取るストリーム
Dim stream As Windows.Media.SpeechSynthesis.SpeechSynthesisStream
' SpeechSynthesizerオブジェクトを作る
Using synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer()
' 「Zira」を指定
synth.Voice = zira
' プレーン・テキストから合成音声ストリームを生成する
stream = Await synth.SynthesizeTextToStreamAsync(text)
End Using
' ストリームをMediaElementコントロールに渡して、再生させる
Me.mediaElement1.SetSource(stream, stream.ContentType)
Me.mediaElement1.Play()
○英語の「声」に日本語テキストを読み上げさせると?
ところで、上のコードで日本語のテキスト、例えば「こんにちは」などを「Microsoft Zira Desktop」(米国英語/女性)に読み上げさせたらどうなるだろうか?
実際に試してもらえば分かるが、全く音が出ない。それぞれの「声」は、与えられたテキストを自分の言語(=Languageプロパティ)だけで読み上げようとするためだ。そのため、知らない言語表記は読み飛ばすし、発音もそれぞれの言語による*4。
*4例えば「BMW」を、「Microsoft Zira Desktop」(米国英語/女性)は「ビー・エム・ダブリュー」のように発音し、「Microsoft Hedda Desktop」(ドイツ語/女性)は「ビー・エム・ヴィー」のように読み上げる。なお付け加えておくと、音声合成エンジンに搭載されている発声辞書とIMEの変換辞書は別物であるため、筆者の環境ではカナ漢字変換で「さんごくし」は「三国志」になるが、「Microsoft Haruka Desktop」(日本語/女性)では「三国志」は「さんごくこころざし」と読み上げられる。
「声」によっては日本語の読み上げができないとなると、世界中に配布するようなアプリでは読み上げが可能かどうか確認する必要が出てくるだろう。それには、前に掲載した「VoiceInformationクラスの主なプロパティ」表にあるLanguageプロパティを確認すればよい。
例えば、日本語の読み上げができない場合にメッセージ・ダイアログを出すには、次のコードのようにする。
// 特定の言語(ここではja-JP)を喋るVoiceInformationが存在することを確認する
const string language= "ja-JP";
var allVoices = Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices;
var voice = allVoices.FirstOrDefault(v =>
string.Equals(v.Language, language, StringComparison.OrdinalIgnoreCase)
);
if (voice == null)
{
await (new Windows.UI.Popups.MessageDialog(
"¥"" + language + "¥"を読めるSpeechSynthesizerが搭載されていません。"
)
).ShowAsync();
}
' 特定の言語(ここではja-JP)を喋るVoiceInformationが存在することを確認する
Const language As String = "ja-JP"
Dim allVoices = Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices
Dim voice = allVoices.FirstOrDefault(Function(v) _
String.Equals(v.Language, language, StringComparison.OrdinalIgnoreCase)
)
If (voice Is Nothing) Then
Await (New Windows.UI.Popups.MessageDialog(
"""" + language + """を読めるSpeechSynthesizerが搭載されていません。"
)
).ShowAsync()
End If
音声の再生に使っているMediaElementコントロールのPlayメソッドは、非同期で動作する。しかし、Taskオブジェクト(System.Threading.Tasks名前空間)などを返してくれるわけではないため、読み上げがいつ終わったのか分からない*5。そのタイミングを知るには、MediaElementコントロールのMediaEndedイベントを利用する(次のコード)。
<MediaElement x:Name="mediaElement1" Volume="100" MediaEnded="mediaElement1_MediaEnded" />
*5ここまでのサンプル・コードで、SpeechSynthesisStreamオブジェクト(変数「stream」)のDisposeメソッドを呼び出していないことに疑問を持ってくれた読者もいるだろう。MediaElementコントロールのPlayメソッドを呼び出した直後にSpeechSynthesisStreamオブジェクトを解放してしまうと、非同期で動作しているPlayメソッドの内部では解放後のストリームを再生することになってしまう(=無音になる)ので、解放する処理を書けなかったのだ。SpeechSynthesisStreamオブジェクトを明示的に解放するには、ここで説明したMediaEndedイベント・ハンドラで行わねばならない。なお、Disposeパターンが正しく実装されているオブジェクトでは、ガベージ・コレクタがオブジェクトのファイナライザを自動的に呼び出したときにリソースが解放されるので、コードからDisposeメソッドを呼び出していなくてもメモリ・リークなどを起こすことはない。公開するサンプル・ソースでは、ローカル変数として宣言したSpeechSynthesisStreamオブジェクトのDisposeメソッドを呼び出すためにかなり複雑なことをやっているが、通常はそこまでする必要はなく、ガベージ・コレクタに任せればよい。
SpeechSynthesizerオブジェクトを使うと、プレーン・テキストだけでなく、「Speech Synthesis Markup Language(SSML)」で記述したテキストも読み上げられる。SSMLはW3Cの規格で、MSDNからは「Speech Synthesis Markup Language (SSML) Version 1.0」へのリンクが張られている(SSMLの最新版はVersion 1.1)。
SSMLの仕様の説明は割愛するが、次のコードのようにしてSpeechSynthesizerオブジェクトのSynthesizeSsmlToStreamAsyncメソッドを使えばよい。
string ssml =
@"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='ja-JP'>
今日は、よい天気です。
</speak>"; // 読み上げるSSMLテキスト
// 合成した音声を受け取るストリーム
Windows.Media.SpeechSynthesis.SpeechSynthesisStream stream;
// SpeechSynthesizerオブジェクトを作る
using (var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer())
{
// SSMLテキストから音声合成ストリームを生成する
stream = await synth. SynthesizeSsmlToStreamAsync(ssml);
}
// ストリームをMediaElementコントロールに渡して、再生させる
this.mediaElement1.SetSource(stream, stream.ContentType);
this.mediaElement1.Play();
Dim ssml As String =
"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='ja-JP'>" + vbCr +
"今日は、よい天気です。" + vbCr +
"</speak>" ' 読み上げるSSMLテキスト
' 合成した音声を受け取るストリーム
Dim stream As Windows.Media.SpeechSynthesis.SpeechSynthesisStream
' SpeechSynthesizerオブジェクトを作る
Using synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer()
' SSMLテキストから合成音声ストリームを生成する
stream = Await synth.SynthesizeSsmlToStreamAsync(ssml)
End Using
' ストリームをMediaElementコントロールに渡して、再生させる
Me.mediaElement1.SetSource(stream, stream.ContentType)
Me.mediaElement1.Play()
なお、SSMLの開始タグには「xml:lang」属性で言語を指定しなければならないが、その指定はSpeechSynthesizerクラスのLanguageプロパティに対する指定よりも優先する。
SSMLを使うと、声の高さ(=ピッチ)/速度(=レート)/音量を調整できる。プレーン・テキストを読み上げる場合にも、それをSSMLに埋め込むことで調整が可能になる。SSMLの「prosody」タグを使って、次のコードのようにする。
// パラメータ
string lang = "ja-JP";
string _pitch = "high"; // ピッチ:high/medium/low/±○○Hzなど
string _rate = "slow"; // レート:fast/medium/slow/○○%など
string _volume = "loud"; // 音量:loud/medium/soft/0.0〜100.0など
string text = ……省略……; // 読み上げるテキスト
// SSMLに埋め込むテキストはHTMLエンコードしておく
string htmlEncoded = System.Net.WebUtility.HtmlEncode(text);
// SSMLのタグで囲み、<prosody>タグの属性でピッチやレートなどを設定する
string ssml = string.Format(
@"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='{0}'>
<prosody pitch='{1}' rate='{2}' volume='{3}'>{4}</prosody>
</speak>", lang, _pitch, _rate, _volume, htmlEncoded);
// 合成した音声を受け取るストリーム
Windows.Media.SpeechSynthesis.SpeechSynthesisStream stream;
// SpeechSynthesizerオブジェクトを作る
using (var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer())
{
// SSMLテキストから合成音声ストリームを生成する
stream = await synth.SynthesizeSsmlToStreamAsync(ssml);
}
// ストリームをMediaElementコントロールに渡して、再生させる
this.mediaElement1.SetSource(stream, stream.ContentType);
this.mediaElement1.Play();
' パラメータ
Dim lang As String = "ja-JP"
Dim _pitch As String = "high" ' ピッチ:high/medium/low/± ○○Hzなど
Dim _rate As String = "slow" ' レート:fast/medium/slow/○○%など
Dim _volume As String = "loud" ' 音量:loud/medium/soft/0.0〜100.0など
Dim text As String = ……省略…… '読み上げるテキスト
' SSMLに埋め込むテキストはHTMLエンコードしておく
Dim htmlEncoded As String = System.Net.WebUtility.HtmlEncode(text)
' SSMLのタグで囲み、<prosody>タグの属性でピッチやレートなどを設定する
Dim ssml As String = String.Format(
"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='{0}'>" + vbCr +
"<prosody pitch='{1}' rate='{2}' volume='{3}'>{4}</prosody>" + vbCr +
"</speak>", lang, _pitch, _rate, _volume, htmlEncoded)
' 合成した音声を受け取るストリーム
Dim stream As Windows.Media.SpeechSynthesis.SpeechSynthesisStream
' SpeechSynthesizerオブジェクトを作る
Using synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer()
' SSMLテキストから合成音声ストリームを生成する
stream = Await synth.SynthesizeSsmlToStreamAsync(ssml)
End Using
' ストリームをMediaElementコントロールに渡して、再生させる
Me.mediaElement1.SetSource(stream, stream.ContentType)
Me.mediaElement1.Play()
SpeechSynthesizerクラスを使うと簡単にテキストの読み上げができる。ただし、Windowsの言語によっては、デフォルト状態で日本語の読み上げが可能とは限らない。また、SSMLを利用すればピッチやレートなどの微調整ができる。
音声合成については、次のドキュメントも参照してほしい。
Win 8.1には、コントロール・パネルから「声」を追加インストールできる(手動操作)。
例として、ドイツ語の「声」を追加する手順を紹介しよう。まず、コントロール・パネルで[時計、言語、および地域]−[言語]を開く。[言語の追加]をクリックして切り替わった画面の右上の検索ボックスに「ドイツ」と入力して検索する(次の画像)。
*6画像のように[ドイツ語]を選んだ状態では、[ドイツ語]にさらに複数の選択肢があるため[開く]ボタンになっている。選択していないか、選択していてもさらなる選択肢がない場合には[追加]ボタンに変わる。例えば、「英国」で検索すると英国英語(=「en-GB」)だけに絞り込まれるので、上の画像の[開く]ボタンの表記が「追加」となり、その[追加]ボタンをクリックするだけで言語が追加される。
上の画像で、[ドイツ語]を選んで[開く]ボタンをクリックすると複数のドイツ語が提示される。そこで[ドイツ語(ドイツ)]を選んでまた[追加]ボタンをクリックすると、ドイツ国のドイツ語(=「de-DE」)が追加されて画面が切り替わる(次の画像)。
上の画像のように追加されたドイツ語(=「Deutsch(Deutschland)」)の右にある[オプション]リンクをクリックすると、[言語のオプション]画面に切り替わる(次の画像)。
上の画像の画面で、[言語パックをダウンロードしてインストールします]というリンクをクリックすると、ドイツ国のドイツ語のための言語パックがダウンロードされてインストールされる(次の画像)。言語パックには、読み上げに使う「声」も含まれている。
これで、ドイツ語の読み上げもできるようになった。正しくインストールされたことを確認するには、レジストリの「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens」の下を見る。
Copyright© Digital Advantage Corp. All Rights Reserved.