@@ -31,6 +31,7 @@ typedef struct {
31
31
int id ;
32
32
SDL_RWops * src ;
33
33
SDL_bool freesrc ;
34
+ SDL_bool playing ;
34
35
} MusicHTML5 ;
35
36
36
37
static SDL_bool html5_opened (void )
@@ -40,6 +41,20 @@ static SDL_bool html5_opened(void)
40
41
});
41
42
}
42
43
44
+ static void html5_handle_music_stopped (void * context )
45
+ {
46
+ // Sets music->playing to FALSE. Call "finished" handler explicitly
47
+ // in devappd/html5_mixer which does not run its own sound loop.
48
+
49
+ MusicHTML5 * music = (MusicHTML5 * )context ;
50
+ if (music )
51
+ music -> playing = SDL_FALSE ;
52
+
53
+ #ifdef HTML5_MIXER
54
+ run_music_finished_hook ();
55
+ #endif
56
+ }
57
+
43
58
static int MusicHTML5_Open (const SDL_AudioSpec * spec )
44
59
{
45
60
(void )spec ;
@@ -48,6 +63,8 @@ static int MusicHTML5_Open(const SDL_AudioSpec *spec)
48
63
return 0 ;
49
64
50
65
EM_ASM (({
66
+ const wasmMusicStopped = $0 ;
67
+
51
68
Module ["SDL2Mixer" ] = {
52
69
blob : {
53
70
// URL.createObjectURL(...): numUses (int)
@@ -147,9 +164,7 @@ static int MusicHTML5_Open(const SDL_AudioSpec *spec)
147
164
audio .dataset .playCount = 0 ;
148
165
audio .currentTime = 0 ;
149
166
audio .loop = false;
150
-
151
- // Signal `true` to run hookMusicFinished
152
- return true;
167
+ dynCall ("vi" , wasmMusicStopped , [audio .dataset .context ]);
153
168
}
154
169
},
155
170
@@ -166,22 +181,10 @@ static int MusicHTML5_Open(const SDL_AudioSpec *spec)
166
181
audio .dataset .playCount = 0 ;
167
182
audio .currentTime = 0 ;
168
183
audio .loop = false;
184
+ dynCall ("vi" , wasmMusicStopped , [audio .dataset .context ]);
169
185
}
170
186
};
171
- }));
172
-
173
- #ifdef HTML5_MIXER
174
- // HookMusicFinished support for html5_mixer (a minimal implementation of SDL2_mixer)
175
- EM_ASM ({
176
- const hookMusicFinished = $0 ;
177
- Module ["SDL2Mixer" ].musicFinished = function (predefined ) {
178
- return function (e ) {
179
- if (predefined (e ))
180
- dynCall ("v" , hookMusicFinished , []);
181
- }
182
- }(Module ["SDL2Mixer" ].musicFinished );
183
- }, run_music_finished_hook );
184
- #endif
187
+ }), html5_handle_music_stopped );
185
188
186
189
return 0 ;
187
190
}
@@ -191,7 +194,12 @@ static void *MusicHTML5_CreateFromRW(SDL_RWops *src, int freesrc)
191
194
void * buf ;
192
195
int id = -1 ;
193
196
int size = src -> size (src );
194
- MusicHTML5 * music ;
197
+ MusicHTML5 * music = (MusicHTML5 * )SDL_calloc (1 , sizeof * music );
198
+
199
+ if (music == NULL ) {
200
+ Mix_SetError ("Out of memory" );
201
+ return NULL ;
202
+ }
195
203
196
204
if (src -> type == SDL_RWOPS_STDFILE )
197
205
buf = src -> hidden .stdio .fp ;
@@ -210,6 +218,7 @@ static void *MusicHTML5_CreateFromRW(SDL_RWops *src, int freesrc)
210
218
id = EM_ASM_INT ({
211
219
const ptr = $0 ;
212
220
const size = $1 ;
221
+ const context = $2 ;
213
222
214
223
const arr = new Uint8Array (Module .HEAPU8 .buffer , ptr , size );
215
224
const blob = new Blob ([arr ], { type : "octet/stream" });
@@ -225,22 +234,22 @@ static void *MusicHTML5_CreateFromRW(SDL_RWops *src, int freesrc)
225
234
Module ["SDL2Mixer" ].music [id ] = new Audio (url );
226
235
Module ["SDL2Mixer" ].music [id ].addEventListener ("ended" , Module ["SDL2Mixer" ].musicFinished , false);
227
236
Module ["SDL2Mixer" ].music [id ].addEventListener ("error" , Module ["SDL2Mixer" ].musicError , false);
237
+ Module ["SDL2Mixer" ].music [id ].dataset .context = context ;
228
238
return id ;
229
- }, buf , size );
239
+ }, buf , size , music );
230
240
}
231
241
232
242
if (id == -1 )
233
- return NULL ;
234
-
235
- /* Allocate and fill the music structure */
236
- music = (MusicHTML5 * )SDL_calloc (1 , sizeof * music );
237
- if (music == NULL ) {
238
- Mix_SetError ("Out of memory" );
243
+ {
244
+ SDL_free (music );
239
245
return NULL ;
240
246
}
247
+
248
+ /* Fill the music structure */
241
249
music -> id = id ;
242
250
music -> src = src ;
243
251
music -> freesrc = freesrc ;
252
+ music -> playing = SDL_TRUE ;
244
253
245
254
/* We're done */
246
255
return music ;
@@ -249,9 +258,14 @@ static void *MusicHTML5_CreateFromRW(SDL_RWops *src, int freesrc)
249
258
/* Load a music stream from the given file */
250
259
static void * MusicHTML5_CreateFromFile (const char * file )
251
260
{
252
- MusicHTML5 * music ;
261
+ MusicHTML5 * music = ( MusicHTML5 * ) SDL_calloc ( 1 , sizeof * music ); ;
253
262
int id = -1 ;
254
263
264
+ if (music == NULL ) {
265
+ Mix_SetError ("Out of memory" );
266
+ return NULL ;
267
+ }
268
+
255
269
SDL_RWops * src = SDL_RWFromFile (file , "rb" );
256
270
if (src != NULL )
257
271
{
@@ -312,6 +326,7 @@ static void *MusicHTML5_CreateFromFile(const char *file)
312
326
}
313
327
314
328
const url = UTF8ToString ($0 );
329
+ const context = $1 ;
315
330
316
331
if (!isValidUrl ) {
317
332
console .error (`URL $ {url } is invalid `);
@@ -322,21 +337,20 @@ static void *MusicHTML5_CreateFromFile(const char *file)
322
337
Module ["SDL2Mixer" ].music [id ] = new Audio (url );
323
338
Module ["SDL2Mixer" ].music [id ].addEventListener ("ended" , Module ["SDL2Mixer" ].musicFinished , false);
324
339
Module ["SDL2Mixer" ].music [id ].addEventListener ("error" , Module ["SDL2Mixer" ].musicError , false);
340
+ Module ["SDL2Mixer" ].music [id ].dataset .context = context ;
325
341
return id ;
326
- }, file );
342
+ }, file , music );
327
343
}
328
344
329
- if (id == -1 )
330
- return NULL ;
331
-
332
- /* Allocate and fill the music structure */
333
- music = (MusicHTML5 * )SDL_calloc (1 , sizeof * music );
334
- if (music == NULL ) {
335
- Mix_SetError ("Out of memory" );
345
+ if (id == -1 ) {
346
+ SDL_free (music );
336
347
return NULL ;
337
348
}
349
+
350
+ /* Fill the music structure */
338
351
music -> id = id ;
339
352
music -> freesrc = SDL_FALSE ;
353
+ music -> playing = SDL_TRUE ;
340
354
341
355
/* We're done */
342
356
return music ;
@@ -404,15 +418,37 @@ static SDL_bool MusicHTML5_IsPlaying(void *context)
404
418
{
405
419
MusicHTML5 * music = (MusicHTML5 * )context ;
406
420
407
- return EM_ASM_INT ({
421
+ if (!music ) {
422
+ // Call "finished" handler in devappd/html5_mixer
423
+ html5_handle_music_stopped (context );
424
+ return SDL_FALSE ;
425
+ }
426
+
427
+ // We track a music->playing variable to play nice with music_mixer()'s
428
+ // IsPlaying() check on every frame. E.g., the check will run when
429
+ // <audio> is buffering and the music is technically not "playing".
430
+ // Ergo, the HookMusicFinished() callback is called immediately when
431
+ // the <audio> has not even begun playback.
432
+ //
433
+ // To resolve this, we rely on JavaScript callbacks to reset the
434
+ // music->playing status on end, on error, etc.
435
+
436
+ int safeStatus = EM_ASM_INT ({
408
437
const id = $0 ;
409
- return Module ["SDL2Mixer" ].music [id ]
410
- && Module ["SDL2Mixer" ].music [id ].currentTime > 0
411
- // SDL Mixer considers "paused" music as "playing"
412
- //&& !Module["SDL2Mixer"].music[id].paused
413
- && !Module ["SDL2Mixer" ].music [id ].ended
414
- && Module ["SDL2Mixer" ].music [id ].readyState > 2 ;
438
+ return Module ["SDL2Mixer" ].music [id ]
439
+ && !Module ["SDL2Mixer" ].music [id ].ended
440
+ // SDL Mixer considers "paused" music as "playing"
441
+ //&& !Module["SDL2Mixer"].music[id].paused
442
+ // These conditions interfere with the "playing" check
443
+ //&& Module["SDL2Mixer"].music[id].readyState > 2;
444
+ //&& Module["SDL2Mixer"].music[id].currentTime > 0
445
+ ;
415
446
}, music -> id );
447
+
448
+ if (!safeStatus )
449
+ html5_handle_music_stopped (context );
450
+
451
+ return music -> playing ;
416
452
}
417
453
418
454
/* Jump (seek) to a given position (time is in seconds) */
@@ -464,6 +500,8 @@ static void MusicHTML5_Stop(void *context)
464
500
Module ["SDL2Mixer" ].music [id ].currentTime = 0 ;
465
501
Module ["SDL2Mixer" ].music [id ].loop = false;
466
502
}, music -> id );
503
+
504
+ html5_handle_music_stopped (context );
467
505
}
468
506
469
507
/* Close the given music stream */
0 commit comments