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