mixer.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 07 Nov 2018 07:47:50 -0800
changeset 924 9be60a9582a6
parent 855 a7dee77dd60f
child 926 d6c9518fb5ee
permissions -rw-r--r--
The Debian maintainers aren't using these rules, so enable dynamic loading of shared libraries by default for the Steam Linux Runtime
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2018 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(Mix_MusicType music_type, SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   531 {
   532     int i;
   533     Mix_MusicInterface *interface = NULL;
   534     void *music = NULL;
   535     Sint64 start;
   536     SDL_bool playing;
   537     MusicFragment *first = NULL, *last = NULL, *fragment = NULL;
   538     int count = 0;
   539     int fragment_size;
   540 
   541     if (!load_music_type(music_type) || !open_music_type(music_type)) {
   542         return NULL;
   543     }
   544 
   545     *spec = mixer;
   546 
   547     /* Use fragments sized on full audio frame boundaries - this'll do */
   548     fragment_size = spec->size;
   549 
   550     start = SDL_RWtell(src);
   551     for (i = 0; i < get_num_music_interfaces(); ++i) {
   552         interface = get_music_interface(i);
   553         if (!interface->opened) {
   554             continue;
   555         }
   556         if (interface->type != music_type) {
   557             continue;
   558         }
   559         if (!interface->CreateFromRW || !interface->GetAudio) {
   560             continue;
   561         }
   562 
   563         /* These music interfaces are not safe to use while music is playing */
   564         if (interface->api == MIX_MUSIC_CMD ||
   565              interface->api == MIX_MUSIC_MIKMOD ||
   566              interface->api == MIX_MUSIC_NATIVEMIDI) {
   567             continue;
   568         }
   569 
   570         music = interface->CreateFromRW(src, freesrc);
   571         if (music) {
   572             /* The interface owns the data source now */
   573             freesrc = SDL_FALSE;
   574             break;
   575         }
   576 
   577         /* Reset the stream for the next decoder */
   578         SDL_RWseek(src, start, RW_SEEK_SET);
   579     }
   580 
   581     if (!music) {
   582         if (freesrc) {
   583             SDL_RWclose(src);
   584         }
   585         Mix_SetError("Unrecognized audio format");
   586         return NULL;
   587     }
   588 
   589     Mix_LockAudio();
   590 
   591     if (interface->Play) {
   592         interface->Play(music, 1);
   593     }
   594     playing = SDL_TRUE;
   595 
   596     while (playing) {
   597         int left;
   598 
   599         fragment = (MusicFragment *)SDL_malloc(sizeof(*fragment));
   600         if (!fragment) {
   601             /* Uh oh, out of memory, let's return what we have */
   602             break;
   603         }
   604         fragment->data = (Uint8 *)SDL_malloc(fragment_size);
   605         if (!fragment->data) {
   606             /* Uh oh, out of memory, let's return what we have */
   607             SDL_free(fragment);
   608             break;
   609         }
   610         fragment->next = NULL;
   611 
   612         left = interface->GetAudio(music, fragment->data, fragment_size);
   613         if (left > 0) {
   614             playing = SDL_FALSE;
   615         } else if (interface->IsPlaying) {
   616             playing = interface->IsPlaying(music);
   617         }
   618         fragment->size = (fragment_size - left);
   619 
   620         if (!first) {
   621             first = fragment;
   622         }
   623         if (last) {
   624             last->next = fragment;
   625         }
   626         last = fragment;
   627         ++count;
   628     }
   629 
   630     if (interface->Stop) {
   631         interface->Stop(music);
   632     }
   633 
   634     if (music) {
   635         interface->Delete(music);
   636     }
   637 
   638     Mix_UnlockAudio();
   639 
   640     if (count > 0) {
   641         *audio_len = (count - 1) * fragment_size + fragment->size;
   642         *audio_buf = (Uint8 *)SDL_malloc(*audio_len);
   643         if (*audio_buf) {
   644             Uint8 *dst = *audio_buf;
   645             for (fragment = first; fragment; fragment = fragment->next) {
   646                 SDL_memcpy(dst, fragment->data, fragment->size);
   647                 dst += fragment->size;
   648             }
   649         } else {
   650             SDL_OutOfMemory();
   651             spec = NULL;
   652         }
   653     } else {
   654         Mix_SetError("No audio data");
   655         spec = NULL;
   656     }
   657 
   658     while (first) {
   659         fragment = first;
   660         first = first->next;
   661         SDL_free(fragment->data);
   662         SDL_free(fragment);
   663     }
   664 
   665     if (freesrc) {
   666         SDL_RWclose(src);
   667     }
   668     return spec;
   669 }
   670 
   671 /* Load a wave file */
   672 Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
   673 {
   674     Uint8 magic[4];
   675     Mix_Chunk *chunk;
   676     SDL_AudioSpec wavespec, *loaded;
   677     SDL_AudioCVT wavecvt;
   678     int samplesize;
   679 
   680     /* rcg06012001 Make sure src is valid */
   681     if (!src) {
   682         SDL_SetError("Mix_LoadWAV_RW with NULL src");
   683         return(NULL);
   684     }
   685 
   686     /* Make sure audio has been opened */
   687     if (!audio_opened) {
   688         SDL_SetError("Audio device hasn't been opened");
   689         if (freesrc) {
   690             SDL_RWclose(src);
   691         }
   692         return(NULL);
   693     }
   694 
   695     /* Allocate the chunk memory */
   696     chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
   697     if (chunk == NULL) {
   698         SDL_SetError("Out of memory");
   699         if (freesrc) {
   700             SDL_RWclose(src);
   701         }
   702         return(NULL);
   703     }
   704 
   705     /* Find out what kind of audio file this is */
   706     if (SDL_RWread(src, magic, 1, 4) != 4) {
   707         if (freesrc) {
   708             SDL_RWclose(src);
   709         }
   710         Mix_SetError("Couldn't read first 4 bytes of audio data");
   711         return NULL;
   712     }
   713     /* Seek backwards for compatibility with older loaders */
   714     SDL_RWseek(src, -4, RW_SEEK_CUR);
   715 
   716     if (SDL_memcmp(magic, "WAVE", 4) == 0 || SDL_memcmp(magic, "RIFF", 4) == 0) {
   717         loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
   718     } else if (SDL_memcmp(magic, "FORM", 4) == 0) {
   719         loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
   720     } else if (SDL_memcmp(magic, "Crea", 4) == 0) {
   721         loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
   722     } else {
   723         Mix_MusicType music_type = detect_music_type_from_magic(magic);
   724         loaded = Mix_LoadMusic_RW(music_type, src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
   725     }
   726     if (!loaded) {
   727         /* The individual loaders have closed src if needed */
   728         SDL_free(chunk);
   729         return(NULL);
   730     }
   731 
   732 #if 0
   733     PrintFormat("Audio device", &mixer);
   734     PrintFormat("-- Wave file", &wavespec);
   735 #endif
   736 
   737     /* Build the audio converter and create conversion buffers */
   738     if (wavespec.format != mixer.format ||
   739          wavespec.channels != mixer.channels ||
   740          wavespec.freq != mixer.freq) {
   741         if (SDL_BuildAudioCVT(&wavecvt,
   742                 wavespec.format, wavespec.channels, wavespec.freq,
   743                 mixer.format, mixer.channels, mixer.freq) < 0) {
   744             SDL_free(chunk->abuf);
   745             SDL_free(chunk);
   746             return(NULL);
   747         }
   748         samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;
   749         wavecvt.len = chunk->alen & ~(samplesize-1);
   750         wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult);
   751         if (wavecvt.buf == NULL) {
   752             SDL_SetError("Out of memory");
   753             SDL_free(chunk->abuf);
   754             SDL_free(chunk);
   755             return(NULL);
   756         }
   757         SDL_memcpy(wavecvt.buf, chunk->abuf, wavecvt.len);
   758         SDL_free(chunk->abuf);
   759 
   760         /* Run the audio converter */
   761         if (SDL_ConvertAudio(&wavecvt) < 0) {
   762             SDL_free(wavecvt.buf);
   763             SDL_free(chunk);
   764             return(NULL);
   765         }
   766 
   767         chunk->abuf = wavecvt.buf;
   768         chunk->alen = wavecvt.len_cvt;
   769     }
   770 
   771     chunk->allocated = 1;
   772     chunk->volume = MIX_MAX_VOLUME;
   773 
   774     return(chunk);
   775 }
   776 
   777 /* Load a wave file of the mixer format from a memory buffer */
   778 Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
   779 {
   780     Mix_Chunk *chunk;
   781     Uint8 magic[4];
   782 
   783     /* Make sure audio has been opened */
   784     if (! audio_opened) {
   785         SDL_SetError("Audio device hasn't been opened");
   786         return(NULL);
   787     }
   788 
   789     /* Allocate the chunk memory */
   790     chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk));
   791     if (chunk == NULL) {
   792         SDL_SetError("Out of memory");
   793         return(NULL);
   794     }
   795 
   796     /* Essentially just skip to the audio data (no error checking - fast) */
   797     chunk->allocated = 0;
   798     mem += 12; /* WAV header */
   799     do {
   800         SDL_memcpy(magic, mem, 4);
   801         mem += 4;
   802         chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
   803         mem += 4;
   804         chunk->abuf = mem;
   805         mem += chunk->alen;
   806     } while (memcmp(magic, "data", 4) != 0);
   807     chunk->volume = MIX_MAX_VOLUME;
   808 
   809     return(chunk);
   810 }
   811 
   812 /* Load raw audio data of the mixer format from a memory buffer */
   813 Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
   814 {
   815     Mix_Chunk *chunk;
   816 
   817     /* Make sure audio has been opened */
   818     if (! audio_opened) {
   819         SDL_SetError("Audio device hasn't been opened");
   820         return(NULL);
   821     }
   822 
   823     /* Allocate the chunk memory */
   824     chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
   825     if (chunk == NULL) {
   826         SDL_SetError("Out of memory");
   827         return(NULL);
   828     }
   829 
   830     /* Essentially just point at the audio data (no error checking - fast) */
   831     chunk->allocated = 0;
   832     chunk->alen = len;
   833     chunk->abuf = mem;
   834     chunk->volume = MIX_MAX_VOLUME;
   835 
   836     return(chunk);
   837 }
   838 
   839 /* Free an audio chunk previously loaded */
   840 void Mix_FreeChunk(Mix_Chunk *chunk)
   841 {
   842     int i;
   843 
   844     /* Caution -- if the chunk is playing, the mixer will crash */
   845     if (chunk) {
   846         /* Guarantee that this chunk isn't playing */
   847         Mix_LockAudio();
   848         if (mix_channel) {
   849             for (i=0; i<num_channels; ++i) {
   850                 if (chunk == mix_channel[i].chunk) {
   851                     mix_channel[i].playing = 0;
   852                     mix_channel[i].looping = 0;
   853                 }
   854             }
   855         }
   856         Mix_UnlockAudio();
   857         /* Actually free the chunk */
   858         if (chunk->allocated) {
   859             SDL_free(chunk->abuf);
   860         }
   861         SDL_free(chunk);
   862     }
   863 }
   864 
   865 /* Set a function that is called after all mixing is performed.
   866    This can be used to provide real-time visual display of the audio stream
   867    or add a custom mixer filter for the stream data.
   868 */
   869 void Mix_SetPostMix(void (SDLCALL *mix_func)
   870                     (void *udata, Uint8 *stream, int len), void *arg)
   871 {
   872     Mix_LockAudio();
   873     mix_postmix_data = arg;
   874     mix_postmix = mix_func;
   875     Mix_UnlockAudio();
   876 }
   877 
   878 /* Add your own music player or mixer function.
   879    If 'mix_func' is NULL, the default music player is re-enabled.
   880  */
   881 void Mix_HookMusic(void (SDLCALL *mix_func)(void *udata, Uint8 *stream, int len),
   882                                                                 void *arg)
   883 {
   884     Mix_LockAudio();
   885     if (mix_func != NULL) {
   886         music_data = arg;
   887         mix_music = mix_func;
   888     } else {
   889         music_data = NULL;
   890         mix_music = music_mixer;
   891     }
   892     Mix_UnlockAudio();
   893 }
   894 
   895 void *Mix_GetMusicHookData(void)
   896 {
   897     return(music_data);
   898 }
   899 
   900 void Mix_ChannelFinished(void (SDLCALL *channel_finished)(int channel))
   901 {
   902     Mix_LockAudio();
   903     channel_done_callback = channel_finished;
   904     Mix_UnlockAudio();
   905 }
   906 
   907 
   908 /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
   909    them dynamically to the next sample if requested with a -1 value below.
   910    Returns the number of reserved channels.
   911  */
   912 int Mix_ReserveChannels(int num)
   913 {
   914     if (num > num_channels)
   915         num = num_channels;
   916     reserved_channels = num;
   917     return num;
   918 }
   919 
   920 static int checkchunkintegral(Mix_Chunk *chunk)
   921 {
   922     int frame_width = 1;
   923 
   924     if ((mixer.format & 0xFF) == 16) frame_width = 2;
   925     frame_width *= mixer.channels;
   926     while (chunk->alen % frame_width) chunk->alen--;
   927     return chunk->alen;
   928 }
   929 
   930 /* Play an audio chunk on a specific channel.
   931    If the specified channel is -1, play on the first free channel.
   932    'ticks' is the number of milliseconds at most to play the sample, or -1
   933    if there is no limit.
   934    Returns which channel was used to play the sound.
   935 */
   936 int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
   937 {
   938     int i;
   939 
   940     /* Don't play null pointers :-) */
   941     if (chunk == NULL) {
   942         Mix_SetError("Tried to play a NULL chunk");
   943         return(-1);
   944     }
   945     if (!checkchunkintegral(chunk)) {
   946         Mix_SetError("Tried to play a chunk with a bad frame");
   947         return(-1);
   948     }
   949 
   950     /* Lock the mixer while modifying the playing channels */
   951     Mix_LockAudio();
   952     {
   953         /* If which is -1, play on the first free channel */
   954         if (which == -1) {
   955             for (i=reserved_channels; i<num_channels; ++i) {
   956                 if (mix_channel[i].playing <= 0)
   957                     break;
   958             }
   959             if (i == num_channels) {
   960                 Mix_SetError("No free channels available");
   961                 which = -1;
   962             } else {
   963                 which = i;
   964             }
   965         }
   966 
   967         /* Queue up the audio data for this channel */
   968         if (which >= 0 && which < num_channels) {
   969             Uint32 sdl_ticks = SDL_GetTicks();
   970             if (Mix_Playing(which))
   971                 _Mix_channel_done_playing(which);
   972             mix_channel[which].samples = chunk->abuf;
   973             mix_channel[which].playing = chunk->alen;
   974             mix_channel[which].looping = loops;
   975             mix_channel[which].chunk = chunk;
   976             mix_channel[which].paused = 0;
   977             mix_channel[which].fading = MIX_NO_FADING;
   978             mix_channel[which].start_time = sdl_ticks;
   979             mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0;
   980         }
   981     }
   982     Mix_UnlockAudio();
   983 
   984     /* Return the channel on which the sound is being played */
   985     return(which);
   986 }
   987 
   988 /* Change the expiration delay for a channel */
   989 int Mix_ExpireChannel(int which, int ticks)
   990 {
   991     int status = 0;
   992 
   993     if (which == -1) {
   994         int i;
   995         for (i=0; i < num_channels; ++ i) {
   996             status += Mix_ExpireChannel(i, ticks);
   997         }
   998     } else if (which < num_channels) {
   999         Mix_LockAudio();
  1000         mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0;
  1001         Mix_UnlockAudio();
  1002         ++ status;
  1003     }
  1004     return(status);
  1005 }
  1006 
  1007 /* Fade in a sound on a channel, over ms milliseconds */
  1008 int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks)
  1009 {
  1010     int i;
  1011 
  1012     /* Don't play null pointers :-) */
  1013     if (chunk == NULL) {
  1014         return(-1);
  1015     }
  1016     if (!checkchunkintegral(chunk)) {
  1017         Mix_SetError("Tried to play a chunk with a bad frame");
  1018         return(-1);
  1019     }
  1020 
  1021     /* Lock the mixer while modifying the playing channels */
  1022     Mix_LockAudio();
  1023     {
  1024         /* If which is -1, play on the first free channel */
  1025         if (which == -1) {
  1026             for (i=reserved_channels; i<num_channels; ++i) {
  1027                 if (mix_channel[i].playing <= 0)
  1028                     break;
  1029             }
  1030             if (i == num_channels) {
  1031                 which = -1;
  1032             } else {
  1033                 which = i;
  1034             }
  1035         }
  1036 
  1037         /* Queue up the audio data for this channel */
  1038         if (which >= 0 && which < num_channels) {
  1039             Uint32 sdl_ticks = SDL_GetTicks();
  1040             if (Mix_Playing(which))
  1041                 _Mix_channel_done_playing(which);
  1042             mix_channel[which].samples = chunk->abuf;
  1043             mix_channel[which].playing = chunk->alen;
  1044             mix_channel[which].looping = loops;
  1045             mix_channel[which].chunk = chunk;
  1046             mix_channel[which].paused = 0;
  1047             mix_channel[which].fading = MIX_FADING_IN;
  1048             mix_channel[which].fade_volume = mix_channel[which].volume;
  1049             mix_channel[which].fade_volume_reset = mix_channel[which].volume;
  1050             mix_channel[which].volume = 0;
  1051             mix_channel[which].fade_length = (Uint32)ms;
  1052             mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks;
  1053             mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0;
  1054         }
  1055     }
  1056     Mix_UnlockAudio();
  1057 
  1058     /* Return the channel on which the sound is being played */
  1059     return(which);
  1060 }
  1061 
  1062 /* Set volume of a particular channel */
  1063 int Mix_Volume(int which, int volume)
  1064 {
  1065     int i;
  1066     int prev_volume = 0;
  1067 
  1068     if (which == -1) {
  1069         for (i=0; i<num_channels; ++i) {
  1070             prev_volume += Mix_Volume(i, volume);
  1071         }
  1072         prev_volume /= num_channels;
  1073     } else if (which < num_channels) {
  1074         prev_volume = mix_channel[which].volume;
  1075         if (volume >= 0) {
  1076             if (volume > MIX_MAX_VOLUME) {
  1077                 volume = MIX_MAX_VOLUME;
  1078             }
  1079             mix_channel[which].volume = volume;
  1080         }
  1081     }
  1082     return(prev_volume);
  1083 }
  1084 /* Set volume of a particular chunk */
  1085 int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
  1086 {
  1087     int prev_volume;
  1088 
  1089     prev_volume = chunk->volume;
  1090     if (volume >= 0) {
  1091         if (volume > MIX_MAX_VOLUME) {
  1092             volume = MIX_MAX_VOLUME;
  1093         }
  1094         chunk->volume = volume;
  1095     }
  1096     return(prev_volume);
  1097 }
  1098 
  1099 /* Halt playing of a particular channel */
  1100 int Mix_HaltChannel(int which)
  1101 {
  1102     int i;
  1103 
  1104     if (which == -1) {
  1105         for (i=0; i<num_channels; ++i) {
  1106             Mix_HaltChannel(i);
  1107         }
  1108     } else if (which < num_channels) {
  1109         Mix_LockAudio();
  1110         if (mix_channel[which].playing) {
  1111             _Mix_channel_done_playing(which);
  1112             mix_channel[which].playing = 0;
  1113             mix_channel[which].looping = 0;
  1114         }
  1115         mix_channel[which].expire = 0;
  1116         if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
  1117             mix_channel[which].volume = mix_channel[which].fade_volume_reset;
  1118         mix_channel[which].fading = MIX_NO_FADING;
  1119         Mix_UnlockAudio();
  1120     }
  1121     return(0);
  1122 }
  1123 
  1124 /* Halt playing of a particular group of channels */
  1125 int Mix_HaltGroup(int tag)
  1126 {
  1127     int i;
  1128 
  1129     for (i=0; i<num_channels; ++i) {
  1130         if(mix_channel[i].tag == tag) {
  1131             Mix_HaltChannel(i);
  1132         }
  1133     }
  1134     return(0);
  1135 }
  1136 
  1137 /* Fade out a channel and then stop it automatically */
  1138 int Mix_FadeOutChannel(int which, int ms)
  1139 {
  1140     int status;
  1141 
  1142     status = 0;
  1143     if (audio_opened) {
  1144         if (which == -1) {
  1145             int i;
  1146 
  1147             for (i=0; i<num_channels; ++i) {
  1148                 status += Mix_FadeOutChannel(i, ms);
  1149             }
  1150         } else if (which < num_channels) {
  1151             Mix_LockAudio();
  1152             if (mix_channel[which].playing &&
  1153                 (mix_channel[which].volume > 0) &&
  1154                 (mix_channel[which].fading != MIX_FADING_OUT)) {
  1155                 mix_channel[which].fade_volume = mix_channel[which].volume;
  1156                 mix_channel[which].fading = MIX_FADING_OUT;
  1157                 mix_channel[which].fade_length = (Uint32)ms;
  1158                 mix_channel[which].ticks_fade = SDL_GetTicks();
  1159 
  1160                 /* only change fade_volume_reset if we're not fading. */
  1161                 if (mix_channel[which].fading == MIX_NO_FADING) {
  1162                     mix_channel[which].fade_volume_reset = mix_channel[which].volume;
  1163                 }
  1164                 ++status;
  1165             }
  1166             Mix_UnlockAudio();
  1167         }
  1168     }
  1169     return(status);
  1170 }
  1171 
  1172 /* Halt playing of a particular group of channels */
  1173 int Mix_FadeOutGroup(int tag, int ms)
  1174 {
  1175     int i;
  1176     int status = 0;
  1177     for (i=0; i<num_channels; ++i) {
  1178         if(mix_channel[i].tag == tag) {
  1179             status += Mix_FadeOutChannel(i,ms);
  1180         }
  1181     }
  1182     return(status);
  1183 }
  1184 
  1185 Mix_Fading Mix_FadingChannel(int which)
  1186 {
  1187     if (which < 0 || which >= num_channels) {
  1188         return MIX_NO_FADING;
  1189     }
  1190     return mix_channel[which].fading;
  1191 }
  1192 
  1193 /* Check the status of a specific channel.
  1194    If the specified mix_channel is -1, check all mix channels.
  1195 */
  1196 int Mix_Playing(int which)
  1197 {
  1198     int status;
  1199 
  1200     status = 0;
  1201     if (which == -1) {
  1202         int i;
  1203 
  1204         for (i=0; i<num_channels; ++i) {
  1205             if ((mix_channel[i].playing > 0) ||
  1206                 mix_channel[i].looping)
  1207             {
  1208                 ++status;
  1209             }
  1210         }
  1211     } else if (which < num_channels) {
  1212         if ((mix_channel[which].playing > 0) ||
  1213              mix_channel[which].looping)
  1214         {
  1215             ++status;
  1216         }
  1217     }
  1218     return(status);
  1219 }
  1220 
  1221 /* rcg06072001 Get the chunk associated with a channel. */
  1222 Mix_Chunk *Mix_GetChunk(int channel)
  1223 {
  1224     Mix_Chunk *retval = NULL;
  1225 
  1226     if ((channel >= 0) && (channel < num_channels)) {
  1227         retval = mix_channel[channel].chunk;
  1228     }
  1229 
  1230     return(retval);
  1231 }
  1232 
  1233 /* Close the mixer, halting all playing audio */
  1234 void Mix_CloseAudio(void)
  1235 {
  1236     int i;
  1237 
  1238     if (audio_opened) {
  1239         if (audio_opened == 1) {
  1240             for (i = 0; i < num_channels; i++) {
  1241                 Mix_UnregisterAllEffects(i);
  1242             }
  1243             Mix_UnregisterAllEffects(MIX_CHANNEL_POST);
  1244             close_music();
  1245             Mix_SetMusicCMD(NULL);
  1246             Mix_HaltChannel(-1);
  1247             _Mix_DeinitEffects();
  1248             SDL_CloseAudioDevice(audio_device);
  1249             audio_device = 0;
  1250             SDL_free(mix_channel);
  1251             mix_channel = NULL;
  1252 
  1253             /* rcg06042009 report available decoders at runtime. */
  1254             SDL_free((void *)chunk_decoders);
  1255             chunk_decoders = NULL;
  1256             num_decoders = 0;
  1257         }
  1258         --audio_opened;
  1259     }
  1260 }
  1261 
  1262 /* Pause a particular channel (or all) */
  1263 void Mix_Pause(int which)
  1264 {
  1265     Uint32 sdl_ticks = SDL_GetTicks();
  1266     if (which == -1) {
  1267         int i;
  1268 
  1269         for (i=0; i<num_channels; ++i) {
  1270             if (mix_channel[i].playing > 0) {
  1271                 mix_channel[i].paused = sdl_ticks;
  1272             }
  1273         }
  1274     } else if (which < num_channels) {
  1275         if (mix_channel[which].playing > 0) {
  1276             mix_channel[which].paused = sdl_ticks;
  1277         }
  1278     }
  1279 }
  1280 
  1281 /* Resume a paused channel */
  1282 void Mix_Resume(int which)
  1283 {
  1284     Uint32 sdl_ticks = SDL_GetTicks();
  1285 
  1286     Mix_LockAudio();
  1287     if (which == -1) {
  1288         int i;
  1289 
  1290         for (i=0; i<num_channels; ++i) {
  1291             if (mix_channel[i].playing > 0) {
  1292                 if(mix_channel[i].expire > 0)
  1293                     mix_channel[i].expire += sdl_ticks - mix_channel[i].paused;
  1294                 mix_channel[i].paused = 0;
  1295             }
  1296         }
  1297     } else if (which < num_channels) {
  1298         if (mix_channel[which].playing > 0) {
  1299             if(mix_channel[which].expire > 0)
  1300                 mix_channel[which].expire += sdl_ticks - mix_channel[which].paused;
  1301             mix_channel[which].paused = 0;
  1302         }
  1303     }
  1304     Mix_UnlockAudio();
  1305 }
  1306 
  1307 int Mix_Paused(int which)
  1308 {
  1309     if (which < 0) {
  1310         int status = 0;
  1311         int i;
  1312         for(i=0; i < num_channels; ++i) {
  1313             if (mix_channel[i].paused) {
  1314                 ++ status;
  1315             }
  1316         }
  1317         return(status);
  1318     } else if (which < num_channels) {
  1319         return(mix_channel[which].paused != 0);
  1320     } else {
  1321         return(0);
  1322     }
  1323 }
  1324 
  1325 /* Change the group of a channel */
  1326 int Mix_GroupChannel(int which, int tag)
  1327 {
  1328     if (which < 0 || which > num_channels)
  1329         return(0);
  1330 
  1331     Mix_LockAudio();
  1332     mix_channel[which].tag = tag;
  1333     Mix_UnlockAudio();
  1334     return(1);
  1335 }
  1336 
  1337 /* Assign several consecutive channels to a group */
  1338 int Mix_GroupChannels(int from, int to, int tag)
  1339 {
  1340     int status = 0;
  1341     for(; from <= to; ++ from) {
  1342         status += Mix_GroupChannel(from, tag);
  1343     }
  1344     return(status);
  1345 }
  1346 
  1347 /* Finds the first available channel in a group of channels */
  1348 int Mix_GroupAvailable(int tag)
  1349 {
  1350     int i;
  1351     for(i=0; i < num_channels; i ++) {
  1352         if (((tag == -1) || (tag == mix_channel[i].tag)) &&
  1353                             (mix_channel[i].playing <= 0))
  1354             return i;
  1355     }
  1356     return(-1);
  1357 }
  1358 
  1359 int Mix_GroupCount(int tag)
  1360 {
  1361     int count = 0;
  1362     int i;
  1363     for(i=0; i < num_channels; i ++) {
  1364         if (mix_channel[i].tag==tag || tag==-1)
  1365             ++ count;
  1366     }
  1367     return(count);
  1368 }
  1369 
  1370 /* Finds the "oldest" sample playing in a group of channels */
  1371 int Mix_GroupOldest(int tag)
  1372 {
  1373     int chan = -1;
  1374     Uint32 mintime = SDL_GetTicks();
  1375     int i;
  1376     for(i=0; i < num_channels; i ++) {
  1377         if ((mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1378              && mix_channel[i].start_time <= mintime) {
  1379             mintime = mix_channel[i].start_time;
  1380             chan = i;
  1381         }
  1382     }
  1383     return(chan);
  1384 }
  1385 
  1386 /* Finds the "most recent" (i.e. last) sample playing in a group of channels */
  1387 int Mix_GroupNewer(int tag)
  1388 {
  1389     int chan = -1;
  1390     Uint32 maxtime = 0;
  1391     int i;
  1392     for(i=0; i < num_channels; i ++) {
  1393         if ((mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1394              && mix_channel[i].start_time >= maxtime) {
  1395             maxtime = mix_channel[i].start_time;
  1396             chan = i;
  1397         }
  1398     }
  1399     return(chan);
  1400 }
  1401 
  1402 
  1403 
  1404 /*
  1405  * rcg06122001 The special effects exportable API.
  1406  *  Please see effect_*.c for internally-implemented effects, such
  1407  *  as Mix_SetPanning().
  1408  */
  1409 
  1410 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1411 static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f,
  1412                 Mix_EffectDone_t d, void *arg)
  1413 {
  1414     effect_info *new_e;
  1415 
  1416     if (!e) {
  1417         Mix_SetError("Internal error");
  1418         return(0);
  1419     }
  1420 
  1421     if (f == NULL) {
  1422         Mix_SetError("NULL effect callback");
  1423         return(0);
  1424     }
  1425 
  1426     new_e = SDL_malloc(sizeof (effect_info));
  1427     if (new_e == NULL) {
  1428         Mix_SetError("Out of memory");
  1429         return(0);
  1430     }
  1431 
  1432     new_e->callback = f;
  1433     new_e->done_callback = d;
  1434     new_e->udata = arg;
  1435     new_e->next = NULL;
  1436 
  1437     /* add new effect to end of linked list... */
  1438     if (*e == NULL) {
  1439         *e = new_e;
  1440     } else {
  1441         effect_info *cur = *e;
  1442         while (1) {
  1443             if (cur->next == NULL) {
  1444                 cur->next = new_e;
  1445                 break;
  1446             }
  1447             cur = cur->next;
  1448         }
  1449     }
  1450 
  1451     return(1);
  1452 }
  1453 
  1454 
  1455 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1456 static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f)
  1457 {
  1458     effect_info *cur;
  1459     effect_info *prev = NULL;
  1460     effect_info *next = NULL;
  1461 
  1462     if (!e) {
  1463         Mix_SetError("Internal error");
  1464         return(0);
  1465     }
  1466 
  1467     for (cur = *e; cur != NULL; cur = cur->next) {
  1468         if (cur->callback == f) {
  1469             next = cur->next;
  1470             if (cur->done_callback != NULL) {
  1471                 cur->done_callback(channel, cur->udata);
  1472             }
  1473             SDL_free(cur);
  1474 
  1475             if (prev == NULL) {   /* removing first item of list? */
  1476                 *e = next;
  1477             } else {
  1478                 prev->next = next;
  1479             }
  1480             return(1);
  1481         }
  1482         prev = cur;
  1483     }
  1484 
  1485     Mix_SetError("No such effect registered");
  1486     return(0);
  1487 }
  1488 
  1489 
  1490 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1491 static int _Mix_remove_all_effects(int channel, effect_info **e)
  1492 {
  1493     effect_info *cur;
  1494     effect_info *next;
  1495 
  1496     if (!e) {
  1497         Mix_SetError("Internal error");
  1498         return(0);
  1499     }
  1500 
  1501     for (cur = *e; cur != NULL; cur = next) {
  1502         next = cur->next;
  1503         if (cur->done_callback != NULL) {
  1504             cur->done_callback(channel, cur->udata);
  1505         }
  1506         SDL_free(cur);
  1507     }
  1508     *e = NULL;
  1509 
  1510     return(1);
  1511 }
  1512 
  1513 
  1514 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1515 int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f,
  1516             Mix_EffectDone_t d, void *arg)
  1517 {
  1518     effect_info **e = NULL;
  1519 
  1520     if (channel == MIX_CHANNEL_POST) {
  1521         e = &posteffects;
  1522     } else {
  1523         if ((channel < 0) || (channel >= num_channels)) {
  1524             Mix_SetError("Invalid channel number");
  1525             return(0);
  1526         }
  1527         e = &mix_channel[channel].effects;
  1528     }
  1529 
  1530     return _Mix_register_effect(e, f, d, arg);
  1531 }
  1532 
  1533 int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f,
  1534             Mix_EffectDone_t d, void *arg)
  1535 {
  1536     int retval;
  1537     Mix_LockAudio();
  1538     retval = _Mix_RegisterEffect_locked(channel, f, d, arg);
  1539     Mix_UnlockAudio();
  1540     return retval;
  1541 }
  1542 
  1543 
  1544 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1545 int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f)
  1546 {
  1547     effect_info **e = NULL;
  1548 
  1549     if (channel == MIX_CHANNEL_POST) {
  1550         e = &posteffects;
  1551     } else {
  1552         if ((channel < 0) || (channel >= num_channels)) {
  1553             Mix_SetError("Invalid channel number");
  1554             return(0);
  1555         }
  1556         e = &mix_channel[channel].effects;
  1557     }
  1558 
  1559     return _Mix_remove_effect(channel, e, f);
  1560 }
  1561 
  1562 int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f)
  1563 {
  1564     int retval;
  1565     Mix_LockAudio();
  1566     retval = _Mix_UnregisterEffect_locked(channel, f);
  1567     Mix_UnlockAudio();
  1568     return(retval);
  1569 }
  1570 
  1571 /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
  1572 int _Mix_UnregisterAllEffects_locked(int channel)
  1573 {
  1574     effect_info **e = NULL;
  1575 
  1576     if (channel == MIX_CHANNEL_POST) {
  1577         e = &posteffects;
  1578     } else {
  1579         if ((channel < 0) || (channel >= num_channels)) {
  1580             Mix_SetError("Invalid channel number");
  1581             return(0);
  1582         }
  1583         e = &mix_channel[channel].effects;
  1584     }
  1585 
  1586     return _Mix_remove_all_effects(channel, e);
  1587 }
  1588 
  1589 int Mix_UnregisterAllEffects(int channel)
  1590 {
  1591     int retval;
  1592     Mix_LockAudio();
  1593     retval = _Mix_UnregisterAllEffects_locked(channel);
  1594     Mix_UnlockAudio();
  1595     return(retval);
  1596 }
  1597 
  1598 void Mix_LockAudio(void)
  1599 {
  1600     SDL_LockAudioDevice(audio_device);
  1601 }
  1602 
  1603 void Mix_UnlockAudio(void)
  1604 {
  1605     SDL_UnlockAudioDevice(audio_device);
  1606 }
  1607 
  1608 /* end of mixer.c ... */
  1609 
  1610 /* vi: set ts=4 sw=4 expandtab: */