0

In my program I need class(which can be some thread) to check some list like "say_list" and when other classes add some text to it, pyttsx say that text. I search in pyttsx docs and I find some external loops feature but I can not find example which work correctly. I want something like this:

import pyttsx
import threading

class VoiceAssistant(threading.Thread):
    def __init__(self):
        super(VoiceAssistant, self).__init__()
        self.engine = pyttsx.init()
        self.say_list = []

    def add_say(self, msg):
        self.say_list.append(msg)

    def run(self):
        while True:
            if len(self.say_list) > 0:
                self.engine.say(self.say_list[0])
                self.say_list.remove(self.say_list[0])


if __name__ == '__main__':
    va = VoiceAssistant()
    va.start()

Thanks.

Marcus Höglund
  • 16,172
  • 11
  • 47
  • 69
destrat18
  • 197
  • 2
  • 14

2 Answers2

3

I can get the proper results by using python's built in Queue class:

import pyttsx
from Queue import Queue
from threading import Thread

q = Queue()

def say_loop():
    engine = pyttsx.init()
    while True:
        engine.say(q.get())
        engine.runAndWait()
        q.task_done()

def another_method():
    t = Thread(target=say_loop)
    t.daemon = True
    t.start()
    for i in range(0, 3):
        q.put('Sally sells seashells by the seashore.')
    print "end of another method..."

def third_method():
    q.put('Something Something Something')

if __name__=="__main__":
    another_method()
    third_method()
    q.join() # ends the loop when queue is empty

Above is a simple example I whipped up. It uses the 'queue/consumer' model to allow separate functions/classes to access the same queue, then a worker that will execute anytime the queue has items. Should be pretty easy to adapt to your needs.

Further reading about Queues: https://docs.python.org/2/library/queue.html There appears to be an interface for this in the docs you linked to, but it seemed like you were already on the separate thread track so this seemed closer to what you wanted.

And here's the modified version of your code:

import pyttsx
from Queue import Queue
import threading

class VoiceAssistant(threading.Thread):
    def __init__(self):
        super(VoiceAssistant, self).__init__()
        self.engine = pyttsx.init()
        self.q = Queue()
        self.daemon = True

    def add_say(self, msg):
        self.q.put(msg)

    def run(self):
        while True:
            self.engine.say(self.q.get())
            self.engine.runAndWait()
            self.q.task_done()


if __name__ == '__main__':
    va = VoiceAssistant()
    va.start()
    for i in range(0, 3):
        va.add_say('Sally sells seashells by the seashore.')
    print "now we want to exit..."
    va.q.join() # ends the loop when queue is empty
shark3y
  • 166
  • 6
  • thanks. it helps alot. but second code is not worked but first one work correctly – destrat18 Aug 22 '16 at 11:37
  • 1
    What seems to be the problem with the second one? It runs fine for me. – shark3y Aug 23 '16 at 04:08
  • i do not know. when i run it a pyttsx don't say any thing. first one worked and and it help me a lot. so i leave second one :D. i wish u understand my words with my bad english – destrat18 Aug 28 '16 at 14:33
  • Note: I'm having problems with runAndWait() deadlocking (when running on Windows 10, Python 3.6 and using [jpercent/pyttsx](https://github.com/jpercent/pyttsx?files=1)'s fork. (Print statements and Queue->queue changes also required) – Oddthinking May 01 '17 at 14:04
0

pyttsx3 loop pronunciation

import pyttsx3

voice_text = 'test'

def loopSound():
    def onEnd(name, completed):
        print('finishing', name, completed)
        engine.say(voice_text, 'voice_a')

    engine = pyttsx3.init()
    engine.connect('finished-utterance', onEnd)
    engine.say(voice_text, 'voice_e')
    engine.startLoop()

loopSound()
dong
  • 51
  • 6