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

Create basic GUI, threading of playback stream and logging

parent ba380ea2
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <orderEntry type="jdk" jdkName="Python 3.5 (pyGUI)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.6 GUI (QT)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 GUI (QT)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.5 (pyGUI)" project-jdk-type="Python SDK" />
</project> </project>
\ No newline at end of file
...@@ -24,30 +24,108 @@ import numpy as np ...@@ -24,30 +24,108 @@ import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import soundfile as sf import soundfile as sf
import time import time
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
class SoundLibrary: from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, QTextEdit
def __init__(self, fs, sample_length, freqs): from PyQt5.QtCore import QSize
self._data = {} import sys
self.fs = fs from SoundLibrary import SoundLibrary
self.sample_length = sample_length import random
self.generate_sounds(freqs)
def generate_sounds(self, freqs): class TestThread(QtCore.QThread):
t = np.arange(0, self.sample_length, 1 / self.fs) played_sound = QtCore.pyqtSignal(float, float)
for f in freqs:
sound = np.sin(2 * np.pi * f * t) def __init__(self, library: SoundLibrary):
self._data[f] = sound QtCore.QThread.__init__(self)
self.library = library
@property
def freqs(self): def __del__(self):
return self._data.keys() self.wait()
def play(self, freq): # def _get_top_post(self, subreddit):
sd.play(self[freq], self.fs, blocking=False) # """
# Return a pre-formatted string with top post title, author,
def __getitem__(self, item): # and subreddit name from the subreddit passed as the only required
return self._data[item] # argument.
#
# :param subreddit: A valid subreddit name
# :type subreddit: str
# :return: A string with top post title, author,
# and subreddit name from that subreddit.
# :rtype: str
# """
# url = "https://www.reddit.com/r/{}.json?limit=1".format(subreddit)
# headers = {'User-Agent': 'nikolak@outlook.com tutorial code'}
# request = urllib2.Request(url, headers=headers)
# response = urllib2.urlopen(request)
# data = json.load(response)
# top_post = data['data']['children'][0]['data']
# return "'{title}' by {author} in {subreddit}".format(**top_post)
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 HelloWindow(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)
grid_layout.addWidget(self.title, 0, 0)
grid_layout.addWidget(self.log, 1, 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):
self.log.append('Key {} pressed at {}'.format(event.key(), time.clock()))
def run_test(self):
self.log.append('Starting...')
self.testing_thread = TestThread(self.library)
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.log.append('Finished test')
def main(): def main():
...@@ -55,12 +133,17 @@ def main(): ...@@ -55,12 +133,17 @@ def main():
device = sd.query_devices(sd.default.device['output']) device = sd.query_devices(sd.default.device['output'])
fs = device['default_samplerate'] fs = device['default_samplerate']
length = 0.5 length = 0.5
f = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000] f = [100, 200, 500, 1000, 2000, 5000, 10000]
library = SoundLibrary(fs, length, f) library = SoundLibrary(fs, length, f)
for freq in sorted(library.freqs): # for freq in sorted(library.freqs):
library.play(freq) # library.play(freq)
time.sleep(1) # time.sleep(1)
app = QtWidgets.QApplication(sys.argv)
main_win = HelloWindow(library)
main_win.show()
sys.exit(app.exec_())
if __name__ == '__main__': if __name__ == '__main__':
......
import numpy as np
import sounddevice as sd
from typing import List
class SoundLibrary:
def __init__(self, fs: float, sample_length: int, freqs: List[float]):
self._data = {}
self.fs = fs
self.sample_length = sample_length
self.generate_sounds(freqs)
def generate_sounds(self, freqs: List[float]):
t = np.arange(0, self.sample_length, 1 / self.fs)
for f in freqs:
sound = np.sin(2 * np.pi * f * t)
self._data[f] = sound
@property
def freqs(self):
return self._data.keys()
def play(self, freq: float, volume: float=1) -> None:
sd.play(self[freq]*volume, self.fs, blocking=False)
def __getitem__(self, item: float):
return self._data[item]
Supports Markdown
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