連載
» 2013年11月07日 15時39分 公開

WinRT/Metro TIPS:Windows 8.1の新機能、テキスト読み上げを利用するには?[Windows 8.1ストア・アプリ開発]

Windows 8.1用のストア・アプリで追加された「テキスト・データの読み上げ機能」の使い方を解説。発声のピッチやレートの調整や、使用上の注意点を説明する。

[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 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.Media.SpeechSynthesis名前空間

 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" />
……省略……

MediaElementコントロールを画面に配置した(XAML)
太字の部分を追加する。
どこに置いてもよいが、ここではトップ・レベルのグリッド・コントロールの行定義部分の直後に置いた。また、Buttonコントロールなどを使って適当なUIを作成し、イベント・ハンドラを追加しておこう。なお、公開するサンプル・ソースではUIを作り込み、ロジックも本稿で紹介するものよりも込み入ったものにしてあるので参考にしてほしい。

 そうしたら、コードビハインドで次のようなコードを書けばよい。

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()

既定の「声」でプレーン・テキストを読み上げる(上:C#、下:VB)
メイン画面にButtonコントロールを配置してイベント・ハンドラを作成したうえで、上記のコードを記述する。また、非同期呼び出しを行っているのでasync/Asyncキーワードの追加も必要だ。

 これだけでプレーン・テキストの読み上げができる。読み上げる「声」は既定のもので、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()

特定の「声」でプレーン・テキストを読み上げる(上:C#、下:VB)
太字の部分を追加する。
ここではエラー処理を省いている。実際には、その「声」がインストールされていなければ変数「zira」がnull/Nothingになるので、その対処が必要だ。

○英語の「声」に日本語テキストを読み上げさせると?

 ところで、上のコードで日本語のテキスト、例えば「こんにちは」などを「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

日本語の読み上げができない場合にメッセージ・ダイアログを出す(上:C#、下:VB)
実際に試してみるときには「ja-JP」を「en-GB」など、インストールされていない「声」に変更してみてほしい。

読み終えるまで待つには?

 音声の再生に使っているMediaElementコントロールのPlayメソッドは、非同期で動作する。しかし、Taskオブジェクト(System.Threading.Tasks名前空間)などを返してくれるわけではないため、読み上げがいつ終わったのか分からない*5。そのタイミングを知るには、MediaElementコントロールのMediaEndedイベントを利用する(次のコード)。

<MediaElement x:Name="mediaElement1" Volume="100" MediaEnded="mediaElement1_MediaEnded" />

MediaElementコントロールにMediaEndedイベントのハンドラを追加した(XAML)
太字の部分を追加する。
あとは、コードビハインドにmediaElement1_MediaEndedメソッドを自動生成させ、そこに読み上げ終了時の処理を書けばよい。

*5ここまでのサンプル・コードで、SpeechSynthesisStreamオブジェクト(変数「stream」)のDisposeメソッドを呼び出していないことに疑問を持ってくれた読者もいるだろう。MediaElementコントロールのPlayメソッドを呼び出した直後にSpeechSynthesisStreamオブジェクトを解放してしまうと、非同期で動作しているPlayメソッドの内部では解放後のストリームを再生することになってしまう(=無音になる)ので、解放する処理を書けなかったのだ。SpeechSynthesisStreamオブジェクトを明示的に解放するには、ここで説明したMediaEndedイベント・ハンドラで行わねばならない。なお、Disposeパターンが正しく実装されているオブジェクトでは、ガベージ・コレクタがオブジェクトのファイナライザを自動的に呼び出したときにリソースが解放されるので、コードからDisposeメソッドを呼び出していなくてもメモリ・リークなどを起こすことはない。公開するサンプル・ソースでは、ローカル変数として宣言したSpeechSynthesisStreamオブジェクトのDisposeメソッドを呼び出すためにかなり複雑なことをやっているが、通常はそこまでする必要はなく、ガベージ・コレクタに任せればよい。


SSMLデータを読み上げるには?

 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テキストを読み上げる(上:C#、下:VB)
「プレーン・テキストを読み上げるには?」で示したコードとの違いは、読み上げるテキストの記述方法とSynthesizeSsmlToStreamAsyncメソッドを呼び出すことだけである(太字部分)。

 なお、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()

SSMLの「prosody」タグを利用してピッチやレートなどを変える(上:C#、下:VB)
<prosody>タグの詳細はSSMLの規格書を参照。SSML Version 1.1の日本語訳がある。

まとめ

 SpeechSynthesizerクラスを使うと簡単にテキストの読み上げができる。ただし、Windowsの言語によっては、デフォルト状態で日本語の読み上げが可能とは限らない。また、SSMLを利用すればピッチやレートなどの微調整ができる。

 音声合成については、次のドキュメントも参照してほしい。

付録:「声」を追加するには?

 Win 8.1には、コントロール・パネルから「声」を追加インストールできる(手動操作)。

 例として、ドイツ語の「声」を追加する手順を紹介しよう。まず、コントロール・パネルで[時計、言語、および地域]−[言語]を開く。[言語の追加]をクリックして切り替わった画面の右上の検索ボックスに「ドイツ」と入力して検索する(次の画像)。

コントロール・パネルでドイツ語を追加する コントロール・パネルでドイツ語を追加する
この画像は、[時計、言語、および地域]−[言語]で[言語の追加]をクリックし、画面の右上の検索ボックスに「ドイツ」と入力して検索したところ。ここで[ドイツ語]を選んで[開く]ボタン*6をクリックすると複数のドイツ語が提示されるので、その中から[ドイツ語(ドイツ)]を選んでまた[追加]ボタンをクリックすると、ドイツ語が追加される。

*6画像のように[ドイツ語]を選んだ状態では、[ドイツ語]にさらに複数の選択肢があるため[開く]ボタンになっている。選択していないか、選択していてもさらなる選択肢がない場合には[追加]ボタンに変わる。例えば、「英国」で検索すると英国英語(=「en-GB」)だけに絞り込まれるので、上の画像の[開く]ボタンの表記が「追加」となり、その[追加]ボタンをクリックするだけで言語が追加される。


 上の画像で、[ドイツ語]を選んで[開く]ボタンをクリックすると複数のドイツ語が提示される。そこで[ドイツ語(ドイツ)]を選んでまた[追加]ボタンをクリックすると、ドイツ国のドイツ語(=「de-DE」)が追加されて画面が切り替わる(次の画像)。

コントロール・パネルでドイツ語を追加した コントロール・パネルでドイツ語を追加した
ドイツ語を追加したところ(最下段)。「Windowsの表示言語:ダウンロード可能」と表示されていることに注目(日本語は最初からここが「有効」になっている)。右端の[オプション]をクリックすると、「声」をインストールできる画面に切り替わる。

 上の画像のように追加されたドイツ語(=「Deutsch(Deutschland)」)の右にある[オプション]リンクをクリックすると、[言語のオプション]画面に切り替わる(次の画像)。

コントロール・パネルで言語パックをインストールする コントロール・パネルで言語パックをインストールする
ドイツ語の[言語のオプション](=この画面)で[言語パックをダウンロードしてインストールします]というリンクをクリックすると、ドイツ語の言語パックがダウンロードされてインストールされる。

 上の画像の画面で、[言語パックをダウンロードしてインストールします]というリンクをクリックすると、ドイツ国のドイツ語のための言語パックがダウンロードされてインストールされる(次の画像)。言語パックには、読み上げに使う「声」も含まれている。

言語パックがダウンロードされてインストールされる 言語パックがダウンロードされてインストールされる
ダウンロードするファイルのサイズは、言語パックごとに100MBytes前後ある。

 これで、ドイツ語の読み上げもできるようになった。正しくインストールされたことを確認するには、レジストリの「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens」の下を見る。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。