Implemented unified test starting function

This commit is contained in:
2018-12-02 22:22:24 +00:00
parent c57dc48cc4
commit d9ff781fcc
16 changed files with 159 additions and 54 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"directory" : "static/bower_components"
}
+13 -7
View File
@@ -63,7 +63,7 @@ class EEGTestThread(Thread):
self.question_files = [] self.question_files = []
self.question = [] self.question = []
self.socketio.on_event('eeg_page_loaded', self.setPageLoaded, namespace='/main') self.socketio.on_event('page_loaded', self.setPageLoaded, namespace='/main')
self.socketio.on_event('submit_eeg_response', self.submitTestResponse, namespace='/main') self.socketio.on_event('submit_eeg_response', self.submitTestResponse, namespace='/main')
self.socketio.on_event('finish_eeg_test', self.finishTestEarly, namespace='/main') self.socketio.on_event('finish_eeg_test', self.finishTestEarly, namespace='/main')
# Percent speech inteligibility (estimated using behavioural measure) # Percent speech inteligibility (estimated using behavioural measure)
@@ -119,6 +119,15 @@ class EEGTestThread(Thread):
self.unsetPageLoaded() self.unsetPageLoaded()
self.socketio.emit('processing-complete', {'data': ''}, namespace='/main') self.socketio.emit('processing-complete', {'data': ''}, namespace='/main')
self.waitForPageLoad() self.waitForPageLoad()
self.fillTable()
def fillTable(self):
'''
'''
symb = [[symb_dict[x], symb_dict[y]] for x, y in self.answers]
set_trace()
self.socketio.emit('eeg_test_fill_table', {'data': symb}, namespace='/main')
def setMatrix(self, questions): def setMatrix(self, questions):
''' '''
@@ -141,11 +150,8 @@ class EEGTestThread(Thread):
True: 10003, True: 10003,
False: 10007 False: 10007
} }
try: self.answers[self.trial_ind, self.q_ind] = self.answer in self.response
self.answers[self.trial_ind, self.q_ind] = self.answer in self.response symb = symb_dict[self.answers[self.trial_ind, self.q_ind]]
symb = symb_dict[self.answers[self.trial_ind, self.q_ind]]
except:
set_trace()
self.socketio.emit('eeg_test_resp', {'q_ind': self.q_ind, 'trial_ind': self.trial_ind, "ans": symb}, namespace='/main') self.socketio.emit('eeg_test_resp', {'q_ind': self.q_ind, 'trial_ind': self.trial_ind, "ans": symb}, namespace='/main')
def finishTestEarly(self): def finishTestEarly(self):
@@ -310,7 +316,7 @@ class EEGTestThread(Thread):
def saveState(self, out="eeg_test_state.pkl"): def saveState(self, out="eeg_test_state.pkl"):
toSave = ['marker_files', 'clinPageLoaded', 'wav_files', 'participant', toSave = ['marker_files', 'clinPageLoaded', 'wav_files', 'participant',
'response', 'pageLoaded', 'backupFilepath', 'noise_path', 'response', 'backupFilepath', 'noise_path',
'question_files', 'partPageLoaded', 'si', 'question', 'answers'] 'question_files', 'partPageLoaded', 'si', 'question', 'answers']
saveDict = {k:self.__dict__[k] for k in toSave} saveDict = {k:self.__dict__[k] for k in toSave}
with open(out, 'wb') as f: with open(out, 'wb') as f:
+7 -3
View File
@@ -190,13 +190,17 @@ def calc_spectrum(files, silences, fs=44100, plot=False):
def gen_noise(OutDir, b, fs): def gen_noise(OutDir, b, fs):
print("Generating noise...") print("Generating noise...")
# Generate 10 minutes of white noise # Generate 10 minutes of white noise
x = np.random.randn(int(fs*60.*20.)) x = np.random.randn(int(fs*60.*10.))
x /= x.max() x /= x.max()
noiseDir = os.path.join(OutDir, 'wav') noiseDir = os.path.join(OutDir, 'wav')
noiseDir = os.path.join(OutDir, 'rms')
dir_must_exist(noiseDir) dir_must_exist(noiseDir)
noiseDir = os.path.join(noiseDir, 'noise') noiseDir = os.path.join(noiseDir, 'noise')
dir_must_exist(noiseDir) dir_must_exist(noiseDir)
y = block_lfilter_wav(b, [1.0], x, os.path.join(noiseDir, 'noise.wav'), 65538, 44100) y = block_lfilter_wav(b, [1.0], x, os.path.join(noiseDir, 'noise.wav'), 65538, 44100)
noise_rms_path = os.path.join(noiseDir, 'noise_rms.npy')
rms = np.mean(np.sqrt(y**2))
np.save(noise_rms_path, rms)
return y return y
@@ -211,11 +215,11 @@ if __name__ == "__main__":
help='Matrix test speech data location') help='Matrix test speech data location')
parser.add_argument('--OutDir', type=PathType(exists=None, type='dir'), parser.add_argument('--OutDir', type=PathType(exists=None, type='dir'),
default='./stimulus', help='Output directory') default='./stimulus', help='Output directory')
parser.add_argument('--SkipRMS', action='store_true') parser.add_argument('--CalcRMS', action='store_true')
args = {k:v for k,v in vars(parser.parse_args()).items() if v is not None} args = {k:v for k,v in vars(parser.parse_args()).items() if v is not None}
rmsDir = os.path.join(args['OutDir'], "rms") rmsDir = os.path.join(args['OutDir'], "rms")
if not args['SkipRMS']: if args['CalcRMS']:
indexes = gen_indexes() indexes = gen_indexes()
wavFiles = gen_audio_stim(args['MatrixDir'], args['OutDir'], indexes) wavFiles = gen_audio_stim(args['MatrixDir'], args['OutDir'], indexes)
rmsFiles = gen_rms(wavFiles, rmsDir) rmsFiles = gen_rms(wavFiles, rmsDir)
+1 -1
View File
@@ -322,7 +322,7 @@ if __name__ == "__main__":
gen2(args['MatrixDir'], args['OutDir'], y) gen2(args['MatrixDir'], args['OutDir'], y)
generateAudioStimulus(**args) generateAudioStimulus(**args)
#generateNoiseFromSentences(args['OutDir'], noiseDir) generateNoiseFromSentences(args['OutDir'], noiseDir)
#generateDecoderAudio(args['OutDir'], noiseDir, decoderDir) #generateDecoderAudio(args['OutDir'], noiseDir, decoderDir)
+23 -20
View File
@@ -10,7 +10,7 @@ from scipy.optimize import minimize
import csv import csv
from shutil import copy2 from shutil import copy2
from pysndfile import sndio from pysndfile import sndio, PySndfile
from matrix_test.filesystem import globDir from matrix_test.filesystem import globDir
import sounddevice as sd import sounddevice as sd
import pdb import pdb
@@ -18,7 +18,7 @@ import pdb
from config import socketio from config import socketio
def run_matrix_thread(listN=None, sessionFilepath=None, participant=None): def run_matrix_thread(listN=3, sessionFilepath=None, participant=None):
global matThread global matThread
if 'matThread' in globals(): if 'matThread' in globals():
if matThread.isAlive() and isinstance(matThread, MatTestThread): if matThread.isAlive() and isinstance(matThread, MatTestThread):
@@ -59,8 +59,11 @@ class MatTestThread(Thread):
''' '''
Thread for running server side matrix test operations Thread for running server side matrix test operations
''' '''
def __init__(self, listN=3, sessionFilepath=None, noiseFilepath="./matrix_test/stimulus/wav/noise/noise.wav", def __init__(self, listN=3, sessionFilepath=None,
listFolder="./matrix_test/stimulus/wav/sentence-lists/", socketio=None, participant=None): noiseFilepath="./matrix_test/stimulus/wav/noise/noise.wav",
noiseRMSFilepath="./matrix_test/stimulus/rms/noise/noise_rms.npy",
listFolder="./matrix_test/stimulus/wav/sentence-lists/",
socketio=None, participant=None):
super(MatTestThread, self).__init__() super(MatTestThread, self).__init__()
self.participant=participant self.participant=participant
self.newResp = False self.newResp = False
@@ -72,14 +75,14 @@ class MatTestThread(Thread):
self.socketio = socketio self.socketio = socketio
# Attach messages from gui to class methods # Attach messages from gui to class methods
self.socketio.on_event('submit_mat_response', self.submitMatResponse, namespace='/main') self.socketio.on_event('submit_mat_response', self.submitMatResponse, namespace='/main')
self.socketio.on_event('mat_page_loaded', self.setPageLoaded, namespace='/main') self.socketio.on_event('page_loaded', self.setPageLoaded, namespace='/main')
self.socketio.on_event('save_file_dialog_resp', self.manualSave, namespace='/main') self.socketio.on_event('save_file_dialog_resp', self.manualSave, namespace='/main')
self.socketio.on_event('load_file_dialog_resp', self.loadStateSocketHandle, namespace='/main') self.socketio.on_event('load_file_dialog_resp', self.loadStateSocketHandle, namespace='/main')
self.socketio.on_event('repeat_stimulus', self.playStimulusSocketHandle, namespace='/main') self.socketio.on_event('repeat_stimulus', self.playStimulusSocketHandle, namespace='/main')
self.socketio.on_event('finish_test', self.finishTestEarly, namespace='/main') self.socketio.on_event('finish_test', self.finishTestEarly, namespace='/main')
self.socketio.on_event('finalise_results', self.finaliseResults, namespace='/main') self.socketio.on_event('finalise_results', self.finaliseResults, namespace='/main')
self.listN = listN self.listN = int(listN)
self.loadedLists = [] self.loadedLists = []
self.lists = [] self.lists = []
self.listsRMS = [] self.listsRMS = []
@@ -128,10 +131,11 @@ class MatTestThread(Thread):
# If loading session from file, load session variables from the file # If loading session from file, load session variables from the file
if sessionFilepath: if sessionFilepath:
self.loadState(sessionFilepath) self.loadState(sessionFilepath)
self.loadNoise(noiseFilepath, noiseRMSFilepath)
else: else:
# Preload audio at start of the test # Preload audio at start of the test
self.loadStimulus(listFolder, n=self.listN) self.loadStimulus(listFolder, n=self.listN)
self.loadNoise(noiseFilepath) self.loadNoise(noiseFilepath, noiseRMSFilepath)
def testLoop(self): def testLoop(self):
@@ -413,13 +417,12 @@ class MatTestThread(Thread):
random.shuffle(self.availableSentenceInds) random.shuffle(self.availableSentenceInds)
def loadNoise(self, noiseFilepath): def loadNoise(self, noiseFilepath, noiseRMSFilepath):
''' '''
Read noise samples and calculate the RMS of the signal Read noise samples and calculate the RMS of the signal
''' '''
x, _, _ = sndio.read(noiseFilepath) self.noise = PySndfile(noiseFilepath, 'r')
self.noise = x self.noise_rms = np.load(noiseRMSFilepath)
self.noise_rms = np.sqrt(np.mean(self.noise**2))
def unsetPageLoaded(self): def unsetPageLoaded(self):
@@ -445,13 +448,12 @@ class MatTestThread(Thread):
self.currentWords = self.listsString[0][currentSentenceInd] self.currentWords = self.listsString[0][currentSentenceInd]
# Get noise data # Get noise data
noiseLen = x.size + self.fs noiseLen = x.size + self.fs
start = random.randint(0, self.noise.size-noiseLen) start = random.randint(0, self.noise.frames()-noiseLen)
end = start + noiseLen end = start + noiseLen
x_noise = self.noise[start:end] self.noise.seek(start)
# Calculate RMS of noise x_noise = self.noise.read_frames(end-start)
noise_rms = np.sqrt(np.mean(x_noise**2))
# Scale noise to match the RMS of the speech # Scale noise to match the RMS of the speech
x_noise = x_noise*(x_rms/noise_rms) x_noise = x_noise*(x_rms/self.noise_rms)
y = x_noise y = x_noise
# Set speech to start 500ms after the noise, scaled to the desired SNR # Set speech to start 500ms after the noise, scaled to the desired SNR
sigStart = round(self.fs/2.) sigStart = round(self.fs/2.)
@@ -470,9 +472,9 @@ class MatTestThread(Thread):
def saveState(self, out="mat_state.pkl"): def saveState(self, out="mat_state.pkl"):
toSave = ['listsRMS', 'y', 'currentList', 'slope', 'snr', 'snrTrack', toSave = ['listsRMS', 'y', 'currentList', 'slope', 'snr', 'snrTrack',
'direction', 'noise_rms', 'i', 'currentWords', 'usedLists', 'direction', 'noise_rms', 'i', 'currentWords', 'usedLists',
'availableSentenceInds', 'trialN', 'listsString', 'noise', 'availableSentenceInds', 'trialN', 'listsString', 'fs',
'fs', 'nCorrect', 'loadedLists', 'lists', 'listN', 'nCorrect', 'loadedLists', 'lists', 'listN', 'wordsCorrect',
'wordsCorrect', 'responses', 'presentedWords', 'srt_50', 's_50'] 'responses', 'presentedWords', 'srt_50', 's_50']
saveDict = {k:self.__dict__[k] for k in toSave} saveDict = {k:self.__dict__[k] for k in toSave}
with open(out, 'wb') as f: with open(out, 'wb') as f:
dill.dump(saveDict, f) dill.dump(saveDict, f)
@@ -493,7 +495,8 @@ class MatTestThread(Thread):
def loadState(self, filepath): def loadState(self, filepath):
with open(filepath, 'rb') as f: with open(filepath, 'rb') as f:
self.__dict__.update(dill.load(f)) backup_dict = dill.load(f)
self.__dict__.update(backup_dict)
def run(self): def run(self):
+21
View File
@@ -30,6 +30,12 @@ from matrix_test_thread import run_matrix_thread
from eeg_test_thread import run_eeg_test_thread from eeg_test_thread import run_eeg_test_thread
from eeg_train_thread import run_eeg_train_thread from eeg_train_thread import run_eeg_train_thread
thread_runners = {
"eeg_test": run_eeg_test_thread,
"eeg_train": run_eeg_train_thread,
"mat": run_matrix_thread
}
''' '''
Generic socket handlers Generic socket handlers
''' '''
@@ -160,6 +166,21 @@ def start_saved_eeg_train(msg):
''' '''
EEG test socket handlers EEG test socket handlers
''' '''
@socketio.on('start_test', namespace='/main')
def start_test(msg):
test_name = msg.pop('test_name')
part_key = msg.pop('part_key')
thread_runner = thread_runners[test_name]
socketio.emit('participant_start_{}'.format(test_name), namespace='/main')
if part_key != "--":
participant = participants[part_key]
else:
raise ValueError("Participant must be selected...")
thread_runner(participant=participant, **msg)
@socketio.on('start_eeg_test', namespace='/main') @socketio.on('start_eeg_test', namespace='/main')
def start_eeg_test(msg): def start_eeg_test(msg):
''' '''
+2 -2
View File
@@ -33,12 +33,12 @@
$('#load-backup').click(function(event) { $('#load-backup').click(function(event) {
// Send message to call stimulus generation function in Python // Send message to call stimulus generation function in Python
socket.emit('load_eeg_backup', {part_key: $("#participant").val()}); socket.emit('load_eeg_test_backup', {part_key: $("#participant").val()});
return false; return false;
}) })
$('#load-saved').click(function(event) { $('#load-saved').click(function(event) {
// Send message to call stimulus generation function in Python // Send message to call stimulus generation function in Python
socket.emit('load_eeg_session', {part_key: $("#participant").val()}); socket.emit('load_eeg_test_session', {part_key: $("#participant").val()});
return false; return false;
}) })
$('#start_eeg_train').click(function(event) { $('#start_eeg_train').click(function(event) {
+70
View File
@@ -1,3 +1,73 @@
{% extends 'index.html' %} {% extends 'index.html' %}
{% block content %} {% block content %}
<div class="card">
<div id="main-div" class="card-body">
<table id="eeg_test_table" class="table table-sm table">
<thead class="thead-dark">
<tr>
<th scope="col">Trial #</th>
<th scope="col">1</th>
<th scope="col">2</th>
<th scope="col">3</th>
<th scope="col">4</th>
<th scope="col">5</th>
<th scope="col">6</th>
<th scope="col">7</th>
</tr>
</thead>
<tbody>
<tr id="Q1">
<th scope="row">Q1</th>
<td class="T1">-</td>
<td class="T2">-</td>
<td class="T3">-</td>
<td class="T4">-</td>
<td class="T5">-</td>
<td class="T6">-</td>
<td class="T7">-</td>
</tr>
<tr id="Q2">
<th scope="row">Q2</th>
<td class="T1">-</td>
<td class="T2">-</td>
<td class="T3">-</td>
<td class="T4">-</td>
<td class="T5">-</td>
<td class="T6">-</td>
<td class="T7">-</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center mt-2" role="group">
<button type="button" id="mat-save" class="btn btn-primary mx-3">Save and finish test</button>
</div>
</div>
</div>
<script>
$(document).ready(function(){
// Initialise socketio with a namespace called "main"
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
var finalised=0;
window.onbeforeunload = confirmExit;
function confirmExit() {
if (formmodified == 0) {
return "New information not saved. Do you wish to leave the page?";
}
}
$('#mat-save').click(function(event) {
socket.emit("finalise_results")
finalised=1;
window.location.href = '/home';
});
socket.on('eeg_test_fill_table', function(msg) {
var t_ind = msg['trial_ind'];
var q_ind = msg['q_ind'];
$(`#Q${q_ind+1} > .T${t_ind+1}`).text(String.fromCharCode(msg['ans']));
});
});
</script>
{% endblock %} {% endblock %}
-4
View File
@@ -63,10 +63,6 @@
waitingDialog.hide(); waitingDialog.hide();
}); });
socket.on('check-loaded', function(msg) {
socket.emit('eeg_page_loaded', {data: "clinician"});
});
$('#eeg_test_finish').click(function(event) { $('#eeg_test_finish').click(function(event) {
socket.emit("finish_eeg_test") socket.emit("finish_eeg_test")
}); });
-4
View File
@@ -94,10 +94,6 @@
return false; return false;
}); });
socket.on('check-loaded', function(msg) {
socket.emit('eeg_page_loaded', {data: "participant"});
});
socket.on('eeg_stim_done', function(msg) { socket.on('eeg_stim_done', function(msg) {
$('.eeg_submit').find('input, textarea, button, select').removeAttr('disabled'); $('.eeg_submit').find('input, textarea, button, select').removeAttr('disabled');
off() off()
+4
View File
@@ -108,6 +108,10 @@
socket.on('main-notification', function(msg) { socket.on('main-notification', function(msg) {
alert(msg.data) alert(msg.data)
}); });
socket.on('check-loaded', function(msg) {
socket.emit('page_loaded', {data: "clinician"});
});
}); });
</script> </script>
</body> </body>
+10 -4
View File
@@ -21,10 +21,20 @@ $(document).ready(function(){
// Initialise socketio with a namespace called "main" // Initialise socketio with a namespace called "main"
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main'); var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
var finalised=0;
window.onbeforeunload = confirmExit;
function confirmExit() {
if (formmodified == 0) {
return "New information not saved. Do you wish to leave the page?";
}
}
$('#mat-save').click(function(event) { $('#mat-save').click(function(event) {
socket.emit("finalise_results") socket.emit("finalise_results")
finalised=1;
window.location.href = '/home'; window.location.href = '/home';
}); });
$('#mat-repeat').click(function(event) { $('#mat-repeat').click(function(event) {
socket.emit("repeat_stimulus") socket.emit("repeat_stimulus")
}); });
@@ -45,10 +55,6 @@ $(document).ready(function(){
socket.on('load_file_dialog_resp', function(msg) { socket.on('load_file_dialog_resp', function(msg) {
socket.emit("load_file_dialog_resp", msg) socket.emit("load_file_dialog_resp", msg)
}); });
socket.on('check-loaded', function(msg) {
socket.emit('mat_page_loaded', {data: "clinician"});
});
}); });
</script> </script>
{% endblock %} {% endblock %}
-4
View File
@@ -38,10 +38,6 @@ $(document).ready(function(){
socket.emit("load_file_dialog_resp", msg) socket.emit("load_file_dialog_resp", msg)
}); });
socket.on('check-loaded', function(msg) {
socket.emit('mat_page_loaded', {data: "clinician"});
});
// Catch message when asynchronous process is complete // Catch message when asynchronous process is complete
socket.on('processing-complete', function(msg) { socket.on('processing-complete', function(msg) {
// Re-enable all inputs // Re-enable all inputs
-4
View File
@@ -112,10 +112,6 @@
alert("Matrix stimulus processing complete!") alert("Matrix stimulus processing complete!")
window.location.href = '/matrix_test/complete'; window.location.href = '/matrix_test/complete';
}); });
socket.on('check-loaded', function(msg) {
socket.emit('mat_page_loaded', {data: "participant"});
});
}); });
</script> </script>
</div> </div>
+1 -1
View File
@@ -44,7 +44,7 @@
return false; return false;
}) })
$('#submit').click(function(event) { $('#submit').click(function(event) {
socket.emit('start_mat_test', {listN: $("#listN").val(), part_key: $("#participant").val()}); socket.emit('start_test', {listN: $("#listN").val(), part_key: $("#participant").val(), test_name: "mat"});
return false; return false;
}) })
+4
View File
@@ -67,6 +67,10 @@
socket.on('participant_start_mat', function() { socket.on('participant_start_mat', function() {
window.location.href = "/matrix_test/run"; window.location.href = "/matrix_test/run";
}); });
socket.on('check-loaded', function(msg) {
socket.emit('page_loaded', {data: "participant"});
});
}); });
</script> </script>
</body> </body>