def block_mix_wavs(wavpath_a, wavpath_b, out_wavpath, a_gain=1., b_gain=1., block_size=4096, mute_left=False): ''' Mix two wav files, applying gains to each ''' wav_a = PySndfile(wavpath_a, 'r') wav_b = PySndfile(wavpath_b, 'r') out_wav = PySndfile(out_wavpath, 'w', construct_format('wav', 'pcm16'), wav_a.channels(), wav_a.samplerate()) i = 0 while i < wav_a.frames(): if i + block_size > wav_a.frames(): block_size = wav_a.frames() - i x1 = wav_a.read_frames(block_size) x2 = wav_b.read_frames(block_size) x1[:, :2] *= a_gain x2 *= b_gain if x1.shape[1] == 3: y = np.zeros(x1.shape) y[:, 0] = x1[:, 0] + x2 y[:, 1] = x1[:, 1] + x2 y[:, 2] = x1[:, 2] if mute_left: y[:, 0] = 0.0 else: y = x1 + x2 out_wav.write_frames(y) i += block_size
def block_process_wav(wavpath, out_wavpath, func, block_size=4096, **args): ''' Mix two wav files, applying gains to each ''' wav = PySndfile(wavpath, 'r') out_wav = PySndfile(out_wavpath, 'w', construct_format('wav', 'pcm16'), wav.channels(), wav.samplerate()) i = 0 while i < wav.frames(): if i + block_size > wav.frames(): block_size = wav.frames() - i x = wav.read_frames(block_size) y = func(x, **args) out_wav.write_frames(y) i += block_size del out_wav
def main(): wavs = globDir('./out/stim/', '*.wav') silences = globDir('./out/stim/', 'stim_*_silence.npy') outDir = "./out/stim/" for wav, sil in zip(wavs, silences): snd = PySndfile(wav, 'r') fs = int(snd.samplerate()) s = np.load(sil) sil_bool = slice_to_bool(s, snd.frames()) rms = np.sqrt(np.mean(np.abs(snd.read_frames()[~sil_bool]**2))) head, tail = os.path.split(wav) tail = os.path.splitext(tail)[0] tail = tail + "_rms.npy" rms_filepath = os.path.join(outDir, tail) np.save(rms_filepath, rms)
def __init__(self, fn, sr=None, chns=None, blksz=2**16, dtype=np.float32): fnd = False if not fnd and (PySndfile is not None): try: sf = PySndfile(fn, mode='r') except IOError: pass else: if (sr is None or sr == sf.samplerate()) and ( chns is None or chns == sf.channels()): # no resampling required self.channels = sf.channels() self.samplerate = sf.samplerate() self.frames = sf.frames() self.rdr = sndreader(sf, blksz, dtype=dtype) fnd = True if not fnd: ffmpeg = findfile('ffmpeg') or findfile('avconv') if ffmpeg is not None: pipe = sp.Popen([ffmpeg, '-i', fn, '-'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) fmtout = pipe.stderr.read() if (sys.version_info > (3, 0)): fmtout = fmtout.decode() m = re.match( r"^(ffmpeg|avconv) version.*Duration: (\d\d:\d\d:\d\d.\d\d),.*Audio: (.+), (\d+) Hz, (.+), (.+), (\d+) kb/s", " ".join(fmtout.split('\n'))) if m is not None: self.samplerate = int(m.group(4)) if not sr else int(sr) chdef = m.group(5) if chdef.endswith(" channels") and len(chdef.split()) == 2: self.channels = int(chdef.split()[0]) else: try: self.channels = { 'mono': 1, '1 channels (FL+FR)': 1, 'stereo': 2, 'hexadecagonal': 16 }[chdef] if not chns else chns except: print(f"Channel definition '{chdef}' unknown") raise dur = reduce(lambda x, y: x * 60 + y, list(map(float, m.group(2).split(':')))) self.frames = int( dur * self.samplerate ) # that's actually an estimation, because of potential resampling with round-off errors pipe = sp.Popen( [ ffmpeg, '-i', fn, '-f', 'f32le', '-acodec', 'pcm_f32le', '-ar', str(self.samplerate), '-ac', str(self.channels), '-' ], # bufsize=self.samplerate*self.channels*4*50, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) def rdr(): bufsz = (blksz // self.channels) * self.channels * 4 while True: data = pipe.stdout.read(bufsz) if len(data) == 0: break data = np.fromstring(data, dtype=dtype) yield data.reshape((-1, self.channels)).T self.rdr = rdr() fnd = True if not fnd: raise IOError("Format not usable")
def loadStimulus(self): ''' ''' self.participant.load('mat_test') try: srt_50 = self.participant.data['mat_test']['srt_50'] s_50 = self.participant.data['mat_test']['s_50'] except KeyError: raise KeyError( "Behavioural matrix test results not available, make " "sure the behavioural test has been run before " "running this test.") save_dir = self.participant.data_paths['eeg_test/stimulus'] ''' # Estimate speech intelligibility thresholds using predicted # psychometric function s_50 *= 0.01 x = logit(self.si * 0.01) snrs = (x/(4*s_50))+srt_50 snrs = np.append(snrs, np.inf) snr_map = pd.DataFrame({"speech_intel" : np.append(self.si, 0.0), "snr": snrs}) snr_map_path = os.path.join(save_dir, "snr_map.csv") snr_map.to_csv(snr_map_path) snrs = np.repeat(snrs[np.newaxis], 4, axis=0) snrs = roll_independant(snrs, np.array([0,-1,-2,-3])) stim_dirs = [x for x in os.listdir(self.listDir) if os.path.isdir(os.path.join(self.listDir, x))] shuffle(stim_dirs) ''' snrs = self.participant.data['parameters']['decoder_test_SNRs'] + srt_50 stim_dirs = [ x for x in os.listdir(self.listDir) if os.path.isdir(os.path.join(self.listDir, x)) ] ordered_stim_dirs = [] for ind in self.participant_parameters['decoder_test_lists']: for folder in stim_dirs: if re.match(f'Stim_({int(ind)})', folder): ordered_stim_dirs.append(folder) # ordered_stim_dirs *= int(len(snrs)) noise_file = PySndfile(self.noise_path, 'r') wav_files = [] wav_metas = [] question = [] marker_files = [] self.socketio.emit('test_stim_load', namespace='/main') for ind, dir_name in enumerate(ordered_stim_dirs[:snrs.shape[1]]): logger.debug( f"Processing list directory {ind+1} of {snrs.shape[1]}") stim_dir = os.path.join(self.listDir, dir_name) wav = globDir(stim_dir, "*.wav")[0] 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)) 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.) rms_no_silences(speech, fs, -30.) 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 = asl_P56(noise, fs, 16) snr_fs = 10**(-s / 20) if snr_fs == np.inf: snr_fs = 0. elif snr_fs == -np.inf: raise ValueError( "Noise infinitely louder than signal at snr: {}". format(snr)) noise = noise * (speech_rms / noise_rms) out_wav_path = os.path.join( save_dir, "Stim_{0}_{1}.wav".format(ind, ind2)) out_meta_path = os.path.join( save_dir, "Stim_{0}_{1}.npy".format(ind, ind2)) with np.errstate(divide='raise'): try: out_wav = (speech + (np.stack([noise, noise], axis=1) * snr_fs)) * self.reduction_coef except: set_trace() out_wav = np.concatenate([out_wav, triggers[:, np.newaxis]], axis=1) sndio.write(out_wav_path, out_wav, fs, fmt, enc) np.save(out_meta_path, s) wf.append(out_wav_path) wm.append(out_meta_path) wav_metas.append(wm) wav_files.append(wf) out_marker_path = os.path.join(save_dir, "Marker_{0}.csv".format(ind)) marker_files.append(out_marker_path) copyfile(marker_file, out_marker_path) for q_file in question_files: out_q_path = os.path.join( save_dir, "Questions_{0}_{1}.csv".format(ind, ind2)) self.question_files.append(out_q_path) copyfile(q_file, out_q_path) for q_file_path in question_files: q = [] with open(q_file_path, 'r') as q_file: q_reader = csv.reader(q_file) for line in q_reader: q.append(line) question.append(q) self.wav_files = [item for sublist in wav_files for item in sublist] self.wav_metas = [item for sublist in wav_metas for item in sublist] self.question.extend(question) for item in marker_files: self.marker_files.extend([item] * 4) self.answers = np.empty(np.shape(self.question)[:2]) self.answers[:] = np.nan
def concatenateStimuli(MatrixDir, OutDir, Length, n): # Get matrix wav file paths wavFiles = globDir(MatrixDir, '*.wav') stim_parts = os.path.join(MatrixDir, "stim_parts.csv") stim_words = os.path.join(MatrixDir, "stim_words.csv") stim_part_rows = [] with open(stim_parts, 'r') as csvfile: stim_part_rows = [line for line in csv.reader(csvfile)] with open(stim_words, 'r') as csvfile: stim_word_rows = [line for line in csv.reader(csvfile)] wavFiles = natsorted(wavFiles) totalSize = 0 y = [] parts = [] questions = [] i = 0 gapSize = np.uniform(0.8, 1.2, len(wavFiles)) for wav, gap in zip(wavFiles, gapSize): if i == n: break wavObj = PySndfile(wav) fs = wavObj.samplerate() size = wavObj.frames() totalSize += size totalSize += int(gap * fs) if (totalSize / fs) > Length: # total size + 2 second silence at start y.append(np.zeros((totalSize + 2 * fs, 3))) parts.append([]) questions.append([]) i += 1 totalSize = 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].shape[0]: i += 1 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) 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.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) with open('./out/stim/stim_words_{}.csv'.format(ind), 'w') as csvfile: writer = csv.writer(csvfile) writer.writerows(q) with open('./out/stim/stim_parts_{}.csv'.format(ind), 'w') as csvfile: writer = csv.writer(csvfile) writer.writerows(p)