TTS简介

TTS(Text To Speech)是一种语音合成技术,可以让机器将输入文本以语音的方式播放出来,实现机器说话的效果。

TTS分成语音处理及语音合成,先由机器识别输入的文字,再根据语音库进行语音合成。现在有很多可供调用的TTS接口,比如百度智能云的语音合成接口。微软在Windows系统中也提供了TTS的接口,可以调用此接口实现离线的TTS语音合成功能。

本文将使用pyttsx3库作为示范,编写一个语音合成小工具。

pyttsx3官方文档:https://pyttsx3.readthedocs.io

本文源码已上传至GitHub:

https://github.com/XMNHCAS/SpeechSynthesisTool


安装需要的包

安装PyQt5及其GUI设计工具

# 安装PyQt5
pip install PyQt5# 安装PyQt5设计器
pip install PyQt5Designer

本文使用的编辑器是VSCode,不是PyCharm,使用PyQt5的方式可能存在差异,具体使用时可以根据实际情况进行配置。

安装pyttsx3

pip install pyttsx3

UI界面

可参考下图设计简单的GUI界面,由于本文主要为功能实例,故不考虑界面美观问题。

界面应有一个文本输入框,用以输入将要转化为语音的文本,同时需要一个播放按钮,用以触发语音播放的方法。语速、音量和语言可以按需选择。

使用PyQt5的设计工具,可以根据以上配置的GUI界面生成以下UI(XML)代码:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>Form</class><widget class="QWidget" name="Form"><property name="geometry"><rect><x>0</x><y>0</y><width>313</width><height>284</height></rect></property><property name="windowTitle"><string>语音合成器</string></property><property name="windowIcon"><iconset><normaloff>voice.ico</normaloff>voice.ico</iconset></property><widget class="QWidget" name="verticalLayoutWidget"><property name="geometry"><rect><x>10</x><y>10</y><width>291</width><height>261</height></rect></property><layout class="QVBoxLayout" name="verticalLayout"><property name="spacing"><number>20</number></property><item><layout class="QHBoxLayout" name="horizontalLayout_2"><item><widget class="QLabel" name="label"><property name="text"><string>播报文本</string></property><property name="alignment"><set>Qt::AlignJustify|Qt::AlignTop</set></property></widget></item><item><widget class="QTextEdit" name="tbx_text"/></item></layout></item><item><layout class="QHBoxLayout" name="horizontalLayout_4"><item><widget class="QLabel" name="label_3"><property name="text"><string>语速</string></property></widget></item><item><widget class="QSlider" name="slider_rate"><property name="maximum"><number>300</number></property><property name="orientation"><enum>Qt::Horizontal</enum></property></widget></item><item><widget class="QLabel" name="label_rate"><property name="minimumSize"><size><width>30</width><height>0</height></size></property><property name="text"><string>0</string></property><property name="alignment"><set>Qt::AlignCenter</set></property></widget></item></layout></item><item><layout class="QHBoxLayout" name="horizontalLayout_3"><item><widget class="QLabel" name="label_2"><property name="text"><string>音量</string></property></widget></item><item><widget class="QSlider" name="slider_volumn"><property name="maximum"><number>100</number></property><property name="orientation"><enum>Qt::Horizontal</enum></property></widget></item><item><widget class="QLabel" name="label_volumn"><property name="minimumSize"><size><width>30</width><height>0</height></size></property><property name="text"><string>0</string></property><property name="alignment"><set>Qt::AlignCenter</set></property></widget></item></layout></item><item><layout class="QHBoxLayout" name="horizontalLayout"><item><widget class="QLabel" name="label_4"><property name="text"><string>选择语言</string></property></widget></item><item><widget class="QRadioButton" name="rbtn_zh"><property name="text"><string>中文</string></property><property name="checked"><bool>true</bool></property></widget></item><item><widget class="QRadioButton" name="rbtn_en"><property name="text"><string>英文</string></property></widget></item></layout></item><item><layout class="QHBoxLayout" name="horizontalLayout_5"><item><widget class="QLabel" name="label_5"><property name="minimumSize"><size><width>60</width><height>0</height></size></property><property name="text"><string/></property></widget></item><item><widget class="QPushButton" name="btn_play"><property name="minimumSize"><size><width>0</width><height>30</height></size></property><property name="text"><string>播放</string></property></widget></item></layout></item></layout></widget></widget><resources/><connections/>
</ui>

最后再使用PyQt5的界面工具,可以根据以上UI的代码,生成以下的窗体类:

# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'd:\Program\VSCode\Python\TTS_PyQT\tts_form.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Form(object):def setupUi(self, Form):Form.setObjectName("Form")Form.resize(313, 284)icon = QtGui.QIcon()icon.addPixmap(QtGui.QPixmap("./voice.ico"),QtGui.QIcon.Normal, QtGui.QIcon.Off)Form.setWindowIcon(icon)self.verticalLayoutWidget = QtWidgets.QWidget(Form)self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 291, 261))self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)self.verticalLayout.setContentsMargins(0, 0, 0, 0)self.verticalLayout.setSpacing(20)self.verticalLayout.setObjectName("verticalLayout")self.horizontalLayout_2 = QtWidgets.QHBoxLayout()self.horizontalLayout_2.setObjectName("horizontalLayout_2")self.label = QtWidgets.QLabel(self.verticalLayoutWidget)self.label.setAlignment(QtCore.Qt.AlignJustify | QtCore.Qt.AlignTop)self.label.setObjectName("label")self.horizontalLayout_2.addWidget(self.label)self.tbx_text = QtWidgets.QTextEdit(self.verticalLayoutWidget)self.tbx_text.setObjectName("tbx_text")self.horizontalLayout_2.addWidget(self.tbx_text)self.verticalLayout.addLayout(self.horizontalLayout_2)self.horizontalLayout_4 = QtWidgets.QHBoxLayout()self.horizontalLayout_4.setObjectName("horizontalLayout_4")self.label_3 = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_3.setObjectName("label_3")self.horizontalLayout_4.addWidget(self.label_3)self.slider_rate = QtWidgets.QSlider(self.verticalLayoutWidget)self.slider_rate.setMaximum(300)self.slider_rate.setOrientation(QtCore.Qt.Horizontal)self.slider_rate.setObjectName("slider_rate")self.horizontalLayout_4.addWidget(self.slider_rate)self.label_rate = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_rate.setMinimumSize(QtCore.QSize(30, 0))self.label_rate.setAlignment(QtCore.Qt.AlignCenter)self.label_rate.setObjectName("label_rate")self.horizontalLayout_4.addWidget(self.label_rate)self.verticalLayout.addLayout(self.horizontalLayout_4)self.horizontalLayout_3 = QtWidgets.QHBoxLayout()self.horizontalLayout_3.setObjectName("horizontalLayout_3")self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_2.setObjectName("label_2")self.horizontalLayout_3.addWidget(self.label_2)self.slider_volumn = QtWidgets.QSlider(self.verticalLayoutWidget)self.slider_volumn.setMaximum(100)self.slider_volumn.setOrientation(QtCore.Qt.Horizontal)self.slider_volumn.setObjectName("slider_volumn")self.horizontalLayout_3.addWidget(self.slider_volumn)self.label_volumn = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_volumn.setMinimumSize(QtCore.QSize(30, 0))self.label_volumn.setAlignment(QtCore.Qt.AlignCenter)self.label_volumn.setObjectName("label_volumn")self.horizontalLayout_3.addWidget(self.label_volumn)self.verticalLayout.addLayout(self.horizontalLayout_3)self.horizontalLayout = QtWidgets.QHBoxLayout()self.horizontalLayout.setObjectName("horizontalLayout")self.label_4 = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_4.setObjectName("label_4")self.horizontalLayout.addWidget(self.label_4)self.rbtn_zh = QtWidgets.QRadioButton(self.verticalLayoutWidget)self.rbtn_zh.setChecked(True)self.rbtn_zh.setObjectName("rbtn_zh")self.horizontalLayout.addWidget(self.rbtn_zh)self.rbtn_en = QtWidgets.QRadioButton(self.verticalLayoutWidget)self.rbtn_en.setObjectName("rbtn_en")self.horizontalLayout.addWidget(self.rbtn_en)self.verticalLayout.addLayout(self.horizontalLayout)self.horizontalLayout_5 = QtWidgets.QHBoxLayout()self.horizontalLayout_5.setObjectName("horizontalLayout_5")self.label_5 = QtWidgets.QLabel(self.verticalLayoutWidget)self.label_5.setMinimumSize(QtCore.QSize(60, 0))self.label_5.setText("")self.label_5.setObjectName("label_5")self.horizontalLayout_5.addWidget(self.label_5)self.btn_play = QtWidgets.QPushButton(self.verticalLayoutWidget)self.btn_play.setMinimumSize(QtCore.QSize(0, 30))self.btn_play.setObjectName("btn_play")self.horizontalLayout_5.addWidget(self.btn_play)self.verticalLayout.addLayout(self.horizontalLayout_5)self.retranslateUi(Form)QtCore.QMetaObject.connectSlotsByName(Form)def retranslateUi(self, Form):_translate = QtCore.QCoreApplication.translateForm.setWindowTitle(_translate("Form", "语音合成器"))self.label.setText(_translate("Form", "播报文本"))self.label_3.setText(_translate("Form", "语速"))self.label_rate.setText(_translate("Form", "0"))self.label_2.setText(_translate("Form", "音量"))self.label_volumn.setText(_translate("Form", "0"))self.label_4.setText(_translate("Form", "选择语言"))self.rbtn_zh.setText(_translate("Form", "中文"))self.rbtn_en.setText(_translate("Form", "英文"))self.btn_play.setText(_translate("Form", "播放"))

