commit a20d8bfbb5b449f2386944164a05f7e7423eee4d Author: sairate Date: Fri Sep 27 20:28:27 2024 +0800 Signed-off-by: sairate diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/AIchat.iml b/.idea/AIchat.iml new file mode 100644 index 0000000..b60f9e2 --- /dev/null +++ b/.idea/AIchat.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/git_toolbox_blame.xml b/.idea/git_toolbox_blame.xml new file mode 100644 index 0000000..7dc1249 --- /dev/null +++ b/.idea/git_toolbox_blame.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a5ec0a3 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9fdea83 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..21067e6 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +``` python +pip install python-dotenv +``` + diff --git a/app.py b/app.py new file mode 100644 index 0000000..0ac17df --- /dev/null +++ b/app.py @@ -0,0 +1,24 @@ +from flask import Flask, render_template +import sqlite3 + +app = Flask(__name__) + + +@app.route('/') +def index(): + # 连接数据库 + conn = sqlite3.connect('conversation.db') + c = conn.cursor() + + # 从数据库中获取数据 + c.execute("SELECT * FROM conversation") + conversations = c.fetchall() + + # 关闭数据库连接 + conn.close() + + return render_template("./index.html", conversations=conversations) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/audio/55d4badc-8a6c-4415-a5f3-ecff6b5b3022.mp3 b/audio/55d4badc-8a6c-4415-a5f3-ecff6b5b3022.mp3 new file mode 100644 index 0000000..ab05330 Binary files /dev/null and b/audio/55d4badc-8a6c-4415-a5f3-ecff6b5b3022.mp3 differ diff --git a/conversation.db b/conversation.db new file mode 100644 index 0000000..11ae193 Binary files /dev/null and b/conversation.db differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..7101365 --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + + + + + Conversation Table + + +

Conversation Table

