HearingTestGUI.py 5.91 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

"""Architecture:
GUI for user
- Start test
- Play sounds
- Register button presses

Generate library of sounds
Play sounds
Record results

Score: hearing and playability

Display score

Save score and agregate
Dump data (for backup) after each test

Display agregated scores in second window
"""

import sounddevice as sd
import numpy as np
import time
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, QTextEdit
from PyQt5.QtCore import QSize
import sys
from SoundLibrary import SoundLibrary
from HearingTest import HearingTest
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class HearingMplCanvas(FigureCanvas):
    """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""

    def __init__(self, parent=None, width=5, height=4, dpi=100):
Ed Rogers's avatar
Ed Rogers committed
41
        self.baseline = -0.1
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)

        # self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        fig.set_facecolor('k')
        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        self.ul_bar = None
        self.ll_bar = None
Ed Rogers's avatar
Ed Rogers committed
56
        self.plot_result(HearingTest(parent.library, self))
57
58

    def plot_result(self, result: HearingTest):
59
        inds = np.arange(0, result.freqs.size)
60
        self.axes.set_facecolor('k')
Ed Rogers's avatar
Ed Rogers committed
61
62
        self.ul_bar = self.axes.bar(inds, result.upper_bounds - self.baseline, color=(0, 0.5, 0), bottom=self.baseline)
        self.ll_bar = self.axes.bar(inds, result.lower_bounds - self.baseline, color=(0, 1, 0), bottom=self.baseline)
63
        xlim = self.axes.get_xlim()
Ed Rogers's avatar
Ed Rogers committed
64
        for i in np.arange(result.lower_lim + self.baseline, result.upper_lim, result.get_block_size()):
65
            self.axes.plot(self.axes.get_xlim(), np.ones(2)*i, color='k')
66
        labels = [str(b) for b in result.freqs]
67
68
        labels.insert(0, '')
        self.axes.set_xticklabels(labels)
Ed Rogers's avatar
Ed Rogers committed
69
        self.axes.set_ylim(self.baseline, result.upper_lim - self.baseline)
70
71
72
        self.axes.set_xlim(xlim)

    def update_result(self, result: HearingTest) -> None:
Ed Rogers's avatar
Ed Rogers committed
73
74
75
        self.set_bar_heights(self.ul_bar, result.upper_bounds-self.baseline)
        self.set_bar_heights(self.ll_bar, result.lower_bounds-self.baseline)
        self.draw()
76
77
78
79
80
81
82
83
84
85

    @staticmethod
    def set_bar_heights(bars: matplotlib.container.BarContainer, vals: np.ndarray):
        for bar, h in zip(bars, vals):
            bar.set_height(h)

    # TODO add animation


class HearingTestThread(QtCore.QThread):
86
    played_sound = QtCore.pyqtSignal(float, float, float)
87
88
89
90
91
92
93
94
95
96
97

    def __init__(self, test: HearingTest):
        QtCore.QThread.__init__(self)
        self.test = test

    def __del__(self):
        self.wait()

    def run(self):
        for _ in range(5):
            # TODO convert to finished at end of test
98
99
            finished, freq, volume, played_time, next_sleep_time = self.test.play_next_sound()
            self.played_sound.emit(freq, volume, played_time)
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

            self.sleep(next_sleep_time)


class TestWindow(QMainWindow):

    def __init__(self, library):
        self.library = library
        self.result = None
        self.testing_thread = None
        self.test_running = False

        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(640, 480))
        self.setWindowTitle("Hearing Test")

        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        grid_layout = QGridLayout(self)
        central_widget.setLayout(grid_layout)

        self.title = QLabel("Press any key to start...", self)
        self.title.setAlignment(QtCore.Qt.AlignCenter)

        self.log = QTextEdit()
        self.log.setReadOnly(True)
        self.log.setFocusPolicy(QtCore.Qt.NoFocus)

        self.graph = HearingMplCanvas(parent=self)

        grid_layout.addWidget(self.title, 0, 0)
        grid_layout.addWidget(self.log, 1, 0)
        grid_layout.addWidget(self.graph, 2, 0)

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if not self.test_running:
            self.test_running = True
            self.run_test()
        else:
Ed Rogers's avatar
Ed Rogers committed
141
142
143
            key_press_time = time.time()
            self.result.handle_key_press(key_press_time)
            self.record_key_press(event, key_press_time)
144

Ed Rogers's avatar
Ed Rogers committed
145
146
    def record_key_press(self, event: QtGui.QKeyEvent, _time):
        self.log.append('Key {} pressed at {}'.format(event.key(), _time))
147
148
149
150
151

    def run_test(self):
        self.log.setText('')
        self.title.setText('Test running')
        self.log.append('Starting...')
Ed Rogers's avatar
Ed Rogers committed
152
        self.result = HearingTest(self.library, self.graph)
153
154
155
156
157
158
        self.testing_thread = HearingTestThread(self.result)
        self.testing_thread.played_sound.connect(self.sound_played)
        self.testing_thread.finished.connect(self.test_finished)
        self.testing_thread.start()
        # need to prevent another thread starting

159
160
    def sound_played(self, freq, volume, played_time):
        self.log.append('Played freq {}, at volume {} and time {}'.format(freq, volume, played_time))
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

    def test_finished(self):
        self.test_running = False
        self.title.setText("Press any key to start...")
        self.log.append("Finished test")
        # TODO store test
        # TODO calculate score
        # TODO display test
        self.result = None


def main():
    print(sd.default.device['output'])
    device = sd.query_devices(sd.default.device['output'])
    fs = device['default_samplerate']
    length = 0.5
    f = [100, 200, 500, 1000, 2000, 5000, 10000]

    library = SoundLibrary(fs, length, f)

    app = QtWidgets.QApplication(sys.argv)
    main_win = TestWindow(library)
    main_win.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()