Files
ACE-Step/infer.py
T
Leonid PershinandClaude Opus 5 e9ea6b9bab feat: generation options for infer.py, and split out training deps
infer.py could only render a random example from examples/input_params:
there was no way to pass your own prompt, lyrics, duration or seed, so
using it for anything specific meant writing a separate script. Add
--prompt, --lyrics/--lyrics_file, --duration, --steps, --guidance_scale,
--scheduler, --cfg_type, --omega_scale, --seed and --format alongside the
existing runtime flags. Without --prompt the old random-example behaviour
is kept, so existing invocations are unaffected.

Also move the training-only packages out of the default install.
datasets, pytorch_lightning, matplotlib, tensorboard and tensorboardX are
imported by trainer.py and convert2hf_dataset.py, never on the inference
path, but every user was installing them — and datasets==3.4.1 is a hard
pin that drags constraints onto huggingface-hub. They now live in
requirements-train.txt behind the existing (previously ineffective)
"train" extra: pip install -e ".[train]".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 20:31:07 +03:00

200 lines
6.4 KiB
Python

import click
import os
from acestep.pipeline_ace_step import ACEStepPipeline
from acestep.data_sampler import DataSampler
def sample_data(json_data):
return (
json_data["audio_duration"],
json_data["prompt"],
json_data["lyrics"],
json_data["infer_step"],
json_data["guidance_scale"],
json_data["scheduler_type"],
json_data["cfg_type"],
json_data["omega_scale"],
", ".join(map(str, json_data["actual_seeds"])),
json_data["guidance_interval"],
json_data["guidance_interval_decay"],
json_data["min_guidance_scale"],
json_data["use_erg_tag"],
json_data["use_erg_lyric"],
json_data["use_erg_diffusion"],
", ".join(map(str, json_data["oss_steps"])),
json_data["guidance_scale_text"] if "guidance_scale_text" in json_data else 0.0,
(
json_data["guidance_scale_lyric"]
if "guidance_scale_lyric" in json_data
else 0.0
),
)
@click.command()
@click.option(
"--checkpoint_path", type=str, default="", help="Path to the checkpoint directory"
)
@click.option("--bf16", type=bool, default=True, help="Whether to use bfloat16")
@click.option(
"--torch_compile", type=bool, default=False, help="Whether to use torch compile"
)
@click.option(
"--cpu_offload", type=bool, default=False, help="Whether to use CPU offloading (only load current stage's model to GPU)"
)
@click.option(
"--overlapped_decode", type=bool, default=False, help="Whether to use overlapped decoding (run dcae and vocoder using sliding windows)"
)
@click.option("--device_id", type=int, default=0, help="Device ID to use")
@click.option("--output_path", type=str, default=None, help="Path to save the output")
# --- generation parameters -------------------------------------------------
# Without --prompt the script keeps its original behaviour and generates from a
# random example in examples/input_params.
@click.option(
"--prompt",
type=str,
default=None,
help="Style tags or description, comma separated. If omitted, a random example is used.",
)
@click.option("--lyrics", type=str, default=None, help="Lyrics, with [verse]/[chorus] tags.")
@click.option(
"--lyrics_file",
type=click.Path(exists=True, dir_okay=False),
default=None,
help="Read lyrics from a UTF-8 file. Takes precedence over --lyrics.",
)
@click.option("--duration", type=float, default=60.0, help="Audio duration in seconds (-1 for random).")
@click.option("--steps", type=int, default=60, help="Number of inference steps.")
@click.option("--guidance_scale", type=float, default=15.0, help="Guidance scale.")
@click.option(
"--scheduler",
type=click.Choice(["euler", "heun", "pingpong"]),
default="euler",
help="Scheduler type.",
)
@click.option(
"--cfg_type",
type=click.Choice(["cfg", "apg", "cfg_star"]),
default="apg",
help="CFG type. apg is recommended.",
)
@click.option("--omega_scale", type=float, default=10.0, help="Granularity scale.")
@click.option("--seed", type=str, default=None, help="Manual seeds, comma separated. Random if omitted.")
@click.option(
"--format",
"audio_format",
type=click.Choice(["wav", "mp3", "ogg", "flac"]),
default="wav",
help="Output audio format.",
)
def main(
checkpoint_path,
bf16,
torch_compile,
cpu_offload,
overlapped_decode,
device_id,
output_path,
prompt,
lyrics,
lyrics_file,
duration,
steps,
guidance_scale,
scheduler,
cfg_type,
omega_scale,
seed,
audio_format,
):
os.environ["CUDA_VISIBLE_DEVICES"] = str(device_id)
model_demo = ACEStepPipeline(
checkpoint_dir=checkpoint_path,
dtype="bfloat16" if bf16 else "float32",
torch_compile=torch_compile,
cpu_offload=cpu_offload,
overlapped_decode=overlapped_decode
)
if lyrics_file is not None:
with open(lyrics_file, "r", encoding="utf-8") as f:
lyrics = f.read()
if prompt is None:
# No prompt given: keep the original behaviour and use a random example.
data_sampler = DataSampler()
json_data = data_sampler.sample()
(
audio_duration,
prompt,
sampled_lyrics,
infer_step,
sampled_guidance_scale,
scheduler_type,
sampled_cfg_type,
sampled_omega_scale,
manual_seeds,
guidance_interval,
guidance_interval_decay,
min_guidance_scale,
use_erg_tag,
use_erg_lyric,
use_erg_diffusion,
oss_steps,
guidance_scale_text,
guidance_scale_lyric,
) = sample_data(json_data)
# Explicit options still win over the sampled example.
lyrics = lyrics if lyrics is not None else sampled_lyrics
manual_seeds = seed if seed is not None else manual_seeds
else:
audio_duration = duration
lyrics = lyrics if lyrics is not None else "[instrumental]"
infer_step = steps
sampled_guidance_scale = guidance_scale
scheduler_type = scheduler
sampled_cfg_type = cfg_type
sampled_omega_scale = omega_scale
manual_seeds = seed
guidance_interval = 0.5
guidance_interval_decay = 0.0
min_guidance_scale = 3.0
use_erg_tag = True
use_erg_lyric = True
use_erg_diffusion = True
oss_steps = None
guidance_scale_text = 0.0
guidance_scale_lyric = 0.0
click.echo(f"prompt: {prompt}")
click.echo(f"duration: {audio_duration}s, steps: {infer_step}, seeds: {manual_seeds}")
model_demo(
format=audio_format,
audio_duration=audio_duration,
prompt=prompt,
lyrics=lyrics,
infer_step=infer_step,
guidance_scale=sampled_guidance_scale,
scheduler_type=scheduler_type,
cfg_type=sampled_cfg_type,
omega_scale=sampled_omega_scale,
manual_seeds=manual_seeds,
guidance_interval=guidance_interval,
guidance_interval_decay=guidance_interval_decay,
min_guidance_scale=min_guidance_scale,
use_erg_tag=use_erg_tag,
use_erg_lyric=use_erg_lyric,
use_erg_diffusion=use_erg_diffusion,
oss_steps=oss_steps,
guidance_scale_text=guidance_scale_text,
guidance_scale_lyric=guidance_scale_lyric,
save_path=output_path,
)
if __name__ == "__main__":
main()