gTTS可以说一个列表(PYTHON)吗?



是否可以说出一个列表。现在我正在使用

#Minimum reproducable
import tkinter as tk
from gtts import gTTS
from io import BytesIO
import pygame
def play():
words = [one,boy,girl,man,woman,two]
for i in words:
speak(i)
def speak(text,language="en",accent="com"):
mp3_fp = BytesIO()
phrase = gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():

pygame.time.delay(10)
pygame.event.poll()
play()

此代码有效,但不是最好的。如果您尝试暂停音频,则只有列表中的一个单词会暂停,其余单词会播放。有没有办法说出列表,能够暂停它,然后再次播放而不会出错。费尔利对此很陌生。我正在使用模块,所以我不必保存 mp3。它们被保存到变量中,然后播放。这不涉及要创建的额外文件。此外,当我使用speak()函数时,我必须使用线程才能在播放来自 Pygame Mixer 的音频时与 tkinter 窗口进行交互。

目标:能够暂停列表并重播

感谢您邀请我回答这个问题,很抱歉我花了这么长时间才注意到。

两个小告诫:

  1. 该列表仍然不正确。words = [one,boy,girl,man,woman,two]是变量列表(尚未定义),而不是文本。 这条线必须words = ["one", "boy", "girl", "man", "woman", "two"].
  2. 您已经完成了绝大多数工作,但它并不是真正的最小代码量,因为它没有演示您的暂停问题。

话虽如此,我无法真正重现您的问题。 我确实发现pygame.time.delay()似乎确实存在问题,它似乎无法预测地冻结;至少它在我的机器上是这样(Linux上的Python3)。

为了解决这个问题,我pygame.time.delay()改为pygame.time.wait()

下面的代码演示每个单词在中间暂停,下一个单词直到前一个单词完成后才会开始。 它开始弹奏单词,然后反复暂停和取消暂停,直到单词完成。 我添加了变量delay以便您可以尝试不同的延迟长度。 10ms效果不佳,但在100ms时延迟非常明显。

我还更改了pygame.event.poll()(它只获取一个事件并且不对其进行任何操作)更改为pygame.event.clear(),我认为,这就是该行的目标:保持事件队列为空。

你提到你使用 TkInter 和线程与它进行了交互。 如果您仍然遇到问题,也许可以再次询问,包括您的 TkInter 和线程代码。

# import tkinter as tk
from gtts import gTTS
from io import BytesIO
import pygame
def play():
words = ["one", "boy", "girl", "man", "woman", "two"]
for i in words:
speak(i)
def speak(text,language="en",accent="com"):
mp3_fp = BytesIO()
phrase = gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()

delay = 100
while pygame.mixer.music.get_busy():
pygame.time.wait(delay)
pygame.mixer.music.pause()
pygame.time.wait(delay)
pygame.mixer.music.unpause()
pygame.event.clear()
play()

在我自己对此进行个人测试时,我忍不住将其用作我的测试文本。 你可能认不出来。 您需要注释掉延迟/暂停/取消暂停部分来收听它,并且加载缓冲区需要几秒钟才能播放。

speak("Good morning, and welcome to the Black Mesa transit system.  This automated train is provided for the security and convenience of the Black Mesa Research Facility personnel.  The time is 8:47 A M.  Current topside temperature is 93 degrees with an estimated high of 105.  The Black Mesa compound is maintained at a pleasant 68 degrees at all times.  This train is inbound from level 3 dormitories to sector C test labs and control facilities.  If your intended destination is a high security area beyond sector C, you will need to return to the central transit hub in area 9 and board a high security train.  If you have not yet submitted your identity to the retinal clearance system, you must report to Black Mesa personnel for processing before you will be permitted into the high security branch of the transit system.  Due to the high toxicity of material routinely handled in the Black Mesa compound, no smoking, eating, or drinking are permitted within the Black Mesa transit system.  Please keep your limbs inside the train at all times.  Do not attempt to open the doors until the train has come to a complete halt at the station platform.  In the event of an emergency, passengers are to remain seated and await further instruction.  If it is necessary to exit the train, disabled personnel should be evacuated first. Please, stay away from electrified rails and proceed to an emergency station until assistance arrives.")

