上一讲里,我们通过 Whisper 模型,让 AI“听懂”了我们在说什么。我们可以利用这个能力,让 AI 替我们听播客、做小结。不过,这只是我们和 AI 的单向沟通。那我们能不能更进一步,让 AI 不仅能“听懂”我们说的话,通过 ChatGPT 去回答我们问的问题,最后还能让 AI 把这些内容合成为语音,“说”给我们听呢?

当然可以,这也是我们这一讲的主题,会带你一起来让 AI 说话。和上一讲一样,不仅会教你如何使用云端 API 来做语音合成(Text-To-Speech),也会教你使用开源模型,给你一个用本地 CPU 就能实现的解决方案。这样,你也就不用担心数据安全的问题了。

使用 Azure 云进行语音合成

语音合成其实已经是一个非常成熟的技术了,现在在很多短视频平台里,你听到的很多配音其实都是通过语音合成技术完成的。国内外的各大公司都有类似的云服务,比如科大讯飞、阿里云、百度、AWS Polly、Google Cloud等等。不过,今天我们先来体验一下微软 Azure 云的语音合成 API。选用 Azure,主要有两个原因。

1. 因为微软和 OpenAI 有合作,Azure 还提供了 OpenAI 相关模型的托管。这样,我们在实际的生产环境使用的时候,只需要和一个云打交道就好了。

2. 价格比较便宜,并且提供了免费的额度。如果你每个月的用量在 50 万个字符以内,那么就不用花钱。

在运行代码之前,需要先去注册一个 Azure 云的账号,并且开通微软认知服务,然后开启对应的认知服务资源,获得自己的 API Key。在这里放了对应文档的链接,照着文档一步步操作,就能完成。在下面也放上了关键步骤的截图,具体注册过程,就不一一介绍了。

点击创建认知服务的链接,在自己的 Azure 云账号下,创建一个对应的认知服务

注:我选择了 East US 区域,因为这个区域也可以部署 OpenAI 的 ChatGPT 服务。 

在创建认知服务完成之后,在部署成功的地方,点击Go to resource进入下一个界面

点击左侧的 Keys and Endpoint,然后点击右边的「复制」图标能够拿到对应的 API KEY

在拿到 API Key 之后,还是建议你把 API Key 设置到环境变量里面。避免你使用 Notebook 或者撰写代码的时候,不小心把自己的 Key 暴露出去,被别人免费使用。同样的,我们也在环境变量里设置一下我们使用的 Azure 服务的区域 eastus。 

export AZURE_SPEECH_KEY=YOUR_API_KEY

export AZURE_SPEECH_REGION=eastus

当然,也不要忘了安装对应的 Python 包。

pip install azure-cognitiveservices-speech

基本的语音合成

账号和环境都设置好了之后,我们就可以动手来试试 Azure 语音合成的效果了。

import os

import azure.cognitiveservices.speech as speechsdk

# This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"

speech_config = speechsdk.SpeechConfig(subscription=os.environ.get('AZURE_SPEECH_KEY'), region=os.environ.get('AZURE_SPEECH_REGION'))

audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)

# The language of the voice that speaks.

speech_config.speech_synthesis_voice_name='zh-CN-XiaohanNeural'

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)

text = "今天天气真不错,ChatGPT真好用。"

speech_synthesizer.speak_text_async(text)

运行上面这个代码,你就会听到一个女声说:“今天天气真不错,ChatGPT 真好用。”

这几行代码非常简单。

我们先通过配置读取了 API Key 和 Region。

然后通过 speech_synthesis_voice_name 这个配置参数指定了我们合成语音所使用的声音。

通过 speak_text_async 这个函数,就能异步调用 API 服务,直接把合成的声音播放出来了。

通过 speech_synthesis_voice_name 这个参数,我们还可以选用很多别的声音,包括不同语言和不同的人。对应的列表可以在 Azure 的Language and voice support文档里面找到。我们换一个其他的 voice_name,就可以把对应的语音换成男声。

speech_config.speech_synthesis_voice_name='zh-CN-YunfengNeural'

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)

speech_synthesizer.speak_text_async(text)

指定语音的风格与角色

如果你仔细看了 Language and voice support 的文档,会发现它有很多很多 voice_name。而且很多 voice_name 里,我们还有额外的两个参数可以选择,那就是 Styles 和 Roles,它们分别代表了合成语音的语气和对应的角色。通过这两个参数,我们可以让 AI 把很多场景“演出来”。比如,下面的示例代码就演绎了一段母子之间关于买玩具的一段对话,可以运行一下看看效果。

ssml = """

xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="zh-CN">

儿子看见母亲走了过来,说到:

“妈妈,我想要买个新玩具”

母亲放下包,说:

“我看你长得像个玩具。”

"""

speech_synthesis_result = speech_synthesizer.speak_ssml_async(ssml).get()

