def run(args, parameters): """ This is the core loop of the simulation """ # expand arguments sinr, n_targets, n_interf, n_mics, dist_ratio, room_params, seed = args n_sources = n_targets + n_interf # this is the underdetermined case. We don't do that. if n_mics < n_targets: return [] # set the RNG seed rng_state = np.random.get_state() np.random.seed(seed) # get all the signals files_absolute = [ os.path.join(parameters["base_dir"], fn) for fn in room_params["wav"][:n_sources] ] source_signals = wav_read_center(files_absolute, seed=123) # create the room room = pra.ShoeBox(**room_params["room_kwargs"]) R = np.array(room_params["mic_array"]) room.add_microphone_array(pra.MicrophoneArray(R[:, :n_mics], room.fs)) source_locs = np.array(room_params["sources"]) for n in range(n_sources): room.add_source(source_locs[:, n], signal=source_signals[n, :]) # compute RIRs and RT60 room.compute_rir() rt60 = np.median([ pra.experimental.measure_rt60(room.rir[0][n], fs=room.fs) for n in range(n_targets) ]) # signals after propagation but before mixing # (n_sources, n_mics, n_samples) premix = room.simulate(return_premix=True) n_samples = premix.shape[-1] # create the mix (n_mics, n_samples) # this routine will also resize the signals in premix mix = callback_noise_mixer(premix, sinr=sinr, n_src=n_targets + n_interf, n_tgt=n_targets, **parameters["mix_params"]) # create the reference signals # (n_sources + 1, n_samples) refs = np.zeros((n_targets + 1, n_samples)) refs[:-1, :] = premix[:n_targets, parameters["mix_params"]["ref_mic"], :] refs[-1, :] = np.sum(premix[n_targets:, 0, :], axis=0) # STFT parameters framesize = parameters["stft_params"]["framesize"] hop = parameters["stft_params"]["hop"] if parameters["stft_params"]["window"] == "hann": win_a = pra.hamming(framesize) else: # default is Hann win_a = pra.hann(framesize) # START BSS ########### # shape: (n_frames, n_freq, n_mics) X_all = pra.transform.analysis(mix.T, framesize, hop, win=win_a) X_mics = X_all[:, :, :n_mics] # store results in a list, one entry per algorithm results = [] # compute the initial values of SDR/SIR init_sdr = [] init_sir = [] for full_name, params in parameters["algorithm_kwargs"].items(): name = params["algo"] kwargs = params["kwargs"] if not bss.is_determined[name] and bss.is_dual_update[ name] and n_targets == 1: # Overdetermined algorithms with dual updates cannot be used # in the single source case (they can extract at least two sources) continue elif bss.is_single_source[name] and n_targets > 1: # doesn't work for multi source scenario continue elif bss.is_overdetermined[name] and n_targets == n_mics: # don't run the overdetermined stuff in determined case continue results.append({ "algorithm": full_name, "n_targets": n_targets, "n_interferers": n_interf, "n_mics": n_mics, "rt60": rt60, "dist_ratio": dist_ratio, "sinr": sinr, "seed": seed, "sdr": [], "sir": [], # to store the result "cost": [], "runtime": np.nan, "eval_time": np.nan, "n_samples": n_samples, }) # this is used to keep track of time spent in the evaluation callback eval_time = [] def cb(W, Y, source_model): convergence_callback( W, Y, source_model, X_mics, n_targets, results[-1]["sdr"], results[-1]["sir"], results[-1]["cost"], eval_time, refs, parameters["mix_params"]["ref_mic"], parameters["stft_params"], name, not bss.is_determined[name], ) if "model" not in kwargs: local_model = bss.default.model else: local_model = kwargs["model"] cb(np.eye(n_mics)[None, :, :], X_mics, local_model) try: t_start = time.perf_counter() bss.separate(X_mics, n_src=n_targets, algorithm=name, callback=cb, proj_back=False, **kwargs) t_finish = time.perf_counter() results[-1]["eval_time"] = np.sum(eval_time) results[-1][ "runtime"] = t_finish - t_start - results[-1]["eval_time"] except Exception: # get the traceback tb = traceback.format_exc() report = { "algorithm": name, "n_src": n_targets, "kwargs": kwargs, "result": results[-1], "tb": tb, } pid = os.getpid() # report last sdr/sir as np.nan results[-1]["sdr"].append(np.nan) results[-1]["sir"].append(np.nan) # now write the problem to file fn_err = os.path.join(parameters["_results_dir"], "error_{}.json".format(pid)) with open(fn_err, "a") as f: f.write(json.dumps(report, indent=4)) f.write(",\n") # skip to next iteration continue # restore RNG former state np.random.set_state(rng_state) return results
def one_loop(args): global parameters import time import numpy np = numpy import pyroomacoustics pra = pyroomacoustics import os import sys sys.path.append(parameters["base_dir"]) from auxiva_pca import auxiva_pca, pca_separation from five import five from ive import ogive from overiva import overiva from pyroomacoustics.bss.common import projection_back from room_builder import callback_noise_mixer, random_room_builder # import samples helper routine from get_data import samples_dir sys.path.append(os.path.join(parameters['base_dir'], samples_dir)) from generate_samples import wav_read_center n_targets, n_interferers, n_mics, sinr, wav_files, room_seed, seed = args # this is the underdetermined case. We don't do that. if n_mics < n_targets: return [] # set MKL to only use one thread if present try: import mkl mkl.set_num_threads(1) except ImportError: pass # set the RNG seed rng_state = np.random.get_state() np.random.seed(seed) # STFT parameters framesize = parameters["stft_params"]["framesize"] hop = parameters["stft_params"]["hop"] if parameters["stft_params"]["window"] == "hann": win_a = pra.hamming(framesize) else: # default is Hann win_a = pra.hann(framesize) win_s = pra.transform.compute_synthesis_window(win_a, hop) # Generate the audio signals # get the simulation parameters from the json file # Simulation parameters sources_var = np.ones(n_targets) # total number of sources n_sources = n_targets + n_interferers # Read the signals wav_files = [os.path.join(parameters["base_dir"], fn) for fn in wav_files] signals = wav_read_center(wav_files[:n_sources], seed=123) # Get a random room room, rt60 = random_room_builder(signals, n_mics, seed=room_seed, **parameters["room_params"]) premix = room.simulate(return_premix=True) # mix the signal n_samples = premix.shape[2] mix = callback_noise_mixer( premix, sinr=sinr, diffuse_ratio=parameters["sinr_diffuse_ratio"], n_src=n_sources, n_tgt=n_targets, tgt_std=np.sqrt(sources_var), ref_mic=parameters["ref_mic"], ) # sum up the background # shape (n_mics, n_samples) background = np.sum(premix[n_targets:n_sources, :, :], axis=0) # shape (n_targets+1, n_samples, n_mics) ref = np.zeros((n_targets + 1, premix.shape[2], premix.shape[1]), dtype=premix.dtype) ref[:n_targets, :, :] = premix[:n_targets, :, :].swapaxes(1, 2) ref[n_targets, :, :] = background.T synth = np.zeros_like(ref) # START BSS ########### # shape: (n_frames, n_freq, n_mics) X_all = pra.transform.analysis(mix.T, framesize, hop, win=win_a) X_mics = X_all[:, :, :n_mics] # convergence monitoring callback def convergence_callback(Y, X, n_targets, SDR, SIR, eval_time, ref, framesize, win_s, algo_name): t_in = time.perf_counter() # projection back z = projection_back(Y, X[:, :, 0]) Y = Y * np.conj(z[None, :, :]) from mir_eval.separation import bss_eval_sources if Y.shape[2] == 1: y = pra.transform.synthesis(Y[:, :, 0], framesize, hop, win=win_s)[:, None] else: y = pra.transform.synthesis(Y, framesize, hop, win=win_s) if algo_name not in parameters["overdet_algos"]: new_ord = np.argsort(np.std(y, axis=0))[::-1] y = y[:, new_ord] m = np.minimum(y.shape[0] - hop, ref.shape[1]) synth[:n_targets, :m, 0] = y[hop:m + hop, :n_targets].T synth[n_targets, :m, 0] = y[hop:m + hop, 0] sdr, sir, sar, perm = bss_eval_sources(ref[:n_targets + 1, :m, 0], synth[:, :m, 0]) SDR.append(sdr[:n_targets].tolist()) SIR.append(sir[:n_targets].tolist()) t_out = time.perf_counter() eval_time.append(t_out - t_in) # store results in a list, one entry per algorithm results = [] # compute the initial values of SDR/SIR init_sdr = [] init_sir = [] convergence_callback(X_mics, X_mics, n_targets, init_sdr, init_sir, [], ref, framesize, win_s, "init") for full_name, params in parameters["algorithm_kwargs"].items(): name = params["algo"] kwargs = params["kwargs"] if name == "auxiva_pca" and n_targets == 1: # PCA doesn't work for single source scenario continue elif name in ["ogive", "five"] and n_targets != 1: # OGIVE is only for single target continue results.append({ "algorithm": full_name, "n_targets": n_targets, "n_interferers": n_interferers, "n_mics": n_mics, "rt60": rt60, "sinr": sinr, "seed": seed, "sdr": [], "sir": [], # to store the result "runtime": np.nan, "eval_time": np.nan, "n_samples": n_samples, }) # this is used to keep track of time spent in the evaluation callback eval_time = [] def cb(Y): convergence_callback( Y, X_mics, n_targets, results[-1]["sdr"], results[-1]["sir"], eval_time, ref, framesize, win_s, name, ) # avoid one computation by using the initial values of sdr/sir results[-1]["sdr"].append(init_sdr[0]) results[-1]["sir"].append(init_sir[0]) try: t_start = time.perf_counter() if name == "auxiva": # Run AuxIVA # this calls full IVA when `n_src` is not provided Y = overiva(X_mics, callback=cb, **kwargs) elif name == "auxiva_pca": # Run AuxIVA Y = auxiva_pca(X_mics, n_src=n_targets, callback=cb, proj_back=False, **kwargs) elif name == "overiva": # Run BlinkIVA Y = overiva(X_mics, n_src=n_targets, callback=cb, proj_back=False, **kwargs) elif name == "overiva2": # Run BlinkIVA Y = overiva(X_mics, n_src=n_targets, callback=cb, proj_back=False, **kwargs) elif name == "five": # Run AuxIVE Y = five(X_mics, callback=cb, proj_back=False, **kwargs) elif name == "ilrma": # Run AuxIVA Y = pra.bss.ilrma(X_mics, callback=cb, proj_back=False, **kwargs) elif name == "ogive": # Run OGIVE Y = ogive(X_mics, callback=cb, proj_back=False, **kwargs) elif name == "pca": # Run PCA Y = pca_separation(X_mics, n_src=n_targets) else: continue t_finish = time.perf_counter() # The last evaluation convergence_callback( Y, X_mics, n_targets, results[-1]["sdr"], results[-1]["sir"], [], ref, framesize, win_s, name, ) results[-1]["eval_time"] = np.sum(eval_time) results[-1][ "runtime"] = t_finish - t_start - results[-1]["eval_time"] except: import os, json pid = os.getpid() # report last sdr/sir as np.nan results[-1]["sdr"].append(np.nan) results[-1]["sir"].append(np.nan) # now write the problem to file fn_err = os.path.join(parameters["_results_dir"], "error_{}.json".format(pid)) with open(fn_err, "a") as f: f.write(json.dumps(results[-1], indent=4)) # skip to next iteration continue # restore RNG former state np.random.set_state(rng_state) return results
room.add_microphone_array(pra.MicrophoneArray(mic_locs, fs=room.fs)) # compute RIRs room.compute_rir() # signals after propagation but before mixing # (n_sources, n_mics, n_samples) premix = room.simulate(return_premix=True) n_samples = premix.shape[-1] # create the mix (n_mics, n_samples) # this routine will also resize the signals in premix mix = callback_noise_mixer( premix, sinr=SINR, n_src=n_sources, n_tgt=n_sources_target, ref_mic=ref_mic, diffuse_ratio=SINR_diffuse_ratio, ) # create the reference signals # (n_sources + 1, n_samples) if n_mics == n_sources_target: refs = np.zeros((n_sources_target, n_samples)) else: # in the overdetermined case, we add the background as an extra reference refs = np.zeros((n_sources_target + 1, n_samples)) refs[-1, :] = np.sum(premix[n_sources_target:, 0, :], axis=0) refs[:n_sources_target, :] = premix[:n_sources_target, ref_mic, :] print("Simulation done.")