所以我要添加第二个答案! 从没想过我会这样做。 对于阅读本文的其他人:第二个答案是对OP对我第一个答案的评论的回应,基于我对他的实施知之甚少。

最后完成代码。

你提到你正在使用TkInter,所以我做的第一件事就是实现TkInter代码。我通常先编写图形界面,因为它可以帮助我想象在程序的其余部分需要什么方法。 GUI 是一个事件驱动的接口,本身不应实现任何核心工作代码。

import tkinter as tk
#create a TkInter class to hold our 'play' and 'pause' buttons
#the canonical way to do this is to subclass it from a tkinter.Frame() object
class PlayWindow(tk.Frame):
'''This is the play window to test the pause function.'''
def __init__(self, master, talker):
'''Initialize the PlayWindow'''
super(PlayWindow, self).__init__(master)    #init the super Frame
self.master = master    #sometimes we need this
self.talker = talker    #the talker object

#creates and packs the "Gordon Freeman" button
tk.Button(master, text="Gordon Freeman", command=self.dispatchGordonFreeman).pack()
# tk.Button(master, text="Words", command=self.dispatchWords).pack()

#creates and packs the stop, pause, and play/resume buttons
tk.Button(master, text=u'u25A0', command=self.stop).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
tk.Button(master, text=u'u275Au275A', command=self.pause).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
tk.Button(master, text=u'u25B6', command=self.resume).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
def dispatchWords(self):
'''Dispatches the 'words()' method as a thread to free up the TkInter interface.'''
x = threading.Thread(target=self.GCIreland, daemon=True)
x.start()

def GCIreland(self):
'''Creates a list of words then sends them one at a time to the TTS talker.'''
words = ["one", "boy", "girl", "man", "woman", "two"]
for i in words:
talker.say(i)
def dispatchGordonFreeman(self):
'''Dispatches the 'gordonfreeman()' method as a thread to free up the TkInter interface.'''
x = threading.Thread(target=self.gordonFreeman, daemon=True)
x.start()

def gordonFreeman(self):
'''Creates a list with a well-known speech and hands it off to the Text-To-Speech talker.'''
speech = []
speech.append("Good morning, and welcome to the Black Mesa transit system.")
speech.append("This automated train is provided for the security and convenience of the Black Mesa Research Facility personnel.")
speech.append("The time is 8:47 A.M.")
speech.append("Current topside temperature is 93 degrees with an estimated high of 105.")
speech.append("The Black Mesa compound is maintained at a pleasant 68 degrees at all times.")
speech.append("This train is inbound from level 3 dormitories to sector C test labs and control facilities.")
speech.append("If your intended destination is a high security area beyond sector C, you will need to return to the central transit hub in area 9 and board a high security train.")
speech.append("If you have not yet submitted your identity to the retinal clearance system, you must report to Black Mesa personnel for processing before you will be permitted into the high security branch of the transit system.")
speech.append("Due to the high toxicity of material routinely handled in the Black Mesa compound, no smoking, eating, or drinking are permitted within the Black Mesa transit system.")
speech.append("Please keep your limbs inside the train at all times.")
speech.append("Do not attempt to open the doors until the train has come to a complete halt at the station platform.")
speech.append("In the event of an emergency, passengers are to remain seated and await further instruction.")
speech.append("If it is necessary to exit the train, disabled personnel should be evacuated first. Please, stay away from electrified rails and proceed to an emergency station until assistance arrives.")

for i in speech:
talker.say(i)

def stop(self):
'''Completely stops playback.  (Not implemented.)'''
pass

def resume(self):
'''Hopefully this resumes playback from whence it paused.'''
self.talker.resume()

def pause(self):
'''With any luck at all this will pause playback in a way that it can be resumed.  I dunno -- your guess is as good as mine!'''
self.talker.pause()

很明显,能够将文本列表提供给"谈话者"对您来说很重要,因此我实施了该程序来满足这一明显要求。 当我编写这个程序时,我发现暂停单音节单词的播放有些困难,所以我所做的是包含第二组方法来实现"Gordon Freeman"语音而不是单词列表。 该按钮用于说出单词列表,您所要做的就是取消注释它。

只是关于这部分代码的一些评论:

