0

Is there any way to pause , resume and stop pyttsx3 from speaking?

I've tried many ways but I couldn't find a solution.

Here's the code:

from tkinter import *
import pyttsx3
import threading

root = Tk()

def read():
    engine.say(text.get(1.0 , END))
    engine.runAndWait()

def stop():
    # Code to stop pyttsx3 from speaking
    pass

def pause():
    # Code to pause pyttsx3
    pass

def unpause():
    # Code to unpause pyttsx3
    pass

engine = pyttsx3.init()

text = Text(width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert(END , "This is a text widget\n"*10)

read_button = Button(root , text = "Read aloud" , command = lambda: threading.Thread(target=read, daemon=True).start())
read_button.pack(pady = 20)

pause_button = Button(root , text = "Pause" , command = lambda: threading.Thread(target=pause , daemon = True).start())
pause_button.pack()

unpause_button = Button(root , text = "Unpause" , command = unpause)
unpause_button.pack(pady = 20)

stop_button = Button(root , text = "Stop" , command = threading.Thread(target=stop, daemon = True).start())
stop_button.pack()

mainloop()

What I want is to pause, resume, and stop pyttsx3 whenever I want by clicking the buttons.

Is there any way to achieve this in tkinter?

It would be great if anyone could help me out.

Lenovo 360
  • 569
  • 1
  • 7
  • 27

2 Answers2

1

There is no pause and resume function provided by pyttsx3, but you can simulate these feature by splitting the sentence into words, and then speak word by word. In between words, you can check whether pause, unpause or stop button is clicked and do corresponding action:

from tkinter import *
import pyttsx3
import threading

root = Tk()

class Speaking(threading.Thread):
    def __init__(self, sentence, **kw):
        super().__init__(**kw)
        self.words = sentence.split()
        self.paused = False

    def run(self):
        self.running = True
        while self.words and self.running:
            if not self.paused:
                word = self.words.pop(0)
                print(word)
                engine.say(word)
                engine.runAndWait()
        print("finished")
        self.running = False

    def stop(self):
        self.running = False

    def pause(self):
        self.paused = True

    def resume(self):
        self.paused = False

speak = None

def read():
    global speak
    if speak is None or not speak.running:
        speak = Speaking(text.get(1.0, END), daemon=True)
        speak.start()

def stop():
    global speak
    if speak:
        speak.stop()
        speak = None

def pause():
    if speak:
        speak.pause()

def unpause():
    if speak:
        speak.resume()

engine = pyttsx3.init()

text = Text(width=65, height=20, font="consolas 14")
text.pack()

text.insert(END, "This is a text widget\n"*10)

read_button = Button(root, text="Read aloud", command=read)
read_button.pack(pady=20)

pause_button = Button(root, text="Pause", command=pause)
pause_button.pack()

unpause_button = Button(root, text="Unpause", command=unpause)
unpause_button.pack(pady=20)

stop_button = Button(root, text="Stop", command=stop)
stop_button.pack()

mainloop()

Update: Using pygame as the player:

from tkinter import *
import pyttsx3
import pygame

pygame.mixer.init()
engine = pyttsx3.init()

root = Tk()

def read():
    outfile = "temp.wav"
    engine.save_to_file(text.get('1.0', END), outfile)
    engine.runAndWait()
    pygame.mixer.music.load(outfile)
    pygame.mixer.music.play()

def stop():
    pygame.mixer.music.stop()

def pause():
    pygame.mixer.music.pause()

def unpause():
    pygame.mixer.music.unpause()


text = Text(width=65, height=20, font="consolas 14")
text.pack()

text.insert(END, "This is a text widget\n"*10)

read_button = Button(root, text="Read aloud", command=read)
read_button.pack(pady=20)

pause_button = Button(root, text="Pause", command=pause)
pause_button.pack()

unpause_button = Button(root, text="Unpause", command=unpause)
unpause_button.pack(pady=20)

stop_button = Button(root, text="Stop", command=stop)
stop_button.pack()

mainloop()
acw1668
  • 40,144
  • 5
  • 22
  • 34
  • This solved my problem , but pyttsx3 is reading too slowly and there is a lot of gap between 2 words. Is there any way to shorten the gap between two words? I tried decreasing the gap by engine.set_property('rate' , 400) , but there is still a lot of gap. – Lenovo 360 Jan 15 '21 at 05:51
  • Setting the `rate` property is the only way, I think. – acw1668 Jan 15 '21 at 06:01
  • When I set the rate property, only the speed increases and the gap stays the same. – Lenovo 360 Jan 15 '21 at 06:16
  • The gap may be caused by `engine.runAndWait()` which I can do nothing with it. – acw1668 Jan 15 '21 at 06:24
  • May be you can try split on lines instead of words: `self.words = sentence.splitlines()`. – acw1668 Jan 15 '21 at 07:01
  • This is a good idea , but when I click the pause button , pyttsx3 stops only after it is done speaking the sentence. – Lenovo 360 Jan 15 '21 at 07:09
  • If split by lines, then it will be paused only the current line is finished. As I said there is no pause feature from `pyttsx3`, so you cannot pause when it is speaking. – acw1668 Jan 15 '21 at 07:18
  • @Lenovo360 Updated answer which uses `pygame` as the player. – acw1668 Jan 15 '21 at 07:36
  • Oh thanks a lot! This method is way better. – Lenovo 360 Jan 15 '21 at 14:44
0

using pygame and pyttsx3 without tkinter

from pygame import mixer
import pyttsx3
engine = pyttsx3.init()
  
say = 'getting details of current voice'
voices = engine.getProperty('voices')      
# engine.setProperty('volume',1.0)    
engine.setProperty('voice', voices[1].id)  
engine.setProperty('rate', 200)     # setting up new voice rate
outfile = "temp.wav"
engine.save_to_file(say, outfile)
engine.runAndWait()
  
mixer.init()
mixer.music.load("temp.wav")
mixer.music.play()


def stop():
    mixer.music.stop()

def pause():
    mixer.music.pause()

def unpause():
    mixer.music.unpause()

 
while True:
      
    print("Press 'p' to pause, 'r' to resume")
    print("Press 'e' to exit the program")
    query = input("  ")
      
    if query == 'p':
        pause() 
    elif query == 'r':
        unpause() 
  
    elif query == 'e':
        mixer.music.stop()
        break