music.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:54:52 -0700
changeset 779 a2b494c054d5
parent 777 92882ef2ab81
child 780 74df2aa47195
permissions -rw-r--r--
Updated Xcode project and fixed warnings
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include <string.h> /* for strtok() and strtok_s() */
    22 
    23 #include "SDL_hints.h"
    24 #include "SDL_timer.h"
    25 
    26 #include "SDL_mixer.h"
    27 #include "mixer.h"
    28 #include "music.h"
    29 
    30 #include "music_cmd.h"
    31 #include "music_wav.h"
    32 #include "music_mikmod.h"
    33 #include "music_modplug.h"
    34 #include "music_nativemidi.h"
    35 #include "music_fluidsynth.h"
    36 #include "music_timidity.h"
    37 #include "music_ogg.h"
    38 #include "music_mpg123.h"
    39 #include "music_mad.h"
    40 #include "music_smpeg.h"
    41 #include "music_flac.h"
    42 #include "native_midi/native_midi.h"
    43 
    44 
    45 char *music_cmd = NULL;
    46 int volatile music_active = 1;
    47 static Mix_Music * volatile music_playing = NULL;
    48 int music_loops = 0;
    49 int music_volume = MIX_MAX_VOLUME;
    50 SDL_AudioSpec music_spec;
    51 
    52 struct _Mix_Music {
    53     Mix_MusicInterface *interface;
    54     void *context;
    55 
    56     SDL_bool playing;
    57     Mix_Fading fading;
    58     int fade_step;
    59     int fade_steps;
    60     int error;
    61 };
    62 
    63 /* Used to calculate fading steps */
    64 static int ms_per_step;
    65 
    66 /* rcg06042009 report available decoders at runtime. */
    67 static const char **music_decoders = NULL;
    68 static int num_decoders = 0;
    69 
    70 /* Semicolon-separated SoundFont paths */
    71 char* soundfont_paths = NULL;
    72 
    73 /* Interfaces for the various music interfaces, ordered by priority */
    74 static Mix_MusicInterface *s_music_interfaces[] =
    75 {
    76 #ifdef MUSIC_CMD
    77     &Mix_MusicInterface_CMD,
    78 #endif
    79 #ifdef MUSIC_WAV
    80     &Mix_MusicInterface_WAV,
    81 #endif
    82 #ifdef MUSIC_MOD_MODPLUG
    83     &Mix_MusicInterface_MODPLUG,
    84 #endif
    85 #ifdef MUSIC_MOD_MIKMOD
    86     &Mix_MusicInterface_MIKMOD,
    87 #endif
    88 #ifdef MUSIC_MID_FLUIDSYNTH
    89     &Mix_MusicInterface_FLUIDSYNTH,
    90 #endif
    91 #ifdef MUSIC_MID_TIMIDITY
    92     &Mix_MusicInterface_TIMIDITY,
    93 #endif
    94 #ifdef MUSIC_MID_NATIVE
    95     &Mix_MusicInterface_NATIVEMIDI,
    96 #endif
    97 #ifdef MUSIC_OGG
    98     &Mix_MusicInterface_OGG,
    99 #endif
   100 #ifdef MUSIC_MP3_MPG123
   101     &Mix_MusicInterface_MPG123,
   102 #endif
   103 #ifdef MUSIC_MP3_MAD
   104     &Mix_MusicInterface_MAD,
   105 #endif
   106 #ifdef MUSIC_MP3_SMPEG
   107     &Mix_MusicInterface_SMPEG,
   108 #endif
   109 #ifdef MUSIC_FLAC
   110     &Mix_MusicInterface_FLAC,
   111 #endif
   112 };
   113 
   114 int get_num_music_interfaces(void)
   115 {
   116     return SDL_arraysize(s_music_interfaces);
   117 }
   118 
   119 Mix_MusicInterface *get_music_interface(int index)
   120 {
   121     return s_music_interfaces[index];
   122 }
   123 
   124 int Mix_GetNumMusicDecoders(void)
   125 {
   126     return(num_decoders);
   127 }
   128 
   129 const char *Mix_GetMusicDecoder(int index)
   130 {
   131     if ((index < 0) || (index >= num_decoders)) {
   132         return NULL;
   133     }
   134     return(music_decoders[index]);
   135 }
   136 
   137 static void add_music_decoder(const char *decoder)
   138 {
   139     void *ptr = SDL_realloc((void *)music_decoders, (num_decoders + 1) * sizeof (const char *));
   140     if (ptr == NULL) {
   141         return;  /* oh well, go on without it. */
   142     }
   143     music_decoders = (const char **) ptr;
   144     music_decoders[num_decoders++] = decoder;
   145 }
   146 
   147 /* Local low-level functions prototypes */
   148 static void music_internal_initialize_volume(void);
   149 static void music_internal_volume(int volume);
   150 static int  music_internal_play(Mix_Music *music, double position);
   151 static int  music_internal_position(double position);
   152 static SDL_bool music_internal_playing(void);
   153 static void music_internal_halt(void);
   154 
   155 
   156 /* Support for hooking when the music has finished */
   157 static void (*music_finished_hook)(void) = NULL;
   158 
   159 void Mix_HookMusicFinished(void (*music_finished)(void))
   160 {
   161     Mix_LockAudio();
   162     music_finished_hook = music_finished;
   163     Mix_UnlockAudio();
   164 }
   165 
   166 
   167 /* If music isn't playing, halt it if no looping is required, restart it */
   168 /* otherwise. NOP if the music is playing */
   169 static int music_halt_or_loop(void)
   170 {
   171     /* Restart music if it has to loop */
   172 
   173     if (!music_internal_playing())
   174     {
   175         /* Native MIDI handles looping internally */
   176         if (music_playing->interface->api == MIX_MUSIC_NATIVEMIDI) {
   177             music_loops = 0;
   178         }
   179 
   180         /* Restart music if it has to loop at a high level */
   181         if (music_loops)
   182         {
   183             Mix_Fading current_fade;
   184             if (music_loops > 0) {
   185                 --music_loops;
   186             }
   187             current_fade = music_playing->fading;
   188             music_internal_play(music_playing, 0.0);
   189             music_playing->fading = current_fade;
   190         }
   191         else
   192         {
   193             music_internal_halt();
   194             if (music_finished_hook)
   195                 music_finished_hook();
   196 
   197             return 0;
   198         }
   199     }
   200 
   201     return 1;
   202 }
   203 
   204 
   205 /* Mixing function */
   206 void music_mixer(void *udata, Uint8 *stream, int len)
   207 {
   208     if (music_playing && music_active) {
   209         /* Handle fading */
   210         if (music_playing->fading != MIX_NO_FADING) {
   211             if (music_playing->fade_step++ < music_playing->fade_steps) {
   212                 int volume;
   213                 int fade_step = music_playing->fade_step;
   214                 int fade_steps = music_playing->fade_steps;
   215 
   216                 if (music_playing->fading == MIX_FADING_OUT) {
   217                     volume = (music_volume * (fade_steps-fade_step)) / fade_steps;
   218                 } else { /* Fading in */
   219                     volume = (music_volume * fade_step) / fade_steps;
   220                 }
   221                 music_internal_volume(volume);
   222             } else {
   223                 if (music_playing->fading == MIX_FADING_OUT) {
   224                     music_internal_halt();
   225                     if (music_finished_hook) {
   226                         music_finished_hook();
   227                     }
   228                     return;
   229                 }
   230                 music_playing->fading = MIX_NO_FADING;
   231             }
   232         }
   233 
   234         music_halt_or_loop();
   235         if (!music_internal_playing()) {
   236             return;
   237         }
   238 
   239         if (music_playing->interface->GetAudio) {
   240             int left = music_playing->interface->GetAudio(music_playing->context, stream, len);
   241             if (left > 0) {
   242                 music_playing->playing = SDL_FALSE;
   243             }
   244 
   245             /* Handle seamless music looping */
   246             if (left > 0 && left < len) {
   247                 music_halt_or_loop();
   248                 if (music_internal_playing()) {
   249                     music_mixer(udata, stream+(len-left), left);
   250                 }
   251             }
   252         }
   253     }
   254 }
   255 
   256 /* Load the music interface libraries */
   257 int load_music(void)
   258 {
   259     char hint[128];
   260 
   261     int i;
   262     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   263         Mix_MusicInterface *interface = s_music_interfaces[i];
   264         if (interface->loaded) {
   265             continue;
   266         }
   267 
   268         SDL_snprintf(hint, sizeof(hint), "SDL_MIXER_DISABLE_%s", interface->tag);
   269         if (SDL_GetHintBoolean(hint, SDL_FALSE)) {
   270             continue;
   271         }
   272 
   273         if (!interface->Load || interface->Load() == 0) {
   274             interface->loaded = SDL_TRUE;
   275         }
   276     }
   277     return 0;
   278 }
   279 
   280 /* Return SDL_TRUE if the music type is available */
   281 SDL_bool has_music(Mix_MusicType type)
   282 {
   283     int i;
   284     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   285         Mix_MusicInterface *interface = s_music_interfaces[i];
   286         if (interface->type != type) {
   287             continue;
   288         }
   289         if (interface->opened) {
   290             return SDL_TRUE;
   291         }
   292     }
   293     return SDL_FALSE;
   294 }
   295 
   296 /* Initialize the music interfaces with a certain desired audio format */
   297 int open_music(const SDL_AudioSpec *spec)
   298 {
   299     int i;
   300     SDL_bool use_native_midi = SDL_FALSE;
   301 
   302     if (SDL_GetHintBoolean("SDL_NATIVE_MUSIC", SDL_FALSE) && native_midi_detect()) {
   303         use_native_midi = SDL_TRUE;
   304     }
   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         if (interface->api == MIX_MUSIC_SMPEG) {
   525             /* Uh oh, if SMPEG couldn't create a context, it freed the src */
   526             freesrc = SDL_FALSE;
   527             break;
   528         }
   529 
   530         /* Reset the stream for the next decoder */
   531         SDL_RWseek(src, start, RW_SEEK_SET);
   532     }
   533 
   534     if (!*Mix_GetError()) {
   535         Mix_SetError("Unrecognized audio format");
   536     }
   537     if (freesrc) {
   538         SDL_RWclose(src);
   539     } else {
   540         SDL_RWseek(src, start, RW_SEEK_SET);
   541     }
   542     return NULL;
   543 }
   544 
   545 /* Free a music chunk previously loaded */
   546 void Mix_FreeMusic(Mix_Music *music)
   547 {
   548     if (music) {
   549         /* Stop the music if it's currently playing */
   550         Mix_LockAudio();
   551         if (music == music_playing) {
   552             /* Wait for any fade out to finish */
   553             while (music->fading == MIX_FADING_OUT) {
   554                 Mix_UnlockAudio();
   555                 SDL_Delay(100);
   556                 Mix_LockAudio();
   557             }
   558             if (music == music_playing) {
   559                 music_internal_halt();
   560             }
   561         }
   562         Mix_UnlockAudio();
   563 
   564         music->interface->Delete(music->context);
   565         SDL_free(music);
   566     }
   567 }
   568 
   569 /* Find out the music format of a mixer music, or the currently playing
   570    music, if 'music' is NULL.
   571 */
   572 Mix_MusicType Mix_GetMusicType(const Mix_Music *music)
   573 {
   574     Mix_MusicType type = MUS_NONE;
   575 
   576     if (music) {
   577         type = music->interface->type;
   578     } else {
   579         Mix_LockAudio();
   580         if (music_playing) {
   581             type = music_playing->interface->type;
   582         }
   583         Mix_UnlockAudio();
   584     }
   585     return(type);
   586 }
   587 
   588 /* Play a music chunk.  Returns 0, or -1 if there was an error.
   589  */
   590 static int music_internal_play(Mix_Music *music, double position)
   591 {
   592     int retval = 0;
   593 
   594 #if defined(__MACOSX__) && defined(MID_MUSIC_NATIVE)
   595     /* This fixes a bug with native MIDI on Mac OS X, where you
   596        can't really stop and restart MIDI from the audio callback.
   597     */
   598     if (music == music_playing && music->api == MIX_MUSIC_NATIVEMIDI) {
   599         /* Just a seek suffices to restart playing */
   600         music_internal_position(position);
   601         return 0;
   602     }
   603 #endif
   604 
   605     /* Note the music we're playing */
   606     if (music_playing) {
   607         music_internal_halt();
   608     }
   609     music_playing = music;
   610     music_playing->playing = SDL_TRUE;
   611 
   612     /* Set the initial volume */
   613     music_internal_initialize_volume();
   614 
   615     /* Set up for playback */
   616     retval = music->interface->Play(music->context);
   617 
   618     /* Set the playback position, note any errors if an offset is used */
   619     if (retval == 0) {
   620         if (position > 0.0) {
   621             if (music_internal_position(position) < 0) {
   622                 Mix_SetError("Position not implemented for music type");
   623                 retval = -1;
   624             }
   625         } else {
   626             music_internal_position(0.0);
   627         }
   628     }
   629 
   630     /* If the setup failed, we're not playing any music anymore */
   631     if (retval < 0) {
   632         music_playing = NULL;
   633     }
   634     return(retval);
   635 }
   636 
   637 int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
   638 {
   639     int retval;
   640 
   641     if (ms_per_step == 0) {
   642         SDL_SetError("Audio device hasn't been opened");
   643         return(-1);
   644     }
   645 
   646     /* Don't play null pointers :-) */
   647     if (music == NULL) {
   648         Mix_SetError("music parameter was NULL");
   649         return(-1);
   650     }
   651 
   652     /* Setup the data */
   653     if (ms) {
   654         music->fading = MIX_FADING_IN;
   655     } else {
   656         music->fading = MIX_NO_FADING;
   657     }
   658     music->fade_step = 0;
   659     music->fade_steps = ms/ms_per_step;
   660 
   661     /* Play the puppy */
   662     Mix_LockAudio();
   663     /* If the current music is fading out, wait for the fade to complete */
   664     while (music_playing && (music_playing->fading == MIX_FADING_OUT)) {
   665         Mix_UnlockAudio();
   666         SDL_Delay(100);
   667         Mix_LockAudio();
   668     }
   669     music_active = 1;
   670     if (loops == 1) {
   671         /* Loop is the number of times to play the audio */
   672         loops = 0;
   673     }
   674     music_loops = loops;
   675     retval = music_internal_play(music, position);
   676     Mix_UnlockAudio();
   677 
   678     return(retval);
   679 }
   680 int Mix_FadeInMusic(Mix_Music *music, int loops, int ms)
   681 {
   682     return Mix_FadeInMusicPos(music, loops, ms, 0.0);
   683 }
   684 int Mix_PlayMusic(Mix_Music *music, int loops)
   685 {
   686     return Mix_FadeInMusicPos(music, loops, 0, 0.0);
   687 }
   688 
   689 /* Set the playing music position */
   690 int music_internal_position(double position)
   691 {
   692     if (music_playing->interface->Seek) {
   693         return music_playing->interface->Seek(music_playing->context, position);
   694     }
   695     return -1;
   696 }
   697 int Mix_SetMusicPosition(double position)
   698 {
   699     int retval;
   700 
   701     Mix_LockAudio();
   702     if (music_playing) {
   703         retval = music_internal_position(position);
   704         if (retval < 0) {
   705             Mix_SetError("Position not implemented for music type");
   706         }
   707     } else {
   708         Mix_SetError("Music isn't playing");
   709         retval = -1;
   710     }
   711     Mix_UnlockAudio();
   712 
   713     return(retval);
   714 }
   715 
   716 /* Set the music's initial volume */
   717 static void music_internal_initialize_volume(void)
   718 {
   719     if (music_playing->fading == MIX_FADING_IN) {
   720         music_internal_volume(0);
   721     } else {
   722         music_internal_volume(music_volume);
   723     }
   724 }
   725 
   726 /* Set the music volume */
   727 static void music_internal_volume(int volume)
   728 {
   729     if (music_playing->interface->SetVolume) {
   730         music_playing->interface->SetVolume(music_playing->context, volume);
   731     }
   732 }
   733 int Mix_VolumeMusic(int volume)
   734 {
   735     int prev_volume;
   736 
   737     prev_volume = music_volume;
   738     if (volume < 0) {
   739         return prev_volume;
   740     }
   741     if (volume > SDL_MIX_MAXVOLUME) {
   742         volume = SDL_MIX_MAXVOLUME;
   743     }
   744     music_volume = volume;
   745     Mix_LockAudio();
   746     if (music_playing) {
   747         music_internal_volume(music_volume);
   748     }
   749     Mix_UnlockAudio();
   750     return(prev_volume);
   751 }
   752 
   753 /* Halt playing of music */
   754 static void music_internal_halt(void)
   755 {
   756     if (music_playing->interface->Stop) {
   757         music_playing->interface->Stop(music_playing->context);
   758     }
   759 
   760     music_playing->playing = SDL_FALSE;
   761     music_playing->fading = MIX_NO_FADING;
   762     music_playing = NULL;
   763 }
   764 int Mix_HaltMusic(void)
   765 {
   766     Mix_LockAudio();
   767     if (music_playing) {
   768         music_internal_halt();
   769         if (music_finished_hook) {
   770             music_finished_hook();
   771         }
   772     }
   773     Mix_UnlockAudio();
   774 
   775     return(0);
   776 }
   777 
   778 /* Progressively stop the music */
   779 int Mix_FadeOutMusic(int ms)
   780 {
   781     int retval = 0;
   782 
   783     if (ms_per_step == 0) {
   784         SDL_SetError("Audio device hasn't been opened");
   785         return 0;
   786     }
   787 
   788     if (ms <= 0) {  /* just halt immediately. */
   789         Mix_HaltMusic();
   790         return 1;
   791     }
   792 
   793     Mix_LockAudio();
   794     if (music_playing) {
   795         int fade_steps = (ms + ms_per_step - 1) / ms_per_step;
   796         if (music_playing->fading == MIX_NO_FADING) {
   797             music_playing->fade_step = 0;
   798         } else {
   799             int step;
   800             int old_fade_steps = music_playing->fade_steps;
   801             if (music_playing->fading == MIX_FADING_OUT) {
   802                 step = music_playing->fade_step;
   803             } else {
   804                 step = old_fade_steps - music_playing->fade_step + 1;
   805             }
   806             music_playing->fade_step = (step * fade_steps) / old_fade_steps;
   807         }
   808         music_playing->fading = MIX_FADING_OUT;
   809         music_playing->fade_steps = fade_steps;
   810         retval = 1;
   811     }
   812     Mix_UnlockAudio();
   813 
   814     return(retval);
   815 }
   816 
   817 Mix_Fading Mix_FadingMusic(void)
   818 {
   819     Mix_Fading fading = MIX_NO_FADING;
   820 
   821     Mix_LockAudio();
   822     if (music_playing) {
   823         fading = music_playing->fading;
   824     }
   825     Mix_UnlockAudio();
   826 
   827     return(fading);
   828 }
   829 
   830 /* Pause/Resume the music stream */
   831 void Mix_PauseMusic(void)
   832 {
   833     Mix_LockAudio();
   834     if (music_playing && music_playing->interface->Pause) {
   835         music_playing->interface->Pause(music_playing->context);
   836     }
   837     music_active = 0;
   838     Mix_UnlockAudio();
   839 }
   840 
   841 void Mix_ResumeMusic(void)
   842 {
   843     Mix_LockAudio();
   844     music_active = 1;
   845     if (music_playing && music_playing->interface->Resume) {
   846         music_playing->interface->Resume(music_playing->context);
   847     }
   848     Mix_UnlockAudio();
   849 }
   850 
   851 void Mix_RewindMusic(void)
   852 {
   853     Mix_SetMusicPosition(0.0);
   854 }
   855 
   856 int Mix_PausedMusic(void)
   857 {
   858     return (music_active == 0);
   859 }
   860 
   861 /* Check the status of the music */
   862 static SDL_bool music_internal_playing(void)
   863 {
   864     if (music_playing == NULL) {
   865         return SDL_FALSE;
   866     }
   867 
   868     if (music_playing->interface->IsPlaying) {
   869         return music_playing->interface->IsPlaying(music_playing->context);
   870     }
   871     return music_playing->playing;
   872 }
   873 int Mix_PlayingMusic(void)
   874 {
   875     int playing = 0;
   876 
   877     Mix_LockAudio();
   878     if (music_playing) {
   879         playing = music_loops || music_internal_playing();
   880     }
   881     Mix_UnlockAudio();
   882 
   883     return(playing);
   884 }
   885 
   886 /* Set the external music playback command */
   887 int Mix_SetMusicCMD(const char *command)
   888 {
   889     Mix_HaltMusic();
   890     if (music_cmd) {
   891         SDL_free(music_cmd);
   892         music_cmd = NULL;
   893     }
   894     if (command) {
   895         size_t length = SDL_strlen(command) + 1;
   896         music_cmd = (char *)SDL_malloc(length);
   897         if (music_cmd == NULL) {
   898             return SDL_OutOfMemory();
   899         }
   900         SDL_memcpy(music_cmd, command, length);
   901     }
   902     return 0;
   903 }
   904 
   905 int Mix_SetSynchroValue(int i)
   906 {
   907     /* Not supported by any players at this time */
   908     return(-1);
   909 }
   910 
   911 int Mix_GetSynchroValue(void)
   912 {
   913     /* Not supported by any players at this time */
   914     return(-1);
   915 }
   916 
   917 
   918 /* Uninitialize the music interfaces */
   919 void close_music(void)
   920 {
   921     int i;
   922 
   923     Mix_HaltMusic();
   924 
   925     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   926         Mix_MusicInterface *interface = s_music_interfaces[i];
   927         if (!interface || !interface->opened) {
   928             continue;
   929         }
   930 
   931         if (interface->Close) {
   932             interface->Close();
   933         }
   934         interface->opened = SDL_FALSE;
   935     }
   936 
   937     /* rcg06042009 report available decoders at runtime. */
   938     SDL_free((void *)music_decoders);
   939     music_decoders = NULL;
   940     num_decoders = 0;
   941 
   942     ms_per_step = 0;
   943 }
   944 
   945 /* Unload the music interface libraries */
   946 void unload_music(void)
   947 {
   948     int i;
   949     for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
   950         Mix_MusicInterface *interface = s_music_interfaces[i];
   951         if (!interface || !interface->loaded) {
   952             continue;
   953         }
   954 
   955         if (interface->Unload) {
   956             interface->Unload();
   957         }
   958         interface->loaded = SDL_FALSE;
   959     }
   960 }
   961 
   962 int Mix_SetSoundFonts(const char *paths)
   963 {
   964     if (soundfont_paths) {
   965         SDL_free(soundfont_paths);
   966         soundfont_paths = NULL;
   967     }
   968 
   969     if (paths) {
   970         if (!(soundfont_paths = SDL_strdup(paths))) {
   971             Mix_SetError("Insufficient memory to set SoundFonts");
   972             return 0;
   973         }
   974     }
   975     return 1;
   976 }
   977 
   978 const char* Mix_GetSoundFonts(void)
   979 {
   980     if (!soundfont_paths || SDL_GetHintBoolean("SDL_FORCE_SOUNDFONTS", SDL_FALSE)) {
   981         return SDL_getenv("SDL_SOUNDFONTS");
   982     } else {
   983         return soundfont_paths;
   984     }
   985 }
   986 
   987 int Mix_EachSoundFont(int (*function)(const char*, void*), void *data)
   988 {
   989     char *context, *path, *paths;
   990     const char* cpaths = Mix_GetSoundFonts();
   991     int soundfonts_found = 0;
   992 
   993     if (!cpaths) {
   994         Mix_SetError("No SoundFonts have been requested");
   995         return 0;
   996     }
   997 
   998     if (!(paths = SDL_strdup(cpaths))) {
   999         Mix_SetError("Insufficient memory to iterate over SoundFonts");
  1000         return 0;
  1001     }
  1002 
  1003 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__)
  1004     for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) {
  1005 #elif defined(_WIN32)
  1006     for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) {
  1007 #else
  1008     for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) {
  1009 #endif
  1010         if (!function(path, data)) {
  1011             continue;
  1012         } else {
  1013             soundfonts_found++;
  1014         }
  1015     }
  1016 
  1017     SDL_free(paths);
  1018     if (soundfonts_found > 0)
  1019         return 1;
  1020     else
  1021         return 0;
  1022 }
  1023 
  1024 /* vi: set ts=4 sw=4 expandtab: */