| from __future__ import annotations |
|
|
| from typing import Optional |
|
|
| import torch |
| from torch import nn |
|
|
|
|
| class ConvNeXtBlock(nn.Module): |
| def __init__( |
| self, |
| dim: int, |
| intermediate_dim: int, |
| layer_scale_init_value: float, |
| ) -> None: |
| super().__init__() |
| self.dwconv = nn.Conv1d(dim, dim, kernel_size=7, padding=3, groups=dim) |
| self.norm = nn.LayerNorm(dim, eps=1e-6) |
| self.pwconv1 = nn.Linear(dim, intermediate_dim) |
| self.act = nn.GELU() |
| self.pwconv2 = nn.Linear(intermediate_dim, dim) |
| self.gamma = nn.Parameter(layer_scale_init_value * torch.ones(dim)) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| residual = x |
| x = self.dwconv(x) |
| x = x.transpose(1, 2) |
| x = self.norm(x) |
| x = self.pwconv1(x) |
| x = self.act(x) |
| x = self.pwconv2(x) |
| x = self.gamma * x |
| x = x.transpose(1, 2) |
| return residual + x |
|
|
|
|
| class VocosBackbone(nn.Module): |
| def __init__( |
| self, |
| input_channels: int = 100, |
| dim: int = 512, |
| intermediate_dim: int = 1536, |
| num_layers: int = 8, |
| layer_scale_init_value: Optional[float] = None, |
| ) -> None: |
| super().__init__() |
| self.input_channels = input_channels |
| self.embed = nn.Conv1d(input_channels, dim, kernel_size=7, padding=3) |
| self.norm = nn.LayerNorm(dim, eps=1e-6) |
| layer_scale_init_value = layer_scale_init_value or 1 / num_layers |
| self.convnext = nn.ModuleList( |
| [ |
| ConvNeXtBlock( |
| dim=dim, |
| intermediate_dim=intermediate_dim, |
| layer_scale_init_value=layer_scale_init_value, |
| ) |
| for _ in range(num_layers) |
| ] |
| ) |
| self.final_layer_norm = nn.LayerNorm(dim, eps=1e-6) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x = self.embed(x) |
| x = self.norm(x.transpose(1, 2)).transpose(1, 2) |
| for conv_block in self.convnext: |
| x = conv_block(x) |
| return self.final_layer_norm(x.transpose(1, 2)) |
|
|
|
|
| class ISTFT(nn.Module): |
| def __init__( |
| self, |
| n_fft: int = 1024, |
| hop_length: int = 256, |
| win_length: int = 1024, |
| padding: str = "center", |
| ) -> None: |
| super().__init__() |
| if padding not in ("center", "same"): |
| raise ValueError("padding must be 'center' or 'same'") |
| self.padding = padding |
| self.n_fft = n_fft |
| self.hop_length = hop_length |
| self.win_length = win_length |
| self.register_buffer("window", torch.hann_window(win_length)) |
|
|
| def forward(self, spec: torch.Tensor) -> torch.Tensor: |
| if self.padding == "center": |
| return torch.istft( |
| spec, |
| self.n_fft, |
| self.hop_length, |
| self.win_length, |
| self.window, |
| center=True, |
| ) |
|
|
| pad = (self.win_length - self.hop_length) // 2 |
| if spec.dim() != 3: |
| raise ValueError("Expected complex spectrogram with shape [B, F, T]") |
|
|
| _, _, frames = spec.shape |
| ifft = torch.fft.irfft(spec, self.n_fft, dim=1, norm="backward") |
| ifft = ifft * self.window[None, :, None] |
|
|
| output_size = (frames - 1) * self.hop_length + self.win_length |
| y = torch.nn.functional.fold( |
| ifft, |
| output_size=(1, output_size), |
| kernel_size=(1, self.win_length), |
| stride=(1, self.hop_length), |
| )[:, 0, 0, pad:-pad] |
|
|
| window_sq = self.window.square().expand(1, frames, -1).transpose(1, 2) |
| window_envelope = torch.nn.functional.fold( |
| window_sq, |
| output_size=(1, output_size), |
| kernel_size=(1, self.win_length), |
| stride=(1, self.hop_length), |
| ).squeeze()[pad:-pad] |
| return y / window_envelope |
|
|
|
|
| class ISTFTHead(nn.Module): |
| def __init__( |
| self, |
| dim: int = 512, |
| n_fft: int = 1024, |
| hop_length: int = 256, |
| padding: str = "center", |
| ) -> None: |
| super().__init__() |
| self.out = nn.Linear(dim, n_fft + 2) |
| self.istft = ISTFT( |
| n_fft=n_fft, |
| hop_length=hop_length, |
| win_length=n_fft, |
| padding=padding, |
| ) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x = self.out(x).transpose(1, 2) |
| mag, phase = x.chunk(2, dim=1) |
| mag = torch.exp(mag).clip(max=1e2) |
| spec = mag * (torch.cos(phase) + 1j * torch.sin(phase)) |
| return self.istft(spec) |
|
|
|
|
| class LocalVocos(nn.Module): |
| def __init__(self) -> None: |
| super().__init__() |
| self.backbone = VocosBackbone() |
| self.head = ISTFTHead() |
|
|
| @torch.inference_mode() |
| def decode(self, features_input: torch.Tensor) -> torch.Tensor: |
| x = self.backbone(features_input) |
| return self.head(x) |
|
|