Implementing calibration

This commit is contained in:
2019-01-13 22:24:23 +00:00
parent fca6d6bead
commit 95aa12ac7c
25 changed files with 486 additions and 951 deletions
+41
View File
@@ -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')
View File
+55
View File
@@ -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()
-929
View File
@@ -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" : [ ]
}
}
+9 -1
View File
@@ -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__":
View File
+10 -2
View File
@@ -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
+260
View File
@@ -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)
-1
View File
@@ -62,7 +62,6 @@ class EEGMatTrainThread(BaseThread):
self.reduction_coef = np.load(red_coef)
self.wav_files = []
self.marker_files = []
self.question_files = []
+28
View File
@@ -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()
-3
View File
@@ -1,3 +0,0 @@
1, What items did the dwarves forge?
2, Could they work in peace?
3, Who is, in fact, Gadfly?
+2
View File
@@ -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])
+26
View File
@@ -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
@@ -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)
@@ -1,8 +0,0 @@
#!/usr/bin/env python3
import sys
sys.path.insert(0, "../helper_modules/")
def main():
if __name__ == "__main__":
main()
@@ -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))
+5
View File
@@ -1,6 +1,7 @@
{% extends 'participant_index.html' %}
{% block content %}
<div id="main-div" class="outer">
<h3 id="question"></h3>
<div class="mat_grid">
<button type="button" href="#" disabled id="mat-0" class="Btn Btn-outline-secondary mat-button">Peter</button>
<button type="button" href="#" disabled id="mat-1" class="Btn Btn-outline-secondary mat-button">got</button>
@@ -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');
+22
View File
@@ -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()