Multithreading streamlit

I was looking simple solution where I can stream text and simultaneously play voice(text to speech) in the background.

from threading import Thread
def speech_thread(text):
    """Run TTS in a separate thread."""
    engine = pyTextToSpeech()
    tts_thread = Thread(target=engine.text_to_speech, args=(text,))
    tts_thread.start()
    print("TTS thread started")


def response_generator(response:str ):
        speech_thread(response)
        for word in response.split():
            yield word + " "
            time.sleep(0.2)

The first function speech_thread creates a secondary thread using pythonā€™s thread() function, thread take two arguments ā€˜targetā€™ which is the function you want to run on that thread in my case ā€˜text_to_speech()ā€™ and ā€˜argsā€™ which goes into target function which is ā€˜textā€™ in text_to_speech().

The second function response_generator() is streamimg function, simply takes string as input and streams it to the streamlit web app, and before the streaming loop we call our speech_thread() which generates speech along the text stream.

2 Likes

nice! great share, thanks; gonna test it out with one of the things iā€™m working on!

Hi @Jagpreet - Which Python TTS engine are you using?

Hi @asehmi I am using pyttsx3

find the code below

class pyTextToSpeech():

    def __init__(self) -> None:
        self.engine = pyttsx3.init()
        self.voices = self.engine.getProperty('voices')
        self.rate = self.engine.getProperty('rate')
        self.engine.setProperty('rate', self.rate-20)
       

    def text_to_speech(self, text:str, voice:str = None):
            # self.engine.setProperty('voice', voice)
            self.engine.say(text)
            self.engine.runAndWait()
            return

Right, I did something very similar, but only saw the word by word text loaded into Streamlit after the TTS was completed. I was expecting when you said ā€œspeech_thread() which generates speech along the text streamā€ that the printed words and voiced words would be almost in sync.

pyttsx3 is not thread safe, hence the run and wait, so Iā€™m not sure itā€™s a good idea to use it in a thread in Streamlit. It also blue-screened my PC when I got it to voice a long piece of text. There are reports on the web that it does that. I found a pyttsx4 which has additional drivers, but my brief research suggested using gTTS or a Hugging Face transformer TTS model.

Anyway, thanks for getting back to me.

the simultaneous stream and speech is working for me, but yeah I am also figuring out to get it synched, One way is to adjust the sleep counter in response_generator function but require trial and error.

I was thinking write it as an python async function but read somewhere that streamlit does not support async.

will share once i have some solution.

ps : was using gtts, but it does not support much configurations so droped it

class googleTextToSpeech():

    def text_to_speech(self, text:str):
        '''
        Function to convert text to speech using Google Text-to-Speech (gTTS) API.
        The function saves the audio file as MP3 and then converts it to WAV format.
        The audio file is then played using simpleaudio package.

        Parameters:
        text (str): The text to be converted to speech.

        Returns:
        None
        '''
        # Convert the text to speech
        tts = gTTS(text=text, lang='en', speed=1.5)
        audio_data = BytesIO()
        tts.write_to_fp(audio_data)
        audio_data.seek(0)

        # Convert MP3 file to WAV format
        audio = AudioSegment.from_file(audio_data, format="mp3")
        play(audio) 
        # return audio_data
        return

Thanks. Not sure gTTS will stream the word segments, so syncing words and text, as you see in Karaoke or Spotify lyrics, will require other techniques.

Iā€™ve done a lot of research on using asyncio with Streamlit, so might be worth writing it up. It is possible, and not too hard! Iā€™ve built a commercial app using asyncio in Streamlit. Thereā€™s very little info about this (that I could find) specifically for Streamlit, especially in this forum. Some small solutions exist, but with no explanations or a complete Streamlit app showing how to do it. The main ideas on how to get it to work come from the Jupyter world, so you can start there.

FYI - I wrote that article after all: Got that asyncio feeling? How to run async code in Streamlit