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