如果直接复制此代码,可能会出现图标丢失的问题。这个需要根据实际情况修改icon的配置,并添加要使用的ico图标文件。


功能代码

语音工具类

首先我们需要初始化并获取语音合成用的语音引擎对象。

# tts对象
engine = pyttsx3.init()

我们可以通过该对象的setProperty方法,对语音合成的对象的属性进行修改:

属性名 解释
rate 以每分钟字数表示的整数语速
volume 音量,取值范围为[0.0, 1.0]
voices 语音的字符串标识符

语音工具类代码如下,代码含义可参考注释:

import pyttsx3class VoiceEngine():'''tts 语音工具类'''def __init__(self):'''初始化'''# tts对象self.__engine = pyttsx3.init()# 语速self.__rate = 150# 音量self.__volume = 100# 语音ID,0为中文,1为英文self.__voice = 0@propertydef Rate(self):'''语速属性'''return self.__rate@Rate.setterdef Rate(self, value):self.__rate = value@propertydef Volume(self):'''音量属性'''return self.__volume@Volume.setterdef Volume(self, value):self.__volume = value@propertydef VoiceID(self):'''语音ID:0 -- 中文;1 -- 英文'''return self.__voice@VoiceID.setterdef VoiceID(self, value):self.__voice = valuedef Say(self, text):'''播放语音'''self.__engine.setProperty('rate', self.__rate)self.__engine.setProperty('volume', self.__volume)# 获取可用语音列表,并设置语音voices = self.__engine.getProperty('voices')self.__engine.setProperty('voice', voices[self.__voice].id)# 保存语音文件# self.__engine.save_to_file(text, 'test.mp3')self.__engine.say(text)self.__engine.runAndWait()self.__engine.stop()

窗体类

我们可以创建一个继承于我们刚刚创建的PyQt5的窗体类,并为窗体的拖拽事件和点击事件注册回调函数,同时创建一个语音工具类的实例,用以实现指定事件触发时需要执行的语音操作。

import sys
import _thread as th
from PyQt5.QtWidgets import QMainWindow, QApplication
from Ui_tts_form import Ui_Formclass MainWindow(QMainWindow, Ui_Form):'''窗体类'''def __init__(self, parent=None):'''初始化窗体'''super(MainWindow, self).__init__(parent)self.setupUi(self)# 获取tts工具类实例self.engine = VoiceEngine()self.__isPlaying = False# 设置初始文本self.tbx_text.setText('床前明月光,疑似地上霜。\n举头望明月,低头思故乡。')# 进度条数据绑定到label中显示self.slider_rate.valueChanged.connect(self.setRateTextValue)self.slider_volumn.valueChanged.connect(self.setVolumnTextValue)# 设置进度条初始值self.slider_rate.setValue(self.engine.Rate)self.slider_volumn.setValue(self.engine.Volume)# RadioButton选择事件self.rbtn_zh.toggled.connect(self.onSelectVoice_zh)self.rbtn_en.toggled.connect(self.onSelectVoice_en)# 播放按钮点击事件self.btn_play.clicked.connect(self.onPlayButtonClick)def setRateTextValue(self):'''修改语速label文本值'''value = self.slider_rate.value()self.label_rate.setText(str(value))self.engine.Rate = valuedef setVolumnTextValue(self):'''修改音量label文本值'''value = self.slider_volumn.value()self.label_volumn.setText(str(value / 100))self.engine.Volume = valuedef onSelectVoice_zh(self):'''修改中文的语音配置及默认播放文本'''self.tbx_text.setText('床前明月光,疑似地上霜。\n举头望明月,低头思故乡。')self.engine.VoiceID = 0def onSelectVoice_en(self):'''修改英文的语音配置及默认的播放文本'''self.tbx_text.setText('Hello World')self.engine.VoiceID = 1def playVoice(self):'''播放'''if self.__isPlaying is not True:self.__isPlaying = Truetext = self.tbx_text.toPlainText()self.engine.Say(text)self.__isPlaying = Falsedef onPlayButtonClick(self):'''播放按钮点击事件开启线程新线程播放语音,避免窗体因为语音播放而假卡死'''th.start_new_thread(self.playVoice, ())

