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