Azure 并不是通过让你在 API 里面配置一些参数来指定一段文本的角色和语气,而是通过一个叫做 SSML 格式的 XML 文件做到这一点的。这个 SSML 是 Speech Synthesis Markup Language 的首字母缩写,翻译过来就是语音合成标记语言。它不是一个 Azure 云专属的格式,而是一个 W3C 的标准,所以同样的 XML 不仅可以用在 Azure 云里,也一样可以用在 Google Cloud 里。

通过 SSML 里面元素的属性配置,我们可以指定不同文本段的 voice_name、role 和 style。比如,在上面的这个例子里面,我们就用两个 voice 元素,表示了两个不同的人的声音。voice 元素里面的 name 属性,指定了这段声音的 voice_name。而在 voice 元素内部,你还可以内嵌 mstss:express-as 元素,在这个元素里我们可以指定 role 和 style。这样一来,我们就可以让一个 voice_name 在不同的场景片段下,用不同的语气和角色来说话。

ssml = """

xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">

That'd be just amazing!

What's next?

"""

speech_synthesis_result = speech_synthesizer.speak_ssml_async(ssml).get()

在我自己实际使用的体验里面,中文的语气和角色效果不算明显。但是英文的效果还是很明显的,可以根据文档用不同的参数尝试一下。

SSML 这个格式,不只支持 style 和 role,还有更多丰富的参数可以配置,可以去看看 Azure 文档的协议标准。

指定语音的输出方式

到目前为止,我们都是使用异步调用的方式,直接把语音播放出来了。但很多时候,我们可能需要把对应的语音存储下来。那下面的代码就可以做到这一点。

speech_config.speech_synthesis_language='zh-CN'

speech_config.speech_synthesis_voice_name='zh-CN-XiaohanNeural'

audio_config = speechsdk.audio.AudioOutputConfig(filename="./data/tts.wav")

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)

text = "今天天气真不错,ChatGPT真好用"

speech_synthesizer.speak_text_async(text)

我们只需要把原先设置成 use_default_speaker=True 的 AudioOutputConfig,改为设置成一个 .wav 的输出文件就好了。我们之后调用 speak_text_async 的函数,就会把语音输出到相应的.wav 文件里。

audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)

当然,你可以把对应的语音,暂时放在内存里面,而不是存储到文件系统中,也可以把输出的内容通过我们习惯的 MP3 格式存储下来。

speech_config.set_speech_synthesis_output_format(speechsdk.SpeechSynthesisOutputFormat.Audio48Khz192KBitRateMonoMp3)

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)

result = speech_synthesizer.speak_text_async(text).get()

stream =speechsdk.AudioDataStream(result)

stream.save_to_wav_file("./data/tts.mp3")

我们只需要给 speech_config 这个参数设定一个 synthesis_output_format 就好了。我们上面就是把输出格式设置成了一个 48kHz 采样、192K 码率的 MP3 格式。然后,这一次我们把 AudioConfig 设置成了 None。在 speak_text_async 函数被调用之后,我们又调用了一下 get 函数,拿到对应的 SpeechSynthesisResult 对象。然后把这个对象放到 AudioDataStream 里,之后我们就可以把这个 AudioDataStream 按照我们的需要进行处理了。这里,我们是直接把它存储成了一个 MP3 文件。

使用开源模型进行语音合成

虽然通过 Azure 云的 API,我们可以很容易地进行语音合成,速度也很快。但很多时候因为数据安全的问题,我们还是希望能够直接在我们自己的服务器上进行语音合成。当然,这也是能够办到的,有很多开源项目都支持语音合成。

我们在这里,就不妨试一下百度开源的 PaddleSpeech 的语音合成功能,看看效果怎么样。

我们还是要先安装 PaddleSpeech 相关的 Python 包。

%pip install paddlepaddle

%pip install paddlespeech

然后通过 PaddleSpeech 自带的 TTSExecutor,可以将对应的文本内容转换成 WAV 文件。需要注意,这个过程中,PaddleSpeech 需要下载对应的模型,所以第一次运行的时候也要花费一定的时间。

from paddlespeech.cli.tts.infer import TTSExecutor

tts_executor = TTSExecutor()

text = "今天天气十分不错,百度也能做语音合成。"

output_file = "./data/paddlespeech.wav"

tts_executor(text=text, output=output_file)

PaddleSpeech 的 TTSExecutor,只是把你的文本输入转化成了一个 WAV 文件。要在 Python 里面播放对应的声音,我们还要借助于 PyAudio 这个包。对应的,我们要先安装 PyAudio 依赖的 portaudio 库,然后再安装 PyAudio 包。

Mac 下可以通过 homebrew 来安装 portaudio。

brew install portaudio

如果在 Unbuntu 或者 Debian 下,你就可以通过 apt-get 来安装 portaudio。

sudo apt-get install portaudio19-dev

只有在 portaudio 安装成功之后,我们才能安装 PyAudio 包,不然会报缺少依赖的错误。

pip install pyaudio