完整代码

import sys
import _thread as th
from PyQt5.QtWidgets import QMainWindow, QApplication
from Ui_tts_form import Ui_Form
import pyttsx3class VoiceEngine():'''tts 语音工具类'''def __init__(self):'''初始化'''# tts对象self.__engine = pyttsx3.init()# 语速self.__rate = 150# 音量self.__volume = 100# 语音ID,0为中文,1为英文self.__voice = 0@propertydef Rate(self):'''语速属性'''return self.__rate@Rate.setterdef Rate(self, value):self.__rate = value@propertydef Volume(self):'''音量属性'''return self.__volume@Volume.setterdef Volume(self, value):self.__volume = value@propertydef VoiceID(self):'''语音ID:0 -- 中文;1 -- 英文'''return self.__voice@VoiceID.setterdef VoiceID(self, value):self.__voice = valuedef Say(self, text):'''播放语音'''self.__engine.setProperty('rate', self.__rate)self.__engine.setProperty('volume', self.__volume)voices = self.__engine.getProperty('voices')self.__engine.setProperty('voice', voices[self.__voice])# 保存语音文件# self.__engine.save_to_file(text, 'test.mp3')self.__engine.say(text)self.__engine.runAndWait()self.__engine.stop()class MainWindow(QMainWindow, Ui_Form):'''窗体类'''def __init__(self, parent=None):'''初始化窗体'''super(MainWindow, self).__init__(parent)self.setupUi(self)# 获取tts工具类实例self.engine = VoiceEngine()self.__isPlaying = False# 设置初始文本self.tbx_text.setText('床前明月光,疑似地上霜。\n举头望明月,低头思故乡。')# 进度条数据绑定到label中显示self.slider_rate.valueChanged.connect(self.setRateTextValue)self.slider_volumn.valueChanged.connect(self.setVolumnTextValue)# 设置进度条初始值self.slider_rate.setValue(self.engine.Rate)self.slider_volumn.setValue(self.engine.Volume)# RadioButton选择事件self.rbtn_zh.toggled.connect(self.onSelectVoice_zh)self.rbtn_en.toggled.connect(self.onSelectVoice_en)# 播放按钮点击事件self.btn_play.clicked.connect(self.onPlayButtonClick)def setRateTextValue(self):'''修改语速label文本值'''value = self.slider_rate.value()self.label_rate.setText(str(value))self.engine.Rate = valuedef setVolumnTextValue(self):'''修改音量label文本值'''value = self.slider_volumn.value()self.label_volumn.setText(str(value / 100))self.engine.Volume = valuedef onSelectVoice_zh(self):'''修改中文的语音配置及默认播放文本'''self.tbx_text.setText('床前明月光,疑似地上霜。\n举头望明月,低头思故乡。')self.engine.VoiceID = 0def onSelectVoice_en(self):'''修改英文的语音配置及默认的播放文本'''self.tbx_text.setText('Hello World')self.engine.VoiceID = 1def playVoice(self):'''播放'''if self.__isPlaying is not True:self.__isPlaying = Truetext = self.tbx_text.toPlainText()self.engine.Say(text)self.__isPlaying = Falsedef onPlayButtonClick(self):'''修改语速label文本值'''th.start_new_thread(self.playVoice, ())if __name__ == "__main__":'''主函数'''app = QApplication(sys.argv)form = MainWindow()form.show()sys.exit(app.exec_())

