|
|
| import math |
| import torch |
| import torchaudio |
| import numpy as np |
| import scipy.signal |
| class EMAWarmup: |
| """Implements an EMA warmup using an inverse decay schedule. |
| If inv_gamma=1 and power=1, implements a simple average. inv_gamma=1, power=2/3 are |
| good values for models you plan to train for a million or more steps (reaches decay |
| factor 0.999 at 31.6K steps, 0.9999 at 1M steps), inv_gamma=1, power=3/4 for models |
| you plan to train for less (reaches decay factor 0.999 at 10K steps, 0.9999 at |
| 215.4k steps). |
| Args: |
| inv_gamma (float): Inverse multiplicative factor of EMA warmup. Default: 1. |
| power (float): Exponential factor of EMA warmup. Default: 1. |
| min_value (float): The minimum EMA decay rate. Default: 0. |
| max_value (float): The maximum EMA decay rate. Default: 1. |
| start_at (int): The epoch to start averaging at. Default: 0. |
| last_epoch (int): The index of last epoch. Default: 0. |
| """ |
|
|
| def __init__(self, inv_gamma=1., power=1., min_value=0., max_value=1., start_at=0, |
| last_epoch=0): |
| self.inv_gamma = inv_gamma |
| self.power = power |
| self.min_value = min_value |
| self.max_value = max_value |
| self.start_at = start_at |
| self.last_epoch = last_epoch |
|
|
| def state_dict(self): |
| """Returns the state of the class as a :class:`dict`.""" |
| return dict(self.__dict__.items()) |
|
|
| def load_state_dict(self, state_dict): |
| """Loads the class's state. |
| Args: |
| state_dict (dict): scaler state. Should be an object returned |
| from a call to :meth:`state_dict`. |
| """ |
| self.__dict__.update(state_dict) |
|
|
| def get_value(self): |
| """Gets the current EMA decay rate.""" |
| epoch = max(0, self.last_epoch - self.start_at) |
| value = 1 - (1 + epoch / self.inv_gamma) ** -self.power |
| return 0. if epoch < 0 else min(self.max_value, max(self.min_value, value)) |
|
|
| def step(self): |
| """Updates the step count.""" |
| self.last_epoch += 1 |
|
|
|
|
| def resample_batch(audio, fs, fs_target, length_target=None): |
|
|
| device=audio.device |
| dtype=audio.dtype |
| B=audio.shape[0] |
| |
| |
| |
| if fs_target==22050: |
| if (fs==44100).all(): |
| audio=torchaudio.functional.resample(audio, 2,1) |
| return audio[:, 0:length_target] |
| elif (fs==48000).all(): |
| |
| audio=torchaudio.functional.resample(audio, 160*2,147) |
| return audio[:, 0:length_target] |
| else: |
| |
| proc_batch=torch.zeros((B,length_target), device=device) |
| for i, (a, f_s) in enumerate(zip(audio, fs)): |
| if f_s==44100: |
| |
| a=torchaudio.functional.resample(a, 2,1) |
| elif f_s==48000: |
| a=torchaudio.functional.resample(a, 160*2,147) |
| elif f_s==22050: |
| pass |
| else: |
| print("WARNING, strange fs", f_s) |
| |
| proc_batch[i]=a[0:length_target] |
| return proc_batch |
| elif fs_target==44100: |
| if (fs==44100).all(): |
| return audio[:, 0:length_target] |
| elif (fs==48000).all(): |
| |
| audio=torchaudio.functional.resample(audio, 160,147) |
| return audio[:, 0:length_target] |
| else: |
| |
| |
| |
| proc_batch=torch.zeros((B,length_target), device=device) |
| |
| |
| |
| for i, (a, f_s) in enumerate(zip(audio, fs)): |
| |
| if f_s==44100: |
| |
| pass |
| elif f_s==22050: |
| a=torchaudio.functional.resample(a, 1,2) |
| elif f_s==48000: |
| a=torchaudio.functional.resample(a, 160,147) |
| elif f_s==96000: |
| a=torchaudio.functional.resample(a, 320, 147) |
| else: |
| print("WARNING, strange fs", f_s) |
| |
|
|
| proc_batch[i]=a[...,0:length_target] |
| return proc_batch |
| else: |
| if (fs==44100).all(): |
| audio=torchaudio.functional.resample(audio, 44100, fs_target) |
| return audio[...,0:length_target] |
| elif (fs==48000).all(): |
| print("resampling 48000 to 16000", length_target, audio.shape) |
| |
| audio=torchaudio.functional.resample(audio, 48000,fs_target) |
| print(audio.shape) |
| return audio[..., 0:length_target] |
| else: |
| |
| proc_batch=torch.zeros((B,length_target), device=device) |
| for i, (a, f_s) in enumerate(zip(audio, fs)): |
| if f_s==44100: |
| |
| a=torchaudio.functional.resample(a, 44100,fs_target) |
| elif f_s==48000: |
| a=torchaudio.functional.resample(a, 48000,fs_target) |
| else: |
| print("WARNING, strange fs", f_s) |
| |
| proc_batch[i]=a[...,0:length_target] |
| return proc_batch |
|
|
| def load_state_dict( state_dict, network=None, ema=None, optimizer=None, log=True): |
| ''' |
| utility for loading state dicts for different models. This function sequentially tries different strategies |
| args: |
| state_dict: the state dict to load |
| returns: |
| True if the state dict was loaded, False otherwise |
| Assuming the operations are don in_place, this function will not create a copy of the network and optimizer (I hope) |
| ''' |
| |
| if log: print("Loading state dict") |
| if log: |
| print(state_dict.keys()) |
| |
| try: |
| if log: print("Attempt 1: trying with strict=True") |
| if network is not None: |
| network.load_state_dict(state_dict['network']) |
| if optimizer is not None: |
| optimizer.load_state_dict(state_dict['optimizer']) |
| if ema is not None: |
| ema.load_state_dict(state_dict['ema']) |
| return True |
| except Exception as e: |
| if log: |
| print("Could not load state dict") |
| print(e) |
| try: |
| print("assuming the network was saved from a DDP model, and I forgot to add .module to the keys") |
| |
| if network is not None: |
| network_state_dict = network.state_dict() |
| for key in list(state_dict['network'].keys()): |
| print("checking", key) |
| if key.startswith('module.'): |
| new_key = key.replace('module.', '') |
| state_dict['network'][new_key] = state_dict['network'].pop(key) |
|
|
| network.load_state_dict(state_dict['network']) |
| |
| if optimizer is not None: |
| optimizer.load_state_dict(state_dict['optimizer']) |
| if ema is not None: |
| ema.load_state_dict(state_dict['ema']) |
|
|
| print("loaded state dict with .module removed from the keys") |
|
|
| return True |
|
|
| except Exception as e: |
| if log: |
| print("Could not load state dict") |
| print(e) |
|
|
| try: |
| if log: print("Attempt 2: trying with strict=False") |
| if network is not None: |
| network.load_state_dict(state_dict['network'], strict=False) |
| |
| |
| if ema is not None: |
| ema.load_state_dict(state_dict['ema'], strict=False) |
| return True |
| except Exception as e: |
| if log: |
| print("Could not load state dict") |
| print(e) |
| print("training from scratch") |
| try: |
| if log: print("Attempt 3: trying with strict=False,but making sure that the shapes are fine") |
| if ema is not None: |
| ema_state_dict = ema.state_dict() |
| if network is not None: |
| network_state_dict = network.state_dict() |
| i=0 |
| if network is not None: |
| for name, param in state_dict['network'].items(): |
| if log: print("checking",name) |
| if name in network_state_dict.keys(): |
| if network_state_dict[name].shape==param.shape: |
| network_state_dict[name]=param |
| if log: |
| print("assigning",name) |
| i+=1 |
| network.load_state_dict(network_state_dict) |
| if ema is not None: |
| for name, param in state_dict['ema'].items(): |
| if log: print("checking",name) |
| if name in ema_state_dict.keys(): |
| if ema_state_dict[name].shape==param.shape: |
| ema_state_dict[name]=param |
| if log: |
| print("assigning",name) |
| i+=1 |
| |
| ema.load_state_dict(ema_state_dict) |
| |
| if i==0: |
| if log: print("WARNING, no parameters were loaded") |
| raise Exception("No parameters were loaded") |
| elif i>0: |
| if log: print("loaded", i, "parameters") |
| return True |
|
|
| except Exception as e: |
| print(e) |
| print("the second strict=False failed") |
|
|
|
|
| try: |
| if log: print("Attempt 4: Assuming the naming is different, with the network and ema called 'state_dict'") |
| if network is not None: |
| network.load_state_dict(state_dict['state_dict']) |
| if ema is not None: |
| ema.load_state_dict(state_dict['state_dict']) |
| except Exception as e: |
| if log: |
| print("Could not load state dict") |
| print(e) |
| print("training from scratch") |
| print("It failed 3 times!! but not giving up") |
| |
|
|
| try: |
| if log: print("Attempt 5: trying to load with different names, now model='model' and ema='ema_weights'") |
| if ema is not None: |
| dic_ema = {} |
| for (key, tensor) in zip(state_dict['model'].keys(), state_dict['ema_weights']): |
| dic_ema[key] = tensor |
| ema.load_state_dict(dic_ema) |
| return True |
| except Exception as e: |
| if log: |
| print(e) |
|
|
| try: |
| if log: print("Attempt 6: If there is something wrong with the name of the ema parameters, we can try to load them using the names of the parameters in the model") |
| if ema is not None: |
| dic_ema = {} |
| i=0 |
| for (key, tensor) in zip(state_dict['model'].keys(), state_dict['model'].values()): |
| if tensor.requires_grad: |
| dic_ema[key]=state_dict['ema_weights'][i] |
| i=i+1 |
| else: |
| dic_ema[key]=tensor |
| ema.load_state_dict(dic_ema) |
| return True |
| except Exception as e: |
| if log: |
| print(e) |
|
|
|
|
| try: |
| |
| print("Attempt 7: Trying to load the parameters one by one. This is for the dance diffusion model, looking for parameters starting with 'diffusion.' or 'diffusion_ema.'") |
| if ema is not None: |
| ema_state_dict = ema.state_dict() |
| if network is not None: |
| network_state_dict = ema.state_dict() |
| i=0 |
| if network is not None: |
| for name, param in state_dict['state_dict'].items(): |
| print("checking",name) |
| if name.startswith("diffusion."): |
| i+=1 |
| name=name.replace("diffusion.","") |
| if network_state_dict[name].shape==param.shape: |
| |
| network_state_dict[name]=param |
| |
| |
| network.load_state_dict(network_state_dict, strict=False) |
| |
| if ema is not None: |
| for name, param in state_dict['state_dict'].items(): |
| if name.startswith("diffusion_ema."): |
| i+=1 |
| name=name.replace("diffusion_ema.","") |
| if ema_state_dict[name].shape==param.shape: |
| if log: |
| print(param.shape, ema.state_dict()[name].shape) |
| ema_state_dict[name]=param |
| |
| ema.load_state_dict(ema_state_dict, strict=False) |
| |
| if i==0: |
| print("WARNING, no parameters were loaded") |
| raise Exception("No parameters were loaded") |
| elif i>0: |
| print("loaded", i, "parameters") |
| return True |
| except Exception as e: |
| if log: |
| print(e) |
| |
| |
| if network is not None: |
| network.load_state_dict(state_dict, strict=True) |
| if ema is not None: |
| ema.load_state_dict(state_dict, strict=True) |
| return True |
|
|
| |
| |
| |
|
|
| return False |
|
|
| |
| def unnormalize(x,stds, args): |
| |
| new_std=args.exp.normalization.target_std |
| if new_std=="sigma_data": |
| new_std=args.diff_params.sigma_data |
| x=stds*x/(new_std+1e-8) |
| return x |
| def normalize( xS, xT, xN, args, return_std=False): |
| |
| if args.exp.normalization.mode=="None": |
| pass |
| elif args.exp.normalization.mode=="residual_noise": |
| |
|
|
| std=xN.std(dim=-1, keepdim=True).mean(dim=1, keepdim=True) |
| new_std=args.exp.normalization.target_std |
|
|
| if new_std=="sigma_data": |
| new_std=args.diff_params.sigma_data |
|
|
| |
|
|
| xN=new_std*xN/(std+1e-8) |
| |
| xS=new_std*xS/(std+1e-8) |
| xT=new_std*xT/(std+1e-8) |
| elif args.exp.normalization.mode=="residual_noise_batch": |
| |
| |
| std=xN.std(dim=(0,1,2),unbiased=True, keepdim=False) |
| |
| new_std=args.exp.normalization.target_std |
|
|
| if new_std=="sigma_data": |
| new_std=args.diff_params.sigma_data |
|
|
| |
|
|
| xN=new_std*xN/(std+1e-8) |
| |
| xS=new_std*xS/(std+1e-8) |
| xT=new_std*xT/(std+1e-8) |
|
|
| elif args.exp.normalization.mode=="all": |
| std=(xN+xS+xT).std(dim=-1, keepdim=True).mean(dim=1, keepdim=True) |
| new_std=args.exp.normalization.target_std |
| if new_std=="sigma_data": |
| new_std=args.diff_params.sigma_data |
| xN=new_std*xN/(std+1e-8) |
| xS=new_std*xS/(std+1e-8) |
| xT=new_std*xT/(std+1e-8) |
| |
| else: |
| print("normalization mode not recognized") |
| pass |
|
|
| try: |
| if return_std: |
| return xS, xT, xN, std |
| except Exception as e: |
| print(e) |
| print("warning!, std cannot be returned") |
| pass |
|
|
| return xS, xT, xN |
|
|
|
|
| |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| |
|
|
| |
| |
|
|
| |
|
|
| |
|
|
|
|
| def align_batch(y, x, sample_rate): |
|
|
| x = x.double() |
| y = y.double() |
| N = x.size(-1) |
| M = y.size(-1) |
| y_device=y.device |
| x_device=x.device |
| y_dtype=y.dtype |
| x_dtype=x.dtype |
|
|
| X = torch.fft.rfft(x, n=N + M - 1) |
| X_flipped=torch.fft.rfft(x*-1, n=N + M - 1) |
|
|
| Y = torch.fft.rfft(y, n=N + M - 1) |
|
|
| corr = torch.fft.irfft(X.conj() * Y) |
| corr_flipped = torch.fft.irfft(X_flipped.conj() * Y) |
|
|
| corr=corr.sum(dim=1) |
| corr_flipped=corr_flipped.sum(dim=1) |
|
|
| |
| shifts = torch.argmax(corr, dim=-1) |
|
|
| shifts_flipped = torch.argmax(corr_flipped, dim=-1) |
| |
| |
| |
|
|
| shifts=shifts.to(torch.int64) |
| |
| result=[] |
| for i in range(len(shifts)): |
| corr_value=corr[i, shifts[i]] |
| corr_value_flipped=corr_flipped[i, shifts_flipped[i]] |
| if corr_value < corr_value_flipped: |
| shift=shifts_flipped[i].item() |
| if shift >=N: |
| shift = shift - N - M + 1 |
| result.append(torch.roll(x[i]*-1, shifts=shift, dims=-1) ) |
| else: |
| shift=shifts[i].item() |
| if shift >=N: |
| shift = shift - N - M + 1 |
| result.append(torch.roll(x[i], shifts=shift, dims=-1) ) |
|
|
|
|
| x= torch.stack(result) |
| |
|
|
| return y.to(y_device).to(y_dtype), x.to(x_device).to(x_dtype) |
|
|
| def get_pink_noise_magnitude(freqs, device='cpu'): |
| a=torch.ones_like(freqs, device=device) |
| return a / torch.sqrt(torch.clamp(freqs, min=1e-6)) |
|
|
| def generate_pink_noise(shape, device='cpu'): |
| """ |
| Generate pink noise with 1/f frequency scaling for a batch of stereo signals. |
| |
| Args: |
| shape (tuple): Shape of the noise signal (B, 2, T). |
| device (str): Device for tensor computation ('cpu' or 'cuda'). |
| |
| Returns: |
| torch.Tensor: Pink noise signal with shape (B, 2, T). |
| """ |
| B, C, T = shape |
| |
| |
| white_noise = torch.randn(B, C, T, device=device) |
| |
| |
| fft = torch.fft.rfft(white_noise, dim=-1) |
| |
| |
| freqs = torch.fft.rfftfreq(T, d=1.0).to(device) |
| |
| |
| |
| H= get_pink_noise_magnitude(freqs, device=device) |
| fft*=H.unsqueeze(0).unsqueeze(0) |
| |
| |
| pink_noise = torch.fft.irfft(fft, n=T, dim=-1) |
| |
| return pink_noise |
|
|
| def add_pink_noise(signal, snr_db): |
| """ |
| Add pink noise to a signal based on the desired SNR. |
| |
| Args: |
| signal (torch.Tensor): Original signal with shape (B, 2, T). |
| snr_db (float): Desired Signal-to-Noise Ratio in decibels. |
| |
| Returns: |
| torch.Tensor: Noisy signal with the specified SNR. |
| """ |
| |
| signal_power = torch.mean(signal ** 2, dim=(-1, -2), keepdim=True) |
|
|
| |
| |
| snr_linear = 10 ** (snr_db / 10) |
| noise_power = signal_power / snr_linear.view(-1, 1, 1) |
| |
| |
| pink_noise = generate_pink_noise(signal.shape, device=signal.device) |
| |
| |
| noise_scaling_factor = torch.sqrt(noise_power / torch.mean(pink_noise ** 2, dim=-1, keepdim=True)) |
|
|
| scaled_noise = pink_noise * noise_scaling_factor |
| |
| |
| noisy_signal = signal + scaled_noise |
| |
| return noisy_signal |
|
|
|
|
| def Gauss_smooth(X,f,Noct=1, smooth_filter=None): |
| """ |
| based on https://github.com/IoSR-Surrey/MatlabToolbox/blob/4bff1bb2da7c95de0ce2713e7c710a0afa70c705/%2Biosr/%2Bdsp/smoothSpectrum.m |
| Smooths the magnitude spectrum X using a Gaussian filter. |
| |
| Args: |
| X (torch.Tensor): Input spectrum to be smoothed, shape (B, N,). |
| f (torch.Tensor): Frequency bins corresponding to the spectrum, shape (N,). |
| Noct (int, optional): Number of octaves for smoothing. Default is 1 |
| Returns: |
| torch.Tensor: Smoothed spectrum, same shape as X. |
| """ |
|
|
| def gauss_f(f_x,F,Noct): |
| sigma = (F/Noct)/np.pi |
| g = torch.exp(-(((f_x-F)**2)/(2*(sigma**2)))) |
| g = g/torch.sum(g) |
| return g |
|
|
| shape=X.shape |
|
|
| x_oct = X.clone().view(-1, shape[-1]) |
| if Noct>0: |
| for i in range(1,len(f)): |
| g = gauss_f(f,f[i],Noct) |
| g=g.to(X.device).view(1, -1) |
| x_oct[...,i] = torch.sum(g*X) |
|
|
|
|
| x_oct = x_oct.clamp(min=0) |
|
|
| return x_oct.view(shape) |
|
|
| def prepare_smooth_filter(f, Noct=1): |
| f_matrix = f.unsqueeze(0) |
| F_matrix = f.unsqueeze(1) |
| |
| |
| sigma = (F_matrix / Noct) / np.pi |
| |
| |
| g = torch.exp(-((f_matrix - F_matrix)**2) / (2 * (sigma**2))) |
| |
| |
| g = g / g.sum(dim=1, keepdim=True) |
| |
| |
| return g |
|
|
| def Gauss_smooth_vectorized(X, f, Noct=1, smooth_filter=None): |
| """ |
| based on https://github.com/IoSR-Surrey/MatlabToolbox/blob/4bff1bb2da7c95de0ce2713e7c710a0afa70c705/%2Biosr/%2Bdsp/smoothSpectrum.m |
| Smooths the magnitude spectrum X using a Gaussian filter. |
| |
| Args: |
| X (torch.Tensor): Input spectrum to be smoothed, shape (B, N,). |
| f (torch.Tensor): Frequency bins corresponding to the spectrum, shape (N,). |
| Noct (int, optional): Number of octaves for smoothing. Default is 1 |
| Returns: |
| torch.Tensor: Smoothed spectrum, same shape as X. |
| """ |
| shape = X.shape |
| x_oct = X.clone().view(-1, shape[-1]) |
| |
| if Noct > 0: |
| |
| |
| if smooth_filter is None: |
| f_matrix = f.unsqueeze(0) |
| F_matrix = f.unsqueeze(1) |
| |
| |
| sigma = (F_matrix / Noct) / np.pi |
| |
| |
| g = torch.exp(-((f_matrix - F_matrix)**2) / (2 * (sigma**2))) |
| |
| |
| g = g / g.sum(dim=1, keepdim=True) |
| |
| |
| else: |
| g=smooth_filter |
|
|
| g = g.to(X.device) |
| |
| |
| |
| x_oct_new = torch.matmul(x_oct, g[1:].T) |
| |
| |
| x_oct[:, 1:] = x_oct_new |
| |
| |
| x_oct = x_oct.clamp(min=0) |
| |
| return x_oct.view(shape) |
|
|
| def create_music_mean_spectrum_curve(freqs_Hz, device='cpu'): |
| """ |
| Create a target curve approximating the mean spectrum of music. |
| |
| Args: |
| freqs_Hz (torch.Tensor or np.ndarray): Frequency bins (Hz). |
| device (str): Device for tensor computation ('cpu' or 'cuda'). |
| |
| Returns: |
| torch.Tensor: Target curve for equalization. |
| """ |
| import torch |
| import numpy as np |
| |
| |
| if isinstance(freqs_Hz, torch.Tensor): |
| freqs_np = freqs_Hz.cpu().numpy() |
| else: |
| freqs_np = freqs_Hz |
| |
| |
| target = np.ones_like(freqs_np) |
| |
| |
| f1 = 100 |
| f2 = 1000 |
| f3 = 5000 |
| |
| |
| for i, f in enumerate(freqs_np): |
| if f < f1: |
| |
| target[i] = np.sqrt(f / f1) |
| elif f <= f2: |
| |
| target[i] = 1.0 |
| elif f <= f3: |
| |
| target[i] = np.sqrt(f2 / f) |
| else: |
| |
| |
| val_at_f3 = np.sqrt(f2 / f3) |
| |
| target[i] = val_at_f3 * (f3 / f) |
| |
| |
| target_curve = torch.tensor(target, device=device) |
| target_curve[0]=target_curve[1] |
|
|
| |
| target_curve = target_curve * 0.1 |
| |
| return target_curve |