Windows環境で音声合成（音声読み上げ）

# はじめに
`Windows`の`API`を使って、音声合成（音声読み上げ）処理を実装しました。

# 環境
* Windows 10 (バージョン 2004)
* .NET Framework 4.8
* [Visual C++ ビルドツール 2019](https://visualstudio.microsoft.com/ja/downloads/)　→　[インストール手順](https://www.kkaneko.jp/tools/win/buildtool.html)
* [DMD](https://dlang.org/download.html) v2.096.0

# ソースコード
音声合成には、[SpeechSynthesizer クラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.speech.synthesis.speechsynthesizer?view=netframework-4.8)を使用しています。
`.NET API`を利用するために`C++/CLI`を経由しています。
英語は`Microsoft Zira Desktop`、日本語は`Microsoft Haruka Desktop`とそれぞれに適した音声で再生します。
ソースコードではコメントアウトしていますが、`wave`ファイルへの出力も可能です。

```d:speak.d
import std.stdio;
import std.utf;

extern (C++) {
	void speak(const(wchar)* phrase, const(wchar)* speaker, const(wchar)* filename = cast(wchar*)0);
}

void main()
{
	wstring phrase = "D is a general-purpose programming language with static typing, systems-level access, and C-like syntax.";
	writefln("input : %s", phrase);
	phrase.toUTF16z.speak("Microsoft Zira Desktop");
	phrase = "D言語は、静的かたづけ、システムレベルのアクセス、およびCのような構文を備えた汎用プログラミング言語です。";
	writefln("input : %s", phrase);
	phrase.toUTF16z.speak("Microsoft Haruka Desktop");
//	waveファイルに出力することも可能
//	phrase.toUTF16z.speak("Microsoft Haruka Desktop", "output.wav");
}
```

```c++:speechsynthesizer.cpp
#using <C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Speech\v4.0_4.0.0.0__31bf3856ad364e35\System.Speech.dll>

using namespace System;
using namespace System::Speech::Synthesis;

void outputToWaveFile(char16_t const *phrase, char16_t const *speaker, char16_t const *filename)
{
	SpeechSynthesizer^ synth = gcnew SpeechSynthesizer();
	if ( filename != 0 ){
		synth->SetOutputToWaveFile(gcnew String(reinterpret_cast<wchar_t const*>(filename)));
	}
	synth->SelectVoice(gcnew String(reinterpret_cast<wchar_t const*>(speaker)));
	String^ p = gcnew String(reinterpret_cast<wchar_t const*>(phrase));
	synth->Speak(p);
}

void speak(char16_t const *phrase, char16_t const *speaker, char16_t const *filename = 0)
{
	outputToWaveFile(phrase, speaker, filename);
}
```

# コンパイル、実行
`x64 Native Tools Command Prompt for VS 2019`を起動して、以下の順に実行します。
音声合成の技術は、思っていたより向上していて、いい感じに読み上げてくれました。
こんなに簡単に実装できたのにも驚きです。

```text:コマンドプロンプト
**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.4.2
** Copyright (c) 2019 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

d:\Dev> cl /clr /c speechsynthesizer.cpp
Microsoft(R) C/C++ Optimizing Compiler Version 19.24.28314
Microsoft (R) .NET Framework の場合 バージョン 4.08.4300.0
Copyright (C) Microsoft Corporation.  All rights reserved.

speechsynthesizer.cpp

d:\Dev> dmd -m64 -release -L=/NODEFAULTLIB:libcmt.lib speak.d speechsynthesizer.obj "C:\Program Files (x86)\Windows　Kits\NETFXSDK\4.8\Lib\um\x64\mscoree.lib"

d:\Dev> chcp 65001
Active code page: 65001

d:\Dev> speak
input : D is a general-purpose programming language with static typing, systems-level access, and C-like syntax.
input : D言語は、静的かたづけ、システムレベルのアクセス、およびCのような構文を備えた汎用プログラミング言語です。
```

# 実験：隠れキャラ（？）の呼び出し
*この実験ではレジストリの書き換えを行っています。推奨しているわけではありません。*

`.NET Framework 4.8`環境の[SpeechSynthesizer クラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.speech.synthesis.speechsynthesizer?view=netframework-4.8)で日本語読み上げに適した音声は`Microsoft Haruka Desktop`しか用意されていません。
しかし、音声認識の音声選択では`Microsoft Ayumi`、`Microsoft Ichiro`なる音声が存在します。

![voice.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/f1ff26a3-825a-398a-202b-68bc5d4e432a.png)

レジストリを探したところ、`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens`以下に何か定義があります。さらに、そこには`Microsoft Sayaka`という隠れキャラ（？）まで存在しました。
これらの定義を`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens`以下でも追加すれば、音声合成に使えるのではないかと思い、試しました。
結論として、これでうまく音声合成ができました。

以下、追加したレジストリ定義です。

```text:ayumi.reg
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_AYUMI_11.0]
@="Microsoft Ayumi - Japanese (Japan)"
"411"="Microsoft Ayumi - Japanese (Japan)"
"CLSID"="{179F3D56-1B0B-42B2-A962-59B7EF59FE1B}"
"LangDataPath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,\
  00,70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,\
  65,00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,\
  00,5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,53,00,54,00,54,00,53,00,\
  4c,00,6f,00,63,00,6a,00,61,00,4a,00,50,00,2e,00,64,00,61,00,74,00,00,00
"VoicePath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,00,\
  70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,65,\
  00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,00,\
  5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,31,00,30,00,34,00,31,00,41,\
  00,79,00,75,00,6d,00,69,00,00,00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_AYUMI_11.0\Attributes]
"Age"="Adult"
"Gender"="Female"
"Language"="411"
"Name"="Microsoft Ayumi"
"SharedPronunciation"=""
"Vendor"="Microsoft"
"Version"="11.0"
```

```text:ichiro.reg
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_ICHIRO_11.0]
@="Microsoft Ichiro - Japanese (Japan)"
"411"="Microsoft Ichiro - Japanese (Japan)"
"CLSID"="{179F3D56-1B0B-42B2-A962-59B7EF59FE1B}"
"LangDataPath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,\
  00,70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,\
  65,00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,\
  00,5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,53,00,54,00,54,00,53,00,\
  4c,00,6f,00,63,00,6a,00,61,00,4a,00,50,00,2e,00,64,00,61,00,74,00,00,00
"VoicePath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,00,\
  70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,65,\
  00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,00,\
  5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,31,00,30,00,34,00,31,00,49,\
  00,63,00,68,00,69,00,72,00,6f,00,00,00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_ICHIRO_11.0\Attributes]
"Age"="Adult"
"Gender"="Male"
"Language"="411"
"Name"="Microsoft Ichiro"
"SharedPronunciation"=""
"Vendor"="Microsoft"
"Version"="11.0"
```

```text:sayaka.reg
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_SAYAKA_11.0]
@="Microsoft Sayaka - Japanese (Japan)"
"411"="Microsoft Sayaka - Japanese (Japan)"
"CLSID"="{179F3D56-1B0B-42B2-A962-59B7EF59FE1B}"
"LangDataPath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,\
  00,70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,\
  65,00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,\
  00,5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,53,00,54,00,54,00,53,00,\
  4c,00,6f,00,63,00,6a,00,61,00,4a,00,50,00,2e,00,64,00,61,00,74,00,00,00
"VoicePath"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,53,00,\
  70,00,65,00,65,00,63,00,68,00,5f,00,4f,00,6e,00,65,00,43,00,6f,00,72,00,65,\
  00,5c,00,45,00,6e,00,67,00,69,00,6e,00,65,00,73,00,5c,00,54,00,54,00,53,00,\
  5c,00,6a,00,61,00,2d,00,4a,00,50,00,5c,00,4d,00,31,00,30,00,34,00,31,00,53,\
  00,61,00,79,00,61,00,6b,00,61,00,00,00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_SAYAKA_11.0\Attributes]
"Age"="Adult"
"Gender"="Female"
"Language"="411"
"Name"="Microsoft Sayaka"
"SharedPronunciation"=""
"Vendor"="Microsoft"
"Version"="11.0"
```

レジストリ追加後の実装例

```d:speak1.d
import std.stdio;
import std.utf;

extern (C++) {
	void speak(const(wchar)* phrase, const(wchar)* speaker, const(wchar)* filename = cast(wchar*)0);
}

void main()
{
	wstring phrase = "D言語は、静的かたづけ、システムレベルのアクセス、およびCのような構文を備えた汎用プログラミング言語です。";
	writefln("input : %s", phrase);
	phrase.toUTF16z.speak("Microsoft Haruka Desktop");
	phrase.toUTF16z.speak("Microsoft Ichiro");
	phrase.toUTF16z.speak("Microsoft Ayumi");
	phrase.toUTF16z.speak("Microsoft Sayaka");
}
```

# 参考情報
[インストールされている .NET Framework バージョンを確認する](https://docs.microsoft.com/ja-jp/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed)
[SpeechSynthesizer クラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.speech.synthesis.speechsynthesizer?view=netframework-4.8)