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