Commit Graph

25 Commits

Author SHA1 Message Date
duffyduck 32ddac002f fix: stream_chunk_size auf 250 erhoeht — weniger Render-Artefakte
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>
2026-04-22 18:56:00 +02:00
duffyduck 0623de32a0 tune: stream_chunk_size 200 -> 100 gegen 6s Initial-Latenz
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>
2026-04-22 18:08:10 +02:00
duffyduck cd5e6e7ee6 fix: stream_chunk_size 40 -> 200 gegen Audio-Abbrueche mid-sentence
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>
2026-04-22 18:06:25 +02:00
duffyduck 4cbe184faa feat: XTTS auf local-Mode (dauerhaft im VRAM) + /tts_stream + Fallback
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>
2026-04-22 17:38:53 +02:00
duffyduck 647a1cb726 fix: XTTS nutzt direkt /tts_to_audio/ — /tts_stream nicht verfuegbar
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>
2026-04-22 17:23:55 +02:00
duffyduck 73263b69a6 fix: /tts_stream — speaker_wav muss IMMER als query-param gesetzt sein
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>
2026-04-22 16:47:28 +02:00
duffyduck c62ceafdc2 fix: XTTS-Endpoint mit Fallback-Chain + Diagnose-Logs
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>
2026-04-22 15:53:10 +02:00
duffyduck 9b5a35cb4a fix: /tts_stream als GET mit Query-Params (war 405 Method Not Allowed)
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>
2026-04-20 16:52:16 +02:00
duffyduck 5ac1a0a522 revert: XTTS-Endpoint zurueck auf /tts_to_audio/
/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>
2026-04-20 16:48:27 +02:00
duffyduck 59c8d36a3d fix: Streaming TTS nutzt jetzt echt den /tts_stream Endpoint von XTTS
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>
2026-04-20 16:35:55 +02:00
duffyduck 402bddc18a fix: Streaming TTS — Queue in XTTS-Bridge + groesserer Android-Buffer
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>
2026-04-19 23:27:16 +02:00
duffyduck 350069d371 fix: Streaming TTS — doppeltes Audio + Gaps zwischen Saetzen
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>
2026-04-19 23:15:57 +02:00
duffyduck fc2438be2d fix/feat: XTTS-Voice korrekt persistiert, Loeschen + Voice-per-Request
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>
2026-04-19 22:43:26 +02:00
duffyduck 6ab6196739 feat: Streaming TTS — PCM-Stream statt WAV-Chunks (Weg A)
Pipeline: XTTS-Server → xtts-bridge → aria-bridge → RVS → App AudioTrack

XTTS-Bridge (Gaming-PC):
- streamXTTSAsPCM(): liest /tts_to_audio/ Response inkrementell,
  parst WAV-Header (samplerate/channels), teilt PCM in 8KB-Chunks
  (~170ms bei 24kHz s16 mono) und sendet jeden als audio_pcm.
- Finaler Chunk mit final=true nach letztem Text-Chunk

aria-bridge:
- audio_pcm Handler leitet payload 1:1 weiter, filled messageId aus
  requestId → messageId Map falls XTTS-Bridge messageId nicht hatte
- Alter xtts_response Pfad bleibt als Legacy-Fallback (WAV)

RVS: audio_pcm in ALLOWED_TYPES

Android Native:
- PcmStreamPlayerModule (Kotlin): AudioTrack MODE_STREAM mit
  Writer-Thread und BlockingQueue. start(rate, ch) / writeChunk(b64)
  / end() / stop()
- 8x MinBufferSize grosszuegig dimensioniert, glatt auch bei
  Netz-Aussetzern
- Registered im MainApplication via PcmStreamPlayerPackage

App JS:
- audioService.handlePcmChunk(): erkennt neue Session (messageId-Wechsel),
  started nativen Stream, cached PCM-Bytes pro Message. Bei final=true
  Stream sauber schliessen + _savePcmBufferAsWav → WAV-File im
  tts_cache/<messageId>.wav
- _savePcmBufferAsWav: baut 44-byte WAV-Header (PCM s16le, korrekte
  samplerate/channels), haengt alle gesammelten base64-PCM-Chunks an
- stopPlayback beendet auch aktiven PCM-Stream
- ChatScreen routet type=audio_pcm an handlePcmChunk, bei final
  setzt audioPath in der Message

