diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md new file mode 100644 index 0000000..6a9b4a1 --- /dev/null +++ b/BEDIENUNGSANLEITUNG.md @@ -0,0 +1,434 @@ +# Bedienungsanleitung — Voice Assistant + +## Inhaltsverzeichnis + +1. [Voraussetzungen](#1-voraussetzungen) +2. [Installation der Abhängigkeiten](#2-installation-der-abhängigkeiten) +3. [Schnellstart](#3-schnellstart) +4. [Die fünf Zustände](#4-die-fünf-zustände) +5. [Steuer-Wörter](#5-steuer-wörter) +6. [Alle Kommandozeilenoptionen](#6-alle-kommandozeilenoptionen) +7. [Mikrofon wählen](#7-mikrofon-wählen) +8. [Audio-Ausgang wählen](#8-audio-ausgang-wählen) +9. [LLM-Backend konfigurieren](#9-llm-backend-konfigurieren) +10. [Sprache und Stimme](#10-sprache-und-stimme) +11. [Standalone-Werkzeuge](#11-standalone-werkzeuge) +12. [Tipps und Fehlerbehebung](#12-tipps-und-fehlerbehebung) + +--- + +## 1. Voraussetzungen + +### Hardware + +| Komponente | Empfehlung | +|---|---| +| Mikrofon | ReSpeaker XVF3800 (Hardware-AEC, 4 Mikrofone, 48 kHz) | +| GPU | NVIDIA RTX (CUDA) für Whisper-Transkription | +| Audio-System | PipeWire mit `paplay` | + +Jedes USB-Mikrofon funktioniert. Das ReSpeaker XVF3800 ist empfohlen, weil es +Hardware-seitig Echo cancellation (AEC) und Beamforming bietet — das verhindert, +dass die TTS-Ausgabe als Spracheingabe erkannt wird. + +### Software + +- Python 3.12+ +- PipeWire als Audio-Server (`pactl`, `paplay` müssen verfügbar sein) +- CUDA-Treiber für NVIDIA-GPU + +--- + +## 2. Installation der Abhängigkeiten + +```bash +pip install faster-whisper openwakeword piper-tts sounddevice scipy langdetect openai +``` + +Das Whisper-Modell (`large-v3`) wird beim ersten Start automatisch heruntergeladen (~3 GB). +Piper-Stimmen werden bei Bedarf automatisch heruntergeladen (~60–120 MB pro Sprache). + +--- + +## 3. Schnellstart + +```bash +# Standard: ReSpeaker-Mikrofon, llama.cpp auf localhost:8001, Deutsch +python3 assistant.py + +# Ohne Wake-Word — sofort aufnahmebereit (gut zum Testen) +python3 assistant.py --no-wakeword + +# Mit Ollama +python3 assistant.py --backend ollama --model llama3.2 + +# Mit OpenAI +export OPENAI_API_KEY=sk-... +python3 assistant.py --backend openai --model gpt-4o-mini +``` + +Beim ersten Start werden Modelle geladen (ca. 10–30 Sekunden). Danach erscheint: + +``` +Warte auf "Hey Jarvis" … +[LAUSCH] Warte auf "Hey Jarvis" … +``` + +--- + +## 4. Die fünf Zustände + +Der Assistent durchläuft folgende Zustände. Der aktuelle Zustand wird in der +Statuszeile links in eckigen Klammern angezeigt: `[ZUSTAND] Info` + +``` + "Hey Jarvis" +LAUSCH ─────────────► AUFNAHME + ▲ │ + │ Timeout/Stopp Stille ≥5s + │ oder "over" + │ │ + │ Stopp ▼ +DIALOG ◄──────────── VORLESE ◄── GENERATOR + │ Vorlesen fertig Transkription + │ + LLM-Antwort + └── Sprache erkannt ──► AUFNAHME +``` + +### LAUSCH +Wartet auf das Wake-Word **„Hey Jarvis"**. Kein Quittungston, kein Feedback bis +das Wake-Word erkannt wird. Ein kurzer Piepton signalisiert den Übergang zu AUFNAHME. + +### AUFNAHME +Nimmt die Sprache auf. Die Statuszeile zeigt die bisherige Aufnahmedauer und die +aktuelle Stille-Dauer: +``` +[AUFNAHME] 3.2s (Stille 1.1s) +``` +Die Aufnahme endet wenn: +- Stille ≥ `--silence-sec` (Standard: 5 Sekunden) +- Das Stop-Wort gesagt wird (Standard: „over") +- „Stopp" gesagt wird → direkt zurück zu LAUSCH (ohne Antwort) +- Maximaldauer von 60 Sekunden erreicht ist + +### GENERATOR +Der Assistent sagt den Quittungssatz (Standard: „Moment, ich antworte gleich."), +während er die Transkription und den LLM-Aufruf durchführt. Auch hier ist „Stopp" +aktiv und bricht den Vorgang ab. + +### VORLESE +Die Antwort wird vorgelesen. Alle **Steuer-Wörter** sind aktiv (→ Abschnitt 5). +Mehrere Sätze werden parallel synthetisiert und nahtlos abgespielt. + +### DIALOG +Nach dem Vorlesen öffnet sich ein Zeitfenster (Standard: 5 Sekunden). Wird in +dieser Zeit gesprochen, geht der Assistent direkt in AUFNAHME — **ohne Wake-Word**. +So sind Rückfragen flüssig möglich. Nach Ablauf des Zeitfensters oder bei „Stopp" +kehrt der Assistent zu LAUSCH zurück. + +--- + +## 5. Steuer-Wörter + +Das Wort **„bitte"** wird vor dem Abgleich immer ignoriert (z. B. „Stopp bitte" = „Stopp"). + +### Während VORLESE + +| Gesprochener Befehl | Wirkung | +|---|---| +| **„Stopp"** | Wiedergabe sofort abbrechen → DIALOG | +| **„Pause"** | Wiedergabe anhalten (bleibt pausiert) | +| **„Weiter"** | Nach Pause: aktuellen Satz von vorne abspielen | +| **„Noch einmal"** | Letzten Satz wiederholen | +| **„Absatz noch einmal"** | Zum Anfang des letzten Absatzes springen | +| **„Alles von vorn"** | Gesamte Antwort von Anfang an wiederholen | + +### In allen anderen aktiven Zuständen + +| Zustand | Befehl | Wirkung | +|---|---|---| +| AUFNAHME | **„Stopp"** | Aufnahme abbrechen → LAUSCH | +| GENERATOR | **„Stopp"** | Verarbeitung abbrechen → LAUSCH | +| DIALOG | **„Stopp"** | Dialogfenster schließen → LAUSCH | + +--- + +## 6. Alle Kommandozeilenoptionen + +``` +python3 assistant.py [OPTIONEN] +``` + +### Eingabe + +| Option | Standard | Beschreibung | +|---|---|---| +| `--mic NAME` | `respeaker` | Mikrofon: `respeaker` \| `motu` \| `camera` \| `default` \| `bluetooth` \| `` \| `` | +| `--list-mics` | — | Alle verfügbaren Mikrofone anzeigen und beenden | +| `--silence-sec N` | `5.0` | Stille-Dauer in Sekunden bis die Aufnahme endet | +| `--stop-word WORT` | `over` | Codewort zum manuellen Beenden der Aufnahme | +| `--no-wakeword` | — | Wake-Word deaktivieren, sofort in AUFNAHME starten | +| `--wakeword-threshold N` | `0.5` | Empfindlichkeit Wake-Word-Erkennung (0.0–1.0) | + +### LLM-Backend + +| Option | Standard | Beschreibung | +|---|---|---| +| `--backend NAME` | `llama` | Backend: `llama` \| `ollama` \| `openai` | +| `--model NAME` | auto | Modellname (bei llama.cpp: automatisch vom Server) | +| `--system TEXT` | (eingebaut) | System-Prompt für den Assistenten | +| `--history N` | `10` | Maximale Anzahl gespeicherter Gesprächsrunden | + +### Spracherkennung (Whisper) + +| Option | Standard | Beschreibung | +|---|---|---| +| `--whisper-model NAME` | `large-v3` | Whisper-Modell | +| `--gpu N` | `1` | CUDA-Index für Whisper (in `CUDA_VISIBLE_DEVICES`) | +| `--lang CODE` | `de` | Sprache der Benutzereingabe (`de`, `en`, `fr`, …) | + +### Sprachausgabe (TTS) + +| Option | Standard | Beschreibung | +|---|---|---| +| `--voice NAME` | `de_DE-thorsten-high` | Standard-Piper-Stimme (Fallback) | +| `--en-variant` | `us` | Englische Variante: `us` (Ryan) \| `gb` (Alan) | +| `--out NAME` | `default` | Ausgabe-Sink: `default` \| `respeaker` \| `hdmi` \| `motu` \| `bluetooth` \| `` | +| `--ack-text TEXT` | `Moment, ich antworte gleich.` | Quittungssatz im GENERATOR-Zustand | +| `--dialog-timeout N` | `5.0` | DIALOG-Fenster in Sekunden | + +### Typische Kombinationen + +```bash +# Entwicklung / Debugging (kein Wake-Word, kurze Stille) +python3 assistant.py --no-wakeword --silence-sec 2.0 + +# MOTU M2 als Mikrofon und Ausgang +python3 assistant.py --mic motu --out motu + +# Englisch mit britischer Stimme +python3 assistant.py --lang en --voice en_GB-alan-medium --en-variant gb + +# Empfindlicheres Wake-Word +python3 assistant.py --wakeword-threshold 0.3 + +# Längeres Dialogfenster +python3 assistant.py --dialog-timeout 10.0 +``` + +--- + +## 7. Mikrofon wählen + +```bash +# Alle verfügbaren Mikrofone anzeigen +python3 assistant.py --list-mics +# oder +python3 mic.py +``` + +**Vordefinierte Kurznamen:** + +| Kurzname | Gerät | +|---|---| +| `respeaker` | ReSpeaker XVF3800 (Standard) | +| `motu` | MOTU M2 Mikrofon-Eingang | +| `camera` | USB-Kamera-Mikrofon (VF0680) | +| `default` | System-Standard-Eingabe (PipeWire) | +| `bluetooth` | Erstes verbundenes Bluetooth-Gerät | + +Alternativ kann ein **numerischer Index** (`--mic 3`) oder ein **Substring** des +Gerätenamens (`--mic "USB Audio"`) angegeben werden. + +--- + +## 8. Audio-Ausgang wählen + +```bash +# Alle verfügbaren Ausgänge anzeigen +python3 speak.py --list +``` + +**Vordefinierte Kurznamen:** + +| Kurzname | Gerät | +|---|---| +| `default` | PipeWire-Standard-Sink | +| `respeaker` | ReSpeaker XVF3800 3,5-mm-Klinke | +| `hdmi` | HDA NVidia HDMI | +| `motu` | MOTU M2 Ausgang | +| `bluetooth` | Erstes verbundenes Bluetooth-Gerät | + +Vollständige Sink-Namen aus `pactl list sinks short` können direkt verwendet werden. + +--- + +## 9. LLM-Backend konfigurieren + +### llama.cpp (Standard) + +```bash +# llama.cpp muss auf Port 8001 laufen +python3 assistant.py --backend llama +``` + +Der Modellname wird automatisch vom Server abgefragt. + +### Ollama + +```bash +# Ollama muss laufen: ollama serve +python3 assistant.py --backend ollama --model llama3.2 +python3 assistant.py --backend ollama --model mistral +``` + +### OpenAI + +```bash +export OPENAI_API_KEY=sk-... +python3 assistant.py --backend openai --model gpt-4o-mini +python3 assistant.py --backend openai --model gpt-4o +``` + +--- + +## 10. Sprache und Stimme + +### Automatische Spracherkennung + +Der Assistent erkennt automatisch die Sprache der LLM-Antwort und wählt die +passende Piper-Stimme. Fehlende Stimmen werden beim ersten Bedarf automatisch +heruntergeladen. + +| Sprache | Stimme | +|---|---| +| Deutsch | `de_DE-thorsten-high` | +| Englisch (US) | `en_US-ryan-high` | +| Englisch (GB) | `en_GB-alan-medium` | +| Französisch | `fr_FR-upmc-medium` | +| Spanisch | `es_ES-davefx-medium` | +| Italienisch | `it_IT-paola-medium` | +| Niederländisch | `nl_NL-mls-medium` | +| Portugiesisch | `pt_BR-faber-medium` | +| Russisch | `ru_RU-ruslan-medium` | +| Polnisch | `pl_PL-bass-high` | +| Ukrainisch | `uk_UA-lada-x_low` | +| Tschechisch | `cs_CZ-jirka-medium` | + +### Englische Variante wählen + +Da automatische Erkennung nicht zwischen en-US und en-GB unterscheiden kann, +wird die Variante beim Start festgelegt: + +```bash +python3 assistant.py --en-variant us # Standard: amerikanisches Englisch +python3 assistant.py --en-variant gb # britisches Englisch +``` + +### Eigene Standardstimme + +Die `--voice`-Option setzt die Fallback-Stimme (wenn Spracherkennung fehlschlägt +oder die erkannte Sprache nicht in der Tabelle ist): + +```bash +python3 assistant.py --voice de_DE-kerstin-low +``` + +--- + +## 11. Standalone-Werkzeuge + +### speak.py — Sprachausgabe testen + +```bash +# Text vorlesen +python3 speak.py --text "Hallo, ich bin dein Assistent." + +# Mit bestimmter Stimme und Ausgang +python3 speak.py --text "Hello World" --voice en_US-ryan-high --out respeaker + +# Text aus stdin +echo "Test" | python3 speak.py + +# Alle Ausgänge anzeigen +python3 speak.py --list + +# Alle verfügbaren Piper-Stimmen (Deutsch) +python3 speak.py --list-voices --voice-lang de + +# Alle Stimmen aller Sprachen +python3 speak.py --list-voices --voice-lang "" +``` + +### transcribe.py — Transkription testen + +```bash +# Live-Transkription mit ReSpeaker +python3 transcribe.py + +# Mit anderem Mikrofon +python3 transcribe.py --mic motu + +# Alle Mikrofone anzeigen +python3 transcribe.py --list-mics +``` + +### mic.py — Mikrofone anzeigen + +```bash +python3 mic.py +``` + +--- + +## 12. Tipps und Fehlerbehebung + +### Wake-Word wird nicht erkannt + +- Lautstärke des Mikrofons erhöhen: `pactl set-source-volume 150%` +- Schwellenwert senken: `--wakeword-threshold 0.3` +- Zum Testen `--no-wakeword` verwenden + +### Transkription ist ungenau + +- Lautstärke erhöhen +- Hintergrundgeräusche reduzieren +- ReSpeaker XVF3800 verwenden (Hardware-AEC unterdrückt Lautsprecher-Echo) +- `--lang` korrekt setzen (z. B. `--lang de` für Deutsch) + +### „Stopp" wird nicht erkannt + +- Deutlich und mit normaler Lautstärke sprechen +- Latenz: ca. 0,5–1 Sekunde bis zur Reaktion (Whisper muss verarbeiten) +- In AUFNAHME: `--stop-word` ist ein anderes Wort; „Stopp" wird separat erkannt + +### TTS-Stimme klingt falsch für die Sprache + +- Stimme wurde mit falscher Sprache verwendet (z. B. Download fehlgeschlagen) +- Fehlermeldung erscheint im Terminal; nochmals versuchen (automatischer Retry 3×) +- Netzwerkverbindung prüfen (Stimmen werden von HuggingFace geladen) + +### Piper-Stimme fehlt / Download schlägt fehl + +```bash +# Verfügbare Stimmen anzeigen +python3 speak.py --list-voices --voice-lang de + +# Stimme manuell laden (wird dann gecacht) +python3 speak.py --text "Test" --voice ru_RU-ruslan-medium +``` + +### audio: input overflow + +Diese Meldung wird bewusst unterdrückt. Sie tritt auf wenn Whisper die +Verarbeitung verzögert und der PortAudio-Puffer überläuft. Das ist normal +und beeinflusst die Qualität nicht wesentlich. + +### llama.cpp antwortet nicht + +```bash +# Prüfen ob der Server läuft +curl http://localhost:8001/v1/models + +# Modell und Port in llama.cpp-Konfiguration prüfen +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..697c258 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Voice Assistant + +Lokaler, vollständig offline-fähiger Sprachassistent mit Wake-Word-Erkennung, +GPU-Transkription, LLM-Antwortgenerierung und mehrsprachiger Sprachausgabe. + +## Features + +- **Wake-Word** „Hey Jarvis" via [openwakeword](https://github.com/dscripka/openWakeWord) +- **Spracherkennung** via [faster-whisper](https://github.com/SYSTRAN/faster-whisper) (`large-v3`, CUDA) +- **LLM-Backends**: llama.cpp (lokal), Ollama, OpenAI-kompatible APIs +- **Text-to-Speech** via [Piper](https://github.com/rhasspy/piper) mit automatischer Spracherkennung +- **11 TTS-Sprachen** (de/en/fr/es/it/nl/pt/ru/pl/uk/cs), Stimmen werden bei Bedarf automatisch heruntergeladen +- **5-Zustands-Maschine** (LAUSCH → AUFNAHME → GENERATOR → VORLESE → DIALOG) +- **Steuer-Wörter** während der Wiedergabe: Stopp, Pause, Weiter, Noch einmal, … +- **„Stopp"** funktioniert in allen aktiven Zuständen (AUFNAHME, GENERATOR, VORLESE, DIALOG) +- **Wählbares Mikrofon**: ReSpeaker XVF3800, MOTU M2, Kamera-Mikrofon, Bluetooth, Index +- **Wählbarer Ausgang**: beliebiger PipeWire-Sink + +## Voraussetzungen + +### Hardware +- Mikrofon (empfohlen: [ReSpeaker XVF3800](https://wiki.seeedstudio.com/ReSpeaker_XVF3800/) mit Hardware-AEC) +- NVIDIA GPU mit CUDA (für Whisper; llama.cpp kann separat konfiguriert werden) + +### Software +``` +Python >= 3.12 +faster-whisper +openwakeword +piper-tts +sounddevice +scipy +langdetect +openai # Python-Client (auch für lokale Backends) +``` + +PipeWire + paplay müssen als Audio-Backend aktiv sein. + +## Schnellstart + +```bash +# Verfügbare Mikrofone anzeigen +python3 assistant.py --list-mics + +# Starten mit Standard-Einstellungen (ReSpeaker, llama.cpp lokal) +python3 assistant.py + +# Ohne Wake-Word (direkt in Aufnahme-Modus) +python3 assistant.py --no-wakeword + +# Mit Ollama-Backend +python3 assistant.py --backend ollama --model llama3.2 + +# Britisches Englisch als TTS-Variante +python3 assistant.py --en-variant gb +``` + +## Dateien + +| Datei | Beschreibung | +|---|---| +| `assistant.py` | Hauptprogramm — Zustands-Maschine, LLM-Integration | +| `speak.py` | TTS-Modul — Piper-Wrapper, Spracherkennung, Sink-Auswahl | +| `mic.py` | Mikrofon-Modul — Geräteauflösung, Alias-Tabelle | +| `transcribe.py` | Standalone-Transkription zum Testen | + +## Architektur + +``` +Mikrofon (PortAudio) + │ + ▼ +audio_q + │ + ├─► LAUSCH: openwakeword → "Hey Jarvis" → AUFNAHME + ├─► AUFNAHME: Stille/Stop-Wort/Stopp → record_buffer + ├─► GENERATOR: whisper.transcribe → LLM-Stream → VORLESE + ├─► VORLESE: piper-synth (Thread) + paplay + ctrl_monitor + └─► DIALOG: 5s-Fenster für Rückfrage ohne Wake-Word +``` + +## Separate Tools + +```bash +# Sprachausgabe testen +python3 speak.py --text "Hallo Welt" --out respeaker + +# Verfügbare Ausgänge anzeigen +python3 speak.py --list + +# Live-Transkription testen +python3 transcribe.py --mic respeaker + +# Verfügbare Mikrofone anzeigen +python3 mic.py +```