mixer.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Feb 2008 07:31:03 +0000
changeset 382 50501e45c57b
parent 373 6271eafc6e70
child 383 0d05ac848d6a
permissions -rw-r--r--
Austen Dicken - Tue Feb 26 23:28:27 PST 2008

Ok, here is the patch I made for FLAC support.

I have tested it relatively thoroughly and currently the patch allows:
1. Pre-loading FLAC files and playing them via LoadWAV
2. The patch allows for FLAC support in the LoadMUS setting as well as:
* Pause / Resume
* Volume control
* Seeking

I also did a little benchmarking by comparing memory/cpu usage of playmus to
that of mplayer, and the results were very good. playmus typically took about
half the RAM as mplayer, though that may be attributed to mplayer being a more
"bulky" program. As such I would say that the two are probably about equal in
efficiency.

Also, it is important to note that, similar to the OGG support currently
built-in, my FLAC patch only supports 16 bit stereo-encoded sound. Also, it
is only for Native FLAC (standard) and not the derivative, Ogg-FLAC.

I have tried to find a simple way to detect Ogg-FLAC files, as the only
difference between Ogg-FLAC and Native FLAC support is changing the init_
function call, but after digging a little deeper it seems that Ogg-FLAC is
basically FLAC wrapped in an Ogg transport layer, so it would be better to have
a way to read the Ogg transport layer which then reads the inner audio files
according to the proper codec.

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