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