From 4e07e737f0136f923efc487460884dd42b981f8c Mon Sep 17 00:00:00 2001 From: Richard Roberson Date: Sun, 2 Mar 2025 21:16:24 -0700 Subject: [PATCH] Stop using audio blobs --- .github/workflows/playwright.yml | 2 +- src/contexts/TTSContext.tsx | 23 +++++++++++++---------- src/utils/audio.ts | 10 ---------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f45bfa9..62b82c8 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -22,7 +22,7 @@ jobs: NEXT_PUBLIC_NODE_ENV: test API_BASE: https://tts.richardr.dev/v1 API_KEY: not-needed - run: npx playwright test --reporter=github,html + run: npx playwright test --reporter=list,github,html - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} with: diff --git a/src/contexts/TTSContext.tsx b/src/contexts/TTSContext.tsx index da24080..7044586 100644 --- a/src/contexts/TTSContext.tsx +++ b/src/contexts/TTSContext.tsx @@ -29,7 +29,6 @@ import toast from 'react-hot-toast'; import { useParams } from 'next/navigation'; import { useConfig } from '@/contexts/ConfigContext'; -import { audioBufferToURL } from '@/utils/audio'; import { useAudioCache } from '@/hooks/audio/useAudioCache'; import { useVoiceManagement } from '@/hooks/audio/useVoiceManagement'; import { useMediaSession } from '@/hooks/audio/useMediaSession'; @@ -488,7 +487,13 @@ export function TTSProvider({ children }: { children: ReactNode }) { const processPromise = (async () => { try { const audioBuffer = await getAudio(sentence); - return audioBufferToURL(audioBuffer!); + if (!audioBuffer) throw new Error('No audio data generated'); + + // Convert to base64 data URI + const bytes = new Uint8Array(audioBuffer); + const binaryString = bytes.reduce((acc, byte) => acc + String.fromCharCode(byte), ''); + const base64String = btoa(binaryString); + return `data:audio/mp3;base64,${base64String}`; } catch (error) { setIsProcessing(false); throw error; @@ -520,9 +525,10 @@ export function TTSProvider({ children }: { children: ReactNode }) { } try { - const audioUrl = await processSentence(sentence); - if (!audioUrl) { - throw new Error('No audio URL generated'); + // Get the processed audio data URI directly from processSentence + const audioDataUri = await processSentence(sentence); + if (!audioDataUri) { + throw new Error('No audio data generated'); } // Force unload any previous Howl instance to free up resources @@ -531,7 +537,7 @@ export function TTSProvider({ children }: { children: ReactNode }) { } const howl = new Howl({ - src: [audioUrl], + src: [audioDataUri], format: ['mp3'], html5: true, preload: true, @@ -548,7 +554,6 @@ export function TTSProvider({ children }: { children: ReactNode }) { } }, onend: () => { - URL.revokeObjectURL(audioUrl); howl.unload(); setActiveHowl(null); if (isPlaying) { @@ -559,13 +564,11 @@ export function TTSProvider({ children }: { children: ReactNode }) { console.warn('Error loading audio:', error); setIsProcessing(false); setActiveHowl(null); - URL.revokeObjectURL(audioUrl); howl.unload(); setIsPlaying(false); }, onstop: () => { setIsProcessing(false); - URL.revokeObjectURL(audioUrl); howl.unload(); } }); @@ -590,7 +593,7 @@ export function TTSProvider({ children }: { children: ReactNode }) { advance(); return null; } - }, [isPlaying, processSentence, advance, activeHowl]); + }, [isPlaying, advance, activeHowl, processSentence]); const playAudio = useCallback(async () => { const howl = await playSentenceWithHowl(sentences[currentIndex]); diff --git a/src/utils/audio.ts b/src/utils/audio.ts index fd1802c..be57152 100644 --- a/src/utils/audio.ts +++ b/src/utils/audio.ts @@ -8,16 +8,6 @@ interface AudioChunk { startTime: number; } -/** - * Creates a URL from an ArrayBuffer containing MP3 audio data - * @param buffer The ArrayBuffer containing MP3 audio data - * @returns A blob URL that can be used for audio playback - */ -export const audioBufferToURL = (buffer: ArrayBuffer): string => { - const blob = new Blob([buffer], { type: 'audio/mp3' }); - return URL.createObjectURL(blob); -}; - /** * Combines audio chunks into a single audio file * @param audioChunks Array of audio chunks with metadata