| import numpy as np |
| import torch |
| import matplotlib.pyplot as plt |
| from munch import Munch |
| import os |
| import subprocess |
| from pynvml import nvmlInit, nvmlDeviceGetHandleByIndex, nvmlDeviceGetMemoryInfo |
|
|
| nv_init = False |
| def init_weights(m, mean=0.0, std=0.01): |
| classname = m.__class__.__name__ |
| if classname.find("Conv") != -1: |
| m.weight.data.normal_(mean, std) |
|
|
|
|
| def apply_weight_norm(m): |
| classname = m.__class__.__name__ |
| if classname.find("Conv") != -1: |
| weight_norm(m) |
|
|
|
|
| def get_padding(kernel_size, dilation=1): |
| return int((kernel_size * dilation - dilation) / 2) |
|
|
| def print_gpu_vram(tag): |
| if False: |
| global nv_init |
| if not nv_init: |
| nvmlInit() |
| nv_init = True |
| handle = nvmlDeviceGetHandleByIndex(0) |
| info = nvmlDeviceGetMemoryInfo(handle) |
| print(f"{tag} - GPU memory occupied: {info.used//1024**2} MB.") |
|
|
|
|
| def maximum_path(neg_cent, mask): |
| """Cython optimized version. |
| neg_cent: [b, t_t, t_s] |
| mask: [b, t_t, t_s] |
| """ |
| device = neg_cent.device |
| dtype = neg_cent.dtype |
| neg_cent = np.ascontiguousarray(neg_cent.data.cpu().numpy().astype(np.float32)) |
| path = np.ascontiguousarray(np.zeros(neg_cent.shape, dtype=np.int32)) |
|
|
| t_t_max = np.ascontiguousarray( |
| mask.sum(1)[:, 0].data.cpu().numpy().astype(np.int32) |
| ) |
| t_s_max = np.ascontiguousarray( |
| mask.sum(2)[:, 0].data.cpu().numpy().astype(np.int32) |
| ) |
| maximum_path_c(path, neg_cent, t_t_max, t_s_max) |
| return torch.from_numpy(path).to(device=device, dtype=dtype) |
|
|
|
|
| def get_data_path_list(path): |
| result = [] |
| if os.path.isfile(path): |
| with open(path, "r", encoding="utf-8", errors="ignore") as f: |
| result = f.readlines() |
| return result |
|
|
|
|
| def sequence_mask(length, max_length=None): |
| if max_length is None: |
| max_length = length.max() |
| x = torch.arange(max_length, dtype=length.dtype, device=length.device) |
| return x.unsqueeze(0) < length.unsqueeze(1) |
|
|
|
|
| def length_to_mask(lengths) -> torch.Tensor: |
| mask = ( |
| torch.arange(lengths.max()) |
| .unsqueeze(0) |
| .expand(lengths.shape[0], -1) |
| .type_as(lengths) |
| ) |
| mask = torch.gt(mask + 1, lengths.unsqueeze(1)) |
| return mask |
|
|
|
|
| |
| def log_norm(x, mean=-4, std=4, dim=2): |
| """ |
| normalized log mel -> mel -> norm -> log(norm) |
| """ |
| |
| x = (torch.exp(x * std + mean) ** 0.33).sum(dim=dim) |
| return x |
|
|
|
|
| def plot_spectrogram_to_figure( |
| spectrogram, |
| title="Spectrogram", |
| figsize=(12, 5), |
| dpi=150, |
| interpolation="bilinear", |
| cmap="viridis", |
| ): |
| """Converts a spectrogram tensor/numpy array to a matplotlib figure with improved quality.""" |
| plt.switch_backend("agg") |
|
|
| |
| if isinstance(spectrogram, torch.Tensor): |
| spectrogram_np = spectrogram.detach().cpu().numpy() |
| elif isinstance(spectrogram, np.ndarray): |
| spectrogram_np = spectrogram |
| else: |
| raise TypeError("Input spectrogram must be a torch.Tensor or numpy.ndarray") |
|
|
| |
| if spectrogram_np.ndim > 2: |
| if spectrogram_np.shape[0] == 1: |
| spectrogram_np = spectrogram_np.squeeze(0) |
| else: |
| |
| |
| spectrogram_np = spectrogram_np[0, :, :] |
| |
| |
|
|
| fig, ax = plt.subplots(figsize=figsize, dpi=dpi) |
|
|
| |
| valid_interpolations = [ |
| None, |
| "none", |
| "nearest", |
| "bilinear", |
| "bicubic", |
| "spline16", |
| "spline36", |
| "hanning", |
| "hamming", |
| "hermite", |
| "kaiser", |
| "quadric", |
| "catrom", |
| "gaussian", |
| "bessel", |
| "mitchell", |
| "sinc", |
| "lanczos", |
| "blackman", |
| ] |
| if interpolation not in valid_interpolations: |
| print(f"Warning: Invalid interpolation '{interpolation}'. Using 'bilinear'.") |
| interpolation = "bilinear" |
|
|
| im = ax.imshow( |
| spectrogram_np, |
| aspect="auto", |
| origin="lower", |
| interpolation=interpolation, |
| cmap=cmap, |
| ) |
|
|
| plt.colorbar(im, ax=ax) |
| plt.xlabel("Frames") |
| plt.ylabel("Mel Channels") |
| plt.title(title) |
| plt.tight_layout() |
| |
| return fig |
|
|
|
|
| def plot_mel_signed_difference_to_figure( |
| mel_gt_normalized_np, |
| mel_pred_log_np, |
| mean: float, |
| std: float, |
| title="Signed Mel Log Difference (GT - Pred)", |
| figsize=(12, 5), |
| dpi=150, |
| cmap="vanimo", |
| max_abs_diff_clip=None, |
| static_max_abs=None, |
| ): |
| """Plots the signed difference between two mel spectrograms using a diverging colormap.""" |
| plt.switch_backend("agg") |
|
|
| |
| min_len = min(mel_gt_normalized_np.shape[1], mel_pred_log_np.shape[1]) |
| mel_gt_trimmed = mel_gt_normalized_np[:, :min_len] |
| mel_pred_log_trimmed = mel_pred_log_np[:, :min_len] |
|
|
| |
| mel_pred_normalized_np = (mel_pred_log_trimmed - mean) / std |
|
|
| |
| diff = mel_gt_trimmed - mel_pred_normalized_np |
|
|
| fig, ax = plt.subplots(figsize=figsize, dpi=dpi) |
|
|
| if static_max_abs is not None: |
| |
| vmin = -static_max_abs |
| vmax = static_max_abs |
| else: |
| |
| max_abs_val = np.max(np.abs(diff)) + 1e-9 |
| if max_abs_diff_clip is not None: |
| max_abs_val = min( |
| max_abs_val, max_abs_diff_clip |
| ) |
|
|
| vmin = -max_abs_val |
| vmax = max_abs_val |
|
|
| im = ax.imshow( |
| diff, |
| aspect="auto", |
| origin="lower", |
| interpolation="none", |
| cmap=cmap, |
| vmin=vmin, |
| vmax=vmax, |
| ) |
|
|
| plt.colorbar( |
| im, ax=ax, label="Signed Normalized Log Difference (GT - Pred)" |
| ) |
| plt.xlabel("Frames") |
| plt.ylabel("Mel Channels") |
| plt.title(title) |
| plt.tight_layout() |
| |
| return fig |
|
|
|
|
| def get_image(arrs): |
| plt.switch_backend("agg") |
| fig = plt.figure() |
| ax = plt.gca() |
| im = ax.imshow(arrs) |
| plt.colorbar(im, ax=ax) |
| return fig |
|
|
|
|
| def recursive_munch(d): |
| if isinstance(d, dict): |
| return Munch((k, recursive_munch(v)) for k, v in d.items()) |
| elif isinstance(d, list): |
| return [recursive_munch(v) for v in d] |
| else: |
| return d |
|
|
|
|
| def get_git_commit_hash(): |
| try: |
| commit_hash = ( |
| subprocess.check_output(["git", "rev-parse", "HEAD"]) |
| .strip() |
| .decode("utf-8") |
| ) |
| return commit_hash |
| except subprocess.CalledProcessError as e: |
| print("Error obtaining git commit hash:", e) |
| return "unknown" |
|
|
|
|
| def get_git_diff(): |
| try: |
| |
| diff_output = subprocess.check_output(["git", "diff"]).decode("utf-8") |
| return diff_output |
| except subprocess.CalledProcessError as e: |
| print("Error obtaining git diff:", e) |
| return "" |
|
|
|
|
| def save_git_diff(out_dir): |
| hash = get_git_commit_hash() |
| diff = get_git_diff() |
| diff_file = os.path.join(out_dir, "git_state.txt") |
| with open(diff_file, "w") as f: |
| f.write(f"Git commit hash: {hash}\n\n") |
| f.write(diff) |
| print(f"Git diff saved to {diff_file}") |
|
|
|
|
| def duration_to_alignment(duration: torch.Tensor) -> torch.Tensor: |
| """Convert a sequence of duration values to an attention matrix. |
| |
| duration -- [t]ext length |
| result -- [t]ext length x [a]udio length""" |
| indices = torch.repeat_interleave( |
| torch.arange(duration.shape[0], device=duration.device), duration.to(torch.int) |
| ) |
| result = torch.zeros((duration.shape[0], indices.shape[0]), device=duration.device) |
| result[indices, torch.arange(indices.shape[0])] = 1 |
| return result |
|
|
|
|
| def clamped_exp(x: torch.Tensor) -> torch.Tensor: |
| x = x.clamp(-35, 35) |
| return torch.exp(x) |
|
|
|
|
| def leaky_clamp( |
| x_in: torch.Tensor, min_f: float, max_f: float, slope: float = 0.001 |
| ) -> torch.Tensor: |
| x = x_in |
| min_t = torch.full_like(x, min_f, device=x.device) |
| max_t = torch.full_like(x, max_f, device=x.device) |
| x = torch.maximum(x, min_t + slope * (x - min_t)) |
| x = torch.minimum(x, max_t + slope * (x - max_t)) |
| return x |
|
|
|
|
| class DecoderPrediction: |
| def __init__( |
| self, |
| audio=None, |
| x=None, |
| y=None, |
| magnitude=None, |
| ): |
| self.audio = audio |
| self.x = x |
| self.y = y |
| self.magnitude = magnitude |