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