Secondary thread blocking the main thread?

Hello everyone!

I’ve been using Panda3D for some time now, specially for small programs and demos. Thanks to the good documentation and this great forum all my doubts have been solved till now.

But I’ve finally started developing a big application and now there is an issue which is keeping me stuck for too much time I think… :S I would appreciate so much if somebody could give me some clue about it:

This application is about 3D talking interactive historical characters, and I’m using a Text-to-Speech engine to make them speak. I’m currently working on Windows, with the pyspeech module (code.google.com/p/pyspeech/) to drive the synthesized voice.

And the problem is: when you call the pyspeech say function (which is a synchronous function that starts the TTS system to play a human voice), the current thread gets blocked until this function ends playing the speech and so returns. This, obviously means that my whole Panda application hangs until that function returns… and all my headache is how can I make it to call this function somehow in a separate thread that prevents the main thread from blocking.

I’ve been trying with a threaded task chain but it apparently doesn’t make any difference… I don’t know if I’m missing something or there is some better way to do it. Here is what I’m doing:

taskMgr.setupTaskChain('SpeechChain', numThreads=1, threadPriority=-1)
...
taskMgr.add(speakFunction, 'speakingTask', taskChain='SpeechChain', extraArgs=[phraseToBeSpoken])
...
def speakFunction(self,phrase):
 speech.say(phrase)
 return Task.done

Do you know why the main thread is blocking until this call to the speakFunction returns? I’m not sure about the values I can pass to the threadPriority parameter, and if they should have some effect on this… or if there is some other way to force the main thread loop execution by increasing the main thread priority…

Any help will be so much appreciated!

Thanks!!! :wink:
Dani

Hi again :wink:

Just to add some extra info: I’ve just tried also to do it with a custom thread (inheriting stdpy.threading.Thread), and the result is the same: when this custom thread runs the speech method, the whole panda application hangs until the method returns…

And as I can see it’s not an issue with that speech function I’m using, since I’ve also tried calling a dummy processor-intensive function (which just iterates over a very long list), and it results in the same general blocking (so the application hangs till this method returns)… so it clearly looks like any thread created in python acts absolutely greedy, is that right?

The bad news for me is that since my speech long-lasting method does not contain any iteration, i can’t call any yielding method during its execution… so, is there something I can do to prevent this second thread to consume all processing resources once started?

Thanks in advance!
Dani

Panda threads by default are cooperative, so yes, unless you explicitly call Thread.considerYield() in your threaded code, it will be greedy. Of course you can’t do this in the third-party library for tts.

But the third-party library also uses standard threads, not Panda threads, which means it’s completely incompatible with Panda. That’s OK, it just means that (a) you have to be absolutely sure that your tts thread never makes a call to any Panda object at all, including simple objects like Vec3’s, and (b) you use standard Python threads to create it (e.g. the threading library) instead of Panda threads (the stdpy.threading library).

If you do this, you should be all right.

David

Hei thanks for your quick response! :slight_smile:

Ok then, so I’ll try making a standard python thread and tomorrow I’ll post what happened (no time to try it right now! :frowning: )

Thanks!!!
Dani

Ok that’s done!

Thank you very much David! The TTS process runs perfectly in parallel with my panda code when called from a python thread! (I did not dare to try it by myself since I read in the documentation that it was not advisable to use python threads from panda).

As simple as this:

from threading import Thread

class VoiceThread(Thread):

    def __init__(self,voiceMgr,text):
        Thread.__init__(self)
        self.setDaemon(True)
        self.mgr= voiceMgr
        self.phrase= text

    def run(self):
        speech.say(self.phrase)
        self.mgr.notifyPhraseEnded()

I assume it is not dangerous to call that mgr.notifyPhraseEnded() from this thread since this mgr does not extend any Panda class and it just changes a flag and returns… As far as I’ve tried it it works fine :wink:

See ya!
Dani

Sounds reasonable. :slight_smile:

David