I'm running a Python application under windows, and I want to make use
of the Microsoft Speech API to sythesize Text-To-Speech output, without
halting the execution of my program.
I'm making my calls like this:
(intialization)
from win32com.client import constants
import win32com.client
self.speech = win32com.client.Dispatch("SAPI.SpVoice")
(during execution)
self.speech.Speak(text)
I'm not very familiar with win32com calls, but I'm wondering if there's
an easy to to either:
1) Make the call such that the process occurs in the background, and
application execution continues to flow
2) Make the call inside a thread which produces the same effect.
Can anyone please offer me some tips/sample code to achieve this?
Thanks in advance.
Cheers,
Steve Castellotti
SteveC (at) innocent.com
http://cogengine.sourceforge.net/
This might work (I don't know anything about COM though). Try putting
your own code inside the run method of a thread:
import threading
class Background(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print 'Starting'
pass # your own code goes here
print 'Done'
>>> b = Background()
>>> b.start()
Starting
<possibly a pause here?>
Done
If at this point you get a >>> prompt back right away, but the
speech generation is carrying on "in the background" then you're
all set. Make sure you do this with code you've already tested
outside of the thread, so you'll know that any failure is not
the fault of your own code...
-Peter
David LeBlanc
Seattle, WA USA
Just for the record, here's what the full implementation looks like.
This code allows text to be piped to Microsoft's Text-To-Speech
sythesizer, spoken in the background, in a thread-safe manner:
#####################################################################
import threading
class Background_TTS(threading.Thread):
def __init__(self, debug_mode):
threading.Thread.__init__(self)
self.debug_mode = debug_mode
self.loop = 1
import Queue
self.text_queue = Queue.Queue()
#####################################################################
def Speak(self, text):
self.text_queue.put(text)
#####################################################################
def stop(self):
self.loop = 0
#####################################################################
def run(self):
from win32com.client import constants
import win32com.client
import time
speech = win32com.client.Dispatch("SAPI.SpVoice")
while self.loop:
if not(self.text_queue.empty()):
text = self.text_queue.get()
speech.Speak(text)
time.sleep(.001)
#####################################################################
initialization looks like this:
try:
self.speech = Background_TTS(self.gameInformation.debug_mode)
self.speech.start()
except:
if (self.gameInformation.debug_mode):
print "Error Initializing Microsoft Speech API."
else:
self.text_to_speech_enabled = 1
#####################################################################
calling the synthesizer:
self.speech.Speak(text)
#####################################################################
and finally, stopping the process:
self.speech.stop()
Once again guys, thanks!
-Steve Castellotti
SteveC (at) innocent.com
http://cogengine.sourceforge.net/
Beautiful... thanks for the followup Steven. I especially like
your use of deferred imports to speed up initial program loading.
[...]
> if not(self.text_queue.empty()):
> text = self.text_queue.get()
> speech.Speak(text)
> time.sleep(.001)
Note that the time.sleep() as shown above is doing nothing
useful for you. I suspect you had it unindented but your
posting probably used tabs and spaces which makes it hard
to see properly on some readers.
The reason it does nothing useful is that Python will
automatically switch between different threads every so
often anyway, so the concept of giving up the CPU for
what amounts to a tiny fraction of a second is somewhat
unnecessary.
-Peter
Would this do as well?:
#done gets set by the other thread.
while not done:
# block here until queue has content
text = self.text_queue.get()
speech.Speak(text)
#done became true
#do clean up/exit stuff
David LeBlanc
Seattle, WA USA
> -----Original Message-----
> From: python-l...@python.org
> [mailto:python-l...@python.org]On Behalf Of Peter Hansen
> Sent: Thursday, June 20, 2002 18:23
> To: pytho...@python.org
> Subject: Re: Dispatching a Win32 COM in the background?
>
>
If Background_TTS is the background speacher thread, shouldn't it have the
queue.get() in it and the foreground have the queue.put()?
It's also worth noting that self isn't used or needed at the global level,
so self.speech is kind of different ;-)
Actually... tell me how this works :) :
import threading
# Private class
class _speech_processor(threading.Thread):
def __init__(self, speech_queue, talker):
threading.Thread.__init__(self)
self._talk_queue = speech_queue
self._talker = talker
def run(self):
tq = self._talk_queue # tiny speed up?
talker = self._talker
while 1: # the main thread will kill us.
talker.Speak(tq.get())
# end of _speech_processor
class BackgroundSpeaker():
def __init__(self, debug_mode):
self._debug_mode = debug_mode
self._bg = None
self._speech_queue = None
self._talker = None
self._statusOK = 1 # Must be before try/excepts. Success unless failure!
try:
from win32com.client import constants # I think this is redundant
import win32com.client # since you'll get the constants here too
except ImportError:
if (self._debug_mode):
print "Unable to import Win32COM extensions - are they installed?"
self._statusOK = 0
try:
self._talker = win32com.client.Dispatch("SAPI.SpVoice")
except:
if (self._debug_mode):
print "Error Initializing Microsoft Speech API."
self._statusOK = 0
# leading '_' means private method (by agreement only - not enforced)
def _start_bg(self):
import Queue
self._speech_queue = Queue.Queue()
self._bg = _speech_processor(self._speech_queue, self._talker)
self._bg.start()
def say(self, speech):
if not self._statusOK:
if self._debug_mode:
print "Unable to say: %s." % speech, "Components not installed."
return
if self._bg is None:
self._start_bg()
self._speech_queue.put(speech)
# end of BackgroundSpeaker
if __name__ == '__main__'
import time
import sys
debug_mode = 1
Speaker = BackgroundSpeaker(debug_mode) # It always creates, even if it
can't talk.
while 1:
speech = raw_input("Enter a phrase to speak, then hit Enter")
if speech == "":
Speaker.say("All done")
time.sleep(20) # give some time for finishing up.
sys.exit()
Speaker.say(speech)
I would be interested in hearing if this works! It may have typos since I
did it all manually. Don't have the speech kit to test with.
HTH,
I'm pretty sure from previous postings he was going to call this from
within another object, so the "self." used elsewhere might refer to it.
As near as I can tell, with that in mind, the .get() and .put() are in
exactly the right places.
-Peter
> "Steven M. Castellotti" wrote:
>>
>> Thanks a lot guys, your suggestions have worked perfectly!
>>
>> Just for the record, here's what the full implementation looks
>> like.
>
> Beautiful... thanks for the followup Steven. I especially like your use
> of deferred imports to speed up initial program loading.
Thank you for the compliment. Speeding up program loading by deferring
imports is really only a side bonus though. The program is actually
cross-platfrom, meant to be run under both Linux and Windows. Since I'm
making calls to the Festival TTS program under linux, I have a seperate
class object, depending on what gets returned by os.name.
> [...]
>> if not(self.text_queue.empty()):
>> text = self.text_queue.get()
>> speech.Speak(text)
>> time.sleep(.001)
>
> Note that the time.sleep() as shown above is doing nothing useful for
> you. I suspect you had it unindented but your posting probably used
> tabs and spaces which makes it hard to see properly on some readers.
>
> The reason it does nothing useful is that Python will automatically
> switch between different threads every so often anyway, so the concept
> of giving up the CPU for what amounts to a tiny fraction of a second is
> somewhat unnecessary.
That makes sense to me, I think I'll remove the time.sleep() call. The
reason I had it in there in the first place (and it was unindented) was
because I have a similar call in a loop to Pygame (a wrapper to the SDL
graphics library), which is not multithreaded. I needed the calls in the
Pygame loop in order to prevent CPU usage from sitting at 100%, but if
multithreading will happen properly without it, I won't use it.
Cheers,
That's right, this object is getting called from another class object.
All of this is part of a GPL'd program that allows children to create
their own video games using drawings and pictures, without them having to
learn how program.
Since the program runs under Linux and Windows, there's separate
objects loaded for each enviroment, both having a reference set to
"self.speech" in order to abstract the exact implementation away from
other parts of the program.
Cheers,