Dirdx
/

Dirdx's picture ahmadw's picture
Duplicate from ahmadw/YuE2-3B-MLX
be2c293
Raw History Blame Contribute Delete
2.95 kB
"""YuE2 Oobleck VAE decoder in MLX (channels-last; weight norm folded by convert.py)."""
from __future__ import annotations
import json
import math
from pathlib import Path
import mlx.core as mx
import mlx.nn as nn
FRAME = 1920 # samples per latent frame at 48 kHz
class Snake(nn.Module):
def __init__(self, ch):
super().__init__()
self.alpha = mx.zeros((ch,))
self.beta = mx.zeros((ch,))
def __call__(self, x):
alpha, beta = mx.exp(self.alpha), mx.exp(self.beta)
return x + (1.0 / (beta + 1e-9)) * mx.sin(x * alpha) ** 2
class ResidualUnit(nn.Module):
def __init__(self, ch, dilation):
super().__init__()
self.layers = [Snake(ch), nn.Conv1d(ch, ch, 7, dilation=dilation, padding=3 * dilation),
Snake(ch), nn.Conv1d(ch, ch, 1)]
def __call__(self, x):
y = x
for layer in self.layers:
y = layer(y)
return x + y
class DecoderBlock(nn.Module):
def __init__(self, cin, cout, stride):
super().__init__()
self.layers = [Snake(cin),
nn.ConvTranspose1d(cin, cout, 2 * stride, stride=stride, padding=math.ceil(stride / 2)),
ResidualUnit(cout, 1), ResidualUnit(cout, 3), ResidualUnit(cout, 9)]
def __call__(self, x):
for layer in self.layers:
x = layer(x)
return x
class OobleckDecoder(nn.Module):
def __init__(self, cfg):
super().__init__()
c, mults, strides = cfg["channels"], [1] + list(cfg["c_mults"]), cfg["strides"]
layers = [nn.Conv1d(cfg["latent_dim"], mults[-1] * c, 7, padding=3)]
for i in range(len(mults) - 1, 0, -1):
layers.append(DecoderBlock(mults[i] * c, mults[i - 1] * c, strides[i - 1]))
layers += [Snake(mults[0] * c), nn.Conv1d(mults[0] * c, cfg["out_channels"], 7, padding=3, bias=False)]
self.layers = layers
def __call__(self, x):
for layer in self.layers:
x = layer(x)
return x
def decode_tiled(self, z, core=1024, halo=16):
"""z [T,64] float32 → audio [1920*T-64, 2] float32, exact tile cores, no crossfade."""
frames, total = z.shape[0], FRAME * z.shape[0] - 64
out = []
for start in range(0, frames, core):
end = min(frames, start + core)
left, right = max(0, start - halo), min(frames, end + halo)
tile = self(z[None, left:right])[0]
crop = (start - left) * FRAME
out.append(tile[crop:crop + min(end * FRAME, total) - start * FRAME])
mx.eval(out[-1])
return mx.concatenate(out)
def load_vae(path: Path) -> OobleckDecoder:
path = Path(path)
cfg = json.loads((path / "vae_config.json").read_text())
decoder = OobleckDecoder(cfg["decoder_config"])
decoder.load_weights(str(path / "vae.safetensors"))
mx.eval(decoder.parameters())
return decoder