ux(voice_loop): Aufnahmedauer anzeigen + korrekt berechnen; Diagnose-Skript
- record_utterance zeigt nach der Aufnahme die Dauer - Dauer aus tatsaechlicher Datenmenge statt WAV-Header (arecord schreibt beim Stoppen per Signal eine falsche Header-Laenge -> sonst absurde Werte wie 67108s) - scripts/diag_record.py: Countdown + gruenes Signal -> Aufnahme -> sofort transkribieren (Standard + vad_filter) zur Pausen-Diagnose Diagnose-Ergebnis: Mehrsatz-Aeusserungen mit Pausen werden vollstaendig erkannt (Aufnahme + faster-whisper verlieren nichts) - im kontrollierten Test und im echten voice_loop-Flow bestaetigt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c25f081f9f
commit
b75382e055
2 changed files with 86 additions and 0 deletions
73
scripts/diag_record.py
Normal file
73
scripts/diag_record.py
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Diagnose: Countdown + grünes Signal -> Aufnahme -> sofort transkribieren.
|
||||||
|
|
||||||
|
Ein Durchlauf, kein Timing-Problem, keine Verwechslung der Datei. Zeigt die
|
||||||
|
WAV-Dauer und die faster-whisper-Segmente (Standard und mit VAD-Filter), damit man
|
||||||
|
sieht, ob Sätze nach Pausen verloren gehen.
|
||||||
|
|
||||||
|
.venv/bin/python scripts/diag_record.py
|
||||||
|
.venv/bin/python scripts/diag_record.py --device plughw:5,0 --seconds 20 --model base
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import wave
|
||||||
|
|
||||||
|
GREEN = "\033[1;32m"
|
||||||
|
RED = "\033[1;31m"
|
||||||
|
RESET = "\033[0m"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--device", default="plughw:5,0", help="Aufnahmegerät (arecord -L)")
|
||||||
|
ap.add_argument("--seconds", type=int, default=20)
|
||||||
|
ap.add_argument("--rate", type=int, default=16000)
|
||||||
|
ap.add_argument("--model", default="base", help="tiny|base|small|medium|large-v3")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not shutil.which("arecord"):
|
||||||
|
sys.exit("Fehlt: arecord")
|
||||||
|
|
||||||
|
wav = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
||||||
|
wav.close()
|
||||||
|
|
||||||
|
print()
|
||||||
|
for n in (3, 2, 1):
|
||||||
|
print(f" Aufnahme startet in {n} …", end="\r", flush=True)
|
||||||
|
time.sleep(1)
|
||||||
|
print(f"\n\n{GREEN}🟢🟢🟢🟢🟢 S P R I C H J E T Z T ({args.seconds}s) 🟢🟢🟢🟢🟢{RESET}\n")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
["arecord", "-q", "-f", "S16_LE", "-r", str(args.rate), "-c", "1",
|
||||||
|
"-D", args.device, "-d", str(args.seconds), wav.name]
|
||||||
|
)
|
||||||
|
print(f"{RED}🔴 STOP — Aufnahme fertig.{RESET}\n")
|
||||||
|
if result.returncode != 0:
|
||||||
|
sys.exit(f"arecord-Fehler (Gerät {args.device}?). Geräte: arecord -L")
|
||||||
|
|
||||||
|
with wave.open(wav.name, "rb") as w:
|
||||||
|
dur = w.getnframes() / w.getframerate()
|
||||||
|
print(f"WAV-Dauer: {dur:.1f}s (Datei: {wav.name})\n")
|
||||||
|
|
||||||
|
print("Lade faster-whisper …")
|
||||||
|
from faster_whisper import WhisperModel
|
||||||
|
|
||||||
|
model = WhisperModel(args.model, device="cpu", compute_type="int8")
|
||||||
|
for title, kw in [("Standard (wie im Provider)", {}), ("mit vad_filter=True", {"vad_filter": True})]:
|
||||||
|
print(f"--- {title} ---")
|
||||||
|
segments, _ = model.transcribe(wav.name, language="de", **kw)
|
||||||
|
count = 0
|
||||||
|
for seg in segments:
|
||||||
|
count += 1
|
||||||
|
print(f" [{seg.start:5.1f}-{seg.end:5.1f}] {seg.text.strip()!r}")
|
||||||
|
if count == 0:
|
||||||
|
print(" (nichts erkannt)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -109,6 +109,19 @@ def record_utterance(recorder: str, device: str | None, rate: int) -> bytes:
|
||||||
|
|
||||||
with open(tmp.name, "rb") as fh:
|
with open(tmp.name, "rb") as fh:
|
||||||
data = fh.read()
|
data = fh.read()
|
||||||
|
# Aufnahmedauer anzeigen (hilft, zu fruehes Stoppen sofort zu erkennen).
|
||||||
|
# Hinweis: arecord schreibt beim Stoppen per Signal eine falsche Laenge in den
|
||||||
|
# WAV-Header -> Dauer aus der TATSAECHLICHEN Datenmenge berechnen, nicht aus dem Header.
|
||||||
|
try:
|
||||||
|
with wave.open(io.BytesIO(data), "rb") as wav_in:
|
||||||
|
rate = wav_in.getframerate() or 16000
|
||||||
|
channels = wav_in.getnchannels() or 1
|
||||||
|
width = wav_in.getsampwidth() or 2
|
||||||
|
raw = wav_in.readframes(10 ** 9) # liest alles Vorhandene (Header-Laenge unzuverlaessig)
|
||||||
|
seconds = len(raw) / (rate * channels * width)
|
||||||
|
print(f" Aufnahme: {seconds:.1f}s")
|
||||||
|
except (wave.Error, EOFError):
|
||||||
|
pass
|
||||||
# Recorder-Fehlertext nur zeigen, wenn nichts/zu wenig aufgenommen wurde.
|
# Recorder-Fehlertext nur zeigen, wenn nichts/zu wenig aufgenommen wurde.
|
||||||
if len(data) < 1000:
|
if len(data) < 1000:
|
||||||
err.seek(0)
|
err.seek(0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue