python语音分割_python文字转语音开发实战,主功能不难,这3个细节才是重点

近日做了个文本转语音的小项目,主功能接百度智能云的API很顺利,但3个小细节处理很费了些事。就像生活中往往一些不起眼的小角色更狠、更能给人添堵,做项目也是一样。特分享记录一下,先看看项目界面和运行效果:

d79420ae35e6db2c3d2f507cea70915e.png
7271c7add8dc0c23c4f562e704ca6556.gif

主功能:在顶部输入框中输入英语单词或语句,点击右侧的三个按钮,识别输入的文本,转换为语音,并进行播放。读单词模式时,中间灰色区域,同步显示英语单词和中文翻译。

1、主功能文本转语音试了三种方法,感觉百度智能云的语音技术甩别的几条街,不知是不是百度曾经的all in ai留下的战果,翻译也就顺带用他家的了。

2、通过“文件导入”功能导入文本时,打开文本、读取文本都不难,但遇到无法解码的特殊字符,只一个就导致读取失败,字符解码的细节容易被忽略,却常常出来惹事。

3、“读单词”模式,通过分割符将文本分割成一个个单词,再逐个进行语音转换,播放。但由于playsound播放文件后,资源不会被放弃,导致无法逐个播放,这是本项目最硬的茬,通过修改源库代码方解决。

4、“读单词”模式,播放语音时,中间同步显示英语和翻译,实际却是等全部读完了,才显示最后一个单词及翻译,后通过添加线程解决。

详细过程:

一、主功能,接百度语音技术解决文本转语音。

首先,需要注册百度智能云,添加语音技术应用,获得ID、key,如下图:

791e586e03ea445d0e1c2216f712bd43.png

接下来写码就ok了。

def text_to_voice(str, cs):

APP_ID = '************************'

API_KEY = '************************'

SECRET_KEY = '************************'

client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

result = client.synthesis(str, 'zh', 1, cs)

if not isinstance(result, dict):

with open('auido.mp3', 'wb') as f:

f.write(result)

if __name__ == "__main__":

cs = {'vol': 5, 'per': 1, 'spd': 8}

text_to_voice('goodmorning', cs)

代码很简单,头三行***为从前面获得的ID、key。‘str’为要读取的文本内容,‘cs’为播放声音的参数,'vol'音调,'per'1为男声,0为女声,'spd'为语速。作为参数带入函数块,利于界面交互。

二、文件导入写入文本

def open_file():

global word

path = './'

filename = filedialog.askopenfilename(

title=u'选择文件',

filetypes=[("记事本", ".txt")],

initialdir=(os.path.expanduser(path)))

if filename != '':

f = open(filename, 'rb')

lines = f.readlines()

n = 1

for line in lines:

line = line.strip().decode('utf-8')

if line != '':

word = word + line

n += 1

e.delete(0, 'end')

e.insert(0, word)

e.delete(0, 'end')为删除文本框原内容

e.insert(0, word)将word里的内容显示在文本框里。

读取生成word内容时,line = line.strip().decode('utf-8')用decode解码,可以应付多数情况了。

三、解决不能循环播放mp3文件的问题

导入模块from playsound import playsound

直接播放playsound('auido.mp3')

但在for循环里批量播放时报错,因为playsound库源码里没有关闭文件的代码,从网上借鉴到的做法,修改源码,增加关闭功能。

打开python安装路径,找到playsound.py文件打开,

51d07906bc4c6c361b36a6040c5030e6.png

在下面的位置,增加红框内的代码

14549ef5f5354b40743be63f4bf3e007.png

增加的代码为:

while True:

if winCommand('status', alias, 'mode').decode() == 'stopped':

winCommand('close', alias)

break

保存后,再循环运行playsound('auido.mp3')就没问题了。

将一组单词逐个进行阅读的代码:

def read_text(text):

cs['vol'] = s1.get()

cs['spd'] = s2.get()

cs['per'] = v.get()

ttv.text_to_voice(text, cs)

playsound('auido.mp3')

def one_by_one():

tt = re.split(',|.|?|。|,', content.get())

if tt != '':

for t in tt:

if t != '':

label22['text'] = t.strip() + '' + '' + en_to_zh.fy(t)

read_text(t)

time.sleep(1)

四、多线程同步显示单词及翻译

上面label22['text'] = t.strip() + '' + '' + en_to_zh.fy(t)中en_to_zh.fy(t)即为翻译的中文,首先导入自己写的含翻译功能py文件en_to_zh,再利用文件里的fy函数块进行翻译得到中文。en_to_zh.py代码为百度翻译api提供的代码,只修改为自己的id和key,然后设置return语句返回获得的中文即可:

import http.client

import hashlib

import urllib

import random

import json

def fy(txt):

appid = '*************************' # 填写你的appid

secretKey = '*************************' # 填写你的密钥

httpClient = None

myurl = '/api/trans/vip/translate'

fromLang = 'en' #原文语种

toLang = 'zh' #译文语种

salt = random.randint(32768, 65536)

q = txt#'apple'

sign = appid + q + str(salt) + secretKey

sign = hashlib.md5(sign.encode()).hexdigest()

myurl = myurl + '?appid=' + appid + '&q=' + urllib.parse.quote(

q) + '&from=' + fromLang + '&to=' + toLang + '&salt=' + str(

salt) + '&sign=' + sign

try:

httpClient = http.client.HTTPConnection('api.fanyi.baidu.com')

