diff --git a/android/src/services/audio.ts b/android/src/services/audio.ts index 91ba372..60f6d45 100644 --- a/android/src/services/audio.ts +++ b/android/src/services/audio.ts @@ -347,18 +347,61 @@ class AudioService { this._releaseFocusDeferred(); } - /** TTS-Wiedergabe haart stoppen — z.B. wenn ein Anruf reinkommt. - * Released auch sofort den AudioFocus damit der Anruf-Klingelton hoerbar ist. */ + /** TTS-Wiedergabe haart stoppen — z.B. fuer Barge-In. Buffer wird geleert, + * kein Auto-Resume. Released auch sofort den AudioFocus. */ haltAllPlayback(reason: string = ''): void { console.log('[Audio] haltAllPlayback: %s', reason || '(no reason)'); this._conversationFocusActive = false; this.stopPlayback(); } + /** Speziell fuer Anrufe: AudioTrack stoppen + Focus releasen, ABER pcm- + * Buffer + messageId behalten damit weitere Chunks der unterbrochenen + * Antwort weiter gesammelt werden. isFinal schreibt dann die WAV trotz + * Anruf — und resumeFromInterruption findet sie. */ + pauseForCall(reason: string = ''): void { + console.log('[Audio] pauseForCall: %s', reason || '(no reason)'); + this._conversationFocusActive = false; + this._pausedForCall = true; + // Foreground-Service stoppen — Notification waere sonst irrefuehrend + stopBackgroundAudio().catch(() => {}); + // SoundPool/RNSound (Resume-Sound, Play-Button) stoppen — nicht relevant fuer Auto-Resume + if (this.currentSound) { + try { this.currentSound.stop(); this.currentSound.release(); } catch {} + this.currentSound = null; + } + if (this.resumeSound) { + try { this.resumeSound.stop(); this.resumeSound.release(); } catch {} + this.resumeSound = null; + } + // AudioTrack hart stoppen damit nichts mehr aus dem Lautsprecher kommt. + // pcmStreamActive bleibt true, pcmBuffer/pcmMessageId BLEIBEN — damit + // weitere Chunks gesammelt werden und isFinal die WAV schreiben kann. + PcmStreamPlayer?.stop().catch(() => {}); + this._cancelDeferredFocusRelease(); + AudioFocus?.release().catch(() => {}); + } + + /** Anruf vorbei → weitere Chunks duerfen wieder abgespielt werden. + * resumeFromInterruption uebernimmt die Wiedergabe ab gemerkter Position. */ + endCallPause(): void { + if (!this._pausedForCall) return; + this._pausedForCall = false; + console.log('[Audio] endCallPause'); + } + /** Bei Anruf: aktuelle Wiedergabe-Position merken damit wir nach dem * Auflegen von dort weitermachen koennen. Returnt Position in Sekunden - * oder 0 wenn nichts spielte. */ + * oder 0 wenn nichts spielte. + * + * Idempotent: bei mehrfachem Aufruf (ringing → offhook) wird die Position + * vom ersten Mal NICHT ueberschrieben. playbackStartTime laeuft stumpf + * weiter obwohl das Audio gestoppt ist — der erste Halt ist der echte. */ captureInterruption(): number { + if (this.pausedMessageId) { + // Schon erfasst — nicht ueberschreiben (zweiter Aufruf bei offhook). + return this.pausedPosition; + } if (!this.playbackStartTime || !this.currentPlaybackMsgId) { this.pausedPosition = 0; this.pausedMessageId = ''; @@ -788,7 +831,9 @@ class AudioService { }): Promise { // Globaler Mute-Flag uebersteuert das per-Call silent — verhindert // Race-Conditions wenn der User zwischen Chunks den Mute-Knopf drueckt. - const silent = !!payload.silent || this._muted; + // _pausedForCall: AudioTrack ist gestoppt waehrend Anruf — Chunks weiter + // sammeln (fuer WAV-Cache), aber NICHT in den Player schicken. + const silent = !!payload.silent || this._muted || this._pausedForCall; if (!silent && !PcmStreamPlayer) { console.warn('[Audio] PcmStreamPlayer Native Module nicht verfuegbar'); return ''; @@ -1074,6 +1119,10 @@ class AudioService { * — die Bridge kann einen Chunk im selben JS-Tick liefern in dem der * User Mute geklickt hat. */ private _muted: boolean = false; + /** Anruf laeuft → Chunks werden nur in den Cache-Buffer gepusht, nicht + * abgespielt. Wird in pauseForCall gesetzt, in endCallPause/resumeFrom- + * Interruption zurueckgenommen. */ + private _pausedForCall: boolean = false; setMuted(muted: boolean): void { this._muted = muted; if (muted) this.stopPlayback(); diff --git a/android/src/services/phoneCall.ts b/android/src/services/phoneCall.ts index aac67a9..e115fa4 100644 --- a/android/src/services/phoneCall.ts +++ b/android/src/services/phoneCall.ts @@ -189,12 +189,17 @@ class PhoneCallService { private _haltForCall(toast: string): void { // Position merken bevor wir den Stream killen — fuer Auto-Resume. audioService.captureInterruption(); - audioService.haltAllPlayback(toast); + // pauseForCall (statt haltAllPlayback): pcmBuffer + messageId bleiben, + // weitere Chunks werden weiter gesammelt damit isFinal die WAV schreibt. + audioService.pauseForCall(toast); wakeWordService.pauseForCall().catch(() => {}); ToastAndroid.show(toast, ToastAndroid.SHORT); } private _resumeAfterCall(toast: string): void { + // Anruf-Pause aufheben — neue Chunks duerfen wieder direkt abgespielt + // werden (falls die Bridge mid-Anruf isFinal noch nicht geschickt hat). + audioService.endCallPause(); wakeWordService.resumeFromCall().catch(() => {}); ToastAndroid.show(toast, ToastAndroid.SHORT); // Auto-Resume: ab gemerkter Position weiterspielen wenn ARIA vor dem