相关文章推荐
仗义的斑马  ·  语音合成C++ ...·  1 周前    · 
侠义非凡的板栗  ·  Unity C# 之 Azure ...·  3 月前    · 
睡不着的跑步鞋  ·  4.1K ...·  5 月前    · 
霸气的麦片  ·  AVFoundation 命名空间 | ...·  5 月前    · 
八块腹肌的大葱  ·  Bicep warnings and ...·  3 周前    · 

一、简单介绍

Unity 工具类,自己整理的一些 游戏开发 可能用到的模块,单独独立使用,方便游戏开发。

本节介绍,这里在使用微软的Azure 使用SSML进行 SS语音合成 的音频,并且获取表情嘴型Animation 数据,并且保存到本地,在特定的情况下,用于本地读取音频和表情嘴型Animation 数据,直接使用,避免可能网络访问造成的延迟问题,这里简单说明,如果你有更好的方法,欢迎留言交流。

语音合成标记语言 (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"/>
        <phoneme alphabet="string" ph="string"></phoneme>
        <prosody pitch="value" contour="value" range="value" rate="value" volume="value"></prosody>
        <say-as interpret-as="string" format="string" detail="string"></say-as>
        <sub alias="string"></sub>
    </voice>
</speak>

SSML 语音和声音
语音合成标记语言 (SSML) 的语音和声音 - 语音服务 - Azure AI services | Microsoft Learn

官网注册:

面向学生的 Azure - 免费帐户额度 | Microsoft Azure

官网技术文档网址:

技术文档 | Microsoft Learn

官网的TTS:

文本转语音快速入门 - 语音服务 - Azure Cognitive Services | Microsoft Learn

Azure Unity SDK  包官网:

安装语音 SDK - Azure Cognitive Services | Microsoft Learn

SDK具体链接:

https://aka.ms/csspeech/unitypackage

二、实现原理

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、本地保存音频,以及表情嘴型 Animation 数据

    // 获取到视频的数据,保存为 .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);

三、注意事项

1、不是所有的 speechSynthesisVoiceName 都能生成对应的 表情嘴型 Animation 数据

四、实现步骤

这里是直接使用 .Net VS 中进行代码测试

1、在 NuGet 中安装 微软的 Speech 包

 2、代码编写实现 SSML 合成语音,并且本地保存对应的 音频文件和表情嘴型 Animation json 数据

3、运行代码,运行完后,就会本地保存对应的 音频文件和表情嘴型 Animation json 数据

 4、本地查看保存的数据

五、关键代码

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);
                    Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。本节介绍,这里在使用微软的Azure 使用SSML进行SS语音合成的音频,并且获取表情嘴型Animation 数据,并且保存到本地,在特定的情况下,用于本地读取音频和表情嘴型Animation 数据,直接使用,避免可能网络访问造成的延迟问题,这里简单说明,如果你有更好的方法,欢迎留言交流。语音合成标记语言 (SSML) 是一种基于 XML 的标记语言,可用于微调文本转语音输出属性,例如音调、发音、语速、音量等。
				
SALSA使用探索 之前做项目时想做人物说话的嘴部动作,因为我们的语音是AI合成的,语音片段很多,如果能根据语音生成嘴部动画,那将极大便利我们的工作。后面是找到了SALSA的这款插件,并摸索出使用方法。 1 插件介绍 官方网站:https://crazyminnowstudio.com/ 一个能做2D纸片人和3D人物模型嘴部和眼睛动画的插件,由两人组成的独立工作室(疯狂米诺工作室)开发。 插件全名为SALSA With RandomEyes,2014年上架Asset Store,目前是已下架,替换为新版本
LipSync是什么? LipSync是一个基于Unity的独立,轻量化口型匹配解决方案。它可以帮助开发者在Unityy上,用相对少的时间效应实现效果相对令人满意的“口型匹配”功能。的人物模型,口型动画以及语音资源,实现即时的口型匹配功能。您只需要告诉LipSync语音数据的来源,带有口型BlendShape的目标对象以及BlendShape属性名,并进行简单的设置,就能够让你的人物转换语音的播放动起他/她/它的嘴巴。 不过LipSync不是万能的。为了能够愉快而有效地使用LipSync,你需要知道它目前做得到的事情有: 实时地分析语音数据,运用语音识别的一些理论,识别出时间时间帧中这段语音
Unity中,你可以使用Text-to-SpeechTTS)技术来合成语音。这可以通过以下步骤实现: 1. 首先,你需要选择一个TTS引擎。目前有很多免费和商业的TTS引擎可用,例如Microsoft Azure Cognitive Services、Google Cloud Text-to-Speech等。选择一个适合你需求的引擎,并且按照他们的文档进行注册和设置。 2. 在Unity中创建一个空物体或者使用已存在的物体来管理TTS合成过程。给该物体添加一个脚本。 3. 在脚本中,使用合适的API将文本发送给TTS引擎,并且接收返回的音频数据。 4. 将收到的音频数据转换为Unity中的AudioClip,并且播放出来。 下面是一个简单的示例代码: ```csharp using UnityEngine; using UnityEngine.Networking; using System.Collections; public class TTSManager : MonoBehaviour [SerializeField private string apiKey; [SerializeField private string textToSpeechUrl; public AudioSource audioSource; public void SynthesizeText(string text) StartCoroutine(SynthesizeRequest(text)); private IEnumerator SynthesizeRequest(string text) string requestUrl = $"{textToSpeechUrl}?key={apiKey}&text={UnityWebRequest.EscapeURL(text)}"; using (UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(requestUrl, AudioType.W***)) yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) AudioClip audioClip = DownloadHandlerAudioClip.GetContent(request); audioSource.clip = audioClip; audioSource.Play(); Debug.LogError("TTS request failed: " + request.error); 你需要将apiKey和textToSpeechUrl替换为你所选择的TTS引擎的相关信息。然后,你可以在其他脚本中调用TTSManager的SynthesizeText方法,传入要合成的文本。 请注意,这只是一个简单的示例代码,你可能需要根据你选择的TTS引擎的API进行适当的调整和修改。希望对你有所帮助!
Git 常见错误 之 error: src refspec xxx does not match any / error: failed to push some refs to 简单解决方法 118997 Unity HttpClient 之 使用MultipartFormDataContent 发起 内容类型为 multipart/form-data 的数据 Post 请求(正常与流式响应处理) 妲己把画插: 您单单写一个multipart/form-data的数据提交不行吗。整这么多花活,web还不支持多线程,一搜还就推着篇,哎呦,,头疼。 Python 机器学习 基础 之 监督学习 [决策树集成] 算法 的简单说明 普通网友: 写的真好,细节很到位!【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】