diff --git a/ITU_P56.py b/ITU_P56.py index f597127..7e6291e 100644 --- a/ITU_P56.py +++ b/ITU_P56.py @@ -22,6 +22,7 @@ import scipy.signal as signal import matplotlib.pyplot as plt def asl_P56(x, fs, nbits): + nbits = int(nbits) eps = np.finfo(float).eps x = x[:] # make sure x is column vector if len(x.shape) < 2: diff --git a/ITU_P56/asl_P56_Fred_v2.m b/ITU_P56/asl_P56_Fred_v2.m index 4cd8f84..7d2d18b 100644 --- a/ITU_P56/asl_P56_Fred_v2.m +++ b/ITU_P56/asl_P56_Fred_v2.m @@ -26,6 +26,7 @@ g = exp( -1/( fs* T)); % smoothing factor in envelop detection c( 1: thres_no)= 2.^ (-15: thres_no- 16); % vector with thresholds from one quantizing level up to half the maximum % code, at a step of 2, in the case of 16bit samples, from 2^-15 to 0.5; + a( 1: thres_no) = 0; % activity counter for each level threshold hang( 1: thres_no) = I; % hangover counter for each level threshold diff --git a/calibration/calibration_patch.maxpat b/calibration/calibration_patch.maxpat index 4beb482..ad09601 100755 --- a/calibration/calibration_patch.maxpat +++ b/calibration/calibration_patch.maxpat @@ -3,8 +3,8 @@ "fileversion" : 1, "appversion" : { "major" : 8, - "minor" : 0, - "revision" : 8, + "minor" : 1, + "revision" : 1, "architecture" : "x64", "modernui" : 1 } @@ -159,11 +159,12 @@ "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-18", + "linecount" : 2, "maxclass" : "comment", "numinlets" : 1, "numoutlets" : 0, - "patching_rect" : [ 408.0, 215.0, 81.75, 20.0 ], - "text" : "70 dB SPL" + "patching_rect" : [ 408.0, 215.0, 81.75, 33.0 ], + "text" : "70 dB Peak SPL" } } @@ -684,8 +685,8 @@ "followglobaltempo" : 0, "formantcorrection" : 0, "mode" : "basic", - "originallength" : [ 866265.382312924717553, "ticks" ], - "originaltempo" : 119.999999999999943, + "originallength" : [ 866265.382312924601138, "ticks" ], + "originaltempo" : 119.999999999999929, "pitchcorrection" : 0, "quality" : "basic", "timestretch" : [ 0 ] @@ -969,8 +970,8 @@ "followglobaltempo" : 0, "formantcorrection" : 0, "mode" : "basic", - "originallength" : [ 137594.144217686989577, "ticks" ], - "originaltempo" : 119.999999999999929, + "originallength" : [ 137594.144217686960474, "ticks" ], + "originaltempo" : 119.999999999999915, "pitchcorrection" : 0, "quality" : "basic", "timestretch" : [ 0 ] diff --git a/calibration/gen_calibrated_stim.py b/calibration/gen_calibrated_stim.py new file mode 100755 index 0000000..540ef30 --- /dev/null +++ b/calibration/gen_calibrated_stim.py @@ -0,0 +1,38 @@ +#!/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 + +import matplotlib.pyplot as plt + +def main(): + ''' + ''' + fs = 44100 + f = 1000.0 + n = np.arange(fs * 60 * 5) + y = np.sin(2*np.pi*f*n/fs) + coef = np.load('./out/calibration_coefficients/click_cal_coef.npy') + y *= coef + 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__": + main() diff --git a/calibration/gen_stim_for_cal.py b/calibration/gen_stim_for_cal.py new file mode 100755 index 0000000..0f1227f --- /dev/null +++ b/calibration/gen_stim_for_cal.py @@ -0,0 +1,74 @@ +#!/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 +from shutil import copyfile + +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 = -5. + 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 = ["../tone_stim/stimulus/tone_2000/tone_3000_2000Hz.wav", "../tone_stim/stimulus/tone_500/tone_3000_500Hz.wav"] + story_dir = "../eeg_story_stim/stimulus" + mat_dir = "../matrix_test/speech_components" + noise_file = "../matrix_test/behavioural_stim/stimulus/wav/noise/noise_norm.wav" + da_noise_file = "../da_stim/noise/wav/noise/noise_norm.wav" + + story_wavs = globDir(story_dir, '*.wav') + mat_wavs = globDir(mat_dir, '*.wav') + + out_dir = "./out" + out_red_dir = os.path.join(out_dir, 'reduction_coefficients') + out_stim_dir = os.path.join(out_dir, 'stimulus') + dir_must_exist(out_dir) + dir_must_exist(out_red_dir) + dir_must_exist(out_stim_dir) + + 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") + + mat_cal_stim = "../matrix_test/long_concat_stim/out/stim/stim_0.wav" + da_cal_stim = "./out/stimulus/1k_tone.wav" + # click_cal_stim = "../tone_stim/stimulus/tone_2000/tone_3000_2000Hz.wav" + story_cal_stim = "../eeg_story_stim/stimulus/odin_1_1.wav" + + mat_out_stim = os.path.join(out_stim_dir, "mat_cal_stim.wav") + # click_out_stim = os.path.join(out_stim_dir, "click_cal_stim.wav") + da_out_stim = os.path.join(out_stim_dir, "1k_cal_stim.wav") + story_out_stim = os.path.join(out_stim_dir, "story_cal_stim.wav") + + block_process_wav(mat_cal_stim, mat_out_stim, lambda x: x * mat_coef) + block_process_wav(story_cal_stim, story_out_stim, lambda x: x * story_coef) + block_process_wav(da_cal_stim, da_out_stim, lambda x: x * da_coef) + # block_process_wav(click_cal_stim, click_out_stim, lambda x: x * click_coef) + #copyfile(click_cal_stim, click_out_stim) + + +if __name__ == "__main__": + main() diff --git a/calibration/gen_uncalibrated_tone.py b/calibration/gen_uncalibrated_tone.py new file mode 100755 index 0000000..e118cc2 --- /dev/null +++ b/calibration/gen_uncalibrated_tone.py @@ -0,0 +1,27 @@ +#!/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 + +import matplotlib.pyplot as plt + +def main(): + ''' + ''' + fs = 44100 + f = 1000.0 + n = np.arange(fs * 60 * 5) + y = np.sin(2*np.pi*f*n/fs) + y = np.array([y, y]).T + sndio.write("./out/stimulus/1k_tone.wav", y, fs, format='wav', enc='pcm16') + + +if __name__ == "__main__": + main() diff --git a/da_test_thread.py b/da_test_thread.py index 18e8ee0..345e257 100644 --- a/da_test_thread.py +++ b/da_test_thread.py @@ -44,7 +44,7 @@ class DaTestThread(BaseThread): ''' def __init__(self, sessionFilepath=None, stimFolder='./tone_stim/stimulus', - noiseFilepath="./tone_stim/noise/wav/noise/noise.wav", + noiseFilepath="./tone_stim/noise/wav/noise/noise_norm.wav", noiseintensityFilepath="./tone_stim/noise/intensity/noise_intensity.npy", red_coef="./calibration/out/reduction_coefficients/tone_red_coef.npy", cal_coef="./calibration/out/calibration_coefficients/tone_cal_coef.npy", diff --git a/eeg_story_stim/add_triggers.py b/eeg_story_stim/add_triggers.py index 8a1fefa..7a82680 100755 --- a/eeg_story_stim/add_triggers.py +++ b/eeg_story_stim/add_triggers.py @@ -16,8 +16,8 @@ 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]) + breakpoint() y = np.vstack([x, x, np.zeros(x.shape[0])]).T trigger = gen_trigger(idx, 2., 0.01, fs) y[:, 2] = trigger diff --git a/eeg_story_stim/stimulus/odin_4_1.csv b/eeg_story_stim/stimulus/odin_4_1.csv index 004e0e8..de0236c 100644 --- a/eeg_story_stim/stimulus/odin_4_1.csv +++ b/eeg_story_stim/stimulus/odin_4_1.csv @@ -1,3 +1,3 @@ 1, Who are the characters of this story? -2, What are they up to? -3, What did they wager? \ No newline at end of file +2, What are the characters doing? +3, What did the characters wager? \ No newline at end of file diff --git a/eeg_story_train_thread.py b/eeg_story_train_thread.py index 5077679..8e72e49 100644 --- a/eeg_story_train_thread.py +++ b/eeg_story_train_thread.py @@ -19,6 +19,9 @@ import csv import pdb import dill +import sounddevice as sd +from hearing_loss_sim import apply_hearing_loss_sim + symb_dict = { True: 10003, False: 10007 @@ -38,7 +41,7 @@ class EEGStoryTrainThread(BaseThread): Thread for running server side matrix test operations ''' def __init__(self, sessionFilepath=None, - stimFolder='./eeg_story_stim/', nTrials=2, + stimFolder='./eeg_story_stim/stimulus/', nTrials=2, socketio=None, participant=None, srt_50=None, s_50=None): self.test_name = 'eeg_story_train' self.stimDir = stimFolder @@ -90,6 +93,7 @@ class EEGStoryTrainThread(BaseThread): if self._stopevent.isSet() or self.finishTest: break # Play concatenated matrix sentences at set SNR + self.playStimulus(wav) self.waitForResponse() if self._stopevent.isSet() or self.finishTest: @@ -142,21 +146,38 @@ class EEGStoryTrainThread(BaseThread): def displayInstructions(self): self.socketio.emit('display_instructions', namespace='/main') - - def playStimulus(self, wav_file, replay=False): + def playStimulus(self, wav): + ''' + Output audio stimulus from numpy array + ''' self.newResp = False self.socketio.emit("stim_playing", namespace="/main") - # if not replay: - # self.y = self.generateTrial(self.snr) + x, fs, _ = sndio.read(wav) + if self.participant.parameters['hl_sim_active']: + y = apply_hearing_loss_sim(x, fs) # Play audio - # sd.play(self.y, self.fs, blocking=True) if not self.dev_mode: - self.play_wav(wav_file, 'finish_test') + sd.play(y, fs, blocking=True) else: - self.play_wav('./da_stim/DA_170.wav', 'finish_test') - + self.play_wav('./da_stim/DA_170.wav', '') self.socketio.emit("stim_done", namespace="/main") + # def playStimulus(self, wav_file, replay=False): + + # x, fs, _ = sndio.read(wav_file) + # self.newResp = False + # self.socketio.emit("stim_playing", namespace="/main") + # # if not replay: + # # self.y = self.generateTrial(self.snr) + # # Play audio + # # sd.play(self.y, self.fs, blocking=True) + # if not self.dev_mode: + # self.play_wav(wav_file, 'finish_test') + # else: + # self.play_wav('./da_stim/DA_170.wav', 'finish_test') + + # self.socketio.emit("stim_done", namespace="/main") + def saveState(self, out="test_state.pkl"): saveDict = {k:self.__dict__[k] for k in self.toSave} diff --git a/eeg_test_thread.py b/eeg_test_thread.py index 4d3f162..3a3be14 100644 --- a/eeg_test_thread.py +++ b/eeg_test_thread.py @@ -10,7 +10,10 @@ import numpy as np import pandas as pd from shutil import copy2 import re +import sounddevice as sd +from ITU_P56 import asl_P56 +from hearing_loss_sim import apply_hearing_loss_sim from test_base import BaseThread, run_test_thread from scipy.special import logit from config import socketio @@ -52,7 +55,7 @@ class EEGTestThread(BaseThread): ''' def __init__(self, sessionFilepath=None, listFolder="./matrix_test/short_concat_stim/out", - noiseFilepath="./matrix_test/behavioural_stim/stimulus/wav/noise/noise.wav", + noiseFilepath="./matrix_test/behavioural_stim/stimulus/wav/noise/noise_norm.wav", red_coef="./calibration/out/reduction_coefficients/mat_red_coef.npy", cal_coef="./calibration/out/calibration_coefficients/mat_cal_coef.npy", socketio=None, participant=None, srt_50=None, s_50=None): @@ -116,7 +119,7 @@ class EEGTestThread(BaseThread): logger.info("{0:<25}".format("Current question 2:") + f"{' '.join(q[1][:-1])} | Answer: {q[1][-1]}") logger.info("{0:<25}".format("Current SNR(-srt):") + f"{snr}") # Play concatenated matrix sentences at set SNR - self.playStimulusWav(wav) + self.playStimulus(wav) self.setMatrix(q) self.saveState(out=self.backupFilepath) logger.info("-"*78) @@ -171,8 +174,8 @@ class EEGTestThread(BaseThread): def finaliseResults(self): toSave = ['marker_files', 'clinPageLoaded', 'wav_files', 'participant', 'response', 'backupFilepath', 'noise_path', 'question_files', - 'si', 'question', 'answers', 'trial_ind'] - saveDict = {k:self.__dict__[k] for k in toSave} + 'question', 'answers', 'trial_ind'] + saveDict = {k:self.__dict__[k] for k in toSave if k in self.__dict__.keys()} self.participant['eeg_test'].update(saveDict) self.participant.save("eeg_test") backup_path = os.path.join(self.participant.data_paths['eeg_test'], @@ -240,20 +243,22 @@ class EEGTestThread(BaseThread): csv_files = natsorted(globDir(stim_dir, "*.csv")) marker_file = csv_files[0] question_files = csv_files[1:] - rms_file = globDir(stim_dir, "*.npy")[0] - speech_rms = float(np.load(rms_file)) + # rms_file = globDir(stim_dir, "*.npy")[0] + # speech_rms = float(np.load(rms_file)) snr = snrs[:, ind] audio, fs, enc, fmt = sndio.read(wav, return_format=True) speech = audio[:, :2] triggers = audio[:, 2] + speech_rms, _, _ = asl_P56(speech, fs, 16.) wf = [] wm = [] for ind2, s in enumerate(snr): start = randint(0, noise_file.frames()-speech.shape[0]) noise_file.seek(start) noise = noise_file.read_frames(speech.shape[0]) - noise_rms = np.sqrt(np.mean(noise**2)) + #noise_rms = np.sqrt(np.mean(noise**2)) + noise_rms = asl_P56(noise, fs, 16) snr_fs = 10**(-s/20) if snr_fs == np.inf: snr_fs = 0. @@ -302,6 +307,22 @@ class EEGTestThread(BaseThread): self.answers[:] = np.nan + def playStimulus(self, wav): + ''' + Output audio stimulus from numpy array + ''' + self.newResp = False + self.socketio.emit("stim_playing", namespace="/main") + x, fs, _ = sndio.read(wav) + if self.participant.parameters['hl_sim_active']: + y = apply_hearing_loss_sim(x, fs) + # Play audio + if not self.dev_mode: + sd.play(y, fs, blocking=True) + else: + self.play_wav('./da_stim/DA_170.wav', '') + self.socketio.emit("{}_stim_done".format(self.test_name), namespace="/main") + def submitTestResponse(self, msg): ''' Get and store participant response for current trial diff --git a/gen_participants.py b/gen_participants.py index 4d9c8b0..8512b20 100755 --- a/gen_participants.py +++ b/gen_participants.py @@ -18,6 +18,7 @@ import os from pathlib import Path from datetime import datetime import re +from copy import deepcopy logger = logging.getLogger(__name__) nowtime = datetime.now() @@ -42,7 +43,7 @@ def find_participants(folder='./participant_data/'): if os.path.isdir(os.path.join(folder,o))] for path in part_folder: part_key = os.path.basename(path) - participants[part_key] = Participant(participant_dir=path) + participants[part_key] = deepcopy(Participant(participant_dir=path)) participants[part_key].load('info') participants[part_key].load('parameters') return participants @@ -144,6 +145,7 @@ class Participant: ''' ''' folder = os.path.join(self.participant_dir, data_key) + # print(f"Participant {self.data['info']['number']}: {folder}") with open(os.path.join(folder, "{}.pkl".format(data_key)), 'rb') as f: self.data[data_key].update(dill.load(f)) diff --git a/hearing_loss_sim.py b/hearing_loss_sim.py new file mode 100644 index 0000000..44ec554 --- /dev/null +++ b/hearing_loss_sim.py @@ -0,0 +1,13 @@ +from scipy import signal +import matplotlib.pyplot as plt +import numpy as np + +def apply_hearing_loss_sim(x, fs, channels=[0, 1]): + b, a = signal.butter(4, 1170.0/(fs/2.), 'low') + if len(x.shape) < 2: + x = x[:, np.newaxis] + for channel in channels: + x[:, channel] = signal.filtfilt(b, a, x[:, channel]) + return x + # w, h = signal.freqs(b, a) + diff --git a/matrix_test/behavioural_stim/gen_noise.py b/matrix_test/behavioural_stim/gen_noise.py index 74652d8..3c199ae 100755 --- a/matrix_test/behavioural_stim/gen_noise.py +++ b/matrix_test/behavioural_stim/gen_noise.py @@ -3,6 +3,8 @@ import sys #sys.path.insert(0, "../helper_modules") #sys.path.insert(0, "../matrix_test/helper_modules/") +sys.path.insert(0, "../../") +sys.path.insert(0, "../helper_modules") import argparse import os @@ -16,6 +18,11 @@ from natsort import natsorted from collections import namedtuple from pysndfile import PySndfile, sndio import matplotlib.pyplot as plt +from ITU_P56 import asl_P56 +from pathlib import Path + +from multiprocessing.dummy import Pool as ThreadPool +import multiprocessing from pathops import dir_must_exist from signalops import rolling_window_lastaxis, block_lfilter, calc_rms, block_process_wav @@ -191,7 +198,7 @@ def calc_spectrum(files, silences, fs=44100, plot=False): return b -def gen_noise(OutDir, b, fs, s_rms): +def gen_noise(OutDir, b, fs): print("Generating noise...") # Generate 10 minutes of white noise x = np.random.randn(int(fs*60.*5.)) @@ -203,9 +210,14 @@ def gen_noise(OutDir, b, fs, s_rms): dir_must_exist(noiseDir) y, y_max = block_lfilter_wav(b, [1.0], x, os.path.join(noiseDir, 'noise.wav'), 65538, 44100) block_process_wav(os.path.join(noiseDir, 'noise.wav'), os.path.join(noiseDir, 'noise_norm.wav'), lambda x: x / (y_max * 1.05)) + noise_norm_wav = PySndfile(os.path.join(noiseDir, 'noise_norm.wav'), 'r') noise_rms_path = os.path.join(noiseRMSDir, 'noise_rms.npy') + y = noise_norm_wav.read_frames(fs*60) y = y/(np.abs(y).max() * 0.95) - rms = np.sqrt(np.mean(y**2)) + # rms = np.sqrt(np.mean(y**2)) + rms, _, _ = asl_P56(y, fs, 16) + print(f"Noise level: {rms}") + peak = np.abs(y).max() np.save(noise_rms_path, rms) np.save('./stimulus/peak/noise_peak.npy', peak) @@ -215,21 +227,43 @@ def gen_noise(OutDir, b, fs, s_rms): def calc_speech_rms(files, silences, rmsDir, fs=44100, plot=False): ''' ''' + files = files[:3] + #silences = silences[:3] f = sum(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((sil.shape[0], t.size), dtype=bool) - for ind3, s in enumerate(sil): - sTemp[ind3, :] = np.logical_and(t > s[0], t < s[1]) - 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_speech_rms.npy'), rms) + n_files = len(f) + #for ind, (wavfile, sil) in enumerate(zip(f, silences)): + def level_calc(args): + ind, wavfile = args + x, fs, _ = sndio.read(wavfile) + level = asl_P56(x, fs, 16.)[0] + print(f"Calculated level of {Path(wavfile).name} ({ind+1}/{n_files}): {level}") + return level + + # Make the Pool of workers + pool = ThreadPool(multiprocessing.cpu_count()-1) + # Open the urls in their own threads + # and return the results + levels = pool.map(level_calc, enumerate(f)) + #close the pool and wait for the work to finish + pool.close() + pool.join() + rms = np.mean(levels) + + # f = sum(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((sil.shape[0], t.size), dtype=bool) + # for ind3, s in enumerate(sil): + # sTemp[ind3, :] = np.logical_and(t > s[0], t < s[1]) + # 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_speech_rms.npy'), rms) return rms #sentenceFFT.append(np.abs(Zxx[:, ~np.any(sTemp, axis=0)])) @@ -268,4 +302,4 @@ if __name__ == "__main__": silences = detect_silences(rmsFiles, 44100) s_rms = calc_speech_rms(wavFiles, silences, rmsDir) b = calc_spectrum(wavFiles, silences) - y = gen_noise(args['OutDir'], b, 44100, s_rms) + y = gen_noise(args['OutDir'], b, 44100) diff --git a/matrix_test/helper_modules/signalops.py b/matrix_test/helper_modules/signalops.py index 73f9775..bd961f0 100644 --- a/matrix_test/helper_modules/signalops.py +++ b/matrix_test/helper_modules/signalops.py @@ -98,6 +98,7 @@ def block_process_wav(wavpath, out_wavpath, func, block_size=4096, **args): y = func(x, **args) out_wav.write_frames(y) i += block_size + del out_wav def window_rms(a, window_size): print("Squaring...") diff --git a/matrix_test/short_concat_stim/short_concat_stim.py b/matrix_test/short_concat_stim/short_concat_stim.py index 1aeb43f..87c6685 100755 --- a/matrix_test/short_concat_stim/short_concat_stim.py +++ b/matrix_test/short_concat_stim/short_concat_stim.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import sys sys.path.insert(0, "../helper_modules/") +sys.path.insert(0, "../../") import os from filesystem import globDir @@ -17,17 +18,27 @@ import csv from copy import copy from contextlib import ExitStack from scipy.signal import square +from ITU_P56 import asl_P56 +from pathlib import Path + def calc_potential_max(stim_folder, noise_filepath, out_dir): max_wav_samp = 0 max_wav_rms = 0 wavs = globDir(stim_folder, '*.wav') - for wav in wavs: + n_files = len(wavs) + for ind, wav in enumerate(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))]) + #max_wav_rms = np.max([max_wav_rms, np.sqrt(np.mean(x**2))]) + level = asl_P56(x, fs, 16.)[0] + max_wav_rms = np.max([max_wav_rms, ]) + print(f"Calculated level of {Path(wav).name} ({ind+1}/{n_files}): {level}") x, fs, enc = sndio.read(noise_filepath) - noise_rms = np.sqrt(np.mean(x**2)) + # noise_rms = np.sqrt(np.mean(x**2)) + print(f"Calculating level of {Path(noise_filepath).name}") + noise_rms, _, _ = asl_P56(x, fs, 16.) + print(f"Calculated level of {Path(noise_filepath).name}: {noise_rms}") max_noise_samp = max(np.abs(x)) snr = -15.0 @@ -56,7 +67,7 @@ def main(): dir_must_exist(wav_dir) dir_must_exist(noise_dir) - noise_filepath = "../behavioural_stim/stimulus/wav/noise/noise.wav" + noise_filepath = "../behavioural_stim/stimulus/wav/noise/noise_norm.wav" folders = os.listdir(base_dir) folders = natsorted(folders)[1:15] diff --git a/matrix_test_thread.py b/matrix_test_thread.py index 7d8c885..02d363c 100644 --- a/matrix_test_thread.py +++ b/matrix_test_thread.py @@ -21,6 +21,9 @@ import sounddevice as sd import pdb from config import socketio +from hearing_loss_sim import apply_hearing_loss_sim +from ITU_P56 import asl_P56 +from pathlib import Path import logging logger = logging.getLogger(__name__) @@ -143,6 +146,7 @@ class MatTestThread(BaseThread): self.loadNoise(noiseFilepath, noiseRMSFilepath) self.dev_mode = False + self.audio_cal = False def displayInstructions(self): @@ -185,6 +189,8 @@ class MatTestThread(BaseThread): self.lists[0][currentSentenceInd], self.listsRMS[0][currentSentenceInd] ) + if self.participant.parameters['hl_sim_active']: + self.y = apply_hearing_loss_sim(self.y, self.fs, channels=[0]) # Define words presented in the current trial self.currentWords = self.listsString[0][currentSentenceInd] @@ -193,7 +199,11 @@ class MatTestThread(BaseThread): logger.info("{0:<25}".format("Current track index:") + f"{self.adTrInd}") logger.info("{0:<25}".format("Current trial number:") + f"{self.trialN}") logger.info("{0:<25}".format("Current SNR:") + f"{self.adaptiveTracks[self.adTrInd].snr}") - self.playStimulus(self.y, self.fs) + if self.audio_cal: + y, fs, fmt = sndio.read('./calibration/out/stimulus/mat_cal_stim.wav') + self.playStimulus(y, fs) + else: + self.playStimulus(self.y, self.fs) self.waitForResponse() self.checkSentencesAvailable() if self.finishTest: @@ -388,9 +398,9 @@ class MatTestThread(BaseThread): # Get data for each sentence for fp, words, level_file in zip(listAudiofiles, csv_reader, levels): # Read in audio file and calculate it's RMS - level = loadmat(level_file) x, self.fs, _ = sndio.read(fp) - x_rms = np.sqrt(np.mean(x**2)) + logger.info(f"Calculating level for {Path(fp).name}") + x_rms, _, _ = asl_P56(x, self.fs, 16.) self.lists[-1].append(x) self.listsRMS[-1].append(x_rms) self.listsString[-1].append(words) @@ -531,7 +541,7 @@ class AdaptiveTrack(): x_noise *= x_rms/self.noise_rms y = x_noise # Set speech to start 500ms after the noise, scaled to the desired SNR - sigStart = random.randint(self.fs, round(2*self.fs)) + sigStart = random.randint(round(self.fs/2.), round(2*self.fs)) y[sigStart:sigStart+x.size] += x*snr_fs y *= self.reduction_coef return y diff --git a/templates/eeg_setup.html b/templates/eeg_setup.html index 5dc5246..51cdb64 100644 --- a/templates/eeg_setup.html +++ b/templates/eeg_setup.html @@ -20,13 +20,13 @@