fix: Audio queue for sequential TTS playback (no overlap/skip)
- Audio packets queued instead of stopping previous - _playNext() plays sequentially, each sentence after the previous - stopPlayback() clears queue - Fixes overlapping/skipping with XTTS sentence-by-sentence rendering Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,10 @@ class AudioService {
|
|||||||
private recorder: AudioRecorderPlayer;
|
private recorder: AudioRecorderPlayer;
|
||||||
private recordingPath: string = '';
|
private recordingPath: string = '';
|
||||||
|
|
||||||
|
// Audio-Queue fuer sequentielle TTS-Wiedergabe
|
||||||
|
private audioQueue: string[] = [];
|
||||||
|
private isPlaying: boolean = false;
|
||||||
|
|
||||||
// VAD State
|
// VAD State
|
||||||
private vadEnabled: boolean = false;
|
private vadEnabled: boolean = false;
|
||||||
private lastSpeechTime: number = 0;
|
private lastSpeechTime: number = 0;
|
||||||
@@ -198,15 +202,27 @@ class AudioService {
|
|||||||
|
|
||||||
// --- Wiedergabe ---
|
// --- Wiedergabe ---
|
||||||
|
|
||||||
/** Base64-kodiertes Audio abspielen (z.B. TTS-Antwort von ARIA) */
|
/** Base64-kodiertes Audio in die Queue stellen und abspielen */
|
||||||
async playAudio(base64Data: string): Promise<void> {
|
async playAudio(base64Data: string): Promise<void> {
|
||||||
if (!base64Data) return;
|
if (!base64Data) return;
|
||||||
|
|
||||||
// Laufende Wiedergabe stoppen
|
this.audioQueue.push(base64Data);
|
||||||
this.stopPlayback();
|
if (!this.isPlaying) {
|
||||||
|
this._playNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Naechstes Audio aus der Queue abspielen */
|
||||||
|
private async _playNext(): Promise<void> {
|
||||||
|
if (this.audioQueue.length === 0) {
|
||||||
|
this.isPlaying = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isPlaying = true;
|
||||||
|
const base64Data = this.audioQueue.shift()!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Base64 -> temporaere WAV-Datei -> Sound abspielen
|
|
||||||
const tmpPath = `${RNFS.CachesDirectoryPath}/aria_tts_${Date.now()}.wav`;
|
const tmpPath = `${RNFS.CachesDirectoryPath}/aria_tts_${Date.now()}.wav`;
|
||||||
await RNFS.writeFile(tmpPath, base64Data, 'base64');
|
await RNFS.writeFile(tmpPath, base64Data, 'base64');
|
||||||
|
|
||||||
@@ -214,6 +230,7 @@ class AudioService {
|
|||||||
if (error) {
|
if (error) {
|
||||||
console.error('[Audio] Fehler beim Laden:', error);
|
console.error('[Audio] Fehler beim Laden:', error);
|
||||||
RNFS.unlink(tmpPath).catch(() => {});
|
RNFS.unlink(tmpPath).catch(() => {});
|
||||||
|
this._playNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.currentSound?.play((success) => {
|
this.currentSound?.play((success) => {
|
||||||
@@ -225,15 +242,20 @@ class AudioService {
|
|||||||
this.currentSound?.release();
|
this.currentSound?.release();
|
||||||
this.currentSound = null;
|
this.currentSound = null;
|
||||||
RNFS.unlink(tmpPath).catch(() => {});
|
RNFS.unlink(tmpPath).catch(() => {});
|
||||||
|
// Naechstes Audio abspielen
|
||||||
|
this._playNext();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Audio] Wiedergabefehler:', err);
|
console.error('[Audio] Wiedergabefehler:', err);
|
||||||
|
this._playNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Laufende Wiedergabe stoppen */
|
/** Laufende Wiedergabe stoppen + Queue leeren */
|
||||||
stopPlayback(): void {
|
stopPlayback(): void {
|
||||||
|
this.audioQueue = [];
|
||||||
|
this.isPlaying = false;
|
||||||
if (this.currentSound) {
|
if (this.currentSound) {
|
||||||
this.currentSound.stop();
|
this.currentSound.stop();
|
||||||
this.currentSound.release();
|
this.currentSound.release();
|
||||||
|
|||||||
Reference in New Issue
Block a user