Implemented random onset for behavioral test stimulus
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"architecture" : "x64"
|
||||
}
|
||||
,
|
||||
"rect" : [ 0.0, 44.0, 1366.0, 678.0 ],
|
||||
"rect" : [ 0.0, 45.0, 1280.0, 929.0 ],
|
||||
"bglocked" : 0,
|
||||
"openinpresentation" : 0,
|
||||
"default_fontsize" : 12.0,
|
||||
@@ -28,6 +28,35 @@
|
||||
"digest" : "",
|
||||
"tags" : "",
|
||||
"boxes" : [ {
|
||||
"box" : {
|
||||
"fontname" : "Arial",
|
||||
"fontsize" : 12.0,
|
||||
"frgb" : 0.0,
|
||||
"id" : "obj-28",
|
||||
"linecount" : 3,
|
||||
"maxclass" : "comment",
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 1069.0, 608.5, 150.0, 47.0 ],
|
||||
"text" : "Stim generated with guaranteed no clipping at +5dB"
|
||||
}
|
||||
|
||||
}
|
||||
, {
|
||||
"box" : {
|
||||
"fontname" : "Arial",
|
||||
"fontsize" : 12.0,
|
||||
"frgb" : 0.0,
|
||||
"id" : "obj-22",
|
||||
"maxclass" : "comment",
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 1069.0, 586.0, 185.0, 20.0 ],
|
||||
"text" : "0.333, 1.23, 3.169231, 0.861538"
|
||||
}
|
||||
|
||||
}
|
||||
, {
|
||||
"box" : {
|
||||
"fontname" : "Arial",
|
||||
"fontsize" : 12.0,
|
||||
@@ -37,8 +66,8 @@
|
||||
"maxclass" : "comment",
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 1127.0, 559.0, 150.0, 33.0 ],
|
||||
"text" : "RME Fireface Output:\n+4dB"
|
||||
"patching_rect" : [ 1069.0, 553.0, 150.0, 33.0 ],
|
||||
"text" : "RME Fireface Output:\n+1.7dB"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -108,20 +137,6 @@
|
||||
"patching_rect" : [ 99.0, 516.0, 20.0, 20.0 ]
|
||||
}
|
||||
|
||||
}
|
||||
, {
|
||||
"box" : {
|
||||
"fontname" : "Arial",
|
||||
"fontsize" : 12.0,
|
||||
"frgb" : 0.0,
|
||||
"id" : "obj-17",
|
||||
"maxclass" : "comment",
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 889.0, 694.0, 150.0, 20.0 ],
|
||||
"text" : "0.06574271"
|
||||
}
|
||||
|
||||
}
|
||||
, {
|
||||
"box" : {
|
||||
@@ -133,7 +148,7 @@
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 408.0, 215.0, 81.75, 20.0 ],
|
||||
"text" : "80 dB SPL"
|
||||
"text" : "70 dB SPL"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,7 +175,7 @@
|
||||
"numinlets" : 2,
|
||||
"numoutlets" : 1,
|
||||
"outlettype" : [ "signal" ],
|
||||
"patching_rect" : [ 663.0, 694.0, 32.5, 20.0 ],
|
||||
"patching_rect" : [ 663.0, 681.0, 32.5, 20.0 ],
|
||||
"text" : "*~"
|
||||
}
|
||||
|
||||
@@ -285,7 +300,7 @@
|
||||
"numoutlets" : 1,
|
||||
"outlettype" : [ "int" ],
|
||||
"parameter_enable" : 0,
|
||||
"patching_rect" : [ 835.0, 597.0, 20.0, 20.0 ]
|
||||
"patching_rect" : [ 740.25, 565.5, 20.0, 20.0 ]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -298,7 +313,7 @@
|
||||
"numinlets" : 2,
|
||||
"numoutlets" : 1,
|
||||
"outlettype" : [ "signal" ],
|
||||
"patching_rect" : [ 729.0, 617.0, 32.5, 20.0 ],
|
||||
"patching_rect" : [ 663.0, 613.0, 32.5, 20.0 ],
|
||||
"text" : "*~"
|
||||
}
|
||||
|
||||
@@ -312,7 +327,7 @@
|
||||
"numinlets" : 2,
|
||||
"numoutlets" : 1,
|
||||
"outlettype" : [ "signal" ],
|
||||
"patching_rect" : [ 680.0, 563.0, 75.0, 20.0 ],
|
||||
"patching_rect" : [ 663.0, 565.5, 75.0, 20.0 ],
|
||||
"text" : "cycle~ 1000"
|
||||
}
|
||||
|
||||
@@ -953,8 +968,8 @@
|
||||
"numinlets" : 1,
|
||||
"numoutlets" : 1,
|
||||
"outlettype" : [ "" ],
|
||||
"patching_rect" : [ 104.25, 780.0, 552.0, 20.0 ],
|
||||
"text" : "loadmess append OSX:/Users/samuelperry/Work/SOTON/Initial_work/BPLabs/calibration/out/stimulus"
|
||||
"patching_rect" : [ 104.25, 780.0, 509.0, 20.0 ],
|
||||
"text" : "loadmess append OSX:/Users/sam/Work/SOTON/Initial_work/BPLabs/calibration/out/stimulus"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -978,7 +993,7 @@
|
||||
"maxclass" : "ezdac~",
|
||||
"numinlets" : 2,
|
||||
"numoutlets" : 0,
|
||||
"patching_rect" : [ 511.25, 459.0, 45.0, 45.0 ]
|
||||
"patching_rect" : [ 511.25, 476.0, 45.0, 45.0 ]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1130,6 +1145,7 @@
|
||||
"destination" : [ "obj-6", 1 ],
|
||||
"disabled" : 0,
|
||||
"hidden" : 0,
|
||||
"midpoints" : [ 672.5, 723.0, 579.625, 723.0, 579.625, 449.0, 546.75, 449.0 ],
|
||||
"source" : [ "obj-16", 0 ]
|
||||
}
|
||||
|
||||
@@ -1139,6 +1155,7 @@
|
||||
"destination" : [ "obj-6", 0 ],
|
||||
"disabled" : 0,
|
||||
"hidden" : 0,
|
||||
"midpoints" : [ 672.5, 723.0, 570.625, 723.0, 570.625, 449.0, 520.75, 449.0 ],
|
||||
"source" : [ "obj-16", 0 ]
|
||||
}
|
||||
|
||||
@@ -1345,7 +1362,7 @@
|
||||
"destination" : [ "obj-88", 0 ],
|
||||
"disabled" : 0,
|
||||
"hidden" : 0,
|
||||
"midpoints" : [ 389.75, 351.0, 349.0, 351.0, 349.0, 495.0, 389.75, 495.0 ],
|
||||
"midpoints" : [ 389.75, 360.0, 349.0, 360.0, 349.0, 495.0, 389.75, 495.0 ],
|
||||
"source" : [ "obj-56", 0 ]
|
||||
}
|
||||
|
||||
@@ -1440,7 +1457,7 @@
|
||||
"destination" : [ "obj-89", 0 ],
|
||||
"disabled" : 0,
|
||||
"hidden" : 0,
|
||||
"midpoints" : [ 645.5, 351.0, 602.0, 351.0, 602.0, 495.0, 645.5, 495.0 ],
|
||||
"midpoints" : [ 645.5, 358.0, 602.0, 358.0, 602.0, 495.0, 645.5, 495.0 ],
|
||||
"source" : [ "obj-62", 0 ]
|
||||
}
|
||||
|
||||
@@ -1535,7 +1552,7 @@
|
||||
"destination" : [ "obj-90", 0 ],
|
||||
"disabled" : 0,
|
||||
"hidden" : 0,
|
||||
"midpoints" : [ 898.5, 351.0, 850.0, 351.0, 850.0, 495.0, 898.5, 495.0 ],
|
||||
"midpoints" : [ 898.5, 359.0, 850.0, 359.0, 850.0, 495.0, 898.5, 495.0 ],
|
||||
"source" : [ "obj-68", 0 ]
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def calc_potential_max(wavs, noise_filepath, out_dir, out_name):
|
||||
noise_rms = np.sqrt(np.mean(x**2))
|
||||
max_noise_samp = max(np.abs(x))
|
||||
|
||||
snr = -15.
|
||||
snr = 5.
|
||||
snr_fs = 10**(-snr/20)
|
||||
max_noise_samp *= max_wav_rms/noise_rms
|
||||
max_sampl = max_wav_samp+(max_noise_samp*snr_fs)
|
||||
@@ -48,8 +48,7 @@ def main():
|
||||
dir_must_exist(out_dir)
|
||||
dir_must_exist(out_red_dir)
|
||||
dir_must_exist(out_stim_dir)
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
|
||||
story_coef = calc_potential_max(story_wavs, noise_file, out_red_dir, "story_red_coef")
|
||||
mat_coef = calc_potential_max(mat_wavs, noise_file, out_red_dir, "mat_red_coef")
|
||||
da_coef = calc_potential_max(da_files, da_noise_file, out_red_dir, "da_red_coef")
|
||||
|
||||
@@ -21,7 +21,17 @@ def main():
|
||||
y = np.sin(2*np.pi*f*n/fs)
|
||||
coef = np.load('./out/calibration_coefficients/click_cal_coef.npy')
|
||||
y *= coef
|
||||
sndio.write("./out/stimulus/1k_tone.wav", y, fs, format='wav', enc='pcm16')
|
||||
dir_must_exist('./out/calibrated_stim/')
|
||||
sndio.write("./out/calibrated_stim/1k_tone.wav", y, fs, format='wav', enc='pcm16')
|
||||
coef = np.load('./out/calibration_coefficients/da_cal_coef.npy')
|
||||
y, fs, enc = sndio.read('./out/stimulus/da_cal_stim.wav')
|
||||
sndio.write('./out/calibrated_stim/da_cal_stim.wav', y*coef, fs, format='wav', enc='pcm16')
|
||||
coef = np.load('./out/calibration_coefficients/mat_cal_coef.npy')
|
||||
y, fs, enc = sndio.read('./out/stimulus/mat_cal_stim.wav')
|
||||
sndio.write('./out/calibrated_stim/mat_cal_stim.wav', y*coef, fs, format='wav', enc='pcm16')
|
||||
coef = np.load('./out/calibration_coefficients/story_cal_coef.npy')
|
||||
y, fs, enc = sndio.read('./out/stimulus/story_cal_stim.wav')
|
||||
sndio.write('./out/calibrated_stim/story_cal_stim.wav', y*coef, fs, format='wav', enc='pcm16')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+4
-2
@@ -59,8 +59,8 @@ class DaTestThread(BaseThread):
|
||||
self.nTrials = nTrials
|
||||
self.trial_ind = 0
|
||||
self._stopevent = Event()
|
||||
# (Completely clean stimulus added by default later)
|
||||
self.si = np.array([19.0, 50.0, 81.0, 90.0])
|
||||
# (Completely clean stimulus and stimulus at +10dB added by default later)
|
||||
self.si = np.array([19.0, 50.0, 81.0])
|
||||
|
||||
super(DaTestThread, self).__init__(self.test_name,
|
||||
sessionFilepath=sessionFilepath,
|
||||
@@ -133,7 +133,9 @@ class DaTestThread(BaseThread):
|
||||
x = logit(self.si * 0.01)
|
||||
snrs = (x/(4*s_50))+srt_50
|
||||
snrs = np.append(snrs, np.inf)
|
||||
snrs = np.append(snrs, 10.0)
|
||||
self.si = np.append(self.si, np.inf)
|
||||
self.si = np.append(self.si, (10.0*(4*s_50))-srt_50)
|
||||
|
||||
self.snr_fs = 10**(-snrs/20)
|
||||
self.snr_fs[self.snr_fs == np.inf] = 0.
|
||||
|
||||
@@ -164,6 +164,7 @@ class EEGMatTrainThread(BaseThread):
|
||||
)
|
||||
# For each stimulus
|
||||
trials = list(zip(self.stim_paths, self.question))[self.trial_ind:]
|
||||
set_trace()
|
||||
for (wav, q) in trials:
|
||||
self.displayInstructions()
|
||||
self.waitForPartReady()
|
||||
@@ -173,6 +174,7 @@ class EEGMatTrainThread(BaseThread):
|
||||
self.playStimulus(wav)
|
||||
self.setMatrix(q)
|
||||
self.saveState(out=self.backupFilepath)
|
||||
self.finaliseResults()
|
||||
if not self._stopevent.isSet():
|
||||
self.unsetPageLoaded()
|
||||
self.socketio.emit('processing-complete', namespace='/main')
|
||||
@@ -225,10 +227,10 @@ class EEGMatTrainThread(BaseThread):
|
||||
'response', 'backupFilepath', 'noise_path', 'question_files',
|
||||
'si', 'question', 'answers', 'trial_ind']
|
||||
saveDict = {k:self.__dict__[k] for k in toSave}
|
||||
self.participant['eeg_test'].update(saveDict)
|
||||
self.participant.save("eeg_test")
|
||||
self.participant['eeg_mat_train'].update(saveDict)
|
||||
self.participant.save("eeg_mat_train")
|
||||
|
||||
backup_path = os.path.join(self.participant.data_paths['eeg_test'],
|
||||
backup_path = os.path.join(self.participant.data_paths['eeg_mat_train'],
|
||||
'finalised_backup.pkl')
|
||||
copy2(self.backupFilepath, backup_path)
|
||||
self.finalised = True
|
||||
@@ -256,10 +258,10 @@ class EEGMatTrainThread(BaseThread):
|
||||
self.newResp = True
|
||||
|
||||
|
||||
def saveState(self, out="eeg_test_state.pkl"):
|
||||
def saveState(self, out="eeg_mat_train_state.pkl"):
|
||||
toSave = ['marker_files', 'wav_files', 'participant', 'response',
|
||||
'backupFilepath', 'noise_path', 'question_files', 'si',
|
||||
'question', 'answers', 'trial_ind']
|
||||
'question', 'stim_paths', 'answers', 'trial_ind']
|
||||
saveDict = {k:self.__dict__[k] for k in toSave}
|
||||
with open(out, 'wb') as f:
|
||||
dill.dump(saveDict, f)
|
||||
|
||||
@@ -65,7 +65,7 @@ class EEGStoryTrainThread(BaseThread):
|
||||
self.socketio.on_event('finalise_results', self.finaliseResults, namespace='/main')
|
||||
self.loadStimulus()
|
||||
|
||||
self.dev_mode = False
|
||||
self.dev_mode = True
|
||||
|
||||
def setQuestion(self, q):
|
||||
self.socketio.emit('set_question', data=q[0], namespace='/main')
|
||||
@@ -93,6 +93,7 @@ class EEGStoryTrainThread(BaseThread):
|
||||
self.processResponse()
|
||||
self.trial_ind += 1
|
||||
self.saveState(out=self.backupFilepath)
|
||||
self.finaliseResults()
|
||||
if not self._stopevent.isSet():
|
||||
self.unsetPageLoaded()
|
||||
self.socketio.emit('processing-complete', namespace='/main')
|
||||
@@ -148,7 +149,7 @@ class EEGStoryTrainThread(BaseThread):
|
||||
if not self.dev_mode:
|
||||
self.play_wav(wav_file, 'finish_test')
|
||||
else:
|
||||
self.play_wav('./test.wav', 'finish_test')
|
||||
self.play_wav('./da_stim/DA_170.wav', 'finish_test')
|
||||
|
||||
self.socketio.emit("stim_done", namespace="/main")
|
||||
|
||||
|
||||
@@ -128,8 +128,8 @@ def block_mix_wavs(wavpath_a, wavpath_b, out_wavpath, a_gain=1., b_gain=1., bloc
|
||||
y[:, 0] = x1[:, 0] + x2
|
||||
y[:, 1] = x1[:, 1] + x2
|
||||
y[:, 2] = x1[:, 2]
|
||||
elif mute_left:
|
||||
y[:, 0] = 0.0
|
||||
if mute_left:
|
||||
y[:, 0] = 0.0
|
||||
else:
|
||||
y = x1 + x2
|
||||
out_wav.write_frames(y)
|
||||
|
||||
+11
-5
@@ -131,11 +131,17 @@ class MatTestThread(BaseThread):
|
||||
self.loadNoise(noiseFilepath, noiseRMSFilepath)
|
||||
|
||||
|
||||
def displayInstructions(self):
|
||||
self.socketio.emit('display_instructions', namespace='/main')
|
||||
|
||||
def testLoop(self):
|
||||
'''
|
||||
Main loop for iteratively finding the SRT
|
||||
'''
|
||||
self.waitForPageLoad()
|
||||
|
||||
self.displayInstructions()
|
||||
self.waitForPartReady()
|
||||
while not self.finishTest and not self._stopevent.isSet() and len(self.availableSentenceInds):
|
||||
self.plotSNR()
|
||||
self.y = self.generateTrial(self.snr)
|
||||
@@ -386,22 +392,22 @@ class MatTestThread(BaseThread):
|
||||
def generateTrial(self, snr):
|
||||
currentSentenceInd = self.availableSentenceInds.pop(0)
|
||||
# Convert desired SNR to dB FS
|
||||
snr_fs = 10**(snr/20)
|
||||
snr_fs = 10**(snr/20.)
|
||||
# Get speech data
|
||||
x = self.lists[0][currentSentenceInd]
|
||||
x_rms = self.listsRMS[0][currentSentenceInd]
|
||||
self.currentWords = self.listsString[0][currentSentenceInd]
|
||||
# Get noise data
|
||||
noiseLen = x.size + self.fs
|
||||
noiseLen = x.size + self.fs*2.5
|
||||
start = random.randint(0, self.noise.frames()-noiseLen)
|
||||
end = start + noiseLen
|
||||
self.noise.seek(start)
|
||||
x_noise = self.noise.read_frames(end-start)
|
||||
# Scale speech to match the RMS of the noise
|
||||
x = x*(self.noise_rms/x_rms)
|
||||
# Scale noise to match the RMS of the speech
|
||||
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.)
|
||||
sigStart = random.randint(self.fs, round(2*self.fs))
|
||||
y[sigStart:sigStart+x.size] += x*snr_fs
|
||||
y *= self.reduction_coef
|
||||
return y
|
||||
|
||||
+4
-1
@@ -73,7 +73,10 @@ class Participant:
|
||||
"notes": ''
|
||||
},
|
||||
|
||||
"eeg_train": {
|
||||
"eeg_story_train": {
|
||||
"notes": ''
|
||||
},
|
||||
"eeg_mat_train": {
|
||||
"notes": ''
|
||||
},
|
||||
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
{% extends 'participant_index.html' %}
|
||||
{% block content %}
|
||||
<div id="main-div" class="outer">
|
||||
<div id="instructions">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h2>Instructions</h2>
|
||||
<p>
|
||||
In this test, you will be presented with repeated "da"
|
||||
utterances. Simply listen to the stimulus, you do not need to
|
||||
respond.
|
||||
</p>
|
||||
<button type="button" href="#" id="instr_continue" class="Btn Btn-primary">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid mat_grid">
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
@@ -185,11 +198,15 @@
|
||||
<div class="mat_submit">
|
||||
<button type="button" href="#" disabled id="mat-submit" class="Btn Btn-primary">Submit</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(function(){
|
||||
// Initialise socketio with a namespace called "main"
|
||||
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
|
||||
var part_ready = false;
|
||||
loading = false;
|
||||
waitingDialog.show('Loading... Please wait');
|
||||
|
||||
var choice = Array(5).fill('');
|
||||
$('.mat-button').click(function(event) {
|
||||
@@ -216,10 +233,37 @@
|
||||
}
|
||||
});
|
||||
|
||||
function displayInstructions() {
|
||||
$("#instructions").css("display", "table-cell");
|
||||
$("#instructions").fadeIn();
|
||||
waitingDialog.hide();
|
||||
}
|
||||
|
||||
function hideInstructions() {
|
||||
$("#instructions").css("display", "none");
|
||||
$("#instructions").fadeOut();
|
||||
if(loading) {
|
||||
waitingDialog.show('Loading... Please wait');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$('#instr_continue').click(function(event) {
|
||||
hideInstructions();
|
||||
part_ready = true;
|
||||
socket.emit("part_ready")
|
||||
on()
|
||||
});
|
||||
|
||||
socket.on('display_instructions', function(msg) {
|
||||
displayInstructions();
|
||||
});
|
||||
|
||||
socket.on('stim_playing', function(msg) {
|
||||
// Disable all inputs whilst processing
|
||||
$('#main-div').find('input, textarea, button, select').attr('disabled','disabled');
|
||||
});
|
||||
|
||||
socket.on('question', function(msg) {
|
||||
// Disable all inputs whilst processing
|
||||
$('#question').text(msg);
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import sounddevice as sd
|
||||
import dill
|
||||
|
||||
from shutil import copy2
|
||||
from threading import Thread, Event
|
||||
from config import socketio
|
||||
|
||||
|
||||
Reference in New Issue
Block a user