diff --git a/CHANGELOG.md b/CHANGELOG.md index dc54c98..0cbd5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,19 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c - `build.sh` schreibt `org.gradle.java.home` dynamisch in `gradle.properties` — verhindert dass Gradle kaputte JVM-Pfade findet (`/usr/lib/jvm/openjdk-17` ohne bin/java) - `minSdkVersion` 21 → 23 — `react-native-camera-kit` braucht mindestens API 23 +**Docker & Infrastruktur** +- OpenClaw Image fix: `openclaw/openclaw:latest` → `ghcr.io/openclaw/openclaw:latest` +- `libportaudio2` in Bridge Dockerfile hinzugefügt — `sounddevice` braucht PortAudio +- `aria-data/config/aria.env.example` hinzugefügt — Voice Bridge Konfigurationsvorlage + +**Wake-Word Fix (openwakeword)** +- `WakeWordDetector` umgebaut — sucht Custom-Modell `/voices/wake_aria.onnx`, Fallback auf eingebautes `hey_jarvis` +- Alter Code crashte: `wakeword_models=["aria"]` erwartet Dateipfad, kein Keyword + +**Neues Script: `get-voices.sh`** +- Lädt Piper Stimmen (Ramona + Thorsten) von HuggingFace herunter +- Neuer Installationsschritt in README + **ARIA Persönlichkeit** - `AGENT.md` überarbeitet — ARIA ist jetzt Partnerin auf Augenhöhe (Claude-Charakter) - Direkt, ehrlich, humorvoll, lösungsorientiert, kein Theater diff --git a/Josef Behrens.conf b/Josef Behrens.conf new file mode 100644 index 0000000..fcb6d69 --- /dev/null +++ b/Josef Behrens.conf @@ -0,0 +1,11 @@ +[Interface] +Address = 10.252.1.21/32 +PrivateKey = 2JmAeJQ1wL+nfaAVp32RiEsPFcaoXVtZh/p7pqHGCl4= +MTU = 1450 + +[Peer] +PublicKey = IHBroF1ChESXWQQ+2RC4DmrNoHQl54Hc/xhH+iYLTBA= +PresharedKey = A1i59KCEjvwtx9J03pkcqDdGP7Jhr4PcbA5Um32iMoY= +AllowedIPs = 192.168.0.0/24 +Endpoint = stb-er.selfhost.eu:51820 +PersistentKeepalive = 15 diff --git a/README.md b/README.md index 205f90e..87b9e04 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ services: # ─── OpenClaw (ARIA Gehirn) ───────────────────────────── aria: - image: openclaw/openclaw:latest + image: ghcr.io/openclaw/openclaw:latest container_name: aria-core privileged: true # ARIAs Wohnung — sie hat die Schlüssel depends_on: @@ -405,7 +405,18 @@ cp .env.example .env # → RVS_HOST + RVS_PORT eintragen (z.B. rvs.hackersoft.de / 443) ``` -### 3. Token generieren & starten +### 3. Konfiguration & Stimmen + +```bash +# Voice Bridge Konfiguration anlegen +cp aria-data/config/aria.env.example aria-data/config/aria.env +# → Bei Bedarf anpassen (Whisper-Modell, Sprache, etc.) + +# Piper Stimmen herunterladen (Ramona + Thorsten) +./get-voices.sh +``` + +### 4. Token generieren & starten ```bash # Token erzeugen — schreibt RVS_TOKEN automatisch in .env, zeigt QR-Code @@ -415,11 +426,11 @@ cp .env.example .env docker compose up -d ``` -### 4. App verbinden +### 5. App verbinden App öffnen → QR-Code scannen → "ARIA, hörst du mich?" 🎙️ -> Alles was über diese vier Schritte hinausgeht macht ARIA selbst. +> Alles was über diese fünf Schritte hinausgeht macht ARIA selbst. --- @@ -678,6 +689,7 @@ aria/ ← Gitea Repo — hier wird entwickelt ├── README.md ← diese Datei — ARIAs Gedächtnis & Auftrag ├── docker-compose.yml ← ARIA-VM: ein Befehl startet alles ├── generate-token.sh ← Token + QR-Code erzeugen (auf ARIA-VM) +├── get-voices.sh ← Piper Stimmen herunterladen (Ramona + Thorsten) ├── .env.example ← Vorlage (echte .env nie ins Repo!) ├── .gitignore ← siehe unten │ @@ -691,6 +703,7 @@ aria/ ← Gitea Repo — hier wird entwickelt │ │ └── gitea/ │ ├── voices/.gitkeep ← ignoriert — große Binärdateien │ └── config/ +│ ├── aria.env.example ← Vorlage → kopieren nach aria.env │ ├── AGENT.md ← ARIAs Persönlichkeit & Regeln │ ├── USER.md ← Stefans Präferenzen │ └── TOOLING.md ← Liste installierter VM-Tools diff --git a/android/android/.gradle/8.3/executionHistory/executionHistory.lock b/android/android/.gradle/8.3/executionHistory/executionHistory.lock index 04daa0a..d6c5419 100644 Binary files a/android/android/.gradle/8.3/executionHistory/executionHistory.lock and b/android/android/.gradle/8.3/executionHistory/executionHistory.lock differ diff --git a/android/android/.gradle/8.3/fileHashes/fileHashes.bin b/android/android/.gradle/8.3/fileHashes/fileHashes.bin index d38de58..a5b2a6e 100644 Binary files a/android/android/.gradle/8.3/fileHashes/fileHashes.bin and b/android/android/.gradle/8.3/fileHashes/fileHashes.bin differ diff --git a/android/android/.gradle/8.3/fileHashes/fileHashes.lock b/android/android/.gradle/8.3/fileHashes/fileHashes.lock index 3016fc0..a58f99f 100644 Binary files a/android/android/.gradle/8.3/fileHashes/fileHashes.lock and b/android/android/.gradle/8.3/fileHashes/fileHashes.lock differ diff --git a/aria-data/config/aria.env.example b/aria-data/config/aria.env.example new file mode 100644 index 0000000..64f6773 --- /dev/null +++ b/aria-data/config/aria.env.example @@ -0,0 +1,4 @@ +OPENCLAW_URL=http://aria-core:18789 +PIPER_RAMONA=/voices/de_DE-ramona-low.onnx +PIPER_THORSTEN=/voices/de_DE-thorsten-high.onnx +WAKE_WORD=aria \ No newline at end of file diff --git a/bridge/Dockerfile b/bridge/Dockerfile index 4e52ffc..543369c 100644 --- a/bridge/Dockerfile +++ b/bridge/Dockerfile @@ -9,6 +9,7 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y --no-install-recommends \ ffmpeg \ libsndfile1 \ + libportaudio2 \ pulseaudio-utils \ alsa-utils \ && rm -rf /var/lib/apt/lists/* diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 8f8c404..3e0e6da 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -296,22 +296,45 @@ class STTEngine: class WakeWordDetector: - """Erkennt das Wake-Word 'aria' im Audio-Stream.""" + """Erkennt das Wake-Word im Audio-Stream. - WAKE_WORD = "aria" + Nutzt ein Custom-Modell aus /voices/wake_aria.onnx falls vorhanden, + sonst das eingebaute 'hey_jarvis' als Fallback. + """ + + CUSTOM_MODEL_PATH = "/voices/wake_aria.onnx" + FALLBACK_MODEL = "hey_jarvis" THRESHOLD = 0.5 def __init__(self) -> None: self.model: Optional[WakeWordModel] = None + self.wake_word_key: str = "" def initialize(self) -> None: """Laedt das Wake-Word-Modell.""" logger.info("Lade Wake-Word-Modell...") - self.model = WakeWordModel( - wakeword_models=[self.WAKE_WORD], - inference_framework="onnx", - ) - logger.info("Wake-Word-Modell geladen (Trigger: '%s')", self.WAKE_WORD) + + custom_path = Path(self.CUSTOM_MODEL_PATH) + if custom_path.exists(): + # Custom "aria" Modell vorhanden + self.model = WakeWordModel( + wakeword_models=[str(custom_path)], + ) + self.wake_word_key = custom_path.stem + logger.info("Custom Wake-Word-Modell geladen: %s", custom_path) + else: + # Fallback auf eingebautes Modell + self.model = WakeWordModel() + self.wake_word_key = self.FALLBACK_MODEL + logger.warning( + "Kein Custom-Modell (%s) — nutze Fallback '%s'", + self.CUSTOM_MODEL_PATH, + self.FALLBACK_MODEL, + ) + logger.info( + "Tipp: Custom Wake-Word trainieren → " + "https://github.com/dscripka/openWakeWord#training-new-models" + ) def detect(self, audio_chunk: np.ndarray) -> bool: """Prueft ob das Wake-Word im Audio-Chunk enthalten ist. @@ -328,10 +351,10 @@ class WakeWordDetector: prediction = self.model.predict(audio_chunk) # openwakeword gibt Scores pro Modell zurueck - for key, score in prediction.items(): - if score > self.THRESHOLD: - logger.info("Wake-Word erkannt! (Score: %.2f)", score) - return True + score = prediction.get(self.wake_word_key, 0) + if score > self.THRESHOLD: + logger.info("Wake-Word erkannt! (Score: %.2f)", score) + return True return False @@ -689,7 +712,7 @@ class ARIABridge: async def audio_loop(self) -> None: """Wake-Word erkennen, aufnehmen, transkribieren, an aria-core senden.""" - logger.info("Audio-Schleife gestartet — warte auf Wake-Word '%s'...", WakeWordDetector.WAKE_WORD) + logger.info("Audio-Schleife gestartet — warte auf Wake-Word '%s'...", self.wake_word.wake_word_key) loop = asyncio.get_event_loop() diff --git a/docker-compose.yml b/docker-compose.yml index f9bef7b..0007710 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: # ─── OpenClaw (ARIA Gehirn) ───────────────────────────── aria: - image: openclaw/openclaw:latest + image: ghcr.io/openclaw/openclaw:latest container_name: aria-core privileged: true # ARIAs Wohnung — sie hat die Schlüssel depends_on: diff --git a/get-voices.sh b/get-voices.sh new file mode 100755 index 0000000..6e9930b --- /dev/null +++ b/get-voices.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# ════════════════════════════════════════════════ +# ARIA — Piper Stimmen herunterladen +# Ramona (Alltag) + Thorsten (epische Momente) +# ════════════════════════════════════════════════ + +set -e + +VOICES_DIR="aria-data/voices" +BASE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/main/de/de_DE" + +mkdir -p "$VOICES_DIR" +cd "$VOICES_DIR" + +echo "Lade ARIA Stimmen..." +echo "" + +echo "[1/4] Ramona (Modell)..." +wget -q --show-progress "$BASE_URL/ramona/low/de_DE-ramona-low.onnx" + +echo "[2/4] Ramona (Config)..." +wget -q --show-progress "$BASE_URL/ramona/low/de_DE-ramona-low.onnx.json" + +echo "[3/4] Thorsten (Modell)..." +wget -q --show-progress "$BASE_URL/thorsten/high/de_DE-thorsten-high.onnx" + +echo "[4/4] Thorsten (Config)..." +wget -q --show-progress "$BASE_URL/thorsten/high/de_DE-thorsten-high.onnx.json" + +echo "" +echo "Stimmen geladen!" +ls -lh *.onnx