Commits

Anonymous committed 49aed0c

New Qt/Phonon based implementation. Audio server was replaced by Qt's Phonon.

  • Participants
  • Parent commits 97d7012
  • Branches Qt

Comments (0)

Files changed (8)

File AudioServer.py

-import pyaudio
-import wave
-from time import sleep
-
-class AudioServer:
-    """A class to manage wav file playing. Most of the code came straight from the play_wav example."""
-    CHUNK = 1024
-    def __init__(self):
-        self.p = pyaudio.PyAudio()
-
-    def play_wave(self, filepath):
-        wf = wave.open("%s" % filepath, 'rb')
-        # open stream
-        stream = self.p.open(format=self.p.get_format_from_width(wf.getsampwidth()),
-                        channels=wf.getnchannels(),
-                        rate=wf.getframerate(),
-                        output=True)
-        # read data
-        data = wf.readframes(self.CHUNK)
-        # play stream
-        while data != '':
-            stream.write(data)
-            data = wf.readframes(self.CHUNK)
-        sleep(0.5) # hack to avoid end-of-file clipping
-        stream.stop_stream()
-        stream.close()
-
-    def close(self):
-        self.p.terminate()

File GSTServer.py

-from gi.repository import Gst, GObject
-from engine import Engine
-
-class GSTServer:
-    """ GStreamer-based player 
-    (FIXME: engine.py from the wolfgang player should be used as a reference for this class, instead of just used as is). """
-    def __init__(self):
-        GObject.threads_init()
-        self.engine = Engine()
-
-    def play_wave(self, filepath):
-        if self.engine.is_playing:
-            print "Engine state being set to stop"
-            self.engine.stop()
-        self.engine.play(Gst.filename_to_uri(filepath))

File MainInterface.py

-from Tkinter import *
-import tkFont
-#from AudioServer import AudioServer
-from TestController import TestController
-import string, platform, TkSimpleDialog
-from time import sleep
-
-class MainInterface(Frame):
-
-    def play(self, event):
-        self.ctrl.save_result(self.input.get())
-        self.clear(None)
-        try:
-            sample, n = self.ctrl.next_sample()
-            self.master.title('Sample %s' % n)
-            self.master.update()
-            sleep(1)
-            self.audio_srv.play_wave(sample)
-            self.clear(None)
-        except IndexError:
-            self.entryval.set('End of experiment, you can close the application.')
-            self.input['state'] = DISABLED
-
-    def show_entry(self, event):
-        print(self.input.get())
-
-    def clear(self, event):
-        self.entryval.set('')
-        self.master.update()
-
-    def create_widgets(self):
-        self.font = tkFont.Font(family="Arial", size=14)
-
-        self.entryval = StringVar()
-        self.entryval.set('Press "Enter" to begin.')
-        
-        self.input = Entry(self, font=self.font)
-        self.input["textvariable"] = self.entryval
-        self.input.bind('<Return>', self.play)
-        self.input.bind('<Escape>', self.clear)
-        self.input.config(width = 60)
-        
-        self.input.pack({"side": "left"})
-
-        self.master.resizable(0,0)
-
-    def __init__(self, master=None):
-        Frame.__init__(self, master)
-        self.pack()
-        self.create_widgets()
-        id = ParticipantDialog(master).result
-        print "Participant id: %d" % id
-        if platform.system() is 'Windows':
-            print "Using WinAudioServer"
-            import WinAudioServer
-            self.audio_srv = WinAudioServer.WinAudioServer()
-        else:
-            #print "Using AudioServer (based on PortAudio/pyAudio)"
-            #import AudioServer
-            #self.audio_srv = AudioServer.AudioServer()
-            from GSTServer import GSTServer
-            self.audio_srv = GSTServer()
-        self.ctrl = TestController(participant_id = id)
-
-class ParticipantDialog(TkSimpleDialog.Dialog):
-
-    def body(self, master):
-        Label(master, text="Participant ID:").grid(row=0)
-
-        self.e1 = Entry(master)
-        self.e1.bind('<Return>', self.ok)
-        
-        self.e1.grid(row=0, column=1)
-        return self.e1 # initial focus
-
-    def apply(self):
-        self.result = string.atoi(self.e1.get())
-
-root = Tk()
-app = MainInterface(master=root)
-app.master.title("Speech Intelligibility Test")
-app.mainloop()

File MainInterfaceGtk.py

