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>
This commit is contained in:
Hitonabi
2026-07-02 10:29:33 +02:00
parent aff0105700
commit 09a1c98514
49 changed files with 8231 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
"""Test der ROBUSTEN Stream-Kopf-Logik: fester 1.8s-Kopf + Crop am ersten SUBSTANZIELLEN Wort-Segment
(ignoriert Mini-Blips) + FIXER Gain. Ziel: Start immer echtes Wort, out_rms stabil ~0.09, kein Clip."""
import os, numpy as np, librosa, soundfile as sf
from pocket_tts import TTSModel
from faster_whisper import WhisperModel
BASE = r"F:\Coding Stuff\mission-control-2\client\lucy-tts"
src,_ = librosa.load(os.path.join(BASE,"ref.mp3"), sr=24000, mono=True)
srt,_ = librosa.effects.trim(src, top_db=30); ref=os.path.join(BASE,"ref.wav")
sf.write(ref, srt[:int(18*24000)], 24000)
TARGET_RMS=0.09; LEAD="Tja. "
BASE_GAIN = TARGET_RMS / 0.18 # ~0.5 (raw full-rms ~0.18 konsistent)
GMIN, GMAX = 0.7*BASE_GAIN, 1.4*BASE_GAIN # Gain-Clamp -> kann NIE explodieren
HEAD_FIXED = int(2.0*24000)
m = TTSModel.load_model(language="german_24l", lsd_decode_steps=6, temp=0.9)
vs = m.get_state_for_audio_prompt(ref); sr=m.sample_rate
w = WhisperModel("small", device="cpu", compute_type="int8")
TEXTS = ["Hallo Commander, ich höre dich.",
"Natürlich, Commander. Das Backup ist sauber durchgelaufen."]
def crop_and_gain(a):
"""Crop am ersten SUBSTANZIELLEN Wort-Segment nach dem Lead (>=0.2s, ignoriert Mini-Blips);
Gain aus voiced-rms, GECLAMPT (kann nie explodieren)."""
iv = librosa.effects.split(a, top_db=35)
voiced = np.concatenate([a[s:e] for s,e in iv]) if len(iv) else a
rms = float(np.sqrt(np.mean(voiced**2))) or 1e-9
gain = float(np.clip(TARGET_RMS/rms, GMIN, GMAX))
cut = 0
if len(iv) >= 2:
for k in range(1, len(iv)):
if (iv[k][1]-iv[k][0]) >= int(0.20*sr):
cut = max(iv[k-1][1], iv[k][0]-int(0.06*sr)); break
return a[cut:], round(cut/sr,2), round(gain,2)
def run_once(text):
head=[]; hl=0; head_done=False; head_arr=None; rest=[]
for c in m.generate_audio_stream(vs, LEAD+text, frames_after_eos=4):
c=c.numpy() if hasattr(c,"numpy") else np.asarray(c); c=np.asarray(c,dtype=np.float32).reshape(-1)
if not head_done:
head.append(c); hl+=c.size
if hl>=HEAD_FIXED: head_arr=np.concatenate(head); head_done=True
else: rest.append(c)
if head_arr is None: head_arr=np.concatenate(head)
cropped_head, cutpt, gain = crop_and_gain(head_arr)
full = np.concatenate([cropped_head]+rest)
out=np.clip(full*gain,-0.95,0.95)
sf.write(os.path.join(BASE,"out_diag_s3.wav"), out, sr)
seg,_=w.transcribe(os.path.join(BASE,"out_diag_s3.wav"), language="de", beam_size=5)
start=(" ".join(x.text for x in seg)).strip()[:32]
return dict(cut=cutpt, gain=gain, out_rms=round(float(np.sqrt(np.mean(out**2))),3),
out_peak=round(float(np.abs(out).max()),3), start=start)
print(f"gain-clamp=[{round(GMIN,2)},{round(GMAX,2)}] head={HEAD_FIXED/24000}s")
for text in TEXTS:
print(f"=== {text[:28]!r} ===")
for i in range(5):
print(f" run{i}:", run_once(text))
print("DIAG3_DONE")