import os import sys import time import numpy as np import scipy import librosa import pyloudnorm as pyln sys.setrecursionlimit(int(1e6)) import sklearn currentdir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(currentdir) from common_miscellaneous import compute_stft, compute_istft from common_audioeffects import Panner, Compressor, AugmentationChain, ConvolutionalReverb, Equaliser, AlgorithmicReverb import fx_utils import soundfile as sf import aubio import time import warnings # Functions def print_dict(dict_): for i in dict_: print(i) for j in dict_[i]: print('\t', j) def amp_to_db(x): return 20*np.log10(x + 1e-30) def db_to_amp(x): return 10**(x/20) def get_running_stats(x, features, N=20): mean = [] std = [] for i in range(len(features)): mean_, std_ = running_mean_std(x[:,i], N) mean.append(mean_) std.append(std_) mean = np.asarray(mean) std = np.asarray(std) return mean, std def running_mean_std(x, N): with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) cumsum = np.cumsum(np.insert(x, 0, 0)) cumsum2 = np.cumsum(np.insert(x**2, 0, 0)) mean = (cumsum[N:] - cumsum[:-N]) / float(N) std = np.sqrt(((cumsum2[N:] - cumsum2[:-N]) / N) - (mean * mean)) return mean, std def get_eq_matching(audio_t, ref_spec, sr=44100, n_fft=65536, hop_length=16384, min_db=-50, ntaps=101, lufs=-30): audio_t = np.copy(audio_t) max_db = amp_to_db(np.max(np.abs(audio_t))) if max_db > min_db: audio_t = fx_utils.lufs_normalize(audio_t, sr, lufs, log=False) audio_D = compute_stft(np.expand_dims(audio_t, 1), hop_length, n_fft, np.sqrt(np.hanning(n_fft+1)[:-1])) audio_D = np.abs(audio_D) audio_D_avg = np.mean(audio_D, axis=0)[0] m = ref_spec.shape[0] Ts = 1.0/sr # sampling interval n = m # length of the signal kk = np.arange(n) T = n/sr frq = kk/T # two sides frequency range frq /=2 diff_eq = amp_to_db(ref_spec)-amp_to_db(audio_D_avg) diff_eq = db_to_amp(diff_eq) diff_eq = np.sqrt(diff_eq) diff_filter = scipy.signal.firwin2(ntaps, frq/np.max(frq), diff_eq, nfreqs=None, window='hamming', nyq=None, antisymmetric=False) output = scipy.signal.filtfilt(diff_filter, 1, audio_t, axis=-1, padtype='odd', padlen=None, method='pad', irlen=None) else: output = audio_t return output def get_SPS(x, n_fft=2048, hop_length=1024, smooth=False, frames=False): x = np.copy(x) eps = 1e-20 audio_D = compute_stft(x, hop_length, n_fft, np.sqrt(np.hanning(n_fft+1)[:-1])) audio_D_l = np.abs(audio_D[:, 0, :] + eps) audio_D_r = np.abs(audio_D[:, 1, :] + eps) phi = 2 * (np.abs(audio_D_l*np.conj(audio_D_r)))/(np.abs(audio_D_l)**2+np.abs(audio_D_r)**2) phi_l = np.abs(audio_D_l*np.conj(audio_D_r))/(np.abs(audio_D_l)**2) phi_r = np.abs(audio_D_r*np.conj(audio_D_l))/(np.abs(audio_D_r)**2) delta = phi_l - phi_r delta_ = np.sign(delta) SPS = (1-phi)*delta_ phi_mean = np.mean(phi, axis=0) if smooth: phi_mean = scipy.signal.savgol_filter(phi_mean, 501, 1, mode='mirror') SPS_mean = np.mean(SPS, axis=0) if smooth: SPS_mean = scipy.signal.savgol_filter(SPS_mean, 501, 1, mode='mirror') return SPS_mean, phi_mean, SPS, phi def get_mean_side(sps, freqs=[50,2500], sr=44100, n_fft=2048): sign = np.sign(sps+ 1e-10) idx1 = freqs[0] idx2 = freqs[1] f1 = int(np.floor(idx1*n_fft/sr)) f2 = int(np.floor(idx2*n_fft/sr)) sign_mean = np.mean(sign[f1:f2])/np.abs(np.mean(sign[f1:f2])) sign_mean return sign_mean def get_panning_param_values(phi, side): p = np.zeros_like(phi) g = (np.clip(phi+1e-30, 0, 1))/2 for i, g_ in enumerate(g): if side > 0: p[i] = 1 - g_ elif side < 0: p[i] = g_ else: p[i] = 0.5 g_l = 1-p g_r = p return p, [g_l, g_r] def get_panning_matching(audio, ref_phi, sr=44100, n_fft=2048, hop_length=1024, min_db_f=-10, max_freq_pan=16000, frames=True): eps = 1e-20 window = np.sqrt(np.hanning(n_fft+1)[:-1]) audio = np.copy(audio) audio_t = np.pad(audio, ((n_fft, n_fft), (0, 0)), mode='constant') sps_mean_, phi_mean_, _, _ = get_SPS(audio_t, n_fft=n_fft, hop_length=hop_length, smooth=True) side = get_mean_side(sps_mean_, sr=sr, n_fft=n_fft) if side > 0: alpha = 0.7 else: alpha = 0.3 processor = Panner() processor.parameters.pan.value = alpha processor.parameters.pan_law.value = 'linear' processor.update() audio_t_ = processor.process(audio_t) sps_mean_, phi_mean, sps_frames, phi_frames = get_SPS(audio_t_, n_fft=n_fft, hop_length=hop_length, smooth=True, frames=frames) if frames: p_i_ = [] g_i_ = [] p_ref = [] g_ref = [] for i in range(len(sps_frames)): sps_ = sps_frames[i] phi_ = phi_frames[i] p_, g_ = get_panning_param_values(phi_, side) p_i_.append(p_) g_i_.append(g_) p_, g_ = get_panning_param_values(ref_phi, side) p_ref.append(p_) g_ref.append(g_) ratio = (np.asarray(g_ref)/(np.asarray(g_i_)+eps)) g_l = ratio[:,0,:] g_r = ratio[:,1,:] else: p, g = get_panning_param_values(ref_phi, side) p_i, g_i = get_panning_param_values(phi_mean, side) ratio = (np.asarray(g)/np.asarray(g_i)) g_l = ratio[0] g_r = ratio[1] audio_new_D = compute_stft(audio_t_, hop_length, n_fft, window) audio_new_D_mono = audio_new_D.copy() audio_new_D_mono = audio_new_D_mono[:, 0, :] + audio_new_D_mono[:, 1, :] audio_new_D_mono = np.abs(audio_new_D_mono) audio_new_D_phase = np.angle(audio_new_D) audio_new_D = np.abs(audio_new_D) audio_new_D_l = audio_new_D[:, 0, :] audio_new_D_r = audio_new_D[:, 1, :] if frames: for i, frame in enumerate(audio_new_D_mono): max_db = amp_to_db(np.max(np.abs(frame))) if max_db < min_db_f: g_r[i] = np.ones_like(frame) g_l[i] = np.ones_like(frame) idx1 = max_freq_pan f1 = int(np.floor(idx1*n_fft/sr)) ones = np.ones_like(g_l) g_l[f1:] = ones[f1:] g_r[f1:] = ones[f1:] audio_new_D_l = audio_new_D_l*g_l audio_new_D_r = audio_new_D_r*g_r audio_new_D_l = np.expand_dims(audio_new_D_l, 0) audio_new_D_r = np.expand_dims(audio_new_D_r, 0) audio_new_D_ = np.concatenate((audio_new_D_l,audio_new_D_r)) audio_new_D_ = np.moveaxis(audio_new_D_, 0, 1) audio_new_D_ = audio_new_D_ * (np.cos(audio_new_D_phase) + np.sin(audio_new_D_phase)*1j) audio_new_t = compute_istft(audio_new_D_, hop_length, window) audio_new_t = audio_new_t[n_fft:n_fft+audio.shape[0]] return audio_new_t def get_mean_peak(audio, sr=44100, true_peak=False, n_mels=128, percentile=75): # Returns mean peak value in dB after the 1Q is removed. # Input should be in the shape samples x channel audio_ = audio window_size = 2**10 # FFT size hop_size = window_size peak = [] std = [] for ch in range(audio_.shape[-1]): x = np.ascontiguousarray(audio_[:, ch]) if true_peak: x = librosa.resample(x, sr, 4*sr) sr = 4*sr window_size = 4*window_size hop_size = 4*hop_size onset_func = aubio.onset('hfc', buf_size=window_size, hop_size=hop_size, samplerate=sr) frames = np.float32(librosa.util.frame(x, frame_length=window_size, hop_length=hop_size)) onset_times = [] for frame in frames.T: if onset_func(frame): onset_time = onset_func.get_last() onset_times.append(onset_time) samples=[] if onset_times: for i, p in enumerate(onset_times[:-1]): samples.append(onset_times[i]+np.argmax(np.abs(x[onset_times[i]:onset_times[i+1]]))) samples.append(onset_times[-1]+np.argmax(np.abs(x[onset_times[-1]:]))) p_value = [] for p in samples: p_ = amp_to_db(np.abs(x[p])) p_value.append(p_) p_value_=[] for p in p_value: if p > np.percentile(p_value, percentile): p_value_.append(p) if p_value_: peak.append(np.mean(p_value_)) std.append(np.std(p_value_)) elif p_value: peak.append(np.mean(p_value)) std.append(np.std(p_value)) else: return None return [np.mean(peak), np.mean(std)] def compress(processor, audio, sr, th, ratio, attack, release): eps = 1e-20 x = audio processor.parameters.threshold.value = th processor.parameters.ratio.value = ratio processor.parameters.attack_time.value = attack processor.parameters.release_time.value = release processor.update() output = processor.process(x) if np.max(np.abs(output)) >= 1.0: output = np.clip(output, -1.0, 1.0) return output def get_comp_matching(audio, ref_peak, ref_std, ratio, attack, release, sr=44100, min_db=-50, comp_peak_norm=-10.0, min_th=-40, max_ratio=20, n_mels=128, true_peak=False, percentile=75, expander=True): x = audio.copy() if x.ndim < 2: x = np.expand_dims(x, 1) max_db = amp_to_db(np.max(np.abs(x))) if max_db > min_db: x = pyln.normalize.peak(x, comp_peak_norm) peak, std = get_mean_peak(x, sr, n_mels=n_mels, true_peak=true_peak, percentile=percentile) if peak > (ref_peak - ref_std) and peak < (ref_peak + ref_std): return x # DownwardCompress elif peak > (ref_peak - ref_std): processor = Compressor(sample_rate=sr) # print('compress') ratios = np.linspace(ratio, max_ratio, max_ratio-ratio+1) ths = np.linspace(-1-9, min_th, 2*np.abs(min_th)-1-18) for rt in ratios: for th in ths: y = compress(processor, x, sr, th, rt, attack, release) peak, std = get_mean_peak(y, sr, n_mels=n_mels, true_peak=true_peak, percentile=percentile) if peak < (ref_peak + ref_std): break else: continue break return y # Upward Expand elif peak < (ref_peak + ref_std): if expander: processor = Compressor(sample_rate=sr) ratios = np.linspace(ratio, max_ratio, max_ratio-ratio+1) ths = np.linspace(-1, min_th, 2*np.abs(min_th)-1)[::-1] for rt in ratios: for th in ths: y = compress(processor, x, sr, th, 1/rt, attack, release) peak, std = get_mean_peak(y, sr, n_mels=n_mels, true_peak=true_peak, percentile=percentile) if peak > (ref_peak - ref_std): break else: continue break return y else: return x else: return x # REVERB def get_reverb_send(audio, eq_parameters, rv_parameters, impulse_responses=None, eq_prob=1.0, rv_prob=1.0, parallel=True, shuffle=False, sr=44100, bands=['low_shelf', 'high_shelf']): x = audio.copy() if x.ndim < 2: x = np.expand_dims(x, 1) channels = x.shape[-1] eq_gain = eq_parameters.low_shelf_gain.value eq = Equaliser(n_channels=channels, sample_rate=sr, gain_range=(eq_gain, eq_gain), bands=bands, hard_clip=False, name='Equaliser', parameters=eq_parameters) eq.randomize() if impulse_responses: reverb = ConvolutionalReverb(impulse_responses=impulse_responses, sample_rate=sr, parameters=rv_parameters) else: reverb = AlgorithmicReverb(sample_rate=sr, parameters=rv_parameters) reverb.randomize() fxchain = AugmentationChain([ (eq, rv_prob, False), (reverb, eq_prob, False) ], shuffle=shuffle, parallel=parallel) output = fxchain(x) return output # FUNCTIONS TO COMPUTE FEATURES def compute_loudness_features(args_): audio_out_ = args_[0] audio_tar_ = args_[1] idx = args_[2] sr = args_[3] loudness_ = {key:[] for key in ['d_lufs', 'd_peak',]} peak_tar = np.max(np.abs(audio_tar_)) peak_tar_db = 20.0 * np.log10(peak_tar) peak_out = np.max(np.abs(audio_out_)) peak_out_db = 20.0 * np.log10(peak_out) with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) meter = pyln.Meter(sr) # create BS.1770 meter loudness_tar = meter.integrated_loudness(audio_tar_) loudness_out = meter.integrated_loudness(audio_out_) loudness_['d_lufs'].append(sklearn.metrics.mean_absolute_percentage_error([loudness_tar], [loudness_out])) loudness_['d_peak'].append(sklearn.metrics.mean_absolute_percentage_error([peak_tar_db], [peak_out_db])) return loudness_ def compute_spectral_features(args_): audio_out_ = args_[0] audio_tar_ = args_[1] idx = args_[2] sr = args_[3] fft_size = args_[4] hop_length = args_[5] channels = args_[6] audio_out_ = pyln.normalize.peak(audio_out_, -1.0) audio_tar_ = pyln.normalize.peak(audio_tar_, -1.0) spec_out_ = compute_stft(audio_out_, hop_length, fft_size, np.sqrt(np.hanning(fft_size+1)[:-1])) spec_out_ = np.transpose(spec_out_, axes=[1, -1, 0]) spec_out_ = np.abs(spec_out_) spec_tar_ = compute_stft(audio_tar_, hop_length, fft_size, np.sqrt(np.hanning(fft_size+1)[:-1])) spec_tar_ = np.transpose(spec_tar_, axes=[1, -1, 0]) spec_tar_ = np.abs(spec_tar_) spectral_ = {key:[] for key in ['centroid_mean', 'bandwidth_mean', 'contrast_l_mean', 'contrast_m_mean', 'contrast_h_mean', 'rolloff_mean', 'flatness_mean', 'mape_mean', ]} centroid_mean_ = [] centroid_std_ = [] bandwidth_mean_ = [] bandwidth_std_ = [] contrast_l_mean_ = [] contrast_l_std_ = [] contrast_m_mean_ = [] contrast_m_std_ = [] contrast_h_mean_ = [] contrast_h_std_ = [] rolloff_mean_ = [] rolloff_std_ = [] flatness_mean_ = [] for ch in range(channels): tar = spec_tar_[ch] out = spec_out_[ch] tar_sc = librosa.feature.spectral_centroid(y=None, sr=sr, S=tar, n_fft=fft_size, hop_length=hop_length) out_sc = librosa.feature.spectral_centroid(y=None, sr=sr, S=out, n_fft=fft_size, hop_length=hop_length) tar_bw = librosa.feature.spectral_bandwidth(y=None, sr=sr, S=tar, n_fft=fft_size, hop_length=hop_length, centroid=tar_sc, norm=True, p=2) out_bw = librosa.feature.spectral_bandwidth(y=None, sr=sr, S=out, n_fft=fft_size, hop_length=hop_length, centroid=out_sc, norm=True, p=2) # l = 0-250, m = 1-2-3 = 250 - 2000, h = 2000 - SR/2 tar_ct = librosa.feature.spectral_contrast(y=None, sr=sr, S=tar, n_fft=fft_size, hop_length=hop_length, fmin=250.0, n_bands=4, quantile=0.02, linear=False) out_ct = librosa.feature.spectral_contrast(y=None, sr=sr, S=out, n_fft=fft_size, hop_length=hop_length, fmin=250.0, n_bands=4, quantile=0.02, linear=False) tar_ro = librosa.feature.spectral_rolloff(y=None, sr=sr, S=tar, n_fft=fft_size, hop_length=hop_length, roll_percent=0.85) out_ro = librosa.feature.spectral_rolloff(y=None, sr=sr, S=out, n_fft=fft_size, hop_length=hop_length, roll_percent=0.85) tar_ft = librosa.feature.spectral_flatness(y=None, S=tar, n_fft=fft_size, hop_length=hop_length, amin=1e-10, power=2.0) out_ft = librosa.feature.spectral_flatness(y=None, S=out, n_fft=fft_size, hop_length=hop_length, amin=1e-10, power=2.0) eps = 1e-0 N = 40 mean_sc_tar, std_sc_tar = get_running_stats(tar_sc.T+eps, [0], N=N) mean_sc_out, std_sc_out = get_running_stats(out_sc.T+eps, [0], N=N) assert np.isnan(mean_sc_tar).any() == False, f'NAN values mean_sc_tar {idx}' assert np.isnan(mean_sc_out).any() == False, f'NAN values mean_sc_out {idx}' mean_bw_tar, std_bw_tar = get_running_stats(tar_bw.T+eps, [0], N=N) mean_bw_out, std_bw_out = get_running_stats(out_bw.T+eps, [0], N=N) assert np.isnan(mean_bw_tar).any() == False, f'NAN values tar mean bw {idx}' assert np.isnan(mean_bw_out).any() == False, f'NAN values out mean bw {idx}' mean_ct_tar, std_ct_tar = get_running_stats(tar_ct.T, list(range(tar_ct.shape[0])), N=N) mean_ct_out, std_ct_out = get_running_stats(out_ct.T, list(range(out_ct.shape[0])), N=N) assert np.isnan(mean_ct_tar).any() == False, f'NAN values tar mean ct {idx}' assert np.isnan(mean_ct_out).any() == False, f'NAN values out mean ct {idx}' mean_ro_tar, std_ro_tar = get_running_stats(tar_ro.T+eps, [0], N=N) mean_ro_out, std_ro_out = get_running_stats(out_ro.T+eps, [0], N=N) assert np.isnan(mean_ro_tar).any() == False, f'NAN values tar mean ro {idx}' assert np.isnan(mean_ro_out).any() == False, f'NAN values out mean ro {idx}' mean_ft_tar, std_ft_tar = get_running_stats(tar_ft.T, [0], N=800) # gives very high numbers due to N (80) value mean_ft_out, std_ft_out = get_running_stats(out_ft.T, [0], N=800) mape_mean_sc = sklearn.metrics.mean_absolute_percentage_error(mean_sc_tar[0], mean_sc_out[0]) mape_mean_bw = sklearn.metrics.mean_absolute_percentage_error(mean_bw_tar[0], mean_bw_out[0]) mape_mean_ct_l = sklearn.metrics.mean_absolute_percentage_error(mean_ct_tar[0], mean_ct_out[0]) mape_mean_ct_m = sklearn.metrics.mean_absolute_percentage_error(np.mean(mean_ct_tar[1:4], axis=0), np.mean(mean_ct_out[1:4], axis=0)) mape_mean_ct_h = sklearn.metrics.mean_absolute_percentage_error(mean_ct_tar[-1], mean_ct_out[-1]) mape_mean_ro = sklearn.metrics.mean_absolute_percentage_error(mean_ro_tar[0], mean_ro_out[0]) mape_mean_ft = sklearn.metrics.mean_absolute_percentage_error(mean_ft_tar[0], mean_ft_out[0]) centroid_mean_.append(mape_mean_sc) bandwidth_mean_.append(mape_mean_bw) contrast_l_mean_.append(mape_mean_ct_l) contrast_m_mean_.append(mape_mean_ct_m) contrast_h_mean_.append(mape_mean_ct_h) rolloff_mean_.append(mape_mean_ro) flatness_mean_.append(mape_mean_ft) spectral_['centroid_mean'].append(np.mean(centroid_mean_)) spectral_['bandwidth_mean'].append(np.mean(bandwidth_mean_)) spectral_['contrast_l_mean'].append(np.mean(contrast_l_mean_)) spectral_['contrast_m_mean'].append(np.mean(contrast_m_mean_)) spectral_['contrast_h_mean'].append(np.mean(contrast_h_mean_)) spectral_['rolloff_mean'].append(np.mean(rolloff_mean_)) spectral_['flatness_mean'].append(np.mean(flatness_mean_)) spectral_['mape_mean'].append(np.mean([np.mean(centroid_mean_), np.mean(bandwidth_mean_), np.mean(contrast_l_mean_), np.mean(contrast_m_mean_), np.mean(contrast_h_mean_), np.mean(rolloff_mean_), np.mean(flatness_mean_), ])) return spectral_ # PANNING def get_panning_rms_frame(sps_frame, freqs=[0,22050], sr=44100, n_fft=2048): idx1 = freqs[0] idx2 = freqs[1] f1 = int(np.floor(idx1*n_fft/sr)) f2 = int(np.floor(idx2*n_fft/sr)) p_rms = np.sqrt((1/(f2-f1)) * np.sum(sps_frame[f1:f2]**2)) return p_rms def get_panning_rms(sps, freqs=[[0, 22050]], sr=44100, n_fft=2048): p_rms = [] for frame in sps: p_rms_ = [] for f in freqs: rms = get_panning_rms_frame(frame, freqs=f, sr=sr, n_fft=n_fft) p_rms_.append(rms) p_rms.append(p_rms_) return np.asarray(p_rms) def compute_panning_features(args_): audio_out_ = args_[0] audio_tar_ = args_[1] idx = args_[2] sr = args_[3] fft_size = args_[4] hop_length = args_[5] audio_out_ = pyln.normalize.peak(audio_out_, -1.0) audio_tar_ = pyln.normalize.peak(audio_tar_, -1.0) panning_ = {} freqs=[[0, sr//2], [0, 250], [250, 2500], [2500, sr//2]] _, _, sps_frames_tar, _ = get_SPS(audio_tar_, n_fft=fft_size, hop_length=hop_length, smooth=True, frames=True) _, _, sps_frames_out, _ = get_SPS(audio_out_, n_fft=fft_size, hop_length=hop_length, smooth=True, frames=True) p_rms_tar = get_panning_rms(sps_frames_tar, freqs=freqs, sr=sr, n_fft=fft_size) p_rms_out = get_panning_rms(sps_frames_out, freqs=freqs, sr=sr, n_fft=fft_size) # to avoid num instability, deletes frames with zero rms from target if np.min(p_rms_tar) == 0.0: id_zeros = np.where(p_rms_tar.T[0] == 0) p_rms_tar_ = [] p_rms_out_ = [] for i in range(len(freqs)): temp_tar = np.delete(p_rms_tar.T[i], id_zeros) temp_out = np.delete(p_rms_out.T[i], id_zeros) p_rms_tar_.append(temp_tar) p_rms_out_.append(temp_out) p_rms_tar_ = np.asarray(p_rms_tar_) p_rms_tar = p_rms_tar_.T p_rms_out_ = np.asarray(p_rms_out_) p_rms_out = p_rms_out_.T N = 40 mean_tar, std_tar = get_running_stats(p_rms_tar, freqs, N=N) mean_out, std_out = get_running_stats(p_rms_out, freqs, N=N) panning_['P_t_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_tar[0], mean_out[0])] panning_['P_l_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_tar[1], mean_out[1])] panning_['P_m_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_tar[2], mean_out[2])] panning_['P_h_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_tar[3], mean_out[3])] panning_['mape_mean'] = [np.mean([panning_['P_t_mean'], panning_['P_l_mean'], panning_['P_m_mean'], panning_['P_h_mean'], ])] return panning_ # DYNAMIC def get_rms_dynamic_crest(x, frame_length, hop_length): rms = [] dynamic_spread = [] crest = [] for ch in range(x.shape[-1]): frames = librosa.util.frame(x[:, ch], frame_length=frame_length, hop_length=hop_length) rms_ = [] dynamic_spread_ = [] crest_ = [] for i in frames.T: x_rms = amp_to_db(np.sqrt(np.sum(i**2)/frame_length)) x_d = np.sum(amp_to_db(np.abs(i)) - x_rms)/frame_length x_c = amp_to_db(np.max(np.abs(i)))/x_rms rms_.append(x_rms) dynamic_spread_.append(x_d) crest_.append(x_c) rms.append(rms_) dynamic_spread.append(dynamic_spread_) crest.append(crest_) rms = np.asarray(rms) dynamic_spread = np.asarray(dynamic_spread) crest = np.asarray(crest) rms = np.mean(rms, axis=0) dynamic_spread = np.mean(dynamic_spread, axis=0) crest = np.mean(crest, axis=0) rms = np.expand_dims(rms, axis=0) dynamic_spread = np.expand_dims(dynamic_spread, axis=0) crest = np.expand_dims(crest, axis=0) return rms, dynamic_spread, crest def lowpassFiltering(x, f0, sr): b1, a1 = scipy.signal.butter(4, f0/(sr/2),'lowpass') x_f = [] for ch in range(x.shape[-1]): x_f_ = scipy.signal.filtfilt(b1, a1, x[:, ch]).copy(order='F') x_f.append(x_f_) return np.asarray(x_f).T def get_low_freq_weighting(x, sr, n_fft, hop_length, f0 = 1000): x_low = lowpassFiltering(x, f0, sr) X_low = compute_stft(x_low, hop_length, n_fft, np.sqrt(np.hanning(n_fft+1)[:-1])) X_low = np.transpose(X_low, axes=[1, -1, 0]) X_low = np.abs(X_low) X = compute_stft(x, hop_length, n_fft, np.sqrt(np.hanning(n_fft+1)[:-1])) X = np.transpose(X, axes=[1, -1, 0]) X = np.abs(X) eps = 1e-5 ratio = (X_low)/(X+eps) ratio = np.sum(ratio, axis = 1) ratio = np.mean(ratio, axis = 0) return np.expand_dims(ratio, axis=0) def compute_dynamic_features(args_): audio_out_ = args_[0] audio_tar_ = args_[1] idx = args_[2] sr = args_[3] fft_size = args_[4] hop_length = args_[5] audio_out_ = pyln.normalize.peak(audio_out_, -1.0) audio_tar_ = pyln.normalize.peak(audio_tar_, -1.0) dynamic_ = {} with warnings.catch_warnings(): warnings.simplefilter("ignore", category=UserWarning) rms_tar, dyn_tar, crest_tar = get_rms_dynamic_crest(audio_tar_, fft_size, hop_length) rms_out, dyn_out, crest_out = get_rms_dynamic_crest(audio_out_, fft_size, hop_length) low_ratio_tar = get_low_freq_weighting(audio_tar_, sr, fft_size, hop_length, f0=1000) low_ratio_out = get_low_freq_weighting(audio_out_, sr, fft_size, hop_length, f0=1000) N = 40 eps = 1e-10 rms_tar = (-1*rms_tar) + 1.0 rms_out = (-1*rms_out) + 1.0 dyn_tar = (-1*dyn_tar) + 1.0 dyn_out = (-1*dyn_out) + 1.0 mean_rms_tar, std_rms_tar = get_running_stats(rms_tar.T, [0], N=N) mean_rms_out, std_rms_out = get_running_stats(rms_out.T, [0], N=N) mean_dyn_tar, std_dyn_tar = get_running_stats(dyn_tar.T, [0], N=N) mean_dyn_out, std_dyn_out = get_running_stats(dyn_out.T, [0], N=N) mean_crest_tar, std_crest_tar = get_running_stats(crest_tar.T, [0], N=N) mean_crest_out, std_crest_out = get_running_stats(crest_out.T, [0], N=N) mean_low_ratio_tar, std_low_ratio_tar = get_running_stats(low_ratio_tar.T, [0], N=N) mean_low_ratio_out, std_low_ratio_out = get_running_stats(low_ratio_out.T, [0], N=N) dynamic_['rms_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_rms_tar, mean_rms_out)] dynamic_['dyn_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_dyn_tar, mean_dyn_out)] dynamic_['crest_mean'] = [sklearn.metrics.mean_absolute_percentage_error(mean_crest_tar, mean_crest_out)] dynamic_['l_ratio_mean_mape'] = [sklearn.metrics.mean_absolute_percentage_error(mean_low_ratio_tar, mean_low_ratio_out)] dynamic_['l_ratio_mean_l2'] = [sklearn.metrics.mean_squared_error(mean_low_ratio_tar, mean_low_ratio_out)] dynamic_['mape_mean'] = [np.mean([dynamic_['rms_mean'], dynamic_['dyn_mean'], dynamic_['crest_mean'], ])] return dynamic_