mixer.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Aug 2004 12:27:02 +0000
changeset 245 63b3650714de
parent 241 503416fca921
child 300 b7dde67b444c
permissions -rw-r--r--
Here are patches for SDL12 and SDL_mixer for 4 or 6 channel
surround sound on Linux using the Alsa driver. To use them, naturally
you need a sound card that will do 4 or 6 channels and probably also a
recent version of the Alsa drivers and library. Since the only SDL
output driver that knows about surround sound is the Alsa driver,
you���ll want to choose it, using:

export SDL_AUDIODRIVER=alsa

There are no syntactic changes to the programming API. No new
library calls, no differences in arguments.

There are two semantic changes:

(1) For library calls with number of channels as an argument, formerly
you could use only 1 or 2 for the number of channels. Now you
can also use 4 or 6.

(2) The two "left" and "right" arguments to Mix_SetPanning, for the
case of 4 or 6 channels, no longer simply control the volumes of
the left and right channels. Now the "left" argument is converted
to an angle and Mix_SetPosition is called, and the "right" argu-
ment is ignored.

With two exceptions, so far as I know, the modified SDL12 and
SDL_mixer work the same way as the original versions, when opened for
1 or 2 channel output. The two exceptions are bugs which I fixed.
Well, the first, anyway, is a bug for sure. When rate conversions up
or down by a factor of two are applied (in src/audio/SDL_audiocvt.c),
streams with different numbers of channels (that is, mono and stereo)
are treated the same way: either each sample is copied or every other
sample is omitted. This is ok for mono, but for stereo, it is frames
that should be copied or omitted, where by "frame" I mean a portion of
the stream containing one sample for each channel. (In the SDL source,
confusingly, sometimes frames are called "samples".) So for these
rate conversions, stereo streams have to be treated differently, and
they are, in my modified version.

The other problem that might be characterized as a bug arises
when SDL_mixer is passed a multichannel chunk which does not have an
integral number of frames. Due to the way the effect_position code
loops over frames, when the chunk ends with a partial frame, memory
outside the chunk buffer will be accessed. In the case of stereo,
it���s possible that because malloc may give more memory than requested,
this potential problem never actually causes a segment fault. I don���t
know. For 6 channel chunks, I do know, and it does cause segment
faults.


If SDL_mixer is passed defective chunks and this causes a segment
fault, arguably, that���s not a bug in SDL_mixer. Still, whether or not
it counts as a bug, it���s easy to protect against, so why not? I added
code in mixer.c to discard any partial frame at the end of a chunk.

Then what about when SDL or SDL_mixer is opened for 4 or 6 chan-
nel output? What happens with the parts of the current library
designed for stereo? I don���t know whether I���ve covered all the bases,
but I���ve tried:

(1) For playing 2 channel waves, or other cases where SDL knows it has
to match up a 2 channel source with a 4 or 6 channel output, I���ve
added code in SDL_audiocvt.c to make the necessary conversions.

(2) For playing midis using timidity, I���ve converted timidity to do 4
or 6 channel output, upon request.

(3) For playing mods using mikmod, I put ad hoc code in music.c to
convert the stereo output that mikmod produces to 4 or 6 chan-
nels. Obviously it would be better to change the mikmod code to
mix down into 4 or 6 channels, but I have a hard time following
the code in mikmod, so I didn���t do that.

(4) For playing mp3s, I put ad hoc code in smpeg to copy channels in
the case when 4 or 6 channel output is needed.

(5) There seems to be no problem with .ogg files - stereo .oggs can be
up converted as .wavs are.

(6) The effect_position code in SDL_mixer is now generalized to in-
clude the cases of 4 and 6 channel streams.

I���ve done a very limited amount of compatibility testing for some
of the games using SDL I happen to have. For details, see the file
TESTS.

I���ve put into a separate archive, Surround-SDL-testfiles.tgz, a
couple of 6 channel wave files for testing and a 6 channel ogg file.
If you have the right hardware and version of Alsa, you should be able
to play the wave files with the Alsa utility aplay (and hear all
channels, except maybe lfe, for chan-id.wav, since it���s rather faint).
Don���t expect aplay to give good sound, though. There���s something
wrong with the current version of aplay.

The canyon.ogg file is to test loading of 6 channel oggs. After
patching and compiling, you can play it with playmus. (My version of
ogg123 will not play it, and I had to patch mplayer to get it to play
6 channel oggs.)

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