feat(voice_loop): per Default System-Standardgeraete + Probe gegen tote Geraete
'auto'-Recorder folgt jetzt dem System-Standard-Mikrofon und prueft per Kurztest (~0,6 s), dass das Werkzeug WIRKLICH Audio liefert -> nie ein totes Geraet. Kandidaten ffmpeg(-f pulse) -> parecord -> arecord -> pw-record; auf PipeWire- Systemen ohne aktive Session gewinnt ffmpeg (gemessen: pw-record/arecord-default/ parecord liefern hier nichts). Neuer Recorder 'ffmpeg'. Player: paplay vor aplay (folgt System-Default-Sink). --device nur noch, wenn man bewusst abweichen will. Doku (Bedienung + B2 + Fehlerbehebung) entsprechend aktualisiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9d0019274a
commit
295f066b6a
2 changed files with 99 additions and 43 deletions
|
|
@ -111,22 +111,21 @@ Nützliche Optionen:
|
||||||
```bash
|
```bash
|
||||||
python scripts/voice_loop.py --stream-text # Antworttext live anzeigen, waehrend die KI generiert
|
python scripts/voice_loop.py --stream-text # Antworttext live anzeigen, waehrend die KI generiert
|
||||||
python scripts/voice_loop.py --no-stream-audio # satzweises Vorlesen abschalten (Audio erst komplett)
|
python scripts/voice_loop.py --no-stream-audio # satzweises Vorlesen abschalten (Audio erst komplett)
|
||||||
python scripts/voice_loop.py --recorder pw-record # PipeWire-Aufnahme (Standard bei 'auto')
|
python scripts/voice_loop.py --recorder arecord --device plughw:6,0 # bestimmtes Mikrofon erzwingen
|
||||||
python scripts/voice_loop.py --recorder arecord --device hw:1,0 # ALSA, bestimmtes Mikrofon
|
|
||||||
python scripts/voice_loop.py --llm-provider openrouter --tts-provider openrouter
|
python scripts/voice_loop.py --llm-provider openrouter --tts-provider openrouter
|
||||||
python scripts/voice_loop.py --token "$TOKEN" # falls AUTH_ENABLED=true
|
python scripts/voice_loop.py --token "$TOKEN" # falls AUTH_ENABLED=true
|
||||||
python scripts/voice_loop.py --file frage.wav # ohne Mikrofon: WAV senden (Test)
|
python scripts/voice_loop.py --file frage.wav # ohne Mikrofon: WAV senden (Test)
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Aufnahmewerkzeug:** `--recorder auto` (Standard) bevorzugt **PipeWire** (`pw-record`),
|
> **Geräte = System-Standard (automatisch):** Ohne `--device` folgt der Loop dem
|
||||||
> sonst `parecord`/`arecord`. **Falls die Aufnahme scheitert** (z. B.
|
> **am System eingestellten Standard-Mikrofon und -Lautsprecher** (inkl. Bluetooth —
|
||||||
> *„pw_context_connect() failed"* oder *„Fehler beim Öffnen des Gerätes"*), ist der
|
> umstellbar über *Ubuntu → Einstellungen → Ton*, siehe Teil B2). `--recorder auto`
|
||||||
> verlässlichste Weg ein **direktes ALSA-Hardware-Gerät**:
|
> (Standard) wählt selbsttätig ein Aufnahmewerkzeug, das dem Default folgt **und** im
|
||||||
> ```bash
|
> **Kurztest wirklich Audio liefert** (Reihenfolge `ffmpeg` → `parecord` → `arecord` →
|
||||||
> arecord -l # Kartennummern der Mikrofone
|
> `pw-record`) — so wird nie ein totes Gerät gewählt. Beim Start erscheint kurz
|
||||||
> python scripts/voice_loop.py --recorder arecord --device plughw:2,0
|
> „Prüfe Standard-Aufnahmegerät …".
|
||||||
> ```
|
> **Ein bestimmtes Mikrofon** nur bei Bedarf erzwingen, z. B. `--recorder arecord
|
||||||
> (`plughw:KARTE,GERÄT` aus `arecord -l`; z. B. onboard oft Karte 2, USB-Webcam Karte 4.)
|
> --device plughw:6,0` (`arecord -l` zeigt die Kartennummer).
|
||||||
|
|
||||||
## A2. Nur tippen → Antwort hören
|
## A2. Nur tippen → Antwort hören
|
||||||
|
|
||||||
|
|
@ -390,20 +389,24 @@ pactl set-default-sink <SINK_NAME> # z. B. die Bluetooth-Box
|
||||||
Sobald die Bluetooth-Box dort als Standard gesetzt ist (Schritt 2 oben), kommt die
|
Sobald die Bluetooth-Box dort als Standard gesetzt ist (Schritt 2 oben), kommt die
|
||||||
gesprochene Antwort **automatisch über die Box** — ohne zusätzliche Option. Das ist der
|
gesprochene Antwort **automatisch über die Box** — ohne zusätzliche Option. Das ist der
|
||||||
Grund, warum die Bluetooth-Umleitung „einfach funktioniert".
|
Grund, warum die Bluetooth-Umleitung „einfach funktioniert".
|
||||||
- **Eingabe (Aufnahme):** Die Aufnahme folgt **nicht** automatisch dem System-Default —
|
- **Eingabe (Aufnahme):** `voice_loop.py` folgt mit `--recorder auto` (Standard) ebenfalls
|
||||||
`voice_loop.py` nimmt mit einem fest gewählten Gerät auf. Damit der **ReSpeaker** genutzt
|
dem **System-Standard-Mikrofon** — es probiert beim Start automatisch ein Werkzeug, das
|
||||||
wird, das Gerät explizit angeben (ReSpeaker = **Karte 6** → `plughw:6,0`):
|
dem Default folgt und im Kurztest wirklich Audio liefert (`ffmpeg` → `parecord` → `arecord`
|
||||||
|
→ `pw-record`). Stellst du also das Eingabegerät in *Einstellungen → Ton* um (z. B. auf den
|
||||||
|
ReSpeaker), nutzt der Loop es ohne weitere Option.
|
||||||
|
- **Bestimmtes Mikrofon erzwingen** (statt System-Default), z. B. den ReSpeaker fix als ALSA-
|
||||||
|
Gerät (Karte 6):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/voice_loop.py --recorder arecord --device plughw:6,0
|
python scripts/voice_loop.py --recorder arecord --device plughw:6,0
|
||||||
```
|
```
|
||||||
|
|
||||||
(Auf diesem Rechner ist `--recorder arecord` mit `plughw:…` der zuverlässige Weg, weil die
|
(Hilfreich, wenn du gezielt ein anderes als das Standard-Mikrofon willst; Kartennummer mit
|
||||||
Default-folgenden Recorder `pw-record`/`parecord` hier nicht stabil verbinden. Die richtige
|
`arecord -l`. Auf diesem Rechner scheitern `pw-record`/`arecord default` — die `auto`-Probe
|
||||||
Kartennummer notfalls mit `arecord -l` prüfen.)
|
überspringt sie automatisch und nimmt `ffmpeg -f pulse`.)
|
||||||
|
|
||||||
Kurz: **Lautsprecher/Bluetooth umstellen → System-Einstellungen genügen.**
|
Kurz: **Mikrofon UND Lautsprecher/Bluetooth umstellen → die System-Einstellungen genügen;
|
||||||
**Mikrofon für den Sprech-Loop → zusätzlich `--device plughw:6,0` mitgeben.**
|
der Sprech-Loop folgt dem Standard automatisch.** `--device` nur, wenn du bewusst abweichen willst.
|
||||||
|
|
||||||
### Weitere Einstellungen, die du vornehmen kannst
|
### Weitere Einstellungen, die du vornehmen kannst
|
||||||
- **Ausgabe-Lautstärke / Mikrofon-Empfindlichkeit:** *Ton*-Seite oder
|
- **Ausgabe-Lautstärke / Mikrofon-Empfindlichkeit:** *Ton*-Seite oder
|
||||||
|
|
@ -614,7 +617,7 @@ curl -s "$URL/api/metrics?format=prometheus" # Prometheus-Text (kein jq)
|
||||||
| HTTP **502** bei STT/TTS | Cloud-Fehler/Format | `make smoke` ausführen; Modellnamen in `.env` prüfen |
|
| HTTP **502** bei STT/TTS | Cloud-Fehler/Format | `make smoke` ausführen; Modellnamen in `.env` prüfen |
|
||||||
| `VA_PROFILE` wirkt nicht | `DEFAULT_*_PROVIDER` in `.env` überschreibt | diese Zeilen in `.env` auskommentieren |
|
| `VA_PROFILE` wirkt nicht | `DEFAULT_*_PROVIDER` in `.env` überschreibt | diese Zeilen in `.env` auskommentieren |
|
||||||
| `Address already in use` | Port belegt | anderen `PORT` setzen |
|
| `Address already in use` | Port belegt | anderen `PORT` setzen |
|
||||||
| `pw_context_connect() failed` / `arecord: Fehler beim Öffnen des Gerätes` | PipeWire-Client- bzw. ALSA-`default`-Pfad gestört | direktes Gerät nehmen: `arecord -l`, dann `--recorder arecord --device plughw:2,0` |
|
| `pw_context_connect() failed` / `arecord: Fehler beim Öffnen des Gerätes` | PipeWire-Client- bzw. ALSA-`default`-Pfad gestört | `--recorder auto` (Standard) überspringt tote Werkzeuge automatisch (nutzt `ffmpeg -f pulse`); notfalls direktes Gerät: `arecord -l`, dann `--recorder arecord --device plughw:6,0` |
|
||||||
| Keine Aufnahme/Wiedergabe | Werkzeug/Gerät fehlt | `arecord -L` / `aplay -L`; Pakete `pipewire`/`alsa-utils`/`ffmpeg`; Default via `wpctl status` |
|
| Keine Aufnahme/Wiedergabe | Werkzeug/Gerät fehlt | `arecord -L` / `aplay -L`; Pakete `pipewire`/`alsa-utils`/`ffmpeg`; Default via `wpctl status` |
|
||||||
| Profil greift nicht | `config/voice-assistant.toml` fehlt | aus `*.example.toml` kopieren (Abschnitt 2) |
|
| Profil greift nicht | `config/voice-assistant.toml` fehlt | aus `*.example.toml` kopieren (Abschnitt 2) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,19 @@ Nimmt vom Mikrofon auf (Push-to-Talk), schickt das Audio ueber EINE
|
||||||
/ws/voice-Verbindung an das Gateway und spielt die Antwort ab. Das Gespraechs-
|
/ws/voice-Verbindung an das Gateway und spielt die Antwort ab. Das Gespraechs-
|
||||||
gedaechtnis bleibt ueber die `session_id` erhalten.
|
gedaechtnis bleibt ueber die `session_id` erhalten.
|
||||||
|
|
||||||
|
Standardmaessig folgt der Loop dem **System-Standard-Mikrofon und -Lautsprecher**
|
||||||
|
(inkl. Bluetooth). Ein bestimmtes Geraet nur, wenn `--device` explizit gesetzt ist.
|
||||||
|
|
||||||
Beispiele:
|
Beispiele:
|
||||||
python scripts/voice_loop.py
|
python scripts/voice_loop.py # System-Standardgeraete
|
||||||
python scripts/voice_loop.py --url ws://localhost:8003/ws/voice --session oma-anna
|
python scripts/voice_loop.py --url ws://localhost:8003/ws/voice --session oma-anna
|
||||||
python scripts/voice_loop.py --device hw:1,0 # bestimmtes Mikrofon (arecord -L)
|
python scripts/voice_loop.py --recorder arecord --device plughw:6,0 # bestimmtes Mikrofon
|
||||||
python scripts/voice_loop.py --llm-provider openrouter --tts-provider openrouter
|
python scripts/voice_loop.py --llm-provider openrouter --tts-provider openrouter
|
||||||
python scripts/voice_loop.py --tts-provider openrouter --voice Puck # Cloud-Stimme testen
|
python scripts/voice_loop.py --tts-provider openrouter --voice Puck # Cloud-Stimme testen
|
||||||
python scripts/voice_loop.py --file frage.wav # ohne Mikrofon (Test)
|
python scripts/voice_loop.py --file frage.wav # ohne Mikrofon (Test)
|
||||||
|
|
||||||
Voraussetzungen: laufendes Gateway, `arecord` (Aufnahme), ein Player
|
Voraussetzungen: laufendes Gateway, ein Aufnahmewerkzeug (`ffmpeg`/`parecord`/`arecord`),
|
||||||
(`ffplay`/`aplay`/`paplay`), Python-Paket `websockets`.
|
ein Player (`ffplay`/`paplay`/`aplay`), Python-Paket `websockets`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -23,11 +26,13 @@ import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import time
|
||||||
import wave
|
import wave
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -46,28 +51,73 @@ def _require(tool: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def _first_player() -> list[str] | None:
|
def _first_player() -> list[str] | None:
|
||||||
|
# paplay vor aplay: paplay folgt dem System-Standard-Ausgabegeraet (PulseAudio),
|
||||||
|
# aplay nutzt das ALSA-Default, das auf PipeWire-Systemen oft tot ist.
|
||||||
if shutil.which("ffplay"):
|
if shutil.which("ffplay"):
|
||||||
return ["ffplay", "-loglevel", "quiet", "-nodisp", "-autoexit"]
|
return ["ffplay", "-loglevel", "quiet", "-nodisp", "-autoexit"]
|
||||||
if shutil.which("aplay"):
|
|
||||||
return ["aplay", "-q"]
|
|
||||||
if shutil.which("paplay"):
|
if shutil.which("paplay"):
|
||||||
return ["paplay"]
|
return ["paplay"]
|
||||||
|
if shutil.which("aplay"):
|
||||||
|
return ["aplay", "-q"]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def resolve_recorder(choice: str) -> str:
|
def resolve_recorder(choice: str, rate: int = 16000) -> str:
|
||||||
"""Waehlt das Aufnahmewerkzeug. 'auto' bevorzugt PipeWire (pw-record)."""
|
"""Waehlt das Aufnahmewerkzeug.
|
||||||
|
|
||||||
|
'auto' folgt dem **System-Standard-Mikrofon** und prueft per kurzem Test, dass das
|
||||||
|
Werkzeug WIRKLICH Audio liefert -> es wird nie ein totes Geraet gewaehlt. Reihenfolge
|
||||||
|
der Kandidaten: ffmpeg (PulseAudio-Default) -> parecord -> arecord -> pw-record.
|
||||||
|
"""
|
||||||
if choice != "auto":
|
if choice != "auto":
|
||||||
if not shutil.which(choice):
|
if not shutil.which(choice):
|
||||||
sys.exit(f"Fehlt: Aufnahmewerkzeug '{choice}' nicht gefunden.")
|
sys.exit(f"Fehlt: Aufnahmewerkzeug '{choice}' nicht gefunden.")
|
||||||
return choice
|
return choice
|
||||||
for tool in ("pw-record", "parecord", "arecord"):
|
available = [t for t in ("ffmpeg", "parecord", "arecord", "pw-record") if shutil.which(t)]
|
||||||
if shutil.which(tool):
|
if not available:
|
||||||
|
sys.exit("Kein Aufnahmewerkzeug gefunden (ffmpeg / parecord / arecord / pw-record).")
|
||||||
|
print(" Prüfe Standard-Aufnahmegerät …", flush=True)
|
||||||
|
for tool in available:
|
||||||
|
if _probe_records(tool, rate):
|
||||||
return tool
|
return tool
|
||||||
sys.exit("Kein Aufnahmewerkzeug gefunden (pw-record / parecord / arecord).")
|
print(f" ⚠ Kein Werkzeug lieferte im Test Audio; nutze '{available[0]}'."
|
||||||
|
" Ggf. --recorder/--device explizit setzen.")
|
||||||
|
return available[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _probe_records(recorder: str, rate: int) -> bool:
|
||||||
|
"""Kurztest (~0,6 s), ob 'recorder' am System-Default tatsaechlich aufnimmt."""
|
||||||
|
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
||||||
|
tmp.close()
|
||||||
|
size = 0
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
_record_cmd(recorder, None, rate, tmp.name),
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
time.sleep(0.6)
|
||||||
|
proc.send_signal(signal.SIGINT)
|
||||||
|
try:
|
||||||
|
proc.wait(timeout=3)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
proc.kill()
|
||||||
|
size = os.path.getsize(tmp.name)
|
||||||
|
except Exception: # noqa: BLE001 - jedes Problem = Werkzeug taugt nicht
|
||||||
|
size = 0
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
os.unlink(tmp.name)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return size > 2000 # mehr als WAV-Header (44 B) + etwas Audio
|
||||||
|
|
||||||
|
|
||||||
def _record_cmd(recorder: str, device: str | None, rate: int, outfile: str) -> list[str]:
|
def _record_cmd(recorder: str, device: str | None, rate: int, outfile: str) -> list[str]:
|
||||||
|
if recorder == "ffmpeg":
|
||||||
|
# PulseAudio-Eingang folgt dem System-Standard-Mikrofon (device=None -> "default").
|
||||||
|
return ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y",
|
||||||
|
"-f", "pulse", "-i", device or "default",
|
||||||
|
"-ar", str(rate), "-ac", "1", outfile]
|
||||||
if recorder == "arecord":
|
if recorder == "arecord":
|
||||||
cmd = ["arecord", "-q", "-f", "S16_LE", "-r", str(rate), "-c", "1"]
|
cmd = ["arecord", "-q", "-f", "S16_LE", "-r", str(rate), "-c", "1"]
|
||||||
if device:
|
if device:
|
||||||
|
|
@ -158,13 +208,14 @@ def open_stream_player():
|
||||||
waehrend die KI noch weiter generiert. None, wenn kein Player gefunden wird.
|
waehrend die KI noch weiter generiert. None, wenn kein Player gefunden wird.
|
||||||
"""
|
"""
|
||||||
rate = str(TTS_SAMPLE_RATE)
|
rate = str(TTS_SAMPLE_RATE)
|
||||||
|
# paplay vor aplay: folgt dem System-Standard-Ausgabegeraet (PulseAudio/PipeWire).
|
||||||
if shutil.which("ffplay"):
|
if shutil.which("ffplay"):
|
||||||
cmd = ["ffplay", "-loglevel", "quiet", "-nodisp", "-autoexit",
|
cmd = ["ffplay", "-loglevel", "quiet", "-nodisp", "-autoexit",
|
||||||
"-f", "s16le", "-ar", rate, "-ac", "1", "-i", "pipe:0"]
|
"-f", "s16le", "-ar", rate, "-ac", "1", "-i", "pipe:0"]
|
||||||
elif shutil.which("aplay"):
|
|
||||||
cmd = ["aplay", "-q", "-f", "S16_LE", "-r", rate, "-c", "1"]
|
|
||||||
elif shutil.which("paplay"):
|
elif shutil.which("paplay"):
|
||||||
cmd = ["paplay", "--raw", f"--rate={rate}", "--format=s16le", "--channels=1"]
|
cmd = ["paplay", "--raw", f"--rate={rate}", "--format=s16le", "--channels=1"]
|
||||||
|
elif shutil.which("aplay"):
|
||||||
|
cmd = ["aplay", "-q", "-f", "S16_LE", "-r", rate, "-c", "1"]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
return subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
return subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||||
|
|
@ -309,10 +360,10 @@ async def run(args) -> None:
|
||||||
await _send_and_play(url, wav, start_frame)
|
await _send_and_play(url, wav, start_frame)
|
||||||
return
|
return
|
||||||
|
|
||||||
recorder = resolve_recorder(args.recorder)
|
recorder = resolve_recorder(args.recorder, args.rate)
|
||||||
print(f"Ziel: {args.url} (Session '{args.session}')")
|
print(f"Ziel: {args.url} (Session '{args.session}')")
|
||||||
print(f"Aufnahme mit: {recorder}"
|
print(f"Aufnahme mit: {recorder}"
|
||||||
+ (f" (Gerät: {args.device})" if args.device else " (Standardgerät)"))
|
+ (f" (Gerät: {args.device})" if args.device else " (System-Standardgerät)"))
|
||||||
print("Sprich nach 'START', stoppe mit Enter. Strg+C beendet den Loop.")
|
print("Sprich nach 'START', stoppe mit Enter. Strg+C beendet den Loop.")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|
@ -321,11 +372,10 @@ async def run(args) -> None:
|
||||||
print("\nEnde.")
|
print("\nEnde.")
|
||||||
return
|
return
|
||||||
if len(wav) < 1000:
|
if len(wav) < 1000:
|
||||||
print(" ⚠ Keine/zu kurze Aufnahme. Moegliche Ursache: Aufnahmewerkzeug"
|
print(" ⚠ Keine/zu kurze Aufnahme. Pruefe das System-Standard-Mikrofon"
|
||||||
" oder Geraet nicht nutzbar.")
|
" (Ubuntu: Einstellungen → Ton → Eingabe) und ob es Pegel zeigt.")
|
||||||
print(" Verfuegbare Mikrofone: arecord -l")
|
print(" Notfalls direktes ALSA-Geraet erzwingen (arecord -l zeigt die Nummer):")
|
||||||
print(" Direktes ALSA-Geraet verwenden, z. B.:")
|
print(" python scripts/voice_loop.py --recorder arecord --device plughw:6,0")
|
||||||
print(" python scripts/voice_loop.py --recorder arecord --device plughw:2,0")
|
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
await _send_and_play(url, wav, start_frame)
|
await _send_and_play(url, wav, start_frame)
|
||||||
|
|
@ -339,10 +389,13 @@ def main() -> None:
|
||||||
p.add_argument("--session", default="voice-loop")
|
p.add_argument("--session", default="voice-loop")
|
||||||
p.add_argument("--token", default=None, help="Bearer-Token, falls AUTH_ENABLED=true")
|
p.add_argument("--token", default=None, help="Bearer-Token, falls AUTH_ENABLED=true")
|
||||||
p.add_argument("--device", default=None,
|
p.add_argument("--device", default=None,
|
||||||
help="Aufnahmegeraet (arecord: -L; pw-record: --target; parecord: --device)")
|
help="Aufnahmegeraet explizit (ffmpeg/parecord: PulseAudio-Quelle; "
|
||||||
|
"arecord: ALSA z. B. plughw:6,0; pw-record: --target). "
|
||||||
|
"Ohne Angabe folgt der Loop dem System-Standardgeraet.")
|
||||||
p.add_argument("--recorder", default="auto",
|
p.add_argument("--recorder", default="auto",
|
||||||
choices=["auto", "pw-record", "parecord", "arecord"],
|
choices=["auto", "ffmpeg", "pw-record", "parecord", "arecord"],
|
||||||
help="Aufnahmewerkzeug; 'auto' bevorzugt PipeWire (pw-record)")
|
help="Aufnahmewerkzeug; 'auto' folgt dem System-Standardmikrofon und "
|
||||||
|
"prueft per Kurztest, dass es wirklich aufnimmt")
|
||||||
p.add_argument("--rate", type=int, default=16000)
|
p.add_argument("--rate", type=int, default=16000)
|
||||||
p.add_argument("--stt-provider", dest="stt_provider", default=None)
|
p.add_argument("--stt-provider", dest="stt_provider", default=None)
|
||||||
p.add_argument("--llm-provider", dest="llm_provider", default=None)
|
p.add_argument("--llm-provider", dest="llm_provider", default=None)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue