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