AudioFocus.requestDuck nutzt jetzt AUDIOFOCUS_GAIN_TRANSIENT (statt
TRANSIENT_MAY_DUCK) — Spotify/YouTube pausieren komplett solange ARIA
spricht und kommen nicht mitten drin wieder hoch.
PcmStreamPlayer.end() resolved jetzt erst wenn der native Writer-Thread
wirklich fertig ist (alle Samples aus dem Pre-Roll-Puffer ausgespielt).
audio.ts wartet entsprechend, bevor AudioFocus.release() gerufen wird —
behebt das "Musik dreht hoch waehrend Antwort noch laeuft"-Problem.
Mic-Aufnahme: VAD_SILENCE_DURATION_MS 1800 → 2800ms (mehr Toleranz fuer
Sprechpausen), MAX_RECORDING_MS 30s → 120s (laengere Erklaerungen
moeglich, Notbremse bleibt).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neuer aria-f5tts-bridge Container:
- Python-Service, laedt F5TTS_v1_Base beim Start
- Empfaengt xtts_request via RVS, synthetisiert mit Voice-Cloning,
streamt PCM-Chunks (audio_pcm, 16-bit s16le) wie zuvor die XTTS-Bridge
- Teilt lange Texte an Satzgrenzen, streamt satzweise
- Fade-In auf erstem Chunk, Queue gegen parallel-Render
Voice-Management:
- Speicherort weiterhin /voices/, aber jetzt als Paar
{name}.wav + {name}.txt (F5-TTS braucht Referenz-Transkription)
- voice_upload: WAV speichern, intern stt_request an whisper-bridge
senden, Transkription als .txt ablegen → user muss nichts eintippen
- On-the-fly Transkribierung: wenn eine WAV ohne .txt liegt, wird
bei erstem Render/Preload nachgezogen
- Bestehende RVS-Messages (voice_upload/xtts_list_voices/... etc.)
bleiben unveraendert → keine App/Diagnostic-Aenderung noetig
Gaming-PC docker-compose:
- xtts + xtts-bridge Services entfernt
- f5tts-bridge + whisper-bridge bleiben/kommen rein
- Volume xtts-models → f5tts-models
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neuer Container aria-whisper-bridge auf der Gamebox — faster-whisper
CUDA mit float16. Der Container verbindet sich per WebSocket an den RVS,
nimmt stt_request entgegen, laeuft ffmpeg+Whisper, antwortet mit
stt_response. Hoert zusaetzlich auf config-Broadcasts und lädt das
Modell hot-swap bei Diagnostic-Wechsel.
aria-bridge ruft jetzt primaer die Gamebox an; nur wenn die nicht binnen
45s antwortet, faellt auf lokales Whisper (CPU) zurueck. Das lokale
Modell wird lazy geladen, spart RAM auf der VM.
RVS: stt_request/stt_response zur ALLOWED_TYPES-Liste.
Diagnostic-Voice-Config (whisperModel-Feld) bleibt unveraendert —
die Auswahl wird an die Gamebox durchgereicht.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS-Bridge:
- empfaengt neuen voice_preload Type, rendert stumm "ja." fuer die Stimme
via TTS-Queue (damit kein Konflikt mit echtem TTS)
- horcht zusaetzlich auf config-Broadcasts: wenn Diagnostic global die
Stimme wechselt, wird auto-preloaded
- broadcastet voice_ready mit Dauer (loadMs) oder error
RVS: voice_preload + voice_ready zur ALLOWED_TYPES-Liste.
App (SettingsScreen): beim Wechsel senden wir voice_preload, zeigen einen
Spinner in der Voice-Row und einen Toast mit "Stimme X bereit (Ns)".
App (ChatScreen): Toast auch hier — falls User gerade nicht in Settings ist.
Diagnostic (server+UI): voice_ready wird an Browser durchgereicht, ein
Status-Text unter dem Voice-Dropdown zeigt "wird geladen" → "bereit".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS-Bridge: im daswer123 local-Mode erwartet der Server speaker_wav als
Basename (z.B. "Maia"), nicht als Pfad. Wir haben bisher "/voices/Maia.wav"
geschickt, was der Server stumm verwirft und Default nimmt. Jetzt: speaker
name pur senden + Warnlog wenn File fehlt.
App: ChatScreen + SettingsScreen horchen auf type "config" vom RVS —
wenn in Diagnostic die globale XTTS-Voice gewechselt wird, werden alle
Apps auf den neuen Wert zurueckgesetzt (wie vom User gewuenscht).
Lokale App-Wahl bleibt sonst intakt und gewinnt pro Request.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nach der expliziten _UNIT_WORDS-Liste greift eine Fallback-Regel:
alle verbleibenden 2-5-Zeichen-Grossbuchstaben-Woerter werden
buchstabiert. XTTS → X T T S, USB → U S B, DNS → D N S, JSON → J S O N.
Spezielle Faelle (WLAN, NATO — als Wort gesprochen) koennen bei
Bedarf in _UNIT_WORDS explizit ueberschrieben werden.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Kotlin start() nimmt jetzt prerollSeconds als dritten Parameter
(1.0-6.0s geclampt, Fallback 3.5s bei ungueltigem Wert)
- audio.ts liest Wert aus AsyncStorage vor jedem Stream-Start,
exportiert Default/Min/Max/Key als Konstanten
- SettingsScreen: +/- Buttons direkt unter dem TTS-Toggle,
Default auf 3.5s (von 2.5s) angehoben
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS daswer123 hat am Anfang jedes Renders Warmup-Artefakte — die
ersten autoregressiv generierten Tokens haben wenig Kontext und klingen
verzerrt. Ein 120ms Linear-Fade-In auf den ersten ausgehenden PCM-Chunk
blendet das sanft auf und versteckt die Glitches, ohne dass das echte
Audio danach leiser klingt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS daswer123 erzeugt an Chunk-Grenzen oft Glitches in den Worten
die ueber die Grenze gehen. 100 → 250 = weniger Grenzen pro Satz =
sauberere Sprachausgabe. Erste-Audio-Latenz steigt um ein paar Sekunden,
was aber OK ist seit die App Pre-Roll gepuffert ist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- aria_bridge clean_text_for_tts: "0.1" / "0,5" / "1,25" wird jetzt als
"null komma eins" / "null komma fuenf" / "eins komma zwei fuenf"
ausgeschrieben. Lookahead verhindert Match auf IP-artige Strings.
- PcmStreamPlayer: 200ms Stille am Stream-Anfang, damit AudioTrack
sauber anfaehrt und die ersten Worte nicht verschluckt werden.
(XTTS-Warmup + play()-Startup-Latenz)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AudioTrack.stop() + release() direkt nach dem letzten write() killt die
letzten Sekunden Audio — die Samples sind zwar im Buffer, aber noch
nicht durch die Hardware rausgespielt. Deshalb brach die Sprachausgabe
mitten im Satz ab (z.B. bei "diesmal").
Fix: Writer-Thread wartet im finally-Block bis playbackHeadPosition die
Anzahl geschriebener Frames erreicht, dann erst stop()/release().
Safety: 2s Stall-Detection, falls AudioTrack haengen bleibt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-Diagnose: Erneutes Abspielen aus Cache funktioniert komplett, aber
Live-Stream bricht ab. Bedeutet: PCM kommt an, Cache ist okay — Problem
ist Buffer-Underrun im AudioTrack wenn XTTS (RTF 1.48 auf RTX 3060)
langsamer rendert als Echtzeit-Playback konsumiert.
Fix: AudioTrack.play() wird NICHT mehr sofort beim start() aufgerufen.
Stattdessen:
- start() baut AudioTrack, Writer-Thread startet, spielt aber noch nicht
- writeChunk() fuellt queue, Writer schreibt in AudioTrack-internen Buffer
(blocked wenn der voll ist)
- Sobald bytesBuffered >= 2.5s Audio im Buffer: play() aufrufen
- Falls end() kommt bevor Pre-Roll erreicht (kurze Texte): trotzdem play()
Das gibt dem Stream Zeit Vorrat aufzubauen. XTTS kann dann pausieren
zwischen Text-Chunks ohne dass Playback stottert.
Pre-Roll 2.5s reicht fuer typische Render-Pausen zwischen Chunks.
Buffer groesse = 2x Pre-Roll damit wir auch extrem bursty Delivery
puffern koennen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mit RTF 1.48 (RTX 3060) rechnet XTTS fuer 200 chars ca. 6s bis erster
PCM-Chunk rauskommt — User wartet nach ARIA-Antwort 6s auf Sprachausgabe.
stream_chunk_size=100: Erster Chunk in ~3s bereit, reduziert
Initial-Latenz um ~50%. 100 chars sind auch noch gross genug dass
der AudioTrack-Buffer (128KB ≈ 2.7s Audio) zwischen Render-Chunks
nicht leerlaeuft → kein mid-sentence Abbruch wie bei 40.
Falls bei bestimmten Texten doch Gaps: stream_chunk_size zurueck auf
150, oder pre-roll im Android PcmStreamPlayer einbauen (nur starten
wenn X ms gepuffert sind).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bei stream_chunk_size=40 teilte XTTS Text in ~40-char Batches.
Zwischen Batches pausiert XTTS (RTF 1.48 auf RTX 3060 → langsamer
als Realtime-Wiedergabe). AudioTrack-Buffer lief leer, Track
stoppte, nachkommender PCM kam zu spaet → Audio bricht mid-sentence
ab (User-Bug: bei 73-char Text Abbruch nach Wort 'diesmal' was genau
an der 40-char Grenze lag).
stream_chunk_size=200:
- Kurze Saetze (<200 chars) komplett in einem Render → kein Abbruch
- Laengere Texte: groessere Chunks, laenger Audio pro Chunk als
Render-Pause → Buffer bleibt gefuellt
- Kompromiss: first-audio-latency etwas hoeher, aber keine Abbrueche
Wenn spaeter Audio-Abbrueche bei langen Texten: stream_chunk_size
noch groesser setzen ODER einen "pre-roll" Buffer in der App.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Das Image-Default-CMD liest Konfig aus ENV Variablen:
CMD: ... -ms \${MODEL_SOURCE:-"apiManual"}
Also reicht MODEL_SOURCE=local — command bleibt Image-Default und wir
sparen uns den brueckigen Override der schief ging (python nicht da,
flag-Namen raten, etc.).
Zusaetzlich: EXAMPLE_FOLDER=/voices damit der Speaker-Folder auf unser
gemountetes /voices zeigt (sonst /app/example was nur die Demo-Voices
enthaelt).
Kein command override mehr noetig — das Image macht alles wie vorher,
nur mit local-Mode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Image hat nur /usr/bin/python3, kein 'python'-Symlink.
Vorher ging's weil kein command override — das Image-Default CMD
lief durch. Wir ueberschreiben nur damit wir -ms local setzen koennen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NVIDIA-Entrypoint fuehrt 'exec \$@' aus — erstes Arg muss ein
ausfuehrbares sein. Nur Flags zu geben ('--listen') fuehrt zu
'exec: --: invalid option'.
Fix: command=['python','-m','xtts_api_server','-ms','local',...]
Damit wird der xtts_api_server Python-Modul gestartet und im
local-Mode konfiguriert.
Ob die Flag-Namen exakt stimmen (-hs/-p/-ms/-o/-mf/-sf) — falls
nicht, poppt ein klarer Python-Fehler im Log.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause der langen Render-Zeiten und /tts_stream 400-Errors:
daswer123 default ist apiManual/api-Mode — Modell wird pro Request
gefetched/reloaded, Streaming unsupported.
Fix in xtts/docker-compose.yml:
command: ['--listen', '-p', '8020', '-t', 'http://0.0.0.0:8020',
'-ms', 'local',
'-o', '/app/output', '-mf', '/app/xtts_models', '-sf', '/voices']
-ms local:
- Modell dauerhaft im GPU-VRAM (~2GB, passt auf RTX 3060 mit 12GB)
- Render startet sofort, kein per-Request-Load mehr
- /tts_stream unterstuetzt → echtes progressive streaming
- time-to-first-audio ~500ms statt 8-11s
xtts/bridge.js:
/tts_stream primary, /tts_to_audio/ als Fallback wenn Stream fehlt.
Robust: wenn User spaeter den Mode wieder umstellt, fallback greift.
Erste Ladung nach dem Wechsel dauert einmalig laenger (Modell ins VRAM
laden). Danach: schnell + streaming.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS-Server (daswer123) im API-Modus antwortet auf /tts_stream mit:
HTTP 400: "HTTP Streaming is only supported for local models"
Das Feature braucht MODE=local in der XTTS-Config (Modell direkt im
Server-Prozess). Userbetreibt im Remote-Modus → kein Streaming.
Der try /tts_stream + fallback /tts_to_audio Ansatz war reine Ver-
schwendung: jeder Request wartete 6ms auf 400, bevor der Fallback
griff. Jetzt geht's direkt an /tts_to_audio/.
Kein echtes Streaming, aber:
- Queue sorgt fuer sequentielle Verarbeitung (kein Overlap mehr)
- 32x AudioTrack-Buffer faengt den bursty Response ab
- aria-bridge spiegelt audio_pcm nicht mehr (kein Doppel-Audio)
Wenn User spaeter /tts_stream haben will:
XTTS-Server mit MODE=local oder --streaming-mode starten,
dann kann man /tts_stream als primary einfuehren.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XTTS-Server (daswer123) markiert speaker_wav als required Pydantic-Feld.
Mein 'if (speakerWav) qs.set(...)' hat den Key bei default-voice
weggelassen → HTTP 422 'Field required, input: null' → Fallback auf
/tts_to_audio/ hat gegriffen, aber Streaming nie gefunden.
Log-Beweis vom User:
XTTS /tts_stream 422: {"detail":[{"type":"missing","loc":["query",
"speaker_wav"],"msg":"Field required","input":null}]}
Fix: Key immer setzen, leerer String bei default-voice. POST-Variante
(/tts_to_audio/ JSON-Body) hat das auch so akzeptiert — GET-Query nun
gleiches Verhalten.
Ab jetzt sollte /tts_stream endlich greifen und echte Streaming-Latenz
(~300-500ms) zeigen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem: /tts_stream hat bei User nicht funktioniert → keine
Sprachausgabe mehr. Server hatte vorher 405 fuer POST geantwortet,
meine Umstellung auf GET scheint aber einen anderen Fehler zu
produzieren der nicht geloggt wurde.
Fix:
- streamXTTSAsPCM() = /tts_stream (GET, Streaming) mit ausfuehrlichem
Error-Logging bei non-200 Response
- streamXTTSBatch() = /tts_to_audio/ (POST, Batch) als Fallback
- handleTTSRequest versucht Stream zuerst, bei Exception Fallback
auf Batch — so gibt's IMMER Audio, auch wenn /tts_stream kaputt ist
- Log zeigt welcher Pfad benutzt wurde
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
daswer123 xtts-api-server hat /tts_stream nur als GET:
allow: GET → POST gab 405 → Request hing.
Umstellung:
- method: 'GET'
- text/language/speaker_wav/stream_chunk_size als URLSearchParams
im Query-String
- kein body mehr (kein req.write, kein Content-Length)
Ab jetzt echter streaming-Flow: Samples kommen waehrend XTTS noch
rendert, time-to-first-audio ~300-500ms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/tts_stream war bei der aktiven daswer123-Version nicht erreichbar —
Requests hingen stille, App bekam kein Audio.
Zurueck auf /tts_to_audio/ + Queue + 32x AudioTrack-Buffer. Das ist
zwar nicht echt-streaming aber stabil. Ueberlappung sollte durch die
Queue weg sein, Buffer toleriert den bursty Delivery.
Echt-Streaming-Migration spaeter mit verifizierter Server-Version
oder anderem Endpoint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vorher: /tts_to_audio/ — XTTS rendert kompletten WAV BEVOR es
antwortet. Mein "streaming" war nur fake-chunking des fertigen WAV.
Time-to-first-audio = komplette Render-Zeit (2-4s), dann Burst,
dann Stille. Plus bei langen Antworten: Queue blockiert.
Jetzt: /tts_stream — daswer123's chunked-transfer endpoint.
Samples flutschen waehrend der Generierung durch die Response raus.
Parameter:
- stream_chunk_size=40 → XTTS rendert in ~40-char Haeppchen intern,
time-to-first-audio ~300-500ms statt 2-4s
- WAV-Header kommt wie gewohnt am Anfang (44 Bytes), danach raw PCM
→ mein existierender Header-Parser + 8KB-Chunker passen weiter
Voraussetzung: daswer123/xtts-api-server hat diesen Endpoint (ab
Version ~0.8.x). Sollte bei der aktuellen Version drin sein.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Safe-Variante (Default):
docker builder prune -a -f && docker image prune -a -f
→ Build-Cache + ungenutzte Images, KEINE Volumes angefasst.
→ 90% des Platzproblems geloest, Null Datenverlust-Risiko.
Aggressive Variante (nur auf Wunsch, hinter 'Mehr'-Button):
docker system prune -a --volumes -f
→ Zusaetzlich ungenutzte Volumes.
→ Nur sicher wenn alle ARIA-Container LAUFEN (sonst werden
openclaw-config/claude-config/aria-shared als "ungenutzt"
behandelt und zerstoert — Sessions weg).
→ Hinweistext orange hervorgehoben mit Warnung.
Banner-Button 'Sicher aufraeumen' kopiert die sichere Variante.
'Mehr' klappt die Erklaerung der aggressiven Variante aus.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Server:
- checkDiskSpace() prueft alle 30s 'df -B1 /shared' (zeigt Host-Disk
da /shared ein Volume auf dem Docker-FS ist)
- 4 Stufen: ok (<70%), info (70%), warn (85%), critical (95%)
- Broadcastet disk_status nur bei Aenderung (Level oder Prozent)
- currentDiskStatus wird gecached → neu verbundene Clients bekommen
den aktuellen Stand sofort beim 'init'
UI:
- Sticky Banner ganz oben, versteckt wenn Disk ok
- Farbe nach Level: gelb (info), orange (warn), rot (critical)
- Zeigt Prozent, Used/Total/Avail in GB, konkrete Situation
- Cleanup-Command als monospace Code mit Copy-Button ('docker system
prune -a --volumes -f') — Click auf Code oder Button kopiert ins
Clipboard, Fallback auf Range-Selektion
- 'Schliessen' Button fuer temporaeres Ausblenden (kommt aber wieder
bei naechster Aenderung)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1) Ueberlappende Streams
Wenn zwei xtts_requests schnell hintereinander kamen, rannten
sie parallel durch handleTTSRequest. Beide HTTP-Requests an XTTS
liefen gleichzeitig, beide streamen PCM an App → Chunks aus BEIDEN
Renders landeten interleaved in der AudioTrack-Queue → Chaos.
Fix: ttsQueue als Promise-Chain — handleTTSRequest() haengt sich
ans Ende der Kette an. Requests werden sequenziell abgearbeitet.
2) AudioTrack-Buffer zu klein fuer bursty Delivery
XTTS /tts_to_audio/ ist NICHT echt streaming — der Server rendert
intern den kompletten WAV und schickt ihn dann burst-weise. Der
alte 8x-MinBuffer (ca 200-400ms) war zu klein um das abzufangen.
Fix: Buffer auf 32x MinSize / mind. 128KB = ca. 2.7s bei 24kHz.
Das toleriert typische XTTS-Render-Latenz.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zwei Probleme gefunden:
1) DOPPELTES AUDIO (Kern-Ursache der Artefakte)
aria-bridge hat audio_pcm von XTTS-Bridge empfangen und per
_send_to_rvs rebroadcastet. RVS broadcast geht an ALLE Clients
ausser Sender — die App bekam jeden Chunk also zwei mal:
XTTS-Bridge → RVS → App + aria-bridge
aria-bridge → RVS → App (nochmal!) + XTTS-Bridge
Zwei ueberlagerte PCM-Streams klingen wie Doubled/Artefakte.
Fix: aria-bridge ignoriert audio_pcm jetzt. messageId schickt
XTTS-Bridge selbst im Payload (via xtts_request -> messageId).
2) GAPS ZWISCHEN SAETZEN (abgehackt)
xtts/bridge.js teilte Text in ~150-char Chunks und rief pro Chunk
einen eigenen /tts_to_audio/ Request. Zwischen Chunks lag die
XTTS-Render-Zeit (1-3s) → hoerbare Pausen.
Fix: cleanText geht JETZT in einem Request komplett an XTTS.
Ein zusammenhaengender Stream → keine Satz-Gaps mehr.
Kompromiss: Erste Samples kommen spaeter (ganze Text-Render dauert
laenger als der erste Satz alleine), aber dann kontinuierlich
ohne Unterbrechung.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vorher: Diagnostic's setMode sendete einen faked chat mit der
Aktivierungsphrase ('ARIA, Hangar-Modus') — das wurde erst in
_process_core_response auf dem ARIA-Antwort-Text detected, war
unzuverlaessig und nutzte nicht den sauberen mode-Message-Path.
Nachher: sauberer set_mode-Pfad mit Live-Sync.
diagnostic/server.js:
- Neue action 'set_mode' → sendet type=mode an RVS direkt
- RVS-Message-Handler: type=mode Broadcast von Bridge wird an
Browser-Clients durchgereicht
diagnostic/index.html:
- setMode() nutzt jetzt action=set_mode (keine Phrase mehr)
- updateModeUI separat — wird bei Broadcast auch aufgerufen
- Mode-Broadcast vom Server syncs UI live (andere Diagnostic/App
hat gewechselt → unser UI aktualisiert sofort)
- Button data-mode + MODE_LABELS auf kanonische IDs umgestellt
(nicht_stoeren, fluester statt dnd, whisper)
bridge/modes.py:
- canonical_id() liefert die IDs die App + Diagnostic kennen
(nicht_stoeren, fluester, ...) — damit Broadcast-ID zur UI-ID passt
bridge/aria_bridge.py:
- _broadcast_current_mode nutzt canonical_id statt enum.name.lower()
Flow jetzt:
Diagnostic wechselt Mode → set_mode → Bridge → persist + broadcast
→ alle Apps + alle Diagnostic-Browser-Tabs aktualisieren sofort
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vorher:
- Modus war nur in-memory in der Bridge, Restart = zurueck auf NORMAL
- App-Wechsel wurde zwar empfangen, aber nicht an andere Geraete
gebroadcastet (nur Bestaetigung an den Sender)
- Neue App-Verbindung wusste nicht welcher Modus gerade aktiv ist
Jetzt:
- Persistiert in /shared/config/mode.json beim Wechsel
- Beim Bridge-Start: _load_persisted_mode() holt letzten aktiven Modus
- _broadcast_current_mode() sendet an ALLE Clients (Broadcast) —
jedes verbundene Geraet bekommt live den Wechsel mit
- Bei RVS-Reconnect: sofortiger Broadcast damit neu verbundene Apps/
Diagnostic ihre UI syncen koennen
- Loop-Schutz: payload.sender=="bridge" wird im mode-Handler ignoriert
(sonst echo → Broadcast-Storm bei verbundenem RVS)
Beispiel-Flow:
Geraet A aktiviert 'Hangar'
→ Bridge empfaengt mode-msg
→ persist in mode.json
→ broadcast an alle Clients (mit sender="bridge")
→ Geraet B/C/Diagnostic empfangen → UI updated sofort
→ Bridge-Restart spaeter: HANGAR wird wieder geladen
Anmerkung zu echten OS-Push bei geschlossener App:
Das braucht FCM/Firebase + BackgroundService — deutlich mehr Arbeit,
ist separat als Feature fuer spaeter zu sehen. Live-Sync bei geoeffneter
App (WebSocket verbunden) funktioniert jetzt zuverlaessig.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: App ModeSelector sendet rvs.send('mode', { mode: 'normal' })
mit ID, Bridge's detect_mode_switch() sucht aber nach Aktivierungs-
phrasen wie 'aria, normal-modus' → kein Match → Modus-Wechsel
wurde ignoriert, TTS-Verhalten blieb auf NORMAL haengen.
Fix:
- modes.py: mode_from_id() mappt IDs zu Mode-Enum
('normal', 'dnd', 'nicht_stoeren', 'fluester', 'whisper',
'hangar', 'gaming' — flexibel)
- aria_bridge.py: mode-Handler versucht erst ID-Mapping, dann
Phrasen-Erkennung als Fallback
- Unbekannte Modi werden geloggt
- Bestaetigung wird an alle Clients zurueckgesendet damit App-UI
synchron bleibt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App Settings: Voice-Sektion (nur wenn TTS an)
- Liste aller XTTS-Server-Stimmen mit Auswahl-Radio + X zum Loeschen
- 'Standard' fuer Diagnostic-Default-Voice (keine lokale Ueberschreibung)
- 'Aktualisieren' Button laedt Liste neu (xtts_list_voices via RVS)
- 'Eigene Stimme aufnehmen' oeffnet VoiceCloneModal
VoiceCloneModal: 30s Aufnahme + Upload
- Vorlese-Text (>30s Lesedauer, thematisch passend)
- Rot-pulsierender Stop-Button, live Timer + Progressbar
- Auto-Stop bei 30s, Hinweise ab 15s ('genug fuer gute Clonung')
- Nach Stop: Namenseingabe (a-Z, 0-9, _, -), Upload via voice_upload
- Nach Upload: Modal schliesst, Settings bekommt xtts_voice_saved
und setzt automatisch die neue Stimme als gewaehlt
Voice-Flow App → Bridge → XTTS (geraetelokal):
- Jeder chat/audio/tts_request schickt aria_xtts_voice (AsyncStorage)
mit der Message mit
- Bridge speichert _next_voice_override bei chat/audio Empfang,
nutzt es fuer die naechste ARIA-Antwort und resettet dann
- Fallback: globale xtts_voice aus voice_config.json (Diagnostic)
Ergebnis:
- Gerat A hat 'stefan' geclont → ARIA antwortet Geraet A mit stefan
- Gerat B hat nichts gewaehlt → ARIA antwortet Geraet B mit Default
- Diagnostic-Einstellung wirkt als fallback-default fuer neue Geraete
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug-Fix: Voice-Auswahl verschwand nach Page-Load
- xtts_voices_list Handler rebuildet das Dropdown — vorheriger select.value
ging dabei verloren. Jetzt wird der Wert gemerkt und nach Rebuild
wiederhergestellt (falls die Stimme noch existiert).
Feature: Stimmen loeschen (Diagnostic)
- XTTS-Bridge: neuer handleDeleteVoice — entfernt /voices/<name>.wav
und schickt aktualisierte Liste per xtts_voices_list
- RVS: xtts_delete_voice in ALLOWED_TYPES
- Diagnostic Server: Action xtts_delete_voice forwarded via RVS
- Diagnostic UI: renderVoiceList zeigt alle Custom-Voices mit X-Button
Bei Loeschen der gerade aktiven Stimme: auf Default zuruecksetzen
Feature: Voice-per-Request in Bridge
- App kann mit jedem Chat ein voice-Feld mitschicken
- Bridge merkt sich _next_voice_override, nutzt es fuer die NAECHSTE
ARIA-Antwort (einmalig, dann reset)
- tts_request (Play-Button) akzeptiert voice im Payload als Override
- Fallback: globale xtts_voice aus voice_config.json
- So kann jedes Geraet seine eigene Stimme haben ohne den globalen
Default zu aendern
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SettingsScreen:
- Piper-Reste entfernt (defaultVoice, highlightVoice, Speed-Slider,
Highlight-Trigger-Info)
- Nur noch EIN Toggle 'Sprachausgabe auf diesem Geraet' — geraetelokal,
persistent in aria_tts_enabled (AsyncStorage)
- Keine Config-Propagation mehr via RVS (das waere ja global gewesen)
- Hinweis dass Stimme + Voice-Cloning zentral in der Diagnose sind
ChatScreen: Mund-Button (👄 / 🤐)
- Neben Ohr-Button im Eingabebereich, NUR sichtbar wenn TTS im Setting
grundsaetzlich aktiv ist
- Tap toggelt Mute: 👄 an / 🤐 rot gemutet
- Persistent in aria_tts_muted (AsyncStorage)
- Stoppt bei Muten sofort laufende Wiedergabe (stopPlayback)
- Settings-Toggle wird alle 2s gepollt damit Aenderungen greifen
(einfache Loesung ohne globalen State-Context)
Audio-Handling respektiert lokalen Zustand
- Incoming audio/audio_pcm: nur abspielen wenn ttsDeviceEnabled && !ttsMuted
- Cache wird TROTZDEM immer geschrieben — Play-Button funktioniert
spaeter aus Cache, auch waehrend Mute
- audioService.handlePcmChunk akzeptiert silent-Flag: skipt AudioTrack
aber baut weiterhin den WAV-Cache pro messageId
Jedes Android-Geraet mit der App hat seinen eigenen Mute-Zustand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Breaking Change: wenn XTTS-Bridge (Gaming-PC) offline ist, bleibt ARIA
stumm. Chat-Antworten kommen weiter an, aber kein Audio. Das ist
bewusst akzeptiert — XTTS klingt einfach grauenhaft viel besser.
Bridge (aria_bridge.py):
- from piper import ... raus
- VoiceEngine-Klasse komplett entfernt (synthesize, speak, select_voice)
- EPIC_TRIGGERS + load_epic_triggers raus (Highlight-Voice-Feature
ohne Piper sinnlos)
- self.voice_engine, voice_name, requested_voice Aufrufe weg
- _process_core_response: immer XTTS, kein Fallback
- tts_request Handler: immer XTTS
- config Handler: nur ttsEnabled + xttsVoice + whisperModel
- import wave raus
bridge/requirements.txt: piper-tts raus
bridge/Dockerfile: Kommentar aktualisiert
docker-compose.yml: ./aria-data/voices Mount raus
aria-data/config/aria.env.example: PIPER_RAMONA/PIPER_THORSTEN raus
get-voices.sh: komplett geloescht (war nur Piper-Downloader)
Diagnostic UI (index.html):
- Piper Panel (Standard-Stimme / Highlight-Stimme / Speed-Sliders) weg
- TTS Engine Dropdown weg (immer XTTS)
- TTS Diagnose Tab zeigt nur noch XTTS-Status + Test-Button
- sendVoiceConfig sendet nur noch ttsEnabled/xttsVoice/whisperModel
- toggleXTTSPanel als no-op Legacy-Stub (JS-Calls bleiben safe)
Diagnostic Server (server.js):
- handleSendVoiceConfig: nur noch ttsEnabled + xttsVoice + whisperModel
- handleTestTTS: via xtts_request (nicht mehr Piper subprocess)
- handleCheckTTS: via xtts_list_voices ueber RVS
- handleGetVoiceConfig/Defaults bereinigt
- Highlight-Trigger UI bleibt, wird aber von Bridge nicht mehr
ausgewertet (dead-code im UI, spaeter ggf. fuer XTTS-Voice-Switch)
README + issue.md aktualisiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>