From e64df23bb767eb25f733bcff0be5e0ff7e79048f Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 24 Apr 2026 14:40:58 +0200 Subject: [PATCH] fix: TTS pausiert andere Apps statt zu ducken + VAD/Mic laenger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../java/com/ariacockpit/AudioFocusModule.kt | 10 ++++++++-- .../com/ariacockpit/PcmStreamPlayerModule.kt | 20 +++++++++++++++++-- android/src/services/audio.ts | 11 +++++++--- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/android/android/app/src/main/java/com/ariacockpit/AudioFocusModule.kt b/android/android/app/src/main/java/com/ariacockpit/AudioFocusModule.kt index b69017d..e768902 100644 --- a/android/android/app/src/main/java/com/ariacockpit/AudioFocusModule.kt +++ b/android/android/app/src/main/java/com/ariacockpit/AudioFocusModule.kt @@ -53,11 +53,17 @@ class AudioFocusModule(reactContext: ReactApplicationContext) : ReactContextBase promise.resolve(result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) } - /** Andere Apps werden leiser (TTS spricht). */ + /** Andere Apps werden pausiert (TTS spricht). + * + * TRANSIENT (statt TRANSIENT_MAY_DUCK): Spotify/YouTube pausieren komplett + * statt nur leiser zu werden. Verhindert auch das "kommt-wieder-hoch"- + * Problem mit MAY_DUCK, wo das System nach kurzer Zeit den Duck-Effekt + * wieder aufgehoben hat obwohl wir den Fokus noch hielten. + */ @ReactMethod fun requestDuck(promise: Promise) { requestFocus( - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, AudioAttributes.USAGE_ASSISTANT, promise, ) 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 6e19084..fe2fef6 100644 --- a/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt +++ b/android/android/app/src/main/java/com/ariacockpit/PcmStreamPlayerModule.kt @@ -201,11 +201,27 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex } } - /** Signalisiert: keine weiteren Chunks. Writer wartet auf Queue-Abschluss, dann stoppt. */ + /** Signalisiert: keine weiteren Chunks. Writer spielt aus, dann stoppt. + * Das Promise resolved erst wenn der Writer-Thread fertig ist — + * wichtig damit der Aufrufer den AudioFocus erst NACH dem letzten + * abgespielten Sample wieder freigibt (sonst dreht Spotify hoch + * waehrend das Pre-Roll noch ausspielt). + */ @ReactMethod fun end(promise: Promise) { endRequested = true - promise.resolve(true) + val t = writerThread + if (t == null || !t.isAlive) { + promise.resolve(true) + return + } + // Im Hintergrund auf den Writer warten — kein Threading-Block fuer JS-Bridge + Thread({ + try { + t.join(15_000) // hartes Cap, falls Writer haengt + } catch (_: InterruptedException) {} + promise.resolve(true) + }, "PcmStreamEndWaiter").start() } /** Harter Stop (Cancel) — Queue verwerfen. */ diff --git a/android/src/services/audio.ts b/android/src/services/audio.ts index 00ebae1..360dfa2 100644 --- a/android/src/services/audio.ts +++ b/android/src/services/audio.ts @@ -74,12 +74,13 @@ const AUDIO_ENCODING = 'audio/wav'; // VAD (Voice Activity Detection) — Stille-Erkennung const VAD_SILENCE_THRESHOLD_DB = -45; // dB unter dem als "Stille" gilt -const VAD_SILENCE_DURATION_MS = 1800; // ms Stille bevor Auto-Stop +const VAD_SILENCE_DURATION_MS = 2800; // ms Stille bevor Auto-Stop — laenger = mehr Toleranz fuer Sprechpausen const VAD_SPEECH_THRESHOLD_DB = -28; // dB ueber dem als "Sprache" gilt (Sprach-Gate) — hoeher = weniger Umgebungsgeraeusche const VAD_SPEECH_MIN_MS = 500; // ms Sprache bevor Aufnahme zaehlt — laenger = keine Huestler/Klopfer mehr -// Max-Dauer einer Aufnahme in Gespraechsmodus (Notbremse gegen Runaway-Loops) -const MAX_RECORDING_MS = 30000; +// Max-Dauer einer Aufnahme (Notbremse gegen Runaway-Loops). Auf 2 Minuten +// hochgezogen damit auch laengere Erklaerungen durchgehen. +const MAX_RECORDING_MS = 120000; // Pre-Roll: Wie lange Audio im AudioTrack-Buffer liegt bevor play() startet. // Einstellbar via Diagnostic/Settings (Key: aria_tts_preroll_sec). @@ -419,6 +420,10 @@ class AudioService { if (isFinal) { if (!silent) { + // end() resolved jetzt erst wenn der native Writer-Thread fertig + // ist (alle Samples ausgespielt) — danach erst AudioFocus freigeben, + // damit Spotify/YouTube nicht waehrend des Pre-Roll-Ausklangs + // wieder aufdrehen. try { await PcmStreamPlayer!.end(); } catch {} AudioFocus?.release().catch(() => {}); }