Huge iOS playback fix
This commit is contained in:
parent
99868f29b2
commit
0251aae04b
1 changed files with 24 additions and 8 deletions
|
|
@ -174,6 +174,7 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
const abortAudio = useCallback((clearPending = false) => {
|
const abortAudio = useCallback((clearPending = false) => {
|
||||||
if (activeHowl) {
|
if (activeHowl) {
|
||||||
activeHowl.stop();
|
activeHowl.stop();
|
||||||
|
activeHowl.unload(); // Ensure Howl instance is fully cleaned up
|
||||||
setActiveHowl(null);
|
setActiveHowl(null);
|
||||||
}
|
}
|
||||||
if (clearPending) {
|
if (clearPending) {
|
||||||
|
|
@ -274,27 +275,34 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
// Check for blank section first
|
// Check for blank section first
|
||||||
if (handleBlankSection(text)) return;
|
if (handleBlankSection(text)) return;
|
||||||
|
|
||||||
// Keep track of previous state
|
// Keep track of previous state and pause playback
|
||||||
const wasPlaying = isPlaying;
|
const wasPlaying = isPlaying;
|
||||||
|
setIsPlaying(false);
|
||||||
|
abortAudio(true); // Clear pending requests since text is changing
|
||||||
|
setIsProcessing(true); // Set processing state before text processing starts
|
||||||
|
|
||||||
console.log('Setting text:', text);
|
console.log('Setting text:', text);
|
||||||
processTextToSentences(text)
|
processTextToSentences(text)
|
||||||
.then(newSentences => {
|
.then(newSentences => {
|
||||||
if (newSentences.length === 0) {
|
if (newSentences.length === 0) {
|
||||||
console.warn('No sentences found in text');
|
console.warn('No sentences found in text');
|
||||||
|
setIsProcessing(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set all state updates in a predictable order
|
||||||
setSentences(newSentences);
|
setSentences(newSentences);
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
|
setIsProcessing(false);
|
||||||
|
|
||||||
// Only restore previous playback state if we shouldn't pause
|
// Restore playback state if needed
|
||||||
if (shouldPause) setIsPlaying(false);
|
if (!shouldPause && wasPlaying) {
|
||||||
else if (wasPlaying) setIsPlaying(true);
|
setIsPlaying(true);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.warn('Error processing text:', error);
|
console.warn('Error processing text:', error);
|
||||||
|
setIsProcessing(false);
|
||||||
toast.error('Failed to process text', {
|
toast.error('Failed to process text', {
|
||||||
style: {
|
style: {
|
||||||
background: 'var(--background)',
|
background: 'var(--background)',
|
||||||
|
|
@ -303,7 +311,7 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [processTextToSentences, handleBlankSection, isPlaying]);
|
}, [isPlaying, handleBlankSection, abortAudio, processTextToSentences]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the playback state between playing and paused
|
* Toggles the playback state between playing and paused
|
||||||
|
|
@ -493,12 +501,17 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
throw new Error('No audio URL generated');
|
throw new Error('No audio URL generated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force unload any previous Howl instance to free up resources
|
||||||
|
if (activeHowl) {
|
||||||
|
activeHowl.unload();
|
||||||
|
}
|
||||||
|
|
||||||
const howl = new Howl({
|
const howl = new Howl({
|
||||||
src: [audioUrl],
|
src: [audioUrl],
|
||||||
format: ['mp3'],
|
format: ['mp3'],
|
||||||
html5: true,
|
html5: true,
|
||||||
preload: true,
|
preload: true,
|
||||||
pool: 1,
|
pool: 5, // Reduced pool size for iOS compatibility
|
||||||
onplay: () => {
|
onplay: () => {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
|
|
@ -512,6 +525,7 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
},
|
},
|
||||||
onend: () => {
|
onend: () => {
|
||||||
URL.revokeObjectURL(audioUrl);
|
URL.revokeObjectURL(audioUrl);
|
||||||
|
howl.unload(); // Explicitly unload when done
|
||||||
setActiveHowl(null);
|
setActiveHowl(null);
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
advance();
|
advance();
|
||||||
|
|
@ -522,12 +536,14 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
setActiveHowl(null);
|
setActiveHowl(null);
|
||||||
URL.revokeObjectURL(audioUrl);
|
URL.revokeObjectURL(audioUrl);
|
||||||
|
howl.unload(); // Ensure cleanup on error
|
||||||
// Don't auto-advance on load error
|
// Don't auto-advance on load error
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
},
|
},
|
||||||
onstop: () => {
|
onstop: () => {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
URL.revokeObjectURL(audioUrl);
|
URL.revokeObjectURL(audioUrl);
|
||||||
|
howl.unload(); // Ensure cleanup on stop
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -550,7 +566,7 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
||||||
|
|
||||||
advance(); // Skip problematic sentence
|
advance(); // Skip problematic sentence
|
||||||
}
|
}
|
||||||
}, [isPlaying, processSentence, advance]);
|
}, [isPlaying, processSentence, advance, activeHowl]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preloads the next sentence's audio
|
* Preloads the next sentence's audio
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue