| import torch |
| import torch.nn as nn |
| from torch.nn.utils.parametrizations import weight_norm |
| from typing import List, Tuple, Any |
|
|
| class ResBlock1D(nn.Module): |
| """Residual block with dilated 1D convolutions.""" |
| def __init__( |
| self, |
| channels: int, |
| kernel_size: int = 3, |
| dilations: List[int] = [1, 3, 5] |
| ): |
| super().__init__() |
| self.convs1 = nn.ModuleList() |
| self.convs2 = nn.ModuleList() |
| |
| for d in dilations: |
| |
| padding1 = (kernel_size - 1) * d // 2 |
| padding2 = (kernel_size - 1) // 2 |
| self.convs1.append( |
| weight_norm(nn.Conv1d( |
| channels, channels, kernel_size, |
| stride=1, padding=padding1, dilation=d |
| )) |
| ) |
| self.convs2.append( |
| weight_norm(nn.Conv1d( |
| channels, channels, kernel_size, |
| stride=1, padding=padding2, dilation=1 |
| )) |
| ) |
| |
| self.activation = nn.LeakyReLU(0.1) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| """ |
| Args: |
| x: Input tensor of shape (batch, channels, time_steps). |
| |
| Returns: |
| Output tensor of shape (batch, channels, time_steps). |
| """ |
| for c1, c2 in zip(self.convs1, self.convs2): |
| residual = x |
| x = self.activation(x) |
| x = c1(x) |
| x = self.activation(x) |
| x = c2(x) |
| x = x + residual |
| return x |
|
|
|
|
| class WaveformEncoder(nn.Module): |
| """Waveform encoder that downsamples a time-domain signal into a latent space.""" |
| def __init__(self, config_enc: Any): |
| super().__init__() |
| in_channels = config_enc.in_channels |
| base_channels = config_enc.channels |
| strides = config_enc.strides |
| kernel_sizes = config_enc.kernel_sizes |
| dilations = config_enc.dilations |
| |
| |
| self.conv_in = weight_norm(nn.Conv1d(in_channels, base_channels, kernel_size=7, stride=1, padding=3)) |
| |
| |
| self.down_blocks = nn.ModuleList() |
| curr_channels = base_channels |
| |
| for stride, k_size in zip(strides, kernel_sizes): |
| next_channels = curr_channels * 2 |
| self.down_blocks.append( |
| nn.Sequential( |
| nn.LeakyReLU(0.1), |
| weight_norm(nn.Conv1d( |
| curr_channels, next_channels, kernel_size=k_size, |
| stride=stride, padding=(k_size - 1) // 2 |
| )), |
| ResBlock1D(next_channels, kernel_size=3, dilations=dilations) |
| ) |
| ) |
| curr_channels = next_channels |
| |
| self.out_channels = curr_channels |
| |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| """ |
| Args: |
| x: Input waveform tensor of shape (batch, 1, samples). |
| |
| Returns: |
| Encoded features of shape (batch, out_channels, frames). |
| """ |
| x = self.conv_in(x) |
| for block in self.down_blocks: |
| x = block(x) |
| return x |
|
|
|
|
| class WaveformDecoder(nn.Module): |
| """Waveform decoder that upsamples latent representations back into a time-domain waveform.""" |
| def __init__(self, config_dec: Any, in_channels: int): |
| super().__init__() |
| upsample_rates = config_dec.upsample_rates |
| upsample_kernel_sizes = config_dec.upsample_kernel_sizes |
| base_channels = config_dec.channels |
| resblock_kernel_sizes = config_dec.resblock_kernel_sizes |
| resblock_dilations = config_dec.resblock_dilations |
| |
| self.conv_in = weight_norm(nn.Conv1d(in_channels, base_channels * (2 ** len(upsample_rates)), kernel_size=3, stride=1, padding=1)) |
| |
| |
| self.up_blocks = nn.ModuleList() |
| curr_channels = base_channels * (2 ** len(upsample_rates)) |
| |
| for rate, k_size in zip(upsample_rates, upsample_kernel_sizes): |
| next_channels = curr_channels // 2 |
| |
| |
| up_conv = weight_norm(nn.ConvTranspose1d( |
| curr_channels, next_channels, kernel_size=k_size, |
| stride=rate, padding=(k_size - rate) // 2 |
| )) |
| |
| res_blocks = nn.ModuleList() |
| for r_k_size, r_dilations in zip(resblock_kernel_sizes, resblock_dilations): |
| res_blocks.append(ResBlock1D(next_channels, kernel_size=r_k_size, dilations=r_dilations)) |
| |
| self.up_blocks.append(nn.ModuleDict({ |
| "upsample": up_conv, |
| "resblocks": res_blocks |
| })) |
| |
| curr_channels = next_channels |
| |
| self.activation = nn.LeakyReLU(0.1) |
| self.conv_out = weight_norm(nn.Conv1d(curr_channels, 1, kernel_size=7, stride=1, padding=3)) |
| self.tanh = nn.Tanh() |
| |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| """ |
| Args: |
| x: Latent features of shape (batch, in_channels, frames). |
| |
| Returns: |
| Reconstructed waveform of shape (batch, 1, samples). |
| """ |
| x = self.conv_in(x) |
| |
| for block in self.up_blocks: |
| x = self.activation(x) |
| x = block["upsample"](x) |
| |
| |
| res_out = torch.zeros_like(x) |
| for resblock in block["resblocks"]: |
| res_out = res_out + resblock(x) |
| x = res_out / len(block["resblocks"]) |
| |
| x = self.activation(x) |
| x = self.conv_out(x) |
| x = self.tanh(x) |
| return x |
|
|