通常,您要做的是在程序的主要部分创建TkInter根窗口,然后在单独的类中模块化地创建接口的事件驱动功能。 每个这样的类通常都是从tkinter.Frame()对象子类化的。 因为我们.Frame()子类,所以我们需要调用.Frame()__init__()方法。 IMO,最好的方法是使用super().

我将按钮代码作为线程调度,只是为了释放按钮。 这实际上仅用于外观,因此在转换流的整个过程中_io.Bytes()按钮都不会保持按下状态。

您将在按钮代码中看到我做了两件事:我在按钮文本中使用了 unicode,以使其看起来像真正的媒体播放按钮,并且我没有保留按钮对象 ID,因为我无意在创建这些按钮后以任何方式操作它们。 通常,您需要捕获这些 ID,以便可以正确打包它们,在不需要它们时禁用它们,或者以其他方式实现它们。 在这种情况下,我不需要这样做。

我实施的下一个类是谈话者。 这将初始化 PyGame 和混音器,并保存所有开始说话、暂停说话和恢复的方法。

import pygame
import threading
from io import BytesIO
from gtts import gTTS
#create a custom TextToSpeechTalker class to convert text to speech and play it
#   through PyGame's mixer.  Really, the text-to-speech conversion should not
#   really be done in this class as it is unrelated to PyGame.
class TextToSpeechTalker():
def __init__(self, *, language="en", accent="com"):
'''Initializes the Google gTTS Text-To-Speech module.'''
self.language = language    #desired language
self.accent = accent        #desired locaization accent
self.phraseList = []        #a list to hold a bunch of text
self.threadRunning = False  #boolean flag True when thread is running
self.paused = False         #flag that knows if we have paused
pygame.mixer.init()         #initialize pygame's sound mixer
def say(self, text):
'''Converts text to speech, puts it in a buffer, then dispatches the talker
as a thread.'''
self.phraseList.append(text)    #append the text to the list
if not self.threadRunning:
self.dispatchTalkerThread() #dispatch a thread to "say" it

def dispatchTalkerThread(self):
'''Handles dispatching the talker method as a thread.'''
#don't actually need to dispatch it if it's already running
if not self.threadRunning:
self.threadTalker = threading.Thread(target=self.talk, daemon=True)
self.threadRunning = True
self.threadTalker.start()
self.threadTalker.join()    #wait for the thread to terminate
self.threadRunning = False

def talk(self):
'''This plays every entry in our list of text.  It is dispatched as a thread.'''
#this while-loop loops through all entries in the phraseList
while self.phraseList:      #boolean way of checking that there is something in the list
mp3_fp = BytesIO()          #stream buffer to hold mp3 data
phrase = gTTS(text=self.phraseList.pop(0), lang=self.language, tld=self.accent) #creates the TTS object
phrase.write_to_fp(mp3_fp)  #write binary data to the stream buffer
pygame.mixer.music.load(mp3_fp,"mp3")   #load the stream into mixer
pygame.mixer.music.play()               #start playing the mp3

#this is here to prevent multiple mp3s being played simultaneously
#it won't start playing the next buffer until the current one is complete
#   it just waits for the mixer to become not busy
while True:
pygame.time.wait(100)   #arbitrary delay (1/10th second to be nice)
pygame.event.clear()    #keep the event buffer clear for now
if not self.paused and not pygame.mixer.music.get_busy():
break

def pause(self):
'''Pauses the mixer output.'''
pygame.mixer.music.pause()      #pause playback
self.paused = True              #make sure we know we're paused

def resume(self):
'''Resumes mixer output.'''
pygame.mixer.music.unpause()
self.paused = False

讲话者处理控制播放所需的所有 I/O 和方法。 你给说话者一些文本,它把它放到一个列表中,然后它会根据需要转换列表中的每个文本位。 我尝试将文本预转换为_io.Bytes()缓冲区列表,但遇到了一些麻烦,对我来说,将文本保存在列表中比保存一堆二进制缓冲区更快。

通过混音器执行所有"对话"的代码已作为线程调度。 我这样做主要是因为我的直觉告诉我,这是实施它的公正和正确的方法。 这样,我就不必找出一种检查要转换的新文本的方法,并且只有在有要输出的内容时才运行对话线程。 事实证明,它使实现pause()resume方法变得更加容易。

因为它是一个类,你可以使用其他一些文本到语音转换模块重写它,你需要确保你做的是实现.say().stop().pause().resume()方法。