-from gi.repository import Gtk, GObject
-from TestController import TestController
-import string, platform
-from time import sleep
-from os import path
-
-class MainInterfaceGtk():
-
-    def play(self, source):
-        """ Fetch next sentence from the TestController and play it."""
-        if source is not self.next_btn:
-            self.ctrl.save_result(self.sentence_entry.get_text())
-        self.clear()
-        try:
-            sample, n = self.ctrl.next_sample()
-            self.entry_window.set_title('Sample %s' % n)
-            sleep(1)
-            self.sentence_entry.set_sensitive(False)
-            self.audio_srv.play_wave(sample)
-            self.clear()
-        except IndexError:
-            self.sentence_entry.set_text("This was the last sample.")
-
-    def clear(self):
-        """ Clears the entry field for inputting the new sentence."""
-        self.sentence_entry.set_text("")
-
-    def create_widgets(self):
-        """ Load refs from UI file and initialize the GUI"""
-        self.builder = Gtk.Builder()
-        self.builder.add_from_file(path.join(path.curdir, "main.ui"))
-        self.builder.connect_signals(self)
-        self.entry_window = self.builder.get_object("entry_window")
-        self.entry_window.connect("destroy", self.close)
-        self.sentence_entry = self.builder.get_object("sentence_entry")
-        self.sentence_entry.connect("activate", self.play)
-        self.sentence_entry.set_sensitive(False)
-        self.next_btn = self.builder.get_object("next_btn")
-        self.next_btn.connect("clicked", self.start)
-        
-    def start(self, source):
-        self.next_btn.set_visible(False)
-        self.play(self.next_btn)
-
-    def close(self, source):
-        Gtk.main_quit()
-
-    def __init__(self):
-        self.create_widgets()
-        participant_dlg = ParticipantDialog(self)
-        (name, pid) = participant_dlg.run()
-        print "Participant %s, id: %d" % (name, pid)
-        if platform.system() is 'Windows':
-            print "Using WinAudioServer"
-            import WinAudioServer
-            self.audio_srv = WinAudioServer.WinAudioServer()
-        else:
-            from GSTServer import GSTServer
-            self.audio_srv = GSTServer()
-            self.audio_srv.engine.connect("end_of_stream", self.about_to_finish)
-        self.ctrl = TestController(participant_id = pid)
-        self.entry_window.show_all()
-
-    def about_to_finish(self, source):
-        """ When we receive the end_of_stream signal, set entry as
-        editable again."""
-        print "Received end_of_stream signal"
-        self.sentence_entry.set_sensitive(True)
-        self.entry_window.set_focus(self.sentence_entry)
-
-class ParticipantDialog():
-
-    def __init__(self, master):
-        self.dialog = master.builder.get_object("participant_dialog")
-        self.name_entry = master.builder.get_object("name_entry")
-        self.pid_entry = master.builder.get_object("pid_entry")
-        self.dialog.connect("destroy", self.dialog.destroy)
-    
-    def run(self):
-        self.dialog.show_all()
-        response = self.dialog.run()
-        data = (self.name_entry.get_text(), string.atoi(self.pid_entry.get_text()))
-        self.dialog.destroy()
-        return data
-
-app = MainInterfaceGtk()
-Gtk.main()

File MainInterfaceQt.py

