OpenSiv3DでVOICEVOX


OpenSiv3DからVOICEVOXを簡単に利用できる関数GetAudioFromVOICEVOXを作成しました。
https://voicevox.hiroshiba.jp/

```cpp
# include <Siv3D.hpp>



std::string URLEncode(const std::string& str) {
	std::ostringstream os;
	for (char c : str) {
		if (std::isalnum(static_cast<unsigned char>(c)) || (c == '-') || (c == '_') || (c == '.') || (c == '~')) {
			os << c;
		}
		else {
			os << '%' << std::setw(2) << std::setfill('0') << std::uppercase << std::hex << (static_cast<int>(static_cast<unsigned char>(c)) & 0xff);
		}
	}
	return os.str();
}

JSON getAudioQuery(const String& text, int32 speakerID) {
	const URL audioQueryURL = U"http://localhost:50021/audio_query";
	const String encodedText = Unicode::Widen(URLEncode(text.toUTF8()));
	const std::string data = JSON{}.formatUTF8();
	const FilePath jsonFilePath = U"audio_query_result.json";
	if (auto response = SimpleHTTP::Post(audioQueryURL + U"?text=" + encodedText + U"&speaker=" + Format(speakerID), {}, data.data(), data.size(), jsonFilePath)) {
		if (!response.isOK()) {
			throw std::runtime_error("Failed to get audio query.");
		}
		const JSON json = JSON::Load(jsonFilePath);
		if (!json) {
			throw std::runtime_error("Failed to parse JSON.");
		}
		return json;
	}
	throw std::runtime_error("Failed to get response from audio_query API.");
}

Audio synthesizeVoice(const JSON& audioQuery, int32 speakerID) {
	const URL synthesisURL = U"http://localhost:50021/synthesis?speaker=" + Format(speakerID);
	const HashTable<String, String> headers = { { U"Content-Type", U"application/json" } };
	const std::string data = audioQuery.formatUTF8Minimum();
	MemoryWriter memoryWriter;
	if (auto response = SimpleHTTP::Post(synthesisURL, headers, data.data(), data.size(), memoryWriter)) {
		if (!response.isOK()) {
			throw std::runtime_error("Failed to get voice data.");
		}
		const Audio audio{ MemoryReader{memoryWriter.getBlob()} };
		if (!audio) {
			throw std::runtime_error("Failed to load audio.");
		}
		return audio;
	}
	throw std::runtime_error("Failed to get response from synthesis API.");
}

Optional<Audio> GetAudioFromVOICEVOX(const String& originalText, int32 speakerID = 1, double intonationScale = 1.0, double speedScale = 1.0, double volumeScale = 2.0) {
	try {
		JSON audioQuery = getAudioQuery(originalText, speakerID);
		audioQuery[U"intonationScale"] = intonationScale;
		audioQuery[U"speedScale"] = speedScale;
		audioQuery[U"volumeScale"] = volumeScale;
		Audio audio = synthesizeVoice(audioQuery, speakerID);
		return audio;
	}
	catch (const std::exception& e) {
		Print << Unicode::Widen(e.what());
		return std::nullopt;
	}
}

void Main()
{
	auto voice = GetAudioFromVOICEVOX(U"これはボイスボックスのテストです。", 1);

	if (voice.has_value())
	{
		voice.value().play();
	}
	else
	{
		return;
	}

	while (System::Update())
	{		

	}
}

```

# OpenSiv3Dと合成音声

OpenSiv3Dは、C++でのゲームやグラフィック開発を目指したライブラリで、さまざまな機能が用意されています。その中には、Text to Speech（テキスト読み上げ）機能も含まれ、以下のようにして使用します：

```cpp
// 必要に応じて読み上げ言語を設定（OS に読み上げエンジンがインストールされていない場合は使えない）
TextToSpeech::SetDefaultLanguage(LanguageCode::EnglishUS);
//TextToSpeech::SetDefaultLanguage(LanguageCode::Japanese);

// 音声読み上げ出力
Say << U"Hello, Say!";
```

しかし、この機能はシステムにインストールされている読み上げエンジンに依存しています。そのため、読み上げの品質は使用しているエンジンの能力に大きく左右されます。この点でVOICEVOXは、高品質でマルチプラットフォームに対応した合成音声を提供しています。

# 利用方法
VOICEVOXをOpenSiv3Dで利用するためには、まずVOICEVOXをインストールし、エンジンを起動しておく必要があります。
https://voicevox.hiroshiba.jp/

そして、前述した`GetAudioFromVOICEVOX`関数を利用して、VOICEVOXのエンジンを活用した音声を生成できます。今後もOpenSiv3DやVOICEVOXの最新の機能を活用し、ユニークで魅力的なアプリケーションを開発していきましょう。

# 参考
https://voicevox.hiroshiba.jp/
https://github.com/VOICEVOX/voicevox_engine
https://qiita.com/A_T_B/items/1531d78944d8b796b9fa