最后,main()方法。 除了我实际上没有实现main().

if __name__ == "__main__":
#We're going to start by initializing PyGame
#   this should be done here and not in the talker object
pygame.init()
#Now initializing a talker object:
talker = TextToSpeechTalker(language='en', accent='com')    #creates an instance of the TextToSpeechTalker class
#Now we're going to create a TkInter window to start and control playback:
win=tk.Tk()                     #create a TkInter window
win.title("Play/Pause Test")    #put a title on it
PlayWindow(win, talker) #the window needs to know about the talker
#Everything should be ready to go at this point so we enter the TkInter mainloop
win.mainloop()

哦,是的;我忘了。 所以,我以为我在谈话器中初始化了 PyGame,但我忘记了我将pygame.init()移到了程序的主要部分。 我的逻辑是,如果你使用PyGame,你可能会在你的主程序中初始化它,因为你把它用于其他东西,而不是在这样的模块中初始化它。

然后,我们实例化谈话对象,创建 TkInter 窗口,并将谈话者对象传递给 PlayWindow 类。 因为我们使用的是TkInter,所以程序的控制是事件驱动的,我们可以直接跳到TkInter的.mainloop()

如果你正在编写一个同时使用 TkInter 和 PyGame 的游戏,你的一个 PyGame 类将开始处理 PyGame 事件循环,可能还有 PyGame 的 FPS 时钟等。

当你运行程序的时候,你会看到一个主按钮,默认显示"Gordon Freeman",下面是三个控制按钮,分别是停止、暂停和恢复。 未实现停止按钮。 单击"戈登·弗里曼"按钮开始演讲,您将看到您可以随意暂停和恢复该演讲列表的播放。

总之,这完全演示了使用 gTTS 模块将文本转换为语音,并且暂停和恢复语音播放效果很好。

以下是完整的代码清单:

#Gordon Freeman is our hero!
import tkinter as tk
import pygame
import threading
from io import BytesIO
from gtts import gTTS

#create a TkInter class to hold our 'play' and 'pause' buttons
#the canonical way to do this is to subclass it from a tkinter.Frame() object
class PlayWindow(tk.Frame):
'''This is the play window to test the pause function.'''
def __init__(self, master, talker):
'''Initialize the PlayWindow'''
super(PlayWindow, self).__init__(master)    #init the super Frame
self.master = master    #sometimes we need this
self.talker = talker    #the talker object

#creates and packs the "Gordon Freeman" button
tk.Button(master, text="Gordon Freeman", command=self.dispatchGordonFreeman).pack()
# tk.Button(master, text="Words", command=self.dispatchWords).pack()

#creates and packs the stop, pause, and play/resume buttons
tk.Button(master, text=u'u25A0', command=self.stop).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
tk.Button(master, text=u'u275Au275A', command=self.pause).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
tk.Button(master, text=u'u25B6', command=self.resume).pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
def dispatchWords(self):
'''Dispatches the 'words()' method as a thread to free up the TkInter interface.'''
x = threading.Thread(target=self.GCIreland, daemon=True)
x.start()

def GCIreland(self):
'''Creates a list of words then sends them one at a time to the TTS talker.'''
words = ["one", "boy", "girl", "man", "woman", "two"]
for i in words:
talker.say(i)
def dispatchGordonFreeman(self):
'''Dispatches the 'gordonfreeman()' method as a thread to free up the TkInter interface.'''
x = threading.Thread(target=self.gordonFreeman, daemon=True)
x.start()

