diff --git a/MLEexample.py b/MLEexample.py new file mode 100644 index 0000000..c72c3bb --- /dev/null +++ b/MLEexample.py @@ -0,0 +1,41 @@ +import numpy as np +from scipy.optimize import minimize + +def logisticFunction(L, L_50, s_50): + ''' + Calculate logistic function for SNRs L, 50% SRT point L_50, and slope + s_50 + ''' + return 1./(1.+np.exp(4.*s_50*(L_50-L))) + +def logisticFuncLiklihood(self, args): + ''' + Calculate the log liklihood for given L_50 and s_50 parameters. + This function is designed for use with the scipy minimize optimisation + function to find the optimal L_50 and s_50 parameters. + + args: a tuple containing (L_50, s_50) + self.wordsCorrect: an n dimensional binary array of shape (N, 5), + containing the correctness of responses to each of the 5 words for N + trials + self.trackSNR: A sorted list of SNRs of shape N, for N trials + ''' + L_50, s_50 = args + ck = self.wordsCorrect[np.arange(self.trackSNR.shape[0])] + p_lf = self.logisticFunction(self.trackSNR, L_50, s_50) + # Reshape array for vectorized calculation of log liklihood + p_lf = p_lf[:, np.newaxis].repeat(5, axis=1) + # Calculate the liklihood + res = (p_lf**ck)*(((1.-p_lf)**(1.-ck))) + with np.errstate(divide='raise'): + try: + a = np.concatenate(res) + a[a == 0] = a.max() + out = -np.sum(np.log(a)) + except: + set_trace() + return out + +# Called from within a class in my implementation, will not work without a +# class containing member variables for wordsCorrect and trackSNR +res = minimize(logisticFuncLiklihood, np.array([-5.0,1.0]), method='L-BFGS-B') diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calibration/gen_calibration_stimulus.py b/calibration/gen_calibration_stimulus.py new file mode 100755 index 0000000..266add0 --- /dev/null +++ b/calibration/gen_calibration_stimulus.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import sys +sys.path.insert(0, "../matrix_test/helper_modules") + +import numpy as np +from pathops import dir_must_exist +from filesystem import globDir +from pysndfile import sndio +import os +from signalops import block_process_wav + +def calc_potential_max(wavs, noise_filepath, out_dir, out_name): + max_wav_samp = 0 + max_wav_rms = 0 + for wav in wavs: + x, fs, enc = sndio.read(wav) + max_wav_samp = np.max([max_wav_samp, np.max(np.abs(x))]) + max_wav_rms = np.max([max_wav_rms, np.sqrt(np.mean(x**2))]) + x, fs, enc = sndio.read(noise_filepath) + noise_rms = np.sqrt(np.mean(x**2)) + max_noise_samp = max(np.abs(x)) + + snr = -15.0 + snr_fs = 10**(-snr/20) + max_noise_samp *= max_wav_rms/noise_rms + max_sampl = max_wav_samp+(max_noise_samp*snr_fs) + reduction_coef = 1.0/max_sampl + np.save(os.path.join(out_dir, "{}.npy".format(out_name)), reduction_coef) + return reduction_coef + +def main(): + ''' + ''' + da_files = ["../da_stim/stimulus/3000_da.wav"] + story_dir = "../eeg_story_stim/stimulus" + mat_dir = "../matrix_test/speech_components" + noise_file = "../matrix_test/behavioural_stim/stimulus/wav/noise/noise.wav" + + story_wavs = globDir(story_dir, '*.wav') + mat_wavs = globDir(mat_dir, '*.wav') + + out_dir = "./out" + dir_must_exist(out_dir) + story_coef = calc_potential_max(story_wavs, noise_file, out_dir, "story_red_coef") + mat_coef = calc_potential_max(mat_wavs, noise_file, out_dir, "mat_red_coef") + da_coef = calc_potential_max(da_files, noise_file, out_dir, "da_red_coef") + + mat_cal_stim = "../matrix_test/long_concat_stim/out/stim/stim_0.wav" + da_cal_stim = "../da_stim/stimulus/wav/10min_da.wav" + click_cal_stim = "../click_stim/click_3000_20Hz.wav" + story_cal_stim = "../eeg_story_stim/stimulus/odin_1_1.wav" + +if __name__ == "__main__": + main() diff --git a/calibration_patch.maxpat b/calibration_patch.maxpat deleted file mode 100644 index 8d0f19f..0000000 --- a/calibration_patch.maxpat +++ /dev/null @@ -1,929 +0,0 @@ -{ - "patcher" : { - "fileversion" : 1, - "appversion" : { - "major" : 6, - "minor" : 1, - "revision" : 10, - "architecture" : "x64" - } -, - "rect" : [ 0.0, 44.0, 1223.0, 678.0 ], - "bglocked" : 0, - "openinpresentation" : 0, - "default_fontsize" : 12.0, - "default_fontface" : 0, - "default_fontname" : "Arial", - "gridonopen" : 0, - "gridsize" : [ 15.0, 15.0 ], - "gridsnaponopen" : 0, - "statusbarvisible" : 2, - "toolbarvisible" : 1, - "boxanimatetime" : 200, - "imprint" : 0, - "enablehscroll" : 1, - "enablevscroll" : 1, - "devicewidth" : 0.0, - "description" : "", - "digest" : "", - "tags" : "", - "boxes" : [ { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-51", - "maxclass" : "flonum", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "parameter_enable" : 0, - "patching_rect" : [ 53.25, 407.0, 88.0, 20.0 ], - "presentation_rect" : [ 376.0, 544.0, 0.0, 0.0 ] - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-52", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 141.25, 447.0, 32.5, 20.0 ], - "presentation_rect" : [ 464.0, 584.0, 0.0, 0.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-53", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 175.25, 447.0, 32.5, 20.0 ], - "presentation_rect" : [ 498.0, 584.0, 0.0, 0.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-54", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 141.25, 373.0, 33.0, 20.0 ], - "presentation_rect" : [ 464.0, 510.0, 0.0, 0.0 ], - "text" : "sig~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-55", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 173.75, 333.0, 32.5, 18.0 ], - "presentation_rect" : [ 496.5, 470.0, 0.0, 0.0 ], - "text" : "1" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-56", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 141.25, 333.0, 32.5, 18.0 ], - "presentation_rect" : [ 464.0, 470.0, 0.0, 0.0 ], - "text" : "0" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-57", - "maxclass" : "newobj", - "numinlets" : 3, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 141.25, 407.0, 110.0, 20.0 ], - "text" : "groove~ noise_buf" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-33", - "maxclass" : "flonum", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "parameter_enable" : 0, - "patching_rect" : [ 253.25, 407.0, 88.0, 20.0 ] - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-34", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 341.25, 447.0, 32.5, 20.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-35", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 375.25, 447.0, 32.5, 20.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-37", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 341.25, 373.0, 33.0, 20.0 ], - "text" : "sig~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-39", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 373.75, 333.0, 32.5, 18.0 ], - "text" : "1" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-48", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 341.25, 333.0, 32.5, 18.0 ], - "text" : "0" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-50", - "maxclass" : "newobj", - "numinlets" : 3, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 341.25, 407.0, 94.0, 20.0 ], - "text" : "groove~ da_buf" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-32", - "maxclass" : "flonum", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "parameter_enable" : 0, - "patching_rect" : [ 442.25, 407.0, 88.0, 20.0 ], - "presentation_rect" : [ 557.0, 512.0, 0.0, 0.0 ] - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-30", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 530.25, 447.0, 32.5, 20.0 ], - "presentation_rect" : [ 503.0, 573.0, 0.0, 0.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-31", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 564.25, 447.0, 32.5, 20.0 ], - "presentation_rect" : [ 537.0, 573.0, 0.0, 0.0 ], - "text" : "*~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-19", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 530.25, 373.0, 33.0, 20.0 ], - "presentation_rect" : [ 732.0, 455.0, 0.0, 0.0 ], - "text" : "sig~" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-25", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 562.75, 333.0, 32.5, 18.0 ], - "presentation_rect" : [ 697.0, 411.0, 0.0, 0.0 ], - "text" : "1" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-26", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 530.25, 333.0, 32.5, 18.0 ], - "presentation_rect" : [ 622.0, 416.0, 0.0, 0.0 ], - "text" : "0" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-29", - "maxclass" : "newobj", - "numinlets" : 3, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 530.25, 407.0, 104.0, 20.0 ], - "text" : "groove~ click_buf" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-17", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 53.25, 682.0, 486.0, 20.0 ], - "text" : "loadmess append OSX:/Users/samuelperry/Work/SOTON/Initial_work/BPLabs/click_stim/" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-13", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 835.75, 204.0, 97.0, 20.0 ], - "presentation_rect" : [ 512.0, 408.0, 0.0, 0.0 ], - "text" : "prepend replace" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-14", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 835.75, 164.0, 79.0, 20.0 ], - "presentation_rect" : [ 512.0, 368.0, 0.0, 0.0 ], - "text" : "absolutepath" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-15", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 835.75, 132.0, 127.0, 18.0 ], - "presentation_rect" : [ 512.0, 299.0, 0.0, 0.0 ], - "text" : "click_3000_20Hz.wav" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-16", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "patching_rect" : [ 835.75, 235.0, 98.0, 20.0 ], - "text" : "buffer~ click_buf" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-28", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 53.25, 702.0, 476.0, 20.0 ], - "text" : "loadmess append OSX:/Users/samuelperry/Work/SOTON/Initial_work/BPLabs/da_stim/" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-27", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 53.25, 662.0, 596.0, 20.0 ], - "text" : "loadmess append OSX:/Users/samuelperry/Work/SOTON/Initial_work/BPLabs/matrix_test/stimulus/wav/noise/" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-23", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 650.0, 132.0, 64.0, 18.0 ], - "text" : "noise.wav" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-20", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 650.0, 204.0, 97.0, 20.0 ], - "text" : "prepend replace" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-21", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 650.0, 164.0, 79.0, 20.0 ], - "text" : "absolutepath" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-22", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "patching_rect" : [ 650.0, 235.0, 103.0, 20.0 ], - "text" : "buffer~ noise_buf" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-18", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 508.0, 204.0, 97.0, 20.0 ], - "text" : "prepend replace" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-2", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 53.25, 750.0, 49.0, 20.0 ], - "text" : "filepath" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-11", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 508.0, 164.0, 79.0, 20.0 ], - "text" : "absolutepath" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-10", - "maxclass" : "message", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 508.0, 132.0, 82.0, 18.0 ], - "text" : "3000_da.wav" - } - - } -, { - "box" : { - "fontname" : "Arial", - "fontsize" : 12.0, - "id" : "obj-8", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 2, - "outlettype" : [ "float", "bang" ], - "patching_rect" : [ 508.0, 235.0, 88.0, 20.0 ], - "text" : "buffer~ da_buf" - } - - } -, { - "box" : { - "id" : "obj-6", - "maxclass" : "ezdac~", - "numinlets" : 2, - "numoutlets" : 0, - "patching_rect" : [ 341.25, 537.0, 45.0, 45.0 ] - } - - } - ], - "lines" : [ { - "patchline" : { - "destination" : [ "obj-11", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-10", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-18", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-11", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-16", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-13", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-13", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-14", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-14", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-15", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-2", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-17", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-8", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-18", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-29", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-19", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-22", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-20", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-20", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-21", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-21", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-23", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-19", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-25", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-19", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-26", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-2", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-27", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-2", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-28", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-30", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-29", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-31", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-29", 1 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-30", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-31", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-30", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-32", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-31", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-32", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-34", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-33", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-35", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-33", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-34", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-35", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-50", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-37", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-37", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-39", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-37", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-48", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-34", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-50", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-35", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-50", 1 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-52", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-51", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-53", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-51", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-52", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 1 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-53", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-57", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-54", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-54", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-55", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-54", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-56", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-52", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-57", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-53", 0 ], - "disabled" : 0, - "hidden" : 0, - "source" : [ "obj-57", 1 ] - } - - } - ], - "dependency_cache" : [ ] - } - -} diff --git a/click_stim/gen_click.py b/click_stim/gen_click.py index 8e2289e..ad3370e 100755 --- a/click_stim/gen_click.py +++ b/click_stim/gen_click.py @@ -1,10 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import sys +sys.path.insert(0, "../matrix_test/helper_modules") from scipy.signal import square from pysndfile import sndio import numpy as np import pdb import matplotlib.pyplot as plt +from signalops import gen_trigger def gen_click(idx, freq, length, fs): duty = length*freq @@ -22,7 +25,12 @@ def main(): y = (np.arange(length) % period == 0).astype(float) y[np.where(y == 1.0)[0][1::2]] = -1.0 y = np.concatenate([np.zeros(fs), y, np.zeros(fs)]) - print("Number of clicks generated: {}".format(np.sum(np.abs(y) == 1.0))) + + idx = np.arange(y.size) + trigger = gen_trigger(idx, 2., 0.01, fs) + y = np.vstack((y, y, trigger)).T + + print("Number of clicks generated: {}".format(np.sum(np.abs(y[:, 0]) == 1.0))) sndio.write('./click_3000_20Hz.wav', y, rate = fs, format='wav', enc='pcm16') if __name__ == "__main__": diff --git a/da_stim/__init__.py b/da_stim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/da_stim/gen_da.py b/da_stim/gen_da.py index 19b2069..19fcd55 100755 --- a/da_stim/gen_da.py +++ b/da_stim/gen_da.py @@ -8,6 +8,7 @@ import numpy as np import pdb import matplotlib.pyplot as plt from pathops import dir_must_exist +from signalops import gen_trigger def gen_da_stim(n, outpath): da_file = './BioMAP_da-40ms.wav' @@ -21,8 +22,15 @@ def gen_da_stim(n, outpath): y_part_inv = -y_part y_2part = np.concatenate([y_part, y_part_inv]) - y = np.tile(y_2part, n) - sndio.write(outpath, y, rate = fs, format = fmt, enc=enc) + y_r = np.tile(y_2part, n) + y_r = np.insert(y_r, 0, np.zeros(fs)) + y_l = np.zeros(y_r.size) + + idx = np.arange(y_l.size) + trigger = gen_trigger(idx, 2., 0.01, fs) + + y = np.vstack((y_l, y_r, trigger)) + sndio.write(outpath, y.T, rate = fs, format = fmt, enc=enc) return outpath diff --git a/da_stim/gen_da_shaped_noise.py b/da_stim/gen_da_shaped_noise.py new file mode 100755 index 0000000..67db02f --- /dev/null +++ b/da_stim/gen_da_shaped_noise.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import sys +sys.path.insert(0, "../matrix_test/helper_modules") + +import argparse +import os +import errno +import shutil +import re +import fnmatch +import pdb +import numpy as np +from natsort import natsorted +from collections import namedtuple +from pysndfile import PySndfile, sndio +import matplotlib.pyplot as plt + +from gen_da import gen_da_stim + +from pathops import dir_must_exist +try: + from signalops import rolling_window_lastaxis, calc_rms +except ImportError: + from .signalops import rolling_window_lastaxis, block_lfilter, calc_rms + +import scipy.signal as sgnl +from scipy.stats import pearsonr + +from pyswarm import pso + +try: + from lpc import lpc +except ImportError: + from .lpc import lpc + +try: + from filesystem import globDir, organiseWavs, prepareOutDir +except ImportError: + from .filesystem import globDir, organiseWavs, prepareOutDir + + +def block_lfilter_wav(b, a, x, outfile, fmt, fs, blocksize=8192): + ''' + Filter 1D signal in blocks. For use with large signals + ''' + new_state = np.zeros(b.size-1) + sndfile = PySndfile(outfile, 'w', fmt, 1, fs) + i = 0 + y_out = np.zeros(x.size) + while i < x.size: + print("Filtering {0} to {1} of {2}".format(i, i+blocksize, x.size)) + if i+blocksize > x.size: + y, new_state = sgnl.lfilter(b,a,x[i:-1], zi=new_state) + sndfile.write_frames(y) + y_out[i:i+y.size] = y + else: + y, new_state = sgnl.lfilter(b,a,x[i:i+blocksize], zi=new_state) + sndfile.write_frames(y) + y_out[i:i+y.size] = y + i += blocksize + return y_out + + +def synthesize_trial(wavFileMatrix, indexes): + ''' + Using the matrix of alternative words and the selected words for each + column, generate samples from audio files + Returns an array of samples generated by concatenating the selected audio + files + ''' + columnNames = ['a', 'b', 'c', 'd', 'e'] + indexes = np.pad(indexes, ((0, 1)), 'constant', constant_values=0) + indexes = rolling_window_lastaxis(indexes, 2) + offset = 10 + y = np.array([]) + filenames = [] + for name, ind in zip(columnNames, indexes): + if name == 'e': + offset = 1 + wavFilename, wavFilepath = wavFileMatrix[name][(ind[0]*offset)+ind[1]] + wav = PySndfile(wavFilepath) + fs = wav.samplerate() + x = wav.read_frames() + y = np.append(y, x) + filenames.append(wavFilename) + return (y, {'rate': fs, 'format': wav.major_format_str(), 'enc': wav.encoding_str()}, filenames) + + +def gen_audio_stim(da, OutDir, indexes): + if os.path.exists(OutDir): + shutil.rmtree(OutDir) + os.makedirs(OutDir) + wavFiles = globDir(MatrixDir, '*.wav') + wavFileMatrix = organiseWavs(wavFiles) + wavDir = os.path.join(OutDir, "wav") + dir_must_exist(wavDir) + wavDir = os.path.join(wavDir, "noise-sentences") + dir_must_exist(wavDir) + files = [] + n = 0 + o = 0 + for sentenceList in indexes: + n += 1 + o = 0 + files.append([]) + for ind in sentenceList: + o += 1 + y, wavInfo, partnames = synthesize_trial(wavFileMatrix, ind) + fileName = os.path.join(wavDir, 'Trial_{0:02d}_{1:02d}.wav'.format(n, o)) + print("Generating: " + fileName) + sndio.write(fileName, y, **wavInfo) + files[-1].append(fileName) + + return files + +def gen_indexes(): + x = np.repeat(np.arange(10), 5) + x = x.reshape(10, 5) + + y = np.zeros((50, 10, 5), dtype=int) + + # 50 lists + for i in range(50): + x[:, 1] = np.roll(x[:, 1], 1) + x[:, 2] = np.roll(x[:, 2], 2) + x[:, 3] = np.roll(x[:, 3], 3) + x[:, 4] = np.roll(x[:, 4], 4) + y[i] = x.copy() + return y + +def gen_rms(files, OutDir): + rmsFiles = [] + for file in files: + head, tail = os.path.split(file) + tail = os.path.splitext(tail)[0] + tail = tail + "_rms.npy" + dir_must_exist(OutDir) + rmsFilepath = os.path.join(OutDir, tail) + print("Generating: "+rmsFilepath) + y, fs, _ = sndio.read(file) + y_rms = calc_rms(y, round(0.02*fs)) + np.save(rmsFilepath, y_rms) + rmsFiles.append(rmsFilepath) + return rmsFiles + +def detect_silences(rmsFiles, fs, seg_min_size=0.02): + print("Detecting silence in wav files...") + if not seg_min_size: + seg_min_size = np.inf + silences = [] + for envelopeFile in rmsFiles: + env = np.load(envelopeFile) + silence = env < 0.001 + # Get segment start end indexes for all silences in envelope + silentSegs = np.where(np.concatenate(([silence[0]],silence[:-1]!=silence[1:],[True])))[0].reshape(-1, 2) + validSegs = np.diff(silentSegs) > seg_min_size*fs + silences.append(silentSegs[np.repeat(validSegs, 2, axis=1)].reshape(-1, 2)) + return silences + + +def calc_spectrum(files, silences, fs=44100, plot=False): + window = 4096 + sentenceLen = [] + sentenceFFT = [] + + print("Calculating LTASS...") + for ind, file in enumerate(files): + x, fs, _ = sndio.read(file) + f, t, Zxx = sgnl.stft(x, window=np.ones(window), nperseg=window, noverlap=0) + sil = silences[ind] + sTemp = np.zeros((sil.shape[0], t.size), dtype=bool) + for ind3, s in enumerate(sil): + sTemp[ind3, :] = np.logical_and(t > s[0], t < s[1]) + invalidFFT = np.any(sTemp, axis=0) + sentenceFFT.append(np.abs(Zxx[:, ~np.any(sTemp, axis=0)])) + sentenceLen.append(x.size) + sentenceLen = np.array([sentenceLen]).T + sentenceLen = sentenceLen / sentenceLen.max() + sentenceFFT = [x * sentenceLen[i] for i, x in enumerate(sentenceFFT)] + sentenceFFT = np.concatenate([x.T for x in sentenceFFT]) + + grandAvgFFT = np.mean(sentenceFFT, axis=0) + grandAvgFFT = grandAvgFFT / grandAvgFFT.max() + print("Fitting filter to LTASS...") + b = sgnl.firls(2049, np.linspace(0, 1, 2049)[1:], grandAvgFFT[1:]) + if plot: + plt.semilogy(np.abs(sgnl.freqz(b)[1])) + plt.plot(np.linspace(0, 512, 2049), grandAvgFFT) + plt.show() + return b + + +def gen_noise(OutDir, b, fs, s_rms): + print("Generating noise...") + # Generate 10 minutes of white noise + x = np.random.randn(int(fs*60.*20.)) + x /= x.max() + noiseDir = os.path.join(OutDir, 'wav') + noiseRMSDir = 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(noiseRMSDir, 'noise_rms.npy') + rms = np.sqrt(np.mean(y**2)) + np.save(noise_rms_path, rms) + return y + + +def calc_speech_rms(files, silences, rmsDir, fs=44100, plot=False): + ''' + ''' + f = files + sumsqrd = 0.0 + n = 0 + for wavfile, sil in zip(f, silences): + y, fs, _ = sndio.read(wavfile) + t = np.arange(y.size) + sTemp = np.zeros(t.size, dtype=bool) + print("Started") + for ind, s in enumerate(sil): + print("Check {}".format(ind)) + sTemp = np.logical_or(sTemp, np.logical_and(t > s[0], t < s[1])) + print("Done") + silentSamples = np.any(sTemp, axis=0) + y_temp = y[~silentSamples] + sumsqrd += np.sum(y_temp**2) + n += y_temp.size + rms = np.sqrt(sumsqrd/n) + np.save(os.path.join(rmsDir, 'overall_da_rms.npy'), rms) + return rms + #sentenceFFT.append(np.abs(Zxx[:, ~np.any(sTemp, axis=0)])) + + +if __name__ == "__main__": + from pathtype import PathType + # Create commandline interface + parser = argparse.ArgumentParser(description='Generate stimulus for ' + 'training TRF decoder by concatenating ' + 'matrix test materials') + parser.add_argument('--OutDir', type=PathType(exists=None, type='dir'), + default='./stimulus', help='Output directory') + 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") + dir_must_exist(rmsDir) + wavDir = os.path.join(args['OutDir'], "wav") + dir_must_exist(wavDir) + if args['CalcRMS']: + daFile = gen_da_stim(3333, os.path.join(wavDir, '10min_da.wav')) + rmsFiles = gen_rms([daFile], rmsDir) + else: + daFile = globDir(wavDir, '*.wav')[0] + rmsFile = globDir(rmsDir, '*.npy')[0] + silences = detect_silences([rmsFile], 44100, None) + s_rms = calc_speech_rms([daFile], silences, rmsDir) + b = calc_spectrum([daFile], silences) + y = gen_noise(args['OutDir'], b, 44100, s_rms) diff --git a/eeg_mat_train_thread.py b/eeg_mat_train_thread.py index 4a8df69..34f239f 100644 --- a/eeg_mat_train_thread.py +++ b/eeg_mat_train_thread.py @@ -62,7 +62,6 @@ class EEGMatTrainThread(BaseThread): self.reduction_coef = np.load(red_coef) - self.wav_files = [] self.marker_files = [] self.question_files = [] diff --git a/eeg_story_stim/add_triggers.py b/eeg_story_stim/add_triggers.py new file mode 100755 index 0000000..8a1fefa --- /dev/null +++ b/eeg_story_stim/add_triggers.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import sys +sys.path.insert(0, "../matrix_test/helper_modules") + +from filesystem import globDir +from pudb import set_trace +from pysndfile import sndio +from signalops import gen_trigger +import os +import numpy as np + +def main(): + ''' + ''' + wavs = globDir("./stimulus", "*.wav") + for wav in wavs: + x, fs, enc, fmt = sndio.read(wav, return_format=True) + y_r = np.insert(x, 0, np.zeros(fs)) + idx = np.arange(x.shape[0]) + y = np.vstack([x, x, np.zeros(x.shape[0])]).T + trigger = gen_trigger(idx, 2., 0.01, fs) + y[:, 2] = trigger + wav_out = os.path.splitext(wav)[0] + "_trig.wav" + sndio.write(wav_out, y, rate=fs, format=fmt, enc=enc) + +if __name__ == "__main__": + main() diff --git a/eeg_story_stim/odin_4_2 b/eeg_story_stim/odin_4_2 deleted file mode 100644 index 1b35bfe..0000000 --- a/eeg_story_stim/odin_4_2 +++ /dev/null @@ -1,3 +0,0 @@ -1, What items did the dwarves forge? -2, Could they work in peace? -3, Who is, in fact, Gadfly? \ No newline at end of file diff --git a/eeg_story_stim/odin_1_1.csv b/eeg_story_stim/stimulus/odin_1_1.csv similarity index 100% rename from eeg_story_stim/odin_1_1.csv rename to eeg_story_stim/stimulus/odin_1_1.csv diff --git a/eeg_story_stim/odin_1_2.csv b/eeg_story_stim/stimulus/odin_1_2.csv similarity index 100% rename from eeg_story_stim/odin_1_2.csv rename to eeg_story_stim/stimulus/odin_1_2.csv diff --git a/eeg_story_stim/odin_1_3.csv b/eeg_story_stim/stimulus/odin_1_3.csv similarity index 100% rename from eeg_story_stim/odin_1_3.csv rename to eeg_story_stim/stimulus/odin_1_3.csv diff --git a/eeg_story_stim/odin_1_4.csv b/eeg_story_stim/stimulus/odin_1_4.csv similarity index 100% rename from eeg_story_stim/odin_1_4.csv rename to eeg_story_stim/stimulus/odin_1_4.csv diff --git a/eeg_story_stim/odin_4_1.csv b/eeg_story_stim/stimulus/odin_4_1.csv similarity index 100% rename from eeg_story_stim/odin_4_1.csv rename to eeg_story_stim/stimulus/odin_4_1.csv diff --git a/eeg_story_stim/odin_4_3.csv b/eeg_story_stim/stimulus/odin_4_3.csv similarity index 100% rename from eeg_story_stim/odin_4_3.csv rename to eeg_story_stim/stimulus/odin_4_3.csv diff --git a/eeg_story_stim/odin_4_4.csv b/eeg_story_stim/stimulus/odin_4_4.csv similarity index 100% rename from eeg_story_stim/odin_4_4.csv rename to eeg_story_stim/stimulus/odin_4_4.csv diff --git a/eeg_test_thread.py b/eeg_test_thread.py index e60cab1..20773dd 100644 --- a/eeg_test_thread.py +++ b/eeg_test_thread.py @@ -59,6 +59,8 @@ class EEGTestThread(BaseThread): self.question = [] self.response = [] + self.reduction_coef = np.load(red_coef) + # Percent speech inteligibility (estimated using behavioural measure) # to present stimuli at self.si = np.array([20.0, 35.0, 50.0, 65.0, 80.0, 90.0, 100.0]) diff --git a/matrix_test/helper_modules/signalops.py b/matrix_test/helper_modules/signalops.py index b1f58ba..23768f2 100644 --- a/matrix_test/helper_modules/signalops.py +++ b/matrix_test/helper_modules/signalops.py @@ -5,6 +5,7 @@ import queue import sys import threading from pysndfile import PySndfile, construct_format +from scipy.signal import square import sounddevice as sd import soundfile as sf @@ -83,6 +84,24 @@ def block_lfilter(b, a, x, blocksize=8192): i += blocksize return out +def block_process_wav(wavpath, out_wavpath, func, block_size=4096, **args): + ''' + Mix two wav files, applying gains to each + ''' + wav = PySndfile(wavpath_a, 'r') + + out_wav = PySndfile(out_wavpath, 'w', construct_format('wav', 'pcm16'), wav.channels(), wav.samplerate()) + + i = 0 + while i < wav_a.frames(): + if i+block_size > wav_a.frames(): + block_size = wav_a.frames()-i + x = wav_a.read_frames(block_size) + y = func(x, **args) + out_wav.write_frames(x1) + i += block_size + + def block_mix_wavs(wavpath_a, wavpath_b, out_wavpath, a_gain=1., b_gain=1., block_size=4096): ''' Mix two wav files, applying gains to each @@ -104,6 +123,13 @@ def block_mix_wavs(wavpath_a, wavpath_b, out_wavpath, a_gain=1., b_gain=1., bloc i += block_size +def gen_trigger(x, freq, length, fs): + + duty = length*freq + trigger = square(2*np.pi*(x/fs)*freq, duty=duty) + trigger[trigger < 0] = 0 + return trigger + def calc_rms(y, window, plot=False): y_2 = y**2 diff --git a/matrix_test/long_concat_stim/concatenate_stimuli.py b/matrix_test/long_concat_stim/concatenate_stimuli.py index 79f71f0..0b60bf5 100755 --- a/matrix_test/long_concat_stim/concatenate_stimuli.py +++ b/matrix_test/long_concat_stim/concatenate_stimuli.py @@ -10,13 +10,14 @@ import shutil import errno import re import fnmatch -import pdb +import pudb import numpy as np from natsort import natsorted from collections import namedtuple import pysndfile from pysndfile import sndio, PySndfile +from signalops import gen_trigger from pathtype import PathType from tokens_to_words import tokens_to_words, load_component_map @@ -82,29 +83,44 @@ def concatenateStimuli(MatrixDir, OutDir, Length, n): totalSize += size totalSize += int(0.1*fs) if (totalSize/fs) > Length: - y.append(np.zeros(totalSize)) + # total size + 2 second silence at start + y.append(np.zeros((totalSize+2*fs, 3))) parts.append([]) questions.append([]) i += 1 totalSize = 0 - writePtr = 0 + writePtr = 2*fs + idx = np.arange(0, writePtr) + chunk = np.zeros(idx.size) + chunk = np.vstack([chunk, chunk, chunk]).T + trigger = gen_trigger(idx, 2., 0.01, fs) + chunk[:, 2] = trigger + for i, _ in enumerate(y): + y[i][0:writePtr, :] = chunk + i = 0 for wav, word, part in zip(wavFiles, stim_word_rows, stim_part_rows): - if writePtr >= y[i].size: + if writePtr >= y[i].shape[0]: i += 1 - writePtr = 0 + writePtr = fs*2 if i == n: break x, fs, encStr, fmtStr = sndio.read(wav, return_format=True) threeMs = int(0.1*fs) silence = np.zeros(threeMs) chunk = np.append(x, silence) - y[i][writePtr:writePtr + chunk.size] = chunk + + idx = np.arange(writePtr, writePtr+chunk.shape[0]) + chunk = np.vstack([chunk, chunk, np.zeros(chunk.shape[0])]).T + trigger = gen_trigger(idx, 2., 0.01, fs) + chunk[:, 2] = trigger + + y[i][writePtr:writePtr + chunk.shape[0], :] = chunk questions[i].append(word) parts[i].append(part) - writePtr += chunk.size + writePtr += chunk.shape[0] for ind, (data, q, p) in enumerate(zip(y, questions, parts)): pysndfile.sndio.write(os.path.join(OutDir, 'stim_{}.wav'.format(ind)), data, format=fmtStr, enc=encStr) diff --git a/matrix_test/long_concat_stim/generate_questions.py b/matrix_test/long_concat_stim/generate_questions.py deleted file mode 100755 index d23b561..0000000 --- a/matrix_test/long_concat_stim/generate_questions.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys -sys.path.insert(0, "../helper_modules/") - -def main(): - -if __name__ == "__main__": - main() diff --git a/matrix_test/short_concat_stim/short_concat_stim.py b/matrix_test/short_concat_stim/short_concat_stim.py index 6ad62c4..1aeb43f 100755 --- a/matrix_test/short_concat_stim/short_concat_stim.py +++ b/matrix_test/short_concat_stim/short_concat_stim.py @@ -108,6 +108,11 @@ def main(): sample(range(stim_count_half, stim_count-1), n_questions) ]).T a = 0 + silence = np.zeros((88200, 3)) + idx = np.arange(0, silence.shape[0]) + trigger = gen_trigger(idx, 2., 0.01, fs) + silence[:, 2] = trigger + out_wav.write_frames(silence) for ind, (wav, txt) in enumerate(zip(merged_wavs, words)): csv_line = [counter] silence = np.zeros((int(np.random.uniform(int(0.3*44100), int(0.4*44100), 1)), 3)) diff --git a/templates/mat_test_run.html b/templates/mat_test_run.html index e670403..ec4fbbc 100644 --- a/templates/mat_test_run.html +++ b/templates/mat_test_run.html @@ -1,6 +1,7 @@ {% extends 'participant_index.html' %} {% block content %}
+

@@ -100,6 +101,10 @@ // 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); + }); socket.on('stim_done', function(msg) { $('#main-div').find('input, textarea, button, select').removeAttr('disabled'); diff --git a/trigger_test/test_1.py b/trigger_test/test_1.py new file mode 100644 index 0000000..9ef2f44 --- /dev/null +++ b/trigger_test/test_1.py @@ -0,0 +1,22 @@ +from pysndfile import sndio +import numpy as np +import pdb +import matplotlib.pyplot as plt + +def main(): + ''' + Generate train of equally spaced clicks + ''' + fs = 44100 + n = np.arange(fs*10) + trig_s = np.where(np.mod(n, fs/2.) == 0) + click = np.ones(int(0.01*fs)) + y = np.zeros(n.size) + for i in trig_s[0]: + y[i:i+click.size] = click + + sndio.write("./trig_test.wav", y, fs, format='wav', enc='pcm16') + + pdb.set_trace() +if __name__ == "__main__": + main()