mixer.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 09 Jul 2013 06:59:10 -0700
changeset 648 bd8389c6dd20
parent 644 030181ff9f59
child 655 bbc63f965931
permissions -rw-r--r--
Fixed looping behavior and Mix_Playing() result for looping channels

Lee Salzman

If you passed in a negative loop count, it is supposed to behave as if it loops forever. However, it blindly decrements the loop counter regardless of whether or not the loop count is actually positive. So first fix is just to put a > 0 guard before the loop counter decrement.

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