def gordonFreeman(self):
'''Creates a list with a well-known speech and hands it off to the Text-To-Speech talker.'''
speech = []
speech.append("Good morning, and welcome to the Black Mesa transit system.")
speech.append("This automated train is provided for the security and convenience of the Black Mesa Research Facility personnel.")
speech.append("The time is 8:47 A.M.")
speech.append("Current topside temperature is 93 degrees with an estimated high of 105.")
speech.append("The Black Mesa compound is maintained at a pleasant 68 degrees at all times.")
speech.append("This train is inbound from level 3 dormitories to sector C test labs and control facilities.")
speech.append("If your intended destination is a high security area beyond sector C, you will need to return to the central transit hub in area 9 and board a high security train.")
speech.append("If you have not yet submitted your identity to the retinal clearance system, you must report to Black Mesa personnel for processing before you will be permitted into the high security branch of the transit system.")
speech.append("Due to the high toxicity of material routinely handled in the Black Mesa compound, no smoking, eating, or drinking are permitted within the Black Mesa transit system.")
speech.append("Please keep your limbs inside the train at all times.")
speech.append("Do not attempt to open the doors until the train has come to a complete halt at the station platform.")
speech.append("In the event of an emergency, passengers are to remain seated and await further instruction.")
speech.append("If it is necessary to exit the train, disabled personnel should be evacuated first. Please, stay away from electrified rails and proceed to an emergency station until assistance arrives.")

for i in speech:
talker.say(i)

def stop(self):
'''Completely stops playback.  (Not implemented.)'''
pass

def resume(self):
'''Hopefully this resumes playback from whence it paused.'''
self.talker.resume()

def pause(self):
'''With any luck at all this will pause playback in a way that it can be resumed.  I dunno -- your guess is as good as mine!'''
self.talker.pause()

import pygame
import threading
from io import BytesIO
from gtts import gTTS
#create a custom TextToSpeechTalker class to convert text to speech and play it
#   through PyGame's mixer.  Really, the text-to-speech conversion should not
#   really be done in this class as it is unrelated to PyGame.
class TextToSpeechTalker():
def __init__(self, *, language="en", accent="com"):
'''Initializes the Google gTTS Text-To-Speech module.'''
self.language = language    #desired language
self.accent = accent        #desired locaization accent
self.phraseList = []        #a list to hold a bunch of text
self.threadRunning = False  #boolean flag True when thread is running
self.paused = False         #flag that knows if we have paused
pygame.mixer.init()         #initialize pygame's sound mixer
def say(self, text):
'''Converts text to speech, puts it in a buffer, then dispatches the talker
as a thread.'''
self.phraseList.append(text)    #append the text to the list
if not self.threadRunning:
self.dispatchTalkerThread() #dispatch a thread to "say" it

def dispatchTalkerThread(self):
'''Handles dispatching the talker method as a thread.'''
#don't actually need to dispatch it if it's already running
if not self.threadRunning:
self.threadTalker = threading.Thread(target=self.talk, daemon=True)
self.threadRunning = True
self.threadTalker.start()
self.threadTalker.join()    #wait for the thread to terminate
self.threadRunning = False

def talk(self):
'''This plays every entry in our list of text.  It is dispatched as a thread.'''
#this while-loop loops through all entries in the phraseList
while self.phraseList:      #boolean way of checking that there is something in the list
mp3_fp = BytesIO()          #stream buffer to hold mp3 data
phrase = gTTS(text=self.phraseList.pop(0), lang=self.language, tld=self.accent) #creates the TTS object
phrase.write_to_fp(mp3_fp)  #write binary data to the stream buffer
pygame.mixer.music.load(mp3_fp,"mp3")   #load the stream into mixer
pygame.mixer.music.play()               #start playing the mp3

#this is here to prevent multiple mp3s being played simultaneously
#it won't start playing the next buffer until the current one is complete
#   it just waits for the mixer to become not busy
while True:
pygame.time.wait(100)   #arbitrary delay (1/10th second to be nice)
pygame.event.clear()    #keep the event buffer clear for now
if not self.paused and not pygame.mixer.music.get_busy():
break

def pause(self):
'''Pauses the mixer output.'''
pygame.mixer.music.pause()      #pause playback
self.paused = True              #make sure we know we're paused

def resume(self):
'''Resumes mixer output.'''
pygame.mixer.music.unpause()
self.paused = False
if __name__ == "__main__":
#We're going to start by initializing PyGame
#   this should be done here and not in the talker object
pygame.init()
#Now initializing a talker object:
talker = TextToSpeechTalker(language='en', accent='com')    #creates an instance of the TextToSpeechTalker class
#Now we're going to create a TkInter window to start and control playback:
win=tk.Tk()                     #create a TkInter window
win.title("Play/Pause Test")    #put a title on it
PlayWindow(win, talker) #the window needs to know about the talker
#Everything should be ready to go at this point so we enter the TkInter mainloop
win.mainloop()

最新更新