1、前言:
最近在翻自己电脑的时候,发现之前还做过这个。为了防止换电脑时丢失,这里写个博客记录下。
2、功能模块:
整个项目分为五个文件,如下表:
文件名 | 作用 |
main.py | 主函数 |
ChatgptApi.py | 实现与api2d交互的接口 |
RecordModule.py | 实现录音功能 |
TextToSpeechModule.py | 实现文本转语音功能 |
SpeechToTextModule.py | 实现语音转文本功能 |
项目整体思路:通过RecordModule模块录音,生成音频文件。再通过SpeechToTextModule模块将音频文件转换为文本。之后,通过ChatgptApi模块将文本发送给chatgpt,返回回答后调用TextToSpeechModule模块将文本转为音频文件并播放。
2.1、main.py
import RecordModule
import ChatgptApi
import TextToSpeechModule
import SpeechToTextModule
import keyboard
from playsound import playsound
# 利用api2d接口实现的chatgpt语音对话,其中文本转语音是使用whisper库离线转的,而回复以及语音转文本则是使用了api2d的接口
if __name__ == '__main__':
press = 'alt'
confirmPress = 'enter'
mode = 'debug mode'
# 本地语音只支持中英文
# 使用本地语音节省经费,但效果不如OPENAI
isUseLocalSpeaker = "true"
text = ''
background = '输入给你的文字是通过其他语音转文本模型识别的,可能会出错,需要你自行纠正并理解,你输出的文字会转换为语音。要求输出口语化,并加入一些语气词'
ChatgptApi.SetBackGround(background)
ChatgptApi.SetMemory(flag=False)
if mode == 'debug mode':
print('press ' + press + ' to recording')
while True:
if keyboard.is_pressed(press):
RecordModule.setPressKey(press)
RecordModule.Record('question.mp3')
text = SpeechToTextModule.STT('question.mp3')
print('question:')
print(text)
if mode == 'debug mode':
text = input("修正内容:") or text
print(text)
text = ChatgptApi.rquestChatGpt(text)
print('response:')
print(text)
if isUseLocalSpeaker == "true":
# 本地文本转语音
TextToSpeechModule.TTSLcoal(text=text,language=0,rate=200,volume=0.9,filename="respones.mp3",sayit=1)
else:
TextToSpeechModule.TTS(text, 'mp3')
playsound('response.mp3')
print('press ' + press + ' to recording')
elif keyboard.is_pressed('esc'):
break
elif mode == 'text mode':
while True:
text = input("内容:") or text
print(text)
if text == 'esc':
break
text = ChatgptApi.rquestChatGpt(text)
print('response:')
print(text)
2.2、ChatgptApi.py
import requests
import json
import whisper
url = "https://oa.api2d.net/v1/chat/completions"
msg = []
jsonBody = {}
question = '你好'
background = '输入可能会出错,需要你自行纠正并理解。要求输出口语化,并加入一些语气词。你的回复尽量简短'
isMem = True
def rquestChatGpt(text):
question = text
msg.append({'role': 'system', 'content': background})
msg.append({'role': 'user', 'content': question})
jsonBody["model"] = "gpt-3.5-turbo-0613"
jsonBody['messages'] = msg
jsonBody['safe_mode'] = False
# jsonBody['max_tokens'] = 100
payload = json.dumps(jsonBody)
headers = {
'Authorization': 'Bearer <在api2d官网上申请的Forward Key>',
'User-Agent': 'Apifox/1.0.0 (https://openai.api2d.net)',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
response = json.loads(response.text)
response = response['choices'][0]['message']['content']
if isMem == True:
msg.append({'role': 'assistant', 'content': response})
else:
msg.clear()
msg.append({'role': 'system', 'content': background})
return response
def ClearMemory():
msg.clear()
msg.append({'role': 'system', 'content': background})
def SetMemory(flag = True):
global isMem
isMem = flag
def SetBackGround(str):
global background
background = str
国内无法直接访问openAI的网络api,可以通过api2d中转,就是费用会贵一点点。
api2d官网:API2D
2.3、RecordModule.py
import pyaudio
import wave
import keyboard
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 16000
FILE = 'question.mp3'
press = 'space'
def setPressKey(key):
global press
press = key
def Record(file):
FILE = file
p = pyaudio.PyAudio() # 初始化
print('Recording ON, press ' + press + ' to stop Recording')
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK) # 创建录音文件
frames = []
while True:
if keyboard.is_pressed(press):
break
data = stream.read(CHUNK)
frames.append(data) # 开始录音
print("Recording OFF")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(FILE, 'wb') # 保存
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
2.4、TextToSpeechModule.py
import requests
import json
import pyttsx3
from zhconv import convert
def TTS(input, type):
url = "https://oa.api2d.net/v1/audio/speech"
msg = []
jsonBody = {}
jsonBody["model"] = "tts-1"
jsonBody['input'] = input
jsonBody['voice'] = 'alloy'
jsonBody['response_format'] = type
payload = json.dumps(jsonBody)
headers = {
'Authorization': 'Bearer <在api2d官网上申请的Forward Key>',
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
file = open('response.' + type,'wb')
file.write(response.content)
file.close()
#print(response.text)
return response.content
def TTSLcoal(text, language, rate, volume, filename, sayit=0):
# 参数说明: 六个重要参数,阅读的文字,语言(0-英文/1-中文),语速,音量(0-1),保存的文件名(以.mp3收尾),是否发言(0否1是)
text = convert(text, 'zh-hans') # 转换字符串为简中
engine = pyttsx3.init() # 初始化语音引擎
engine.setProperty('rate', rate) # 设置语速
# 速度调试结果:50戏剧化的慢,200正常,350用心听小说,500敷衍了事
engine.setProperty('volume', volume) # 设置音量
voices = engine.getProperty('voices') # 获取当前语音的详细信息
if int(language) == 0:
engine.setProperty('voice', voices[0].id) # 设置第一个语音合成器 #改变索引,改变声音。0中文,1英文(只有这两个选择)
elif int(language) == 1:
engine.setProperty('voice', voices[1].id)
if int(sayit) == 1:
engine.say(text) # pyttsx3->将结果念出来
elif int(sayit) == 0:
print("那我就不念了哈")
engine.save_to_file(text, filename) # 保存音频文件
print(filename, "保存成功")
engine.runAndWait() # pyttsx3结束语句(必须加)
engine.stop() # pyttsx3结束语句(必须加)
这里有两个接口,其中TTS接口是调用了OpenAI文本转语音的网络api,效果很好,但是需要花钱。而TTSLcoal接口则是使用了第三方库pyttsx3来实现,免费但比较僵硬。
2.5、SpeechToTextModule.py
import whisper
def STT(file = 'question.mp3'):
model = whisper.load_model("base")
audio = whisper.load_audio(file)
audio = whisper.pad_or_trim(audio)
mel = whisper.log_mel_spectrogram(audio).to(model.device)
_, probs = model.detect_language(mel)
print(f"Detected language: {max(probs, key=probs.get)}")
options = whisper.DecodingOptions(fp16=False) # cpu一定要設置false
result = whisper.decode(model, mel, options)
return result.text