mixer.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 04 Aug 2014 11:17:32 -0400
changeset 694 59ef72d7586d
parent 665 8b25e64c7ac7
child 696 9599bb0ff844
permissions -rw-r--r--
Removed a FIXME (this code actually does what the FIXME requested now).
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2013 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 
    22 /* $Id$ */
    23 
    24 #include <stdio.h>
    25 #include <stdlib.h>
    26 #include <string.h>
    27 
    28 #include "SDL_mutex.h"
    29 #include "SDL_endian.h"
    30 #include "SDL_timer.h"
    31 
    32 #include "SDL_mixer.h"
    33 #include "load_aiff.h"
    34 #include "load_voc.h"
    35 #include "load_mp3.h"
    36 #include "load_ogg.h"
    37 #include "load_flac.h"
    38 #include "dynamic_flac.h"
    39 #include "dynamic_fluidsynth.h"
    40 #include "dynamic_modplug.h"
    41 #include "dynamic_mod.h"
    42 #include "dynamic_mp3.h"
    43 #include "dynamic_ogg.h"
    44 
    45 #define __MIX_INTERNAL_EFFECT__
    46 #include "effects_internal.h"
    47 
    48 /* Magic numbers for various audio file formats */
    49 #define RIFF        0x46464952      /* "RIFF" */
    50 #define WAVE        0x45564157      /* "WAVE" */
    51 #define FORM        0x4d524f46      /* "FORM" */
    52 #define OGGS        0x5367674f      /* "OggS" */
    53 #define CREA        0x61657243      /* "Crea" */
    54 #define FLAC        0x43614C66      /* "fLaC" */
    55 
    56 static int audio_opened = 0;
    57 static SDL_AudioSpec mixer;
    58 
    59 typedef struct _Mix_effectinfo
    60 {
    61     Mix_EffectFunc_t callback;
    62     Mix_EffectDone_t done_callback;
    63     void *udata;
    64     struct _Mix_effectinfo *next;
    65 } effect_info;
    66 
    67 static struct _Mix_Channel {
    68     Mix_Chunk *chunk;
    69     int playing;
    70     int paused;
    71     Uint8 *samples;
    72     int volume;
    73     int looping;
    74     int tag;
    75     Uint32 expire;
    76     Uint32 start_time;
    77     Mix_Fading fading;
    78     int fade_volume;
    79     int fade_volume_reset;
    80     Uint32 fade_length;
    81     Uint32 ticks_fade;
    82     effect_info *effects;
    83 } *mix_channel = NULL;
    84 
    85 static effect_info *posteffects = NULL;
    86 
    87 static int num_channels;
    88 static int reserved_channels = 0;
    89 
    90 
    91 /* Support for hooking into the mixer callback system */
    92 static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;
    93 static void *mix_postmix_data = NULL;
    94 
    95 /* rcg07062001 callback to alert when channels are done playing. */
    96 static void (*channel_done_callback)(int channel) = NULL;
    97 
    98 /* Music function declarations */
    99 extern int open_music(SDL_AudioSpec *mixer);
   100 extern void close_music(void);
   101 
   102 /* Support for user defined music functions, plus the default one */
   103 extern int volatile music_active;
   104 extern void music_mixer(void *udata, Uint8 *stream, int len);
   105 static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
   106 static void *music_data = NULL;
   107 
   108 /* rcg06042009 report available decoders at runtime. */
   109 static const char **chunk_decoders = NULL;
   110 static int num_decoders = 0;
   111 
   112 /* Semicolon-separated SoundFont paths */
   113 #ifdef MID_MUSIC
   114 extern char* soundfont_paths;
   115 #endif
   116 
   117 int Mix_GetNumChunkDecoders(void)
   118 {
   119     return(num_decoders);
   120 }
   121 
   122 const char *Mix_GetChunkDecoder(int index)
   123 {
   124     if ((index < 0) || (index >= num_decoders)) {
   125         return NULL;
   126     }
   127     return(chunk_decoders[index]);
   128 }
   129 
   130 static void add_chunk_decoder(const char *decoder)
   131 {
   132     void *ptr = SDL_realloc((void *)chunk_decoders, (num_decoders + 1) * sizeof (const char *));
   133     if (ptr == NULL) {
   134         return;  /* oh well, go on without it. */
   135     }
   136     chunk_decoders = (const char **) ptr;
   137     chunk_decoders[num_decoders++] = decoder;
   138 }
   139 
   140 /* rcg06192001 get linked library's version. */
   141 const SDL_version *Mix_Linked_Version(void)
   142 {
   143     static SDL_version linked_version;
   144     SDL_MIXER_VERSION(&linked_version);
   145     return(&linked_version);
   146 }
   147 
   148 static int initialized = 0;
   149 
   150 int Mix_Init(int flags)
   151 {
   152     int result = 0;
   153 
   154     if (flags & MIX_INIT_FLUIDSYNTH) {
   155 #ifdef USE_FLUIDSYNTH_MIDI
   156         if ((initialized & MIX_INIT_FLUIDSYNTH) || Mix_InitFluidSynth() == 0) {
   157             result |= MIX_INIT_FLUIDSYNTH;
   158         }
   159 #else
   160         Mix_SetError("Mixer not built with FluidSynth support");
   161 #endif
   162     }
   163     if (flags & MIX_INIT_FLAC) {
   164 #ifdef FLAC_MUSIC
   165         if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) {
   166             result |= MIX_INIT_FLAC;
   167         }
   168 #else
   169         Mix_SetError("Mixer not built with FLAC support");
   170 #endif
   171     }
   172     if (flags & MIX_INIT_MODPLUG) {
   173 #ifdef MODPLUG_MUSIC
   174         if ((initialized & MIX_INIT_MODPLUG) || Mix_InitModPlug() == 0) {
   175             result |= MIX_INIT_MODPLUG;
   176         }
   177 #else
   178         Mix_SetError("Mixer not built with MOD modplug support");
   179 #endif
   180     }
   181     if (flags & MIX_INIT_MOD) {
   182 #ifdef MOD_MUSIC
   183         if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) {
   184             result |= MIX_INIT_MOD;
   185         }
   186 #else
   187         Mix_SetError("Mixer not built with MOD mikmod support");
   188 #endif
   189     }
   190     if (flags & MIX_INIT_MP3) {
   191 #ifdef MP3_MUSIC
   192         if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) {
   193             result |= MIX_INIT_MP3;
   194         }
   195 #else
   196         Mix_SetError("Mixer not built with MP3 support");
   197 #endif
   198     }
   199     if (flags & MIX_INIT_OGG) {
   200 #ifdef OGG_MUSIC
   201         if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) {
   202             result |= MIX_INIT_OGG;
   203         }
   204 #else
   205         Mix_SetError("Mixer not built with Ogg Vorbis support");
   206 #endif
   207     }
   208     initialized |= result;
   209 
   210     return (result);
   211 }
   212 
   213 void Mix_Quit()
   214 {
   215 #ifdef USE_FLUIDSYNTH_MIDI
   216     if (initialized & MIX_INIT_FLUIDSYNTH) {
   217         Mix_QuitFluidSynth();
   218     }
   219 #endif
   220 #ifdef FLAC_MUSIC
   221     if (initialized & MIX_INIT_FLAC) {
   222         Mix_QuitFLAC();
   223     }
   224 #endif
   225 #ifdef MODPLUG_MUSIC
   226     if (initialized & MIX_INIT_MODPLUG) {
   227         Mix_QuitModPlug();
   228     }
   229 #endif
   230 #ifdef MOD_MUSIC
   231     if (initialized & MIX_INIT_MOD) {
   232         Mix_QuitMOD();
   233     }
   234 #endif
   235 #ifdef MP3_MUSIC
   236     if (initialized & MIX_INIT_MP3) {
   237         Mix_QuitMP3();
   238     }
   239 #endif
   240 #ifdef OGG_MUSIC
   241     if (initialized & MIX_INIT_OGG) {
   242         Mix_QuitOgg();
   243     }
   244 #endif
   245 #ifdef MID_MUSIC
   246     if (soundfont_paths) {
   247         SDL_free(soundfont_paths);
   248         soundfont_paths = NULL;
   249     }
   250 #endif
   251     initialized = 0;
   252 }
   253 
   254 static int _Mix_remove_all_effects(int channel, effect_info **e);
   255 
   256 /*
   257  * rcg06122001 Cleanup effect callbacks.
   258  *  MAKE SURE SDL_LockAudio() is called before this (or you're in the
   259  *   audio callback).
   260  */
   261 static void _Mix_channel_done_playing(int channel)
   262 {
   263     if (channel_done_callback) {
   264         channel_done_callback(channel);
   265     }
   266 
   267     /*
   268      * Call internal function directly, to avoid locking audio from
   269      *   inside audio callback.
   270      */
   271     _Mix_remove_all_effects(channel, &mix_channel[channel].effects);
   272 }
   273 
   274 
   275 static void *Mix_DoEffects(int chan, void *snd, int len)
   276 {
   277     int posteffect = (chan == MIX_CHANNEL_POST);
   278     effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);
   279     void *buf = snd;
   280 
   281     if (e != NULL) {    /* are there any registered effects? */
   282         /* if this is the postmix, we can just overwrite the original. */
   283         if (!posteffect) {
   284             buf = SDL_malloc(len);
   285             if (buf == NULL) {
   286                 return(snd);
   287             }
   288             SDL_memcpy(buf, snd, len);
   289         }
   290 
   291         for (; e != NULL; e = e->next) {
   292             if (e->callback != NULL) {
   293                 e->callback(chan, buf, len, e->udata);
   294             }
   295         }
   296     }
   297 
   298     /* be sure to SDL_free() the return value if != snd ... */
   299     return(buf);
   300 }
   301 
   302 
   303 /* Mixing function */
   304 static void mix_channels(void *udata, Uint8 *stream, int len)
   305 {
   306     Uint8 *mix_input;
   307     int i, mixable, volume = SDL_MIX_MAXVOLUME;
   308     Uint32 sdl_ticks;
   309 
   310 #if SDL_VERSION_ATLEAST(1, 3, 0)
   311     /* Need to initialize the stream in SDL 1.3+ */
   312     SDL_memset(stream, mixer.silence, len);
   313 #endif
   314 
   315     /* Mix the music (must be done before the channels are added) */
   316     if ( music_active || (mix_music != music_mixer) ) {
   317         mix_music(music_data, stream, len);
   318     }
   319 
   320     /* Mix any playing channels... */
   321     sdl_ticks = SDL_GetTicks();
   322     for ( i=0; i<num_channels; ++i ) {
   323         if( ! mix_channel[i].paused ) {
   324             if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) {
   325                 /* Expiration delay for that channel is reached */
   326                 mix_channel[i].playing = 0;
   327                 mix_channel[i].looping = 0;
   328                 mix_channel[i].fading = MIX_NO_FADING;
   329                 mix_channel[i].expire = 0;
   330                 _Mix_channel_done_playing(i);
   331             } else if ( mix_channel[i].fading != MIX_NO_FADING ) {
   332                 Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;
   333                 if( ticks > mix_channel[i].fade_length ) {
   334                     Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */
   335                     if( mix_channel[i].fading == MIX_FADING_OUT ) {
   336                         mix_channel[i].playing = 0;
   337                         mix_channel[i].looping = 0;
   338                         mix_channel[i].expire = 0;
   339                         _Mix_channel_done_playing(i);
   340                     }
   341                     mix_channel[i].fading = MIX_NO_FADING;
   342                 } else {
   343                     if( mix_channel[i].fading == MIX_FADING_OUT ) {
   344                         Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))
   345                                    / mix_channel[i].fade_length );
   346                     } else {
   347                         Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length );
   348                     }
   349                 }
   350             }
   351             if ( mix_channel[i].playing > 0 ) {
   352                 int index = 0;
   353                 int remaining = len;
   354                 while (mix_channel[i].playing > 0 && index < len) {
   355                     remaining = len - index;
   356                     volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;
   357                     mixable = mix_channel[i].playing;
   358                     if ( mixable > remaining ) {
   359                         mixable = remaining;
   360                     }
   361 
   362                     mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
   363                     SDL_MixAudio(stream+index,mix_input,mixable,volume);
   364                     if (mix_input != mix_channel[i].samples)
   365                         SDL_free(mix_input);
   366 
   367                     mix_channel[i].samples += mixable;
   368                     mix_channel[i].playing -= mixable;
   369                     index += mixable;
   370 
   371                     /* rcg06072001 Alert app if channel is done playing. */
   372                     if (!mix_channel[i].playing && !mix_channel[i].looping) {
   373                         _Mix_channel_done_playing(i);
   374                     }
   375                 }
   376 
   377                 /* If looping the sample and we are at its end, make sure
   378                    we will still return a full buffer */
   379                 while ( mix_channel[i].looping && index < len ) {
   380                     int alen = mix_channel[i].chunk->alen;
   381                     remaining = len - index;
   382                     if (remaining > alen) {
   383                         remaining = alen;
   384                     }
   385 
   386                     mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
   387                     SDL_MixAudio(stream+index, mix_input, remaining, volume);
   388                     if (mix_input != mix_channel[i].chunk->abuf)
   389                         SDL_free(mix_input);
   390 
   391                     if (mix_channel[i].looping > 0) {
   392                         --mix_channel[i].looping;
   393                     }
   394                     mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
   395                     mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
   396                     index += remaining;
   397                 }
   398                 if ( ! mix_channel[i].playing && mix_channel[i].looping ) {
   399                     if (mix_channel[i].looping > 0) {
   400                         --mix_channel[i].looping;
   401                     }
   402                     mix_channel[i].samples = mix_channel[i].chunk->abuf;
   403                     mix_channel[i].playing = mix_channel[i].chunk->alen;
   404                 }
   405             }
   406         }
   407     }
   408 
   409     /* rcg06122001 run posteffects... */
   410     Mix_DoEffects(MIX_CHANNEL_POST, stream, len);
   411 
   412     if ( mix_postmix ) {
   413         mix_postmix(mix_postmix_data, stream, len);
   414     }
   415 }
   416 
   417 #if 0
   418 static void PrintFormat(char *title, SDL_AudioSpec *fmt)
   419 {
   420     printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF),
   421             (fmt->format&0x8000) ? "signed" : "unsigned",
   422             (fmt->channels > 2) ? "surround" :
   423             (fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
   424 }
   425 #endif
   426 
   427 
   428 /* Open the mixer with a certain desired audio format */
   429 int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize)
   430 {
   431     int i;
   432     SDL_AudioSpec desired;
   433 
   434     /* If the mixer is already opened, increment open count */
   435     if ( audio_opened ) {
   436         if ( format == mixer.format && nchannels == mixer.channels ) {
   437             ++audio_opened;
   438             return(0);
   439         }
   440         while ( audio_opened ) {
   441             Mix_CloseAudio();
   442         }
   443     }
   444 
   445     /* Set the desired format and frequency */
   446     desired.freq = frequency;
   447     desired.format = format;
   448     desired.channels = nchannels;
   449     desired.samples = chunksize;
   450     desired.callback = mix_channels;
   451     desired.userdata = NULL;
   452 
   453     /* Accept nearly any audio format */
   454     if ( SDL_OpenAudio(&desired, &mixer) < 0 ) {
   455         return(-1);
   456     }
   457 #if 0
   458     PrintFormat("Audio device", &mixer);
   459 #endif
   460 
   461     /* Initialize the music players */
   462     if ( open_music(&mixer) < 0 ) {
   463         SDL_CloseAudio();
   464         return(-1);
   465     }
   466 
   467     num_channels = MIX_CHANNELS;
   468     mix_channel = (struct _Mix_Channel *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel));
   469 
   470     /* Clear out the audio channels */
   471     for ( i=0; i<num_channels; ++i ) {
   472         mix_channel[i].chunk = NULL;
   473         mix_channel[i].playing = 0;
   474         mix_channel[i].looping = 0;
   475         mix_channel[i].volume = SDL_MIX_MAXVOLUME;
   476         mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
   477         mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
   478         mix_channel[i].fading = MIX_NO_FADING;
   479         mix_channel[i].tag = -1;
   480         mix_channel[i].expire = 0;
   481         mix_channel[i].effects = NULL;
   482         mix_channel[i].paused = 0;
   483     }
   484     Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
   485 
   486     _Mix_InitEffects();
   487 
   488     /* This list is (currently) decided at build time. */
   489     add_chunk_decoder("WAVE");
   490     add_chunk_decoder("AIFF");
   491     add_chunk_decoder("VOC");
   492 #ifdef OGG_MUSIC
   493     add_chunk_decoder("OGG");
   494 #endif
   495 #ifdef FLAC_MUSIC
   496     add_chunk_decoder("FLAC");
   497 #endif
   498 #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC)
   499     add_chunk_decoder("MP3");
   500 #endif
   501 
   502     audio_opened = 1;
   503     SDL_PauseAudio(0);
   504     return(0);
   505 }
   506 
   507 /* Dynamically change the number of channels managed by the mixer.
   508    If decreasing the number of channels, the upper channels are
   509    stopped.
   510  */
   511 int Mix_AllocateChannels(int numchans)
   512 {
   513     if ( numchans<0 || numchans==num_channels )
   514         return(num_channels);
   515 
   516     if ( numchans < num_channels ) {
   517         /* Stop the affected channels */
   518         int i;
   519         for(i=numchans; i < num_channels; i++) {
   520             Mix_UnregisterAllEffects(i);
   521             Mix_HaltChannel(i);
   522         }
   523     }
   524     SDL_LockAudio();
   525     mix_channel = (struct _Mix_Channel *) SDL_realloc(mix_channel, numchans * sizeof(struct _Mix_Channel));
   526     if ( numchans > num_channels ) {
   527         /* Initialize the new channels */
   528         int i;
   529         for(i=num_channels; i < numchans; i++) {
   530             mix_channel[i].chunk = NULL;
   531             mix_channel[i].playing = 0;
   532             mix_channel[i].looping = 0;
   533             mix_channel[i].volume = SDL_MIX_MAXVOLUME;
   534             mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
   535             mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
   536             mix_channel[i].fading = MIX_NO_FADING;
   537             mix_channel[i].tag = -1;
   538             mix_channel[i].expire = 0;
   539             mix_channel[i].effects = NULL;
   540             mix_channel[i].paused = 0;
   541         }
   542     }
   543     num_channels = numchans;
   544     SDL_UnlockAudio();
   545     return(num_channels);
   546 }
   547 
   548 /* Return the actual mixer parameters */
   549 int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels)
   550 {
   551     if ( audio_opened ) {
   552         if ( frequency ) {
   553             *frequency = mixer.freq;
   554         }
   555         if ( format ) {
   556             *format = mixer.format;
   557         }
   558         if ( channels ) {
   559             *channels = mixer.channels;
   560         }
   561     }
   562     return(audio_opened);
   563 }
   564 
   565 static int detect_mp3(Uint8 *magic)
   566 {
   567     if ( strncmp((char *)magic, "ID3", 3) == 0 ) {
   568         return 1;
   569     }
   570 
   571     /* Detection code lifted from SMPEG */
   572     if(((magic[0] & 0xff) != 0xff) || // No sync bits
   573        ((magic[1] & 0xf0) != 0xf0) || //
   574        ((magic[2] & 0xf0) == 0x00) || // Bitrate is 0
   575        ((magic[2] & 0xf0) == 0xf0) || // Bitrate is 15
   576        ((magic[2] & 0x0c) == 0x0c) || // Frequency is 3
   577        ((magic[1] & 0x06) == 0x00)) { // Layer is 4
   578         return(0);
   579     }
   580     return 1;
   581 }
   582 
   583 /* Load a wave file */
   584 Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
   585 {
   586     Uint32 magic;
   587     Mix_Chunk *chunk;
   588     SDL_AudioSpec wavespec, *loaded;
   589     SDL_AudioCVT wavecvt;
   590     int samplesize;
   591 
   592     /* rcg06012001 Make sure src is valid */
   593     if ( ! src ) {
   594         SDL_SetError("Mix_LoadWAV_RW with NULL src");
   595         return(NULL);
   596     }
   597 
   598     /* Make sure audio has been opened */
   599     if ( ! audio_opened ) {
   600         SDL_SetError("Audio device hasn't been opened");
   601         if ( freesrc ) {
   602             SDL_RWclose(src);
   603         }
   604         return(NULL);
   605     }
   606 
   607     /* Allocate the chunk memory */
   608     chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
   609     if ( chunk == NULL ) {
   610         SDL_SetError("Out of memory");
   611         if ( freesrc ) {
   612             SDL_RWclose(src);
   613         }
   614         return(NULL);
   615     }
   616 
   617     /* Find out what kind of audio file this is */
   618     magic = SDL_ReadLE32(src);
   619     /* Seek backwards for compatibility with older loaders */
   620     SDL_RWseek(src, -(int)sizeof(Uint32), RW_SEEK_CUR);
   621 
   622     switch (magic) {
   623         case WAVE:
   624         case RIFF:
   625             loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec,
   626                     (Uint8 **)&chunk->abuf, &chunk->alen);
   627             break;
   628         case FORM:
   629             loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec,
   630                     (Uint8 **)&chunk->abuf, &chunk->alen);
   631             break;
   632 #ifdef OGG_MUSIC
   633         case OGGS:
   634             loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec,
   635                     (Uint8 **)&chunk->abuf, &chunk->alen);
   636             break;
   637 #endif
   638 #ifdef FLAC_MUSIC
   639         case FLAC:
   640             loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec,
   641                     (Uint8 **)&chunk->abuf, &chunk->alen);
   642             break;
   643 #endif
   644         case CREA:
   645             loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec,
   646                     (Uint8 **)&chunk->abuf, &chunk->alen);
   647             break;
   648         default:
   649 #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC)
   650 			if (detect_mp3((Uint8*)&magic))
   651 			{
   652 				/* note: send a copy of the mixer spec */
   653 				wavespec = mixer;
   654 				loaded = Mix_LoadMP3_RW(src, freesrc, &wavespec,
   655 						(Uint8 **)&chunk->abuf, &chunk->alen);
   656 				break;
   657 			}
   658 #endif
   659             SDL_SetError("Unrecognized sound file type");
   660             if ( freesrc ) {
   661                 SDL_RWclose(src);
   662             }
   663             loaded = NULL;
   664             break;
   665     }
   666     if ( !loaded ) {
   667         /* The individual loaders have closed src if needed */
   668         SDL_free(chunk);
   669         return(NULL);
   670     }
   671 
   672 #if 0
   673     PrintFormat("Audio device", &mixer);
   674     PrintFormat("-- Wave file", &wavespec);
   675 #endif
   676 
   677     /* Build the audio converter and create conversion buffers */
   678     if ( wavespec.format != mixer.format ||
   679          wavespec.channels != mixer.channels ||
   680          wavespec.freq != mixer.freq ) {
   681         if ( SDL_BuildAudioCVT(&wavecvt,
   682                 wavespec.format, wavespec.channels, wavespec.freq,
   683                 mixer.format, mixer.channels, mixer.freq) < 0 ) {
   684             SDL_free(chunk->abuf);
   685             SDL_free(chunk);
   686             return(NULL);
   687         }
   688         samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;
   689         wavecvt.len = chunk->alen & ~(samplesize-1);
   690         wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult);
   691         if ( wavecvt.buf == NULL ) {
   692             SDL_SetError("Out of memory");
   693             SDL_free(chunk->abuf);
   694             SDL_free(chunk);
   695             return(NULL);
   696         }
   697         SDL_memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
   698         SDL_free(chunk->abuf);
   699 
   700         /* Run the audio converter */
   701         if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
   702             SDL_free(wavecvt.buf);
   703             SDL_free(chunk);
   704             return(NULL);
   705         }
   706 
   707         chunk->abuf = wavecvt.buf;
   708         chunk->alen = wavecvt.len_cvt;
   709     }
   710 
   711     chunk->allocated = 1;
   712     chunk->volume = MIX_MAX_VOLUME;
   713 
   714     return(chunk);
   715 }
   716 
   717 /* Load a wave file of the mixer format from a memory buffer */
   718 Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
   719 {
   720     Mix_Chunk *chunk;
   721     Uint8 magic[4];
   722 
   723     /* Make sure audio has been opened */
   724     if ( ! audio_opened ) {
   725         SDL_SetError("Audio device hasn't been opened");
   726         return(NULL);
   727     }
   728 
   729     /* Allocate the chunk memory */
   730     chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk));
   731     if ( chunk == NULL ) {
   732         SDL_SetError("Out of memory");
   733         return(NULL);
   734     }
   735 
   736     /* Essentially just skip to the audio data (no error checking - fast) */
   737     chunk->allocated = 0;
   738     mem += 12; /* WAV header */
   739     do {
   740         SDL_memcpy(magic, mem, 4);
   741         mem += 4;
   742         chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
   743         mem += 4;
   744         chunk->abuf = mem;
   745         mem += chunk->alen;
   746     } while ( memcmp(magic, "data", 4) != 0 );
   747     chunk->volume = MIX_MAX_VOLUME;
   748 
   749     return(chunk);
   750 }
   751 
   752 /* Load raw audio data of the mixer format from a memory buffer */
   753 Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
   754 {
   755     Mix_Chunk *chunk;
   756 
   757     /* Make sure audio has been opened */
   758     if ( ! audio_opened ) {
   759         SDL_SetError("Audio device hasn't been opened");
   760         return(NULL);
   761     }
   762 
   763     /* Allocate the chunk memory */
   764     chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
   765     if ( chunk == NULL ) {
   766         SDL_SetError("Out of memory");
   767         return(NULL);
   768     }
   769 
   770     /* Essentially just point at the audio data (no error checking - fast) */
   771     chunk->allocated = 0;
   772     chunk->alen = len;
   773     chunk->abuf = mem;
   774     chunk->volume = MIX_MAX_VOLUME;
   775 
   776     return(chunk);
   777 }
   778 
   779 /* Free an audio chunk previously loaded */
   780 void Mix_FreeChunk(Mix_Chunk *chunk)
   781 {
   782     int i;
   783 
   784     /* Caution -- if the chunk is playing, the mixer will crash */
   785     if ( chunk ) {
   786         /* Guarantee that this chunk isn't playing */
   787         SDL_LockAudio();
   788         if ( mix_channel ) {
   789             for ( i=0; i<num_channels; ++i ) {
   790                 if ( chunk == mix_channel[i].chunk ) {
   791                     mix_channel[i].playing = 0;
   792                     mix_channel[i].looping = 0;
   793                 }
   794             }
   795         }
   796         SDL_UnlockAudio();
   797         /* Actually free the chunk */
   798         if ( chunk->allocated ) {
   799             SDL_free(chunk->abuf);
   800         }
   801         SDL_free(chunk);
   802     }
   803 }
   804 
   805 /* Set a function that is called after all mixing is performed.
   806    This can be used to provide real-time visual display of the audio stream
   807    or add a custom mixer filter for the stream data.
   808 */
   809 void Mix_SetPostMix(void (*mix_func)
   810                     (void *udata, Uint8 *stream, int len), void *arg)
   811 {
   812     SDL_LockAudio();
   813     mix_postmix_data = arg;
   814     mix_postmix = mix_func;
   815     SDL_UnlockAudio();
   816 }
   817 
   818 /* Add your own music player or mixer function.
   819    If 'mix_func' is NULL, the default music player is re-enabled.
   820  */
   821 void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len),
   822                                                                 void *arg)
   823 {
   824     SDL_LockAudio();
   825     if ( mix_func != NULL ) {
   826         music_data = arg;
   827         mix_music = mix_func;
   828     } else {
   829         music_data = NULL;
   830         mix_music = music_mixer;
   831     }
   832     SDL_UnlockAudio();
   833 }
   834 
   835 void *Mix_GetMusicHookData(void)
   836 {
   837     return(music_data);
   838 }
   839 
   840 void Mix_ChannelFinished(void (*channel_finished)(int channel))
   841 {
   842     SDL_LockAudio();
   843     channel_done_callback = channel_finished;
   844     SDL_UnlockAudio();
   845 }
   846 
   847 
   848 /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
   849    them dynamically to the next sample if requested with a -1 value below.
   850    Returns the number of reserved channels.
   851  */
   852 int Mix_ReserveChannels(int num)
   853 {
   854     if (num > num_channels)
   855         num = num_channels;
   856     reserved_channels = num;
   857     return num;
   858 }
   859 
   860 static int checkchunkintegral(Mix_Chunk *chunk)
   861 {
   862     int frame_width = 1;
   863 
   864     if ((mixer.format & 0xFF) == 16) frame_width = 2;
   865     frame_width *= mixer.channels;
   866     while (chunk->alen % frame_width) chunk->alen--;
   867     return chunk->alen;
   868 }
   869 
   870 /* Play an audio chunk on a specific channel.
   871    If the specified channel is -1, play on the first free channel.
   872    'ticks' is the number of milliseconds at most to play the sample, or -1
   873    if there is no limit.
   874    Returns which channel was used to play the sound.
   875 */
   876 int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
   877 {
   878     int i;
   879 
   880     /* Don't play null pointers :-) */
   881     if ( chunk == NULL ) {
   882         Mix_SetError("Tried to play a NULL chunk");
   883         return(-1);
   884     }
   885     if ( !checkchunkintegral(chunk)) {
   886         Mix_SetError("Tried to play a chunk with a bad frame");
   887         return(-1);
   888     }
   889 
   890     /* Lock the mixer while modifying the playing channels */
   891     SDL_LockAudio();
   892     {
   893         /* If which is -1, play on the first free channel */
   894         if ( which == -1 ) {
   895             for ( i=reserved_channels; i<num_channels; ++i ) {
   896                 if ( mix_channel[i].playing <= 0 )
   897                     break;
   898             }
   899             if ( i == num_channels ) {
   900                 Mix_SetError("No free channels available");
   901                 which = -1;
   902             } else {
   903                 which = i;
   904             }
   905         }
   906 
   907         /* Queue up the audio data for this channel */
   908         if ( which >= 0 && which < num_channels ) {
   909             Uint32 sdl_ticks = SDL_GetTicks();
   910             if (Mix_Playing(which))
   911                 _Mix_channel_done_playing(which);
   912             mix_channel[which].samples = chunk->abuf;
   913             mix_channel[which].playing = chunk->alen;
   914             mix_channel[which].looping = loops;
   915             mix_channel[which].chunk = chunk;
   916             mix_channel[which].paused = 0;
   917             mix_channel[which].fading = MIX_NO_FADING;
   918             mix_channel[which].start_time = sdl_ticks;
   919             mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0;
   920         }
   921     }
   922     SDL_UnlockAudio();
   923 
   924     /* Return the channel on which the sound is being played */
   925     return(which);
   926 }
   927 
   928 /* Change the expiration delay for a channel */
   929 int Mix_ExpireChannel(int which, int ticks)
   930 {
   931     int status = 0;
   932 
   933     if ( which == -1 ) {
   934         int i;
   935         for ( i=0; i < num_channels; ++ i ) {
   936             status += Mix_ExpireChannel(i, ticks);
   937         }
   938     } else if ( which < num_channels ) {
   939         SDL_LockAudio();
   940         mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0;
   941         SDL_UnlockAudio();
   942         ++ status;
   943     }
   944     return(status);
   945 }
   946 
   947 /* Fade in a sound on a channel, over ms milliseconds */
   948 int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks)
   949 {
   950     int i;
   951 
   952     /* Don't play null pointers :-) */
   953     if ( chunk == NULL ) {
   954         return(-1);
   955     }
   956     if ( !checkchunkintegral(chunk)) {
   957         Mix_SetError("Tried to play a chunk with a bad frame");
   958         return(-1);
   959     }
   960 
   961     /* Lock the mixer while modifying the playing channels */
   962     SDL_LockAudio();
   963     {
   964         /* If which is -1, play on the first free channel */
   965         if ( which == -1 ) {
   966             for ( i=reserved_channels; i<num_channels; ++i ) {
   967                 if ( mix_channel[i].playing <= 0 )
   968                     break;
   969             }
   970             if ( i == num_channels ) {
   971                 which = -1;
   972             } else {
   973                 which = i;
   974             }
   975         }
   976 
   977         /* Queue up the audio data for this channel */
   978         if ( which >= 0 && which < num_channels ) {
   979             Uint32 sdl_ticks = SDL_GetTicks();
   980             if (Mix_Playing(which))
   981                 _Mix_channel_done_playing(which);
   982             mix_channel[which].samples = chunk->abuf;
   983             mix_channel[which].playing = chunk->alen;
   984             mix_channel[which].looping = loops;
   985             mix_channel[which].chunk = chunk;
   986             mix_channel[which].paused = 0;
   987             mix_channel[which].fading = MIX_FADING_IN;
   988             mix_channel[which].fade_volume = mix_channel[which].volume;
   989             mix_channel[which].fade_volume_reset = mix_channel[which].volume;
   990             mix_channel[which].volume = 0;
   991             mix_channel[which].fade_length = (Uint32)ms;
   992             mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks;
   993             mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0;
   994         }
   995     }
   996     SDL_UnlockAudio();
   997 
   998     /* Return the channel on which the sound is being played */
   999     return(which);
  1000 }
  1001 
  1002 /* Set volume of a particular channel */
  1003 int Mix_Volume(int which, int volume)
  1004 {
  1005     int i;
  1006     int prev_volume = 0;
  1007 
  1008     if ( which == -1 ) {
  1009         for ( i=0; i<num_channels; ++i ) {
  1010             prev_volume += Mix_Volume(i, volume);
  1011         }
  1012         prev_volume /= num_channels;
  1013     } else if ( which < num_channels ) {
  1014         prev_volume = mix_channel[which].volume;
  1015         if ( volume >= 0 ) {
  1016             if ( volume > SDL_MIX_MAXVOLUME ) {
  1017                 volume = SDL_MIX_MAXVOLUME;
  1018             }
  1019             mix_channel[which].volume = volume;
  1020         }
  1021     }
  1022     return(prev_volume);
  1023 }
  1024 /* Set volume of a particular chunk */
  1025 int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
  1026 {
  1027     int prev_volume;
  1028 
  1029     prev_volume = chunk->volume;
  1030     if ( volume >= 0 ) {
  1031         if ( volume > MIX_MAX_VOLUME ) {
  1032             volume = MIX_MAX_VOLUME;
  1033         }
  1034         chunk->volume = volume;
  1035     }
  1036     return(prev_volume);
  1037 }
  1038 
  1039 /* Halt playing of a particular channel */
  1040 int Mix_HaltChannel(int which)
  1041 {
  1042     int i;
  1043 
  1044     if ( which == -1 ) {
  1045         for ( i=0; i<num_channels; ++i ) {
  1046             Mix_HaltChannel(i);
  1047         }
  1048     } else if ( which < num_channels ) {
  1049         SDL_LockAudio();
  1050         if (mix_channel[which].playing) {
  1051             _Mix_channel_done_playing(which);
  1052             mix_channel[which].playing = 0;
  1053             mix_channel[which].looping = 0;
  1054         }
  1055         mix_channel[which].expire = 0;
  1056         if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
  1057             mix_channel[which].volume = mix_channel[which].fade_volume_reset;
  1058         mix_channel[which].fading = MIX_NO_FADING;
  1059         SDL_UnlockAudio();
  1060     }
  1061     return(0);
  1062 }
  1063 
  1064 /* Halt playing of a particular group of channels */
  1065 int Mix_HaltGroup(int tag)
  1066 {
  1067     int i;
  1068 
  1069     for ( i=0; i<num_channels; ++i ) {
  1070         if( mix_channel[i].tag == tag ) {
  1071             Mix_HaltChannel(i);
  1072         }
  1073     }
  1074     return(0);
  1075 }
  1076 
  1077 /* Fade out a channel and then stop it automatically */
  1078 int Mix_FadeOutChannel(int which, int ms)
  1079 {
  1080     int status;
  1081 
  1082     status = 0;
  1083     if ( audio_opened ) {
  1084         if ( which == -1 ) {
  1085             int i;
  1086 
  1087             for ( i=0; i<num_channels; ++i ) {
  1088                 status += Mix_FadeOutChannel(i, ms);
  1089             }
  1090         } else if ( which < num_channels ) {
  1091             SDL_LockAudio();
  1092             if ( mix_channel[which].playing &&
  1093                 (mix_channel[which].volume > 0) &&
  1094                 (mix_channel[which].fading != MIX_FADING_OUT) ) {
  1095                 mix_channel[which].fade_volume = mix_channel[which].volume;
  1096                 mix_channel[which].fading = MIX_FADING_OUT;
  1097                 mix_channel[which].fade_length = ms;
  1098                 mix_channel[which].ticks_fade = SDL_GetTicks();
  1099 
  1100                 /* only change fade_volume_reset if we're not fading. */
  1101                 if (mix_channel[which].fading == MIX_NO_FADING) {
  1102                     mix_channel[which].fade_volume_reset = mix_channel[which].volume;
  1103                 }
  1104                 ++status;
  1105             }
  1106             SDL_UnlockAudio();
  1107         }
  1108     }
  1109     return(status);
  1110 }
  1111 
  1112 /* Halt playing of a particular group of channels */
  1113 int Mix_FadeOutGroup(int tag, int ms)
  1114 {
  1115     int i;
  1116     int status = 0;
  1117     for ( i=0; i<num_channels; ++i ) {
  1118         if( mix_channel[i].tag == tag ) {
  1119             status += Mix_FadeOutChannel(i,ms);
  1120         }
  1121     }
  1122     return(status);
  1123 }
  1124 
  1125 Mix_Fading Mix_FadingChannel(int which)
  1126 {
  1127     if ( which < 0 || which >= num_channels ) {
  1128         return MIX_NO_FADING;
  1129     }
  1130     return mix_channel[which].fading;
  1131 }
  1132 
  1133 /* Check the status of a specific channel.
  1134    If the specified mix_channel is -1, check all mix channels.
  1135 */
  1136 int Mix_Playing(int which)
  1137 {
  1138     int status;
  1139 
  1140     status = 0;
  1141     if ( which == -1 ) {
  1142         int i;
  1143 
  1144         for ( i=0; i<num_channels; ++i ) {
  1145             if ((mix_channel[i].playing > 0) ||
  1146                 mix_channel[i].looping)
  1147             {
  1148                 ++status;
  1149             }
  1150         }
  1151     } else if ( which < num_channels ) {
  1152         if ( (mix_channel[which].playing > 0) ||
  1153              mix_channel[which].looping )
  1154         {
  1155             ++status;
  1156         }
  1157     }
  1158     return(status);
  1159 }
  1160 
  1161 /* rcg06072001 Get the chunk associated with a channel. */
  1162 Mix_Chunk *Mix_GetChunk(int channel)
  1163 {
  1164     Mix_Chunk *retval = NULL;
  1165 
  1166     if ((channel >= 0) && (channel < num_channels)) {
  1167         retval = mix_channel[channel].chunk;
  1168     }
  1169 
  1170     return(retval);
  1171 }
  1172 
  1173 /* Close the mixer, halting all playing audio */
  1174 void Mix_CloseAudio(void)
  1175 {
  1176     int i;
  1177 
  1178     if ( audio_opened ) {
  1179         if ( audio_opened == 1 ) {
  1180             for (i = 0; i < num_channels; i++) {
  1181                 Mix_UnregisterAllEffects(i);
  1182             }
  1183             Mix_UnregisterAllEffects(MIX_CHANNEL_POST);
  1184             close_music();
  1185             Mix_HaltChannel(-1);
  1186             _Mix_DeinitEffects();
  1187             SDL_CloseAudio();
  1188             SDL_free(mix_channel);
  1189             mix_channel = NULL;
  1190 
  1191             /* rcg06042009 report available decoders at runtime. */
  1192             SDL_free((void *)chunk_decoders);
  1193             chunk_decoders = NULL;
  1194             num_decoders = 0;
  1195         }
  1196         --audio_opened;
  1197     }
  1198 }
  1199 
  1200 /* Pause a particular channel (or all) */
  1201 void Mix_Pause(int which)
  1202 {
  1203     Uint32 sdl_ticks = SDL_GetTicks();
  1204     if ( which == -1 ) {
  1205         int i;
  1206 
  1207         for ( i=0; i<num_channels; ++i ) {
  1208             if ( mix_channel[i].playing > 0 ) {
  1209                 mix_channel[i].paused = sdl_ticks;
  1210             }
  1211         }
  1212     } else if ( which < num_channels ) {
  1213         if ( mix_channel[which].playing > 0 ) {
  1214             mix_channel[which].paused = sdl_ticks;
  1215         }
  1216     }
  1217 }
  1218 
  1219 /* Resume a paused channel */
  1220 void Mix_Resume(int which)
  1221 {
  1222     Uint32 sdl_ticks = SDL_GetTicks();
  1223 
  1224     SDL_LockAudio();
  1225     if ( which == -1 ) {
  1226         int i;
  1227 
  1228         for ( i=0; i<num_channels; ++i ) {
  1229             if ( mix_channel[i].playing > 0 ) {
  1230                 if(mix_channel[i].expire > 0)
  1231                     mix_channel[i].expire += sdl_ticks - mix_channel[i].paused;
  1232                 mix_channel[i].paused = 0;
  1233             }
  1234         }
  1235     } else if ( which < num_channels ) {
  1236         if ( mix_channel[which].playing > 0 ) {
  1237             if(mix_channel[which].expire > 0)
  1238                 mix_channel[which].expire += sdl_ticks - mix_channel[which].paused;
  1239             mix_channel[which].paused = 0;
  1240         }
  1241     }
  1242     SDL_UnlockAudio();
  1243 }
  1244 
  1245 int Mix_Paused(int which)
  1246 {
  1247     if ( which < 0 ) {
  1248         int status = 0;
  1249         int i;
  1250         for( i=0; i < num_channels; ++i ) {
  1251             if ( mix_channel[i].paused ) {
  1252                 ++ status;
  1253             }
  1254         }
  1255         return(status);
  1256     } else if ( which < num_channels ) {
  1257         return(mix_channel[which].paused != 0);
  1258     } else {
  1259         return(0);
  1260     }
  1261 }
  1262 
  1263 /* Change the group of a channel */
  1264 int Mix_GroupChannel(int which, int tag)
  1265 {
  1266     if ( which < 0 || which > num_channels )
  1267         return(0);
  1268 
  1269     SDL_LockAudio();
  1270     mix_channel[which].tag = tag;
  1271     SDL_UnlockAudio();
  1272     return(1);
  1273 }
  1274 
  1275 /* Assign several consecutive channels to a group */
  1276 int Mix_GroupChannels(int from, int to, int tag)
  1277 {
  1278     int status = 0;
  1279     for( ; from <= to; ++ from ) {
  1280         status += Mix_GroupChannel(from, tag);
  1281     }
  1282     return(status);
  1283 }
  1284 
  1285 /* Finds the first available channel in a group of channels */
  1286 int Mix_GroupAvailable(int tag)
  1287 {
  1288     int i;
  1289     for( i=0; i < num_channels; i ++ ) {
  1290         if ( ((tag == -1) || (tag == mix_channel[i].tag)) &&
  1291                             (mix_channel[i].playing <= 0) )
  1292             return i;
  1293     }
  1294     return(-1);
  1295 }
  1296 
  1297 int Mix_GroupCount(int tag)
  1298 {
  1299     int count = 0;
  1300     int i;
  1301     for( i=0; i < num_channels; i ++ ) {
  1302         if ( mix_channel[i].tag==tag || tag==-1 )
  1303             ++ count;
  1304     }
  1305     return(count);
  1306 }
  1307 
  1308 /* Finds the "oldest" sample playing in a group of channels */
  1309 int Mix_GroupOldest(int tag)
  1310 {
  1311     int chan = -1;
  1312     Uint32 mintime = SDL_GetTicks();
  1313     int i;
  1314     for( i=0; i < num_channels; i ++ ) {
  1315         if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1316              && mix_channel[i].start_time <= mintime ) {
  1317             mintime = mix_channel[i].start_time;
  1318             chan = i;
  1319         }
  1320     }
  1321     return(chan);
  1322 }
  1323 
  1324 /* Finds the "most recent" (i.e. last) sample playing in a group of channels */
  1325 int Mix_GroupNewer(int tag)
  1326 {
  1327     int chan = -1;
  1328     Uint32 maxtime = 0;
  1329     int i;
  1330     for( i=0; i < num_channels; i ++ ) {
  1331         if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1332              && mix_channel[i].start_time >= maxtime ) {
  1333             maxtime = mix_channel[i].start_time;
  1334             chan = i;
  1335         }
  1336     }
  1337     return(chan);
  1338 }
  1339 
  1340 
  1341 
  1342 /*
  1343  * rcg06122001 The special effects exportable API.
  1344  *  Please see effect_*.c for internally-implemented effects, such
  1345  *  as Mix_SetPanning().
  1346  */
  1347 
  1348 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1349 static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f,
  1350                 Mix_EffectDone_t d, void *arg)
  1351 {
  1352     effect_info *new_e;
  1353 
  1354     if (!e) {
  1355         Mix_SetError("Internal error");
  1356         return(0);
  1357     }
  1358 
  1359     if (f == NULL) {
  1360         Mix_SetError("NULL effect callback");
  1361         return(0);
  1362     }
  1363 
  1364     new_e = SDL_malloc(sizeof (effect_info));
  1365     if (new_e == NULL) {
  1366         Mix_SetError("Out of memory");
  1367         return(0);
  1368     }
  1369 
  1370     new_e->callback = f;
  1371     new_e->done_callback = d;
  1372     new_e->udata = arg;
  1373     new_e->next = NULL;
  1374 
  1375     /* add new effect to end of linked list... */
  1376     if (*e == NULL) {
  1377         *e = new_e;
  1378     } else {
  1379         effect_info *cur = *e;
  1380         while (1) {
  1381             if (cur->next == NULL) {
  1382                 cur->next = new_e;
  1383                 break;
  1384             }
  1385             cur = cur->next;
  1386         }
  1387     }
  1388 
  1389     return(1);
  1390 }
  1391 
  1392 
  1393 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1394 static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f)
  1395 {
  1396     effect_info *cur;
  1397     effect_info *prev = NULL;
  1398     effect_info *next = NULL;
  1399 
  1400     if (!e) {
  1401         Mix_SetError("Internal error");
  1402         return(0);
  1403     }
  1404 
  1405     for (cur = *e; cur != NULL; cur = cur->next) {
  1406         if (cur->callback == f) {
  1407             next = cur->next;
  1408             if (cur->done_callback != NULL) {
  1409                 cur->done_callback(channel, cur->udata);
  1410             }
  1411             SDL_free(cur);
  1412 
  1413             if (prev == NULL) {   /* removing first item of list? */
  1414                 *e = next;
  1415             } else {
  1416                 prev->next = next;
  1417             }
  1418             return(1);
  1419         }
  1420         prev = cur;
  1421     }
  1422 
  1423     Mix_SetError("No such effect registered");
  1424     return(0);
  1425 }
  1426 
  1427 
  1428 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1429 static int _Mix_remove_all_effects(int channel, effect_info **e)
  1430 {
  1431     effect_info *cur;
  1432     effect_info *next;
  1433 
  1434     if (!e) {
  1435         Mix_SetError("Internal error");
  1436         return(0);
  1437     }
  1438 
  1439     for (cur = *e; cur != NULL; cur = next) {
  1440         next = cur->next;
  1441         if (cur->done_callback != NULL) {
  1442             cur->done_callback(channel, cur->udata);
  1443         }
  1444         SDL_free(cur);
  1445     }
  1446     *e = NULL;
  1447 
  1448     return(1);
  1449 }
  1450 
  1451 
  1452 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1453 int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f,
  1454             Mix_EffectDone_t d, void *arg)
  1455 {
  1456     effect_info **e = NULL;
  1457 
  1458     if (channel == MIX_CHANNEL_POST) {
  1459         e = &posteffects;
  1460     } else {
  1461         if ((channel < 0) || (channel >= num_channels)) {
  1462             Mix_SetError("Invalid channel number");
  1463             return(0);
  1464         }
  1465         e = &mix_channel[channel].effects;
  1466     }
  1467 
  1468     return _Mix_register_effect(e, f, d, arg);
  1469 }
  1470 
  1471 int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f,
  1472             Mix_EffectDone_t d, void *arg)
  1473 {
  1474     int retval;
  1475     SDL_LockAudio();
  1476     retval = _Mix_RegisterEffect_locked(channel, f, d, arg);
  1477     SDL_UnlockAudio();
  1478     return retval;
  1479 }
  1480 
  1481 
  1482 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1483 int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f)
  1484 {
  1485     effect_info **e = NULL;
  1486 
  1487     if (channel == MIX_CHANNEL_POST) {
  1488         e = &posteffects;
  1489     } else {
  1490         if ((channel < 0) || (channel >= num_channels)) {
  1491             Mix_SetError("Invalid channel number");
  1492             return(0);
  1493         }
  1494         e = &mix_channel[channel].effects;
  1495     }
  1496 
  1497     return _Mix_remove_effect(channel, e, f);
  1498 }
  1499 
  1500 int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f)
  1501 {
  1502     int retval;
  1503     SDL_LockAudio();
  1504     retval = _Mix_UnregisterEffect_locked(channel, f);
  1505     SDL_UnlockAudio();
  1506     return(retval);
  1507 }
  1508 
  1509 /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1510 int _Mix_UnregisterAllEffects_locked(int channel)
  1511 {
  1512     effect_info **e = NULL;
  1513 
  1514     if (channel == MIX_CHANNEL_POST) {
  1515         e = &posteffects;
  1516     } else {
  1517         if ((channel < 0) || (channel >= num_channels)) {
  1518             Mix_SetError("Invalid channel number");
  1519             return(0);
  1520         }
  1521         e = &mix_channel[channel].effects;
  1522     }
  1523 
  1524     return _Mix_remove_all_effects(channel, e);
  1525 }
  1526 
  1527 int Mix_UnregisterAllEffects(int channel)
  1528 {
  1529     int retval;
  1530     SDL_LockAudio();
  1531     retval = _Mix_UnregisterAllEffects_locked(channel);
  1532     SDL_UnlockAudio();
  1533     return(retval);
  1534 }
  1535 
  1536 /* end of mixer.c ... */
  1537