拓冰建站拓冰建站
首页 / 资讯中心 / 正文

IoT-For-Beginners 实战:在 Raspberry Pi 上用 Azure 语音服务实现文本转语音(TTS)与语音计时器反馈

IoT-For-Beginners 实战在 Raspberry Pi 上用 Azure 语音服务实现文本转语音TTS与语音计时器反馈【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本文是 IoT-For-Beginners 课程「6-consumer消费类物联网」第 3 课Spoken feedback中 Raspberry Pi 路径的实战指南。你将学习如何用 Python 调用 Azure 认知服务语音服务的 REST API把文本转换成语音并在树莓派上通过扬声器播放最终让上一课构建的smart-timer语音计时器不仅能听懂你的指令还能用语音回复「定时器已设定」并在倒计时结束时发出提醒。背景从单向语音指令到双向语音交互在前两课中你已经实现了「语音 → 文本 → 理解意图LUIS」的链路按下按钮说话convert_speech_to_text将音频转为文本get_timer_time再调用 serverless 函数解析出定时秒数见 single-board-computer-set-timer.md。但此时的反馈还停留在控制台打印文本阶段。本部分要补齐最后一块拼图——文本转语音Text to Speech, TTS。语音服务既能把语音转成文本也能把文本转回语音。你只需把要朗读的文本连同期望的音频格式如采样率发给 REST API就能拿回一段二进制音频数据通过树莓派的扬声器播放出来。完整可运行代码位于 code-spoken-response/pi/smart-timer/app.py。TTS 请求的核心要素语音Voice与 SSML调用 TTS 时不能只给文本还必须指定使用哪个语音voice因为语音服务可以用多种不同的声音来合成语音每种语言都支持一系列语音。语音的指定是通过SSMLSpeech Synthesis Markup Language完成的——它是一种用于语音合成应用的 XML 标记语言不仅能声明要转换的文本还能声明文本的语言、使用的语音甚至可以按词定义语速、音量与音调。一个典型的 SSML 请求体例如用英式英语语音en-GB-MiaNeural朗读「Your 3 minute 5 second time has been set」如下speak version1.0 xml:langen-GB voice xml:langen-GB nameen-GB-MiaNeural Your 3 minute 5 second time has been set /voice /speak关键点是voice标签的name属性——即语音的ShortName。要从数百种候选中挑出合适的 ShortName需要先查询语音列表。任务一获取语音——查询受支持语音列表在smart-timer项目的app.py中say函数上方新增一个get_voice函数向语音服务请求某个语言的语音列表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})这段代码的工作原理请求voices/list端点用get_access_token()获取的 Bearer Token 做鉴权get_access_token在文件上方已定义它用speech_api_key通过Ocp-Apim-Subscription-Key换取令牌解析返回的 JSON 列表用next(...)找出第一个Locale与language匹配、且VoiceType为Neural的语音返回该语音的ShortName并赋值给模块级变量voice、打印到控制台。语音只请求一次即可之后每次转换文本到语音都复用这个voice值。在仓库的参考实现 code-spoken-response/pi/smart-timer/app.py 中get_voice的筛选逻辑与文档一致参考实现按语言匹配第一个结果。语音列表数据本身很大——每个语音由一段 JSON 描述例如en-US-AriaNeural的条目包含Name、ShortName、Gender、Locale、StyleList、SampleRateHertz、VoiceType等字段详见 wio-terminal-text-to-speech.md。 如果你明确想用某个特定语音可以删掉这个函数直接把语音名硬编码例如voice hi-IN-SwaraNeural。任务二转换文本为语音——SSML 请求与音频格式声明音频输出格式在get_voice下方定义一个常量声明希望从语音服务拿到的音频格式playback_format riff-48khz-16bit-mono-pcm可用的格式取决于你的硬件。如果播放音频时出现Invalid sample rate之类的错误就换一个值。需要使用的是riff格式可尝试的值包括riff-16khz-16bit-mono-pcm、riff-24khz-16bit-mono-pcm和riff-48khz-16bit-mono-pcm。树莓派上的 PyAudio/ALSA 组合对 48kHz 16bit mono PCM 通常兼容良好。定义 get_speech 函数接着声明一个名为get_speech的函数用语音服务 REST API 把文本转成语音def get_speech(text):设置请求 URL 与 Headers在get_speech内定义要调用的 URL 和要发送的 headersurl fhttps://{location}.tts.speech.microsoft.com/cognitiveservices/v1 headers { Authorization: Bearer get_access_token(), Content-Type: application/ssmlxml, X-Microsoft-OutputFormat: playback_format }这里把鉴权头设置为生成的访问令牌Content-Type声明请求体是 SSMLapplication/ssmlxmlX-Microsoft-OutputFormat指定所需的音频输出格式——即上一步的playback_format。组装 SSML 请求体在函数内构建要发送给 REST API 的 SSMLssml fspeak version\1.0\ xml:lang\{language}\ ssml fvoice xml:lang\{language}\ name\{voice}\ ssml text ssml /voice ssml /speak这段 SSML 同时指定了语言xml:lang、语音name和要转换的文本内容。发起请求并返回音频最后在函数中发起 REST 请求并返回二进制音频数据response requests.post(url, headersheaders, datassml.encode(utf-8)) return io.BytesIO(response.content)注意ssml必须用utf-8编码后作为data发送返回的response.content是原始音频字节被包装成io.BytesIO便于后续用wave模块按文件方式读取。任务三播放音频——PyAudio 输出流在get_speech函数下方定义一个新函数负责播放 REST API 返回的音频def play_speech(speech):传入的speech就是 REST API 返回的二进制音频数据。用wave模块把它作为 wave 文件打开再交给 PyAudio 播放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()这段代码与录音时的 PyAudio 流用法相似区别在于流被设置为output 流outputTrue并通过output_device_indexspeaker_card_number指定扬声器声卡在参考实现中speaker_card_number 1与microphone_card_number同值见 app.py数据从音频文件中读取readframes(4096)并推送到流中流的细节参数采样格式、声道数、采样率不再硬编码而是从 wave 文件头中读取getsampwidth()、getnchannels()、getframerate()这让playback_format更换后无需修改播放代码。集成把 TTS 接入 say 函数用以下内容替换say函数的实现def say(text): speech get_speech(text) play_speech(speech)这段代码先把文本转成二进制语音数据再播放出来。在参考实现中say被两条路径调用见 app.pycreate_timer设定定时器后立即朗读「X minute Y second timer started.」announce_timer通过threading.Timer在倒计时结束时朗读「Times up on your X minute Y second timer.」。两条路径都通过say(announcement)触发 TTS形成完整的语音交互闭环。运行与验证在 VS Code 中打开smart-timer项目树莓派上直接运行虚拟设备则先激活虚拟环境确保 Functions 应用也在运行它提供text-to-timerHTTP 触发器和 LUIS 意图解析参见 text-to-timer 源码运行app.py按下按钮说出设定定时器的指令你会先听到一条语音确认「定时器已设定」倒计时结束时再听到另一条语音提醒若出现Invalid sample rate错误按上文说明更换playback_format的值。扩展参考serverless 侧的 TTS 实现课程为 Wio Terminal 提供了另一条 TTS 路线由于单片机内存有限音频重采样等工作被放到 serverless 函数中完成。仓库中的 text-to-speech 函数 展示了同样的 SSML 请求模式并额外用librosa把 48kHz 音频重采样为 44.1kHzlibrosa声明在 requirements.txt可作为理解playback_format与音频格式关系的补充材料。树莓派作为完整 Linux 系统没有此限制PyAudio 可直接播放服务返回的原始 WAV 数据。小结至此smart-timer项目完成了「听懂指令 → 解析意图 → 设定定时 → 语音反馈」的完整链路get_voice负责挑选语音get_speech用 SSML 调用 TTS REST API 取回音频play_speech通过 PyAudio 输出流播放。三步合一的say函数让任何文本都能变成树莓派说出口的话。完整参考代码见 code-spoken-response/pi 目录本课其余设备Wio Terminal、虚拟设备的对照实现分别位于同目录下的 wio-terminal-text-to-speech.md 与 virtual-device-text-to-speech.md。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门