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