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:
@@ -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")
|
||||
Reference in New Issue
Block a user