From 569924aa792ef1facd080379313873f7ef25d152 Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Mon, 22 Jan 2024 16:26:11 -0800 Subject: [PATCH 01/10] Added Forest IR Reverb Augmentation Module --- pyha_analyzer/augmentations.py | 261 ++++++++-- pyha_analyzer/forestIR_synthesis/Constants.py | 13 + .../forestIR_synthesis/ForestReverb.py | 470 ++++++++++++++++++ .../forestIR_synthesis/SignalProcessing.py | 292 +++++++++++ pyha_analyzer/forestIR_synthesis/Wav.py | 26 + pyha_analyzer/torchaudio_tests.py | 3 + 6 files changed, 1016 insertions(+), 49 deletions(-) create mode 100644 pyha_analyzer/forestIR_synthesis/Constants.py create mode 100644 pyha_analyzer/forestIR_synthesis/ForestReverb.py create mode 100644 pyha_analyzer/forestIR_synthesis/SignalProcessing.py create mode 100644 pyha_analyzer/forestIR_synthesis/Wav.py create mode 100644 pyha_analyzer/torchaudio_tests.py diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index cf33262..4b49e86 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -11,19 +11,24 @@ import pandas as pd import torch import torchaudio +import scipy +from forestIR_synthesis import (Constants, ForestReverb, SignalProcessing, Wav) +import random from pyha_analyzer import config, utils logger = logging.getLogger("acoustic_multiclass_training") + def invert(seq: Iterable[int]) -> List[float]: """ Replace each element in list with its inverse """ - if 0 in seq: + if 0 in seq: raise ValueError('Passed iterable cannot contain zero') return [1/x for x in seq] + def hyperbolic(seq: Iterable[int]) -> List[Tuple[float, int]]: """ Takes a list of numbers and assigns them a probability @@ -34,14 +39,16 @@ def hyperbolic(seq: Iterable[int]) -> List[Tuple[float, int]]: probabilities = [x/norm_factor for x in invert_seq] return list(zip(probabilities, seq)) + def sample(distribution: List[Tuple[float, int]]) -> int: """ Sample single value from distribution given by list of tuples """ probabilities, values = zip(*distribution) - return np.random.choice(values, p = probabilities) + return np.random.choice(values, p=probabilities) + -def gen_uniform_values(n: int, min_value=0.05) -> List[float] : +def gen_uniform_values(n: int, min_value=0.05) -> List[float]: """ Generates n values uniformly such that their sum is 1 Args: @@ -50,16 +57,18 @@ def gen_uniform_values(n: int, min_value=0.05) -> List[float] : Returns: List of n values """ step = 1/(n-1) - rand_points = np.arange(0, 1, step = step) - rand_points = [0.] + [p + utils.rand(0, step-min_value) for p in rand_points] + rand_points = np.arange(0, 1, step=step) + rand_points = [0.] + \ + [p + utils.rand(0, step-min_value) for p in rand_points] alphas = ( [1 - rand_points[-1]] + [rand_points[i] - rand_points[i-1] for i in range(1, n)] ) - assert sum(alphas) <=1.00005 - assert sum(alphas) >=0.99995 + assert sum(alphas) <= 1.00005 + assert sum(alphas) >= 0.99995 return alphas + class Mixup(torch.nn.Module): """ Attributes: @@ -68,12 +77,13 @@ class Mixup(torch.nn.Module): proportion of new audio in augmented clip p: Probability of mixing """ + def __init__( - self, - df: pd.DataFrame, + self, + df: pd.DataFrame, class_to_idx: Dict[str, Any], cfg: config.Config - ): + ): super().__init__() self.df = df self.class_to_idx = class_to_idx @@ -84,8 +94,8 @@ def __init__( # Get probability distribution for how many clips to mix possible_num_clips = list(range( - cfg.mixup_num_clips_range[0], - cfg.mixup_num_clips_range[1] + 1)) + cfg.mixup_num_clips_range[0], + cfg.mixup_num_clips_range[1] + 1)) self.num_clips_distribution = hyperbolic(possible_num_clips) def get_rand_clip(self) -> Optional[Tuple[torch.Tensor, torch.Tensor]]: @@ -95,26 +105,27 @@ def get_rand_clip(self) -> Optional[Tuple[torch.Tensor, torch.Tensor]]: idx = utils.randint(0, len(self.df)) try: clip, target = utils.get_annotation( - df = self.df, - index = idx, - conf = self.cfg, - class_to_idx = self.class_to_idx) + df=self.df, + index=idx, + conf=self.cfg, + class_to_idx=self.class_to_idx) return clip, target except RuntimeError: logger.error('Error loading other clip, ommitted from mixup') return None - def mix_clips(self, - clip: torch.Tensor, - target: torch.Tensor, + def mix_clips(self, + clip: torch.Tensor, + target: torch.Tensor, other_annotations: List[Tuple[torch.Tensor, torch.Tensor]] - ) -> Tuple[torch.Tensor, torch.Tensor]: + ) -> Tuple[torch.Tensor, torch.Tensor]: """ Mixup clips and targets of clip, target, other_annotations """ annotations = other_annotations + [(clip, target)] clips, targets = zip(*annotations) - mix_factors = gen_uniform_values(len(annotations), min_value = self.min_alpha) + mix_factors = gen_uniform_values( + len(annotations), min_value=self.min_alpha) mixed_clip = sum(c * f for c, f in zip(clips, mix_factors)) mixed_target = sum(t * f for t, f in zip(targets, mix_factors)) @@ -122,14 +133,14 @@ def mix_clips(self, assert isinstance(mixed_clip, torch.Tensor) assert mixed_clip.shape == clip.shape assert mixed_target.shape == target.shape - mixed_target = utils.ceil(mixed_target, interval = self.ceil_interval) + mixed_target = utils.ceil(mixed_target, interval=self.ceil_interval) return mixed_clip, mixed_target def forward( - self, - clip: torch.Tensor, - target: torch.Tensor - ) -> Tuple[torch.Tensor, torch.Tensor]: + self, + clip: torch.Tensor, + target: torch.Tensor + ) -> Tuple[torch.Tensor, torch.Tensor]: """ Args: clip: Tensor of audio data @@ -139,11 +150,12 @@ def forward( chosen clip, Tensor of target mixed with the target of the randomly chosen file """ - if utils.rand(0,1) < self.prob: + if utils.rand(0, 1) < self.prob: return clip, target num_other_clips = sample(self.num_clips_distribution) - other_annotations = [self.get_rand_clip() for _ in range(num_other_clips)] + other_annotations = [self.get_rand_clip() + for _ in range(num_other_clips)] other_annotations = list(filter(None, other_annotations)) return self.mix_clips(clip, target, other_annotations) @@ -158,17 +170,19 @@ def gen_noise(num_samples: int, psd_shape_func: Callable) -> torch.Tensor: Returns: noise Tensor of length num_samples """ - #Reverse fourier transfrom of random array to get white noise + # Reverse fourier transfrom of random array to get white noise white_signal = torch.fft.rfft(torch.rand(num_samples)) # Adjust frequency amplitudes according to # function determining the psd shape shape_signal = psd_shape_func(torch.fft.rfftfreq(num_samples)) # Normalize signal - shape_signal = shape_signal / torch.sqrt(torch.mean(shape_signal.float()**2)) + shape_signal = shape_signal / \ + torch.sqrt(torch.mean(shape_signal.float()**2)) # Adjust frequency amplitudes according to noise type noise = white_signal * shape_signal return torch.fft.irfft(noise) + def noise_generator(func: Callable): """ Given PSD shape function, returns a new function that takes in parameter N @@ -176,31 +190,37 @@ def noise_generator(func: Callable): """ return lambda N: gen_noise(N, func) + @noise_generator def white_noise(vec: torch.Tensor): """White noise PSD shape""" - return torch.ones(vec.shape) + return torch.ones(vec.shape) + @noise_generator def blue_noise(vec: torch.Tensor): """Blue noise PSD shape""" return torch.sqrt(vec) + @noise_generator def violet_noise(vec: torch.Tensor): """Violet noise PSD shape""" return vec + @noise_generator def brown_noise(vec: torch.Tensor): """Brown noise PSD shape""" return 1/torch.where(vec == 0, float('inf'), vec) + @noise_generator def pink_noise(vec: torch.Tensor): """Pink noise PSD shape""" return 1/torch.where(vec == 0, float('inf'), torch.sqrt(vec)) + class SyntheticNoise(torch.nn.Module): """ Attributes: @@ -212,13 +232,14 @@ class SyntheticNoise(torch.nn.Module): 'violet': violet_noise, 'blue': blue_noise, 'white': white_noise} + def __init__(self, cfg: config.Config): super().__init__() self.noise_type = cfg.noise_type self.alpha = cfg.noise_alpha self.device = cfg.prepros_device - def forward(self, clip: torch.Tensor)->torch.Tensor: + def forward(self, clip: torch.Tensor) -> torch.Tensor: """ Args: clip: Tensor of audio data @@ -227,7 +248,7 @@ def forward(self, clip: torch.Tensor)->torch.Tensor: """ noise_function = self.noise_names[self.noise_type] noise = noise_function(len(clip)).to(self.device) - return (1 - self.alpha) * clip + self.alpha* noise + return (1 - self.alpha) * clip + self.alpha * noise class RandomEQ(torch.nn.Module): @@ -241,6 +262,7 @@ class RandomEQ(torch.nn.Module): iterations: number of times to randomly EQ a part of the clip sample_rate: sampling rate of audio """ + def __init__(self, cfg: config.Config): super().__init__() self.f_range = cfg.rand_eq_f_range @@ -268,6 +290,8 @@ def forward(self, clip: torch.Tensor) -> torch.Tensor: # Mald about it pylint! # pylint: disable-next=too-many-instance-attributes + + class BackgroundNoise(torch.nn.Module): """ torch module for adding background noise to audio tensors @@ -276,6 +300,7 @@ class BackgroundNoise(torch.nn.Module): sample_rate: Sample rate (Hz) length: Length of audio clip (s) """ + def __init__(self, cfg: config.Config, norm=False): super().__init__() self.noise_path = Path(cfg.bg_noise_path) @@ -287,17 +312,18 @@ def __init__(self, cfg: config.Config, norm=False): self.norm = norm if self.noise_path_str != "" and cfg.bg_noise_p > 0.0: files = list(os.listdir(self.noise_path)) - audio_extensions = (".mp3",".wav",".ogg",".flac",".opus",".sphere",".pt") - self.noise_clips = [f for f in files if f.endswith(audio_extensions)] + audio_extensions = (".mp3", ".wav", ".ogg", + ".flac", ".opus", ".sphere", ".pt") + self.noise_clips = [ + f for f in files if f.endswith(audio_extensions)] if len(self.noise_clips) == 0: - raise RuntimeError("Background noise path specified, but no audio files found. " \ + raise RuntimeError("Background noise path specified, but no audio files found. " + "Check supported format list in augmentations.py") - elif cfg.bg_noise_p!=0.0: + elif cfg.bg_noise_p != 0.0: raise RuntimeError("Background noise probability is non-zero, " - + "yet no background path was specified. Please update config.yml") + + "yet no background path was specified. Please update config.yml") else: - pass # Background noise is disabled if p=0 and path="" - + pass # Background noise is disabled if p=0 and path="" def forward(self, clip: torch.Tensor) -> torch.Tensor: """ @@ -315,7 +341,8 @@ def forward(self, clip: torch.Tensor) -> torch.Tensor: try: noise_clip = self.choose_random_noise() except RuntimeError as e: - logger.warning('Error loading noise clip, background noise augmentation not performed') + logger.warning( + 'Error loading noise clip, background noise augmentation not performed') logger.error(e) return clip return (1 - alpha)*clip + alpha*noise_clip @@ -329,19 +356,23 @@ def choose_random_noise(self): clip_len = self.sample_rate * self.length if str(noise_file).endswith(".pt"): - waveform = torch.load(noise_file).to(self.device, dtype=torch.float32)/32767.0 + waveform = torch.load(noise_file).to( + self.device, dtype=torch.float32)/32767.0 else: # pryright complains that load isn't called from torchaudio. It is. - waveform, sample_rate = torchaudio.load(noise_file, normalize=True) #pyright: ignore + waveform, sample_rate = torchaudio.load( + noise_file, normalize=True) # pyright: ignore waveform = waveform[0].to(self.device) if sample_rate != self.sample_rate: waveform = torchaudio.functional.resample( - waveform, orig_freq=sample_rate, new_freq=self.sample_rate) - torch.save((waveform*32767).to(dtype=torch.int16), noise_file.with_suffix(".pt")) + waveform, orig_freq=sample_rate, new_freq=self.sample_rate) + torch.save((waveform*32767).to(dtype=torch.int16), + noise_file.with_suffix(".pt")) os.remove(noise_file) file_name = self.noise_clips[rand_idx] self.noise_clips.remove(file_name) - self.noise_clips.append(str(Path(file_name).with_suffix(".pt").name)) + self.noise_clips.append( + str(Path(file_name).with_suffix(".pt").name)) if self.norm: waveform = utils.norm(waveform) start_idx = utils.randint(0, len(waveform) - clip_len) @@ -358,6 +389,7 @@ class LowpassFilter(torch.nn.Module): cutoff: cutoff frequency q_val: Q value for lowpass filter """ + def __init__(self, cfg: config.Config): super().__init__() self.sample_rate = cfg.sample_rate @@ -377,6 +409,7 @@ def forward(self, clip: torch.Tensor) -> torch.Tensor: self.cutoff, self.q_val) + class HighpassFilter(torch.nn.Module): """ Applies highpass filter to audio based on provided parameters. @@ -387,6 +420,7 @@ class HighpassFilter(torch.nn.Module): cutoff: cutoff frequency q_val: Q value for highpass filter """ + def __init__(self, cfg: config.Config): super().__init__() self.sample_rate = cfg.sample_rate @@ -402,6 +436,135 @@ def forward(self, clip: torch.Tensor) -> torch.Tensor: Returns: Tensor of audio data with lowpass filter applied """ return torchaudio.functional.highpass_biquad(clip, - self.sample_rate, - self.cutoff, - self.q_val) + self.sample_rate, + self.cutoff, + self.q_val) + + +class AddReverb(torch.nn.Module): + """ + Applies simulated reverb to a given clip. + + Args: + fs (int): sample rate of the files in Hz + """ + + def __init__(self, fs=44100): + """init + + Args: + fs (int, optional): sample rate of audio in Hz. Defaults to 44100. + """ + self.num_trees = 100_000 + self.fs = fs + self.min_distance = 0 + self.max_distance = 1000 + + def forward(self, clip): + """Applies reverb to all files in the clip passed + + Args: + clip (list): list of the ___ of all the audio clips + + Returns: + list: list of each audio file, with reverb applied, as a list (time + series) + """ + assert isinstance(clip, list) + # assert all([type(value) == calltype for value in clip]) uncomment and replace with whatever type the audio files come in + + # initialize list of outputs + calls_with_reverb = [] + for file in clip: # loop through files in clip + call_reverb = self.add_reverb(file) + calls_with_reverb.append(call_reverb) + # print(calls_with_reverb) + + # temp fix to below problem: pads calls with 0s + max_len = max([len(call) for call in calls_with_reverb]) + print('max:', max_len) + calls_with_reverb = [np.pad(call, (0, max_len - call.size)) + for call + in calls_with_reverb + ] + + # cannot convert because calls may have different lengths + calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) + return calls_with_reverb + + def add_reverb(self, bird_call): + """Applies randomized reverb to the given call + + Args: + bird_call (np.ndarray): time series of the call to process + + Returns: + np.ndarray: Tensor representing the time series of the call after + adding reverb + """ + # assert isinstance(call, calltype) uncomment and replace with whatever type the calls come in + + # read in bird call as time series, convert to mono if needed + if bird_call.ndim >= 2: + bird_call = bird_call.T[0] + fs = self.fs + + # set source position + pos_x = 500 + pos_y = 500 + pos_z = 1.5 + posSrc = np.array([pos_x, pos_y, pos_z]) + + # randomize mic position + micPoss = self.get_mic_pos(posSrc) + result = ForestReverb.simulateForestIR( + nTrees=self.num_trees, + posSrc=posSrc, + micPoss=micPoss.reshape( + [1, 3]), + fs=fs, + sigLen_in_samples=fs*5 + ) + + # apply reverb + impulse = np.reshape(result, result.shape[0]) + call_with_reverb = scipy.signal.convolve( + impulse, bird_call, method='fft' + ) + print(type(call_with_reverb)) + return call_with_reverb + + def get_mic_pos(self, src_pos): + """Generates a np array representing the position of a mic that is a + random distance (within given limits) away from the source + + Args: + src_pos (np.ndarray): position of the source + min (int): minimum distance + max (int): maximum distance + + Returns: + np.ndArray: position of the mic + """ + assert isinstance(src_pos, np.ndarray) + assert len(src_pos) == 3 + # assert isinstance(min, int) + # assert min >= 0 + # assert isinstance(max, int) + # assert max >= 0 + + # FIXME for some reason its not random after the first time (using np.random) + # temp fix import random and use that instead of np + x_offset = random.randint(self.min_distance, self.max_distance) + y_offset = random.randint(self.min_distance, self.max_distance) + mic_x = src_pos[0] + x_offset + mic_y = src_pos[1] + y_offset + print('mic:', x_offset, y_offset) + print('minmax', self.min_distance, self.max_distance) + mic_pos = np.array([mic_x, mic_y, src_pos[2]]) + print('mic at:', mic_pos) + return mic_pos + + +# sanity tests for reverb +test = AddReverb() diff --git a/pyha_analyzer/forestIR_synthesis/Constants.py b/pyha_analyzer/forestIR_synthesis/Constants.py new file mode 100644 index 0000000..c2e4d4a --- /dev/null +++ b/pyha_analyzer/forestIR_synthesis/Constants.py @@ -0,0 +1,13 @@ +__author__ = "Shoken KANEKO" + +import numpy as np +import os + +na = np.newaxis +join = os.path.join + +pi = np.pi +soundVel = 343.0 + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/pyha_analyzer/forestIR_synthesis/ForestReverb.py b/pyha_analyzer/forestIR_synthesis/ForestReverb.py new file mode 100644 index 0000000..b9ea235 --- /dev/null +++ b/pyha_analyzer/forestIR_synthesis/ForestReverb.py @@ -0,0 +1,470 @@ +__author__ = "Shoken KANEKO" + +import numpy as np +import os +from scipy.fft import rfft, irfft +from scipy.special import jn, yn +import time + +na = np.newaxis +join = os.path.join +norm = np.linalg.norm + +from forestIR_synthesis.Constants import pi, soundVel +import forestIR_synthesis.Wav +import forestIR_synthesis.SignalProcessing as sigp +from forestIR_synthesis.SignalProcessing import addSignal, getImpulse +from forestIR_synthesis.SignalProcessing import getFreqsForGivenFilterLength +from forestIR_synthesis.SignalProcessing import getIRFromSpectrum_irfft +from forestIR_synthesis.SignalProcessing import dists, getDelayedSig, floatX + +def computeAirAbsorptionCoef( + freqs, + temperature_in_celsius=20.0, + pressure_in_kiloPascal=101.325, + relativeHumidity_in_percent=50.0): + """ + freqs: array-like with size [F]. + frequencies to compute the air absorption coefficient alpha, + where the sound pressure amplitude A(x) = A0 * exp(-alpha * x) + implementation based on JavaScript code from here: + http://resource.npl.co.uk/acoustics/techguides/absorption/ (accessed: 6/10/2020) + """ + temperature_in_kelvin = 273.15 + temperature_in_celsius + temp_ref = 293.15 + temp_rel = temperature_in_kelvin/temp_ref + T_01 = 273.15 + 0.01 # Triple point isotherm temperature in [K] + P_ref = 101.325 # Reference atmospheric pressure in [kPa] + P_rel = pressure_in_kiloPascal / P_ref + P_sat_over_P_ref = 10**((-6.8346 * (T_01 / temperature_in_kelvin)**1.261) + 4.6151) + H = relativeHumidity_in_percent * (P_sat_over_P_ref / P_rel) + Fro = P_rel * (24 + 40400 * H * (0.02 + H) / (0.391 + H)) + Frn = P_rel / np.sqrt(temp_rel) * (9 + 280 * H * np.power(np.e, (-4.17 * (np.power(temp_rel, (-1 / 3)) - 1)))) + Xc = 0.0000000000184 / P_rel * np.sqrt(temp_rel) + f2 = freqs**2 + Xo = 0.01275 * np.power(np.e, (-2239.1 / temperature_in_kelvin)) * np.power((Fro + (f2 / Fro)), -1) + Xn = 0.1068 * np.power(np.e, (-3352 / temperature_in_kelvin)) * np.power((Frn + (f2 / Frn)), -1) + alpha = 20 * np.log10(np.e) * f2 * (Xc + np.power(temp_rel, (-5 / 2)) * (Xo + Xn)) + return alpha # [F] + +def getFilterBankForAirAbsorption(ntaps, fs, dists, fftLen): + freqs, freqs_half = sigp.getFreqsForGivenFilterLength(ntaps, fs) + alphas = computeAirAbsorptionCoef(freqs_half) # [hF] + attenuations_dB = alphas[:, na] * dists[na, :] # [hF x M] ... in dB + attenuations = sigp.getAmplitudeFromdB(-attenuations_dB) + filters = irfft(attenuations, axis=0) # [F=ntaps x M] + filters = np.roll(filters, shift=ntaps // 2, axis=0) + specs = rfft(filters,n=fftLen,axis=0) + return filters, specs # [ntaps x nDists] + +def computeGammaTable(N,a,freqs): + # a: tree radius + # N: maximum order of Bessel functions + # freqs: [F] + F = len(freqs) + ks = 2*pi*freqs/soundVel # [F] + kas = ks*a # [F] + ns = np.arange(N+2) # [N+2] + jns = [jn(ns,ka) for ka in kas] # [F x N+2] + yns = [yn(ns,ka) for ka in kas] # [F x N+2] + jns = np.array(jns) + yns = np.array(yns) + tanGamma_n = np.zeros([F,N+1],dtype=floatX) + for n in range(1,N+1): + import warnings + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + tanGamma_n[:,n] = (jns[:,n-1] - jns[:,n+1])/(yns[:,n-1] - yns[:,n+1]) + tanGamma_n[:,0] = jns[:,1]/yns[:,1] + tanGamma_n[0, :] = 0 + gamma_n = np.arctan(tanGamma_n) # [F x N+1] + sinGamma_n = np.sin(gamma_n) + cosGamma_n = np.cos(gamma_n) + expiGamma_n = cosGamma_n + 1j * sinGamma_n + sinexpiGamma_n = sinGamma_n * expiGamma_n # [F x N+1] + sinexpiGamma_n[:,1:] *= 2 # multiplying En + sinexpiGamma_n[0,:] = 0 + return sinexpiGamma_n # [F x N+1], (freq)^-0.5 * En * sinGamma_n * expiGamma_n + # the rest: + # multiply cosnphi(freq, angle), + # sum over all n, + # mult (freqs**-0.5) + # mult ((2/pi)**0.5 * np.exp(1j*pi*0.25)), + # apply delay and multiply 1/dist**0.5 + +def computeAngleDependentCylinderScatteringFilter(N, a, freqs, angles_in_radian): + # angles_in_radian: [A] + gammaTable = computeGammaTable(N,a,freqs) # [F x N+1] + ns = np.arange(N+1) # [N+1] + cosnphis = np.cos(ns[na,:]*angles_in_radian[:,na]) # [A x N+1] + sum = np.sum(gammaTable[:,na,:]*cosnphis[na,:,:], axis=-1) # [F x A] + const = ((2/pi)**0.5 * np.exp(1j*pi*0.25)) + import warnings + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + sum = sum * ((freqs**-0.5)*const)[:,na] # [F x A] + sum[0,:] = 1e-8 + return sum + # the rest: + # apply delay and multiply 1/dist**0.5 + +def computeAngleDependentSphereRadiationFilter(maxOrder, a, freqs, angles_in_radian): + # angles_in_radian: [A] + import scipy.special as sp + N = maxOrder + ns = np.arange(maxOrder+1) # [N+1] + kas = 2*np.pi*freqs/soundVel * a # [F] + jnps = np.array([sp.spherical_jn(ns,ka) for ka in kas]) # [F x N+1] + ynps = np.array([sp.spherical_yn(ns,ka) for ka in kas]) # [F x N+1] + hnps = jnps + 1j * ynps # [F x N+1] + coss = np.cos(angles_in_radian) # [A] + Pncoss = np.array([sp.lpn(N, z=cos)[0] for cos in coss]) # [A x N+1] + numerator = ((2*ns+1)*(-1j)**(ns-1)) * Pncoss # [A x N+1] + numerator_LF = (((2*ns+1)*(-1j)**(ns-1)) * Pncoss)[na,:,:] * (kas[:,na]**ns[na,:])[:,na,:] # [F x A x N+1] + denominator = (kas**2)[:,na] * hnps # [F x N+1] + denominator_LF = 1j*(ns+1)*sp.factorial2(2*ns-1) #/(kas[:,na]**ns[na,:]) # [F x N+1] + summand = numerator[na,:,:] / denominator[:,na,:] # [F x A x N+1] + summand_LF = numerator_LF / denominator_LF # [F x A x N+1] + for idx,ka in enumerate(kas): + for n in ns: + if ka<=0.01*n: + summand[idx,:,n] = summand_LF[idx,:,n] + sum = np.sum(summand, axis=-1) # [F x A] + return sum # [F x A] + +def getFilterBankForAngleDependentCylinderScattering( + treeRad=0.25, fs=24000, steps=128, nAngleBins=180, N=50, fftLen=128*3): + a = treeRad + freqs, freqs_half = getFreqsForGivenFilterLength(steps, fs) + angles_in_radian = np.arange(0,nAngleBins+1) * 180/nAngleBins * pi/180 + Ns = [N] # 50 is just enough + F = len(freqs) + A = len(angles_in_radian) + filter_NxhFxA = np.zeros((len(Ns),F//2+1,A),dtype=np.complex128) + for n,N in enumerate(Ns): + filter_hFxA = computeAngleDependentCylinderScatteringFilter(N, a, freqs_half, angles_in_radian) + filter_NxhFxA[n,:,:] = filter_hFxA + + filter_FxA = np.zeros([F,A],dtype=np.complex128) + filter_FxA[:F//2+1,:] = filter_hFxA[:,:] + filter_FxA[F//2+1:,:] = filter_hFxA[1:F//2,:][::-1,:].conj() + irs = getIRFromSpectrum_irfft(filter_hFxA) # [T x C] + irs = np.roll(irs, shift=steps//2, axis=0) + specs = rfft(irs,n=fftLen,axis=0) # [Fh(=2T//2+1=T+1) x nAngles] + return irs, specs # [T x nAngles], [T+1 x nAngles] + +def getFilterBankForSourceDirectivity(fs, ntaps, nAngleBins=180, birdHeadRad=0.025, maxOrder=80, fftLen=128, doPlot=0): + a = birdHeadRad + freqs, freqs_half = getFreqsForGivenFilterLength(ntaps, fs) + angles_in_radian = np.arange(0, nAngleBins + 1) * 180 / nAngleBins * pi / 180 + Ns = [maxOrder] + Fh = len(freqs_half) + F = len(freqs) + A = len(angles_in_radian) + filter_NxFhxA = np.zeros((len(Ns), Fh, A), dtype=np.complex128) + for n, N in enumerate(Ns): + filter_FhxA = computeAngleDependentSphereRadiationFilter(N, a, freqs_half, angles_in_radian) + filter_NxFhxA[n, :, :] = filter_FhxA + filter_FxA = np.zeros([F, A], dtype=np.complex128) + filter_FxA[:F // 2 + 1, :] = filter_FhxA[:, :] + filter_FxA[F // 2 + 1:, :] = filter_FhxA[1:F // 2, :][::-1, :].conj() + irs = getIRFromSpectrum_irfft(filter_FhxA) # [T x C] + irs = np.roll(irs, shift=ntaps // 2, axis=0) + rms_0 = sigp.getAweightedRMS(irs[:,0], fs, energy=1) + irs = irs / rms_0**0.5 + specs = rfft(irs, n=fftLen, axis=0) # [Fh(=2T//2+1=T+1) x nAngles] + return irs, specs # [T x nAngles], [T+1 x nAngles] + +def getTreePoss(forestRange_x, forestRange_y, nTrees, algo="uniform", seed=1234): + + if algo.lower()=="uniform": + np.random.seed(seed) + treePoss = np.random.uniform(size=[nTrees,2]) + else: + raise NotImplementedError + + assert treePoss.shape[0]==nTrees + treePoss[:, 0] *= (forestRange_x[1] - forestRange_x[0]) + treePoss[:, 1] *= (forestRange_y[1] - forestRange_y[0]) + treePoss += np.array([forestRange_x[0], forestRange_y[0]]) + treePoss = np.concatenate([treePoss, np.zeros([nTrees,1])], axis=-1) # [nTrees x 3] + return treePoss # [nTrees x 3] + +def getRotMat2D(theta): + c = np.cos(theta) + s = np.sin(theta) + return np.array([[c,-s],[s,c]]) + +def getRotMat3D_hori(theta): + c = np.cos(theta) + s = np.sin(theta) + return np.array([[c,-s,0],[s,c,0],[0,0,1]]) + +def getCosOf3Pnts(p1,p2,p3): + v1 = (p2 - p1)[:2] + v2 = (p3 - p2)[:2] + cos = v1.dot(v2)/(norm(v1)*norm(v2)) + return cos + +def getCosOf2Vecs(v1,v2): + cos = v1.dot(v2)/(norm(v1)*norm(v2)) + return cos + +def getCossOf3Pnts(p1,p2s,p3): + # p2s: [N x 3] + v1 = (p2s[:,:2] - p1[:2]) # [N x 2] + v2 = (p3[:2] - p2s[:,:2]) + coss = np.sum(v1*v2,axis=-1)/(np.sum(v1**2,axis=-1)**0.5 * np.sum(v2**2,axis=-1)**0.5) + return coss # [N] + +def getCossOf2Vecs_batch(v1,v2s): + # v2s: [N x 3] + coss = np.sum(v1*v2s,axis=-1)/(np.sum(v1**2,axis=-1)**0.5 * np.sum(v2s**2,axis=-1)**0.5) + return coss # [N] + +def getAndStoreMultipleScatteringFilter(filterBank_treeScattering_TxA, angleBins, dicFilter): + if len(angleBins)==1: + if str(angleBins) in dicFilter: + return dicFilter[str(angleBins)], dicFilter + else: + dicFilter[str(angleBins)] = filterBank_treeScattering_TxA[:,angleBins[0]] + return dicFilter[str(angleBins)], dicFilter + if str(angleBins[:-1]) in dicFilter: + filter_new = np.convolve(filterBank_treeScattering_TxA[:,angleBins[-1]],dicFilter[str(angleBins[:-1])]) + dicFilter[str(angleBins)] = filter_new + return filter_new, dicFilter + else: + filter_new, dic_Filter = getAndStoreMultipleScatteringFilter(filterBank_treeScattering_TxA, angleBins[:-1], dicFilter) + filter_new = np.convolve(filterBank_treeScattering_TxA[:,angleBins[-1]],filter_new) + dicFilter[str(angleBins)] = filter_new + return filter_new, dicFilter + + +def getAirFilterIdx(dist, D): + return min(int(dist // 10), D) + +def getAirFilterIdx_batch(dists, Dary): + return np.minimum(dists // 10, Dary).astype(int) + +def simulateForestIR( + nTrees, posSrc, micPoss, fs, sigLen_in_samples=None, forestRange_x=None, forestRange_y=None, reflectionCoef=2.0, + treeRadiationExponent=0.7, applyCylindricalScatteringFilter=1, applyAirAbsorption=1, maxDist=20000.0, + maxReflOrder=1, seed=1234, floorReflectionCoef=0.8, ntaps_treeScattering=128, ntaps_airAbsorption=128, + ntaps_delay=128, sourceDirectivity=0, ntaps_sourceDirectivity=128, sourceDirectivityVec=None, + samplingAlgo="uniform" + ): + # scalable forest reverb generator. + assert maxReflOrder<=1,"Higher order scattering is not supported." + assert applyAirAbsorption==1 + assert applyCylindricalScatteringFilter==1 + print("simulating forest IR...") + if forestRange_x is None: + forestRange_x = [0.0,1000.0] + if forestRange_y is None: + forestRange_y = [0.0,1000.0] + if nTrees>0: + treePoss = getTreePoss(forestRange_x, forestRange_y, nTrees, algo=samplingAlgo, seed=seed) + else: + treePoss = None + if nTrees==0: + ntaps_treeScattering = 0 + dists_src_to_mics_direct = dists(posSrc.reshape([1,3]), micPoss)[0] # [nMics] + posSrc_floorMirror = posSrc.copy() + posSrc_floorMirror[2] *= -1 + dists_src_to_mics_floor = dists(posSrc_floorMirror.reshape([1,3]), micPoss)[0] # [nMics] + if nTrees>0: + dists_src_to_trees = dists(posSrc.reshape([1,3])[:,:2], treePoss[:,:2])[0] # [nTrees] + dists_trees_to_mic = dists(micPoss[:,:2], treePoss[:,:2]) # [nMics x nTrees] + dists_src_to_mics_reflect = dists_src_to_trees[na,:] + dists_trees_to_mic # [nMics x nTrees] + distDiffs_src_to_mics_reflect = dists_src_to_mics_reflect - dists_src_to_mics_direct[:,na] # [nMics x nTrees] + distsMult_src_to_mics_reflect = dists_src_to_trees[na,:] * dists_trees_to_mic**treeRadiationExponent # [nMics x nTrees] + dists_src_to_mics_reflect_ = dists_src_to_mics_reflect + distsMult_src_to_mics_reflect_ = distsMult_src_to_mics_reflect + + domain = "freq" + if domain=="freq": + fftLen = ntaps_treeScattering + ntaps_airAbsorption + ntaps_delay + sourceDirectivity * ntaps_sourceDirectivity + else: + fftLen = 128 + Bt = 100 # batch size in batched path processing + assert nTrees%Bt==0, "The number of trees must be "+str(Bt)+"n (n: integer)." + if applyCylindricalScatteringFilter==1 and nTrees>0: + filterBank_treeScattering_TxA, filterBank_treeScattering_freqDomain_FhxA = \ + getFilterBankForAngleDependentCylinderScattering(fs=fs, steps=ntaps_treeScattering, fftLen=fftLen) + filterBank_treeScattering_TxA *= reflectionCoef + filterBank_treeScattering_freqDomain_FhxA *= reflectionCoef + filterBank_treeScattering_freqDomain_FhxA_ = filterBank_treeScattering_freqDomain_FhxA + + distBins = np.arange(0,maxDist+10,10) + if applyAirAbsorption==1: + filterBank_airAbsorption_TxD, filterBank_airAbsorption_freqDomain_FhxD = \ + getFilterBankForAirAbsorption(ntaps_airAbsorption, fs, dists=distBins, fftLen=fftLen) + D = filterBank_airAbsorption_TxD.shape[1] + Dary = np.ones([Bt]) * D + filterBank_airAbsorption_freqDomain_FhxD_ = filterBank_airAbsorption_freqDomain_FhxD + Dary_ = Dary + + if sourceDirectivity==1: + filterBank_directivity_TxA, filterBank_directivity_FhxA = getFilterBankForSourceDirectivity( + fs, ntaps=ntaps_sourceDirectivity, fftLen=fftLen) + + nMic = len(micPoss) + if sigLen_in_samples is None: + diagonalLen = ((forestRange_y[1]-forestRange_y[0])**2 + (forestRange_x[1]-forestRange_x[0])**2)**0.5 + sigLen_in_samples = int(diagonalLen / soundVel * fs) + signal = np.zeros([sigLen_in_samples, nMic], dtype=floatX) + impulse = getImpulse(1) + # treeIndices = np.arange(nTrees, dtype=int) + + T1 = len(signal) + posSrc_ = posSrc + treePoss_ = treePoss + micPoss_ = micPoss + + for m in range(nMic): + print("computing ir from src to mic",m,"...") + tic = time.time() + if nTrees>0: + distDiffs_src_to_mic_reflect = distDiffs_src_to_mics_reflect[m,:] # [nTrees] + + # compute direct path + direct = impulse + # apply air absorption + if applyAirAbsorption==1: + airFilterIdx = getAirFilterIdx(dists_src_to_mics_direct[m],D) + airFilter = filterBank_airAbsorption_TxD[:,airFilterIdx] + direct = np.convolve(direct,airFilter) + if sourceDirectivity==1: + cos_dir = getCosOf2Vecs(sourceDirectivityVec,micPoss[m]-posSrc) + cos_dir = min(1,cos_dir) + cos_dir = max(-1,cos_dir) + angle_dir = np.arccos(cos_dir) * 180/pi + angleBin_dir = (angle_dir+0.5).astype(int) + dirFilter = filterBank_directivity_TxA[:,angleBin_dir] + direct = np.convolve(direct,dirFilter) + + # apply delay and distance attenuation + delayedSig_direct_m, delayToAdd = getDelayedSig(direct, fs, delayInSec=0.0, distance=dists_src_to_mics_direct[m]) + addSignal(signal, delayedSig_direct_m, delayToAdd=delayToAdd, channel=m) + + # compute floor reflection + floor = impulse + # apply air absorption + if applyAirAbsorption==1: + airFilterIdx = getAirFilterIdx(dists_src_to_mics_direct[m],D) + airFilter = filterBank_airAbsorption_TxD[:,airFilterIdx] + floor = airFilter + if sourceDirectivity==1: + micPos_refl = micPoss[m].copy() + micPos_refl[2] *= -1 + cos_dir = getCosOf2Vecs(sourceDirectivityVec,micPos_refl-posSrc) + cos_dir = min(1,cos_dir) + cos_dir = max(-1,cos_dir) + angle_dir = np.arccos(cos_dir) * 180/pi + angleBin_dir = (angle_dir+0.5).astype(int) + dirFilter = filterBank_directivity_TxA[:,angleBin_dir] + floor = np.convolve(floor, dirFilter) + + # apply delay and distance attenuation + delayedSig_floor_m, delayToAdd = getDelayedSig(floor, fs, delayInSec=0.0, distance=dists_src_to_mics_floor[m]) + addSignal(signal, delayedSig_floor_m * floorReflectionCoef, delayToAdd=delayToAdd, channel=m) + + # compute 1st order scattering by trees + if nTrees>0: + coss = getCossOf3Pnts(posSrc_, treePoss_, micPoss_[m]) + coss[coss<-1] = -1 + coss[coss>1] = 1 + angles = np.arccos(coss) * 180.0/pi + angleBins = (angles+0.5).astype(int) + # if np.max() + assert applyAirAbsorption==1 + assert applyCylindricalScatteringFilter==1 + + if sourceDirectivity==1: + treePoss_[:,2] = micPoss[m,2] + coss_dir = getCossOf2Vecs_batch(sourceDirectivityVec, treePoss_ - posSrc) + coss_dir[coss_dir<-1] = -1 + coss_dir[coss_dir>1] = 1 + angles_dir = np.arccos(coss_dir) * 180 / pi + angleBins_dir = (angles_dir + 0.5).astype(int) + + if domain=="time": + for t in range(nTrees): + # apply tree scattering and air absorption + scatteringFilter = filterBank_treeScattering_TxA[:,angleBins[t]] # [T1] + airFilter = filterBank_airAbsorption_TxD[:, getAirFilterIdx(dists_src_to_mics_reflect[m,t],D)] # [T2] + reflected_m_t = np.convolve(scatteringFilter, airFilter) # [T1+T2-1] + # apply delay and distance attenuation + delayedSig_reflect_m_t, delayToAdd = getDelayedSig( + reflected_m_t, fs, delayInSec=dists_src_to_mics_reflect[m,t]/soundVel, distance=0.0, + nIRwithoutDelay=ntaps_delay) + delayedSig_reflect_m_t /= distsMult_src_to_mics_reflect[m,t] + # add signal to result + addSignal(signal, delayedSig_reflect_m_t, delayToAdd=delayToAdd, channel=m) + elif domain=="freq": + for tHead in range(0,nTrees,Bt): + # apply tree scattering and air absorption + scatteringFilter_freqDomain = filterBank_treeScattering_freqDomain_FhxA_[:,angleBins[tHead:tHead+Bt]] # [Fh x Bt] + airFilter_freqDomain = filterBank_airAbsorption_freqDomain_FhxD_[:, getAirFilterIdx_batch( + dists_src_to_mics_reflect_[m,tHead:tHead+Bt],Dary_)] # [Fh x Bt] + reflected_m_Bt_freqDomain = scatteringFilter_freqDomain * airFilter_freqDomain # [Fh x Bt] + if sourceDirectivity==1: + reflected_m_Bt_freqDomain = reflected_m_Bt_freqDomain * filterBank_directivity_FhxA[:,angleBins_dir[tHead:tHead+Bt]] + # apply delay + delayedSigs_reflect_m_Bt, delaysToAdd = \ + sigp.getDelayedSig_batch_freqDomain( + reflected_m_Bt_freqDomain, + fs, dists_src_to_mics_reflect_[m,tHead:tHead+Bt]/soundVel, + nIRwithoutDelay=ntaps_delay, fftLen=fftLen) # [T1+T2+T3 x Bt], [Bt] + # apply amplitude attenuation + delayedSigs_reflect_m_Bt /= distsMult_src_to_mics_reflect_[m, tHead:tHead+Bt] + + T2 = fftLen + tailsInSig1 = np.minimum(T1, delaysToAdd + T2) + tailsInSig2 = np.minimum(T2, T1 - delaysToAdd) + + t__ = np.where(delaysToAdd stepSize + freqs,times,spec_FxnumChxnumSteps = \ + spstft(sig_TxnCh, fs=fs, window='hann', nperseg=winSize, noverlap=noverlap, + nfft=None, detrend=False, return_onesided=False, boundary='zeros', padded=True, axis=0) + if sig_TxnCh.ndim==2: + spec = np.array(spec_FxnumChxnumSteps).transpose([2,0,1]) # [numSteps x F x numCh] <- [F x numCh x numSteps] + elif sig_TxnCh.ndim==1: + spec = spec_FxnumChxnumSteps.T # [numSteps x F] <- [F x numSteps] + else: + raise NotImplementedError + return freqs, times, spec + +def istft(spec_TxFxnCh, fs, winSize, stepSize): + if stepSize is None: + stepSize = winSize//2 + noverlap = winSize - stepSize + assert winSize > stepSize + _, sig_TxnCh = spistft(spec_TxFxnCh, fs=fs, window='hann', nperseg=winSize, noverlap=noverlap, + nfft=None, input_onesided=False, boundary=True, time_axis=0, freq_axis=1) + return sig_TxnCh + +def getZeroPadded(sig, retSigLen): + if sig.ndim==1: + T = len(sig) + assert retSigLen>T + ret = np.concatenate([sig, np.zeros([retSigLen - T],dtype=sig.dtype)]) + elif sig.ndim==2: + T,C = sig.shape + assert retSigLen>T + ret = np.concatenate([sig, np.zeros([retSigLen - T,C],dtype=sig.dtype)],axis=0) + else: + raise NotImplementedError + return ret + +def dist(p1,p2): + # p1, p2: [3] + return np.sum((p1-p2)**2,axis=-1)**0.5 + +def dist_hori(p1,p2): + # p1, p2: [3] + return np.sum((p1-p2)[:2]**2,axis=-1)**0.5 + +def dists(ps1,ps2): + # ps1: [nPnts1 x 3] + # ps2: [nPnts2 x 3] + return np.sum((ps1[:,na,:]-ps2[na,:,:])**2,axis=-1)**0.5 # [nPnts1 x nPnts2] + +def delayInSec(p1,p2): + d = dist(p1, p2) + return d / soundVel + +def delaysInSec(ps1,ps2): + d = dists(ps1, ps2) + return d / soundVel # [nPnts1 x nPnts2] + +def delayInSamples(p1,p2,fs): + d = dist(p1,p2) + return d / soundVel * fs + +def convolve(sig1, sig2, domain="time"): + # sig1: [T1] or [T1 x C1] + # sig2: [T2] or [T2 x C2] + if sig1.ndim == 1: + sig1_ = sig1.reshape([len(sig1),1]) + else: + sig1_ = sig1 + if sig2.ndim == 1: + sig2_ = sig2.reshape([len(sig2),1]) + else: + sig2_ = sig2 + + if domain=="time": + ret = [] + for c1 in range(sig1_.shape[1]): + ret.append([np.convolve(sig1_[:,c1],sig2_[:,c2]) for c2 in range(sig2_.shape[1])]) + # ret: [c1][c2][T3] + return np.array(ret).transpose([2,0,1]) # [T3 x c1 x c2] + elif domain=="freq": + from scipy.fft import rfft + n = len(sig1_) + len(sig2_) - 1 + sig1_f = rfft(sig1_,n=n,axis=0) # [Fh x c1] + sig2_f = rfft(sig2_,n=n,axis=0) # [Fh x c2] + conved_f = sig1_f[:,:,na] * sig2_f[:,na,:] # [Fh x c1 x c2] + conved = irfft(conved_f,axis=0) # [T x c1 x c2] + return conved.real + else: + raise NotImplementedError + +def getDelayedSig(sig, fs, delayInSec=None, distance=None, nIRwithoutDelay = 128): + # sig: [T] + assert sig.ndim==1 + # set distance to None if only time delay but no amplitude reduction should be applied + # set delayInSec to None if delay should be computed from distance + if delayInSec is None or delayInSec==0.0: + assert distance is not None + delayInSec = distance / soundVel + if distance is None or distance==0.0: + distance = 1.0 + assert distance != 0 + delayInSamples = delayInSec * fs + delayIRLen = int(delayInSamples + nIRwithoutDelay//2) + if delayInSamples > nIRwithoutDelay//2: + delayToAdd = int(delayInSamples - nIRwithoutDelay // 2) + else: + delayToAdd = 0 + ts = np.arange(start=delayToAdd,stop=delayIRLen) + x = pi * (ts - delayInSamples) + delayIR = np.sin(x)/(x * distance) + if delayIRLen > delayInSamples and delayInSamples - int(delayInSamples) == 0: + delayIR[int(delayInSamples) - delayToAdd] = 1.0 / distance + delayed = np.convolve(sig, delayIR) + return delayed, delayToAdd + +def getDelayedSig_batch(sigs, fs, delaysInSec=None, nIRwithoutDelay = 128): + # sigs: [T x C] + # delaysInSec, distances: [C] + assert sigs.ndim == 2 + T,C = sigs.shape + delaysInSamples = delaysInSec * fs # [C] ... this is a floating point number + delaysInSamples_int = delaysInSamples.astype(int) + delaysToAdd = delaysInSamples_int - int(nIRwithoutDelay // 2) # [C] + effectiveIRLen = nIRwithoutDelay + delaysToAdd[delaysInSamples <= nIRwithoutDelay//2] = 0 + ts = np.arange(effectiveIRLen) # [T] + ts = np.tile(ts.reshape([effectiveIRLen,1]),reps=(1,C)) # [T x C] + ts = ts + delaysToAdd # [T x C] + x = pi * (ts - delaysInSamples) # [T x C] + import warnings + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + delayIRs = np.sin(x)/x # [T x C] + singulars = (x==0) * (delaysInSamples == delaysInSamples_int) + delayIRs[singulars] = 1.0 + delayed = np.array([np.convolve(sigs[:,i], delayIRs[:,i]) for i in range(C)]).T # [T x C] + return delayed, delaysToAdd # [T x C], [C] + +def getDelayedSig_batch_freqDomain(sigs, fs, delaysInSec=None, nIRwithoutDelay=128, fftLen=128*3): + # sigs: [Fh x C] + # delaysInSec, distances: [C] + assert sigs.ndim == 2 + _,C = sigs.shape + delaysInSamples = delaysInSec * fs # [C] ... this is a floating point number + delaysInSamples_int = delaysInSamples.astype(np.int32) + delaysToAdd = delaysInSamples_int - int(nIRwithoutDelay // 2) # [C] + effectiveIRLen = nIRwithoutDelay + delaysToAdd[delaysInSamples <= nIRwithoutDelay//2] = 0 + ts = np.arange(effectiveIRLen) # [T] + ts = np.tile(ts.reshape([effectiveIRLen,1]),reps=(1,C)) # [T x C] + ts = ts + delaysToAdd # [T x C] + x = pi * (ts - delaysInSamples) # [T x C] + delayIRs = np.sin(x)/x # [T x C] + singulars = (x==0) * (delaysInSamples == delaysInSamples_int) + delayIRs[singulars] = 1.0 + from scipy.fft import rfft, irfft + delayIRs_freqDomain = rfft(delayIRs,n=fftLen,axis=0) # [Fh x C] + delayed_freqDomain = sigs * delayIRs_freqDomain + delayed = irfft(delayed_freqDomain, axis=0) # [T x C] + return delayed, delaysToAdd # [T x C], [C] + +def getFreqsForGivenFilterLength(nTaps, fs): + nFreqBins = nTaps + df = fs / nFreqBins + freqs = np.arange(nFreqBins) * df + freqs_half = freqs[:nFreqBins // 2 + 1] + return freqs, freqs_half + +def getHPF(nTaps, fs, fcut): + import scipy.signal as spsig + assert nTaps%2==1 + fir = spsig.firwin(nTaps, cutoff=fcut, fs=fs, pass_zero=False) + return fir + +def getBPF(nTaps, fs, bandFreq_low, bandFreq_high): + import scipy.signal as spsig + assert nTaps%2==1 + fir = spsig.firwin(nTaps,cutoff=[bandFreq_low,bandFreq_high],fs=fs, pass_zero=False) + return fir + +def get_dB_from_amplitude(spec_TxF, eps=1e-8): + return 20*np.log(np.abs(spec_TxF)+eps)/np.log(10) + +def getAmplitudeFromdB(dB): + amp = np.exp(dB*np.log(10)/20) + return amp + +def getIRFromSpectrum(spec_FxC): + from scipy.fft import ifft + ir = ifft(spec_FxC, axis=0).real + return ir # [T x C] + +def getIRFromSpectrum_irfft(spec_hFxC): + ir = irfft(spec_hFxC, axis=0).real + return ir # [T x C] + +def getLongerSignalByRepeating(sig, desiredLen): + # sig: [T] or [T x C] + T = len(sig) + nRep = int(desiredLen/T)+1 + if sig.ndim==2: + rep = np.tile(sig, reps=[nRep, 1]) + else: + rep = np.tile(sig, reps=nRep) + assert len(rep)>=desiredLen + return rep[:desiredLen] + +def addSignal(sig1, sig2, delayToAdd, channel): + # sig1: [T1 x C] + # sig2: [T2] + # delayToAdd: position in sig1 to start adding sig2 + assert int(delayToAdd)==delayToAdd + T1 = len(sig1) + T2 = len(sig2) + tailInSig1 = min(T1, delayToAdd + T2) + tailInSig2 = min(T2, T1 - delayToAdd) + assert channel is not None + if delayToAdd Date: Mon, 22 Jan 2024 17:14:36 -0800 Subject: [PATCH 02/10] Added TODO's and FIXME's for AddReverb --- pyha_analyzer/augmentations.py | 16 ++++++++++------ pyha_analyzer/torchaudio_tests.py | 3 --- 2 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 pyha_analyzer/torchaudio_tests.py diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 4b49e86..006c83a 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -449,7 +449,7 @@ class AddReverb(torch.nn.Module): fs (int): sample rate of the files in Hz """ - def __init__(self, fs=44100): + def __init__(self, fs=44_100): """init Args: @@ -471,7 +471,8 @@ def forward(self, clip): series) """ assert isinstance(clip, list) - # assert all([type(value) == calltype for value in clip]) uncomment and replace with whatever type the audio files come in + # assert all([type(value) == calltype for value in clip]) + # TODO: uncomment and replace with whatever type the audio files come in # initialize list of outputs calls_with_reverb = [] @@ -486,9 +487,9 @@ def forward(self, clip): calls_with_reverb = [np.pad(call, (0, max_len - call.size)) for call in calls_with_reverb - ] + ] - # cannot convert because calls may have different lengths + # FIXME: cannot convert because calls may have different lengths calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) return calls_with_reverb @@ -502,7 +503,8 @@ def add_reverb(self, bird_call): np.ndarray: Tensor representing the time series of the call after adding reverb """ - # assert isinstance(call, calltype) uncomment and replace with whatever type the calls come in + # assert isinstance(call, calltype) + # TODO: uncomment and replace with whatever type the calls come in # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: @@ -517,6 +519,7 @@ def add_reverb(self, bird_call): # randomize mic position micPoss = self.get_mic_pos(posSrc) + result = ForestReverb.simulateForestIR( nTrees=self.num_trees, posSrc=posSrc, @@ -567,4 +570,5 @@ def get_mic_pos(self, src_pos): # sanity tests for reverb -test = AddReverb() +test = AddReverb(55000) +print("hello") diff --git a/pyha_analyzer/torchaudio_tests.py b/pyha_analyzer/torchaudio_tests.py deleted file mode 100644 index 0bd50f7..0000000 --- a/pyha_analyzer/torchaudio_tests.py +++ /dev/null @@ -1,3 +0,0 @@ -import torchaudio -#from torchaudio import transforms -print("yay!") \ No newline at end of file From 23310e9f1f39a721f4f4e6814d82c2e23ce97a90 Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Wed, 31 Jan 2024 09:13:32 -0800 Subject: [PATCH 03/10] added some assert statements for reverb --- pyha_analyzer/augmentations.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 006c83a..d72158f 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -470,9 +470,8 @@ def forward(self, clip): list: list of each audio file, with reverb applied, as a list (time series) """ - assert isinstance(clip, list) - # assert all([type(value) == calltype for value in clip]) - # TODO: uncomment and replace with whatever type the audio files come in + assert isinstance(clip, torch.Tensor) + assert all([type(value) == np.ndarray for value in clip]) # initialize list of outputs calls_with_reverb = [] @@ -489,7 +488,6 @@ def forward(self, clip): in calls_with_reverb ] - # FIXME: cannot convert because calls may have different lengths calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) return calls_with_reverb @@ -503,8 +501,7 @@ def add_reverb(self, bird_call): np.ndarray: Tensor representing the time series of the call after adding reverb """ - # assert isinstance(call, calltype) - # TODO: uncomment and replace with whatever type the calls come in + assert isinstance(bird_call, np.ndarray) # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: @@ -526,7 +523,7 @@ def add_reverb(self, bird_call): micPoss=micPoss.reshape( [1, 3]), fs=fs, - sigLen_in_samples=fs*5 + sigLen_in_samples=fs*5 # not sure why its x5 here ) # apply reverb @@ -570,5 +567,6 @@ def get_mic_pos(self, src_pos): # sanity tests for reverb -test = AddReverb(55000) print("hello") +test = AddReverb(55000) +print("Goodbye") From af43872ec3b002ca9d7d1c1316067e3d26877a9f Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Wed, 7 Feb 2024 12:31:13 -0800 Subject: [PATCH 04/10] Added source link for IR code --- pyha_analyzer/augmentations.py | 5 ++++- pyha_analyzer/forestIR_synthesis/Constants.py | 5 +++++ pyha_analyzer/forestIR_synthesis/ForestReverb.py | 5 +++++ pyha_analyzer/forestIR_synthesis/SignalProcessing.py | 5 +++++ pyha_analyzer/forestIR_synthesis/Wav.py | 5 +++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index d72158f..29ea1de 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -444,7 +444,10 @@ def forward(self, clip: torch.Tensor) -> torch.Tensor: class AddReverb(torch.nn.Module): """ Applies simulated reverb to a given clip. - + This module depends on Microsoft's Forest IR Synthesis repo, which can be + found here: + https://github.com/microsoft/Forest_IR_synthesis/tree/main + Date of retrieval: February 7, 2024 Args: fs (int): sample rate of the files in Hz """ diff --git a/pyha_analyzer/forestIR_synthesis/Constants.py b/pyha_analyzer/forestIR_synthesis/Constants.py index c2e4d4a..e74c0d9 100644 --- a/pyha_analyzer/forestIR_synthesis/Constants.py +++ b/pyha_analyzer/forestIR_synthesis/Constants.py @@ -1,3 +1,8 @@ +""" +This file is taken from the repo found here: +https://github.com/microsoft/Forest_IR_synthesis/tree/main +Date of retrieval: February 7, 2024 +""" __author__ = "Shoken KANEKO" import numpy as np diff --git a/pyha_analyzer/forestIR_synthesis/ForestReverb.py b/pyha_analyzer/forestIR_synthesis/ForestReverb.py index b9ea235..37379ad 100644 --- a/pyha_analyzer/forestIR_synthesis/ForestReverb.py +++ b/pyha_analyzer/forestIR_synthesis/ForestReverb.py @@ -1,3 +1,8 @@ +""" +This file is taken from the repo found here: +https://github.com/microsoft/Forest_IR_synthesis/tree/main +Date of retrieval: February 7, 2024 +""" __author__ = "Shoken KANEKO" import numpy as np diff --git a/pyha_analyzer/forestIR_synthesis/SignalProcessing.py b/pyha_analyzer/forestIR_synthesis/SignalProcessing.py index a726080..f910e97 100644 --- a/pyha_analyzer/forestIR_synthesis/SignalProcessing.py +++ b/pyha_analyzer/forestIR_synthesis/SignalProcessing.py @@ -4,6 +4,11 @@ import os from scipy.signal import stft as spstft from scipy.signal import istft as spistft +""" +This file is taken from the repo found here: +https://github.com/microsoft/Forest_IR_synthesis/tree/main +Date of retrieval: February 7, 2024 +""" from scipy.fft import irfft from forestIR_synthesis.Constants import soundVel diff --git a/pyha_analyzer/forestIR_synthesis/Wav.py b/pyha_analyzer/forestIR_synthesis/Wav.py index 1c3a481..70d4f56 100644 --- a/pyha_analyzer/forestIR_synthesis/Wav.py +++ b/pyha_analyzer/forestIR_synthesis/Wav.py @@ -1,3 +1,8 @@ +""" +This file is taken from the repo found here: +https://github.com/microsoft/Forest_IR_synthesis/tree/main +Date of retrieval: February 7, 2024 +""" __author__ = "Shoken KANEKO" import numpy as np From 3df2f2b176fb6ffc9ad6e69a7d11b61805e29c3f Mon Sep 17 00:00:00 2001 From: tqzhang04 Date: Tue, 20 Feb 2024 21:08:29 +0000 Subject: [PATCH 05/10] fix: fixed imports in forestIR files changed the import statements in the forestIR files to specify pyha_analyzer --- pyha_analyzer/forestIR_synthesis/Constants.py | 1 + pyha_analyzer/forestIR_synthesis/ForestReverb.py | 15 ++++++++------- .../forestIR_synthesis/SignalProcessing.py | 13 +++++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pyha_analyzer/forestIR_synthesis/Constants.py b/pyha_analyzer/forestIR_synthesis/Constants.py index e74c0d9..b1b21a9 100644 --- a/pyha_analyzer/forestIR_synthesis/Constants.py +++ b/pyha_analyzer/forestIR_synthesis/Constants.py @@ -1,6 +1,7 @@ """ This file is taken from the repo found here: https://github.com/microsoft/Forest_IR_synthesis/tree/main +and slightly modified to better fit our purposes. Date of retrieval: February 7, 2024 """ __author__ = "Shoken KANEKO" diff --git a/pyha_analyzer/forestIR_synthesis/ForestReverb.py b/pyha_analyzer/forestIR_synthesis/ForestReverb.py index 37379ad..ca1f772 100644 --- a/pyha_analyzer/forestIR_synthesis/ForestReverb.py +++ b/pyha_analyzer/forestIR_synthesis/ForestReverb.py @@ -1,6 +1,7 @@ """ This file is taken from the repo found here: https://github.com/microsoft/Forest_IR_synthesis/tree/main +and slightly modified to better fit our purposes. Date of retrieval: February 7, 2024 """ __author__ = "Shoken KANEKO" @@ -15,13 +16,13 @@ join = os.path.join norm = np.linalg.norm -from forestIR_synthesis.Constants import pi, soundVel -import forestIR_synthesis.Wav -import forestIR_synthesis.SignalProcessing as sigp -from forestIR_synthesis.SignalProcessing import addSignal, getImpulse -from forestIR_synthesis.SignalProcessing import getFreqsForGivenFilterLength -from forestIR_synthesis.SignalProcessing import getIRFromSpectrum_irfft -from forestIR_synthesis.SignalProcessing import dists, getDelayedSig, floatX +from pyha_analyzer.forestIR_synthesis.Constants import pi, soundVel +import pyha_analyzer.forestIR_synthesis.Wav +import pyha_analyzer.forestIR_synthesis.SignalProcessing as sigp +from pyha_analyzer.forestIR_synthesis.SignalProcessing import addSignal, getImpulse +from pyha_analyzer.forestIR_synthesis.SignalProcessing import getFreqsForGivenFilterLength +from pyha_analyzer.forestIR_synthesis.SignalProcessing import getIRFromSpectrum_irfft +from pyha_analyzer.forestIR_synthesis.SignalProcessing import dists, getDelayedSig, floatX def computeAirAbsorptionCoef( freqs, diff --git a/pyha_analyzer/forestIR_synthesis/SignalProcessing.py b/pyha_analyzer/forestIR_synthesis/SignalProcessing.py index f910e97..9596465 100644 --- a/pyha_analyzer/forestIR_synthesis/SignalProcessing.py +++ b/pyha_analyzer/forestIR_synthesis/SignalProcessing.py @@ -1,16 +1,17 @@ +""" +This file is taken from the repo found here: +https://github.com/microsoft/Forest_IR_synthesis/tree/main +and slightly modified to better fit our purposes. +Date of retrieval: February 7, 2024 +""" __author__ = "Shoken KANEKO" import numpy as np import os from scipy.signal import stft as spstft from scipy.signal import istft as spistft -""" -This file is taken from the repo found here: -https://github.com/microsoft/Forest_IR_synthesis/tree/main -Date of retrieval: February 7, 2024 -""" from scipy.fft import irfft -from forestIR_synthesis.Constants import soundVel +from pyha_analyzer.forestIR_synthesis.Constants import soundVel na = np.newaxis join = os.path.join From 87a744cb013abad91767bf3a30c4ef5ce518c75c Mon Sep 17 00:00:00 2001 From: tqzhang04 Date: Tue, 20 Feb 2024 21:39:27 +0000 Subject: [PATCH 06/10] feat: integrated AddReverb into augmentations Added the reverb module to the data aug pipeline BREAKING CHANGE: dataset, test now import all functions from augmentations --- pyha_analyzer/aug_viewer.py | 6 ++++-- pyha_analyzer/augmentations.py | 14 +++++++------- pyha_analyzer/dataset.py | 6 +++--- pyha_analyzer/forestIR_synthesis/Wav.py | 1 + pyha_analyzer/test.py | 6 +++--- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/pyha_analyzer/aug_viewer.py b/pyha_analyzer/aug_viewer.py index 6f429ec..20e3065 100644 --- a/pyha_analyzer/aug_viewer.py +++ b/pyha_analyzer/aug_viewer.py @@ -12,7 +12,8 @@ from pyha_analyzer import config from pyha_analyzer.utils import get_annotation from pyha_analyzer.augmentations import (BackgroundNoise, LowpassFilter, Mixup, - HighpassFilter, RandomEQ, SyntheticNoise) + HighpassFilter, RandomEQ, SyntheticNoise, + AddReverb) from pyha_analyzer.dataset import PyhaDFDataset, get_datasets SYNTH_COLORS = ["white","pink","brown","violet","blue"] @@ -69,7 +70,8 @@ def get_augs(dataset: PyhaDFDataset, cfg) -> Tuple[List[Callable],List[str]]: LowpassFilter(cfg) : "Lowpass Filter", HighpassFilter(cfg) : "Highpass Filter", RandomEQ(cfg) : "Random EQ", - BackgroundNoise(cfg) : "Background Noise"}) + BackgroundNoise(cfg) : "Background Noise", + AddReverb(cfg): "Add Reverb"}) #Mixup mixup = Mixup(df = dataset.samples, diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 29ea1de..78d14d7 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -12,7 +12,7 @@ import torch import torchaudio import scipy -from forestIR_synthesis import (Constants, ForestReverb, SignalProcessing, Wav) +from pyha_analyzer.forestIR_synthesis import ForestReverb import random from pyha_analyzer import config, utils @@ -452,14 +452,14 @@ class AddReverb(torch.nn.Module): fs (int): sample rate of the files in Hz """ - def __init__(self, fs=44_100): + def __init__(self, cfg: config.Config): """init Args: fs (int, optional): sample rate of audio in Hz. Defaults to 44100. """ self.num_trees = 100_000 - self.fs = fs + self.sample_rate = cfg.sample_rate self.min_distance = 0 self.max_distance = 1000 @@ -509,7 +509,7 @@ def add_reverb(self, bird_call): # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: bird_call = bird_call.T[0] - fs = self.fs + fs = self.sample_rate # set source position pos_x = 500 @@ -570,6 +570,6 @@ def get_mic_pos(self, src_pos): # sanity tests for reverb -print("hello") -test = AddReverb(55000) -print("Goodbye") +# print("hello") +# test = AddReverb(55000) +# print("Goodbye") diff --git a/pyha_analyzer/dataset.py b/pyha_analyzer/dataset.py index b7096c3..5bb5a7c 100644 --- a/pyha_analyzer/dataset.py +++ b/pyha_analyzer/dataset.py @@ -24,8 +24,7 @@ from pyha_analyzer import config from pyha_analyzer import utils -from pyha_analyzer.augmentations import (BackgroundNoise, LowpassFilter, Mixup, RandomEQ, - HighpassFilter, SyntheticNoise) +from pyha_analyzer.augmentations import * from pyha_analyzer.chunking_methods import sliding_chunks tqdm.pandas() @@ -94,7 +93,8 @@ def __init__(self, RandomEQ : cfg.rand_eq_p, LowpassFilter : cfg.lowpass_p, HighpassFilter : cfg.highpass_p, - BackgroundNoise : cfg.bg_noise_p + BackgroundNoise : cfg.bg_noise_p, + AddReverb : cfg.add_reverb_p }.items() # List around aug(cfg) is necessary # because RandomApply expects an iterable diff --git a/pyha_analyzer/forestIR_synthesis/Wav.py b/pyha_analyzer/forestIR_synthesis/Wav.py index 70d4f56..281bb74 100644 --- a/pyha_analyzer/forestIR_synthesis/Wav.py +++ b/pyha_analyzer/forestIR_synthesis/Wav.py @@ -1,6 +1,7 @@ """ This file is taken from the repo found here: https://github.com/microsoft/Forest_IR_synthesis/tree/main +and slightly modified to better fit our purposes. Date of retrieval: February 7, 2024 """ __author__ = "Shoken KANEKO" diff --git a/pyha_analyzer/test.py b/pyha_analyzer/test.py index efd0291..2cbda5b 100644 --- a/pyha_analyzer/test.py +++ b/pyha_analyzer/test.py @@ -14,8 +14,7 @@ from pyha_analyzer import config from pyha_analyzer import utils -from pyha_analyzer.augmentations import (BackgroundNoise, LowpassFilter, Mixup, RandomEQ, - HighpassFilter, SyntheticNoise) +from pyha_analyzer.augmentations import * from pyha_analyzer.chunking_methods.sliding_chunks import convolving_chunk from pyha_analyzer.models.early_stopper import EarlyStopper from pyha_analyzer.models.timm_model import TimmModel @@ -55,7 +54,8 @@ def test_augs(self): augs+= [RandomEQ(cfg), BackgroundNoise(cfg), LowpassFilter(cfg), - HighpassFilter(cfg)] + HighpassFilter(cfg), + AddReverb(cfg)] augmented_audio = [aug(audio) for aug in augs] for aug_audio in augmented_audio: assert aug_audio.shape == audio.shape, "Augmented audio should not change shape" From 82b05eb57fdc272ef50ce1f7f4294c2e9dfb2ee1 Mon Sep 17 00:00:00 2001 From: tqzhang04 Date: Thu, 22 Feb 2024 00:05:01 +0000 Subject: [PATCH 07/10] fix: solved issues with training --- pyha_analyzer/aug_viewer.ipynb | 15 ++++- pyha_analyzer/augmentations.py | 67 +++++++++++------------ pyha_analyzer/dataset.py | 2 + ~./132_peru_xc_BC_2020/.metadata.csv.swp | Bin 0 -> 131072 bytes 4 files changed, 45 insertions(+), 39 deletions(-) create mode 100755 ~./132_peru_xc_BC_2020/.metadata.csv.swp diff --git a/pyha_analyzer/aug_viewer.ipynb b/pyha_analyzer/aug_viewer.ipynb index 3c6423f..424b88a 100644 --- a/pyha_analyzer/aug_viewer.ipynb +++ b/pyha_analyzer/aug_viewer.ipynb @@ -2,9 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ubuntu/miniconda3/envs/asid/lib/python3.8/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "# Import statements\n", "import os\n", @@ -81,7 +90,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.16" + "version": "3.8.18" } }, "nbformat": 4, diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 78d14d7..3d45ebf 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -454,10 +454,11 @@ class AddReverb(torch.nn.Module): def __init__(self, cfg: config.Config): """init - - Args: - fs (int, optional): sample rate of audio in Hz. Defaults to 44100. + + Relevant options in config file: + sample_rate: sample rate of audio files """ + super().__init__() self.num_trees = 100_000 self.sample_rate = cfg.sample_rate self.min_distance = 0 @@ -467,44 +468,42 @@ def forward(self, clip): """Applies reverb to all files in the clip passed Args: - clip (list): list of the ___ of all the audio clips + clip (torch.Tensor): time series of audio clip Returns: - list: list of each audio file, with reverb applied, as a list (time - series) + torch.Tensor: audio clip with reverb added """ assert isinstance(clip, torch.Tensor) - assert all([type(value) == np.ndarray for value in clip]) + assert all([type(value) == torch.Tensor for value in clip]) # initialize list of outputs - calls_with_reverb = [] - for file in clip: # loop through files in clip - call_reverb = self.add_reverb(file) - calls_with_reverb.append(call_reverb) + # calls_with_reverb = [] + # print('clip:',clip) + call_reverb = self.add_reverb(clip) # print(calls_with_reverb) - # temp fix to below problem: pads calls with 0s - max_len = max([len(call) for call in calls_with_reverb]) - print('max:', max_len) - calls_with_reverb = [np.pad(call, (0, max_len - call.size)) - for call - in calls_with_reverb - ] + # # temp fix to below problem: pads calls with 0s + # max_len = max([len(call) for call in calls_with_reverb]) + # # print('max:', max_len) + # calls_with_reverb = [np.pad(call, (0, max_len - call.size)) + # for call + # in calls_with_reverb + # ] - calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) - return calls_with_reverb + # calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) + return call_reverb def add_reverb(self, bird_call): """Applies randomized reverb to the given call Args: - bird_call (np.ndarray): time series of the call to process + bird_call (torch.Tensor): time series of the call to process Returns: - np.ndarray: Tensor representing the time series of the call after + torch.Tensor: Tensor representing the time series of the call after adding reverb """ - assert isinstance(bird_call, np.ndarray) + assert isinstance(bird_call, torch.Tensor) # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: @@ -531,11 +530,15 @@ def add_reverb(self, bird_call): # apply reverb impulse = np.reshape(result, result.shape[0]) + + # print('impulse:',impulse.ndim) + # print('call:',bird_call.ndim) call_with_reverb = scipy.signal.convolve( impulse, bird_call, method='fft' ) - print(type(call_with_reverb)) - return call_with_reverb + # print(type(call_with_reverb)) + # print('shape:',call_with_reverb.shape) + return torch.Tensor(call_with_reverb) def get_mic_pos(self, src_pos): """Generates a np array representing the position of a mic that is a @@ -556,20 +559,12 @@ def get_mic_pos(self, src_pos): # assert isinstance(max, int) # assert max >= 0 - # FIXME for some reason its not random after the first time (using np.random) - # temp fix import random and use that instead of np x_offset = random.randint(self.min_distance, self.max_distance) y_offset = random.randint(self.min_distance, self.max_distance) mic_x = src_pos[0] + x_offset mic_y = src_pos[1] + y_offset - print('mic:', x_offset, y_offset) - print('minmax', self.min_distance, self.max_distance) + # print('mic:', x_offset, y_offset) + # print('minmax', self.min_distance, self.max_distance) mic_pos = np.array([mic_x, mic_y, src_pos[2]]) - print('mic at:', mic_pos) + # print('mic at:', mic_pos) return mic_pos - - -# sanity tests for reverb -# print("hello") -# test = AddReverb(55000) -# print("Goodbye") diff --git a/pyha_analyzer/dataset.py b/pyha_analyzer/dataset.py index 5bb5a7c..35d580d 100644 --- a/pyha_analyzer/dataset.py +++ b/pyha_analyzer/dataset.py @@ -464,5 +464,7 @@ def main() -> None: # _, _, infer_dataloader = get_datasets() # for _, (_, _) in enumerate(infer_dataloader): # break + + if __name__ == '__main__': main() diff --git a/~./132_peru_xc_BC_2020/.metadata.csv.swp b/~./132_peru_xc_BC_2020/.metadata.csv.swp new file mode 100755 index 0000000000000000000000000000000000000000..fb2c359ff103a3293c0138d26e2e6e663e6479f7 GIT binary patch literal 131072 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 z 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≤0-qL40X=VY&OiIy^+~^VtYqNbdZ#fEUF4 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 literal 0 HcmV?d00001 From 867b553156cdbbecae40786740f317d54f0fd8a3 Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Thu, 22 Feb 2024 00:24:55 -0800 Subject: [PATCH 08/10] refactor: sped up sim reverb changed AddReverb to simulate a set number of impulses, then randomly pick from those to convolve with removed prints --- pyha_analyzer/augmentations.py | 75 +++++++++---------- .../forestIR_synthesis/ForestReverb.py | 6 +- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 3d45ebf..63a7c5e 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -454,7 +454,7 @@ class AddReverb(torch.nn.Module): def __init__(self, cfg: config.Config): """init - + Relevant options in config file: sample_rate: sample rate of audio files """ @@ -463,6 +463,10 @@ def __init__(self, cfg: config.Config): self.sample_rate = cfg.sample_rate self.min_distance = 0 self.max_distance = 1000 + self.num_IRs = 10 + + self.impulses = self.get_impulses(self.num_IRs) + def forward(self, clip): """Applies reverb to all files in the clip passed @@ -474,23 +478,9 @@ def forward(self, clip): torch.Tensor: audio clip with reverb added """ assert isinstance(clip, torch.Tensor) - assert all([type(value) == torch.Tensor for value in clip]) - - # initialize list of outputs - # calls_with_reverb = [] - # print('clip:',clip) + assert all([type(value) == torch.Tensor for value in clip]) + call_reverb = self.add_reverb(clip) - # print(calls_with_reverb) - - # # temp fix to below problem: pads calls with 0s - # max_len = max([len(call) for call in calls_with_reverb]) - # # print('max:', max_len) - # calls_with_reverb = [np.pad(call, (0, max_len - call.size)) - # for call - # in calls_with_reverb - # ] - - # calls_with_reverb = torch.tensor(np.array(calls_with_reverb)) return call_reverb def add_reverb(self, bird_call): @@ -503,34 +493,17 @@ def add_reverb(self, bird_call): torch.Tensor: Tensor representing the time series of the call after adding reverb """ - assert isinstance(bird_call, torch.Tensor) + assert isinstance(bird_call, torch.Tensor) # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: bird_call = bird_call.T[0] fs = self.sample_rate - # set source position - pos_x = 500 - pos_y = 500 - pos_z = 1.5 - posSrc = np.array([pos_x, pos_y, pos_z]) + # choose random impulse to convolve with + impulse_ind = np.random.choice(len(self.impulses)) + impulse = self.impulses[impulse_ind] - # randomize mic position - micPoss = self.get_mic_pos(posSrc) - - result = ForestReverb.simulateForestIR( - nTrees=self.num_trees, - posSrc=posSrc, - micPoss=micPoss.reshape( - [1, 3]), - fs=fs, - sigLen_in_samples=fs*5 # not sure why its x5 here - ) - - # apply reverb - impulse = np.reshape(result, result.shape[0]) - # print('impulse:',impulse.ndim) # print('call:',bird_call.ndim) call_with_reverb = scipy.signal.convolve( @@ -540,6 +513,32 @@ def add_reverb(self, bird_call): # print('shape:',call_with_reverb.shape) return torch.Tensor(call_with_reverb) + def get_impulses(self, num_impulses): + # set source position + pos_x = 500 + pos_y = 500 + pos_z = 1.5 + posSrc = np.array([pos_x, pos_y, pos_z]) + + impulses = [] + for _ in range(num_impulses): + # randomize mic position + micPoss = self.get_mic_pos(posSrc) + + result = ForestReverb.simulateForestIR( + nTrees=self.num_trees, + posSrc=posSrc, + micPoss=micPoss.reshape( + [1, 3]), + fs=self.sample_rate, + sigLen_in_samples=self.sample_rate*5 # not sure why its x5 here + ) + + # reshape to get 1d impulse + impulse = np.reshape(result, result.shape[0]) + impulses.append(impulse) + return impulses + def get_mic_pos(self, src_pos): """Generates a np array representing the position of a mic that is a random distance (within given limits) away from the source diff --git a/pyha_analyzer/forestIR_synthesis/ForestReverb.py b/pyha_analyzer/forestIR_synthesis/ForestReverb.py index ca1f772..d722351 100644 --- a/pyha_analyzer/forestIR_synthesis/ForestReverb.py +++ b/pyha_analyzer/forestIR_synthesis/ForestReverb.py @@ -263,7 +263,7 @@ def simulateForestIR( assert maxReflOrder<=1,"Higher order scattering is not supported." assert applyAirAbsorption==1 assert applyCylindricalScatteringFilter==1 - print("simulating forest IR...") + # print("simulating forest IR...") if forestRange_x is None: forestRange_x = [0.0,1000.0] if forestRange_y is None: @@ -328,7 +328,7 @@ def simulateForestIR( micPoss_ = micPoss for m in range(nMic): - print("computing ir from src to mic",m,"...") + # print("computing ir from src to mic",m,"...") tic = time.time() if nTrees>0: distDiffs_src_to_mic_reflect = distDiffs_src_to_mics_reflect[m,:] # [nTrees] @@ -438,7 +438,7 @@ def simulateForestIR( raise NotImplementedError toc = time.time() - print(" time:",toc-tic) + # print(" time:",toc-tic) return signal # [T x mMic] From dd0dd9bfe7da3410b1f97d271e012bb8323df59b Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Fri, 23 Feb 2024 23:52:09 -0800 Subject: [PATCH 09/10] fix: made forestIR work with params in config --- pyha_analyzer/augmentations.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index 63a7c5e..ab2a150 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -459,13 +459,13 @@ def __init__(self, cfg: config.Config): sample_rate: sample rate of audio files """ super().__init__() - self.num_trees = 100_000 + self.num_trees = cfg.num_trees self.sample_rate = cfg.sample_rate - self.min_distance = 0 - self.max_distance = 1000 - self.num_IRs = 10 + self.min_distance = cfg.distance_range[0] + self.max_distance = cfg.distance_range[1] + self.num_impulses = cfg.num_impulses - self.impulses = self.get_impulses(self.num_IRs) + self.impulses = self.get_impulses(self.num_impulses) def forward(self, clip): @@ -515,7 +515,9 @@ def add_reverb(self, bird_call): def get_impulses(self, num_impulses): # set source position - pos_x = 500 + # since it's simulating a 1000 by 1000 square, (500, 500) is about center + # z coordinate chosen arbitrarily based on their input + pos_x = 500 pos_y = 500 pos_z = 1.5 posSrc = np.array([pos_x, pos_y, pos_z]) From e0d5e3e53cb37556563bc600c3764e7c66307b69 Mon Sep 17 00:00:00 2001 From: TQ Zhang Date: Wed, 28 Feb 2024 10:04:03 -0800 Subject: [PATCH 10/10] fix: remove asserts commented out assert statements in Reverb to (hopefully) speed up code --- pyha_analyzer/augmentations.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyha_analyzer/augmentations.py b/pyha_analyzer/augmentations.py index ab2a150..e05ce6a 100644 --- a/pyha_analyzer/augmentations.py +++ b/pyha_analyzer/augmentations.py @@ -469,7 +469,7 @@ def __init__(self, cfg: config.Config): def forward(self, clip): - """Applies reverb to all files in the clip passed + """Applies reverb to the clip passed Args: clip (torch.Tensor): time series of audio clip @@ -477,8 +477,8 @@ def forward(self, clip): Returns: torch.Tensor: audio clip with reverb added """ - assert isinstance(clip, torch.Tensor) - assert all([type(value) == torch.Tensor for value in clip]) + # assert isinstance(clip, torch.Tensor) + # assert all([type(value) == torch.Tensor for value in clip]) call_reverb = self.add_reverb(clip) return call_reverb @@ -493,7 +493,7 @@ def add_reverb(self, bird_call): torch.Tensor: Tensor representing the time series of the call after adding reverb """ - assert isinstance(bird_call, torch.Tensor) + # assert isinstance(bird_call, torch.Tensor) # read in bird call as time series, convert to mono if needed if bird_call.ndim >= 2: @@ -553,8 +553,8 @@ def get_mic_pos(self, src_pos): Returns: np.ndArray: position of the mic """ - assert isinstance(src_pos, np.ndarray) - assert len(src_pos) == 3 + # assert isinstance(src_pos, np.ndarray) + # assert len(src_pos) == 3 # assert isinstance(min, int) # assert min >= 0 # assert isinstance(max, int)