@@ -234,41 +234,56 @@ class StreamingTTS {
234234 }
235235
236236 stop ( ) {
237+ // 1. Close the socket first to prevent new messages.
237238 if ( this . #currentSocket) {
239+ this . #currentSocket. onclose = null ; // Prevent the onclose handler from running
240+ this . #currentSocket. onerror = null ; // Prevent the onerror handler
238241 this . #currentSocket. close ( ) ;
239242 this . #currentSocket = null ;
240243 }
241- if ( this . #mediaSource && this . #mediaSource. readyState === "open" ) {
242- try {
243- this . #mediaSource. endOfStream ( ) ;
244- } catch ( e ) {
245- /* Ignore */
246- }
247- }
248- this . #audioPlayer. pause ( ) ;
249- this . #audioPlayer. removeAttribute ( "src" ) ;
244+
245+ // 2. Clear the queue to stop any pending appends.
250246 this . #audioQueue = [ ] ;
251247 this . #isAppending = false ;
248+
249+ // 3. Gracefully end the media stream.
250+ // This will handle the sourceBuffer and mediaSource correctly.
251+ this . #finalizeStream( ) ;
252+
253+ // 4. Reset the audio player.
254+ this . #audioPlayer. pause ( ) ;
255+ if ( this . #audioPlayer. src . startsWith ( "blob:" ) ) {
256+ URL . revokeObjectURL ( this . #audioPlayer. src ) ;
257+ }
258+ this . #audioPlayer. removeAttribute ( "src" ) ;
252259 }
253260
254261 // --- Private Helper Methods ---
255262 #finalizeStream( ) {
263+ // This is the more robust version that prevents Firefox warnings and is generally safer.
264+ if ( ! this . #mediaSource || this . #mediaSource. readyState !== "open" ) {
265+ return ;
266+ }
267+
256268 const end = ( ) => {
257- if ( this . #mediaSource && this . #mediaSource . readyState === "open" ) {
269+ if ( this . #mediaSource. readyState === "open" ) {
258270 try {
259271 this . #mediaSource. endOfStream ( ) ;
260272 } catch ( e ) {
261- console . warn ( "MediaSource already ended." ) ;
273+ console . warn (
274+ "Error calling endOfStream, stream likely already closed." ,
275+ e ,
276+ ) ;
262277 }
263278 }
264279 } ;
265- if ( this . #isAppending || this . #audioQueue . length > 0 ) {
266- const interval = setInterval ( ( ) => {
267- if ( ! this . #isAppending && this . #audioQueue . length === 0 ) {
268- clearInterval ( interval ) ;
269- end ( ) ;
270- }
271- } , 50 ) ;
280+
281+ if ( this . #sourceBuffer && this . #sourceBuffer . updating ) {
282+ const onUpdateEnd = ( ) => {
283+ this . #sourceBuffer . removeEventListener ( "updateend" , onUpdateEnd ) ;
284+ end ( ) ;
285+ } ;
286+ this . #sourceBuffer . addEventListener ( "updateend" , onUpdateEnd ) ;
272287 } else {
273288 end ( ) ;
274289 }
0 commit comments