fix(audio): Auto-Resume nach Anruf — pcmBuffer bleibt erhalten
Logs zeigten "WAV nicht binnen 30000ms verfuegbar" — pcmBuffer wurde von haltAllPlayback geleert, isFinal schrieb daher eine leere WAV (oder gar keine, weil pcmMessageId leer war). Neue Methode pauseForCall (statt haltAllPlayback im Anruf-Pfad): - AudioTrack stoppt + AudioFocus release (Spotify resumed) - pcmBuffer + pcmMessageId BLEIBEN — Bridge-Chunks werden weiter gesammelt - _pausedForCall macht weitere Chunks "silent" (kein writeChunk, nur Cache) - isFinal schreibt WAV trotz Anruf → resumeFromInterruption findet sie Plus captureInterruption idempotent gemacht: ringing→offhook ueberschreibt die Position vom ersten Halt nicht mehr (Date.now-Tracking laeuft stumpf weiter obwohl Audio gestoppt ist — der erste Halt ist die echte Position). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<string> {
|
||||
// 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();
|
||||
|
||||
Reference in New Issue
Block a user