Administrator approval is now required for registering new accounts. If you are registering a new account, and are external to the University, please ask the repository owner to contact ServiceLine to request your account be approved. Repository owners must include the newly registered email address, and specific repository in the request for approval.

Commit b4fbaa00 authored by Ed Rogers's avatar Ed Rogers
Browse files

Set up PyCharm project properly and move HearingTest class to separate file

parent c2533934
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.6 (pyGUI)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
......
"""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 soundfile as sf
import time
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, QTextEdit
from PyQt5.QtCore import QSize
import sys
from SoundLibrary import SoundLibrary
import numpy as np
import random
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from SoundLibrary import SoundLibrary
class HearingResult:
class HearingTest:
upper_lim = 100
lower_lim = 0
n_blocks = 20
max_response_time = 1 # in seconds
@staticmethod
def get_block_size():
return (HearingResult.upper_lim - HearingResult.lower_lim)/HearingResult.n_blocks
def __init__(self, bands):
self.bands = bands
self.lower_bounds = np.ones_like(bands) * self.lower_lim
self.upper_bounds = np.ones_like(bands) * self.upper_lim
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):
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
self.plot_result(HearingResult(parent.library.freqs))
def plot_result(self, result: HearingResult):
inds = np.arange(0, result.bands.size)
baseline = -10
self.axes.set_axis_bgcolor('k')
self.ul_bar = self.axes.bar(inds, result.upper_bounds-baseline, color=(0, 0.5, 0), bottom=baseline)
self.ll_bar = self.axes.bar(inds, result.lower_bounds-baseline, color=(0, 1, 0), bottom=baseline)
xlim = self.axes.get_xlim()
for i in np.arange(result.lower_lim+baseline, result.upper_lim, result.get_block_size()):
self.axes.plot(self.axes.get_xlim(), np.ones(2)*i, color='k')
labels = [str(b) for b in result.bands]
labels.insert(0, '')
self.axes.set_xticklabels(labels)
# print(result.bands)
# print(labels)
# print(result.lower_bounds)
self.axes.set_ylim(-10, 110)
self.axes.set_xlim(xlim)
def update_result(self, result: HearingResult) -> None:
self.set_bar_heights(self.ul_bar, result.upper_bounds)
self.set_bar_heights(self.ll_bar, result.lower_bounds)
@staticmethod
def set_bar_heights(bars: matplotlib.container.BarContainer, vals: np.ndarray):
for bar, h in zip(bars, vals):
bar.set_height(h)
class HearingTestThread(QtCore.QThread):
played_sound = QtCore.pyqtSignal(float, float)
return (HearingTest.upper_lim - HearingTest.lower_lim) / HearingTest.n_blocks
def __init__(self, library: SoundLibrary):
QtCore.QThread.__init__(self)
self.library = library
def __del__(self):
self.wait()
def run(self):
for _ in range(5):
volume = random.random()
freq = random.choice(list(self.library.freqs))
self.library.play(freq, volume)
self.played_sound.emit(freq, volume)
self.sleep(1)
class TestWindow(QMainWindow):
def __init__(self, library):
self.library = library
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:
self.record_key_press(event)
def record_key_press(self, event: QtGui.QKeyEvent):
self.log.append('Key {} pressed at {}'.format(event.key(), time.clock()))
def run_test(self):
self.log.setText('')
self.title.setText('Test running')
self.log.append('Starting...')
self.testing_thread = HearingTestThread(self.library, 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
def sound_played(self, freq, volume):
self.log.append('Played freq {}, at volume {} and time {}'.format(freq, volume, time.clock()))
def test_finished(self):
self.test_running = False
self.title.setText("Press any key to start...")
self.log.append('Finished test')
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)
# for freq in sorted(library.freqs):
# library.play(freq)
# time.sleep(1)
app = QtWidgets.QApplication(sys.argv)
main_win = TestWindow(library)
main_win.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
self.lower_bounds = np.full_like(self.bands, fill_value=self.lower_lim)
self.upper_bounds = np.full_like(self.bands, fill_value=self.upper_lim)
self.false_presses = 0
self.sounds = []
@property
def bands(self):
return self.library.freqs
def handle_key_press(self):
# recent_sound = self.sounds[-1]
# if time.clock() - recent_sound.time_played < self.max_response_time:
# pass
# TODO finish me
return
def play_next_sound(self):
# TODO select tone based on results
volume = random.random()
freq = random.choice(self.bands)
self.library.play(freq, volume)
# TODO randomise time
next_sleep_time = 1
# TODO register sound playing for score
test_finished = False
return test_finished, freq, volume, next_sleep_time
"""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 random
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
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):
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
self.plot_result(HearingTest(parent.library))
def plot_result(self, result: HearingTest):
inds = np.arange(0, result.bands.size)
baseline = -10
self.axes.set_facecolor('k')
self.ul_bar = self.axes.bar(inds, result.upper_bounds-baseline, color=(0, 0.5, 0), bottom=baseline)
self.ll_bar = self.axes.bar(inds, result.lower_bounds-baseline, color=(0, 1, 0), bottom=baseline)
xlim = self.axes.get_xlim()
for i in np.arange(result.lower_lim+baseline, result.upper_lim, result.get_block_size()):
self.axes.plot(self.axes.get_xlim(), np.ones(2)*i, color='k')
labels = [str(b) for b in result.bands]
labels.insert(0, '')
self.axes.set_xticklabels(labels)
self.axes.set_ylim(-10, 110)
self.axes.set_xlim(xlim)
def update_result(self, result: HearingTest) -> None:
self.set_bar_heights(self.ul_bar, result.upper_bounds)
self.set_bar_heights(self.ll_bar, result.lower_bounds)
@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):
played_sound = QtCore.pyqtSignal(float, float)
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
finished, freq, volume, next_sleep_time = self.test.play_next_sound()
self.played_sound.emit(freq, volume)
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:
self.result.handle_key_press()
self.record_key_press(event)
def record_key_press(self, event: QtGui.QKeyEvent):
self.log.append('Key {} pressed at {}'.format(event.key(), time.clock()))
def run_test(self):
self.log.setText('')
self.title.setText('Test running')
self.log.append('Starting...')
self.result = HearingTest(self.library)
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
def sound_played(self, freq, volume):
self.log.append('Played freq {}, at volume {} and time {}'.format(freq, volume, time.clock()))
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()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment