formatting code

This commit is contained in:
lanvent
2023-04-17 01:00:08 +08:00
parent 3b8972ce1f
commit 8f72e8c3e6
92 changed files with 1850 additions and 1188 deletions

View File

@@ -1,9 +1,12 @@
import shutil
import wave
import pysilk
from pydub import AudioSegment
sil_supports=[8000, 12000, 16000, 24000, 32000, 44100, 48000] # slk转wav时支持的采样率
sil_supports = [8000, 12000, 16000, 24000, 32000, 44100, 48000] # slk转wav时支持的采样率
def find_closest_sil_supports(sample_rate):
"""
找到最接近的支持的采样率
@@ -19,6 +22,7 @@ def find_closest_sil_supports(sample_rate):
mindiff = diff
return closest
def get_pcm_from_wav(wav_path):
"""
从 wav 文件中读取 pcm
@@ -29,31 +33,42 @@ def get_pcm_from_wav(wav_path):
wav = wave.open(wav_path, "rb")
return wav.readframes(wav.getnframes())
def any_to_wav(any_path, wav_path):
"""
把任意格式转成wav文件
"""
if any_path.endswith('.wav'):
if any_path.endswith(".wav"):
shutil.copy2(any_path, wav_path)
return
if any_path.endswith('.sil') or any_path.endswith('.silk') or any_path.endswith('.slk'):
if (
any_path.endswith(".sil")
or any_path.endswith(".silk")
or any_path.endswith(".slk")
):
return sil_to_wav(any_path, wav_path)
audio = AudioSegment.from_file(any_path)
audio.export(wav_path, format="wav")
def any_to_sil(any_path, sil_path):
"""
把任意格式转成sil文件
"""
if any_path.endswith('.sil') or any_path.endswith('.silk') or any_path.endswith('.slk'):
if (
any_path.endswith(".sil")
or any_path.endswith(".silk")
or any_path.endswith(".slk")
):
shutil.copy2(any_path, sil_path)
return 10000
if any_path.endswith('.wav'):
if any_path.endswith(".wav"):
return pcm_to_sil(any_path, sil_path)
if any_path.endswith('.mp3'):
if any_path.endswith(".mp3"):
return mp3_to_sil(any_path, sil_path)
raise NotImplementedError("Not support file type: {}".format(any_path))
def mp3_to_wav(mp3_path, wav_path):
"""
把mp3格式转成pcm文件
@@ -61,6 +76,7 @@ def mp3_to_wav(mp3_path, wav_path):
audio = AudioSegment.from_mp3(mp3_path)
audio.export(wav_path, format="wav")
def pcm_to_sil(pcm_path, silk_path):
"""
wav 文件转成 silk
@@ -72,12 +88,12 @@ def pcm_to_sil(pcm_path, silk_path):
pcm_s16 = audio.set_sample_width(2)
pcm_s16 = pcm_s16.set_frame_rate(rate)
wav_data = pcm_s16.raw_data
silk_data = pysilk.encode(
wav_data, data_rate=rate, sample_rate=rate)
silk_data = pysilk.encode(wav_data, data_rate=rate, sample_rate=rate)
with open(silk_path, "wb") as f:
f.write(silk_data)
return audio.duration_seconds * 1000
def mp3_to_sil(mp3_path, silk_path):
"""
mp3 文件转成 silk
@@ -95,6 +111,7 @@ def mp3_to_sil(mp3_path, silk_path):
f.write(silk_data)
return audio.duration_seconds * 1000
def sil_to_wav(silk_path, wav_path, rate: int = 24000):
"""
silk 文件转 wav

View File

