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
+57
View File
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""Warum 'manchmal zu laut' + 'Stimme verändert sich am Anfang'? Stream-Kopf-Logik N× laufen lassen
und Gain, Crop-Punkt, Kopf-Segmente, Whisper-Start, rms/peak protokollieren."""
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. "
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 run_once(text):
chunks=[]
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)
chunks.append(np.asarray(c,dtype=np.float32).reshape(-1))
full=np.concatenate(chunks)
# Kopf adaptiv (wie Server): bis >=2 Segmente oder 2.5s
head=[]; hl=0; head_arr=None
for c in chunks:
head.append(c); hl+=c.size
if hl < int(0.5*sr): continue
a=np.concatenate(head)
if len(librosa.effects.split(a, top_db=35))>=2 or hl>=int(2.5*sr):
head_arr=a; break
if head_arr is None: head_arr=np.concatenate(head)
iv=librosa.effects.split(head_arr, top_db=35)
segs=[(round(s/sr,2),round(e/sr,2)) for s,e in iv]
# Gain aus voiced (aktuelle Server-Logik)
voiced=np.concatenate([head_arr[s:e] for s,e in iv]) if len(iv) else head_arr
rms=float(np.sqrt(np.mean(voiced**2))) or 1e-9
gain=TARGET_RMS/rms
# Crop (aktuell): kurz vor echtem Wort
if len(iv)>=2:
cut=max(iv[0][1], iv[1][0]-int(0.06*sr)); cropped=full[cut:]
else:
cropped=full
out=np.clip(cropped*gain,-0.95,0.95)
sf.write(os.path.join(BASE,"out_diag_s.wav"), out, sr)
seg,_=w.transcribe(os.path.join(BASE,"out_diag_s.wav"), language="de", beam_size=5)
start=(" ".join(x.text for x in seg)).strip()[:30]
return dict(segs=segs, voiced_rms=round(rms,3), gain=round(gain,2),
out_rms=round(float(np.sqrt(np.mean(out**2))),3), out_peak=round(float(np.abs(out).max()),3),
start=start)
for text in TEXTS:
print(f"=== {text[:30]!r} ===")
for i in range(4):
print(f" run{i}:", run_once(text))
print("DIAG2_DONE")