httpClient.request('GET', myurl)

# response是HTTPResponse对象

response = httpClient.getresponse()

result_all = response.read().decode("utf-8")

result = json.loads(result_all)

return result['trans_result'][0]['dst']

except Exception as e:

print(e)

finally:

if httpClient:

httpClient.close()

在原代码里仅加了“return result['trans_result'][0]['dst']”语句就好。该处的id和key是百度翻译里获取的,与前面的语音技术id是两码事,但注册操作方法一样。

上面获得了内容,但我们需要在for循环里动态显示在label框里,却无法实现,只能在for循环完成后,再显示最后一次的结果,显然这不是我们要的。这是因为单线程不能同时干两件事导致的,解决方法是增加线程,让同时处理两件事。

在按钮“读单词”的触发程序块里设置增加线程的代码:

951ef4854489a0c2fff36c4b3e074060.png

bt31 = tkinter.Button(root, ... command=read_one)

def read_one():

read_text('请跟我读:')

t = threading.Thread(target=one_by_one, args=(), name='thread-refresh')

t.setDaemon(True)

t.start()

真正的运行阅读代码写在函数one_by_one()里。

五、交互界面代码

为丰富播放体验,设置了单选按钮、数据拉条进行CS参数设置,具体过程不细讲,代码附上:

if __name__ == "__main__":

cs = {'vol': 8, 'per': 0, 'spd': 4}

root = tkinter.Tk()

root.title("英语领读")

root.geometry('660x420-20+30')

# 第0行

ft0 = tkFont.Font(family='楷体', size=15)

titlelabel = tkinter.Label(root, text='输入或通过文件导入阅读内容', width=40, font=ft0, anchor='s')

titlelabel.grid(row=0, column=0, columnspan=6, pady=10, sticky='S')

# 第1行

pic_11 = tkinter.PhotoImage(file='pic/99.png')

bt11 = tkinter.Button(root, text='从文件导入', image=pic_11, width=65, height=25, command=open_file)

bt11.grid(row=1, column=0, pady=8, padx=5, columnspan=1, sticky='NW')

content = tkinter.StringVar()

e = tkinter.Entry(root, textvariable=content, font=ft0, width=46)

e.grid(row=1, column=1, columnspan=4, pady=8, sticky='NW')

content.set('hai, nice to meet you!')

pic_1 = tkinter.PhotoImage(file='pic/12.png')

bt12 = tkinter.Button(root, text='清空', width=8,

command=clear) #, image=pic_1

bt12.grid(row=1, column=5, pady=8, padx=5)

# 第2行

ft = tkFont.Font(family='楷体', size=38, weight=tkFont.BOLD)

label22 = tkinter.Label(root,

width=17,

height=5,

bg='DarkGray',#DarkSeaGreen DarkGray

fg='black',

justify='left',

relief='sunken',

font=ft) #wraplength=120,

label22.grid(row=2, column=1, rowspan=3, columnspan=4, sticky='NW')

# 第3行

pic_31 = tkinter.PhotoImage(file='pic/one11.png')

bt31 = tkinter.Button(root,

text='读单词',

width=60,

image=pic_31,

command=read_one)

bt31.grid(row=2, column=5, padx=15)

pic_32 = tkinter.PhotoImage(file='pic/all11.png')

bt32 = tkinter.Button(root,

text='读整段',

width=60,

image=pic_32,

command=read_all)

bt32.grid(row=3, column=5, padx=15)

pic_33 = tkinter.PhotoImage(file='pic/tx1.png')

bt33 = tkinter.Button(root,

text='听写',

width=60,

image=pic_33,

command=dictation)

bt33.grid(row=4, column=5, padx=15)

ft2 = tkFont.Font(family='Fixdsys', size=8)

label8 = tkinter.Label(root,

text='版本: bmy-001',

width=20,

font=ft2)

label8.grid(row=5, column=5)

s1 = tkinter.Scale(root,

label='音调',

from_=0,

to=10,

orient=tkinter.HORIZONTAL,

length=200,

showvalue=0,

tickinterval=10,

resolution=1)

s1.grid(row=5, column=1, columnspan=2, padx=15)

s1.set(5)

s2 = tkinter.Scale(root,

label='语速',

from_=0,

to=10,

orient=tkinter.HORIZONTAL,

length=200,

showvalue=0,

tickinterval=10,

resolution=1)

s2.grid(row=5, column=3, columnspan=2, padx=15, pady=1, sticky='N')

s2.set(5)

v = tkinter.IntVar()

boy = tkinter.PhotoImage(file='pic/男1.png')

tkinter.Radiobutton(

root,

text='男声',

variable=v,

image=boy,

value=1,

).grid(row=2, column=0, sticky='S')

girl = tkinter.PhotoImage(file='pic/女1.png')

tkinter.Radiobutton(

root,

text='女声',

variable=v,

image=girl,

value=0,

).grid(row=3, column=0)

v.set(0)

photo = tkinter.PhotoImage(file="./pic/古代儿童.png") # file:t图片路径

imgLabel = tkinter.Label(root, image=photo)

imgLabel.grid(row=4, column=0, rowspan=2, sticky='S')

root.mainloop()

六、预留功能:随时暂停播放过程的功能暂未设置,因为用生涩的底层控制代码去实现一个普通不过的mp3文件暂停播放功能,感觉性价比真的好差,所以......

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值