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