From 402bddc18a31f5d7057858fd88fb307215e101ad Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sun, 19 Apr 2026 23:27:16 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Streaming=20TTS=20=E2=80=94=20Queue=20in?= =?UTF-8?q?=20XTTS-Bridge=20+=20groesserer=20Android-Buffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../java/com/ariacockpit/PcmStreamPlayerModule.kt | 6 ++++-- xtts/bridge.js | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt b/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt index ab8d8b1..ee84268 100644 --- a/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt +++ b/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt @@ -50,8 +50,10 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex val channelConfig = if (channels == 2) AudioFormat.CHANNEL_OUT_STEREO else AudioFormat.CHANNEL_OUT_MONO val encoding = AudioFormat.ENCODING_PCM_16BIT val minBuf = AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding) - // Etwas grosszuegiger Buffer: 8x MinSize (ca. 200-400ms bei 24kHz) — glatt auch bei kleinen Netzwerk-Aussetzern - val bufferSize = (minBuf * 8).coerceAtLeast(32 * 1024) + // Grosszuegiger Buffer: 32x MinSize — tolerant gegen Netzwerk-Jitter und + // bursty XTTS-Delivery (Render dauert 1-3s, dann kommen alle Samples + // auf einmal). Bei 24kHz mono s16 entspricht 128KB ca. 2.7 Sekunden. + val bufferSize = (minBuf * 32).coerceAtLeast(128 * 1024) val newTrack = AudioTrack.Builder() .setAudioAttributes( diff --git a/xtts/bridge.js b/xtts/bridge.js index f05a133..3ec7655 100644 --- a/xtts/bridge.js +++ b/xtts/bridge.js @@ -95,7 +95,20 @@ function connectRVS(forcePlain) { // ── TTS Request Handler ───────────────────────────── -async function handleTTSRequest(payload) { +// ── TTS-Queue ────────────────────────────────────── +// XTTS verarbeitet Requests sequenziell, damit Streams sich nicht ueberlappen. +// Ohne Queue wuerden parallele Requests parallel streamen → App bekommt +// interleaved PCM-Chunks aus zwei Rendern → klingt wie Chaos. +let ttsQueue = Promise.resolve(); + +function handleTTSRequest(payload) { + ttsQueue = ttsQueue.then(() => _runTTSRequest(payload)).catch(err => { + log(`TTS-Queue Fehler: ${err.message}`); + }); + return ttsQueue; +} + +async function _runTTSRequest(payload) { const { text, voice, requestId, language, messageId } = payload; if (!text) return;