Python语音合成小工具(PyQt5 + pyttsx3)相关推荐

  1. 用Python编写小工具下载OSM路网数据

    文章来源于Python大数据分析,作者费弗里 本文对应脚本已上传至Github仓库: https://github.com/CNFeffery/DataScienceStudyNotes[1] 1 简 ...

  2. python 处理数据小工具_用Python这个小工具,一次性把论文作图与数据处理全部搞定!...

    原标题:用Python这个小工具,一次性把论文作图与数据处理全部搞定! 一入科研深似海-- 随着大学纷纷开学,"防脱发用生姜还是黑芝麻?", 研究僧们又开始为自己所剩无几的头发发愁 ...

  3. 有关Python的小工具 - picture

    有关Python的小工具 - picture 读取图片 通过cv2.imread读取图片 通过cv2.imread读取比特流 通过Image.open读取图片 通过io.BytesIO读取比特流 保存 ...

  4. python自动翻译小工具_Python实现翻译小工具

    一.背景 利用Requests模块获取有道词典web页面的post信息,BeautifulSoup来获取需要的内容,通过tkinter模块生成gui界面. 二.代码 git源码地址 Python实现翻 ...

  5. python 上传excel_简历批量合并Python+VBA小工具

    每年的校招,才是对招聘系统体验的真正大考. 如何帮HR快速标记.筛选成千上万的简历? 如何快速应答同学们的问询? 群面如何签到?如何自动分组? 怎样让候选人清楚面试流程与候场时间? 如何让面试官方便地 ...

  6. python趣味小工具,图片转Execl,图片转TXT工具

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. WHJWNAVY | 作者 Demo大师 | 来源 python 趣味实用 ...

  7. 你生日那天的宇宙有多美?Python制作小工具一键查询图片!

    这两天微博有个热搜--你生日那天的宇宙,看上去很有趣,点进去链接发现是 NASA(美国航空航天局) 提供的一个网站(庆祝哈勃望远镜30周年),可以查询一下你生日那天哈勃望远镜拍到的太空图片.但是大概是 ...

  8. 如何用python写小工具_用python写一个录音小工具

    Python的paramiko,wxPython库的应用 Sound eXchange 命令行 需求 最近在给一个做语音识别的项目做QA工作.众所周知,此类人工智能方面的项目都需要一些数据收集的工作. ...

  9. 记Python开发小工具过程

    前言 因为压测一个系统,需要大量的id.网上找的工具一次最多生成500个,完全是杯水车薪了.就找了python生成的代码,修改成一次生成36万个,这次够用了.为了方便以后使用,就学习开发一个GUI界面 ...

  10. python小应用之整理手机图片_用Python这个小工具,一次性把论文作图与数据处理全部搞定!...

    整理完这些资料已经凌晨3点了,困得眼睛都睁不开,一想到明天就截稿了,他只好随便拼凑一篇应付. 结果可想而知,导师还没看完第一页就把论文给退回来了,说论文通篇缺乏具体数据分析,研究计划就很粗糙,实施起来 ...

最新文章

  1. xBIM WeXplorer 设置模型颜色
  2. 上传文件到服务器的Linux命令
  3. java spring省略jsp,Java +Tomcat + SpringMVC实现页面访问示例解析
  4. sns.distplot图例标注怎么添加_百度地图API图标、文本、图例与连线
  5. redis 主从模式_Redis主从模式部署文档
  6. Windows Server 2012 R2 安装完勒索病毒后出现的共享和DNS等问题
  7. java 图片缓存工具,java缓存读取图片解决方案
  8. Linux基础(存储结构和磁盘划分)
  9. linux修改栈指针x86,x86-堆栈指针未填充16时libc的system()导致分段...
  10. varnish服务器在内存大量富余时使用交换空间的原因及解决方法
  11. Databricks文档05----使用 Azure Databricks 连接SQL Server查询数据
  12. 在Paint事件中绘制控件(边框)
  13. 电影推荐系统kaggle
  14. 【企业】掌握理查德·费曼学习法,提高学习效率
  15. 华杉讲透《孙子兵法》阅读有感(一)
  16. 工业级手持式扫描仪3d扫描首选迪万科技抄数服务
  17. 如何在CSDN上上传资源
  18. dotween damage text 飘血伤害数字
  19. iPhone--什么是解锁
  20. Tesseract-OCR下载和安装

热门文章

  1. Intel VT-d(1)- 简介
  2. 背壳BAKAZU 2013复古邮差风帆布休闲潮男腰包 A801BK 黑色 腰包胸包【图片 价格 品牌 报价】-京东商城...
  3. 【CSDN竞赛第五期】“三而竭”采用等比求和公式法的思考
  4. 菜鸟海外仓智慧供应链系统“货运参谋”上线,全球跨境仓库100+覆盖30国
  5. vue自动计算日期天数
  6. 5种高大上的yml文件读取方式,你知道吗?
  7. JBX+WL8+Struts国际化中的一些问题
  8. 超微服务器双路主板系列,超微发布X12DPL系列服务器主板 支持双路10nm Ice Lake至强处理器...
  9. linux连接蓝牙设备,Linux 下连接蓝牙设备
  10. DSP TMS320C5509A 控制DDS AD9854芯片驱动