实时语音对话能力
更新时间:2025-04-18
目标
实现一个实时语音对话功能,支持多种语音音色。用户可以参考cookbook代码,通过AppBuilder-SDK将实时语音融入到自己的平台、应用中。
实现原理
通过循环不断处理用户的语音,将语音转文本,然后进行对话,最后将对话结果通过TTS进行播报。
- 使用大模型的 ASR 进行语音转文本。
- 使用用户自己创建的Agent进行对话,适配用户的应用场景,并具有上下文理解能力。
- 使用大模型的 TTS 进行文本转语音并进行播报。
前置条件
- 使用内置ASR、TTS组件之前,请先开通组件服务并够买额度,可参考开通组件服务
- pip安装pyaudio、webrtcvad依赖包
- 给程序开放麦克风权限
- 创建好自己的Agent应用
示例代码
JSON
1# Copyright (c) 2024 Baidu, Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16import time
17import wave
18import sys
19import pyaudio
20import webrtcvad
21import appbuilder
22import re
23
24# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5
25# 设置环境变量
26os.environ["APPBUILDER_TOKEN"] = (
27 "..."
28)
29# 已发布AppBuilder应用的ID
30app_id = "..."
31appbuilder.logger.setLoglevel("ERROR")
32
33CHUNK = 1024
34FORMAT = pyaudio.paInt16
35CHANNELS = 1 if sys.platform == "darwin" else 2
36RATE = 16000
37DURATION = 30 # ms
38CHUNK = RATE // 1000 * DURATION
39
40
41class Chatbot:
42 def __init__(self):
43 self.p = pyaudio.PyAudio()
44 self.tts = appbuilder.TTS()
45 self.asr = appbuilder.ASR()
46 self.agent = appbuilder.AppBuilderClient(app_id)
47 self.conversation_id = self.agent.create_conversation()
48
49 def run(self):
50 self.run_tts_and_play_audio(
51 "我是你的专属聊天机器人,如果你有什么问题,可以直接问我"
52 )
53 while True:
54 # Record
55 audio_path = "output.wav"
56 print("开始记录音频...")
57 if self.record_audio(audio_path) < 1000:
58 time.sleep(1)
59 continue
60 print("音频记录结束")
61
62 # ASR
63 print("开始执行ASR...")
64 query = self.run_asr(audio_path)
65 print("结束执行ASR")
66
67 # Agent
68 print("query: ", query)
69 if len(query) == 0:
70 continue
71 answer = self.run_agent(query)
72 results = re.findall(r"(https?://[^\s]+)", answer)
73 for result in results:
74 print("链接地址:", result)
75 answer = answer.replace(result, "")
76 print("answer:", answer)
77
78 # TTS
79 print("开始执行TTS并播报...")
80 self.run_tts_and_play_audio(answer)
81 print("结束TTS并播报结束")
82
83 def record_audio(self, path):
84 with wave.open(path, "wb") as wf:
85 wf.setnchannels(CHANNELS)
86 wf.setsampwidth(self.p.get_sample_size(FORMAT))
87 wf.setframerate(RATE)
88 stream = self.p.open(
89 format=FORMAT, channels=CHANNELS, rate=RATE, input=True
90 )
91 vad = webrtcvad.Vad(1)
92 not_speech_times = 0
93 speech_times = 0
94 total_times = 0
95 start_up_times = 33 * 5 # 初始时间设置为5秒
96 history_speech_times = 0
97 while True:
98 if history_speech_times > 33 * 10:
99 break
100 data = stream.read(CHUNK, False)
101 if vad.is_speech(data, RATE):
102 speech_times += 1
103 wf.writeframes(data)
104 else:
105 not_speech_times += 1
106 total_times += 1
107 if total_times >= start_up_times:
108 history_speech_times += speech_times
109 # 模拟滑窗重新开始计数
110 if float(not_speech_times) / float(total_times) > 0.7:
111 break
112 not_speech_times = 0
113 speech_times = 0
114 total_times = 0
115 start_up_times = start_up_times / 2
116 if start_up_times < 33:
117 start_up_times = 33
118 stream.close()
119 return history_speech_times * DURATION
120
121 def run_tts_and_play_audio(self, text: str):
122 # AppBuilder内置的TTS使用文档,用户可根据文档调整参数:https://github.com/baidubce/app-builder/tree/master/python/core/components/tts
123 msg = self.tts.run(
124 appbuilder.Message(content={"text": text}),
125 speed=5,
126 pitch=5,
127 volume=5,
128 person=0,
129 audio_type="pcm",
130 model="paddlespeech-tts",
131 stream=True,
132 )
133 stream = self.p.open(
134 format=self.p.get_format_from_width(2),
135 channels=1,
136 rate=24000,
137 output=True,
138 frames_per_buffer=2048,
139 )
140 for pcm in msg.content:
141 stream.write(pcm)
142 stream.stop_stream()
143 stream.close()
144
145 # AppBuilder内置的ASR使用文档,用户可根据文档调整参数:https://github.com/baidubce/app-builder/blob/master/python/core/components/asr/README.md
146 def run_asr(self, audio_path: str):
147 with open(audio_path, "rb") as f:
148 content_data = {"audio_format": "wav", "raw_audio": f.read(), "rate": 16000}
149 msg = appbuilder.Message(content_data)
150 out = self.asr.run(msg)
151 text = out.content["result"][0]
152 return text
153
154 def run_agent(self, query):
155 msg = self.agent.run(self.conversation_id, query, stream=True)
156 answer = ""
157 for content in msg.content:
158 answer += content.answer
159 return answer
160
161
162if __name__ == "__main__":
163 chatbot = Chatbot()
164 chatbot.run()
使用方法
直接运行程序即可
用户也可以将下面的功能模块替换成自己的其他实现或模型:
- record_audio: 录音
- run_asr: 语音识别语音识别,AppBuilder ASR组件使用文档
- run_agent: Agent对话功能,AppBuilder TTS组件使用文档
- run_tts_and_play_audio:回复的语音生成并播报
AppBuilder TTS组件参数
参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 |
---|---|---|---|---|
message | String | 是 | 待转成语音的文本 | Message(content={"text": "需合成的文本"}) |
model | String | 否 | 默认是baidu-tts模型,可选值:paddlespeech-tts、baidu-tts | paddlespeech-tts |
speed | Integer | 否 | 语音语速,默认是5中等语速,取值范围在0~15之间,仅当模型为baidu-tts参数有效,如果模型为paddlespeech-tts,参数自动失效 | 5 |
pitch | Integer | 否 | 语音音调,默认是5中等音调,取值范围在0~15之间,仅当模型为baidu-tts参数有效,如果模型为paddlespeech-tts,参数自动失效 | 5 |
volume | Integer | 否 | 语音音量,默认是5中等音量,取值范围在0~15之间,,仅当模型为baidu-tts参数有效,如果模型为paddlespeech-tts,参数自动失效 | 5 |
person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、5118(度小鹿)、106(度博文)、110(度小童)、111(度小萌)、103(度米朵)、5(度小娇);臻品音库包括:4003(度逍遥-情感男声)、4106(度博文-专业男主播)、4115(度小贤-电台男主播)、4119(度小鹿-甜美女声)、4105(度灵儿-清激女声)、4117(度小乔-活泼女声)、4100(度小雯-活力女主播)、4103(度米朵-可爱女声)、4144(度姗姗-娱乐女声)、4278(度小贝-知识女主播)、4143(度清风-配音男声)、4140(度小新-专业女主播)、4129(度小彦-知识男主播)、4149(度星河-广告男声)、4254(度小清-广告女声)、4206(度博文-综艺男声)、4226(南方-电台女主播)。仅当模型为baidu-tts参数有效,如果模型为paddlespeech-tts,参数自动失效 | 0 |
audio_type | String | 否 | 音频文件格式,如果使用baidu-tts模型可选mp3,wav; 如果使用paddlespeech-tts模型非流式返回,参数只能设为wav;如果使用paddlespeech-tts模型流式返回,参数只能设为pcm | wav |
stream | Bool | 否 | 默认是False, 目前paddlespeech-tts模型支持流式返回,baidu-tts模型不支持流式返回 | False |
retry | Integer | 否 | HTTP重试次数 | 3 |
timeout | Integer | 否 | HTTP超时时间 | 5 |