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