Skip to main content

语音合成(text-to-speech)指南

分类:  Azure认知服务 标签:  #Azure #人工智能 #语音服务 #文本转语音 发布于: 2023-06-05 16:55:04

我们在前一章学习了如何通过.Net 5 SDK来使用Azure语音识别的服务,本节我们来学习Azure语音合同。
本章的源代码可以从这里下载 Demo Code

创建语音合成项目

运行如下的命令创建语音合成的项目:

dontnet new console -n TextToSpeech
cd TextToSpeech
dotnet add package Microsoft.CognitiveServices.Speech

项目创建成功过之后,使用编辑器或者IDE打开该项目,打开文件Program.cs, 在文件的顶部假如如下的包依赖:

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;

SpeechConfig对象

我们仍然需要创建SpeechConfig对象,在Azure语音服务中所有的服务的都是必须的要创建的对象,用于配置Speech语音服务。

public class Program
{
    static async Task Main(string[] args)
    {
        var config = SpeechConfig.FromSubscription("", "");
        await SynthesizeAudioAsyncToFile(config);
    }
}

将合成的结果存入文件

这里的逻辑是我们先创建一个SpeechConfig对象,然后再创建一个AudioConfig配置对象,这个对象我们在前一篇文章中也使用过了,用于音频的输入,输出以及格式转换等等。随后使用这两个对象创建一个SpeechSynThesizer对象用于语音合成。

static async Task SynthesizeAudioAsyncToFile(SpeechConfig config)
{
    using var audioConfig = AudioConfig.FromWavFileOutput("file.wav");
    using var synthesizer = new SpeechSynthesizer(config, audioConfig);
    await synthesizer.SpeakTextAsync("A simple test to write to a file.");
}

将合成的语音从扬声器输出

上例是从将合成的语音保存到文件中去,但是在本例中我们是需要将通过文本合成的语音从扬声器输出,定义如下的方法:

static async Task SynthesizeAudioAsyncToSpeaker(SpeechConfig config)
{
    using var synthesizer = new SpeechSynthesizer(config);
    await synthesizer.SpeakTextAsync("Synthesizing directly to speaker output.");
}

将合成后的语音存入内存中

前面我们讨论过了如何将合成的语音存入到文件和直接由扬声器播放,但是有些时候我们需要将合成的语音和其他的服务进行集成,或者需要生成的语音进行其他的自定义格式,例如增加自定义的头等等,因此我们可以将合成后的语音存入到内存中。在前面的两个例子里,如果我们指定了AudioConfig,那么可以用于存入到文件中,没有指定AudioConfig那么缺省会默认从扬声器播放,为了能够将生成的音频流存入到内存,我们可以在生成SpeechSynthesier对象的时候传入一个null对象,代码如下:
这里为了演示方便,我们将生成的流写入文件和通过扬声器播放出来,这里使用了NAudio库,所以大家需要在项目里添加这个包的引用。
另外需要记得将项目的target设置为.net5.0-windows, 如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CognitiveServices.Speech" Version="1.16.0" />
    <PackageReference Include="NAudio" Version="2.0.0" />
  </ItemGroup>
</Project>
static async Task SynthesizeAudioAsyncToMemory(SpeechConfig config)
{
    using var synthesizer = new SpeechSynthesizer(config, null);

    var result = await synthesizer.SpeakTextAsync("Getting the response as an in-memory stream.");
    using var stream = AudioDataStream.FromResult(result);
    //为了演示,我们直接用stream 存成文件
    await stream.SaveToWaveFileAsync("stream.wav");
    //或者为了演示,我们想播放该生成的语音
    using var mstream = new MemoryStream(result.AudioData);
    using var reader = new WaveFileReader(mstream);
    using var player = new WaveOutEvent();

    player.Init(reader);
    player.Play();

    while (player.PlaybackState == PlaybackState.Playing)
    {

        Thread.Sleep(500);
    }

}

这样就可以将合成之后的音频流存入到内存,然后进行各种自己需要的处理。

支持不同的语音(例如中文)

我们通过设置对象SpeechConfig的属性``SpeechSynthesisVoiceName`来设置支持的语言,另外在Speech语音服务支持标准的语音和神经语音,新创建的语音合成服务均使用神经语音,关于语音合成服务支持的语音种类,可以参考这篇文档语音合成服务支持的语音

例如我们要支持中文语音,使用语音模型为``zh-CN-XiaoyouNeural`

config.SpeechSynthesisVoiceName = "zh-CN-XiaoyouNeural";

使用SSML自定义语音特征

SSML是指语音合成标记语言(Speech Synthesis Markup Language)允许用户对语音合成的效果进行:

  • 调整声音的强度
  • 调整发音
  • 调整语速
  • 调整音量
  • .......

使用SSML的一般步骤

为了使用SSML来合成语音,我们首先需要定义一个SSML文件,一个SSML文件就是一个XML格式的文件,其根元素是

<speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
  <voice name="zh-CN-XiaoyouNeural">
    大灰狼想吃掉小红帽,小红帽害怕极了,急忙大喊一声:奶奶!
  </voice>
</speak>

我们先将上述XML的内容存为demo.xml

然后我们需要使用SpeechSynthesizer对象从SSML文件中读取内容,然后进行合成,如下实例:

public static async Task SynthesizeAudioAsyncFromSSML(SpeechConfig config)
{
    using var synthesizer = new SpeechSynthesizer(config, null);

    var ssml = File.ReadAllText("demo.xml");
    var result = await synthesizer.SpeakSsmlAsync(ssml);


    //使用扬声器播放
    using var mstream = new MemoryStream(result.AudioData);
    using var reader = new WaveFileReader(mstream);
    using var player = new WaveOutEvent();

    player.Init(reader);
    player.Play();

    while (player.PlaybackState == PlaybackState.Playing)
    {

        Thread.Sleep(500);
    }

}

上述是使用SSML的基本步骤,实际上也就是定义好标记文件,然后使用SDK来合成就可以了,不过SSML的功能会比这个强大太多,我们来稍微过一下SSML的基本标记和用法

SSML简单指南

上一小节我们已经学习了基本的步骤,那么使用SSML可以给我们带来哪些好处呢?

  • 使用多种语音模型,也就是在同一个文件中使用不同的角色来进行转换,例如小女孩,成年男性,成年女性等等。
  • 调整语气,语速,提高或者降低音量
  • 添加各种情绪,例如高兴,恐惧,平静等等

下面我来看看如何一一实现。

添加多种语音模型

添加多种语音模型之前,我们需要禁止``word boundary`, 也很简单:

speechConfig.SetProperty(
    "SpeechServiceResponse_Synthesis_WordBoundaryEnabled", "false");

然后就可以在SSML文件里添加多种语音模型了,添加语音模型也很简单,就是在定义<voice>标签时,指定不同的语音就可以了,例如可以同时指定``zh-CN-XiaoxiaoNeuralzh-CN-YunyeNeural`

调整说话的风格

说话的风格可以调整的点主要是:

  • 说话的角色(部分模型支持): zh-CN-XiaomoNeuralzh-CN-XiaoxuanNeural, 中文仅有这个两个模型支持
  • 说话的情感: 例如悲伤,恐惧,高兴,平静等等。
  • 情感的程度: 情感的程度取值在0.01 到2 之间,数值越大,表示情感程度越大。

如何定义?定义其实也非常简单,下面就是一个例子:

<mstts:express-as style="string"></mstts:express-as>

指定情感类型

<mstts:express-as style="string" styledegree="value"></mstts:express-as>

指定情感的程度

<mstts:express-as role="string" style="string"></mstts:express-as>

指定情感的角色。

我们来一个全面的实例:

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
       xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="zh-CN">
    <voice name="zh-CN-XiaomoNeural">
        女儿看见父亲走了进来,问道:
        <mstts:express-as role="YoungAdultFemale" style="calm">
            “您来的挺快的,怎么过来的?”
        </mstts:express-as>
        父亲放下手提包,说:
        <mstts:express-as role="OlderAdultMale" style="calm">
            “刚打车过来的,路上还挺顺畅。”
        </mstts:express-as>
    </voice>
</speak>
添加一个暂停

暂停表示说完一句话之后,暂停一下,然后再继续说后面的话。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="en-US-AriaNeural">
        Welcome to Microsoft Cognitive Services <break time="100ms" /> Text-to-Speech API.
    </voice>
</speak>
调整语气

调整说话的语气,我们使用标签<prosody>, 在这个标签里我们可以调整语气,语调,声音强度等等,如下:

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="en-US-GuyNeural">
        <prosody rate="+30.00%">
            Welcome to Microsoft Cognitive Services Text-to-Speech API.
        </prosody>
    </voice>
</speak>

调整语速

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="en-US-JennyNeural">
        <prosody volume="+20.00%">
            Welcome to Microsoft Cognitive Services Text-to-Speech API.
        </prosody>
    </voice>
</speak>

调整音量

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="en-US-AriaNeural">
        Welcome to <prosody pitch="high">Microsoft Cognitive Services Text-to-Speech API.</prosody>
    </voice>
</speak>

调整语调。

关于SSML更加详细的指南,您可以参考这个地址: SSML指南

下面我们来给一个例子,代码不变,但是更改一下demo.xml的定义如下:

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="zh-CN">

    <voice name="zh-CN-XiaoxiaoNeural">
        <mstts:express-as role="Girl" style="fearful" styledegree="2">
            快走吧,路上一定要注意安全,千万不要回头,一直往西走!
            <break time="1000ms" />
            <prosody pitch="high" rate="+10.00%" volume="+110.00%">
             记住了!不要回头!
                    </prosody>
        </mstts:express-as>
    </voice>

</speak>

运行的效果如下:

Demo.mp4