Files
soprano-factory/model/encoder.py
T
2026-02-09 01:01:44 -05:00

79 lines
2.3 KiB
Python

# model/encoder.py - Standardized for V3
import torch
from torch import nn
import torchaudio
from .common import VocosBackbone
from .quantizer import FSQSTE
def safe_log(x: torch.Tensor, clip_val: float = 1e-5) -> torch.Tensor:
"""Prevents log(0) errors. Explicitly Float32."""
return torch.log(torch.clip(x, min=clip_val))
class Encoder(nn.Module):
"""
Encodes audio into discrete quantized tokens.
Standardized for Float32 precision and Windows compatibility.
"""
def __init__(
self,
num_input_mels=80,
encoder_dim=512,
encoder_layers=8,
bottleneck_channels=5,
):
super().__init__()
# 1. Mel Spectrogram Transform
self.mel_spec = torchaudio.transforms.MelSpectrogram(
sample_rate=32000,
n_fft=2048,
hop_length=512,
n_mels=num_input_mels,
normalized=True
)
# 2. Backbone Network
self.backbone = VocosBackbone(
input_channels=num_input_mels,
dim=encoder_dim,
intermediate_dim=encoder_dim * 3,
num_layers=encoder_layers,
)
# 3. Downsampling Projector
self.downsample_factor = 4
self.down_proj = nn.Linear(encoder_dim, bottleneck_channels)
# 4. Quantizer
self.quantizer = FSQSTE(levels=[8, 8, 5, 5, 5])
def preprocess(self, audio):
"""Converts raw audio to log-mel spectrogram (Float32)."""
if audio.dim() == 2:
audio = audio.unsqueeze(1) # Ensure [B, 1, T]
mel = self.mel_spec(audio.float()) # Force Float32 math
return safe_log(mel).squeeze(1)
def forward(self, audio, return_indices=False):
"""
Audio -> Spectrogram -> Features -> Quantized Codes
"""
# 1. Mel Spec
x = self.preprocess(audio)
# 2. Backbone
x = self.backbone(x) # [B, Dim, Frames]
# 3. Downsample (Slicing)
x = x[:, :, ::self.downsample_factor]
# 4. Project to Bottleneck
x = x.transpose(1, 2) # [B, Frames//4, Dim]
x = self.down_proj(x) # [B, Frames//4, 5]
# 5. Quantize
if return_indices:
return self.quantizer.to_codebook_index(x)
return self.quantizer(x)