Unity C# の Azure Microsoft SSML 音声合成 TTS ストリームで音声データを取得し、表情や口のアニメーションを簡単に配置
目次
Unity C# の Azure Microsoft SSML 音声合成 TTS ストリームで音声データを取得し、表情や口のアニメーションを簡単に配置
1. 簡単な紹介
Unity ツール クラスは、私が企画したゲーム開発で使用できるいくつかのモジュールであり、ゲーム開発を容易にするために独立して使用できます。
このセクションでは、Microsoft Azure が SSML を使用してSS 音声合成音声を実行し、表情口アニメーション データを取得してローカルに保存することを紹介します。場合によっては、音声と表情口アニメーション データをローカルで読み取るために使用されます。直接使用します。ネットワーク アクセスによる遅延を避けるため、簡単に説明します。より良い方法がある場合は、メッセージを残して連絡してください。
音声合成マークアップ言語 (SSML) は、ピッチ、発音、速度、音量などのテキスト読み上げ出力プロパティを微調整するために使用できる XML ベースのマークアップ言語です。プレーン テキスト入力よりもはるかに優れた制御性と柔軟性が得られます。
SSML を使用して次のことを行うことができます。
- 入力テキストの構造を定義します。これにより、テキスト読み上げ出力の構造、内容、その他の特性が決まります。たとえば、SSML を使用して、段落、文、休憩/休止、または沈黙を定義できます。テキストはブックマークや口形素などのイベント マーカーで囲むことができ、後でアプリケーションで処理できます。
- 声、言語、名前、スタイル、役割を選択します。単一の SSML ドキュメントで複数の音声を使用できます。アクセント、話速、ピッチ、音量を調整します。SSML を使用して、効果音や音符などの事前に録音されたオーディオを挿入することもできます。
- 出力オーディオの発音を制御します。たとえば、SSML を音素やカスタム辞書とともに使用して、発音を改善できます。SSML は、単語や数式の特定の発音を定義するために使用することもできます。
以下は、SSML ドキュメントの基本構造と構文のサブセットです。
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="string"> <mstts:backgroundaudio src="string" volume="string" fadein="string" fadeout="string"/> <voice name="string" effect="string"> <audio src="string"></audio> <bookmark mark="string"/> <break strength="string" time="string" /> <emphasis level="value"></emphasis> <lang xml:lang="string"></lang> <lexicon uri="string"/> <math xmlns="http://www.w3.org/1998/Math/MathML"></math> <mstts:audioduration value="string"/> <mstts:express-as style="string" styledegree="value" role="string"></mstts:express-as> <mstts:silence type="string" value="string"/> <mstts:viseme type="string"/> <p></p> <phoneme alphabet="string" ph="string"></phoneme> <prosody pitch="value" contour="value" range="value" rate="value" volume="value"></prosody> <s></s> <say-as interpret-as="string" format="string" detail="string"></say-as> <sub alias="string"></sub> </voice> </speak>
SSML 音声とサウンド
音声合成マークアップ言語 (SSML) の音声とサウンド - 音声サービス - Azure AI サービス | Microsoft Learn
公式サイト登録:
学生向け Azure - 無料のアカウント クレジット | Microsoft Azure
公式ウェブサイトの技術資料 URL:
公式サイトのTTS:
Text-to-Speech クイックスタート - 音声サービス - Azure Cognitive Services | Microsoft Learn
Azure Unity SDK パッケージの公式 Web サイト:
Speech SDK をインストールする - Azure Cognitive Services | Microsoft Learn
SDK 固有のリンク:
https://aka.ms/csspeech/unitypackage
2. 実施原則
1.公式サイトで音声合成に対応したSPEECH_KEYとSPEECH_REGIONを申請
2. 次に、言語と必要なサウンド構成をそれに応じて設定します。
3. ストリーミングで SSML を使用してオーディオ データを取得し、それを再生または音源に保存するだけです。サンプルは次のとおりです。
public static async Task SynthesizeAudioAsync()
{
var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
using var speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
var ssml = File.ReadAllText("./ssml.xml");
var result = await speechSynthesizer.SpeakSsmlAsync(ssml);
using var stream = AudioDataStream.FromResult(result);
await stream.SaveToWaveFileAsync("path/to/write/file.wav");
}
4.表情や口の形の音声とアニメーションデータをローカルに保存
// 获取到视频的数据,保存为 .wav
using var stream = AudioDataStream.FromResult(speechSynthesisResult);
await stream.SaveToWaveFileAsync($"./{fileName}.wav");
/// <summary>
/// 嘴型 animation 数据,本地保存为 json 数据
/// </summary>
/// <param name="fileName">保存文件名</param>
/// <param name="content">保存内容</param>
/// <returns></returns>
static async Task CommitAsync(string fileName,string content)
{
var bits = Encoding.UTF8.GetBytes(content);
using (var fs = new FileStream(
path: @$"d:\temp\{fileName}.json",
mode: FileMode.Create,
access: FileAccess.Write,
share: FileShare.None,
bufferSize: 4096,
useAsync: true))
{
await fs.WriteAsync(bits, 0, bits.Length);
}
}
3. 注意すべき事項
1. すべての speechSynthesisVoiceName が表情と口の形の対応するアニメーション データを生成できるわけではありません
4. 導入手順
これは.Net VSを直接使用したコードテストです。
1. Microsoft の Speech パッケージを NuGet にインストールします
2. SSML 合成音声を実現するコードを記述し、対応する音声ファイルと絵文字の口の形のアニメーション JSON データをローカルに保存します
3. コードを実行すると、対応する音声ファイルと口の形のアニメーション JSON データがローカルに保存されます。
4. 保存されたデータをローカルで表示する
5. キーコード
using Microsoft.CognitiveServices.Speech;
using System.Text;
class Program
{
// This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
static string speechKey = "YOUR_SPEECH_KEY";
static string speechRegion = "YOUR_SPEECH_REGION";
static string speechSynthesisVoiceName = "zh-CN-XiaoxiaoNeural";
static string fileName = "Test" + "Hello";
static string InputAudioContent = "黄河之水天上来,奔流到海不复回"; // 生成的
static int index = 0; // 记录合成的表情口型动画的数据数组个数
static string content="["; // [ 是为了组成 json 数组
async static Task Main(string[] args)
{
var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
// 根据需要可以使用更多 xml 配置,让合成的声音更加生动立体
var ssml = @$"<speak version='1.0' xml:lang='zh-CN' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>
<voice name='{speechSynthesisVoiceName}'>
<mstts:viseme type='FacialExpression'/>
<mstts:express-as style='friendly'>{InputAudioContent}</mstts:express-as>
</voice>
</speak>";
// Required for sentence-level WordBoundary events
speechConfig.SetProperty(PropertyId.SpeechServiceResponse_RequestSentenceBoundary, "true");
using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
{
// Subscribe to events
// 注册表情嘴型数据
speechSynthesizer.VisemeReceived += async (s, e) =>
{
Console.WriteLine($"VisemeReceived event:" +
$"\r\n\tAudioOffset: {(e.AudioOffset + 5000) / 10000}ms"
+ $"\r\n\tVisemeId: {e.VisemeId}"
// + $"\r\n\tAnimation: {e.Animation}"
);
if (string.IsNullOrEmpty( e.Animation)==false)
{
// \r\n, 是为了组合 json 格式
content += e.Animation + "\r\n,";
index++;
}
};
// 注册合成完毕的事件
speechSynthesizer.SynthesisCompleted += async (s, e) =>
{
Console.WriteLine($"SynthesisCompleted event:" +
$"\r\n\tAudioData: {e.Result.AudioData.Length} bytes" +
$"\r\n\tindex: {index} " +
$"\r\n\tAudioDuration: {e.Result.AudioDuration}");
content = content.Substring(0, content.Length-1);
content += "]";
await CommitAsync(fileName, content);
};
// Synthesize the SSML
Console.WriteLine($"SSML to synthesize: \r\n{ssml}");
var speechSynthesisResult = await speechSynthesizer.SpeakSsmlAsync(ssml);
// 获取到视频的数据,保存为 .wav
using var stream = AudioDataStream.FromResult(speechSynthesisResult);
await stream.SaveToWaveFileAsync(@$"d:\temp\{fileName}.wav");
// Output the results
switch (speechSynthesisResult.Reason)
{
case ResultReason.SynthesizingAudioCompleted:
Console.WriteLine("SynthesizingAudioCompleted result");
break;
case ResultReason.Canceled:
var cancellation = SpeechSynthesisCancellationDetails.FromResult(speechSynthesisResult);
Console.WriteLine($"CANCELED: Reason={cancellation.Reason}");
if (cancellation.Reason == CancellationReason.Error)
{
Console.WriteLine($"CANCELED: ErrorCode={cancellation.ErrorCode}");
Console.WriteLine($"CANCELED: ErrorDetails=[{cancellation.ErrorDetails}]");
Console.WriteLine($"CANCELED: Did you set the speech resource key and region values?");
}
break;
default:
break;
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
/// <summary>
/// 嘴型 animation 数据,本地保存为 json 数据
/// </summary>
/// <param name="fileName">保存文件名</param>
/// <param name="content">保存内容</param>
/// <returns></returns>
static async Task CommitAsync(string fileName,string content)
{
var bits = Encoding.UTF8.GetBytes(content);
using (var fs = new FileStream(
path: @$"d:\temp\{fileName}.json",
mode: FileMode.Create,
access: FileAccess.Write,
share: FileShare.None,
bufferSize: 4096,
useAsync: true))
{
await fs.WriteAsync(bits, 0, bits.Length);
}
}
}