@@ -1,16 +1,18 @@
"""
azure voice service
"""
import json
import os
import time
import azure.cognitiveservices.speech as speechsdk
from bridge.reply import Reply, ReplyType
from common.log import logger
from common.tmp_dir import TmpDir
from voice.voice import Voice
from config import conf
from voice.voice import Voice
"""
Azure voice
主目录设置文件中需填写azure_voice_api_key和azure_voice_region
@@ -19,50 +21,68 @@ Azure voice
"""
class AzureVoice(Voice):
class AzureVoice(Voice):
def __init__(self):
try:
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
config = None
if not os.path.exists(config_path): #如果没有配置文件,创建本地配置文件
config = { "speech_synthesis_voice_name": "zh-CN-XiaoxiaoNeural", "speech_recognition_language": "zh-CN"}
if not os.path.exists(config_path): # 如果没有配置文件,创建本地配置文件
config = {
"speech_synthesis_voice_name": "zh-CN-XiaoxiaoNeural",
"speech_recognition_language": "zh-CN",
}
with open(config_path, "w") as fw:
json.dump(config, fw, indent=4)
else:
with open(config_path, "r") as fr:
config = json.load(fr)
self.api_key = conf().get('azure_voice_api_key')
self.api_region = conf().get('azure_voice_region')
self.speech_config = speechsdk.SpeechConfig(subscription=self.api_key, region=self.api_region)
self.speech_config.speech_synthesis_voice_name = config["speech_synthesis_voice_name"]
self.speech_config.speech_recognition_language = config["speech_recognition_language"]
self.api_key = conf().get("azure_voice_api_key")
self.api_region = conf().get("azure_voice_region")
self.speech_config = speechsdk.SpeechConfig(
subscription=self.api_key, region=self.api_region
)
self.speech_config.speech_synthesis_voice_name = config[
"speech_synthesis_voice_name"
]
self.speech_config.speech_recognition_language = config[
"speech_recognition_language"
]
except Exception as e:
logger.warn("AzureVoice init failed: %s, ignore " % e)
def voiceToText(self, voice_file):
audio_config = speechsdk.AudioConfig(filename=voice_file)
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=self.speech_config, audio_config=audio_config)
speech_recognizer = speechsdk.SpeechRecognizer(
speech_config=self.speech_config, audio_config=audio_config
)
result = speech_recognizer.recognize_once()
if result.reason == speechsdk.ResultReason.RecognizedSpeech:
logger.info('[Azure] voiceToText voice file name={} text={}'.format(voice_file, result.text))
logger.info(
"[Azure] voiceToText voice file name={} text={}".format(
voice_file, result.text
)
)
reply = Reply(ReplyType.TEXT, result.text)
else:
logger.error('[Azure] voiceToText error, result={}'.format(result))
logger.error("[Azure] voiceToText error, result={}".format(result))
reply = Reply(ReplyType.ERROR, "抱歉,语音识别失败")
return reply
def textToVoice(self, text):
fileName = TmpDir().path() + 'reply-' + str(int(time.time())) + '.wav'
fileName = TmpDir().path() + "reply-" + str(int(time.time())) + ".wav"
audio_config = speechsdk.AudioConfig(filename=fileName)
speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=self.speech_config, audio_config=audio_config)
speech_synthesizer = speechsdk.SpeechSynthesizer(
speech_config=self.speech_config, audio_config=audio_config
)
result = speech_synthesizer.speak_text(text)
if result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted:
logger.info(
'[Azure] textToVoice text={} voice file name={}'.format(text, fileName))
"[Azure] textToVoice text={} voice file name={}".format(text, fileName)
)
reply = Reply(ReplyType.VOICE, fileName)
else:
logger.error('[Azure] textToVoice error, result={}'.format(result))
logger.error("[Azure] textToVoice error, result={}".format(result))
reply = Reply(ReplyType.ERROR, "抱歉,语音合成失败")
return reply

View File

@@ -1,4 +1,4 @@
{
"speech_synthesis_voice_name": "zh-CN-XiaoxiaoNeural",
"speech_recognition_language": "zh-CN"
}
"speech_synthesis_voice_name": "zh-CN-XiaoxiaoNeural",
"speech_recognition_language": "zh-CN"
}

View File

