Search code examples
pythonpyqtpyaudio

How to change string in setTitle for a generated text from outside class


When I try updating a string variable in self.graphWidget.setTitle(phrase, ...) the variable doesn't update. I'm plotting a real time waveform graph while recording from mic using pyaudio and PyQT, what I want to do is instead of printing in terminal I want the speech to text phrase to be shown in MainWindow after recognition is done


'''GUI'''
import struct
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication
import sys

'''Graph'''
import pyqtgraph as pg
from PyQt5 import QtCore
import numpy as np

'''Audio Processing'''
import pyaudio
import wave
import speech_recognition as sr
import multiprocessing as mlti

FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024 * 2

p = pyaudio.PyAudio()
stream = p.open(
    format=FORMAT,
    channels=CHANNELS,
    rate=RATE,
    input=True,
    output=True,
    frames_per_buffer=CHUNK,
    )

frames = []
seconds = 6
phrase = "..."

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        pg.setConfigOptions(antialias=True)
        self.traces = dict()

        '''Display'''
        self.graphWidget =  pg.PlotWidget()
        self.setCentralWidget(self.graphWidget)
        self.setWindowTitle("Waveform")
        self.setGeometry(55, 115, 970, 449)

        '''Data'''
        self.x = np.arange(0, 2 * CHUNK, 2)
        self.f = np.linspace(0, RATE // 2, CHUNK // 2)

        '''Animate'''
        self.timer = QtCore.QTimer()
        self.timer.setInterval(50)
        self.timer.timeout.connect(self.update)
        self.timer.start()  

    def set_plotdata(self, name, data_x, data_y):
        if name in self.traces:
            self.traces[name].setData(data_x, data_y)
        else:
            if name == 'waveform':
                self.traces[name] = self.graphWidget.plot(pen='c', width=3)
                self.graphWidget.setYRange(0, 255, padding=0)
                self.graphWidget.setXRange(0, 2 * CHUNK, padding=0.005)

    def update(self):

        self.wf_data = stream.read(CHUNK)
        self.wf_data = struct.unpack(str(2 * CHUNK) + 'B', self.wf_data)
        self.wf_data = np.array(self.wf_data, dtype='b')[::2] + 128
        self.set_plotdata(name='waveform', data_x=self.x, data_y=self.wf_data)

        self.graphWidget.setTitle(phrase, color="w", size="30pt")          #### Change it

def main():

        app = QtWidgets.QApplication(sys.argv)
        win = MainWindow()
        win.show()
        sys.exit(app.exec_())

def Record():
    for i in range(0, int(RATE/CHUNK*seconds)):
        data = stream.read(CHUNK)
        frames.append(data)
        print(i)

def Refine_Stream():
    stream.stop_stream()
    stream.close()
    p.terminate()

    obj = wave.open("output.wav", "wb")
    obj.setnchannels(CHANNELS)
    obj.setsampwidth(p.get_sample_size(FORMAT))
    obj.setframerate(RATE)
    obj.writeframes(b"".join(frames))
    obj.close()

def Speech_Recog():

        print("Function Started")

        r = sr.Recognizer()

        #usando o microfone
        with sr.AudioFile("output.wav") as source:

            r.adjust_for_ambient_noise(source, duration=1)

            #Armazena o que foi dito numa variavel
            audio = r.listen(source)
            
            phrase = ""

            try:
            #Into recog
                phrase = r.recognize_google(audio,language='pt-BR')          #### To it
                print(phrase)

            #Couldn't make it
            except sr.UnknownValueError:
                phrase = "Not understood"
                print(phrase)

if __name__ == '__main__':
    p1 = mlti.Process(target=main)
    p1.start()


    Record()

    Refine_Stream()

    Speech_Recog()

Hope it was clean code enough


Solution

  • I studied quite a lot and I figured out how to do exactly what I wanted

    I'm using a signal and slot system as my_signal = QtCore.pyqtSignal(str) to send my string-variable as a signal to setTitle that is activated using QTimer after some time

    class MainWindow(QtWidgets.QMainWindow):
    
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
    
            '''Display'''
            self.graphWidget =  pg.PlotWidget()
            self.setCentralWidget(self.graphWidget)
            self.setWindowTitle("Waveform")
            self.setGeometry(55, 115, 970, 449)
            self.graphWidget.setTitle(phrase, color="w", size="30pt")
    
            '''Animate Title'''
            self.picktimer = QTimer()
            self.picktimer.setInterval(7000)                        ###seconds needs to be more than seconds recording
            self.picktimer.timeout.connect(self.Transitioning)
            self.picktimer.start()
    
        def Transitioning(self):
    
            self.example_class = Speech_Recognition(self)
    
            self.example_class.my_signal.connect(self.Update_Title)
            self.example_class.Speech_Recog()                               ###init SpeechRecog
    
        def Update_Title(self, my_title_phrase):
    
            self.graphWidget.setTitle(my_title_phrase, color="w", size="30pt")
            QtWidgets.qApp.processEvents()
    
    def main():
    
            app = QtWidgets.QApplication(sys.argv)
            win = MainWindow()
            win.show()
            sys.exit(app.exec_())
    
    def Record():
        for i in range(0, int(RATE/CHUNK*seconds)):
            data = stream.read(CHUNK)
            frames.append(data)
            print(i)
    
    def Refine_Stream():
        stream.stop_stream()
        stream.close()
        p.terminate()
    
        obj = wave.open("output.wav", "wb")
        obj.setnchannels(CHANNELS)
        obj.setsampwidth(p.get_sample_size(FORMAT))
        obj.setframerate(RATE)
        obj.writeframes(b"".join(frames))
        obj.close()
    
    class Speech_Recognition(QtWidgets.QWidget):
        my_signal = QtCore.pyqtSignal(str)
    
        def Speech_Recog(self):
    
                r = sr.Recognizer()
    
                recorded_phrase = ""
    
                #usando o microfone
                with sr.AudioFile("output.wav") as source:
    
                    r.adjust_for_ambient_noise(source, duration=1)
    
                    #Armazena o que foi dito numa variavel
                    audio = r.listen(source)
    
                    try:
                        recorded_phrase = r.recognize_google(audio,language='pt-BR')
    
                    except sr.UnknownValueError:
                        recorded_phrase = "Not understood"
    
                self.my_signal.emit(recorded_phrase)
    
    if __name__ == '__main__':
    
        p2 = mlti.Process(target=main)
        p2.start()
    
        Record()
    
        Refine_Stream()
    
    

    Still needs to be clean code though