- 删除 03_gen_tts_gptsovits.py、00_transcribe_refs.py、ref_texts.json - 删除 02_gen_tts_fishspeech.py 及 tts/v2_fishspeech/ 生成结果 - 保留 VoxCPM2 / ACE-Step / Stable Audio 三条链路 - 更新执行文档和技术报告,清除两款 TTS 的所有引用 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
步骤 4:音乐 v1 — ACE-Step 1.5 批量生成
|
|
输出目录:audio_1/music/v1_acestep/{music,sfx,ambience}/
|
|
|
|
使用方法:
|
|
conda activate acestep
|
|
python audio_1/scripts/04_gen_music_acestep.py [--dry-run]
|
|
"""
|
|
import os, sys, time, argparse, subprocess
|
|
|
|
import torch
|
|
torch.backends.cuda.preferred_blas_library("cublaslt")
|
|
|
|
PROJECT_DIR = "/home/xsl/blind"
|
|
REPO_DIR = "/home/xsl/tools/ACE-Step-1.5"
|
|
OUT_MUSIC = f"{PROJECT_DIR}/audio_1/music/v1_acestep/music"
|
|
OUT_SFX = f"{PROJECT_DIR}/audio_1/music/v1_acestep/sfx"
|
|
OUT_AMB = f"{PROJECT_DIR}/audio_1/music/v1_acestep/ambience"
|
|
|
|
for d in (OUT_MUSIC, OUT_SFX, OUT_AMB):
|
|
os.makedirs(d, exist_ok=True)
|
|
|
|
sys.path.insert(0, REPO_DIR)
|
|
sys.path.insert(0, f"{PROJECT_DIR}/audio")
|
|
|
|
# 复用原脚本中的 TRACKS 和 SFX 定义
|
|
from gen_music_acestep import BGM_TRACKS, RINGTONES
|
|
from gen_sfx_acestep import SFX_TRACKS, AMBIENCE_TRACKS
|
|
|
|
def wav_to_mp3(src, dst):
|
|
subprocess.run(["ffmpeg", "-y", "-i", src, "-codec:a", "libmp3lame",
|
|
"-b:a", "128k", "-ar", "44100", "-ac", "2", dst],
|
|
check=True, capture_output=True)
|
|
|
|
# ──────────────────────────────────────────────────────────────
|
|
def load_handler():
|
|
from acestep.handler import AceStepHandler
|
|
print("加载 ACE-Step 模型...", end=" ", flush=True)
|
|
t0 = time.time()
|
|
handler = AceStepHandler()
|
|
handler.initialize_service(project_root=REPO_DIR, config_path="acestep-v15-turbo", device="cuda")
|
|
print(f"就绪 ({time.time()-t0:.1f}s)")
|
|
return handler
|
|
|
|
def generate_one(handler, track, out_dir):
|
|
from acestep.inference import GenerationParams, GenerationConfig, generate_music
|
|
filename = track["filename"]
|
|
out_wav = os.path.join(out_dir, f"{filename}_v1.wav")
|
|
out_mp3 = os.path.join(out_dir, f"{filename}_v1.mp3")
|
|
|
|
if os.path.exists(out_mp3):
|
|
print(f" [SKIP] {filename}")
|
|
return "skip"
|
|
|
|
note = track.get("note", "")
|
|
print(f" [{filename}] {track['duration']}s {note} ...", end=" ", flush=True)
|
|
|
|
params = GenerationParams(
|
|
caption=track["caption"],
|
|
lyrics=track.get("lyrics", ""),
|
|
duration=track["duration"],
|
|
instrumental=True,
|
|
inference_steps=track.get("steps", 20),
|
|
seed=track.get("seed", 42),
|
|
)
|
|
config = GenerationConfig(batch_size=1, audio_format="wav")
|
|
t0 = time.time()
|
|
try:
|
|
result = generate_music(handler, None, params, config, save_dir=out_dir)
|
|
elapsed = time.time() - t0
|
|
if result.success:
|
|
gen_path = result.audios[0]["path"]
|
|
if gen_path != out_wav:
|
|
os.rename(gen_path, out_wav)
|
|
wav_to_mp3(out_wav, out_mp3)
|
|
size = os.path.getsize(out_mp3) // 1024
|
|
print(f"ok {elapsed:.1f}s {size}KB")
|
|
return "ok"
|
|
else:
|
|
print(f"FAIL: {result.error}"); return "fail"
|
|
except Exception as e:
|
|
print(f"FAIL: {e}"); return "fail"
|
|
|
|
# ──────────────────────────────────────────────────────────────
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--dry-run", action="store_true")
|
|
parser.add_argument("--category", choices=["music","sfx","ambience","all"], default="all")
|
|
args = parser.parse_args()
|
|
|
|
all_tasks = []
|
|
if args.category in ("music", "all"):
|
|
all_tasks += [(t, OUT_MUSIC) for t in BGM_TRACKS + RINGTONES]
|
|
if args.category in ("sfx", "all"):
|
|
all_tasks += [(t, OUT_SFX) for t in SFX_TRACKS]
|
|
if args.category in ("ambience", "all"):
|
|
all_tasks += [(t, OUT_AMB) for t in AMBIENCE_TRACKS]
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f" 音乐 v1 — ACE-Step ({len(all_tasks)} 条)")
|
|
print(f"{'='*60}\n")
|
|
|
|
if args.dry_run:
|
|
for t, d in all_tasks:
|
|
mp3 = os.path.join(d, f"{t['filename']}_v1.mp3")
|
|
done = "✓" if os.path.exists(mp3) else "·"
|
|
print(f" [{done}] {t['filename']} {t['duration']}s → {os.path.basename(d)}/")
|
|
return
|
|
|
|
handler = load_handler()
|
|
ok = fail = skip = 0
|
|
for track, out_dir in all_tasks:
|
|
r = generate_one(handler, track, out_dir)
|
|
if r == "ok": ok += 1
|
|
elif r == "skip": skip += 1
|
|
else: fail += 1
|
|
|
|
print(f"\n完成: {ok} ok, {skip} skip, {fail} fail")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|