1 /* 2 SDL_mixer: An audio mixer library based on the SDL library 3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 22 /* $Id$ */ 23 24 #include <stdlib.h> 25 #include <string.h> 26 #include <ctype.h> 27 #include <assert.h> 28 #include "SDL_endian.h" 29 #include "SDL_audio.h" 30 #include "SDL_timer.h" 31 32 #include "SDL_mixer.h" 33 34 #ifdef CMD_MUSIC 35 #include "music_cmd.h" 36 #endif 37 #ifdef WAV_MUSIC 38 #include "wavestream.h" 39 #endif 40 #ifdef MODPLUG_MUSIC 41 #include "music_modplug.h" 42 #endif 43 #ifdef MOD_MUSIC 44 #include "music_mod.h" 45 #endif 46 #ifdef MID_MUSIC 47 # ifdef USE_TIMIDITY_MIDI 48 # include "timidity.h" 49 # endif 50 # ifdef USE_FLUIDSYNTH_MIDI 51 # include "fluidsynth.h" 52 # endif 53 # ifdef USE_NATIVE_MIDI 54 # include "native_midi.h" 55 # endif 56 #endif 57 #ifdef OGG_MUSIC 58 #include "music_ogg.h" 59 #endif 60 #ifdef MP3_MUSIC 61 #include "dynamic_mp3.h" 62 #endif 63 #ifdef MP3_MAD_MUSIC 64 #include "music_mad.h" 65 #endif 66 #ifdef FLAC_MUSIC 67 #include "music_flac.h" 68 #endif 69 70 #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) 71 static SDL_AudioSpec used_mixer; 72 #endif 73 74 75 int volatile music_active = 1; 76 static int volatile music_stopped = 0; 77 static int music_loops = 0; 78 static char *music_cmd = NULL; 79 static Mix_Music * volatile music_playing = NULL; 80 static int music_volume = MIX_MAX_VOLUME; 81 82 struct _Mix_Music { 83 Mix_MusicType type; 84 union { 85 #ifdef CMD_MUSIC 86 MusicCMD *cmd; 87 #endif 88 #ifdef WAV_MUSIC 89 WAVStream *wave; 90 #endif 91 #ifdef MODPLUG_MUSIC 92 modplug_data *modplug; 93 #endif 94 #ifdef MOD_MUSIC 95 struct MODULE *module; 96 #endif 97 #ifdef MID_MUSIC 98 #ifdef USE_TIMIDITY_MIDI 99 MidiSong *midi; 100 #endif 101 #ifdef USE_FLUIDSYNTH_MIDI 102 FluidSynthMidiSong *fluidsynthmidi; 103 #endif 104 #ifdef USE_NATIVE_MIDI 105 NativeMidiSong *nativemidi; 106 #endif 107 #endif 108 #ifdef OGG_MUSIC 109 OGG_music *ogg; 110 #endif 111 #ifdef MP3_MUSIC 112 SMPEG *mp3; 113 #endif 114 #ifdef MP3_MAD_MUSIC 115 mad_data *mp3_mad; 116 #endif 117 #ifdef FLAC_MUSIC 118 FLAC_music *flac; 119 #endif 120 } data; 121 Mix_Fading fading; 122 int fade_step; 123 int fade_steps; 124 int error; 125 }; 126 #ifdef MID_MUSIC 127 #ifdef USE_TIMIDITY_MIDI 128 static int timidity_ok; 129 static int samplesize; 130 #endif 131 #ifdef USE_FLUIDSYNTH_MIDI 132 static int fluidsynth_ok; 133 #endif 134 #ifdef USE_NATIVE_MIDI 135 static int native_midi_ok; 136 #endif 137 #endif 138 139 /* Used to calculate fading steps */ 140 static int ms_per_step; 141 142 /* rcg06042009 report available decoders at runtime. */ 143 static const char **music_decoders = NULL; 144 static int num_decoders = 0; 145 146 /* Semicolon-separated SoundFont paths */ 147 #ifdef MID_MUSIC 148 char* soundfont_paths = NULL; 149 #endif 150 151 int Mix_GetNumMusicDecoders(void) 152 { 153 return(num_decoders); 154 } 155 156 const char *Mix_GetMusicDecoder(int index) 157 { 158 if ((index < 0) || (index >= num_decoders)) { 159 return NULL; 160 } 161 return(music_decoders[index]); 162 } 163 164 static void add_music_decoder(const char *decoder) 165 { 166 void *ptr = SDL_realloc(music_decoders, (num_decoders + 1) * sizeof (const char **)); 167 if (ptr == NULL) { 168 return; /* oh well, go on without it. */ 169 } 170 music_decoders = (const char **) ptr; 171 music_decoders[num_decoders++] = decoder; 172 } 173 174 /* Local low-level functions prototypes */ 175 static void music_internal_initialize_volume(void); 176 static void music_internal_volume(int volume); 177 static int music_internal_play(Mix_Music *music, double position); 178 static int music_internal_position(double position); 179 static int music_internal_playing(); 180 static void music_internal_halt(void); 181 182 183 /* Support for hooking when the music has finished */ 184 static void (*music_finished_hook)(void) = NULL; 185 186 void Mix_HookMusicFinished(void (*music_finished)(void)) 187 { 188 SDL_LockAudio(); 189 music_finished_hook = music_finished; 190 SDL_UnlockAudio(); 191 } 192 193 194 /* If music isn't playing, halt it if no looping is required, restart it */ 195 /* otherwhise. NOP if the music is playing */ 196 static int music_halt_or_loop (void) 197 { 198 /* Restart music if it has to loop */ 199 200 if (!music_internal_playing()) 201 { 202 #ifdef USE_NATIVE_MIDI 203 /* Native MIDI handles looping internally */ 204 if (music_playing->type == MUS_MID && native_midi_ok) { 205 music_loops = 0; 206 } 207 #endif 208 209 /* Restart music if it has to loop at a high level */ 210 if (music_loops) 211 { 212 Mix_Fading current_fade; 213 --music_loops; 214 current_fade = music_playing->fading; 215 music_internal_play(music_playing, 0.0); 216 music_playing->fading = current_fade; 217 } 218 else 219 { 220 music_internal_halt(); 221 if (music_finished_hook) 222 music_finished_hook(); 223 224 return 0; 225 } 226 } 227 228 return 1; 229 } 230 231 232 233 /* Mixing function */ 234 void music_mixer(void *udata, Uint8 *stream, int len) 235 { 236 int left = 0; 237 238 if ( music_playing && music_active ) { 239 /* Handle fading */ 240 if ( music_playing->fading != MIX_NO_FADING ) { 241 if ( music_playing->fade_step++ < music_playing->fade_steps ) { 242 int volume; 243 int fade_step = music_playing->fade_step; 244 int fade_steps = music_playing->fade_steps; 245 246 if ( music_playing->fading == MIX_FADING_OUT ) { 247 volume = (music_volume * (fade_steps-fade_step)) / fade_steps; 248 } else { /* Fading in */ 249 volume = (music_volume * fade_step) / fade_steps; 250 } 251 music_internal_volume(volume); 252 } else { 253 if ( music_playing->fading == MIX_FADING_OUT ) { 254 music_internal_halt(); 255 if ( music_finished_hook ) { 256 music_finished_hook(); 257 } 258 return; 259 } 260 music_playing->fading = MIX_NO_FADING; 261 } 262 } 263 264 music_halt_or_loop(); 265 if (!music_internal_playing()) 266 return; 267 268 switch (music_playing->type) { 269 #ifdef CMD_MUSIC 270 case MUS_CMD: 271 /* The playing is done externally */ 272 break; 273 #endif 274 #ifdef WAV_MUSIC 275 case MUS_WAV: 276 left = WAVStream_PlaySome(stream, len); 277 break; 278 #endif 279 #ifdef MODPLUG_MUSIC 280 case MUS_MODPLUG: 281 left = modplug_playAudio(music_playing->data.modplug, stream, len); 282 break; 283 #endif 284 #ifdef MOD_MUSIC 285 case MUS_MOD: 286 left = MOD_playAudio(music_playing->data.module, stream, len); 287 break; 288 #endif 289 #ifdef MID_MUSIC 290 case MUS_MID: 291 #ifdef USE_NATIVE_MIDI 292 if ( native_midi_ok ) { 293 /* Native midi is handled asynchronously */ 294 goto skip; 295 } 296 #endif 297 #ifdef USE_FLUIDSYNTH_MIDI 298 if ( fluidsynth_ok ) { 299 fluidsynth_playsome(music_playing->data.fluidsynthmidi, stream, len); 300 goto skip; 301 } 302 #endif 303 #ifdef USE_TIMIDITY_MIDI 304 if ( timidity_ok ) { 305 int samples = len / samplesize; 306 Timidity_PlaySome(stream, samples); 307 goto skip; 308 } 309 #endif 310 break; 311 #endif 312 #ifdef OGG_MUSIC 313 case MUS_OGG: 314 315 left = OGG_playAudio(music_playing->data.ogg, stream, len); 316 break; 317 #endif 318 #ifdef FLAC_MUSIC 319 case MUS_FLAC: 320 left = FLAC_playAudio(music_playing->data.flac, stream, len); 321 break; 322 #endif 323 #ifdef MP3_MUSIC 324 case MUS_MP3: 325 left = (len - smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len)); 326 break; 327 #endif 328 #ifdef MP3_MAD_MUSIC 329 case MUS_MP3_MAD: 330 left = mad_getSamples(music_playing->data.mp3_mad, stream, len); 331 break; 332 #endif 333 default: 334 /* Unknown music type?? */ 335 break; 336 } 337 } 338 339 skip: 340 /* Handle seamless music looping */ 341 if (left > 0 && left < len) { 342 music_halt_or_loop(); 343 if (music_internal_playing()) 344 music_mixer(udata, stream+(len-left), left); 345 } 346 } 347 348 /* Initialize the music players with a certain desired audio format */ 349 int open_music(SDL_AudioSpec *mixer) 350 { 351 #ifdef WAV_MUSIC 352 if ( WAVStream_Init(mixer) == 0 ) { 353 add_music_decoder("WAVE"); 354 } 355 #endif 356 #ifdef MODPLUG_MUSIC 357 if ( modplug_init(mixer) == 0 ) { 358 add_music_decoder("MODPLUG"); 359 } 360 #endif 361 #ifdef MOD_MUSIC 362 if ( MOD_init(mixer) == 0 ) { 363 add_music_decoder("MIKMOD"); 364 } 365 #endif 366 #ifdef MID_MUSIC 367 #ifdef USE_TIMIDITY_MIDI 368 samplesize = mixer->size / mixer->samples; 369 if ( Timidity_Init(mixer->freq, mixer->format, 370 mixer->channels, mixer->samples) == 0 ) { 371 timidity_ok = 1; 372 add_music_decoder("TIMIDITY"); 373 } else { 374 timidity_ok = 0; 375 } 376 #endif 377 #ifdef USE_FLUIDSYNTH_MIDI 378 if ( fluidsynth_init(mixer) == 0 ) { 379 fluidsynth_ok = 1; 380 add_music_decoder("FLUIDSYNTH"); 381 } else { 382 fluidsynth_ok = 0; 383 } 384 #endif 385 #ifdef USE_NATIVE_MIDI 386 #ifdef USE_FLUIDSYNTH_MIDI 387 native_midi_ok = !fluidsynth_ok; 388 if ( native_midi_ok ) 389 #endif 390 #ifdef USE_TIMIDITY_MIDI 391 native_midi_ok = !timidity_ok; 392 if ( !native_midi_ok ) { 393 native_midi_ok = (getenv("SDL_NATIVE_MUSIC") != NULL); 394 } 395 if ( native_midi_ok ) 396 #endif 397 native_midi_ok = native_midi_detect(); 398 if ( native_midi_ok ) 399 add_music_decoder("NATIVEMIDI"); 400 #endif 401 #endif 402 #ifdef OGG_MUSIC 403 if ( OGG_init(mixer) == 0 ) { 404 add_music_decoder("OGG"); 405 } 406 #endif 407 #ifdef FLAC_MUSIC 408 if ( FLAC_init(mixer) == 0 ) { 409 add_music_decoder("FLAC"); 410 } 411 #endif 412 #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) 413 /* Keep a copy of the mixer */ 414 used_mixer = *mixer; 415 add_music_decoder("MP3"); 416 #endif 417 418 music_playing = NULL; 419 music_stopped = 0; 420 Mix_VolumeMusic(SDL_MIX_MAXVOLUME); 421 422 /* Calculate the number of ms for each callback */ 423 ms_per_step = (int) (((float)mixer->samples * 1000.0) / mixer->freq); 424 425 return(0); 426 } 427 428 /* Portable case-insensitive string compare function */ 429 int MIX_string_equals(const char *str1, const char *str2) 430 { 431 while ( *str1 && *str2 ) { 432 if ( toupper((unsigned char)*str1) != 433 toupper((unsigned char)*str2) ) 434 break; 435 ++str1; 436 ++str2; 437 } 438 return (!*str1 && !*str2); 439 } 440 441 static int detect_mp3(Uint8 *magic) 442 { 443 if ( strncmp((char *)magic, "ID3", 3) == 0 ) { 444 return 1; 445 } 446 447 /* Detection code lifted from SMPEG */ 448 if(((magic[0] & 0xff) != 0xff) || // No sync bits 449 ((magic[1] & 0xf0) != 0xf0) || // 450 ((magic[2] & 0xf0) == 0x00) || // Bitrate is 0 451 ((magic[2] & 0xf0) == 0xf0) || // Bitrate is 15 452 ((magic[2] & 0x0c) == 0x0c) || // Frequency is 3 453 ((magic[1] & 0x06) == 0x00)) { // Layer is 4 454 return(0); 455 } 456 return 1; 457 } 458 459 /* MUS_MOD can't be auto-detected. If no other format was detected, MOD is 460 * assumed and MUS_MOD will be returned, meaning that the format might not 461 * actually be MOD-based. 462 * 463 * Returns MUS_NONE in case of errors. */ 464 static Mix_MusicType detect_music_type(SDL_RWops *rw) 465 { 466 Uint8 magic[5]; 467 Uint8 moremagic[9]; 468 469 int start = SDL_RWtell(rw); 470 if (SDL_RWread(rw, magic, 1, 4) != 4 || SDL_RWread(rw, moremagic, 1, 8) != 8 ) { 471 Mix_SetError("Couldn't read from RWops"); 472 return MUS_NONE; 473 } 474 SDL_RWseek(rw, start, RW_SEEK_SET); 475 magic[4]='\0'; 476 moremagic[8] = '\0'; 477 478 /* WAVE files have the magic four bytes "RIFF" 479 AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ 480 if (((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || 481 (strcmp((char *)magic, "FORM") == 0)) { 482 return MUS_WAV; 483 } 484 485 /* Ogg Vorbis files have the magic four bytes "OggS" */ 486 if (strcmp((char *)magic, "OggS") == 0) { 487 return MUS_OGG; 488 } 489 490 /* FLAC files have the magic four bytes "fLaC" */ 491 if (strcmp((char *)magic, "fLaC") == 0) { 492 return MUS_FLAC; 493 } 494 495 /* MIDI files have the magic four bytes "MThd" */ 496 if (strcmp((char *)magic, "MThd") == 0) { 497 return MUS_MID; 498 } 499 500 if (detect_mp3(magic)) { 501 return MUS_MP3; 502 } 503 504 /* Assume MOD format. 505 * 506 * Apparently there is no way to check if the file is really a MOD, 507 * or there are too many formats supported by MikMod/ModPlug, or 508 * MikMod/ModPlug does this check by itself. */ 509 return MUS_MOD; 510 } 511 512 /* Load a music file */ 513 Mix_Music *Mix_LoadMUS(const char *file) 514 { 515 SDL_RWops *rw; 516 Mix_Music *music; 517 Mix_MusicType type; 518 char *ext = strrchr(file, '.'); 519 520 #ifdef CMD_MUSIC 521 if ( music_cmd ) { 522 /* Allocate memory for the music structure */ 523 music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); 524 if ( music == NULL ) { 525 Mix_SetError("Out of memory"); 526 return(NULL); 527 } 528 music->error = 0; 529 music->type = MUS_CMD; 530 music->data.cmd = MusicCMD_LoadSong(music_cmd, file); 531 if ( music->data.cmd == NULL ) { 532 SDL_free(music); 533 music == NULL; 534 } 535 return music; 536 } 537 #endif 538 539 rw = SDL_RWFromFile(file, "rb"); 540 if ( rw == NULL ) { 541 Mix_SetError("Couldn't open '%s'", file); 542 return NULL; 543 } 544 545 /* Use the extension as a first guess on the file type */ 546 type = MUS_NONE; 547 ext = strrchr(file, '.'); 548 /* No need to guard these with #ifdef *_MUSIC stuff, 549 * since we simply call Mix_LoadMUSType_RW() later */ 550 if ( ext ) { 551 ++ext; /* skip the dot in the extension */ 552 if ( MIX_string_equals(ext, "WAV") ) { 553 type = MUS_WAV; 554 } else if ( MIX_string_equals(ext, "MID") || 555 MIX_string_equals(ext, "MIDI") || 556 MIX_string_equals(ext, "KAR") ) { 557 type = MUS_MID; 558 } else if ( MIX_string_equals(ext, "OGG") ) { 559 type = MUS_OGG; 560 } else if ( MIX_string_equals(ext, "FLAC") ) { 561 type = MUS_FLAC; 562 } else if ( MIX_string_equals(ext, "MPG") || 563 MIX_string_equals(ext, "MPEG") || 564 MIX_string_equals(ext, "MP3") || 565 MIX_string_equals(ext, "MAD") ) { 566 type = MUS_MP3; 567 } 568 } 569 if ( type == MUS_NONE ) { 570 type = detect_music_type(rw); 571 } 572 573 /* We need to know if a specific error occurs; if not, we'll set a 574 * generic one, so we clear the current one. */ 575 Mix_SetError(""); 576 music = Mix_LoadMUSType_RW(rw, type, SDL_TRUE); 577 if ( music == NULL && Mix_GetError()[0] == '\0' ) { 578 SDL_FreeRW(rw); 579 Mix_SetError("Couldn't open '%s'", file); 580 } 581 return music; 582 } 583 584 Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) 585 { 586 return Mix_LoadMUSType_RW(rw, MUS_NONE, SDL_FALSE); 587 } 588 589 Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type, int freesrc) 590 { 591 Mix_Music *music; 592 593 if (!rw) { 594 Mix_SetError("RWops pointer is NULL"); 595 return NULL; 596 } 597 598 /* If the caller wants auto-detection, figure out what kind of file 599 * this is. */ 600 if (type == MUS_NONE) { 601 if ((type = detect_music_type(rw)) == MUS_NONE) { 602 /* Don't call Mix_SetError() here since detect_music_type() 603 * does that. */ 604 return NULL; 605 } 606 } 607 608 /* Allocate memory for the music structure */ 609 music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); 610 if (music == NULL ) { 611 Mix_SetError("Out of memory"); 612 return NULL; 613 } 614 music->error = 0; 615 616 switch (type) { 617 #ifdef WAV_MUSIC 618 case MUS_WAV: 619 /* The WAVE loader needs the first 4 bytes of the header */ 620 { 621 Uint8 magic[5]; 622 int start = SDL_RWtell(rw); 623 if (SDL_RWread(rw, magic, 1, 4) != 4) { 624 Mix_SetError("Couldn't read from RWops"); 625 return MUS_NONE; 626 } 627 SDL_RWseek(rw, start, RW_SEEK_SET); 628 magic[4] = '\0'; 629 music->type = MUS_WAV; 630 music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic, freesrc); 631 } 632 if (music->data.wave == NULL) { 633 music->error = 1; 634 } 635 break; 636 #endif 637 #ifdef OGG_MUSIC 638 case MUS_OGG: 639 music->type = MUS_OGG; 640 music->data.ogg = OGG_new_RW(rw, freesrc); 641 if ( music->data.ogg == NULL ) { 642 music->error = 1; 643 } 644 break; 645 #endif 646 #ifdef FLAC_MUSIC 647 case MUS_FLAC: 648 music->type = MUS_FLAC; 649 music->data.flac = FLAC_new_RW(rw, freesrc); 650 if ( music->data.flac == NULL ) { 651 music->error = 1; 652 } 653 break; 654 #endif 655 #ifdef MP3_MUSIC 656 case MUS_MP3: 657 if ( Mix_Init(MIX_INIT_MP3) ) { 658 SMPEG_Info info; 659 music->type = MUS_MP3; 660 music->data.mp3 = smpeg.SMPEG_new_rwops(rw, &info, 0); 661 if ( !info.has_audio ) { 662 Mix_SetError("MPEG file does not have any audio stream."); 663 music->error = 1; 664 } else { 665 smpeg.SMPEG_actualSpec(music->data.mp3, &used_mixer); 666 } 667 } else { 668 music->error = 1; 669 } 670 break; 671 #elif defined(MP3_MAD_MUSIC) 672 case MUS_MP3: 673 music->type = MUS_MP3_MAD; 674 music->data.mp3_mad = mad_openFileRW(rw, &used_mixer, freesrc); 675 if (music->data.mp3_mad == 0) { 676 Mix_SetError("Could not initialize MPEG stream."); 677 music->error = 1; 678 } 679 break; 680 #endif 681 #ifdef MID_MUSIC 682 case MUS_MID: 683 music->type = MUS_MID; 684 #ifdef USE_NATIVE_MIDI 685 if ( native_midi_ok ) { 686 music->data.nativemidi = native_midi_loadsong_RW(rw, freesrc); 687 if ( music->data.nativemidi == NULL ) { 688 Mix_SetError("%s", native_midi_error()); 689 music->error = 1; 690 } 691 break; 692 } 693 #endif 694 #ifdef USE_FLUIDSYNTH_MIDI 695 if ( fluidsynth_ok ) { 696 music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw, freesrc); 697 if ( music->data.fluidsynthmidi == NULL ) { 698 music->error = 1; 699 } 700 break; 701 } 702 #endif 703 #ifdef USE_TIMIDITY_MIDI 704 if ( timidity_ok ) { 705 music->data.midi = Timidity_LoadSong_RW(rw, freesrc); 706 if ( music->data.midi == NULL ) { 707 Mix_SetError("%s", Timidity_Error()); 708 music->error = 1; 709 } 710 } else { 711 Mix_SetError("%s", Timidity_Error()); 712 music->error = 1; 713 } 714 #endif 715 break; 716 #endif 717 #if defined(MODPLUG_MUSIC) || defined(MOD_MUSIC) 718 case MUS_MOD: 719 music->error = 1; 720 #ifdef MODPLUG_MUSIC 721 if ( music->error ) { 722 music->type = MUS_MODPLUG; 723 music->data.modplug = modplug_new_RW(rw, freesrc); 724 if ( music->data.modplug ) { 725 music->error = 0; 726 } 727 } 728 #endif 729 #ifdef MOD_MUSIC 730 if ( music->error ) { 731 music->type = MUS_MOD; 732 music->data.module = MOD_new_RW(rw, freesrc); 733 if ( music->data.module ) { 734 music->error = 0; 735 } 736 } 737 #endif 738 break; 739 #endif 740 741 default: 742 Mix_SetError("Unrecognized music format"); 743 music->error=1; 744 } /* switch (want) */ 745 746 747 if (music->error) { 748 SDL_free(music); 749 music=NULL; 750 } 751 return(music); 752 } 753 754 /* Free a music chunk previously loaded */ 755 void Mix_FreeMusic(Mix_Music *music) 756 { 757 if ( music ) { 758 /* Stop the music if it's currently playing */ 759 SDL_LockAudio(); 760 if ( music == music_playing ) { 761 /* Wait for any fade out to finish */ 762 while ( music->fading == MIX_FADING_OUT ) { 763 SDL_UnlockAudio(); 764 SDL_Delay(100); 765 SDL_LockAudio(); 766 } 767 if ( music == music_playing ) { 768 music_internal_halt(); 769 } 770 } 771 SDL_UnlockAudio(); 772 switch (music->type) { 773 #ifdef CMD_MUSIC 774 case MUS_CMD: 775 MusicCMD_FreeSong(music->data.cmd); 776 break; 777 #endif 778 #ifdef WAV_MUSIC 779 case MUS_WAV: 780 WAVStream_FreeSong(music->data.wave); 781 break; 782 #endif 783 #ifdef MODPLUG_MUSIC 784 case MUS_MODPLUG: 785 modplug_delete(music->data.modplug); 786 break; 787 #endif 788 #ifdef MOD_MUSIC 789 case MUS_MOD: 790 MOD_delete(music->data.module); 791 break; 792 #endif 793 #ifdef MID_MUSIC 794 case MUS_MID: 795 #ifdef USE_NATIVE_MIDI 796 if ( native_midi_ok ) { 797 native_midi_freesong(music->data.nativemidi); 798 goto skip; 799 } 800 #endif 801 #ifdef USE_FLUIDSYNTH_MIDI 802 if ( fluidsynth_ok ) { 803 fluidsynth_freesong(music->data.fluidsynthmidi); 804 goto skip; 805 } 806 #endif 807 #ifdef USE_TIMIDITY_MIDI 808 if ( timidity_ok ) { 809 Timidity_FreeSong(music->data.midi); 810 goto skip; 811 } 812 #endif 813 break; 814 #endif 815 #ifdef OGG_MUSIC 816 case MUS_OGG: 817 OGG_delete(music->data.ogg); 818 break; 819 #endif 820 #ifdef FLAC_MUSIC 821 case MUS_FLAC: 822 FLAC_delete(music->data.flac); 823 break; 824 #endif 825 #ifdef MP3_MUSIC 826 case MUS_MP3: 827 smpeg.SMPEG_delete(music->data.mp3); 828 break; 829 #endif 830 #ifdef MP3_MAD_MUSIC 831 case MUS_MP3_MAD: 832 mad_closeFile(music->data.mp3_mad); 833 break; 834 #endif 835 default: 836 /* Unknown music type?? */ 837 break; 838 } 839 840 skip: 841 SDL_free(music); 842 } 843 } 844 845 /* Find out the music format of a mixer music, or the currently playing 846 music, if 'music' is NULL. 847 */ 848 Mix_MusicType Mix_GetMusicType(const Mix_Music *music) 849 { 850 Mix_MusicType type = MUS_NONE; 851 852 if ( music ) { 853 type = music->type; 854 } else { 855 SDL_LockAudio(); 856 if ( music_playing ) { 857 type = music_playing->type; 858 } 859 SDL_UnlockAudio(); 860 } 861 return(type); 862 } 863 864 /* Play a music chunk. Returns 0, or -1 if there was an error. 865 */ 866 static int music_internal_play(Mix_Music *music, double position) 867 { 868 int retval = 0; 869 870 #if defined(__MACOSX__) && defined(USE_NATIVE_MIDI) 871 /* This fixes a bug with native MIDI on Mac OS X, where you 872 can't really stop and restart MIDI from the audio callback. 873 */ 874 if ( music == music_playing && music->type == MUS_MID && native_midi_ok ) { 875 /* Just a seek suffices to restart playing */ 876 music_internal_position(position); 877 return 0; 878 } 879 #endif 880 881 /* Note the music we're playing */ 882 if ( music_playing ) { 883 music_internal_halt(); 884 } 885 music_playing = music; 886 887 /* Set the initial volume */ 888 if ( music->type != MUS_MOD ) { 889 music_internal_initialize_volume(); 890 } 891 892 /* Set up for playback */ 893 switch (music->type) { 894 #ifdef CMD_MUSIC 895 case MUS_CMD: 896 MusicCMD_Start(music->data.cmd); 897 break; 898 #endif 899 #ifdef WAV_MUSIC 900 case MUS_WAV: 901 WAVStream_Start(music->data.wave); 902 break; 903 #endif 904 #ifdef MODPLUG_MUSIC 905 case MUS_MODPLUG: 906 /* can't set volume until file is loaded, so finally set it now */ 907 music_internal_initialize_volume(); 908 modplug_play(music->data.modplug); 909 break; 910 #endif 911 #ifdef MOD_MUSIC 912 case MUS_MOD: 913 MOD_play(music->data.module); 914 /* Player_SetVolume() does nothing before Player_Start() */ 915 music_internal_initialize_volume(); 916 break; 917 #endif 918 #ifdef MID_MUSIC 919 case MUS_MID: 920 #ifdef USE_NATIVE_MIDI 921 if ( native_midi_ok ) { 922 native_midi_start(music->data.nativemidi, music_loops); 923 goto skip; 924 } 925 #endif 926 #ifdef USE_FLUIDSYNTH_MIDI 927 if (fluidsynth_ok ) { 928 fluidsynth_start(music->data.fluidsynthmidi); 929 goto skip; 930 } 931 #endif 932 #ifdef USE_TIMIDITY_MIDI 933 if ( timidity_ok ) { 934 Timidity_Start(music->data.midi); 935 goto skip; 936 } 937 #endif 938 break; 939 #endif 940 #ifdef OGG_MUSIC 941 case MUS_OGG: 942 OGG_play(music->data.ogg); 943 break; 944 #endif 945 #ifdef FLAC_MUSIC 946 case MUS_FLAC: 947 FLAC_play(music->data.flac); 948 break; 949 #endif 950 #ifdef MP3_MUSIC 951 case MUS_MP3: 952 smpeg.SMPEG_enableaudio(music->data.mp3,1); 953 smpeg.SMPEG_enablevideo(music->data.mp3,0); 954 smpeg.SMPEG_play(music_playing->data.mp3); 955 break; 956 #endif 957 #ifdef MP3_MAD_MUSIC 958 case MUS_MP3_MAD: 959 mad_start(music->data.mp3_mad); 960 break; 961 #endif 962 default: 963 Mix_SetError("Can't play unknown music type"); 964 retval = -1; 965 break; 966 } 967 968 skip: 969 /* Set the playback position, note any errors if an offset is used */ 970 if ( retval == 0 ) { 971 if ( position > 0.0 ) { 972 if ( music_internal_position(position) < 0 ) { 973 Mix_SetError("Position not implemented for music type"); 974 retval = -1; 975 } 976 } else { 977 music_internal_position(0.0); 978 } 979 } 980 981 /* If the setup failed, we're not playing any music anymore */ 982 if ( retval < 0 ) { 983 music_playing = NULL; 984 } 985 return(retval); 986 } 987 int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) 988 { 989 int retval; 990 991 if ( ms_per_step == 0 ) { 992 SDL_SetError("Audio device hasn't been opened"); 993 return(-1); 994 } 995 996 /* Don't play null pointers :-) */ 997 if ( music == NULL ) { 998 Mix_SetError("music parameter was NULL"); 999 return(-1); 1000 } 1001 1002 /* Setup the data */ 1003 if ( ms ) { 1004 music->fading = MIX_FADING_IN; 1005 } else { 1006 music->fading = MIX_NO_FADING; 1007 } 1008 music->fade_step = 0; 1009 music->fade_steps = ms/ms_per_step; 1010 1011 /* Play the puppy */ 1012 SDL_LockAudio(); 1013 /* If the current music is fading out, wait for the fade to complete */ 1014 while ( music_playing && (music_playing->fading == MIX_FADING_OUT) ) { 1015 SDL_UnlockAudio(); 1016 SDL_Delay(100); 1017 SDL_LockAudio(); 1018 } 1019 music_active = 1; 1020 if (loops == 1) { 1021 /* Loop is the number of times to play the audio */ 1022 loops = 0; 1023 } 1024 music_loops = loops; 1025 retval = music_internal_play(music, position); 1026 SDL_UnlockAudio(); 1027 1028 return(retval); 1029 } 1030 int Mix_FadeInMusic(Mix_Music *music, int loops, int ms) 1031 { 1032 return Mix_FadeInMusicPos(music, loops, ms, 0.0); 1033 } 1034 int Mix_PlayMusic(Mix_Music *music, int loops) 1035 { 1036 return Mix_FadeInMusicPos(music, loops, 0, 0.0); 1037 } 1038 1039 /* Set the playing music position */ 1040 int music_internal_position(double position) 1041 { 1042 int retval = 0; 1043 1044 switch (music_playing->type) { 1045 #ifdef MODPLUG_MUSIC 1046 case MUS_MODPLUG: 1047 modplug_jump_to_time(music_playing->data.modplug, position); 1048 break; 1049 #endif 1050 #ifdef MOD_MUSIC 1051 case MUS_MOD: 1052 MOD_jump_to_time(music_playing->data.module, position); 1053 break; 1054 #endif 1055 #ifdef OGG_MUSIC 1056 case MUS_OGG: 1057 OGG_jump_to_time(music_playing->data.ogg, position); 1058 break; 1059 #endif 1060 #ifdef FLAC_MUSIC 1061 case MUS_FLAC: 1062 FLAC_jump_to_time(music_playing->data.flac, position); 1063 break; 1064 #endif 1065 #ifdef MP3_MUSIC 1066 case MUS_MP3: 1067 smpeg.SMPEG_rewind(music_playing->data.mp3); 1068 smpeg.SMPEG_play(music_playing->data.mp3); 1069 if ( position > 0.0 ) { 1070 smpeg.SMPEG_skip(music_playing->data.mp3, (float)position); 1071 } 1072 break; 1073 #endif 1074 #ifdef MP3_MAD_MUSIC 1075 case MUS_MP3_MAD: 1076 mad_seek(music_playing->data.mp3_mad, position); 1077 break; 1078 #endif 1079 default: 1080 /* TODO: Implement this for other music backends */ 1081 retval = -1; 1082 break; 1083 } 1084 return(retval); 1085 } 1086 int Mix_SetMusicPosition(double position) 1087 { 1088 int retval; 1089 1090 SDL_LockAudio(); 1091 if ( music_playing ) { 1092 retval = music_internal_position(position); 1093 if ( retval < 0 ) { 1094 Mix_SetError("Position not implemented for music type"); 1095 } 1096 } else { 1097 Mix_SetError("Music isn't playing"); 1098 retval = -1; 1099 } 1100 SDL_UnlockAudio(); 1101 1102 return(retval); 1103 } 1104 1105 /* Set the music's initial volume */ 1106 static void music_internal_initialize_volume(void) 1107 { 1108 if ( music_playing->fading == MIX_FADING_IN ) { 1109 music_internal_volume(0); 1110 } else { 1111 music_internal_volume(music_volume); 1112 } 1113 } 1114 1115 /* Set the music volume */ 1116 static void music_internal_volume(int volume) 1117 { 1118 switch (music_playing->type) { 1119 #ifdef CMD_MUSIC 1120 case MUS_CMD: 1121 MusicCMD_SetVolume(volume); 1122 break; 1123 #endif 1124 #ifdef WAV_MUSIC 1125 case MUS_WAV: 1126 WAVStream_SetVolume(volume); 1127 break; 1128 #endif 1129 #ifdef MODPLUG_MUSIC 1130 case MUS_MODPLUG: 1131 modplug_setvolume(music_playing->data.modplug, volume); 1132 break; 1133 #endif 1134 #ifdef MOD_MUSIC 1135 case MUS_MOD: 1136 MOD_setvolume(music_playing->data.module, volume); 1137 break; 1138 #endif 1139 #ifdef MID_MUSIC 1140 case MUS_MID: 1141 #ifdef USE_NATIVE_MIDI 1142 if ( native_midi_ok ) { 1143 native_midi_setvolume(volume); 1144 return; 1145 } 1146 #endif 1147 #ifdef USE_FLUIDSYNTH_MIDI 1148 if ( fluidsynth_ok ) { 1149 fluidsynth_setvolume(music_playing->data.fluidsynthmidi, volume); 1150 return; 1151 } 1152 #endif 1153 #ifdef USE_TIMIDITY_MIDI 1154 if ( timidity_ok ) { 1155 Timidity_SetVolume(volume); 1156 return; 1157 } 1158 #endif 1159 break; 1160 #endif 1161 #ifdef OGG_MUSIC 1162 case MUS_OGG: 1163 OGG_setvolume(music_playing->data.ogg, volume); 1164 break; 1165 #endif 1166 #ifdef FLAC_MUSIC 1167 case MUS_FLAC: 1168 FLAC_setvolume(music_playing->data.flac, volume); 1169 break; 1170 #endif 1171 #ifdef MP3_MUSIC 1172 case MUS_MP3: 1173 smpeg.SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); 1174 break; 1175 #endif 1176 #ifdef MP3_MAD_MUSIC 1177 case MUS_MP3_MAD: 1178 mad_setVolume(music_playing->data.mp3_mad, volume); 1179 break; 1180 #endif 1181 default: 1182 /* Unknown music type?? */ 1183 break; 1184 } 1185 } 1186 int Mix_VolumeMusic(int volume) 1187 { 1188 int prev_volume; 1189 1190 prev_volume = music_volume; 1191 if ( volume < 0 ) { 1192 return prev_volume; 1193 } 1194 if ( volume > SDL_MIX_MAXVOLUME ) { 1195 volume = SDL_MIX_MAXVOLUME; 1196 } 1197 music_volume = volume; 1198 SDL_LockAudio(); 1199 if ( music_playing ) { 1200 music_internal_volume(music_volume); 1201 } 1202 SDL_UnlockAudio(); 1203 return(prev_volume); 1204 } 1205 1206 /* Halt playing of music */ 1207 static void music_internal_halt(void) 1208 { 1209 switch (music_playing->type) { 1210 #ifdef CMD_MUSIC 1211 case MUS_CMD: 1212 MusicCMD_Stop(music_playing->data.cmd); 1213 break; 1214 #endif 1215 #ifdef WAV_MUSIC 1216 case MUS_WAV: 1217 WAVStream_Stop(); 1218 break; 1219 #endif 1220 #ifdef MODPLUG_MUSIC 1221 case MUS_MODPLUG: 1222 modplug_stop(music_playing->data.modplug); 1223 break; 1224 #endif 1225 #ifdef MOD_MUSIC 1226 case MUS_MOD: 1227 MOD_stop(music_playing->data.module); 1228 break; 1229 #endif 1230 #ifdef MID_MUSIC 1231 case MUS_MID: 1232 #ifdef USE_NATIVE_MIDI 1233 if ( native_midi_ok ) { 1234 native_midi_stop(); 1235 goto skip; 1236 } 1237 #endif 1238 #ifdef USE_FLUIDSYNTH_MIDI 1239 if ( fluidsynth_ok ) { 1240 fluidsynth_stop(music_playing->data.fluidsynthmidi); 1241 goto skip; 1242 } 1243 #endif 1244 #ifdef USE_TIMIDITY_MIDI 1245 if ( timidity_ok ) { 1246 Timidity_Stop(); 1247 goto skip; 1248 } 1249 #endif 1250 break; 1251 #endif 1252 #ifdef OGG_MUSIC 1253 case MUS_OGG: 1254 OGG_stop(music_playing->data.ogg); 1255 break; 1256 #endif 1257 #ifdef FLAC_MUSIC 1258 case MUS_FLAC: 1259 FLAC_stop(music_playing->data.flac); 1260 break; 1261 #endif 1262 #ifdef MP3_MUSIC 1263 case MUS_MP3: 1264 smpeg.SMPEG_stop(music_playing->data.mp3); 1265 break; 1266 #endif 1267 #ifdef MP3_MAD_MUSIC 1268 case MUS_MP3_MAD: 1269 mad_stop(music_playing->data.mp3_mad); 1270 break; 1271 #endif 1272 default: 1273 /* Unknown music type?? */ 1274 return; 1275 } 1276 1277 skip: 1278 music_playing->fading = MIX_NO_FADING; 1279 music_playing = NULL; 1280 } 1281 int Mix_HaltMusic(void) 1282 { 1283 SDL_LockAudio(); 1284 if ( music_playing ) { 1285 music_internal_halt(); 1286 } 1287 SDL_UnlockAudio(); 1288 1289 return(0); 1290 } 1291 1292 /* Progressively stop the music */ 1293 int Mix_FadeOutMusic(int ms) 1294 { 1295 int retval = 0; 1296 1297 if ( ms_per_step == 0 ) { 1298 SDL_SetError("Audio device hasn't been opened"); 1299 return 0; 1300 } 1301 1302 if (ms <= 0) { /* just halt immediately. */ 1303 Mix_HaltMusic(); 1304 return 1; 1305 } 1306 1307 SDL_LockAudio(); 1308 if ( music_playing) { 1309 int fade_steps = (ms + ms_per_step - 1)/ms_per_step; 1310 if ( music_playing->fading == MIX_NO_FADING ) { 1311 music_playing->fade_step = 0; 1312 } else { 1313 int step; 1314 int old_fade_steps = music_playing->fade_steps; 1315 if ( music_playing->fading == MIX_FADING_OUT ) { 1316 step = music_playing->fade_step; 1317 } else { 1318 step = old_fade_steps 1319 - music_playing->fade_step + 1; 1320 } 1321 music_playing->fade_step = (step * fade_steps) 1322 / old_fade_steps; 1323 } 1324 music_playing->fading = MIX_FADING_OUT; 1325 music_playing->fade_steps = fade_steps; 1326 retval = 1; 1327 } 1328 SDL_UnlockAudio(); 1329 1330 return(retval); 1331 } 1332 1333 Mix_Fading Mix_FadingMusic(void) 1334 { 1335 Mix_Fading fading = MIX_NO_FADING; 1336 1337 SDL_LockAudio(); 1338 if ( music_playing ) { 1339 fading = music_playing->fading; 1340 } 1341 SDL_UnlockAudio(); 1342 1343 return(fading); 1344 } 1345 1346 /* Pause/Resume the music stream */ 1347 void Mix_PauseMusic(void) 1348 { 1349 music_active = 0; 1350 } 1351 1352 void Mix_ResumeMusic(void) 1353 { 1354 music_active = 1; 1355 } 1356 1357 void Mix_RewindMusic(void) 1358 { 1359 Mix_SetMusicPosition(0.0); 1360 } 1361 1362 int Mix_PausedMusic(void) 1363 { 1364 return (music_active == 0); 1365 } 1366 1367 /* Check the status of the music */ 1368 static int music_internal_playing() 1369 { 1370 int playing = 1; 1371 1372 if (music_playing == NULL) { 1373 return 0; 1374 } 1375 1376 switch (music_playing->type) { 1377 #ifdef CMD_MUSIC 1378 case MUS_CMD: 1379 if (!MusicCMD_Active(music_playing->data.cmd)) { 1380 playing = 0; 1381 } 1382 break; 1383 #endif 1384 #ifdef WAV_MUSIC 1385 case MUS_WAV: 1386 if ( ! WAVStream_Active() ) { 1387 playing = 0; 1388 } 1389 break; 1390 #endif 1391 #ifdef MODPLUG_MUSIC 1392 case MUS_MODPLUG: 1393 if ( ! modplug_playing(music_playing->data.modplug) ) { 1394 playing = 0; 1395 } 1396 break; 1397 #endif 1398 #ifdef MOD_MUSIC 1399 case MUS_MOD: 1400 if ( ! MOD_playing(music_playing->data.module) ) { 1401 playing = 0; 1402 } 1403 break; 1404 #endif 1405 #ifdef MID_MUSIC 1406 case MUS_MID: 1407 #ifdef USE_NATIVE_MIDI 1408 if ( native_midi_ok ) { 1409 if ( ! native_midi_active() ) 1410 playing = 0; 1411 goto skip; 1412 } 1413 #endif 1414 #ifdef USE_FLUIDSYNTH_MIDI 1415 if ( fluidsynth_ok ) { 1416 if ( ! fluidsynth_active(music_playing->data.fluidsynthmidi) ) 1417 playing = 0; 1418 goto skip; 1419 } 1420 #endif 1421 #ifdef USE_TIMIDITY_MIDI 1422 if ( timidity_ok ) { 1423 if ( ! Timidity_Active() ) 1424 playing = 0; 1425 goto skip; 1426 } 1427 #endif 1428 break; 1429 #endif 1430 #ifdef OGG_MUSIC 1431 case MUS_OGG: 1432 if ( ! OGG_playing(music_playing->data.ogg) ) { 1433 playing = 0; 1434 } 1435 break; 1436 #endif 1437 #ifdef FLAC_MUSIC 1438 case MUS_FLAC: 1439 if ( ! FLAC_playing(music_playing->data.flac) ) { 1440 playing = 0; 1441 } 1442 break; 1443 #endif 1444 #ifdef MP3_MUSIC 1445 case MUS_MP3: 1446 if ( smpeg.SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING ) 1447 playing = 0; 1448 break; 1449 #endif 1450 #ifdef MP3_MAD_MUSIC 1451 case MUS_MP3_MAD: 1452 if (!mad_isPlaying(music_playing->data.mp3_mad)) { 1453 playing = 0; 1454 } 1455 break; 1456 #endif 1457 default: 1458 playing = 0; 1459 break; 1460 } 1461 1462 skip: 1463 return(playing); 1464 } 1465 int Mix_PlayingMusic(void) 1466 { 1467 int playing = 0; 1468 1469 SDL_LockAudio(); 1470 if ( music_playing ) { 1471 playing = music_loops || music_internal_playing(); 1472 } 1473 SDL_UnlockAudio(); 1474 1475 return(playing); 1476 } 1477 1478 /* Set the external music playback command */ 1479 int Mix_SetMusicCMD(const char *command) 1480 { 1481 Mix_HaltMusic(); 1482 if ( music_cmd ) { 1483 SDL_free(music_cmd); 1484 music_cmd = NULL; 1485 } 1486 if ( command ) { 1487 music_cmd = (char *)SDL_malloc(strlen(command)+1); 1488 if ( music_cmd == NULL ) { 1489 return(-1); 1490 } 1491 strcpy(music_cmd, command); 1492 } 1493 return(0); 1494 } 1495 1496 int Mix_SetSynchroValue(int i) 1497 { 1498 /* Not supported by any players at this time */ 1499 return(-1); 1500 } 1501 1502 int Mix_GetSynchroValue(void) 1503 { 1504 /* Not supported by any players at this time */ 1505 return(-1); 1506 } 1507 1508 1509 /* Uninitialize the music players */ 1510 void close_music(void) 1511 { 1512 Mix_HaltMusic(); 1513 #ifdef CMD_MUSIC 1514 Mix_SetMusicCMD(NULL); 1515 #endif 1516 #ifdef MODPLUG_MUSIC 1517 modplug_exit(); 1518 #endif 1519 #ifdef MOD_MUSIC 1520 MOD_exit(); 1521 #endif 1522 #ifdef MID_MUSIC 1523 # ifdef USE_TIMIDITY_MIDI 1524 Timidity_Close(); 1525 # endif 1526 #endif 1527 1528 /* rcg06042009 report available decoders at runtime. */ 1529 SDL_free(music_decoders); 1530 music_decoders = NULL; 1531 num_decoders = 0; 1532 1533 ms_per_step = 0; 1534 } 1535 1536 int Mix_SetSoundFonts(const char *paths) 1537 { 1538 #ifdef MID_MUSIC 1539 if (soundfont_paths) { 1540 SDL_free(soundfont_paths); 1541 soundfont_paths = NULL; 1542 } 1543 1544 if (paths) { 1545 if (!(soundfont_paths = SDL_strdup(paths))) { 1546 Mix_SetError("Insufficient memory to set SoundFonts"); 1547 return 0; 1548 } 1549 } 1550 #endif 1551 return 1; 1552 } 1553 1554 #ifdef MID_MUSIC 1555 const char* Mix_GetSoundFonts(void) 1556 { 1557 const char* force = getenv("SDL_FORCE_SOUNDFONTS"); 1558 1559 if (!soundfont_paths || (force && force[0] == '1')) { 1560 return getenv("SDL_SOUNDFONTS"); 1561 } else { 1562 return soundfont_paths; 1563 } 1564 } 1565 1566 int Mix_EachSoundFont(int (*function)(const char*, void*), void *data) 1567 { 1568 char *context, *path, *paths; 1569 const char* cpaths = Mix_GetSoundFonts(); 1570 1571 if (!cpaths) { 1572 Mix_SetError("No SoundFonts have been requested"); 1573 return 0; 1574 } 1575 1576 if (!(paths = SDL_strdup(cpaths))) { 1577 Mix_SetError("Insufficient memory to iterate over SoundFonts"); 1578 return 0; 1579 } 1580 1581 #if defined(__MINGW32__) || defined(__MINGW64__) 1582 for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) { 1583 #elif defined(_WIN32) 1584 for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) { 1585 #else 1586 for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) { 1587 #endif 1588 if (!function(path, data)) { 1589 SDL_free(paths); 1590 return 0; 1591 } 1592 } 1593 1594 SDL_free(paths); 1595 return 1; 1596 } 1597 #endif 1598