| """ |
| rec7_model.py — the rec7 substitute encoder for MiniMax-Music3. |
| |
| Audio -> Flow-VAE latents -> per-frame hidden states (4096-d @ 25 Hz) |
| that the M3 renderer accepts as if the language model had produced them. |
| From those states the 8 code streams and the full renderer condition |
| follow deterministically (semantic head argmax + the released depth |
| decoder's greedy chain), which is how "covers" work: real recording in, |
| M3 renders it back, timbre and performance preserved. |
| |
| Everything here is inference-only and self-contained apart from: |
| - the MiniMax-Music3 checkpoint (Flow-VAE, depth decoder, LM tables) |
| - dav_loader.py (thin loader for the Flow-VAE; ships alongside) |
| """ |
| from __future__ import annotations |
|
|
| import json |
| from pathlib import Path |
|
|
| import numpy as np |
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| |
| LATENT_CHANNELS = 128 |
| FRAMES = 128 |
| LATENT_WINDOW_MAX = 448 |
| RATIO_NUM, RATIO_DEN = 441, 128 |
| CHUNK_FRAMES, CHUNK_HOP = 200, 100 |
| HOP_LATENTS = 345 |
| OWNED_FROM = 25 |
| SEM_VOCAB, AC_VOCAB, N_AC = 16384, 1024, 7 |
| SEM_OFFSET = 151675 |
| H_DIM = 4096 |
|
|
|
|
| def n_dit_windows(n_frames): |
| return max(1, (n_frames - 1) // CHUNK_HOP) |
|
|
|
|
| def frame_latent_starts(n_frames): |
| """Frame t -> first latent index. Piecewise, NOT t*441/128: the |
| renderer denoises 200-frame windows on a 100-frame hop stitched at an |
| integer 345-latent hop, and non-first windows take ownership 25 |
| frames after their nominal start.""" |
| t = np.arange(n_frames + 1, dtype=np.int64) |
| k = np.clip((t - OWNED_FROM) // CHUNK_HOP, 0, n_dit_windows(n_frames) - 1) |
| tau = t - k * CHUNK_HOP |
| Fw = np.minimum(CHUNK_FRAMES, n_frames - k * CHUNK_HOP) |
| L = Fw * RATIO_NUM // RATIO_DEN |
| return k * HOP_LATENTS + (tau * L + Fw - 1) // Fw |
|
|
|
|
| def pool_matrix(bounds): |
| pool = np.zeros((FRAMES, LATENT_WINDOW_MAX), dtype=np.float32) |
| for j in range(FRAMES): |
| a, b = int(bounds[j]), int(bounds[j + 1]) |
| if b > a: |
| pool[j, a:b] = 1.0 / (b - a) |
| return pool |
|
|
|
|
| |
| class ResBlock(nn.Module): |
| def __init__(self, d, dilation): |
| super().__init__() |
| self.norm = nn.GroupNorm(1, d) |
| self.conv1 = nn.Conv1d(d, d, 3, padding=dilation, dilation=dilation) |
| self.conv2 = nn.Conv1d(d, d, 1) |
|
|
| def forward(self, x): |
| h = self.conv1(F.gelu(self.norm(x))) |
| return x + self.conv2(F.gelu(h)) |
|
|
|
|
| class DepthDecoder(nn.Module): |
| """Internal auxiliary head used during training; kept so the |
| checkpoint loads. Not used at inference (the released M3 depth |
| decoder produces the acoustic codes from the states).""" |
|
|
| def __init__(self, d_ctx=1088, d=512, layers=2, heads=8, ff_mult=4, |
| dropout=0.1): |
| super().__init__() |
| self.proj = nn.Linear(d_ctx, d) |
| self.sem_emb = nn.Embedding(SEM_VOCAB, d) |
| self.ac_emb = nn.Embedding((N_AC - 1) * AC_VOCAB, d) |
| self.pos = nn.Parameter(torch.zeros(1, 8, d)) |
| layer = nn.TransformerEncoderLayer( |
| d, heads, d * ff_mult, dropout=dropout, activation="gelu", |
| batch_first=True, norm_first=True) |
| self.tr = nn.TransformerEncoder(layer, layers) |
| self.heads = nn.ModuleList(nn.Linear(d, AC_VOCAB) for _ in range(N_AC)) |
| mask = torch.triu(torch.full((8, 8), float("-inf")), diagonal=1) |
| self.register_buffer("causal", mask, persistent=False) |
|
|
|
|
| class V4Encoder(nn.Module): |
| def __init__(self, d_model=1088, n_layers=8, n_heads=17, ff_mult=4, |
| dropout=0.1, depth_d=512, depth_layers=2, depth_heads=8): |
| super().__init__() |
| self.conv_in = nn.Conv1d(LATENT_CHANNELS, d_model, 7, padding=3) |
| self.blocks = nn.ModuleList(ResBlock(d_model, d) for d in (1, 3, 9)) |
| self.pos = nn.Parameter(torch.zeros(1, FRAMES, d_model)) |
| layer = nn.TransformerEncoderLayer( |
| d_model, n_heads, d_model * ff_mult, dropout=dropout, |
| activation="gelu", batch_first=True, norm_first=True) |
| self.transformer = nn.TransformerEncoder(layer, n_layers) |
| self.norm_out = nn.LayerNorm(d_model) |
| self.sem_head = nn.Linear(d_model, SEM_VOCAB) |
| self.depth = DepthDecoder(d_model, depth_d, depth_layers, depth_heads, |
| ff_mult, dropout) |
|
|
| def features(self, latents, pool): |
| x = self.conv_in(latents.transpose(1, 2)) |
| for b in self.blocks: |
| x = b(x) |
| x = torch.bmm(pool, x.transpose(1, 2)) + self.pos |
| return self.norm_out(self.transformer(x)) |
|
|
| def forward(self, latents, pool): |
| feats = self.features(latents, pool) |
| return feats, self.sem_head(feats) |
|
|
|
|
| class HHead(nn.Module): |
| def __init__(self, d_in=1088): |
| super().__init__() |
| self.net = nn.Sequential(nn.Linear(d_in, 2048), nn.GELU(), |
| nn.Linear(2048, H_DIM)) |
|
|
| def forward(self, x): |
| return self.net(x) |
|
|
|
|
| |
| def load_rec7(weights_dir, device="cuda"): |
| weights_dir = Path(weights_dir) |
| trunk = V4Encoder().to(device).eval() |
| trunk.load_state_dict(torch.load(weights_dir / "trunk.pt", |
| map_location="cpu", |
| weights_only=False)["model"]) |
| head = HHead(trunk.pos.shape[-1]).to(device).eval() |
| head.load_state_dict(torch.load(weights_dir / "head.pt", |
| map_location="cpu", |
| weights_only=False)["model"]) |
| for p in list(trunk.parameters()) + list(head.parameters()): |
| p.requires_grad_(False) |
| return trunk, head |
|
|
|
|
| def load_dav(m3_dir, device="cuda"): |
| """Flow-VAE from the M3 checkpoint, via the small loader shipped here.""" |
| from dav_loader import get_dav_class |
| m3_dir = Path(m3_dir) |
| dav = get_dav_class(m3_dir.parent).from_original_dav( |
| str(m3_dir / "dav.pth")).to(device).eval() |
| for p in dav.parameters(): |
| p.requires_grad_(False) |
| return dav |
|
|
|
|
| def lm_semantic_tables(m3_dir, device="cuda"): |
| """The 16k-row slices of the LM's output head and input embedding |
| that cover the semantic codes. Read directly from safetensors, so |
| the 8B never has to be loaded.""" |
| from safetensors import safe_open |
| m3_dir = Path(m3_dir) |
| cands = [m3_dir / "language_model", m3_dir / "qwen_7B" / "qwen_7B"] |
| root = next((c for c in cands if (c / "model.safetensors.index.json") |
| .exists() or (c / "model.safetensors").exists()), None) |
| if root is None: |
| raise FileNotFoundError("language model safetensors not found " |
| f"under {m3_dir}") |
| idx = root / "model.safetensors.index.json" |
| if idx.exists(): |
| wm = json.loads(idx.read_text())["weight_map"] |
|
|
| def where(suffix): |
| key = next(k for k in wm if k.endswith(suffix)) |
| return root / wm[key], key |
| else: |
| with safe_open(str(root / "model.safetensors"), framework="pt") as f: |
| keys = list(f.keys()) |
|
|
| def where(suffix): |
| return root / "model.safetensors", \ |
| next(k for k in keys if k.endswith(suffix)) |
|
|
| def sl(suffix): |
| fpath, key = where(suffix) |
| with safe_open(str(fpath), framework="pt") as f: |
| return f.get_slice(key)[SEM_OFFSET: SEM_OFFSET + SEM_VOCAB] \ |
| .to(device, torch.bfloat16) |
| return sl("lm_head.weight"), sl("embed_tokens.weight") |
|
|
|
|
| |
| @torch.no_grad() |
| def encode_audio(dav, wav, device="cuda", chunk_s=12.0, overlap_s=2.0, |
| sr=44100, hop=512): |
| """wav [2, N] float32 @ 44.1 kHz -> latents [128, L] @ 86.13 Hz.""" |
| C, O = int(chunk_s * sr), (int(overlap_s * sr) // hop) * hop |
| outs, pos, N = [], 0, wav.shape[-1] |
| while pos < N: |
| end = min(pos + C, N) |
| st = max(0, pos - O) |
| z = dav.encode(wav[..., st:end].unsqueeze(0).to(device))[0] |
| outs.append(z[:, (pos - st) // hop:].cpu()) |
| pos = end |
| return torch.cat(outs, -1) |
|
|
|
|
| @torch.no_grad() |
| def read_states(trunk, head, z, device="cuda", max_frames=None): |
| """latents [128, L] -> states [T, 4096] @ 25 Hz, stitched from |
| overlapping 128-frame windows (hop 72).""" |
| z = z.to(device).float() |
| T_tot = int(z.shape[1] / 3.45) - 2 |
| T = T_tot - 1 if max_frames is None else min(max_frames, T_tot - 1) |
| if T < FRAMES + 2: |
| raise ValueError("audio too short (need > ~5.5 s)") |
| st = frame_latent_starts(T_tot) |
| hop = max(1, (FRAMES * 9) // 16) |
| acc = torch.zeros(T, H_DIM, device=device) |
| cnt = torch.zeros(T, 1, device=device) |
| offs = list(range(0, max(1, T - FRAMES + 1), hop)) |
| if offs[-1] != T - FRAMES: |
| offs.append(max(0, T - FRAMES)) |
| for o in offs: |
| b = st[o: o + FRAMES + 1] - st[o] |
| n = int(b[-1]) |
| if n > LATENT_WINDOW_MAX or st[o] + n > z.shape[1]: |
| continue |
| lat = torch.zeros(1, LATENT_WINDOW_MAX, LATENT_CHANNELS) |
| lat[0, :n] = z[:, int(st[o]): int(st[o]) + n].T |
| pl = torch.from_numpy(pool_matrix(b)).unsqueeze(0) |
| with torch.autocast("cuda", dtype=torch.bfloat16): |
| feats, _ = trunk(lat.to(device), pl.to(device)) |
| hw = head(feats[0].float()) |
| e = min(o + FRAMES, T) |
| acc[o: e] += hw[: e - o] |
| cnt[o: e] += 1 |
| return acc / cnt.clamp_min(1) |
|
|
|
|
| @torch.no_grad() |
| def states_to_streams(h, depth, W_head, W_embed, device="cuda", chunk=512): |
| """states [T, 4096] -> (codes [T, 8] long, condition [T, 32768]) |
| using the released M3 depth decoder: c0 by semantic-head argmax, |
| c1..c7 by the greedy chain, and the 8 hidden streams the renderer's |
| condition encoder expects, concatenated per frame.""" |
| codes_out, cond_out = [], [] |
| for s in range(0, h.shape[0], chunk): |
| hb = h[s: s + chunk].to(device, torch.bfloat16) |
| c0 = (hb @ W_head.T).float().argmax(-1) |
| seq = [depth.projection(hb).unsqueeze(1), |
| depth.projection(W_embed[c0]).unsqueeze(1)] |
| hiddens, cols = [hb], [c0] |
| for k in range(1, 8): |
| hid = depth(torch.cat(seq, dim=1))[:, -1] |
| hiddens.append(hid) |
| lg = depth.audio_heads[k - 1](hid).float() |
| idx = lg.argmax(-1).clamp(0, AC_VOCAB - 1) |
| cols.append(idx) |
| if k < 7: |
| emb = depth.audio_embeddings.weight[(k - 1) * AC_VOCAB + idx] |
| seq.append(depth.projection(emb.to(hb.dtype)).unsqueeze(1)) |
| codes_out.append(torch.stack(cols, dim=-1)) |
| cond_out.append(torch.cat(hiddens, dim=-1)) |
| return torch.cat(codes_out).cpu(), torch.cat(cond_out) |
|
|