Download ogg/codec.py from supermind/OGG-Comfy: direct link, hf CLI and curl.
- Browser
- Download file 15.5 kB
-
https://huggingface.co/supermind/OGG-Comfy/resolve/main/ogg/codec.py
- Command line
-
hf download hf://supermind/OGG-Comfy/ogg/codec.py
-
curl -L -o codec.py https://huggingface.co/supermind/OGG-Comfy/resolve/main/ogg/codec.py
15.5 kB
| """Fish S2-Pro codec (modded DAC) used by Drama 3 and OGG TTS: 44.1 kHz audio <-> 10 codebooks at 2048 samples per frame. | |
| Encode: causal conv encoder (hop 512) -> 2x2 downsampling -> windowed transformer -> one semantic codebook (4096) | |
| and nine residual codebooks (1024). Decode: codebooks -> windowed transformer -> upsampling -> causal conv decoder. | |
| Weight norm is folded by the converter (`tools/convert.py codec`). | |
| """ | |
| import math | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from comfy.ldm.flux.math import apply_rope1 | |
| from comfy.ldm.minimax_music.dav import Snake1d | |
| SAMPLE_RATE = 44100 | |
| FRAME_LENGTH = 2048 | |
| # Decode activations grow with the take, 5.3 MiB per frame in float32 (8 GiB for 75 s), so takes decode a window at a | |
| # time. The codec is causal: after 320 frames of left context a window matches a whole-take decode to 61 dB. | |
| DECODE_WINDOW = 256 | |
| DECODE_CONTEXT = 320 | |
| DECODE_FRAME_BYTES = int(5.3 * 1024 ** 2) | |
| class CausalConv1d(nn.Module): | |
| """Left padding of the dilated kernel minus the stride; right padding only to complete the last window.""" | |
| def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, groups=1, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.conv = operations.Conv1d(in_channels, out_channels, kernel_size, stride=stride, dilation=dilation, groups=groups, dtype=dtype, device=device) | |
| self.stride = stride | |
| self.kernel_size = (kernel_size - 1) * dilation + 1 | |
| self.padding = self.kernel_size - stride | |
| def forward(self, x): | |
| frames = math.ceil((x.shape[-1] - self.kernel_size + self.padding) / self.stride + 1) | |
| extra = (frames - 1) * self.stride + self.kernel_size - self.padding - x.shape[-1] | |
| return self.conv(F.pad(x, (self.padding, extra))) | |
| class CausalConvTranspose1d(nn.Module): | |
| def __init__(self, in_channels, out_channels, kernel_size, stride, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.conv = operations.ConvTranspose1d(in_channels, out_channels, kernel_size, stride=stride, dtype=dtype, device=device) | |
| self.trim = kernel_size - stride | |
| def forward(self, x): | |
| x = self.conv(x) | |
| return x[..., :x.shape[-1] - self.trim] | |
| def residual_unit(dim, dilation, dtype, device, operations): | |
| return nn.Sequential(Snake1d(dim, dtype, device), CausalConv1d(dim, dim, 7, dilation=dilation, dtype=dtype, device=device, operations=operations), | |
| Snake1d(dim, dtype, device), CausalConv1d(dim, dim, 1, dtype=dtype, device=device, operations=operations)) | |
| class ResidualUnit(nn.Module): | |
| def __init__(self, dim, dilation, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.block = residual_unit(dim, dilation, dtype, device, operations) | |
| def forward(self, x): | |
| return x + self.block(x) | |
| def rope_matrices(length, head_dim, base, device): | |
| """The codec's RoPE as 2x2 rotations; its cos/sin table was cached in bf16, so they are rounded the same way.""" | |
| inv_freq = 1.0 / (base ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim)) | |
| angles = torch.outer(torch.arange(length, device=device).float(), inv_freq) | |
| cos, sin = angles.cos().bfloat16().float(), angles.sin().bfloat16().float() | |
| return torch.stack((cos, -sin, sin, cos), dim=-1).view(1, 1, length, head_dim // 2, 2, 2) | |
| class Attention(nn.Module): | |
| def __init__(self, dim, n_head, head_dim, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.n_head, self.head_dim = n_head, head_dim | |
| self.wqkv = operations.Linear(dim, 3 * n_head * head_dim, bias=False, dtype=dtype, device=device) | |
| self.wo = operations.Linear(n_head * head_dim, dim, bias=False, dtype=dtype, device=device) | |
| def forward(self, x, rope, mask): | |
| b, t, _ = x.shape | |
| q, k, v = (y.view(b, t, self.n_head, self.head_dim).transpose(1, 2) for y in self.wqkv(x).chunk(3, dim=-1)) | |
| q = apply_rope1(q.float(), rope).to(x.dtype) | |
| k = apply_rope1(k.float(), rope).to(x.dtype) | |
| out = F.scaled_dot_product_attention(q, k, v, attn_mask=mask) | |
| return self.wo(out.transpose(1, 2).reshape(b, t, -1)) | |
| class FeedForward(nn.Module): | |
| def __init__(self, dim, hidden, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.w1 = operations.Linear(dim, hidden, bias=False, dtype=dtype, device=device) | |
| self.w3 = operations.Linear(dim, hidden, bias=False, dtype=dtype, device=device) | |
| self.w2 = operations.Linear(hidden, dim, bias=False, dtype=dtype, device=device) | |
| def forward(self, x): | |
| return self.w2(F.silu(self.w1(x)) * self.w3(x)) | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dim, eps, dtype=None, device=None): | |
| super().__init__() | |
| self.eps = eps | |
| self.weight = nn.Parameter(torch.empty(dim, dtype=dtype, device=device)) | |
| def forward(self, x): | |
| return F.rms_norm(x.float(), (x.shape[-1],), eps=self.eps).type_as(x) * self.weight.to(x.dtype) | |
| class LayerScale(nn.Module): | |
| def __init__(self, dim, dtype=None, device=None): | |
| super().__init__() | |
| self.gamma = nn.Parameter(torch.empty(dim, dtype=dtype, device=device)) | |
| def forward(self, x): | |
| return x * self.gamma.to(x.dtype) | |
| class TransformerBlock(nn.Module): | |
| def __init__(self, dim, n_head, hidden, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.attention = Attention(dim, n_head, 64, dtype=dtype, device=device, operations=operations) | |
| self.feed_forward = FeedForward(dim, hidden, dtype=dtype, device=device, operations=operations) | |
| self.attention_norm = RMSNorm(dim, 1e-5, dtype=dtype, device=device) | |
| self.ffn_norm = RMSNorm(dim, 1e-5, dtype=dtype, device=device) | |
| self.attention_layer_scale = LayerScale(dim, dtype=dtype, device=device) | |
| self.ffn_layer_scale = LayerScale(dim, dtype=dtype, device=device) | |
| def forward(self, x, rope, mask): | |
| x = x + self.attention_layer_scale(self.attention(self.attention_norm(x), rope, mask)) | |
| return x + self.ffn_layer_scale(self.feed_forward(self.ffn_norm(x))) | |
| class WindowTransformer(nn.Module): | |
| """Causal transformer over [B, C, T] where each step sees itself and the `window - 1` steps before it.""" | |
| def __init__(self, dim, n_layer, window, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.window = window | |
| self.layers = nn.ModuleList(TransformerBlock(dim, dim // 64, 3 * dim, dtype=dtype, device=device, operations=operations) for _ in range(n_layer)) | |
| self.norm = RMSNorm(dim, 1e-5, dtype=dtype, device=device) | |
| def forward(self, x): | |
| x = x.transpose(1, 2) | |
| length = x.shape[1] | |
| rows = torch.arange(length, device=x.device) | |
| mask = (rows[None, :] <= rows[:, None]) & (rows[None, :] > rows[:, None] - self.window) | |
| rope = rope_matrices(length, 64, 10000, x.device) | |
| for layer in self.layers: | |
| x = layer(x, rope, mask) | |
| return self.norm(x).transpose(1, 2) | |
| class ConvNeXtBlock(nn.Module): | |
| def __init__(self, dim, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.dwconv = CausalConv1d(dim, dim, 7, groups=dim, dtype=dtype, device=device, operations=operations) | |
| self.norm = operations.LayerNorm(dim, eps=1e-6, dtype=dtype, device=device) | |
| self.pwconv1 = operations.Linear(dim, 4 * dim, dtype=dtype, device=device) | |
| self.pwconv2 = operations.Linear(4 * dim, dim, dtype=dtype, device=device) | |
| self.gamma = nn.Parameter(torch.empty(dim, dtype=dtype, device=device)) | |
| def forward(self, x): | |
| y = self.norm(self.dwconv(x).transpose(1, 2)) | |
| y = self.pwconv2(F.gelu(self.pwconv1(y))) * self.gamma.to(x.dtype) | |
| return x + y.transpose(1, 2) | |
| class VectorQuantize(nn.Module): | |
| def __init__(self, dim, codebook_size, codebook_dim, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.in_proj = operations.Conv1d(dim, codebook_dim, 1, dtype=dtype, device=device) | |
| self.out_proj = operations.Conv1d(codebook_dim, dim, 1, dtype=dtype, device=device) | |
| self.codebook = operations.Embedding(codebook_size, codebook_dim, dtype=dtype, device=device) | |
| def encode(self, z): | |
| """(quantized z, codes): the nearest codebook entry by cosine distance.""" | |
| latents = self.in_proj(z) | |
| flat = F.normalize(latents.transpose(1, 2).reshape(-1, latents.shape[1]).float()) | |
| codebook = F.normalize(self.codebook.weight.float().to(flat.device)) | |
| codes = (flat @ codebook.T).argmax(-1).view(latents.shape[0], -1) | |
| return self.decode(codes, z.dtype), codes | |
| def decode(self, codes, dtype): | |
| return self.out_proj(self.codebook(codes, out_dtype=dtype).transpose(1, 2)) | |
| class ResidualVectorQuantize(nn.Module): | |
| def __init__(self, n_codebooks, codebook_size, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.quantizers = nn.ModuleList(VectorQuantize(1024, codebook_size, 8, dtype=dtype, device=device, operations=operations) for _ in range(n_codebooks)) | |
| def resample_stage(down, dtype, device, operations): | |
| conv = (CausalConv1d(1024, 1024, 2, stride=2, dtype=dtype, device=device, operations=operations) if down | |
| else CausalConvTranspose1d(1024, 1024, 2, 2, dtype=dtype, device=device, operations=operations)) | |
| return nn.Sequential(conv, ConvNeXtBlock(1024, dtype=dtype, device=device, operations=operations)) | |
| class Quantizer(nn.Module): | |
| def __init__(self, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| args = dict(dtype=dtype, device=device, operations=operations) | |
| self.semantic_quantizer = ResidualVectorQuantize(1, 4096, **args) | |
| self.quantizer = ResidualVectorQuantize(9, 1024, **args) | |
| self.downsample = nn.Sequential(*(resample_stage(True, dtype, device, operations) for _ in range(2))) | |
| self.upsample = nn.Sequential(*(resample_stage(False, dtype, device, operations) for _ in range(2))) | |
| self.pre_module = WindowTransformer(1024, 8, 128, **args) | |
| self.post_module = WindowTransformer(1024, 8, 128, **args) | |
| def encode(self, z): | |
| z = self.pre_module(self.downsample(z)) | |
| semantic, code = self.semantic_quantizer.quantizers[0].encode(z) | |
| residual = z - semantic | |
| codes = [code] | |
| for quantizer in self.quantizer.quantizers: | |
| quantized, code = quantizer.encode(residual) | |
| residual = residual - quantized | |
| codes.append(code) | |
| return torch.stack(codes, dim=1) | |
| def decode(self, codes, dtype): | |
| z = self.semantic_quantizer.quantizers[0].decode(codes[:, 0].clamp(max=4095), dtype) | |
| for index, quantizer in enumerate(self.quantizer.quantizers): | |
| z = z + quantizer.decode(codes[:, 1 + index].clamp(max=1023), dtype) | |
| return self.upsample(self.post_module(z)) | |
| class EncoderBlock(nn.Module): | |
| def __init__(self, dim, stride, transformer_layers, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| half = dim // 2 | |
| stages = [ResidualUnit(half, d, dtype=dtype, device=device, operations=operations) for d in (1, 3, 9)] | |
| stages += [Snake1d(half, dtype, device), CausalConv1d(half, dim, 2 * stride, stride=stride, dtype=dtype, device=device, operations=operations)] | |
| if transformer_layers: | |
| stages.append(WindowTransformer(dim, transformer_layers, 512, dtype=dtype, device=device, operations=operations)) | |
| self.block = nn.Sequential(*stages) | |
| def forward(self, x): | |
| return self.block(x) | |
| class DecoderBlock(nn.Module): | |
| def __init__(self, in_dim, out_dim, stride, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| self.block = nn.Sequential(Snake1d(in_dim, dtype, device), | |
| CausalConvTranspose1d(in_dim, out_dim, 2 * stride, stride, dtype=dtype, device=device, operations=operations), | |
| *(ResidualUnit(out_dim, d, dtype=dtype, device=device, operations=operations) for d in (1, 3, 9))) | |
| def forward(self, x): | |
| return self.block(x) | |
| class Encoder(nn.Module): | |
| def __init__(self, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| args = dict(dtype=dtype, device=device, operations=operations) | |
| self.block = nn.Sequential(CausalConv1d(1, 64, 7, **args), | |
| *(EncoderBlock(dim, stride, layers, **args) for dim, stride, layers in ((128, 2, 0), (256, 4, 0), (512, 8, 0), (1024, 8, 4))), | |
| Snake1d(1024, dtype, device), CausalConv1d(1024, 1024, 3, **args)) | |
| def forward(self, x): | |
| return self.block(x) | |
| class Decoder(nn.Module): | |
| def __init__(self, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| args = dict(dtype=dtype, device=device, operations=operations) | |
| self.model = nn.Sequential(CausalConv1d(1024, 1536, 7, **args), | |
| *(DecoderBlock(1536 // 2 ** i, 768 // 2 ** i, stride, **args) for i, stride in enumerate((8, 8, 4, 2))), | |
| Snake1d(96, dtype, device), CausalConv1d(96, 1, 7, **args), nn.Tanh()) | |
| def forward(self, x): | |
| return self.model(x) | |
| class FishCodec(nn.Module): | |
| def __init__(self, dtype=None, device=None, operations=None): | |
| super().__init__() | |
| args = dict(dtype=dtype, device=device, operations=operations) | |
| self.encoder = Encoder(**args) | |
| self.quantizer = Quantizer(**args) | |
| self.decoder = Decoder(**args) | |
| def encode(self, audio): | |
| """Mono audio [B, samples] at 44.1 kHz -> codes [B, 10, frames]; the audio is padded to whole frames.""" | |
| audio = F.pad(audio, (0, -audio.shape[-1] % FRAME_LENGTH)) | |
| return self.quantizer.encode(self.encoder(audio[:, None])) | |
| def decode(self, codes, dtype): | |
| """Codes [B, 10, frames] -> audio [B, frames * 2048], DECODE_WINDOW frames at a time.""" | |
| pieces = [] | |
| for start in range(0, codes.shape[-1], DECODE_WINDOW): | |
| first = max(0, start - DECODE_CONTEXT) | |
| audio = self.decoder(self.quantizer.decode(codes[..., first:start + DECODE_WINDOW], dtype))[:, 0] | |
| pieces.append(audio[:, (start - first) * FRAME_LENGTH:]) | |
| return torch.cat(pieces, dim=-1) | |
| class Codec: | |
| """The codec module and the ComfyUI patcher that moves it to the GPU and back.""" | |
| def __init__(self, model, patcher): | |
| self.model = model | |
| self.patcher = patcher | |
| def load_codec(path): | |
| """models/vae/drama3_codec.safetensors (tools/convert.py codec of ComfyUI-Drama3, weight norm folded) -> Codec.""" | |
| import comfy.model_management | |
| import comfy.model_patcher | |
| import comfy.ops | |
| import comfy.utils | |
| state = comfy.utils.load_torch_file(path) | |
| load_device = comfy.model_management.get_torch_device() | |
| offload_device = comfy.model_management.vae_offload_device() | |
| model = FishCodec(dtype=torch.float32, device=offload_device, operations=comfy.ops.manual_cast) | |
| comfy.model_management.archive_model_dtypes(model) | |
| model.load_state_dict(state, strict=True) | |
| return Codec(model, comfy.model_patcher.ModelPatcher(model, load_device=load_device, offload_device=offload_device)) | |