【Python + Flask + Web錄音 + baidu-aip + 圖靈機器人 = 人機對話】
阿新 • • 發佈:2019-01-13
安裝baidu-aip:pip install baidu-aip
百度雲網址:https://login.bce.baidu.com
百度語音合成文件:https://ai.baidu.com/docs#/TTS-Online-Python-SDK/top
百度語音識別文件:https://ai.baidu.com/docs#/ASR-Online-Python-SDK/top
百度自然語言處理基礎技術文件:https://ai.baidu.com/docs#/NLP-Python-SDK/6dfe1b04
Mac系統安裝ffmpeg文件:https://blog.csdn.net/stonenotes/article/details/68958332
圖靈機器人官網:http://www.tuling123.com/
圖靈機器人介面說明:https://www.kancloud.cn/turing/www-tuling123-com/718227
後端程式碼
# ⚠️這是在macOS系統上(版本10.14)寫的
# pip install baidu-aip
# 百度雲:https://login.bce.baidu.com
# 百度語音合成文件:https://ai.baidu.com/docs#/TTS-Online-Python-SDK/top
# 百度語音識別文件:https://ai.baidu.com/docs#/ASR-Online-Python-SDK/top
# 百度自然語言處理基礎技術文件:https://ai.baidu.com/docs#/NLP-Python-SDK/6dfe1b04
# Mac系統安裝ffmpeg文件:https://blog.csdn.net/stonenotes/article/details/68958332
# 圖靈機器人官網:http://www.tuling123.com/
# 圖靈機器人介面說明:https://www.kancloud.cn/turing/www-tuling123-com/718227
import os
import time
import uuid
import requests
import subprocess
from aip import AipSpeech, AipNlp # baidu-aip
from flask import Flask, request, render_template, send_file, jsonify
# =======================下面是百度語音合成/識別=======================
class VoiceTextConversion(object):
# 這3個私有屬性是本人的百度應用的appID等資訊
__APP_ID = '15225447'
__API_KEY = 's5m43BMMEGGPaFGxeX3SsY7m'
__SECRET_KEY = 'Lca9FEGpWNZW6yd8WWAHAyCyLovmi6rb'
text_to_voice_error_info = {500: '不支援的輸入', 501: '輸入的引數不正確', 502: 'token驗證失敗', 503: '合成後端錯誤'}
def __init__(self, app_id=None, api_key=None, secret_key=None,
connect_timeout=None, socket_timeout=None, is_external_called=False):
"""
:param APP_ID: 請使用你自己的百度應用
:param API_KEY: 請使用你自己的百度應用
:param SECRET_KEY: 請使用你自己的百度應用
:param connectTimeout: 建立連線的超時時間(單位:毫秒,1000毫秒=1秒)預設60秒
:param socketTimeout: 通過開啟的連線傳輸資料的超時時間(單位:毫秒)預設60秒
:param is_external_called: 當前物件是否為其它應用呼叫
"""
self.__is_external_called = is_external_called
if requests.get('http://www.baidu.com').status_code is not 200:
exit(self.__custom_print("沒網你玩個錘子啊!"))
# 如果你有自己的百度語音應用,請使用你自己的百度語音應用,如果你沒有,請去註冊(免費):
self.client = AipSpeech(app_id, api_key, secret_key) if app_id \
else AipSpeech(self.__APP_ID, self.__API_KEY, self.__SECRET_KEY)
# 可設定連結/傳輸的超時時間:
connect_timeout and self.client.setConnectionTimeoutInMillis(connect_timeout)
socket_timeout and self.client.setSocketTimeoutInMillis(socket_timeout)
def text_to_voice(self, text, filepath='語音檔案', lang='zh', ctp=1, per=3, pid=5, spd=5, vol=5):
"""
文字 -> 語音
:param text: 要轉換的文字
:param filename: 儲存路徑,自動新增 .mp3 字尾
:param lang: 語言,預設中文
:param ctp: ?
:param per: 發音人,0為女聲;1為男聲;3為情感合成-度逍遙;4為情感合成-度牙牙
:param pid: 音調,取值0-9,預設為5 中語速
:param spd: 語速,取值0-9,預設為5 中語速
:param vol: 音量,取值0-15,預設為5 中音量
"""
voice_format = {'per': per, 'pid': pid, 'spd': spd, 'vol': vol}
# 開始轉換:
# 轉換成功時返回bytes語音資料;轉換失敗時返回錯誤資訊字典;
result = self.client.synthesis(text, lang, ctp, voice_format)
# 如果轉換成功,將儲存至磁碟:
if isinstance(result, bytes):
with open(f'{filepath}.mp3', 'wb') as f:
f.write(result)
self.__custom_print(f'合成語音成功:{filepath}', color=32, is_error=False)
# 如果出現錯誤:
else:
err_code = result.get('err_no')
err_info = self.text_to_voice_error_info[err_code]
self.__custom_print(err_info)
def voice_to_text(self, filepath=None, format='pcm', dev_pid=1536):
"""
語音 -> 文字
使用前請安裝ffmpeg,用於將其它格式的語音檔案轉換為 .pcm 格式的語音檔案
:param filepath: 語音檔案路徑
:param format: 語音檔案格式(字尾),.pcm 或 .wav 或 .amr 不區分大小寫。推薦使用 .pcm 檔案
:param dev_pid: 語言,1536普通話(支援簡單的英文識別),1537普通話,1737英語,1637粵語,1837四川話,1936普通話遠場(距離識別)
:return:
"""
# 如果輸入的檔案路徑有誤
if not os.path.isfile(filepath):
return self.__custom_print('語音檔案路徑錯誤')
# 將語音檔案轉換為 format 格式,這一步驟將呼叫系統命令ffmpeg並會生成新檔案,需要安裝該命令(詳見頂部:Mac系統安裝ffmpeg文件連結)
cmd = f'ffmpeg -y -i {filepath} -acodec pcm_s16le -f s16le -ac 1 -ar 16000 {filepath}.{format}'
# ffmpeg命令執行過程中輸出的資訊被儲存在stderr中
r = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
# 命令執行成功,將會生成 format 格式的語音檔案
# 語音檔案生成的很慢,這個很奇怪的問題,先這麼解決吧:
for i in range(999):
time.sleep(0.1)
if os.path.isfile(f'{filepath}.{format}'):
break
# 讀取轉換後的語言檔案:
with open(f'{filepath}.{format}', 'rb') as fp:
voice_content = fp.read()
# 讀取後,刪除生成的 format 格式的檔案
os.remove(f'{filepath}.{format}')
# 開始轉換:
result = self.client.asr(voice_content, format, 16000, {'dev_pid': dev_pid})
# 如果轉換成功,直接輸出文字:
if not result.get('err_no'):
content = result.get('result')[0]
if self.__is_external_called:
return content
self.__custom_print(content, 34, 1, is_error=False)
# 如果轉換錯誤
else:
err_code = result.get('err_no')
self.__custom_print(f'錯誤程式碼:{err_code},詳見頂部:百度語音識別文件')
def short_text_compare(self, text1, text2):
"""
短文字相似度,詳見頂部:百度自然語言處理基礎技術文件
超過72% 極度相似
超過60% 比較相似
超過60% 可能相似
低於50% 不相似
越低越不相似
:param text1: 第一個短文字
:param text2: 第二個短文字
:return: 相似百分比
"""
nlp_client = AipNlp(self.__APP_ID, self.__API_KEY, self.__SECRET_KEY)
# 傳送短文字
result = nlp_client.simnet(text1, text2)
# 獲取相似度
score = result.get('score')
# 轉換百分比
score = f'{round(score * 100)}%'
self.__custom_print(f'相似度:{score}', 35, 1, is_error=False)
def __custom_print(self, data=None, color=31, font=0, is_error=True):
"""
自定義列印資訊的字型及顏色
:param color: 列印顏色,預設紅色,32綠色,34藍色,35紫紅色,更多列印顏色請百度
:param font: 列印字型,預設正常字型,1為黑體(加粗)
:param is_error: 是否列印錯誤資訊
"""
if self.__is_external_called:
return
is_error and self.__custom_print('錯誤!!!', font=1, is_error=False)
print_info = f'\033[{font};{color};0m{data}\033[0m'
print(print_info)
# ---------開始使用---------
# if __name__ == '__main__':
# # 我們先例項化一個物件
# obj = VoiceTextConversion()
#
# # 文字 -> 語音
# text = '人活著就要有夢想,不然跟鹹魚又有什麼區別呢?'
# obj.text_to_voice(text, filepath='語音檔案')
#
# # 語音 -> 文字
# obj.voice_to_text('語音檔案.mp3')
#
# # 短文字相似度
# text1, text2 = '你叫什麼名字', '你的名字叫什麼'
# obj.short_text_compare(text1, text2)
# =======================下面是圖靈機器人=======================
class TuringRobot(object): # 玩轉圖靈機器人,詳見頂部:圖靈機器人介面說明
# 機器人標示,若果你有自己的機器人,請使用你自己的機器人
__API_KEY = '8c512c52ac324a3eb4c40639d43bd507' # 免費版 100次/天
# 圖靈機器人接入地址
URL = 'http://openapi.tuling123.com/openapi/api/v2'
# 請求引數
POST_DATA = {
"reqType": 0, # 輸入型別:0-文字(預設) 1-圖片 2-音訊
"perception": { # 輸入資訊
"inputText": { # 文字資訊
"text": "" # 直接輸入文字,1-128個字元
},
},
"userInfo": { # 使用者引數
"apiKey": "", # 機器人標示
"userId": "", # 使用者唯一標示,用於請求上下文判斷是否為同一個使用者,不可重複
}
}
@classmethod
def run(cls, text='你的名字叫什麼', user_id='user01', api_key=None, *args, **kwargs):
"""
開始玩轉圖靈機器人
:param text: 你對機器人說的話
:param api_key: 圖靈機器人標示,如果你有自己的機器人,請使用你自己的機器人
:param user_id: 使用者唯一ID,用於請求上下文判斷是否為同一個使用者,多個使用者使用時不可重複
:return: 直接返回機器人回的話
"""
cls.POST_DATA.get('userInfo')['apiKey'] = api_key or cls.__API_KEY
cls.POST_DATA.get('userInfo')['userId'] = user_id
cls.POST_DATA.get('perception')['inputText']['text'] = text
result = requests.post(cls.URL, json=cls.POST_DATA).json()
return result.get('results')[0]['values']['text']
# =======================Flase App=======================
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('web.html')
@app.route('/toy_uploader', methods=['GET', 'POST'])
def toy_uploader():
uuid4 = uuid.uuid4()
vct = VoiceTextConversion(is_external_called=True)
# 確保檔案唯一,錄音檔案為 .wav 格式
filename = f'{uuid4}.wav'
# 儲存語音檔案
request.files['record'].save(filename)
# 開始語音轉文字
content = vct.voice_to_text(filename)
os.remove(filename)
# 與圖靈機器人對話
result = TuringRobot.run(text=content, user_id='user01')
# 開始文字轉語音
vct.text_to_voice(text=result, filepath=uuid4)
# 返回給前端語音檔名
return jsonify({'filename': f'{uuid4}.mp3'})
@app.route('/get_audio/<filename>')
def get_audio(filename):
# 前端ajax通過檔名來獲取語音檔案
ret = send_file(filename)
os.remove(filename)
return ret
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
前端程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>人機對話</title>
</head>
<body>
<audio src="" controls autoplay id="player"></audio>
<div>
<button onclick="start_reco()" style="background-color: red">錄製語音</button>
</div>
<div>
<button onclick="stop_reco()" style="background-color: green">傳送語音</button>
</div>
</body>
<script type="text/javascript" src=https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js></script>
<script type="text/javascript" src="/static/Recorder.js"></script>
<script type="text/javascript">
var serv = "http://127.0.0.1:5000";
// var get_music = "http://192.168.1.102:9527/get_music/";
// var get_chat = "http://192.168.1.102:9527/get_chat/";
var reco = null;
var audio_context = new AudioContext();
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.getUserMedia({audio: true}, create_stream, function (err) {
console.log(err)
});
function create_stream(user_media) {
var stream_input = audio_context.createMediaStreamSource(user_media);
reco = new Recorder(stream_input);
}
function start_reco() {
reco.record();
}
function stop_reco() {
reco.stop();
get_audio();
reco.clear();
}
function get_audio() {
reco.exportWAV(function (wav_file) {
// wav_file = Blob物件 file物件
// ws.send(wav_file);
var formdata = new FormData();
formdata.append("record", wav_file);
// formdata.append("sender", toy_id);
// formdata.append("to_user", document.getElementById("from_user").innerText);
$.ajax({
url: serv + "/toy_uploader", // ⚠️
type: 'post',
processData: false,
contentType: false,
data: formdata,
dataType: 'json',
success: function (data) {
console.log(data.A);
// console.log(data);
document.getElementById("player").src =
"http://127.0.0.1:5000/get_audio/" + data.filename; // ⚠️
}
})
})
}
</script>
</html>
前端依賴外掛:
Recorder.js:https://download.csdn.net/download/qq_41964425/10867911
uuid.js:https://download.csdn.net/download/qq_41964425/10867907
使用火狐瀏覽器錄音.