)phT+#iR)o59^kc6ZL9F3h|g@9AQm
z{$L(YZl}oLj@(OJD@>L_$r2KcucmZbFvueMNE=S9axVSNL#O
z27Zs+R>;vUd{Yo~HICToSN22IajprXEXYO%q{Pg6
z3uI1N5T;))qVEE>IQ~rd_e=`+hnd9{0Kgd&u7zk0GA*wKo_i{XfE7?a+u^Il=}hX9
z$y+nKItLA}+RaQVlX^7|--ylmnOF0BiVm=2c8KlaheJ6iotvIOYcUqUvDCHb8L#6K
zlWi`#W}{6>erDBd2XrjIAu^mt#yIQH-dMg5@n^x}OgOZEOmV!n-U^Gg>Gg&USLU<`
zfzK-f_~(1@WKwwfmjL16_UFk=Sp7ej4+5wCy&FzDlX^&AgT|f+U>t8AW`sm|hq)z4
z!~TguZY4|ucvAa1xH8wd7
zB5f5+T7Y+?c|LWc*aovXEgXuH%gYxp)KP;`sxMk(*oZ?uR0sA(R$jWiyt24ZpTBT^
zacQN#^5P4NOGe_#@^bz1OBakJFyec_?YNQLXV*xAl?7mr_3DGqaU%_L>x>3|-8Z?B
z79v6Xrlw;+`W5&PHmDJIDJ5-cB*9J@uLIjgvOg8LdlZm{K-q4vO4-E#GTH?&vMO9U
z_#srvYgt4lRl(;g<$g96mjRvj-{CKmL|Or~O6sbnXkRIs_9gVcg9@K9QQ>VE03?0GokA#_8eLr@mzhM5tfTNhI<|02B5~>LPG#o%u!2ZG$QKDWao>i~22^>n8fFzdf^)p*d+-DLOma#&6
z7e78CrS0dum}&~25-uVnyMHlM9qf$B=hyKjd@xEd3WLEGm*-2ehiU
zJCedK!N$0{j`$Bt5P__zMA>&{bUrT1rpo
X&*_?;)isPX{dZ6vRz_g9_OJf}l&P2+
literal 0
HcmV?d00001
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..fe15471
--- /dev/null
+++ b/main.py
@@ -0,0 +1,207 @@
+import sys
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QIcon
+from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QComboBox, \
+ QSpinBox, QTextEdit, QPushButton, QVBoxLayout, QWidget, QScrollArea, \
+ QPlainTextEdit, QHBoxLayout, QLineEdit, QFileDialog, QMessageBox
+from qt_material import apply_stylesheet
+
+from tts import *
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+
+ '''
+ 设置状态栏
+ '''
+ # 创建状态栏
+ self.statusBar = self.statusBar()
+ # 设置版本号和作者信息
+ version = "2023.V1.0" # 替换为实际的版本号
+ author = "作者:鲸鱼工作室" # 替换为实际的作者名字
+ self.statusBar.showMessage(f"版本号:{version}")
+ # 创建作者信息标签
+ author_label = QLabel(author)
+ author_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # 设置标签靠右对齐
+ # 将作者信息标签添加为状态栏的永久部件
+ self.statusBar.addPermanentWidget(author_label)
+
+ # 初始值
+ self.save_path = ''
+
+ self.setWindowTitle("语音合成助手")
+ self.setGeometry(100, 100, 400, 600) # 调整窗口大小为800x600
+
+ # 创建文本输入框
+ self.text_edit = QTextEdit(self)
+ self.text_edit.setGeometry(40, 40, 720, 200) # 调整文本输入框的尺寸和位置
+ self.text_edit.setPlaceholderText("请输入或粘贴需要合成的文本")
+
+ # 创建名称下拉框
+ self.name_label = QLabel("支持声音列表:", self)
+ self.name_label.setGeometry(40, 280, 80, 30) # 调整名称标签的位置
+ self.name_combo = QComboBox(self)
+ self.name_combo.setGeometry(140, 280, 600, 30) # 调整名称下拉框的尺寸和位置
+ self.name_combo.addItem("01-女:zh-HK-HiuGaaiNeural")
+ self.name_combo.addItem("02-女:zh-HK-HiuMaanNeural")
+ self.name_combo.addItem("03-男:zh-HK-WanLungNeural")
+ self.name_combo.addItem("04-女:zh-CN-XiaoxiaoNeural")
+ self.name_combo.addItem("05-女:zh-CN-XiaoyiNeural")
+ self.name_combo.addItem("06-男:zh-CN-YunjianNeural")
+ self.name_combo.addItem("07-男:zh-CN-YunxiNeural")
+ self.name_combo.addItem("08-男:zh-CN-YunxiaNeural")
+ self.name_combo.addItem("09-男:zh-CN-YunyangNeural")
+ self.name_combo.addItem("10-女:zh-CN-liaoning-XiaobeiNeural")
+ self.name_combo.addItem("11-女:zh-TW-HsiaoChenNeural")
+ self.name_combo.addItem("12-男:zh-TW-YunJheNeural")
+ self.name_combo.addItem("13-女:zh-TW-HsiaoYuNeural")
+ self.name_combo.addItem("14-女:h-CN-shaanxi-XiaoniNeural")
+ # 添加更多的名称选项...
+
+ # 创建语速微调器
+ self.rate_label = QLabel("速度:", self)
+ self.rate_label.setGeometry(40, 340, 80, 30) # 调整语速标签的位置
+ self.rate_spinbox = QSpinBox(self)
+ self.rate_spinbox.setGeometry(140, 340, 600, 30) # 调整语速微调器的尺寸和位置
+ self.rate_spinbox.setRange(0, 100)
+ self.rate_spinbox.setValue(0)
+
+ # 创建音调微调器
+ self.pitch_label = QLabel("语调:", self)
+ self.pitch_label.setGeometry(40, 400, 80, 30) # 调整音调标签的位置
+ self.pitch_spinbox = QSpinBox(self)
+ self.pitch_spinbox.setGeometry(140, 400, 600, 30) # 调整音调微调器的尺寸和位置
+ self.pitch_spinbox.setRange(0, 100)
+ self.pitch_spinbox.setValue(0)
+
+ # 创建选择路径
+ path_layout = QHBoxLayout()
+ self.save_path_label = QLabel("音频保存路径:", self)
+ self.select_path_button = QPushButton("选择路径", self)
+ self.select_path_button.clicked.connect(self.select_output_path)
+ # 将选择路径按钮和文件名输入框放在同一行
+ path_layout.addWidget(self.save_path_label)
+ path_layout.addWidget(self.select_path_button)
+
+ # 创建文件名输入框
+ self.filename_label = QLabel("音频文件名(无需加后缀名):", self)
+ self.filename_edit = QLineEdit(self)
+ self.filename_edit.setPlaceholderText("请输入音频文件名,如test")
+ # 将选择路径按钮和文件名输入框放在同一行
+ filename_layout = QHBoxLayout()
+ filename_layout.addWidget(self.filename_label)
+ filename_layout.addWidget(self.filename_edit)
+
+ # 创建状态标签
+ self.status_label = QLabel("", self)
+ self.status_label.setGeometry(40, 520, 700, 20) # 调整状态标签的尺寸和位置
+
+ # 创建控制台
+ self.console = QPlainTextEdit(self)
+ self.console.setGeometry(40, 500, 700, 80) # 调整控制台的尺寸和位置
+ self.console.setReadOnly(True) # 设置控制台为只读模式
+
+ # 创建生成按钮
+ self.generate_button = QPushButton("开始生成语音", self)
+ self.generate_button.clicked.connect(self.generate_speech)
+
+ # 创建垂直布局
+ layout = QVBoxLayout()
+ layout.addWidget(self.text_edit)
+ layout.addWidget(self.name_label)
+ layout.addWidget(self.name_combo)
+ layout.addWidget(self.rate_label)
+ layout.addWidget(self.rate_spinbox)
+ layout.addWidget(self.pitch_label)
+ layout.addWidget(self.pitch_spinbox)
+ layout.addLayout(path_layout) # 将标签和按钮放在同一行
+ layout.addLayout(filename_layout) # 将选择路径按钮和文件名输入框放在同一行
+ layout.addWidget(self.filename_edit)
+ layout.addWidget(self.generate_button)
+ layout.addWidget(self.console)
+ layout.addWidget(self.status_label)
+
+ # 创建主部件和滚动区域
+ main_widget = QWidget()
+ scroll_area = QScrollArea()
+ scroll_area.setWidget(main_widget)
+ scroll_area.setWidgetResizable(True)
+ main_widget.setLayout(layout)
+
+ # 设置主窗口的中心部件为滚动区域
+ self.setCentralWidget(scroll_area)
+
+ def generate_speech(self):
+ text = self.text_edit.toPlainText().strip()
+ name = self.name_combo.currentText().split(':')[-1]
+ rate = self.rate_spinbox.value()
+ pitch = self.pitch_spinbox.value()
+ filename = self.filename_edit.text()
+
+ if text == '' or filename == '' or self.save_path == '':
+ # 弹出警告
+ QMessageBox.warning(self, '警告', '请检查【文本】或【选择路径】或【文件名】是否为空')
+ else:
+ # 将文件名和输出路径组合以生成完整的文件路径
+ # 输出生成语音的相关信息到控制台
+ self.console.appendPlainText(f"文本: {text}")
+ self.console.appendPlainText(f"声音:'{name}':")
+ self.console.appendPlainText(f"速度: {rate}%")
+ self.console.appendPlainText(f"语调: {pitch}%")
+
+ # 调用生成语音的代码
+ self.console.appendPlainText(f"【{now_time()}】开始生成语音.... 请耐心等待....")
+ rate = f'{rate}%'
+ pitch = f'{pitch}%'
+ SSML_text = get_SSML(name=name, rate=rate, pitch=pitch, text=text)
+ output_path = self.save_path + '/' + filename
+ asyncio.get_event_loop().run_until_complete(mainSeq(SSML_text, output_path))
+ self.console.appendPlainText(f"【{now_time()}】语音生成成功!音频文件保存至:{output_path}.mp3")
+ self.console.appendPlainText("\n")
+
+ def select_output_path(self):
+ print('选择路径')
+ self.save_path = QFileDialog.getExistingDirectory(self, '选择保存路径', '.')
+ # 弹窗表示选择路径成功
+ QMessageBox.information(self, '提示', '已设置保存路径')
+ self.console.appendPlainText('音频保存路径为:{}'.format(self.save_path))
+
+
+if __name__ == '__main__':
+ # 创建应用程序实例
+ app = QApplication(sys.argv)
+
+ theme_list = ['dark_amber.xml',
+ 'dark_blue.xml',
+ 'dark_cyan.xml',
+ 'dark_lightgreen.xml',
+ 'dark_pink.xml',
+ 'dark_purple.xml',
+ 'dark_red.xml',
+ 'dark_teal.xml',
+ 'dark_yellow.xml',
+ 'light_amber.xml',
+ 'light_blue.xml',
+ 'light_cyan.xml',
+ 'light_cyan_500.xml',
+ 'light_lightgreen.xml',
+ 'light_pink.xml',
+ 'light_purple.xml',
+ 'light_red.xml',
+ 'light_teal.xml',
+ 'light_yellow.xml']
+ apply_stylesheet(app, theme='dark_cyan.xml')
+
+ # 创建主窗口
+ window = MainWindow()
+ window.show()
+
+ # 创建图标对象
+ icon = QIcon("voice.png") # 将 "path_to_icon_file.ico" 替换为实际的图标文件路径
+ # 设置应用程序的图标
+ app.setWindowIcon(icon)
+
+ # 运行应用程序
+ sys.exit(app.exec())
diff --git a/output_2023-12-09 10-37-43.mp3 b/output_2023-12-09 10-37-43.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..57a79f2e732b9c25d601f31fbf161fcc52310982
GIT binary patch
literal 13248
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
BY*_#R
literal 0
HcmV?d00001
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..573b879
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+requests==2.15.1
+websockets==10.2
diff --git a/tts.py b/tts.py
new file mode 100644
index 0000000..f73c224
--- /dev/null
+++ b/tts.py
@@ -0,0 +1,146 @@
+'''
+参考代码
+https://github.com/OS984/DiscordBotBackend/blob/3b06b8be39e4dbc07722b0afefeee4c18c136102/NeuralTTS.py
+https://github.com/rany2/edge-tts/blob/master/src/edge_tts/communicate.py
+'''
+
+import websockets
+import asyncio
+from datetime import datetime
+import time
+import re
+import uuid
+import argparse
+
+'''命令行参数解析'''
+
+
+def parseArgs():
+ parser = argparse.ArgumentParser(description='text2speech')
+ parser.add_argument('--output', dest='output', help='保存mp3文件的路径', type=str, required=False)
+ args = parser.parse_args()
+ return args
+
+
+# Fix the time to match Americanisms
+def hr_cr(hr):
+ corrected = (hr - 1) % 24
+ return str(corrected)
+
+
+# Add zeros in the right places i.e 22:1:5 -> 22:01:05
+def fr(input_string):
+ corr = ''
+ i = 2 - len(input_string)
+ while (i > 0):
+ corr += '0'
+ i -= 1
+ return corr + input_string
+
+
+# Generate X-Timestamp all correctly formatted
+def getXTime():
+ now = datetime.now()
+ return fr(str(now.year)) + '-' + fr(str(now.month)) + '-' + fr(str(now.day)) + 'T' + fr(hr_cr(int(now.hour))) + ':' + fr(
+ str(now.minute)) + ':' + fr(str(now.second)) + '.' + str(now.microsecond)[:3] + 'Z'
+
+
+# Async function for actually communicating with the websocket
+async def transferMsTTSData(SSML_text, outputPath):
+ req_id = uuid.uuid4().hex.upper()
+ print(req_id)
+ # TOKEN来源 https://github.com/rany2/edge-tts/blob/master/src/edge_tts/constants.py
+ # 查看支持声音列表 https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4
+ TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4"
+ WSS_URL = (
+ "wss://speech.platform.bing.com/consumer/speech/synthesize/"
+ + "readaloud/edge/v1?TrustedClientToken="
+ + TRUSTED_CLIENT_TOKEN
+ )
+ endpoint2 = f"{WSS_URL}&ConnectionId={req_id}"
+ async with websockets.connect(endpoint2, extra_headers={
+ "Pragma": "no-cache",
+ "Cache-Control": "no-cache",
+ "Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold",
+ "Accept-Encoding": "gzip, deflate, br",
+ "Accept-Language": "en-US,en;q=0.9",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ " (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.41"}) as websocket:
+ message_1 = (
+ f"X-Timestamp:{getXTime()}\r\n"
+ "Content-Type:application/json; charset=utf-8\r\n"
+ "Path:speech.config\r\n\r\n"
+ '{"context":{"synthesis":{"audio":{"metadataoptions":{'
+ '"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},'
+ '"outputFormat":"audio-24khz-48kbitrate-mono-mp3"'
+ "}}}}\r\n"
+ )
+ await websocket.send(message_1)
+
+ message_2 = (
+ f"X-RequestId:{req_id}\r\n"
+ "Content-Type:application/ssml+xml\r\n"
+ f"X-Timestamp:{getXTime()}Z\r\n" # This is not a mistake, Microsoft Edge bug.
+ "Path:ssml\r\n\r\n"
+ f"{SSML_text}")
+ await websocket.send(message_2)
+
+ # Checks for close connection message
+ end_resp_pat = re.compile('Path:turn.end')
+ audio_stream = b''
+ while (True):
+ response = await websocket.recv()
+ print('receiving...')
+ # print(response)
+ # Make sure the message isn't telling us to stop
+ if (re.search(end_resp_pat, str(response)) == None):
+ # Check if our response is text data or the audio bytes
+ if type(response) == type(bytes()):
+ # Extract binary data
+ try:
+ needle = b'Path:audio\r\n'
+ start_ind = response.find(needle) + len(needle)
+ audio_stream += response[start_ind:]
+ except:
+ pass
+ else:
+ break
+ with open(f'{outputPath}.mp3', 'wb') as audio_out:
+ audio_out.write(audio_stream)
+
+
+async def mainSeq(SSML_text, outputPath):
+ await transferMsTTSData(SSML_text, outputPath)
+
+
+def get_SSML(name, rate, pitch, text):
+ SSML = f"""
+
+
+
+ {text}
+
+
+
+ """
+
+ return SSML
+
+
+def now_time():
+ return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+
+
+if __name__ == "__main__":
+ args = parseArgs()
+ name = 'zh-CN-XiaoxiaoNeural'
+ rate = '40%'
+ pitch = '60%'
+ text = '我是tts语音合成助手'
+ SSML_text = get_SSML(name=name, rate=rate, pitch=pitch, text=text)
+
+ output_path = 'output_' + now_time().replace(':', '-')
+ asyncio.get_event_loop().run_until_complete(mainSeq(SSML_text, output_path))
+ print('completed')
+ # python tts.py --input SSML.xml
+ # python tts.py --input SSML.xml --output 保存文件名
diff --git a/voice.png b/voice.png
new file mode 100644
index 0000000000000000000000000000000000000000..02c8c51b7dc66b955822955e5341370dd336fda2
GIT binary patch
literal 7833
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