Загрузка данных
from pathlib import Path
import os
import sys
import inspect
import subprocess
import textwrap
import numpy as np
import torch
import soundfile as sf
# ============================================================
# CONFIG
# ============================================================
COSYVOICE_ROOT = Path(r"C:\Users\Geodezik\image_generation\CosyVoice_official").resolve()
MATCHA_ROOT = COSYVOICE_ROOT / "third_party" / "Matcha-TTS"
MODEL_DIR = COSYVOICE_ROOT / "pretrained_models" / "Fun-CosyVoice3-0.5B"
PROMPT_WAV = COSYVOICE_ROOT / "asset" / "zero_shot_prompt.wav"
OUT_DIR = COSYVOICE_ROOT / "_deep_diag_outputs"
OUT_DIR.mkdir(parents=True, exist_ok=True)
sys.path.insert(0, str(MATCHA_ROOT))
sys.path.insert(0, str(COSYVOICE_ROOT))
# ============================================================
# PRINT HELPERS
# ============================================================
def header(s):
print()
print("=" * 100)
print(s)
print("=" * 100)
def section(s):
print()
print("-" * 100)
print(s)
print("-" * 100)
# ============================================================
# ENV INFO
# ============================================================
header("ENV")
print("python:", sys.executable)
print("cwd:", os.getcwd())
print("COSYVOICE_ROOT:", COSYVOICE_ROOT)
print("MODEL_DIR:", MODEL_DIR)
print("PROMPT_WAV:", PROMPT_WAV)
print("torch:", torch.__version__)
print("torch file:", torch.__file__)
print("cuda available:", torch.cuda.is_available())
if torch.cuda.is_available():
print("cuda device:", torch.cuda.get_device_name(0))
print("cuda capability:", torch.cuda.get_device_capability(0))
print("torch.version.cuda:", torch.version.cuda)
try:
import torchaudio
print("torchaudio:", torchaudio.__version__)
print("torchaudio file:", torchaudio.__file__)
except Exception as e:
print("torchaudio import error:", repr(e))
try:
import onnxruntime as ort
print("onnxruntime:", ort.__version__)
print("onnxruntime providers:", ort.get_available_providers())
except Exception as e:
print("onnxruntime import error:", repr(e))
try:
import soxr
print("soxr:", soxr.__version__)
except Exception as e:
print("soxr not available:", repr(e))
try:
import scipy
print("scipy:", scipy.__version__)
except Exception as e:
print("scipy not available:", repr(e))
# ============================================================
# IMPORT CHECK
# ============================================================
header("IMPORT CHECK")
import cosyvoice
import cosyvoice.cli.cosyvoice as cosyvoice_cli
import cosyvoice.cli.frontend as frontend
import cosyvoice.utils.file_utils as file_utils
print("cosyvoice:", cosyvoice.__file__)
print("cosyvoice cli:", cosyvoice_cli.__file__)
print("frontend:", frontend.__file__)
print("file_utils:", file_utils.__file__)
expected = str(COSYVOICE_ROOT / "cosyvoice").lower()
actual = str(Path(cosyvoice.__file__).parent).lower()
print("expected package dir:", expected)
print("actual package dir:", actual)
if not actual.startswith(expected):
raise RuntimeError("WRONG COSYVOICE IMPORT PATH")
# ============================================================
# SIGNATURES
# ============================================================
header("SIGNATURES")
from cosyvoice.cli.cosyvoice import AutoModel
print("AutoModel:", AutoModel)
print("AutoModel signature:")
try:
print(inspect.signature(AutoModel))
except Exception as e:
print("signature error:", repr(e))
for name in [
"CosyVoice",
"CosyVoice2",
"CosyVoice3",
]:
if hasattr(cosyvoice_cli, name):
cls = getattr(cosyvoice_cli, name)
print()
print(name, cls)
try:
print("__init__:", inspect.signature(cls.__init__))
except Exception as e:
print("__init__ signature error:", repr(e))
# ============================================================
# MODEL CONFIG
# ============================================================
header("MODEL CONFIG FILES")
for name in [
"cosyvoice.yaml",
"cosyvoice2.yaml",
"cosyvoice3.yaml",
"configuration.json",
"config.json",
]:
p = MODEL_DIR / name
print()
print(name, "exists:", p.exists())
if p.exists():
txt = p.read_text(encoding="utf-8", errors="ignore")
print(txt[:4000])
# ============================================================
# PROMPT AUDIO INSPECT
# ============================================================
header("PROMPT AUDIO")
x, sr = sf.read(str(PROMPT_WAV), dtype="float32", always_2d=True)
print("sr:", sr)
print("shape:", x.shape)
print("duration:", x.shape[0] / sr)
print("min/max/std:", float(x.min()), float(x.max()), float(x.std()))
# ============================================================
# RESAMPLERS
# ============================================================
def resample_with_soxr_or_scipy(audio_1d_np: np.ndarray, sr: int, target_sr: int):
if sr == target_sr:
return audio_1d_np.astype(np.float32, copy=False)
try:
import soxr
y = soxr.resample(audio_1d_np, sr, target_sr)
return y.astype(np.float32, copy=False)
except Exception as e:
print("[resample] soxr failed:", repr(e))
try:
from scipy.signal import resample_poly
from math import gcd
g = gcd(sr, target_sr)
up = target_sr // g
down = sr // g
y = resample_poly(audio_1d_np, up, down)
return y.astype(np.float32, copy=False)
except Exception as e:
print("[resample] scipy failed:", repr(e))
# final fallback: torchaudio
import torchaudio.functional as AF
t = torch.from_numpy(audio_1d_np[None, :].astype(np.float32))
y = AF.resample(t, sr, target_sr).squeeze(0).numpy()
return y.astype(np.float32, copy=False)
def make_loader(loader_name: str):
def load_wav_no_torchcodec(wav, target_sr, min_sr=16000):
wav = Path(wav)
audio, sr = sf.read(str(wav), dtype="float32", always_2d=True)
# [samples, channels] -> mono np
mono = audio.mean(axis=1).astype(np.float32, copy=False)
print()
print(f"[{loader_name}]")
print(" file:", wav)
print(" target_sr:", target_sr)
print(" original sr:", sr)
print(" original shape:", audio.shape)
print(" duration:", len(mono) / sr)
print(" np min/max/std:", float(mono.min()), float(mono.max()), float(mono.std()))
if sr < min_sr:
raise ValueError(f"sample rate {sr} is lower than min_sr {min_sr}")
if loader_name == "soundfile_soxr_scipy":
mono = resample_with_soxr_or_scipy(mono, sr, target_sr)
speech = torch.from_numpy(mono).unsqueeze(0)
else:
import torchaudio.functional as AF
speech = torch.from_numpy(mono).unsqueeze(0)
if sr != target_sr:
speech = AF.resample(speech, sr, target_sr)
print(" out shape:", tuple(speech.shape))
print(" out duration:", speech.shape[1] / target_sr)
print(" out min/max/std:", speech.min().item(), speech.max().item(), speech.std().item())
return speech
return load_wav_no_torchcodec
# ============================================================
# SAVE
# ============================================================
def save_outs(outs, output_path, sample_rate):
outs = list(outs)
print("chunks:", len(outs))
if not outs:
raise RuntimeError("No outputs")
for i, o in enumerate(outs):
t = o["tts_speech"]
print(f"chunk {i}: shape={tuple(t.shape)} min={t.min().item():.6f} max={t.max().item():.6f} std={t.std().item():.6f}")
wav = torch.cat([o["tts_speech"] for o in outs], dim=1)
print("concat:", tuple(wav.shape))
print("sr:", sample_rate)
print("duration:", wav.shape[1] / sample_rate)
print("min/max/std:", wav.min().item(), wav.max().item(), wav.std().item())
audio = wav.detach().float().cpu().squeeze(0).numpy()
audio = np.clip(audio, -1.0, 1.0)
sf.write(str(output_path), audio, sample_rate)
print("saved:", output_path)
# ============================================================
# MODEL BUILD PARAMS
# ============================================================
def build_model_with_best_effort(force_cpu=False, fp16_value=None):
kwargs = {}
sig = None
try:
sig = inspect.signature(AutoModel)
except Exception:
sig = None
if sig is not None:
params = sig.parameters
if "fp16" in params and fp16_value is not None:
kwargs["fp16"] = fp16_value
if "device" in params and force_cpu:
kwargs["device"] = "cpu"
if "load_jit" in params:
kwargs["load_jit"] = False
if "load_trt" in params:
kwargs["load_trt"] = False
if "load_vllm" in params:
kwargs["load_vllm"] = False
print("AutoModel kwargs:", kwargs)
if force_cpu:
# Дополнительная страховка: прячем CUDA уже поздно, но некоторые компоненты смотрят на torch.cuda.
print("force_cpu requested")
model = AutoModel(model_dir=str(MODEL_DIR), **kwargs)
return model
# ============================================================
# SINGLE TEST
# ============================================================
def run_test(test_name, loader_name="soundfile_torchaudio", force_cpu=False, fp16_value=None):
header(f"RUN {test_name}")
# patch loader
loader = make_loader(loader_name)
file_utils.load_wav = loader
frontend.load_wav = loader
print("loader:", loader_name)
print("force_cpu:", force_cpu)
print("fp16_value:", fp16_value)
model = build_model_with_best_effort(force_cpu=force_cpu, fp16_value=fp16_value)
print("model object:", type(model))
print("sample_rate:", model.sample_rate)
# Если force_cpu и объект имеет .model, пробуем насильно перевести torch-модули на CPU.
if force_cpu:
try:
if hasattr(model, "model"):
model.model = model.model.cpu()
print("model.model moved to CPU")
except Exception as e:
print("moving model.model to CPU failed:", repr(e))
outs = model.inference_zero_shot(
"八百标兵奔北坡,北坡炮兵并排跑,炮兵怕把标兵碰,标兵怕碰炮兵炮。",
"You are a helpful assistant.<|endofprompt|>希望你以后能够做的比我还好呦。",
str(PROMPT_WAV),
stream=False,
speed=1.0,
)
output_path = OUT_DIR / f"{test_name}.wav"
save_outs(outs, output_path, model.sample_rate)
del model
if torch.cuda.is_available():
torch.cuda.empty_cache()
# ============================================================
# RUN TESTS
# ============================================================
tests = [
# Базовый тест как раньше, но с новым скриптом.
("01_cuda_default_torchaudio_resample", "soundfile_torchaudio", False, None),
# Принудительно fp16=False, если AutoModel это поддерживает.
("02_cuda_fp16_false_torchaudio_resample", "soundfile_torchaudio", False, False),
# Другой resampler без torchaudio.functional.
("03_cuda_fp16_false_soxr_scipy_resample", "soundfile_soxr_scipy", False, False),
]
for test_name, loader_name, force_cpu, fp16_value in tests:
try:
run_test(test_name, loader_name, force_cpu, fp16_value)
except Exception as e:
print()
print("[TEST FAILED]", test_name)
print(type(e).__name__, str(e))
print()
print("=" * 100)
print("DONE")
print("=" * 100)
print("Outputs:", OUT_DIR)
print()
print("Теперь прослушай:")
for p in sorted(OUT_DIR.glob("*.wav")):
print(" ", p)