Huge iOS playback fix

This commit is contained in:
Richard Roberson 2025-02-22 13:36:45 -07:00
parent 99868f29b2
commit 0251aae04b

View file

@ -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