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