+ + + + + + + {% for conversation in conversations %} + + + + + + {% endfor %} +
IDMessage
{{ conversation[0] }}{{ conversation[1] }}
+ + diff --git a/main.py b/main.py new file mode 100644 index 0000000..196121d --- /dev/null +++ b/main.py @@ -0,0 +1,223 @@ + +import pyaudio +import wave +import requests +import json +import base64 +import os +import edge_tts +import asyncio +import pygame +import openai +import uuid # 用于生成唯一的文件名 +import sqlite3 + +def create_connection(db_file='conversation.db'): + # 连接到数据库(如果不存在,则会被创建) + conn = sqlite3.connect('conversation.db') + + # 创建一个游标对象,用于执行SQL语句 + c = conn.cursor() + + # 创建一个名为conversation的表 + c.execute('''CREATE TABLE IF NOT EXISTS conversation + (id INTEGER PRIMARY KEY AUTOINCREMENT, + question TEXT, + answer TEXT, + audio_path TEXT)''') + +def insert_data(question, answer, audio_path): + conn = sqlite3.connect('conversation.db') + cursor = conn.cursor() + cursor.execute("INSERT INTO conversation (question, answer, audio_path) VALUES (?, ?, ?)", + (question, answer, audio_path)) + conn.commit() + conn.close() +# 1.录音 +# 用Pyaudio录制音频(生成wav文件) +def audio_record(rec_time, filename): + """ + :param rec_time : 音频录制时间 + :param filename : 输出音频文件 + :返回值:在当前目录输出一个音频文件 + """ + + CHUNK = 1024 # 定义数据流块 + FORMAT = pyaudio.paInt16 # 16bit编码格式 + CHANNELS = 1 # 单声道 + RATE = 16000 # 16000采样频率 + + # 创建一个音频对象 + p = pyaudio.PyAudio() + + # 创建音频数据流 + stream = p.open(format=FORMAT, + channels=CHANNELS, + rate=RATE, + input=True, + frames_per_buffer=CHUNK) + print('Start recording...') + frames = list() # 空列表用于保存录制的音频流 + # 录制音频数据 + for i in range(0, int(RATE / CHUNK * rec_time)): + data = stream.read(CHUNK) + frames.append(data) + # 录制完成 + # print(frames) + # 停止数据流 + stream.stop_stream() + stream.close() + # 关闭pyaudio + p.terminate() + print('recording done...') + + # 保存音频文件 + with wave.open(filename, 'wb') as f: + f.setnchannels(CHANNELS) # 设置音频声道数 + f.setsampwidth(p.get_sample_size(FORMAT)) # 以字节为样本返回样本宽度 + f.setframerate(RATE) # 设置采样频率 + f.writeframes(b''.join(frames)) + f.close() + + +# 2 获取token + +API_KEY = "7myE5M0cY5gjyKbxcFQqWmZE" # 这里请替换为你的API_KEY +SECRET_KEY = "A2AtUqbqVLdo0kgfiwITWUlB0fxwCA3w" # 这里请替换为你的SECRET_KEY + + +def get_access_token(): + """ + 使用 AK,SK 生成鉴权签名(Access Token) + :return: access_token,或是None(如果错误) + """ + url = "https://aip.baidubce.com/oauth/2.0/token" + params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY} + return str(requests.post(url, params=params).json().get("access_token")) + + +# 3.上传录音文件 +def BaiduYuYin(file_url, token): + """ + :param file_url: 录音文件路径 + :param token: 获取的access token + :return: 录音识别出来的文本 + """ + + try: + RATE = '16000' + FORMAT = 'wav' + CUID = 'rvs7K414cquxm4f62jtasIRi6iNRNXR6' + DEV_PID = '1536' # 普通话,支持简单的英文识别 + + file_url = file_url + token = token + # 以字节格式读取文件之后进行编码 + with open(file_url, 'rb') as f: + speech = base64.b64encode(f.read()).decode('utf-8') + size = os.path.getsize(file_url) # 语音文件的字节数 + headers = {'Content-Type': 'application/json', + 'Accept': 'application/json'} # json格式post上传本地文件 + url = 'https://vop.baidu.com/server_api' + data = { + "format": FORMAT, # 格式 + "rate": RATE, # 取样频率,固定值16000 + "dev_pid": DEV_PID, # 语音识别类型 + "speech": speech, # 本地语音文件的二进制数据,需要进行base64编码 + "cuid": CUID, # 用户唯一标识,用来区分用户 建议填写能区分用户的机器MAC地址或IMEI码,长度为60字符以内。 + "len": size, # 语音文件的字节数 + "channel": 1, # 声道数,仅支持单声道,固定值为1 + "token": token, + } + req = requests.request("POST", url, data=json.dumps(data), + headers=headers) # request.post 改为requests.request("POST"……) + data_dict = json.loads(req.text) + # print(data_dict['result'][0]) + return data_dict['result'][0] # 返回文本 + except: + return '识别不清楚' + + +from ollama import Client +def get_completion(prompt, model="solar"): + client = Client(host='http://8.130.118.164:11434/') + response = client.chat(model, messages=[ + { + 'role': 'user', + 'content': prompt, + }, + ]) + return response['message']['content'] + + +# 5.文本转语音TTS:edge-tts + +async def generate_audio_from_text(text, file_url): + """ + :param text:需要进行转换的文本 + :file_url:转换后输出的音频文件地址 + :return:无 + """ + voice = 'zh-CN-YunxiNeural' + output = file_url + rate = '-4%' + volume = '+0%' + tts = edge_tts.Communicate(text=text, voice=voice, rate=rate, volume=volume) + await tts.save(output) + + +# 6.播放音频文件:pygame +def play_mp3(mp3_file): + """ + :param mp3_file:需要播放的录音文件地址 + :return:无 + """ + pygame.init() # 初始化pygame + pygame.mixer.init() # 初始化音频混合器 + pygame.mixer.music.load(mp3_file) # 加载指定MP3文件 + pygame.mixer.music.play() # 播放 + clock = pygame.time.Clock() + while pygame.mixer.music.get_busy(): # 使用一个循环来等待音频播放完毕,保证程序不会在播放结束前退出 + clock.tick(3) + + +def main(): + create_connection() + while True: + # 1. 提示用户发言 + print('请发言,谢谢!') + # 2. 录制音频 + audio_record(5, 'user_audio.wav') + + print('结束发言') + # 3. 获取百度语音识别的access token + baidu_token = get_access_token() + print('Baidu access token obtained.') + + # 4. 上传录音文件并进行语音识别 + baidu_result = BaiduYuYin('./user_audio.wav', baidu_token) + print('Baidu speech recognition result:', baidu_result) + + # 5. 调用大语言模型进行文本生成 + model_response = get_completion(baidu_result) + print('Model response:', model_response) + + # 6. 将文本转换为语音,保存到唯一的文件名 + unique_audio_filename = "./audio/"+str(uuid.uuid4()) + '.mp3' # 保存为不同的文件名以避免访问冲突 + asyncio.run(generate_audio_from_text(model_response, unique_audio_filename)) + + insert_data(baidu_result, model_response,unique_audio_filename)# 插入数据库 + # 7. 播放生成的语音 + play_mp3(unique_audio_filename) + + # 8. 提示用户继续对话或退出 + user_input = input('继续对话或输入"退出"退出: ') + if user_input == '退出': + break + + +if __name__ == "__main__": + main() + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2754521 Binary files /dev/null and b/requirements.txt differ diff --git a/test.py b/test.py new file mode 100644 index 0000000..fedcfe3 --- /dev/null +++ b/test.py @@ -0,0 +1,11 @@ +from ollama import Client +def get_completion(prompt, model="solar"): + client = Client(host='http://8.130.118.164:11434/') + response = client.chat(model, messages=[ + { + 'role': 'user', + 'content': prompt, + }, + ]) + return response['message']['content'] +print(get_completion("你好,我是小明。")) \ No newline at end of file diff --git a/user_audio.wav b/user_audio.wav new file mode 100644 index 0000000..f45dec7 Binary files /dev/null and b/user_audio.wav differ