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