+from PySide import QtGui, QtCore
+from PySide.phonon import Phonon
+from TestController import TestController
+
+class MainInterfaceQt(QtGui.QWidget):
+    def __init__(self):
+        QtGui.QWidget.__init__(self)
+        self.setWindowTitle('Speech Intelligibility Test')
+        self.media = Phonon.MediaObject(self)
+        self.audio = Phonon.AudioOutput(Phonon.MusicCategory, self)
+        Phonon.createPath(self.media, self.audio)
+        self.buttonStart = QtGui.QPushButton('Start', self)
+        self.slider = Phonon.VolumeSlider(self)
+        self.slider.setAudioOutput(self.audio)
+        self.entry = QtGui.QLineEdit()
+        layout = QtGui.QGridLayout(self)
+        layout.addWidget(self.entry, 0, 0, 1, 2)
+        layout.addWidget(self.buttonStart, 1, 0)
+        layout.addWidget(self.slider, 2, 0, 1, 2)
+        layout.setRowStretch(0, 1)
+        self.media.stateChanged.connect(self.handleStateChanged)
+        self.media.finished.connect(self.handleSampleFinished)
+        self.buttonStart.clicked.connect(self.handleButtonStart)
+        self.entry.returnPressed.connect(self.handleSaveEntry)
+
+    def showEvent(self, event):
+        pid, ok = QtGui.QInputDialog.getInt(self, "Setup", "Participant ID", minValue=0)
+        if ok:
+            self.ctrl = TestController(participant_id = pid)
+        else:
+            print "Not ok"
+            QtGui.QApplication.instance().closeAllWindows()
+
+    def updateAndPlayNext(self):
+        try:
+            path, n = self.ctrl.next_sample()
+            self.media.setCurrentSource(Phonon.MediaSource(path))
+            self.setWindowTitle('Sample %s' % n)
+            self.media.play()
+        except IndexError:
+            self.entry.setText("This was the last sample.")
+            self.entry.setEnabled(False)
+            self.buttonStart.setEnabled(False)
+
+    def handleButtonStart(self):
+        self.updateAndPlayNext()
+        self.buttonStart.setText("Next")
+        self.buttonStart.clicked.disconnect(self.handleButtonStart)
+        self.buttonStart.clicked.connect(self.handleSaveEntry)
+
+    def handleStateChanged(self, newstate, oldstate):
+        if newstate == Phonon.PlayingState:
+            self.entry.setText('')
+            self.entry.setEnabled(False)
+            self.buttonStart.setEnabled(False)
+        elif (newstate != Phonon.LoadingState and
+              newstate != Phonon.BufferingState):
+            if newstate == Phonon.StoppedState:
+                self.entry.setReadOnly(False)
+            if newstate == Phonon.ErrorState:
+                source = self.media.currentSource().fileName()
+                print ('ERROR: could not play: %s' % source)
+                print ('  %s' % self.media.errorString())
+
+    def handleSampleFinished(self):
+        self.entry.setEnabled(True)
+        self.buttonStart.setEnabled(True)
+        self.entry.setFocus()
+
+    def handleSaveEntry(self):
+        self.ctrl.save_result(self.entry.text())
+        self.updateAndPlayNext()
+
+if __name__ == '__main__':
+
+    import sys
+    app = QtGui.QApplication(sys.argv)
+    app.setApplicationName('Speech Intelligibility Test')
+    window = MainInterfaceQt()
+#    window.show()
+    sys.exit(app.exec_())

File WinAudioServer.py

-import winsound
-
-class WinAudioServer:
-    """Using winsound instead of PyAudio on Windows as it is simpler."""
-    
-    def __init__(self):
-        pass
-
-    def play_wave(self, filepath):
-        winsound.PlaySound(filepath, winsound.SND_FILENAME)
-
-    def close(self):
-        pass
+[setup]
+sample_dir = path/to/wav/files
+train = lists/train.txt
+lists =
+       lists/N_SNR_-5_dB.txt
+;      lists/N_SNR_0_dB.txt
+;      lists/N_SNR_5_dB.txt
+;      lists/N_SNR_10_dB.txt
+;      lists/R_T60_300_ms.txt
+;      lists/R_T60_600_ms.txt
+;      lists/R_T60_800_ms.txt
+;      lists/R_T60_1000_ms.txt
+;      lists/R+N_T60_600_ms_SNR_5_dB.txt
+;      lists/R+N_T60_600_ms_SNR_10_dB.txt
+;      lists/R+N_T60_800_ms_SNR_5_dB.txt
+;      lists/R+N_T60_800_ms_SNR_5_dB.txt
+sentences = harvsents.txt

File test.cfg

-[setup]
-sample_dir = /home/jfsantos/Documents/
-train = lists/train.txt
-lists =
-      lists/N_SNR_-5_dB.txt
-;      lists/N_SNR_0_dB.txt
-;      lists/N_SNR_5_dB.txt
-;      lists/N_SNR_10_dB.txt
-;      lists/R_T60_300_ms.txt
-      lists/R_T60_600_ms.txt
-;      lists/R_T60_800_ms.txt
-;      lists/R_T60_1000_ms.txt
-;      lists/R+N_T60_600_ms_SNR_5_dB.txt
-;      lists/R+N_T60_600_ms_SNR_10_dB.txt
-;      lists/R+N_T60_800_ms_SNR_5_dB.txt
-;      lists/R+N_T60_800_ms_SNR_5_dB.txt
-sentences = harvsents.txt
-timelimit = 30
-randomize = within
-pauses = 60