ManyOtherFunctions's picture
Upload 17 files
9040f4d verified
Raw History Blame Contribute Delete
4.48 kB
"""Command-line orchestration: tokenize, release the encoder, then render."""
import argparse
import gc
import os
from pathlib import Path
import numpy as np
import torch
from . import __version__
from .audio import load_audio, save_wav_atomic
from .quantizer import WannabeTokenizer
from .renderer import YuE2Renderer
def positive_int(value: str) -> int:
try:
parsed = int(value)
except ValueError as exc:
raise argparse.ArgumentTypeError("must be an integer") from exc
if str(parsed) != value or parsed < 1:
raise argparse.ArgumentTypeError("must be a positive integer without extra characters")
return parsed
def save_tokens_atomic(path: Path, tokens: np.ndarray) -> None:
temporary = path.with_name(path.name + ".tmp")
try:
with temporary.open("wb") as f:
np.save(f, tokens, allow_pickle=False)
os.replace(temporary, path)
finally:
if temporary.exists():
temporary.unlink()
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Tokenize one audio file with WannabeTokenizer and reconstruct it with YuE2."
)
parser.add_argument("input", type=Path, help="Input audio file")
parser.add_argument("--tokenizer", required=True, type=Path, help="Unified WannabeTokenizer .safetensors file")
parser.add_argument("--yue2-checkpoint", required=True, type=Path, help="YuE2 bf16 all-in-one .safetensors file")
parser.add_argument("--output-dir", type=Path, default=Path("output"), help="Destination folder (default: output)")
parser.add_argument("--device", default="auto", help="Torch device, for example auto, cuda, cuda:0, or cpu")
parser.add_argument("--seed", type=int, default=0, help="NAR noise seed (default: 0)")
parser.add_argument("--steps", type=positive_int, default=32, help="Flow-matching steps (default: 32)")
parser.add_argument("--batch-windows", type=positive_int, default=8, help="Tokenizer windows per batch (default: 8)")
parser.add_argument("--overwrite", action="store_true", help="Replace existing tokens.npy and reconstructed.wav")
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
return parser
def main() -> None:
args = build_parser().parse_args()
audio_path = args.input.expanduser()
tokenizer_path = args.tokenizer.expanduser()
yue2_path = args.yue2_checkpoint.expanduser()
output_dir = args.output_dir.expanduser()
for label, path in (
("Input audio", audio_path),
("Tokenizer checkpoint", tokenizer_path),
("YuE2 checkpoint", yue2_path),
):
if not path.is_file():
raise FileNotFoundError(f"{label} not found: {path}")
device = "cuda" if args.device == "auto" and torch.cuda.is_available() else args.device
if device == "auto":
device = "cpu"
if device.startswith("cuda") and not torch.cuda.is_available():
raise RuntimeError("A CUDA device was requested, but CUDA is not available")
if device == "cpu":
print("Warning: YuE2 reconstruction on CPU is very slow and requires substantial RAM.")
tokens_path = output_dir / "tokens.npy"
audio_output_path = output_dir / "reconstructed.wav"
outputs = (tokens_path, audio_output_path)
if not args.overwrite:
existing = [str(path) for path in outputs if path.exists()]
if existing:
raise FileExistsError("Output already exists (use --overwrite to replace): " + ", ".join(existing))
output_dir.mkdir(parents=True, exist_ok=True)
waveform = load_audio(audio_path)
print(f"Tokenizing on {device}...")
tokenizer = WannabeTokenizer(tokenizer_path, device=device)
tokens = tokenizer.tokenize(waveform, batch_windows=args.batch_windows)
save_tokens_atomic(tokens_path, tokens)
print(f"Wrote {tokens_path} ({len(tokens)} frames, {len(tokens) / 25:.2f} seconds)")
# The encoder and renderer do not need to coexist in accelerator memory.
del tokenizer
gc.collect()
if device.startswith("cuda"):
torch.cuda.empty_cache()
print(f"Reconstructing on {device} with seed {args.seed} and {args.steps} steps...")
renderer = YuE2Renderer(yue2_path, device=device)
reconstructed = renderer.reconstruct(tokens, seed=args.seed, steps=args.steps)
save_wav_atomic(audio_output_path, reconstructed)
print(f"Wrote {audio_output_path} (48 kHz stereo WAV)")