music.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 16:43:23 -0700
changeset 780 74df2aa47195
parent 779 a2b494c054d5
child 797 b4b6adff699a
permissions -rw-r--r--
Fixed various issues with the new SDL_mixer code (thanks Ozkan!)
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 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 #include <string.h> /* for strtok() and strtok_s() */
    22 
    23 #include "SDL_hints.h"
    24 #include "SDL_timer.h"
    25 
    26 #include "SDL_mixer.h"
    27 #include "mixer.h"
    28 #include "music.h"
    29 
    30 #include "music_cmd.h"
    31 #include "music_wav.h"
    32 #include "music_mikmod.h"
    33 #include "music_modplug.h"
    34 #include "music_nativemidi.h"
    35 #include "music_fluidsynth.h"
    36 #include "music_timidity.h"
    37 #include "music_ogg.h"
    38 #include "music_mpg123.h"
    39 #include "music_mad.h"
    40 #include "music_smpeg.h"
    41 #include "music_flac.h"
    42 #include "native_midi/native_midi.h"
    43 
    44 
    45 char *music_cmd = NULL;
    46 int volatile music_active = 1;
    47 static Mix_Music * volatile music_playing = NULL;
    48 int music_loops = 0;
    49 int music_volume = MIX_MAX_VOLUME;
    50 SDL_AudioSpec music_spec;
    51 
    52 struct _Mix_Music {
    53     Mix_MusicInterface *interface;
    54     void *context;
    55 
    56     SDL_bool playing;
    57     Mix_Fading fading;
    58     int fade_step;
    59     int fade_steps;
    60     int error;
    61 };
    62 
    63 /* Used to calculate fading steps */
    64 static int ms_per_step;
    65 
    66 /* rcg06042009 report available decoders at runtime. */
    67 static const char **music_decoders = NULL;
    68 static int num_decoders = 0;
    69 
    70 /* Semicolon-separated SoundFont paths */
    71 char* soundfont_paths = NULL;
    72 
    73 /* Interfaces for the various music interfaces, ordered by priority */
    74 static Mix_MusicInterface *s_music_interfaces[] =
    75 {
    76 #ifdef MUSIC_CMD
    77     &Mix_MusicInterface_CMD,
    78 #endif
    79 #ifdef MUSIC_WAV
    80     &Mix_MusicInterface_WAV,
    81 #endif
    82 #ifdef MUSIC_MOD_MODPLUG
    83     &Mix_MusicInterface_MODPLUG,
    84 #endif
    85 #ifdef MUSIC_MOD_MIKMOD
    86     &Mix_MusicInterface_MIKMOD,
    87 #endif
    88 #ifdef MUSIC_MID_FLUIDSYNTH
    89     &Mix_MusicInterface_FLUIDSYNTH,
    90 #endif
    91 #ifdef MUSIC_MID_TIMIDITY
    92     &Mix_MusicInterface_TIMIDITY,
    93 #endif
    94 #ifdef MUSIC_MID_NATIVE
    95     &Mix_MusicInterface_NATIVEMIDI,
    96 #endif
    97 #ifdef MUSIC_OGG
    98     &Mix_MusicInterface_OGG,
    99 #endif
   100 #ifdef MUSIC_MP3_MPG123
   101     &Mix_MusicInterface_MPG123,
   102 #endif
   103 #ifdef MUSIC_MP3_MAD
   104     &Mix_MusicInterface_MAD,
   105 #endif
   106 #ifdef MUSIC_MP3_SMPEG
   107     &Mix_MusicInterface_SMPEG,
   108 #endif
   109 #ifdef MUSIC_FLAC
   110     &Mix_MusicInterface_FLAC,
   111 #endif
   112 };
   113 
   114 int get_num_music_interfaces(void)
   115 {
   116     return SDL_arraysize(s_music_interfaces);
   117 }
   118 
   119 Mix_MusicInterface *get_music_interface(int index)
   120 {
   121     return s_music_interfaces[index];
   122 }
   123 
   124 int Mix_GetNumMusicDecoders(void)
   125 {
   126     return(num_decoders);
   127 }
   128 
   129 const char *Mix_GetMusicDecoder(int index)
   130 {
   131     if ((index < 0) || (index >= num_decoders)) {
   132         return NULL;
   133     }
   134     return(music_decoders[index]);
   135 }
   136 
   137 static void add_music_decoder(const char *decoder)
   138 {
   139     void *ptr = SDL_realloc((void *)music_decoders, (num_decoders + 1) * sizeof (const char *));
   140     if (ptr == NULL) {
   141         return;  /* oh well, go on without it. */
   142     }
   143     music_decoders = (const char **) ptr;
   144     music_decoders[num_decoders++] = decoder;
   145 }
   146 
   147 /* Local low-level functions prototypes */
   148 static void music_internal_initialize_volume(void);
   149 static void music_internal_volume(int volume);
   150 static int  music_internal_play(Mix_Music *music, double position);
   151 static int  music_internal_position(double position);
   152 static SDL_bool music_internal_playing(void);
   153 static void music_internal_halt(void);
   154 
   155 
   156 /* Support for hooking when the music has finished */
   157 static void (*music_finished_hook)(void) = NULL;
   158 
   159 void Mix_HookMusicFinished(void (*music_finished)(void))
   160 {
   161     Mix_LockAudio();
   162     music_finished_hook = music_finished;
   163     Mix_UnlockAudio();
   164 }
   165 
   166 
   167 /* If music isn't playing, halt it if no looping is required, restart it */
   168 /* otherwise. NOP if the music is playing */
   169 static int music_halt_or_loop(void)
   170 {
   171     /* Restart music if it has to loop */
   172 
   173     if (!music_internal_playing())
   174     {
   175         /* Native MIDI handles looping internally */
   176         if (music_playing->interface->api == MIX_MUSIC_NATIVEMIDI) {
   177             music_loops = 0;
   178         }
   179 
   180         /* Restart music if it has to loop at a high level */
   181         if (music_loops)
   182         {
   183             Mix_Fading current_fade;
   184             if (music_loops > 0) {
   185                 --music_loops;
   186             }
   187             current_fade = music_playing->fading;
   188             music_internal_play(music_playing, 0.0);
   189             music_playing->fading = current_fade;
   190         }
   191         else
   192         {
   193             music_internal_halt();
   194             if (music_finished_hook)
   195                 music_finished_hook();
   196 
   197             return 0;
   198         }
   199     }
   200 
   201     return 1;
   202 }
   203 
   204 
   205 /* Mixing function */
   206 void music_mixer(void *udata, Uint8 *stream, int len)
   207 {
   208     if (music_playing && music_active) {
   209         /* Handle fading */
   210         if (music_playing->fading != MIX_NO_FADING) {
   211             if (music_playing->fade_step++ < music_playing->fade_steps) {
   212                 int volume;
   213                 int fade_step = music_playing->fade_step;
   214                 int fade_steps = music_playing->fade_steps;
   215 
   216                 if (music_playing->fading == MIX_FADING_OUT) {
   217                     volume = (music_volume * (fade_steps-fade_step)) / fade_steps;
   218                 } else { /* Fading in */
   219                     volume = (music_volume * fade_step) / fade_steps;
   220                 }
   221                 music_internal_volume(volume);
   222             } else {
   223                 if (music_playing->fading == MIX_FADING_OUT) {
   224                     music_internal_halt();
   225                     if (music_finished_hook) {
   226                         music_finished_hook();
   227                     }
   228                     return;
   229                 }
   230                 music_playing->fading = MIX_NO_FADING;
   231             }
   232         }
   233 
   234         music_halt_or_loop();
   235         if (!music_internal_playing()) {
   236             return;
   237         }
   238 
   239         if (music_playing->interface->GetAudio) {
   240             int left = music_playing->interface->GetAudio(music_playing->context, stream, len);
   241             if (left > 0) {
   242                 music_playing->playing = SDL_FALSE;
   243             }
   244 
   245             /* Handle seamless music looping */
   246             if (left > 0 && left < len) {
   247                 music_halt_or_loop();
   248                 if (music_internal_playing()) {
   249                     music_mixer(udata, stream+(len-left), left);
   250                 }
   251             }
   252         }
   253     }
   254 }
   255 
   256 /* Load the music interface libraries */
   257 int load_music(void)
   258 {
   259     char hint[128];
   260 
   261     int i;
   262     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   263         Mix_MusicInterface *interface = s_music_interfaces[i];
   264         if (interface->loaded) {
   265             continue;
   266         }
   267 
   268         SDL_snprintf(hint, sizeof(hint), "SDL_MIXER_DISABLE_%s", interface->tag);
   269         if (SDL_GetHintBoolean(hint, SDL_FALSE)) {
   270             continue;
   271         }
   272 
   273         if (!interface->Load || interface->Load() == 0) {
   274             interface->loaded = SDL_TRUE;
   275         }
   276     }
   277     return 0;
   278 }
   279 
   280 /* Return SDL_TRUE if the music type is available */
   281 SDL_bool has_music(Mix_MusicType type)
   282 {
   283     int i;
   284     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   285         Mix_MusicInterface *interface = s_music_interfaces[i];
   286         if (interface->type != type) {
   287             continue;
   288         }
   289         if (interface->opened) {
   290             return SDL_TRUE;
   291         }
   292     }
   293     return SDL_FALSE;
   294 }
   295 
   296 /* Initialize the music interfaces with a certain desired audio format */
   297 int open_music(const SDL_AudioSpec *spec)
   298 {
   299     int i;
   300     SDL_bool use_native_midi = SDL_FALSE;
   301 
   302 #ifdef MUSIC_MID_NATIVE
   303     if (SDL_GetHintBoolean("SDL_NATIVE_MUSIC", SDL_FALSE) && native_midi_detect()) {
   304         use_native_midi = SDL_TRUE;
   305     }
   306 #endif
   307 
   308     music_spec = *spec;
   309     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   310         Mix_MusicInterface *interface = s_music_interfaces[i];
   311         if (!interface->loaded) {
   312             continue;
   313         }
   314 
   315         if (interface->type == MUS_MID && use_native_midi && interface->api != MIX_MUSIC_NATIVEMIDI) {
   316             continue;
   317         }
   318 
   319         if (!interface->Open || interface->Open(spec) == 0) {
   320             interface->opened = SDL_TRUE;
   321             add_music_decoder(interface->tag);
   322         }
   323     }
   324 
   325     if (has_music(MUS_MOD)) {
   326         add_music_decoder("MOD");
   327     }
   328     if (has_music(MUS_MID)) {
   329         add_music_decoder("MIDI");
   330     }
   331     if (has_music(MUS_MP3)) {
   332         add_music_decoder("MP3");
   333     }
   334 
   335     Mix_VolumeMusic(MIX_MAX_VOLUME);
   336 
   337     /* Calculate the number of ms for each callback */
   338     ms_per_step = (int) (((float)spec->samples * 1000.0) / spec->freq);
   339 
   340     return 0;
   341 }
   342 
   343 Mix_MusicType detect_music_type_from_magic(const Uint8 *magic)
   344 {
   345     /* Ogg Vorbis files have the magic four bytes "OggS" */
   346     if (SDL_memcmp(magic, "OggS", 4) == 0) {
   347         return MUS_OGG;
   348     }
   349 
   350     /* FLAC files have the magic four bytes "fLaC" */
   351     if (SDL_memcmp(magic, "fLaC", 4) == 0) {
   352         return MUS_FLAC;
   353     }
   354 
   355     /* MIDI files have the magic four bytes "MThd" */
   356     if (SDL_memcmp(magic, "MThd", 4) == 0) {
   357         return MUS_MID;
   358     }
   359 
   360     if (SDL_memcmp(magic, "ID3", 3) == 0 ||
   361         (magic[0] == 0xFF && (magic[1] & 0xFE) == 0xFA)) {
   362         return MUS_MP3;
   363     }
   364 
   365     /* Assume MOD format.
   366      *
   367      * Apparently there is no way to check if the file is really a MOD,
   368      * or there are too many formats supported by MikMod/ModPlug, or
   369      * MikMod/ModPlug does this check by itself. */
   370     return MUS_MOD;
   371 }
   372 
   373 static Mix_MusicType detect_music_type(SDL_RWops *src)
   374 {
   375     Uint8 magic[12];
   376 
   377     if (SDL_RWread(src, magic, 1, 12) != 12) {
   378         Mix_SetError("Couldn't read first 12 bytes of audio data");
   379         return MUS_NONE;
   380     }
   381     SDL_RWseek(src, -12, RW_SEEK_CUR);
   382 
   383     /* WAVE files have the magic four bytes "RIFF"
   384        AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */
   385     if (((SDL_memcmp(magic, "RIFF", 4) == 0) && (SDL_memcmp((magic+8), "WAVE", 4) == 0)) ||
   386         (SDL_memcmp(magic, "FORM", 4) == 0)) {
   387         return MUS_WAV;
   388     }
   389 
   390     return detect_music_type_from_magic(magic);
   391 }
   392 
   393 /* Load a music file */
   394 Mix_Music *Mix_LoadMUS(const char *file)
   395 {
   396     int i;
   397     void *context;
   398     char *ext;
   399     Mix_MusicType type;
   400     SDL_RWops *src;
   401 
   402     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   403         Mix_MusicInterface *interface = s_music_interfaces[i];
   404         if (!interface->opened || !interface->CreateFromFile) {
   405             continue;
   406         }
   407 
   408         context = interface->CreateFromFile(file);
   409         if (context) {
   410             /* Allocate memory for the music structure */
   411             Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music));
   412             if (music == NULL) {
   413                 Mix_SetError("Out of memory");
   414                 return NULL;
   415             }
   416             music->interface = interface;
   417             music->context = context;
   418             return music;
   419         }
   420     }
   421 
   422     src = SDL_RWFromFile(file, "rb");
   423     if (src == NULL) {
   424         Mix_SetError("Couldn't open '%s'", file);
   425         return NULL;
   426     }
   427 
   428     /* Use the extension as a first guess on the file type */
   429     type = MUS_NONE;
   430     ext = strrchr(file, '.');
   431     if (ext) {
   432         ++ext; /* skip the dot in the extension */
   433         if (SDL_strcasecmp(ext, "WAV") == 0) {
   434             type = MUS_WAV;
   435         } else if (SDL_strcasecmp(ext, "MID") == 0 ||
   436                     SDL_strcasecmp(ext, "MIDI") == 0 ||
   437                     SDL_strcasecmp(ext, "KAR") == 0) {
   438             type = MUS_MID;
   439         } else if (SDL_strcasecmp(ext, "OGG") == 0) {
   440             type = MUS_OGG;
   441         } else if (SDL_strcasecmp(ext, "FLAC") == 0) {
   442             type = MUS_FLAC;
   443         } else  if (SDL_strcasecmp(ext, "MPG") == 0 ||
   444                      SDL_strcasecmp(ext, "MPEG") == 0 ||
   445                      SDL_strcasecmp(ext, "MP3") == 0 ||
   446                      SDL_strcasecmp(ext, "MAD") == 0) {
   447             type = MUS_MP3;
   448         } else if (SDL_strcasecmp(ext, "669") == 0 ||
   449                     SDL_strcasecmp(ext, "AMF") == 0 ||
   450                     SDL_strcasecmp(ext, "AMS") == 0 ||
   451                     SDL_strcasecmp(ext, "DBM") == 0 ||
   452                     SDL_strcasecmp(ext, "DSM") == 0 ||
   453                     SDL_strcasecmp(ext, "FAR") == 0 ||
   454                     SDL_strcasecmp(ext, "IT") == 0 ||
   455                     SDL_strcasecmp(ext, "MED") == 0 ||
   456                     SDL_strcasecmp(ext, "MDL") == 0 ||
   457                     SDL_strcasecmp(ext, "MOD") == 0 ||
   458                     SDL_strcasecmp(ext, "MOL") == 0 ||
   459                     SDL_strcasecmp(ext, "MTM") == 0 ||
   460                     SDL_strcasecmp(ext, "NST") == 0 ||
   461                     SDL_strcasecmp(ext, "OKT") == 0 ||
   462                     SDL_strcasecmp(ext, "PTM") == 0 ||
   463                     SDL_strcasecmp(ext, "S3M") == 0 ||
   464                     SDL_strcasecmp(ext, "STM") == 0 ||
   465                     SDL_strcasecmp(ext, "ULT") == 0 ||
   466                     SDL_strcasecmp(ext, "UMX") == 0 ||
   467                     SDL_strcasecmp(ext, "WOW") == 0 ||
   468                     SDL_strcasecmp(ext, "XM") == 0) {
   469             type = MUS_MOD;
   470         }
   471     }
   472     return Mix_LoadMUSType_RW(src, type, SDL_TRUE);
   473 }
   474 
   475 Mix_Music *Mix_LoadMUS_RW(SDL_RWops *src, int freesrc)
   476 {
   477     return Mix_LoadMUSType_RW(src, MUS_NONE, freesrc);
   478 }
   479 
   480 Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *src, Mix_MusicType type, int freesrc)
   481 {
   482     int i;
   483     void *context;
   484     Sint64 start;
   485 
   486     if (!src) {
   487         Mix_SetError("RWops pointer is NULL");
   488         return NULL;
   489     }
   490     start = SDL_RWtell(src);
   491 
   492     /* If the caller wants auto-detection, figure out what kind of file
   493      * this is. */
   494     if (type == MUS_NONE) {
   495         if ((type = detect_music_type(src)) == MUS_NONE) {
   496             /* Don't call Mix_SetError() since detect_music_type() does that. */
   497             if (freesrc) {
   498                 SDL_RWclose(src);
   499             }
   500             return NULL;
   501         }
   502     }
   503 
   504     Mix_ClearError();
   505 
   506     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   507         Mix_MusicInterface *interface = s_music_interfaces[i];
   508         if (!interface->opened || type != interface->type || !interface->CreateFromRW) {
   509             continue;
   510         }
   511 
   512         context = interface->CreateFromRW(src, freesrc);
   513         if (context) {
   514             /* Allocate memory for the music structure */
   515             Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music));
   516             if (music == NULL) {
   517                 interface->Delete(context);
   518                 Mix_SetError("Out of memory");
   519                 return NULL;
   520             }
   521             music->interface = interface;
   522             music->context = context;
   523             return music;
   524         }
   525 
   526         if (interface->api == MIX_MUSIC_SMPEG) {
   527             /* Uh oh, if SMPEG couldn't create a context, it freed the src */
   528             freesrc = SDL_FALSE;
   529             break;
   530         }
   531 
   532         /* Reset the stream for the next decoder */
   533         SDL_RWseek(src, start, RW_SEEK_SET);
   534     }
   535 
   536     if (!*Mix_GetError()) {
   537         Mix_SetError("Unrecognized audio format");
   538     }
   539     if (freesrc) {
   540         SDL_RWclose(src);
   541     } else {
   542         SDL_RWseek(src, start, RW_SEEK_SET);
   543     }
   544     return NULL;
   545 }
   546 
   547 /* Free a music chunk previously loaded */
   548 void Mix_FreeMusic(Mix_Music *music)
   549 {
   550     if (music) {
   551         /* Stop the music if it's currently playing */
   552         Mix_LockAudio();
   553         if (music == music_playing) {
   554             /* Wait for any fade out to finish */
   555             while (music->fading == MIX_FADING_OUT) {
   556                 Mix_UnlockAudio();
   557                 SDL_Delay(100);
   558                 Mix_LockAudio();
   559             }
   560             if (music == music_playing) {
   561                 music_internal_halt();
   562             }
   563         }
   564         Mix_UnlockAudio();
   565 
   566         music->interface->Delete(music->context);
   567         SDL_free(music);
   568     }
   569 }
   570 
   571 /* Find out the music format of a mixer music, or the currently playing
   572    music, if 'music' is NULL.
   573 */
   574 Mix_MusicType Mix_GetMusicType(const Mix_Music *music)
   575 {
   576     Mix_MusicType type = MUS_NONE;
   577 
   578     if (music) {
   579         type = music->interface->type;
   580     } else {
   581         Mix_LockAudio();
   582         if (music_playing) {
   583             type = music_playing->interface->type;
   584         }
   585         Mix_UnlockAudio();
   586     }
   587     return(type);
   588 }
   589 
   590 /* Play a music chunk.  Returns 0, or -1 if there was an error.
   591  */
   592 static int music_internal_play(Mix_Music *music, double position)
   593 {
   594     int retval = 0;
   595 
   596 #if defined(__MACOSX__) && defined(MID_MUSIC_NATIVE)
   597     /* This fixes a bug with native MIDI on Mac OS X, where you
   598        can't really stop and restart MIDI from the audio callback.
   599     */
   600     if (music == music_playing && music->api == MIX_MUSIC_NATIVEMIDI) {
   601         /* Just a seek suffices to restart playing */
   602         music_internal_position(position);
   603         return 0;
   604     }
   605 #endif
   606 
   607     /* Note the music we're playing */
   608     if (music_playing) {
   609         music_internal_halt();
   610     }
   611     music_playing = music;
   612     music_playing->playing = SDL_TRUE;
   613 
   614     /* Set the initial volume */
   615     music_internal_initialize_volume();
   616 
   617     /* Set up for playback */
   618     retval = music->interface->Play(music->context);
   619 
   620     /* Set the playback position, note any errors if an offset is used */
   621     if (retval == 0) {
   622         if (position > 0.0) {
   623             if (music_internal_position(position) < 0) {
   624                 Mix_SetError("Position not implemented for music type");
   625                 retval = -1;
   626             }
   627         } else {
   628             music_internal_position(0.0);
   629         }
   630     }
   631 
   632     /* If the setup failed, we're not playing any music anymore */
   633     if (retval < 0) {
   634         music_playing = NULL;
   635     }
   636     return(retval);
   637 }
   638 
   639 int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
   640 {
   641     int retval;
   642 
   643     if (ms_per_step == 0) {
   644         SDL_SetError("Audio device hasn't been opened");
   645         return(-1);
   646     }
   647 
   648     /* Don't play null pointers :-) */
   649     if (music == NULL) {
   650         Mix_SetError("music parameter was NULL");
   651         return(-1);
   652     }
   653 
   654     /* Setup the data */
   655     if (ms) {
   656         music->fading = MIX_FADING_IN;
   657     } else {
   658         music->fading = MIX_NO_FADING;
   659     }
   660     music->fade_step = 0;
   661     music->fade_steps = ms/ms_per_step;
   662 
   663     /* Play the puppy */
   664     Mix_LockAudio();
   665     /* If the current music is fading out, wait for the fade to complete */
   666     while (music_playing && (music_playing->fading == MIX_FADING_OUT)) {
   667         Mix_UnlockAudio();
   668         SDL_Delay(100);
   669         Mix_LockAudio();
   670     }
   671     music_active = 1;
   672     if (loops == 1) {
   673         /* Loop is the number of times to play the audio */
   674         loops = 0;
   675     }
   676     music_loops = loops;
   677     retval = music_internal_play(music, position);
   678     Mix_UnlockAudio();
   679 
   680     return(retval);
   681 }
   682 int Mix_FadeInMusic(Mix_Music *music, int loops, int ms)
   683 {
   684     return Mix_FadeInMusicPos(music, loops, ms, 0.0);
   685 }
   686 int Mix_PlayMusic(Mix_Music *music, int loops)
   687 {
   688     return Mix_FadeInMusicPos(music, loops, 0, 0.0);
   689 }
   690 
   691 /* Set the playing music position */
   692 int music_internal_position(double position)
   693 {
   694     if (music_playing->interface->Seek) {
   695         return music_playing->interface->Seek(music_playing->context, position);
   696     }
   697     return -1;
   698 }
   699 int Mix_SetMusicPosition(double position)
   700 {
   701     int retval;
   702 
   703     Mix_LockAudio();
   704     if (music_playing) {
   705         retval = music_internal_position(position);
   706         if (retval < 0) {
   707             Mix_SetError("Position not implemented for music type");
   708         }
   709     } else {
   710         Mix_SetError("Music isn't playing");
   711         retval = -1;
   712     }
   713     Mix_UnlockAudio();
   714 
   715     return(retval);
   716 }
   717 
   718 /* Set the music's initial volume */
   719 static void music_internal_initialize_volume(void)
   720 {
   721     if (music_playing->fading == MIX_FADING_IN) {
   722         music_internal_volume(0);
   723     } else {
   724         music_internal_volume(music_volume);
   725     }
   726 }
   727 
   728 /* Set the music volume */
   729 static void music_internal_volume(int volume)
   730 {
   731     if (music_playing->interface->SetVolume) {
   732         music_playing->interface->SetVolume(music_playing->context, volume);
   733     }
   734 }
   735 int Mix_VolumeMusic(int volume)
   736 {
   737     int prev_volume;
   738 
   739     prev_volume = music_volume;
   740     if (volume < 0) {
   741         return prev_volume;
   742     }
   743     if (volume > SDL_MIX_MAXVOLUME) {
   744         volume = SDL_MIX_MAXVOLUME;
   745     }
   746     music_volume = volume;
   747     Mix_LockAudio();
   748     if (music_playing) {
   749         music_internal_volume(music_volume);
   750     }
   751     Mix_UnlockAudio();
   752     return(prev_volume);
   753 }
   754 
   755 /* Halt playing of music */
   756 static void music_internal_halt(void)
   757 {
   758     if (music_playing->interface->Stop) {
   759         music_playing->interface->Stop(music_playing->context);
   760     }
   761 
   762     music_playing->playing = SDL_FALSE;
   763     music_playing->fading = MIX_NO_FADING;
   764     music_playing = NULL;
   765 }
   766 int Mix_HaltMusic(void)
   767 {
   768     Mix_LockAudio();
   769     if (music_playing) {
   770         music_internal_halt();
   771         if (music_finished_hook) {
   772             music_finished_hook();
   773         }
   774     }
   775     Mix_UnlockAudio();
   776 
   777     return(0);
   778 }
   779 
   780 /* Progressively stop the music */
   781 int Mix_FadeOutMusic(int ms)
   782 {
   783     int retval = 0;
   784 
   785     if (ms_per_step == 0) {
   786         SDL_SetError("Audio device hasn't been opened");
   787         return 0;
   788     }
   789 
   790     if (ms <= 0) {  /* just halt immediately. */
   791         Mix_HaltMusic();
   792         return 1;
   793     }
   794 
   795     Mix_LockAudio();
   796     if (music_playing) {
   797         int fade_steps = (ms + ms_per_step - 1) / ms_per_step;
   798         if (music_playing->fading == MIX_NO_FADING) {
   799             music_playing->fade_step = 0;
   800         } else {
   801             int step;
   802             int old_fade_steps = music_playing->fade_steps;
   803             if (music_playing->fading == MIX_FADING_OUT) {
   804                 step = music_playing->fade_step;
   805             } else {
   806                 step = old_fade_steps - music_playing->fade_step + 1;
   807             }
   808             music_playing->fade_step = (step * fade_steps) / old_fade_steps;
   809         }
   810         music_playing->fading = MIX_FADING_OUT;
   811         music_playing->fade_steps = fade_steps;
   812         retval = 1;
   813     }
   814     Mix_UnlockAudio();
   815 
   816     return(retval);
   817 }
   818 
   819 Mix_Fading Mix_FadingMusic(void)
   820 {
   821     Mix_Fading fading = MIX_NO_FADING;
   822 
   823     Mix_LockAudio();
   824     if (music_playing) {
   825         fading = music_playing->fading;
   826     }
   827     Mix_UnlockAudio();
   828 
   829     return(fading);
   830 }
   831 
   832 /* Pause/Resume the music stream */
   833 void Mix_PauseMusic(void)
   834 {
   835     Mix_LockAudio();
   836     if (music_playing && music_playing->interface->Pause) {
   837         music_playing->interface->Pause(music_playing->context);
   838     }
   839     music_active = 0;
   840     Mix_UnlockAudio();
   841 }
   842 
   843 void Mix_ResumeMusic(void)
   844 {
   845     Mix_LockAudio();
   846     music_active = 1;
   847     if (music_playing && music_playing->interface->Resume) {
   848         music_playing->interface->Resume(music_playing->context);
   849     }
   850     Mix_UnlockAudio();
   851 }
   852 
   853 void Mix_RewindMusic(void)
   854 {
   855     Mix_SetMusicPosition(0.0);
   856 }
   857 
   858 int Mix_PausedMusic(void)
   859 {
   860     return (music_active == 0);
   861 }
   862 
   863 /* Check the status of the music */
   864 static SDL_bool music_internal_playing(void)
   865 {
   866     if (music_playing == NULL) {
   867         return SDL_FALSE;
   868     }
   869 
   870     if (music_playing->interface->IsPlaying) {
   871         return music_playing->interface->IsPlaying(music_playing->context);
   872     }
   873     return music_playing->playing;
   874 }
   875 int Mix_PlayingMusic(void)
   876 {
   877     int playing = 0;
   878 
   879     Mix_LockAudio();
   880     if (music_playing) {
   881         playing = music_loops || music_internal_playing();
   882     }
   883     Mix_UnlockAudio();
   884 
   885     return(playing);
   886 }
   887 
   888 /* Set the external music playback command */
   889 int Mix_SetMusicCMD(const char *command)
   890 {
   891     Mix_HaltMusic();
   892     if (music_cmd) {
   893         SDL_free(music_cmd);
   894         music_cmd = NULL;
   895     }
   896     if (command) {
   897         size_t length = SDL_strlen(command) + 1;
   898         music_cmd = (char *)SDL_malloc(length);
   899         if (music_cmd == NULL) {
   900             return SDL_OutOfMemory();
   901         }
   902         SDL_memcpy(music_cmd, command, length);
   903     }
   904     return 0;
   905 }
   906 
   907 int Mix_SetSynchroValue(int i)
   908 {
   909     /* Not supported by any players at this time */
   910     return(-1);
   911 }
   912 
   913 int Mix_GetSynchroValue(void)
   914 {
   915     /* Not supported by any players at this time */
   916     return(-1);
   917 }
   918 
   919 
   920 /* Uninitialize the music interfaces */
   921 void close_music(void)
   922 {
   923     int i;
   924 
   925     Mix_HaltMusic();
   926 
   927     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   928         Mix_MusicInterface *interface = s_music_interfaces[i];
   929         if (!interface || !interface->opened) {
   930             continue;
   931         }
   932 
   933         if (interface->Close) {
   934             interface->Close();
   935         }
   936         interface->opened = SDL_FALSE;
   937     }
   938 
   939     /* rcg06042009 report available decoders at runtime. */
   940     SDL_free((void *)music_decoders);
   941     music_decoders = NULL;
   942     num_decoders = 0;
   943 
   944     ms_per_step = 0;
   945 }
   946 
   947 /* Unload the music interface libraries */
   948 void unload_music(void)
   949 {
   950     int i;
   951     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   952         Mix_MusicInterface *interface = s_music_interfaces[i];
   953         if (!interface || !interface->loaded) {
   954             continue;
   955         }
   956 
   957         if (interface->Unload) {
   958             interface->Unload();
   959         }
   960         interface->loaded = SDL_FALSE;
   961     }
   962 }
   963 
   964 int Mix_SetSoundFonts(const char *paths)
   965 {
   966     if (soundfont_paths) {
   967         SDL_free(soundfont_paths);
   968         soundfont_paths = NULL;
   969     }
   970 
   971     if (paths) {
   972         if (!(soundfont_paths = SDL_strdup(paths))) {
   973             Mix_SetError("Insufficient memory to set SoundFonts");
   974             return 0;
   975         }
   976     }
   977     return 1;
   978 }
   979 
   980 const char* Mix_GetSoundFonts(void)
   981 {
   982     if (!soundfont_paths || SDL_GetHintBoolean("SDL_FORCE_SOUNDFONTS", SDL_FALSE)) {
   983         return SDL_getenv("SDL_SOUNDFONTS");
   984     } else {
   985         return soundfont_paths;
   986     }
   987 }
   988 
   989 int Mix_EachSoundFont(int (*function)(const char*, void*), void *data)
   990 {
   991     char *context, *path, *paths;
   992     const char* cpaths = Mix_GetSoundFonts();
   993     int soundfonts_found = 0;
   994 
   995     if (!cpaths) {
   996         Mix_SetError("No SoundFonts have been requested");
   997         return 0;
   998     }
   999 
  1000     if (!(paths = SDL_strdup(cpaths))) {
  1001         Mix_SetError("Insufficient memory to iterate over SoundFonts");
  1002         return 0;
  1003     }
  1004 
  1005 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__)
  1006     for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) {
  1007 #elif defined(_WIN32)
  1008     for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) {
  1009 #else
  1010     for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) {
  1011 #endif
  1012         if (!function(path, data)) {
  1013             continue;
  1014         } else {
  1015             soundfonts_found++;
  1016         }
  1017     }
  1018 
  1019     SDL_free(paths);
  1020     if (soundfonts_found > 0)
  1021         return 1;
  1022     else
  1023         return 0;
  1024 }
  1025 
  1026 /* vi: set ts=4 sw=4 expandtab: */