)
IoT-For-Beginners 实战在树莓派上使用 Azure 语音服务实现文本转语音Text to Speech【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本指南对应 IoT-For-Beginners 项目 6-consumer 单元第 3 课《设置定时器并提供语音反馈》中的树莓派Raspberry Pi硬件路径讲解如何基于smart-timer定时器项目通过 Azure 认知服务语音服务的 REST API 将文本转换为语音并通过 PyAudio 在扬声器上播放。读完本指南后你将掌握获取语音列表并挑选合适嗓音、构造 SSML 请求文本转语音、在树莓派上播放返回的 WAV 音频三条完整链路让设备能用自然语音回答用户。背景为什么需要语音输出在前面两课中设备已经实现了语音转文字Speech to Text和从文本提取定时请求通过 LUIS 理解语言。但智能助手不是单向通信设备——用户说设置一个 3 分钟定时器设备应当回应好的您的定时器已设置为 3 分钟并在定时结束时提醒用户。本课要做的就是调用与语音识别相同的语音服务把要回复的文本合成为音频并播放出来。文本转语音的基本原理文本转语音Text to Speech是把文本拆解为构成音素phonemes的过程再用预录音频或 AI 模型生成的音频拼接成语音。典型系统包含三个阶段文本分析Text analysis把原始文本转换为可用于语音合成的词。例如1234可能读作 One thousand, two hundred thirty four也可能读作 One, two, three, four取决于上下文不同地区locale的读法也有差异如美式英语 One hundred twenty 与英式英语 One hundred and twenty。语言分析Linguistic analysis把词拆解为音素并结合上下文补充语调信息例如句尾升调可以把陈述句变成疑问句。波形生成Wave-form generation早期系统用单个音素录音拼接声音单调机械现代系统使用基于深度学习的 ML 模型生成接近真人的语音。发送合成请求时使用语音合成标记语言Speech Synthesis Markup LanguageSSML这是一种基于 XML 的标记语言不仅定义要转换的文本还定义文本的语言、使用的嗓音甚至能控制部分或全部词语的语速、音量和音调。例如以下 SSML 使用英式英语嗓音en-GB-MiaNeural合成一句提示语speak version1.0 xml:langen-GB voice xml:langen-GB nameen-GB-MiaNeural Your 3 minute 5 second time has been set /voice /speak说明文本转语音系统的完整背景、三个阶段的详细讲解与挑战练习参见本课总览文档 README.md。整体思路树莓派如何调用语音服务树莓派属于全功能单板计算机拥有完整的内存与音频栈因此可以直接调用语音服务的 REST API 完成合成与播放无需像 Wio Terminal 那样借助服务端函数代劳重采样对比可参考 wio-terminal-text-to-speech.md。核心调用链如下用 API Key 换取访问令牌get_access_token查询指定语言的可用嗓音get_voice构造 SSML调用合成 REST 接口get_speech拿到 WAV 二进制音频用 PyAudio 输出流播放play_speech。本课涉及的完整可运行代码位于 code-spoken-response/pi/smart-timer/app.py。前置条件与项目准备在开始前请确认已完成本课之前的步骤语音识别speech_api_key、location、language三个变量已配置为你的语音服务资源值、已通过无服务器代码Azure Functions实现text-to-timer意图解析参考 single-board-computer-set-timer.md树莓派上已安装pyaudio、requests、wave等 Python 依赖已接入 Grove 按钮用于触发录音和 USB 声卡/扬声器在 VS Code 中打开smart-timer项目。任务一获取一个嗓音不同语言支持不同的嗓音语音服务提供了 REST 接口用于查询每个语言支持的嗓音列表。在say函数上方添加如下代码def get_voice(): url fhttps://{location}.tts.speech.microsoft.com/cognitiveservices/voices/list headers { Authorization: Bearer get_access_token() } response requests.get(url, headersheaders) voices_json json.loads(response.text) first_voice next(x for x in voices_json if x[Locale].lower() language.lower() and x[VoiceType] Neural) return first_voice[ShortName] voice get_voice() print(fUsing voice {voice})这段代码的工作方式向/cognitiveservices/voices/list端点发起 GET 请求认证方式为Authorization: Bearer token其中令牌由get_access_token()获取用json.loads解析返回的嗓音 JSON 列表用next()找到第一个Locale与当前language匹配、且VoiceType为Neural神经语音音质更自然的条目返回该条目的ShortName如en-US-JennyNeural这是合成请求中真正需要的标识函数调用后把嗓音存入全局变量voice并打印到控制台。该值只需请求一次之后每次文本转语音调用都可复用。提示完整的支持嗓音列表可查阅语音服务的语言和嗓音支持官方文档。如果希望固定使用某个特定嗓音可以删掉该函数直接把ShortName硬编码例如voice hi-IN-SwaraNeural。从源码结构看仓库在无服务器端也提供了等价的嗓音筛选逻辑get-voicesHTTP 触发器get-voices/init.py同样调用/voices/list接口并按请求体中的language过滤、只返回ShortName列表——这正是树莓派版在设备端直接完成的事情。任务二将文本转换为语音2.1 定义音频输出格式常量向语音服务请求音频时可以指定多种输出格式。在get_voice相关代码下方定义常量playback_format riff-48khz-16bit-mono-pcm选用哪种格式取决于你的硬件。如果播放音频时遇到Invalid sample rate错误就换一个值。合成 REST API 支持多种输出格式但树莓派场景必须使用riff格式的 WAV 音频可依次尝试的值包括格式值采样率位深声道riff-16khz-16bit-mono-pcm16 kHz16 bit单声道riff-24khz-16bit-mono-pcm24 kHz16 bit单声道riff-48khz-16bit-mono-pcm48 kHz16 bit单声道2.2 声明合成函数并配置请求头声明get_speech函数它负责通过 REST API 把文本转换为语音def get_speech(text): url fhttps://{location}.tts.speech.microsoft.com/cognitiveservices/v1 headers { Authorization: Bearer get_access_token(), Content-Type: application/ssmlxml, X-Microsoft-OutputFormat: playback_format }三个请求头的含义Authorization使用动态获取的访问令牌而不是直接暴露 API KeyContent-Type声明请求体是application/ssmlxml即 SSML 内容X-Microsoft-OutputFormat指定期望返回的音频格式即上面定义的playback_format。2.3 构造 SSML 请求体ssml fspeak version\1.0\ xml:lang\{language}\ ssml fvoice xml:lang\{language}\ name\{voice}\ ssml text ssml /voice ssml /speak这段 SSML 指定了四要素SSML 版本1.0、整个文档的语言xml:lang、要使用的嗓音voice name...以及真正要朗读的文本text。从仓库源码 app.py 可以看到实际代码逐行拼接出完全一致的 SSML 结构。2.4 发起请求并返回二进制音频response requests.post(url, headersheaders, datassml.encode(utf-8)) return io.BytesIO(response.content)ssml.encode(utf-8)把 SSML 字符串编码为 UTF-8 字节流后作为 POST 请求体发送响应response.content是合成的音频二进制数据WAV 格式用io.BytesIO包装成内存中的类文件对象返回方便后续用wave模块解析。注意本项目中的无服务器函数版本text-to-speech/init.py展示了同样的请求头、SSML 构造与 POST 调用方式区别仅在于它额外用librosa把 48 kHz 音频重采样到 44.1 kHz供 Wio Terminal 的 ReSpeaker 播放。树莓派因为音频栈完整可以直接使用返回的原始 WAV无需重采样。任务三播放音频3.1 定义播放函数在get_speech函数下方定义play_speech播放 REST API 返回的音频def play_speech(speech): with wave.open(speech, rb) as wave_file: stream audio.open(formataudio.get_format_from_width(wave_file.getsampwidth()), channelswave_file.getnchannels(), ratewave_file.getframerate(), output_device_indexspeaker_card_number, outputTrue) data wave_file.readframes(4096) while len(data) 0: stream.write(data) data wave_file.readframes(4096) stream.stop_stream() stream.close()这段代码的关键点wave.open(speech, rb)把get_speech返回的io.BytesIO对象当作 WAV 文件打开读取与录音时创建 PyAudio 输入流类似这里创建的是输出流outputTrue并通过output_device_indexspeaker_card_number指定输出声卡该值在文件头部定义与麦克风卡号microphone_card_number相对流参数采样宽度、声道数、采样率不是硬编码的而是从 WAV 文件头中读取getsampwidth()、getnchannels()、getframerate()保证与合成音频完全匹配每次读取 4096 帧数据写入流循环直到读完整个文件最后停止并关闭流。3.2 更新say函数用以下代码替换say函数的内容def say(text): speech get_speech(text) play_speech(speech)say函数现在成为整个语音回复链路的总入口把文本合成为二进制音频数据然后播放。3.3 完整的定时器语音闭环在 app.py 中可以看到say被两处调用create_timer(total_seconds)设置定时器时拼出X minute Y second timer started. 并立即语音播报announce_timer(minutes, seconds)定时结束后由threading.Timer触发播报Times up on your X minute Y second timer.。这与前面课程实现的语音识别、text-to-timer意图解析形成完整闭环用户说话 → 识别成文本 → 服务端解析出秒数 → 树莓派设置threading.Timer→ 语音播报确认与到期提醒。运行与验证运行应用前请确保函数应用Azure Functions仍在运行text-to-timer等 HTTP 触发器可访问树莓派已连接按钮与扬声器在终端执行python3 app.py。随后按下按钮说出定时需求例如set a 2 minute timer你将先听到设备用选定嗓音播报2 minute timer started.等定时结束后再听到Times up on your 2 minute timer.。故障排查如果播放时出现Invalid sample rate错误说明声卡不支持当前playback_format指定的采样率请按上文表格依次改用riff-24khz-16bit-mono-pcm或riff-16khz-16bit-mono-pcm后重试。与其他硬件路径的对照为了帮助你理解树莓派方案的取舍这里对照本课另外两条路径虚拟 IoT 设备virtual-device-text-to-speech.md使用语音服务 Python SDK 的SpeechSynthesizer调用get_voices_async()获取嗓音、speak_ssml()合成。一个关键细节是播放语音前要recognizer.stop_continuous_recognition()暂停连续识别播完再start_continuous_recognition()否则播报内容可能被识别器捕获、被 LUIS 误判为新的定时请求导致定时器无限嵌套。树莓派路径因为按键触发录音天然规避了此问题。Wio Terminalwio-terminal-text-to-speech.md受单片机内存限制必须把嗓音列表查询77 KB 以上 JSON和音频重采样48 kHz → 44.1 kHz都放到无服务器函数中完成设备端只负责把返回的音频写入 SD 卡。小结通过本指南你在树莓派上完成了文本转语音的完整实现先通过 REST API 查询并选定与language匹配的 Neural 嗓音再构造 SSML 请求合成 WAV 音频最后用 PyAudio 输出流播放让smart-timer项目能够用自然语音播报定时器状态。完整的可运行代码参见 code-spoken-response/pi。后续可以挑战进阶任务assignment.md例如通过 LUIS 识别取消定时器的意图并用同样的语音链路给出回应。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考