通过 PyAudio,我们可以直接播放 WAV 文件的内容了。对应的代码我放在下面了,其实我不太熟悉 PyAudio 库,但是这样简单的代码直接让 ChatGPT 帮我写,一次就能运行成功。如果你仔细读一下这段代码,也不难理解它的含义。实际就是打开了一个 PyAudio 的 Stream,然后不断从我们的 WAV 文件里面读入数据,然后写入这个 Stream,写入之后声音就播放出来了。如果你把 stream.write(data) 那一行去掉,那么你就会发现整个程序运行的过程里,是没有声音的。

import wave

import pyaudio

def play_wav_audio(wav_file):

# open the wave file

wf = wave.open(wav_file, 'rb')

# instantiate PyAudio

p = pyaudio.PyAudio()

# open a stream

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),

channels=wf.getnchannels(),

rate=wf.getframerate(),

output=True)

# read data from the wave file and play it

data = wf.readframes(1024)

while data:

stream.write(data)

data = wf.readframes(1024)

# close the stream and terminate PyAudio

stream.stop_stream()

stream.close()

p.terminate()

play_wav_audio(output_file)

不过,我们调用的 PaddleSpeech 代码里的默认参数有一个小问题,就是它只支持中文的语音合成。如果你的文本带上英文运行一下,你会发现合成的语音里面只有中文,没有英文。

tts_executor = TTSExecutor()

text = "今天天气十分不错,Paddle Speech也能做语音合成。"

output_file = "./data/paddlespeech_missing.wav"

tts_executor(text=text, output=output_file)

play_wav_audio(output_file)

运行上面的代码,你会发现,PaddleSpeech 在合成的语音里面丢失了。

这是因为,PaddleSpeech 默认情况下使用的是一个只支持中文的模型。我们可以通过一些参数来指定使用的模型,一样能够做中英文混合的语音合成。

对应的代码:

tts_executor = TTSExecutor()

text = "早上好, how are you? 百度Paddle Speech一样能做中英文混合的语音合成。"

output_file = "./data/paddlespeech_mix.wav"

tts_executor(text=text, output=output_file,

am="fastspeech2_mix", voc="hifigan_csmsc",

lang="mix", spk_id=174)

play_wav_audio(output_file)

可以看到,和上面的代码相比,我们增加了 4 个参数。

am,是 acoustic model 的缩写,也就是我们使用的声学模型。我们这里选用的是 fastspeech2_mix。fastspeech2 也是一个基于 Transformer 的语音合成模型,速度快、质量高。这里带了一个 mix,代表这个模型是支持中英文混合生成的。

voc,是 vocoder 的缩写,叫做音码器。声学模型只是把我们的文本变成了一个声音波形的信号。我们还需要通过音码器,把声学模型给出的波形变成可以播放的音频。我们这里选择的 HiFiGAN_csMSC,是一个高保真(HiFi)、基于对抗生成网络(GAN)技术的模型,它的训练数据用到了 HiFiSinger 和 csMSC,而模型的名字就来自这些关键词的组合。

lang,代表我们模型支持的语言,这里我们自然应该选 mix。

spk_id,类似于我们之前在 Azure 里看到的 voice_name,不同的 spk_id 听起来就是不同的人说的话。

运行这个代码,一样能够正常地生成中英文混合的语音内容。如果想要了解 PaddleSpeech 的语音合成功能,还有它所支持的各种模型和各种应用场景,可以参看 GitHub 上的 Demo 文档。

小结

这一讲我们学会了两种语音合成的方式。一种是使用 Azure 云提供的 API,另一种则是使用百度开源的 PaddleSpeech。Azure 云的语音合成,不仅仅是能简单地把文本变成人声,还能通过 SSML 这个 W3C 标准下的 XML 标记语言,指定不同的人声(voice_name)、语气(style)还有角色(role)。这些功能都是非常有实用价值的,能够帮助我们处理各种场景下的语音合成需求。

而 PaddleSpeech 则带给了我们一个开源方案,并且它也支持中英文混合在一起的语音生成。它背后可供选择的模型里,我们使用的也是基于 Transformer 的 fastspeech2 模型。可以看到,目前 Transformer 类型的模型在各个领域都已经占据了主流。

学到这里,我们的 AI 就拥有了声音。而在下一讲里,会拿我们已经学到的知识,搭建一个可以通过语音和你聊天的机器人。并且更进一步地,我们还会为它配上你的虚拟形象,希望你和我一样对下一讲充满期待!

思考题

PaddleSpeech 不仅能拿来做语音合成,也能用来做语音识别。你能试试看用它做语音识别的效果吗?和 OpenAI Whisper 比起来,你觉得它们两个哪个效果更好?

推荐阅读

PaddleSpeech 的中文文档里面,包含了大量使用 PaddleSpeech 这个开源库的场景和方法,有兴趣可以去看一看。百度的 PaddlePaddle 的社区里面,也有专门的语音相关的课程,如果想深入了解的话,也可以去看一看。

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: