Files
mission-control-v2/client/lucy-tts/make_samples.py
T
Hitonabi 09a1c98514 Lucy-TTS/F5: Skripte + Batches versionieren, schwere Assets ignoriert
- pocket_server.py (Produktions-TTS mit Stimmen-Waechter), text_norm, Bench-/Diag-Skripte
- lucy-f5: f5_server/f5_test/bench_dml (DirectML-Experiment, Phase C/D offen)
- .gitignore: venvs/Modelle/Audio/Logs der beiden Ordner + box_recon/gemma_swap-Scratch

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:29:33 +02:00

65 lines
3.1 KiB
Python

# -*- coding: utf-8 -*-
"""make_samples.py — erzeugt Lucy-Hörproben auf dem Desktop, um A1 (safetensors-Cache) und
A2 (Referenz-Cleaning) hörbar zu testen. Nutzt 1:1 die Produktionslogik aus pocket_server.py
(cleanup + Doppel-Wächter _gen_gated), läuft offline mit dem lokal gecachten german_24l-Modell.
Output: %USERPROFILE%\\Desktop\\lucy_samples\\
lucy_clean_{kurz,mittel,lang}.wav (A2 AN: Highpass+Peak-Norm, Stimme aus safetensors-Cache)
lucy_raw_{kurz,mittel,lang}.wav (A2 AUS: Roh-Referenz, direkt geklont)
So hörst du den Cleaning-Unterschied direkt im A/B.
"""
import os, time
import numpy as np, soundfile as sf, librosa
from pocket_tts import TTSModel
import pocket_server as ps # reuse: _prep_ref, cleanup, _gen_gated, _fingerprint, Konstanten
DESK = os.path.join(os.path.expanduser("~"), "Desktop", "lucy_samples")
os.makedirs(DESK, exist_ok=True)
SENT = {
"kurz": "Hallo Commander, ich höre dich.",
"mittel": "Guten Morgen, Commander. Das Backup ist sauber durchgelaufen und es gab keine Fehler.",
"lang": "Natürlich kümmere ich mich darum, Commander. Ich starte den Dienst neu, prüfe die "
"Protokolle und melde mich, sobald alles wieder läuft.",
}
def build(ref_clean: bool, tag: str, use_cache: bool):
ps.REF_CLEAN = ref_clean # A2-Schalter zur Laufzeit setzen
ref = ps._prep_ref() # schreibt ref.wav (ggf. gecleant)
print(f"\n[{tag}] lade Modell {ps.LANG} (lsd={ps.LSD} temp={ps.TEMP} nc={ps.NOISE_CLAMP}) ...")
t0 = time.time()
m = TTSModel.load_model(language=ps.LANG, lsd_decode_steps=ps.LSD, temp=ps.TEMP,
noise_clamp=ps.NOISE_CLAMP, quantize=ps.QUANTIZE)
sr = m.sample_rate
print(f"[{tag}] Modell in {time.time()-t0:.1f}s (sr={sr})")
if use_cache: # A1: einmal exportieren, dann aus Cache laden
vs = m.get_state_for_audio_prompt(ref)
try:
ps.export_model_state(vs, ps.VOICE_ST)
vs = m.get_state_for_audio_prompt(ps.VOICE_ST)
print(f"[{tag}] Voice-State exportiert+geladen <- {os.path.basename(ps.VOICE_ST)}")
except Exception as e:
print(f"[{tag}] Export/Cache fehlgeschlagen, nutze Live-Klon: {e}")
else:
vs = m.get_state_for_audio_prompt(ref)
ref_audio, _ = librosa.load(ref, sr=sr, mono=True)
ps.STATE.update(m=m, vs=vs, sr=sr, ref_fp=ps._fingerprint(ref_audio, sr))
for name, text in SENT.items():
t0 = time.time()
a = ps.cleanup(ps._gen_gated(text), sr) # Produktions-Wächter + cleanup
dt = time.time() - t0
p = os.path.join(DESK, f"lucy_{tag}_{name}.wav")
sf.write(p, a, sr)
print(f"[{tag}] {name:6} gen={dt:5.2f}s audio={a.size/sr:4.1f}s -> {p}")
ps.STATE.clear()
del m
if __name__ == "__main__":
print("=== A2 AN (Highpass+Norm) + A1 safetensors-Cache ===")
build(ref_clean=True, tag="clean", use_cache=True)
print("\n=== A2 AUS (Roh-Referenz, direkt geklont) ===")
build(ref_clean=False, tag="raw", use_cache=False)
print(f"\nFERTIG. Samples liegen in: {DESK}")