WinRT で音声合成を試します。従来の API よりも扱える音声が増えたことを確認します。C# を使用します。
この記事のコードは以下のリポジトリに掲載しています。
他の言語での利用については以下の記事を参照してください。
JavaScript との比較については以下の記事を参照してください。
Windows の音声合成
Windows 10 でサポートされる音声の一覧です。
日本語以外の言語を使用する場合は追加します。
今回はすべての言語を追加した状態でテストします。
音声一覧
従来の API と WinRT とで結果が異なります。
Windows 10 での新しい音声は OneCore という枠組みで扱われます。従来の API では OneCore の音声が扱えません。
従来の API
.NET Framework の System.Speech.Synthesis で音声一覧を取得します。
using System;
using System.Speech.Synthesis;
class Program
{
static void Main()
{
var synth = new SpeechSynthesizer();
foreach (var voice in synth.GetInstalledVoices()) {
var vi = voice.VoiceInfo;
Console.WriteLine("{0}: {1}", vi.Culture, vi.Description);
}
}
}
ja-JP: Microsoft Haruka Desktop - Japanese
en-GB: Microsoft Hazel Desktop - English (Great Britain)
en-US: Microsoft David Desktop - English (United States)
en-US: Microsoft Zira Desktop - English (United States)
es-ES: Microsoft Helena Desktop - Spanish (Spain)
es-MX: Microsoft Sabina Desktop - Spanish (Mexico)
fr-FR: Microsoft Hortense Desktop - French
it-IT: Microsoft Elsa Desktop - Italian (Italy)
de-DE: Microsoft Hedda Desktop - German
ko-KR: Microsoft Heami Desktop - Korean
pl-PL: Microsoft Paulina Desktop - Polish
pt-BR: Microsoft Maria Desktop - Portuguese(Brazil)
ru-RU: Microsoft Irina Desktop - Russian
zh-CN: Microsoft Huihui Desktop - Chinese (Simplified)
zh-HK: Microsoft Tracy Desktop - Chinese(Traditional, HongKong SAR)
zh-TW: Microsoft Hanhan Desktop - Chinese (Taiwan)
一部の音声しか取得できません。SAPI ではレジストリを参照することで OneCore の音声を取得できますが、その方法は System.Speech.Synthesis では使えません。
また、System.Speech.Synthesis は .NET Core 3.1 では動きません。WinRT に移行するようにということのようです。
WinRT
ほぼすべて(後述)の音声が取得できます。
using System;
using Windows.Media.SpeechSynthesis;
class Program
{
static void Main()
{
foreach (var voice in SpeechSynthesizer.AllVoices) {
Console.WriteLine("{0}: {1}", voice.Language, voice.Description);
}
}
}
fr-FR: Microsoft Hortense - French (France)
en-GB: Microsoft George - English (United Kingdom)
en-US: Microsoft David - English (United States)
es-ES: Microsoft Pablo - Spanish (Spain)
de-DE: Microsoft Stefan - German (Germany)
it-IT: Microsoft Cosimo - Italian (Italy)
en-AU: Microsoft Catherine - English (Australia)
en-CA: Microsoft Linda - English (Canada)
ca-ES: Microsoft Herena - Catalan (Catalan)
en-GB: Microsoft Hazel - English (United Kingdom)
en-GB: Microsoft Susan - English (United Kingdom)
en-IN: Microsoft Heera - English (India)
da-DK: Microsoft Helle - Danish (Denmark)
en-US: Microsoft Zira - English (United States)
es-ES: Microsoft Helena - Spanish (Spain)
es-ES: Microsoft Laura - Spanish (Spain)
de-DE: Microsoft Hedda - German (Germany)
es-MX: Microsoft Sabina - Spanish (Mexico)
fi-FI: Microsoft Heidi - Finnish (Finland)
fr-CA: Microsoft Caroline - French (Canada)
de-DE: Microsoft Katja - German (Germany)
fr-FR: Microsoft Julie - French (France)
hi-IN: Microsoft Kalpana - Hindi (India)
ja-JP: Microsoft Ayumi - Japanese (Japan)
it-IT: Microsoft Elsa - Italian (Italy)
ar-EG: Microsoft Hoda - Arabic (Egypt)
ja-JP: Microsoft Haruka - Japanese (Japan)
ko-KR: Microsoft Heami - Korean (Korean)
pl-PL: Microsoft Paulina - Polish (Poland)
pt-BR: Microsoft Maria - Portuguese (Brazil)
pt-PT: Microsoft Helia - Portuguese (Portugal)
ru-RU: Microsoft Irina - Russian (Russia)
zh-CN: Microsoft Huihui - Chinese (Simplified, PRC)
zh-CN: Microsoft Yaoyao - Chinese (Simplified, PRC)
zh-HK: Microsoft Tracy - Chinese (Traditional, Hong Kong S.A.R.)
zh-TW: Microsoft Hanhan - Chinese (Traditional, Taiwan)
zh-TW: Microsoft Yating - Chinese (Traditional, Taiwan)
he-IL: Microsoft Asaf - Hebrew (Israel)
hi-IN: Microsoft Hemant - Hindi (India)
de-AT: Microsoft Michael - German (Austria)
hr-HR: Microsoft Matej - Croatian (Croatia)
hu-HU: Microsoft Szabolcs - Hungarian (Hungary)
id-ID: Microsoft Andika - Indonesian (Indonesia)
en-US: Microsoft Mark - English (United States)
en-AU: Microsoft James - English (Australia)
de-CH: Microsoft Karsten - German (Switzerland)
en-CA: Microsoft Richard - English (Canada)
ja-JP: Microsoft Ichiro - Japanese (Japan)
ar-SA: Microsoft Naayf - Arabic (Saudi)
ms-MY: Microsoft Rizwan - Malay (Malaysia)
nb-NO: Microsoft Jon - Norwegian (Bokmal)
nl-BE: Microsoft Bart - Dutch (Belgium)
nl-NL: Microsoft Frank - Dutch (Netherlands)
pl-PL: Microsoft Adam - Polish (Poland)
es-MX: Microsoft Raul - Spanish (Mexico)
pt-BR: Microsoft Daniel - Portuguese (Brazil)
cs-CZ: Microsoft Jakub - Czech (Czech Republic)
bg-BG: Microsoft Ivan - Bulgarian (Bulgaria)
ro-RO: Microsoft Andrei - Romanian (Romania)
en-IE: Microsoft Sean - English (Ireland)
ru-RU: Microsoft Pavel - Russian (Russia)
sk-SK: Microsoft Filip - Slovak (Slovakia)
sl-SI: Microsoft Lado - Slovenian (Slovenia)
sv-SE: Microsoft Bengt - Swedish
ta-IN: Microsoft Valluvar - Tamil (India)
th-TH: Microsoft Pattara - Thai (Thailand)
tr-TR: Microsoft Tolga - Turkish (Turkey)
vi-VN: Microsoft An - Vietnamese (Vietnam)
fr-CA: Microsoft Claude - French (Canada)
zh-CN: Microsoft Kangkang - Chinese (Simplified, PRC)
fr-CH: Microsoft Guillaume - French (Switzerland)
zh-HK: Microsoft Danny - Chinese (Traditional, Hong Kong S.A.R.)
el-GR: Microsoft Stefanos - Greek (Greece)
en-IN: Microsoft Ravi - English (India)
fr-FR: Microsoft Paul - French (France)
zh-TW: Microsoft Zhiwei - Chinese (Traditional, Taiwan)
SAPI でレジストリを指定して取得した結果と比較すると、以下の2つの音声が取得できません。
fr-CA: Microsoft Nathalie - French (Canada)
ja-JP: Microsoft Sayaka - Japanese (Japan)
これらは音声の設定にも現れませんが、Chromium 版 Edge では使えます。隠れキャラなのでしょうか。
音声合成
音声を指定してしゃべらせます。
using System;
using System.Threading.Tasks;
using Windows.Media.Core;
using Windows.Media.SpeechSynthesis;
using Windows.Media.Playback;
class Program
{
static void SetVoice(SpeechSynthesizer synthesizer, string v) {
foreach (var voice in SpeechSynthesizer.AllVoices) {
if (voice.DisplayName == v) {
synthesizer.Voice = voice;
break;
}
}
}
static async Task Main()
{
var voice = "Microsoft Ichiro";
var text = "こんにちは、世界";
var synthesizer = new SpeechSynthesizer();
SetVoice(synthesizer, voice);
var stream = await synthesizer.SynthesizeTextToStreamAsync(text);
var player = new MediaPlayer();
player.Source = MediaSource.CreateFromStream(stream, stream.ContentType);
var tcs = new TaskCompletionSource<int>();
player.MediaEnded += (sender, o) => tcs.SetResult(0);
player.Play();
await tcs.Task;
}
}
再生が終了するまで待つようになっています。
参考までにハマったポイントを書いておきます。
ストリームの変換
当初、SpeechSynthesizer.SynthesizeTextToStreamAsync()
から返される SpeechSynthesisStream
が直接受け取れる MediaPlayer.SetStreamSource()
を使おうとしましたが、obsolete でした。
MediaPlayer.SetMediaSource may be altered or unavailable after Windows 10. Use MediaPlayer.Source instead.
代替の MediaPlayer.Source
は IMediaPlaybackSource
を受け取るため変換が必要です。
player.Source = MediaSource.CreateFromStream(stream, stream.ContentType);
以下に変換方法についての質問がありますが、1番目の回答では引数が足りないため、2番目の回答から ContentType
の部分を補う必要があります。
今回は WinRT の MediaPlayer
を使用しましたが、以下の記事では WPF の SoundPlayer
を使用しています。こちらは Stream
への変換が必要になります。
終了待ち
再生は非同期で行われるため、再生が終わるまでプログラムが終了しないように待ちます。
再生の終了は MediaEnded
で通知されるので、TaskCompletionSource
に値をセットして Task
を終了させます。
var tcs = new TaskCompletionSource<int>();
player.MediaEnded += (sender, o) => tcs.SetResult(0);
player.Play();
await tcs.Task;
以下を参考にしました。
TaskCompletionSource
の使い方は JavaScript の Promise
に近いと思いました。
csproj
csproj ファイルでは以下のように WinRT を参照しています。
<ItemGroup>
<Reference Include="System.Runtime.WindowsRuntime">
<HintPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.WindowsRuntime.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Windows">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Facade\windows.winmd</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Windows.Foundation.FoundationContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Windows.Foundation.UniversalApiContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
以下を参考にしました。