Play-Button: falls messageId einen audioPath hat → WAV aus Cache
(Sound-basiert), egal ob Original-TTS Piper oder XTTS war.

Audio-Focus:
- requestDuck() beim Stream-Start, release() bei Stream-Ende
- Andere Apps (Spotify etc.) werden leiser waehrend ARIA spricht

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:01:27 +02:00
duffyduck 764619f076 fix: Comprehensive markdown/formatting cleanup for TTS (Piper + XTTS)
- Remove **bold**, *italic*, `code`, code blocks, links, headers, quotes, lists
- Replace newlines with natural pauses (period/comma)
- Remove quotation marks, empty brackets
- Fixes text being swallowed/garbled by TTS engines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:47:04 +02:00
duffyduck 949c573c49 fix: XTTS chunk size 150 chars (faster render, preload overlaps playback)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:52:56 +02:00
duffyduck f7f450a09d fix: XTTS streaming mode - send each chunk immediately, comma between sentences
- Back to streaming: render chunk → send immediately → next chunk
- App plays with preloading queue (no waiting for all chunks)
- Comma instead of dot between sentences in chunk (no "Punkt" read aloud)
- Sentence-ending dots already removed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:48:50 +02:00
duffyduck 81f7c38383 fix: XTTS splits concatenated audio into ~8s parts (seamless with preload)
- All chunks rendered and PCM concatenated (consistent voice)
- Split into ~8 second WAV parts (not per-sentence)
- 8s is long enough for preload overlap, small enough for WebSocket
- Parts include part/totalParts metadata

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:41:14 +02:00
duffyduck 2c785cb37a feat: XTTS concatenates chunks into seamless WAV (no stuttering)
- All chunks rendered sequentially, PCM data concatenated
- Single WAV with proper header sent back (no queue needed in app)
- If total > 800KB, split into parts (WebSocket limit)
- Eliminates stuttering between sentences

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:40:16 +02:00
duffyduck 8929bc99bb fix: XTTS groups sentences into ~250 char chunks for consistent voice quality
- 2-3 sentences per chunk (more context = stable voice/volume)
- Max 250 chars per chunk (keeps WebSocket packets manageable)
- Dots re-added between sentences within a chunk (natural pauses)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:23:29 +02:00
duffyduck 0428c06612 fix: Audio preloading to prevent stuttering, remove trailing dots for XTTS
- Preload next audio while current plays (eliminates gap between sentences)
- Remove trailing dots from sentences (XTTS reads them aloud)
- stopPlayback cleans up preloaded audio

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:21:19 +02:00
duffyduck b3d3b8b6bc fix: XTTS bridge splits text into sentences sequentially
- XTTS-Bridge does sentence splitting (not ARIA-Bridge)
- Sequential rendering: correct order guaranteed
- Each sentence sent as separate xtts_response
- Markdown removal before splitting
- App starts playback after first sentence (faster UX)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:03:29 +02:00
duffyduck 16847ce6f7 fix: TTS toggle global above engine selector, health check /docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:27:55 +02:00
duffyduck a1e1ee31bd fix: XTTS bridge port 8020, longer startup wait
- XTTS API runs on port 8020 (not 8000)
- Bridge waits up to 5min for model download (30x10s)
- Health check uses / instead of /docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:39:45 +02:00
duffyduck a242693751 feat: XTTS v2 integration, auto-update system, TTS engine abstraction
- XTTS v2: Docker setup for Gaming-PC (GPU), bridge via RVS relay
- XTTS: Voice cloning UI in Diagnostic (multi-file upload)
- XTTS: Engine selectable (Piper local vs XTTS remote) with fallback
- Auto-Update: RVS serves APK over WebSocket (no HTTP needed)
- Auto-Update: App checks version on start, prompts install
- Auto-Update: release.sh copies APK to RVS via scp
- Bridge: TTS engine abstraction (piper/xtts), config persistent
- Bridge: xtts_response handler, tts_request on-demand
- Diagnostic: TTS engine dropdown, XTTS voice panel, voice cloning
- App: Play button on ARIA messages, chat search, update service
- Wake word: Disabled LiveAudioStream (crash fix), Phase 1 placeholder
- Watchdog: Container restart after 8min stuck
- Chat backup: on-the-fly to /shared/config/chat_backup.jsonl

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:42:10 +02:00