""" Module: libfmp.c8.c8s1_hps Author: Meinard Müller, Frank Zalkow License: The MIT license, https://opensource.org/licenses/MIT This file is part of the FMP Notebooks (https://www.audiolabs-erlangen.de/FMP) """ from collections import OrderedDict import numpy as np from scipy import signal import librosa import IPython.display as ipd import pandas as pd def median_filter_horizontal(x, filter_len): """Apply median filter in horizontal direction Notebook: C8/C8S1_HPS.ipynb """ return signal.medfilt(x, [1, filter_len]) def median_filter_vertical(x, filter_len): """Apply median filter in vertical direction Notebook: C8/C8S1_HPS.ipynb """ return signal.medfilt(x, [filter_len, 1]) def convert_l_sec_to_frames(L_h_sec, Fs=22050, N=1024, H=512): """Convert filter length parameter from seconds to frame indices Notebook: C8/C8S1_HPS.ipynb """ L_h = int(np.ceil(L_h_sec * Fs / H)) return L_h def convert_l_hertz_to_bins(L_p_Hz, Fs=22050, N=1024, H=512): """Convert filter length parameter from Hertz to frequency bins Notebook: C8/C8S1_HPS.ipynb """ L_p = int(np.ceil(L_p_Hz * N / Fs)) return L_p def make_integer_odd(n): """Convert integer into odd integer Notebook: C8/C8S1_HPS.ipynb """ if(n % 2 == 0): n += 1 return n def hps(x, Fs, N, H, L_h, L_p, L_unit='physical', mask='binary', eps=0.001, detail=False): """Harmonic-percussive separation (HPS) algorithm Notebook: C8/C8S1_HPS.ipynb Args: x: Input signal Fs: Sampling rate of x N: Frame length H: Hopsize L_h: Horizontal median filter length given in seconds or frames L_p: Percussive median filter length given in Hertz or bins L_unit: Adjusts unit, either 'pyhsical' or 'indices' mask: Either 'binary' or 'soft' eps: Parameter used in soft maskig detail (bool): Returns detailed information Returns: x_h: Harmonic signal x_p: Percussive signal dict: dictionary containing detailed information; returned if "detail=True" """ assert L_unit in ['physical', 'indices'] assert mask in ['binary', 'soft'] # stft X = librosa.stft(x, n_fft=N, hop_length=H, win_length=N, window='hann', center=True, pad_mode='constant') # power spectrogram Y = np.abs(X) ** 2 # median filtering if L_unit == 'physical': L_h = convert_l_sec_to_frames(L_h_sec=L_h, Fs=Fs, N=N, H=H) L_p = convert_l_hertz_to_bins(L_p_Hz=L_p, Fs=Fs, N=N, H=H) L_h = make_integer_odd(L_h) L_p = make_integer_odd(L_p) Y_h = signal.medfilt(Y, [1, L_h]) Y_p = signal.medfilt(Y, [L_p, 1]) # masking if mask == 'binary': M_h = np.int8(Y_h >= Y_p) M_p = np.int8(Y_h < Y_p) if mask == 'soft': eps = 0.00001 M_h = (Y_h + eps / 2) / (Y_h + Y_p + eps) M_p = (Y_p + eps / 2) / (Y_h + Y_p + eps) X_h = X * M_h X_p = X * M_p # istft x_h = librosa.istft(X_h, hop_length=H, win_length=N, window='hann', center=True, length=x.size) x_p = librosa.istft(X_p, hop_length=H, win_length=N, window='hann', center=True, length=x.size) if detail: return x_h, x_p, dict(Y_h=Y_h, Y_p=Y_p, M_h=M_h, M_p=M_p, X_h=X_h, X_p=X_p) else: return x_h, x_p def generate_audio_tag_html_list(list_x, Fs, width='150', height='40'): """Generates audio tag for html needed to be shown in table Notebook: C8/C8S1_HPS.ipynb """ audio_tag_html_list = [] for i in range(len(list_x)): audio_tag = ipd.Audio(list_x[i], rate=Fs) audio_tag_html = audio_tag._repr_html_().replace('\n', '').strip() audio_tag_html = audio_tag_html.replace('