Implemented unified test starting function
This commit is contained in:
+13
-7
@@ -63,7 +63,7 @@ class EEGTestThread(Thread):
|
||||
self.question_files = []
|
||||
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('finish_eeg_test', self.finishTestEarly, namespace='/main')
|
||||
# Percent speech inteligibility (estimated using behavioural measure)
|
||||
@@ -119,6 +119,15 @@ class EEGTestThread(Thread):
|
||||
self.unsetPageLoaded()
|
||||
self.socketio.emit('processing-complete', {'data': ''}, namespace='/main')
|
||||
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):
|
||||
'''
|
||||
@@ -141,11 +150,8 @@ class EEGTestThread(Thread):
|
||||
True: 10003,
|
||||
False: 10007
|
||||
}
|
||||
try:
|
||||
self.answers[self.trial_ind, self.q_ind] = self.answer in self.response
|
||||
symb = symb_dict[self.answers[self.trial_ind, self.q_ind]]
|
||||
except:
|
||||
set_trace()
|
||||
self.answers[self.trial_ind, self.q_ind] = self.answer in self.response
|
||||
symb = symb_dict[self.answers[self.trial_ind, self.q_ind]]
|
||||
self.socketio.emit('eeg_test_resp', {'q_ind': self.q_ind, 'trial_ind': self.trial_ind, "ans": symb}, namespace='/main')
|
||||
|
||||
def finishTestEarly(self):
|
||||
@@ -310,7 +316,7 @@ class EEGTestThread(Thread):
|
||||
|
||||
def saveState(self, out="eeg_test_state.pkl"):
|
||||
toSave = ['marker_files', 'clinPageLoaded', 'wav_files', 'participant',
|
||||
'response', 'pageLoaded', 'backupFilepath', 'noise_path',
|
||||
'response', 'backupFilepath', 'noise_path',
|
||||
'question_files', 'partPageLoaded', 'si', 'question', 'answers']
|
||||
saveDict = {k:self.__dict__[k] for k in toSave}
|
||||
with open(out, 'wb') as f:
|
||||
|
||||
@@ -190,13 +190,17 @@ def calc_spectrum(files, silences, fs=44100, plot=False):
|
||||
def gen_noise(OutDir, b, fs):
|
||||
print("Generating 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()
|
||||
noiseDir = os.path.join(OutDir, 'wav')
|
||||
noiseDir = os.path.join(OutDir, 'rms')
|
||||
dir_must_exist(noiseDir)
|
||||
noiseDir = os.path.join(noiseDir, 'noise')
|
||||
dir_must_exist(noiseDir)
|
||||
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
|
||||
|
||||
|
||||
@@ -211,11 +215,11 @@ if __name__ == "__main__":
|
||||
help='Matrix test speech data location')
|
||||
parser.add_argument('--OutDir', type=PathType(exists=None, type='dir'),
|
||||
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}
|
||||
|
||||
rmsDir = os.path.join(args['OutDir'], "rms")
|
||||
if not args['SkipRMS']:
|
||||
if args['CalcRMS']:
|
||||
indexes = gen_indexes()
|
||||
wavFiles = gen_audio_stim(args['MatrixDir'], args['OutDir'], indexes)
|
||||
rmsFiles = gen_rms(wavFiles, rmsDir)
|
||||
|
||||
@@ -322,7 +322,7 @@ if __name__ == "__main__":
|
||||
gen2(args['MatrixDir'], args['OutDir'], y)
|
||||
generateAudioStimulus(**args)
|
||||
|
||||
#generateNoiseFromSentences(args['OutDir'], noiseDir)
|
||||
generateNoiseFromSentences(args['OutDir'], noiseDir)
|
||||
#generateDecoderAudio(args['OutDir'], noiseDir, decoderDir)
|
||||
|
||||
|
||||
|
||||
+23
-20
@@ -10,7 +10,7 @@ from scipy.optimize import minimize
|
||||
import csv
|
||||
from shutil import copy2
|
||||
|
||||
from pysndfile import sndio
|
||||
from pysndfile import sndio, PySndfile
|
||||
from matrix_test.filesystem import globDir
|
||||
import sounddevice as sd
|
||||
import pdb
|
||||
@@ -18,7 +18,7 @@ import pdb
|
||||
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
|
||||
if 'matThread' in globals():
|
||||
if matThread.isAlive() and isinstance(matThread, MatTestThread):
|
||||
@@ -59,8 +59,11 @@ class MatTestThread(Thread):
|
||||
'''
|
||||
Thread for running server side matrix test operations
|
||||
'''
|
||||
def __init__(self, listN=3, sessionFilepath=None, noiseFilepath="./matrix_test/stimulus/wav/noise/noise.wav",
|
||||
listFolder="./matrix_test/stimulus/wav/sentence-lists/", socketio=None, participant=None):
|
||||
def __init__(self, listN=3, sessionFilepath=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__()
|
||||
self.participant=participant
|
||||
self.newResp = False
|
||||
@@ -72,14 +75,14 @@ class MatTestThread(Thread):
|
||||
self.socketio = socketio
|
||||
# Attach messages from gui to class methods
|
||||
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('load_file_dialog_resp', self.loadStateSocketHandle, 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('finalise_results', self.finaliseResults, namespace='/main')
|
||||
|
||||
self.listN = listN
|
||||
self.listN = int(listN)
|
||||
self.loadedLists = []
|
||||
self.lists = []
|
||||
self.listsRMS = []
|
||||
@@ -128,10 +131,11 @@ class MatTestThread(Thread):
|
||||
# If loading session from file, load session variables from the file
|
||||
if sessionFilepath:
|
||||
self.loadState(sessionFilepath)
|
||||
self.loadNoise(noiseFilepath, noiseRMSFilepath)
|
||||
else:
|
||||
# Preload audio at start of the test
|
||||
self.loadStimulus(listFolder, n=self.listN)
|
||||
self.loadNoise(noiseFilepath)
|
||||
self.loadNoise(noiseFilepath, noiseRMSFilepath)
|
||||
|
||||
|
||||
def testLoop(self):
|
||||
@@ -413,13 +417,12 @@ class MatTestThread(Thread):
|
||||
random.shuffle(self.availableSentenceInds)
|
||||
|
||||
|
||||
def loadNoise(self, noiseFilepath):
|
||||
def loadNoise(self, noiseFilepath, noiseRMSFilepath):
|
||||
'''
|
||||
Read noise samples and calculate the RMS of the signal
|
||||
'''
|
||||
x, _, _ = sndio.read(noiseFilepath)
|
||||
self.noise = x
|
||||
self.noise_rms = np.sqrt(np.mean(self.noise**2))
|
||||
self.noise = PySndfile(noiseFilepath, 'r')
|
||||
self.noise_rms = np.load(noiseRMSFilepath)
|
||||
|
||||
|
||||
def unsetPageLoaded(self):
|
||||
@@ -445,13 +448,12 @@ class MatTestThread(Thread):
|
||||
self.currentWords = self.listsString[0][currentSentenceInd]
|
||||
# Get noise data
|
||||
noiseLen = x.size + self.fs
|
||||
start = random.randint(0, self.noise.size-noiseLen)
|
||||
start = random.randint(0, self.noise.frames()-noiseLen)
|
||||
end = start + noiseLen
|
||||
x_noise = self.noise[start:end]
|
||||
# Calculate RMS of noise
|
||||
noise_rms = np.sqrt(np.mean(x_noise**2))
|
||||
self.noise.seek(start)
|
||||
x_noise = self.noise.read_frames(end-start)
|
||||
# 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
|
||||
# Set speech to start 500ms after the noise, scaled to the desired SNR
|
||||
sigStart = round(self.fs/2.)
|
||||
@@ -470,9 +472,9 @@ class MatTestThread(Thread):
|
||||
def saveState(self, out="mat_state.pkl"):
|
||||
toSave = ['listsRMS', 'y', 'currentList', 'slope', 'snr', 'snrTrack',
|
||||
'direction', 'noise_rms', 'i', 'currentWords', 'usedLists',
|
||||
'availableSentenceInds', 'trialN', 'listsString', 'noise',
|
||||
'fs', 'nCorrect', 'loadedLists', 'lists', 'listN',
|
||||
'wordsCorrect', 'responses', 'presentedWords', 'srt_50', 's_50']
|
||||
'availableSentenceInds', 'trialN', 'listsString', 'fs',
|
||||
'nCorrect', 'loadedLists', 'lists', 'listN', 'wordsCorrect',
|
||||
'responses', 'presentedWords', 'srt_50', 's_50']
|
||||
saveDict = {k:self.__dict__[k] for k in toSave}
|
||||
with open(out, 'wb') as f:
|
||||
dill.dump(saveDict, f)
|
||||
@@ -493,7 +495,8 @@ class MatTestThread(Thread):
|
||||
|
||||
def loadState(self, filepath):
|
||||
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):
|
||||
|
||||
@@ -30,6 +30,12 @@ from matrix_test_thread import run_matrix_thread
|
||||
from eeg_test_thread import run_eeg_test_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
|
||||
'''
|
||||
@@ -160,6 +166,21 @@ def start_saved_eeg_train(msg):
|
||||
'''
|
||||
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')
|
||||
def start_eeg_test(msg):
|
||||
'''
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
|
||||
$('#load-backup').click(function(event) {
|
||||
// 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;
|
||||
})
|
||||
$('#load-saved').click(function(event) {
|
||||
// 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;
|
||||
})
|
||||
$('#start_eeg_train').click(function(event) {
|
||||
|
||||
@@ -1,3 +1,73 @@
|
||||
{% extends 'index.html' %}
|
||||
{% 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 %}
|
||||
|
||||
@@ -63,10 +63,6 @@
|
||||
waitingDialog.hide();
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('eeg_page_loaded', {data: "clinician"});
|
||||
});
|
||||
|
||||
$('#eeg_test_finish').click(function(event) {
|
||||
socket.emit("finish_eeg_test")
|
||||
});
|
||||
|
||||
@@ -94,10 +94,6 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('eeg_page_loaded', {data: "participant"});
|
||||
});
|
||||
|
||||
socket.on('eeg_stim_done', function(msg) {
|
||||
$('.eeg_submit').find('input, textarea, button, select').removeAttr('disabled');
|
||||
off()
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
socket.on('main-notification', function(msg) {
|
||||
alert(msg.data)
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('page_loaded', {data: "clinician"});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -21,10 +21,20 @@ $(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';
|
||||
});
|
||||
|
||||
$('#mat-repeat').click(function(event) {
|
||||
socket.emit("repeat_stimulus")
|
||||
});
|
||||
@@ -45,10 +55,6 @@ $(document).ready(function(){
|
||||
socket.on('load_file_dialog_resp', function(msg) {
|
||||
socket.emit("load_file_dialog_resp", msg)
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('mat_page_loaded', {data: "clinician"});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -38,10 +38,6 @@ $(document).ready(function(){
|
||||
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
|
||||
socket.on('processing-complete', function(msg) {
|
||||
// Re-enable all inputs
|
||||
|
||||
@@ -112,10 +112,6 @@
|
||||
alert("Matrix stimulus processing complete!")
|
||||
window.location.href = '/matrix_test/complete';
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('mat_page_loaded', {data: "participant"});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
return false;
|
||||
})
|
||||
$('#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;
|
||||
})
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
socket.on('participant_start_mat', function() {
|
||||
window.location.href = "/matrix_test/run";
|
||||
});
|
||||
|
||||
socket.on('check-loaded', function(msg) {
|
||||
socket.emit('page_loaded', {data: "participant"});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user