@@ -29,7 +29,7 @@ dev_pid 必填 语言选择,填写语言对应的dev_pid值
2、对于def textToVoice(self, text)函数中调用的百度语音合成API,中接口调用synthesis参数在本目录下的`config.json`文件中进行配置。
参数 可需 描述
tex 必填 合成的文本使用UTF-8编码请注意文本长度必须小于1024字节
tex 必填 合成的文本使用UTF-8编码请注意文本长度必须小于1024字节
lan 必填 固定值zh。语言选择,目前只有中英文混合模式填写固定值zh
spd 选填 语速取值0-15默认为5中语速
pit 选填 音调取值0-15默认为5中语调
@@ -40,14 +40,14 @@ aue 选填 3为mp3格式(默认) 4为pcm-16k5为pcm-8k6为wav
关于per参数的说明注意您购买的哪个音库就填写哪个音库的参数否则会报错。如果您购买的是基础音库那么per参数只能填写0到4如果您购买的是精品音库那么per参数只能填写50035118106,110,111,103,5其他的都会报错。
### 配置文件
将文件夹中`config.json.template`复制为`config.json`
``` json
{
"lang": "zh",
"lang": "zh",
"ctp": 1,
"spd": 5,
"spd": 5,
"pit": 5,
"vol": 5,
"per": 0

View File

@@ -1,17 +1,19 @@
"""
baidu voice service
"""
import json
import os
import time
from aip import AipSpeech
from bridge.reply import Reply, ReplyType
from common.log import logger
from common.tmp_dir import TmpDir
from voice.voice import Voice
from voice.audio_convert import get_pcm_from_wav
from config import conf
from voice.audio_convert import get_pcm_from_wav
from voice.voice import Voice
"""
百度的语音识别API.
dev_pid:
@@ -28,40 +30,37 @@ from config import conf
class BaiduVoice(Voice):
def __init__(self):
try:
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
bconf = None
if not os.path.exists(config_path): #如果没有配置文件,创建本地配置文件
bconf = { "lang": "zh", "ctp": 1, "spd": 5,
"pit": 5, "vol": 5, "per": 0}
if not os.path.exists(config_path): # 如果没有配置文件,创建本地配置文件
bconf = {"lang": "zh", "ctp": 1, "spd": 5, "pit": 5, "vol": 5, "per": 0}
with open(config_path, "w") as fw:
json.dump(bconf, fw, indent=4)
else:
with open(config_path, "r") as fr:
bconf = json.load(fr)
self.app_id = conf().get('baidu_app_id')
self.api_key = conf().get('baidu_api_key')
self.secret_key = conf().get('baidu_secret_key')
self.dev_id = conf().get('baidu_dev_pid')
self.app_id = conf().get("baidu_app_id")
self.api_key = conf().get("baidu_api_key")
self.secret_key = conf().get("baidu_secret_key")
self.dev_id = conf().get("baidu_dev_pid")
self.lang = bconf["lang"]
self.ctp = bconf["ctp"]
self.spd = bconf["spd"]
self.pit = bconf["pit"]
self.vol = bconf["vol"]
self.per = bconf["per"]
self.client = AipSpeech(self.app_id, self.api_key, self.secret_key)
except Exception as e:
logger.warn("BaiduVoice init failed: %s, ignore " % e)
def voiceToText(self, voice_file):
# 识别本地文件
logger.debug('[Baidu] voice file name={}'.format(voice_file))
logger.debug("[Baidu] voice file name={}".format(voice_file))
pcm = get_pcm_from_wav(voice_file)
res = self.client.asr(pcm, "pcm", 16000, {"dev_pid": self.dev_id})
if res["err_no"] == 0:
@@ -72,21 +71,25 @@ class BaiduVoice(Voice):
logger.info("百度语音识别出错了: {}".format(res["err_msg"]))
if res["err_msg"] == "request pv too much":
logger.info(" 出现这个原因很可能是你的百度语音服务调用量超出限制,或未开通付费")
reply = Reply(ReplyType.ERROR,
"百度语音识别出错了;{0}".format(res["err_msg"]))
reply = Reply(ReplyType.ERROR, "百度语音识别出错了;{0}".format(res["err_msg"]))
return reply
def textToVoice(self, text):
result = self.client.synthesis(text, self.lang, self.ctp, {
'spd': self.spd, 'pit': self.pit, 'vol': self.vol, 'per': self.per})
result = self.client.synthesis(
text,
self.lang,
self.ctp,
{"spd": self.spd, "pit": self.pit, "vol": self.vol, "per": self.per},
)
if not isinstance(result, dict):
fileName = TmpDir().path() + 'reply-' + str(int(time.time())) + '.mp3'
with open(fileName, 'wb') as f:
fileName = TmpDir().path() + "reply-" + str(int(time.time())) + ".mp3"
with open(fileName, "wb") as f:
f.write(result)
logger.info(
'[Baidu] textToVoice text={} voice file name={}'.format(text, fileName))
"[Baidu] textToVoice text={} voice file name={}".format(text, fileName)
)
reply = Reply(ReplyType.VOICE, fileName)
else:
logger.error('[Baidu] textToVoice error={}'.format(result))
logger.error("[Baidu] textToVoice error={}".format(result))
reply = Reply(ReplyType.ERROR, "抱歉,语音合成失败")
return reply

View File

@@ -1,8 +1,8 @@
{
"lang": "zh",
"ctp": 1,
"spd": 5,
"pit": 5,
"vol": 5,
"per": 0
}
{
"lang": "zh",
"ctp": 1,
"spd": 5,
"pit": 5,
"vol": 5,
"per": 0
}

View File

@@ -1,11 +1,12 @@
"""
google voice service
"""
import time
import speech_recognition
from gtts import gTTS
from bridge.reply import Reply, ReplyType
from common.log import logger
from common.tmp_dir import TmpDir
@@ -22,9 +23,12 @@ class GoogleVoice(Voice):
with speech_recognition.AudioFile(voice_file) as source:
audio = self.recognizer.record(source)
try:
text = self.recognizer.recognize_google(audio, language='zh-CN')
text = self.recognizer.recognize_google(audio, language="zh-CN")
logger.info(
'[Google] voiceToText text={} voice file name={}'.format(text, voice_file))
"[Google] voiceToText text={} voice file name={}".format(
text, voice_file
)
)
reply = Reply(ReplyType.TEXT, text)
except speech_recognition.UnknownValueError:
reply = Reply(ReplyType.ERROR, "抱歉,我听不懂")
@@ -32,13 +36,15 @@ class GoogleVoice(Voice):
reply = Reply(ReplyType.ERROR, "抱歉,无法连接到 Google 语音识别服务;{0}".format(e))
finally:
return reply
def textToVoice(self, text):
try:
mp3File = TmpDir().path() + 'reply-' + str(int(time.time())) + '.mp3'
tts = gTTS(text=text, lang='zh')
tts.save(mp3File)
mp3File = TmpDir().path() + "reply-" + str(int(time.time())) + ".mp3"
tts = gTTS(text=text, lang="zh")
tts.save(mp3File)
logger.info(
'[Google] textToVoice text={} voice file name={}'.format(text, mp3File))
"[Google] textToVoice text={} voice file name={}".format(text, mp3File)
)
reply = Reply(ReplyType.VOICE, mp3File)
except Exception as e:
reply = Reply(ReplyType.ERROR, str(e))

View File

@@ -1,29 +1,32 @@
"""
google voice service
"""
import json
import openai
from bridge.reply import Reply, ReplyType
from config import conf
from common.log import logger
from config import conf
from voice.voice import Voice
class OpenaiVoice(Voice):
def __init__(self):
openai.api_key = conf().get('open_ai_api_key')
openai.api_key = conf().get("open_ai_api_key")
def voiceToText(self, voice_file):
logger.debug(
'[Openai] voice file name={}'.format(voice_file))
logger.debug("[Openai] voice file name={}".format(voice_file))
try:
file = open(voice_file, "rb")
result = openai.Audio.transcribe("whisper-1", file)
text = result["text"]
reply = Reply(ReplyType.TEXT, text)
logger.info(
'[Openai] voiceToText text={} voice file name={}'.format(text, voice_file))
"[Openai] voiceToText text={} voice file name={}".format(
text, voice_file
)
)
except Exception as e:
reply = Reply(ReplyType.ERROR, str(e))
finally:

View File

@@ -1,10 +1,11 @@
"""
pytts voice service (offline)
"""
import time
import pyttsx3
from bridge.reply import Reply, ReplyType
from common.log import logger
from common.tmp_dir import TmpDir
@@ -16,20 +17,21 @@ class PyttsVoice(Voice):
def __init__(self):
# 语速
self.engine.setProperty('rate', 125)
self.engine.setProperty("rate", 125)
# 音量
self.engine.setProperty('volume', 1.0)
for voice in self.engine.getProperty('voices'):
self.engine.setProperty("volume", 1.0)
for voice in self.engine.getProperty("voices"):
if "Chinese" in voice.name:
self.engine.setProperty('voice', voice.id)
self.engine.setProperty("voice", voice.id)
def textToVoice(self, text):
try:
wavFile = TmpDir().path() + 'reply-' + str(int(time.time())) + '.wav'
wavFile = TmpDir().path() + "reply-" + str(int(time.time())) + ".wav"
self.engine.save_to_file(text, wavFile)
self.engine.runAndWait()
logger.info(
'[Pytts] textToVoice text={} voice file name={}'.format(text, wavFile))
"[Pytts] textToVoice text={} voice file name={}".format(text, wavFile)
)
reply = Reply(ReplyType.VOICE, wavFile)
except Exception as e:
reply = Reply(ReplyType.ERROR, str(e))

View File

@@ -2,6 +2,7 @@
Voice service abstract class
"""
class Voice(object):
def voiceToText(self, voice_file):
"""
@@ -13,4 +14,4 @@ class Voice(object):
"""
Send text to voice service and get voice
"""
raise NotImplementedError
raise NotImplementedError

View File

@@ -2,25 +2,31 @@
voice factory
"""
def create_voice(voice_type):
"""
create a voice instance
:param voice_type: voice type code
:return: voice instance
"""
if voice_type == 'baidu':
if voice_type == "baidu":
from voice.baidu.baidu_voice import BaiduVoice
return BaiduVoice()
elif voice_type == 'google':
elif voice_type == "google":
from voice.google.google_voice import GoogleVoice
return GoogleVoice()
elif voice_type == 'openai':
elif voice_type == "openai":
from voice.openai.openai_voice import OpenaiVoice
return OpenaiVoice()
elif voice_type == 'pytts':
elif voice_type == "pytts":
from voice.pytts.pytts_voice import PyttsVoice
return PyttsVoice()
elif voice_type == 'azure':
elif voice_type == "azure":
from voice.azure.azure_voice import AzureVoice
return AzureVoice()
raise RuntimeError