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