Download wannabe_tokenizer/cli.py from ManyOtherFunctions/Yue2-WannabeTokenizer: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/ManyOtherFunctions/Yue2-WannabeTokenizer/resolve/main/wannabe_tokenizer/cli.py
- Command line
-
hf download hf://ManyOtherFunctions/Yue2-WannabeTokenizer/wannabe_tokenizer/cli.py
-
curl -L -o cli.py https://huggingface.co/ManyOtherFunctions/Yue2-WannabeTokenizer/resolve/main/wannabe_tokenizer/cli.py
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)") | |