music.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Aug 2004 12:27:02 +0000
changeset 245 63b3650714de
parent 241 503416fca921
child 246 9fa5d0f9d042
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@138
     5
    This library is free software; you can redistribute it and/or
slouken@138
     6
    modify it under the terms of the GNU Library General Public
slouken@138
     7
    License as published by the Free Software Foundation; either
slouken@138
     8
    version 2 of the License, or (at your option) any later version.
slouken@0
     9
slouken@138
    10
    This library is distributed in the hope that it will be useful,
slouken@138
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@138
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@138
    13
    Library General Public License for more details.
slouken@0
    14
slouken@138
    15
    You should have received a copy of the GNU Library General Public
slouken@138
    16
    License along with this library; if not, write to the Free
slouken@138
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@0
    18
slouken@138
    19
    Sam Lantinga
slouken@138
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@0
    22
slouken@140
    23
/* $Id$ */
slouken@138
    24
slouken@3
    25
#include <stdlib.h>
slouken@3
    26
#include <string.h>
slouken@125
    27
#include <ctype.h>
slouken@24
    28
#include "SDL_endian.h"
slouken@24
    29
#include "SDL_audio.h"
slouken@29
    30
#include "SDL_timer.h"
slouken@0
    31
slouken@34
    32
#include "SDL_mixer.h"
slouken@0
    33
slouken@245
    34
#define SDL_SURROUND
slouken@245
    35
slouken@245
    36
#ifdef SDL_SURROUND
slouken@245
    37
#define MAX_OUTPUT_CHANNELS 6
slouken@245
    38
#else
slouken@245
    39
#define MAX_OUTPUT_CHANNELS 2
slouken@245
    40
#endif
slouken@245
    41
slouken@0
    42
/* The music command hack is UNIX specific */
slouken@0
    43
#ifndef unix
slouken@0
    44
#undef CMD_MUSIC
slouken@0
    45
#endif
slouken@0
    46
slouken@0
    47
#ifdef CMD_MUSIC
slouken@0
    48
#include "music_cmd.h"
slouken@0
    49
#endif
slouken@0
    50
#ifdef WAV_MUSIC
slouken@0
    51
#include "wavestream.h"
slouken@0
    52
#endif
slouken@0
    53
#ifdef MOD_MUSIC
slouken@29
    54
#  include "mikmod.h"
slouken@29
    55
#  if defined(LIBMIKMOD_VERSION)                /* libmikmod 3.1.8 */
slouken@29
    56
#    define UNIMOD			MODULE
slouken@29
    57
#    define MikMod_Init()		MikMod_Init(NULL)
slouken@29
    58
#    define MikMod_LoadSong(a,b)	Player_Load(a,b,0)
slouken@226
    59
#    ifdef USE_RWOPS
slouken@226
    60
#      define MikMod_LoadSongRW(a,b)	Player_LoadRW(a,b,0)
slouken@226
    61
#    endif
slouken@29
    62
#    define MikMod_FreeSong		Player_Free
slouken@29
    63
     extern int MikMod_errno;
slouken@29
    64
#  else                                        /* old MikMod 3.0.3 */
slouken@29
    65
#    define MikMod_strerror(x)		_mm_errmsg[x])
slouken@29
    66
#    define MikMod_errno		_mm_errno
slouken@29
    67
#  endif
slouken@0
    68
#endif
slouken@0
    69
#ifdef MID_MUSIC
slouken@106
    70
#  ifdef USE_TIMIDITY_MIDI
slouken@106
    71
#    include "timidity.h"
slouken@106
    72
#  endif
slouken@106
    73
#  ifdef USE_NATIVE_MIDI
slouken@106
    74
#    include "native_midi.h"
slouken@106
    75
#  endif
slouken@106
    76
#  if defined(USE_TIMIDITY_MIDI) && defined(USE_NATIVE_MIDI)
slouken@106
    77
#    define MIDI_ELSE	else
slouken@106
    78
#  else
slouken@106
    79
#    define MIDI_ELSE
slouken@106
    80
#  endif
slouken@0
    81
#endif
slouken@63
    82
#ifdef OGG_MUSIC
slouken@63
    83
#include "music_ogg.h"
slouken@63
    84
#endif
slouken@0
    85
#ifdef MP3_MUSIC
slouken@83
    86
#include "smpeg.h"
slouken@0
    87
slouken@0
    88
static SDL_AudioSpec used_mixer;
slouken@0
    89
#endif
slouken@0
    90
slouken@42
    91
int volatile music_active = 1;
slouken@42
    92
static int volatile music_stopped = 0;
slouken@0
    93
static int music_loops = 0;
slouken@0
    94
static char *music_cmd = NULL;
slouken@42
    95
static Mix_Music * volatile music_playing = NULL;
slouken@17
    96
static int music_volume = MIX_MAX_VOLUME;
slouken@0
    97
static int music_swap8;
slouken@0
    98
static int music_swap16;
megastep@4
    99
slouken@0
   100
struct _Mix_Music {
slouken@177
   101
	Mix_MusicType type;
slouken@0
   102
	union {
slouken@0
   103
#ifdef CMD_MUSIC
slouken@0
   104
		MusicCMD *cmd;
slouken@0
   105
#endif
slouken@0
   106
#ifdef WAV_MUSIC
slouken@0
   107
		WAVStream *wave;
slouken@0
   108
#endif
slouken@0
   109
#ifdef MOD_MUSIC
slouken@0
   110
		UNIMOD *module;
slouken@0
   111
#endif
slouken@0
   112
#ifdef MID_MUSIC
slouken@106
   113
#ifdef USE_TIMIDITY_MIDI
slouken@0
   114
		MidiSong *midi;
slouken@106
   115
#endif
slouken@98
   116
#ifdef USE_NATIVE_MIDI
slouken@98
   117
		NativeMidiSong *nativemidi;
slouken@98
   118
#endif
slouken@0
   119
#endif
slouken@63
   120
#ifdef OGG_MUSIC
slouken@63
   121
		OGG_music *ogg;
slouken@63
   122
#endif
slouken@0
   123
#ifdef MP3_MUSIC
slouken@0
   124
		SMPEG *mp3;
slouken@0
   125
#endif
slouken@0
   126
	} data;
megastep@4
   127
	Mix_Fading fading;
slouken@17
   128
	int fade_step;
slouken@17
   129
	int fade_steps;
slouken@0
   130
	int error;
slouken@0
   131
};
slouken@29
   132
#ifdef MID_MUSIC
slouken@106
   133
#ifdef USE_TIMIDITY_MIDI
slouken@0
   134
static int timidity_ok;
slouken@142
   135
static int samplesize;
slouken@106
   136
#endif
slouken@98
   137
#ifdef USE_NATIVE_MIDI
slouken@98
   138
static int native_midi_ok;
slouken@98
   139
#endif
slouken@29
   140
#endif
slouken@0
   141
slouken@245
   142
/* Reference for converting mikmod output to 4/6 channels */
slouken@245
   143
static int current_output_channels;
slouken@245
   144
static Uint16 current_output_format;
slouken@245
   145
slouken@17
   146
/* Used to calculate fading steps */
slouken@17
   147
static int ms_per_step;
slouken@17
   148
megastep@10
   149
/* Local low-level functions prototypes */
icculus@237
   150
static void music_internal_initialize_volume(void);
slouken@173
   151
static void music_internal_volume(int volume);
slouken@173
   152
static int  music_internal_play(Mix_Music *music, double position);
slouken@173
   153
static int  music_internal_position(double position);
slouken@173
   154
static int  music_internal_playing();
slouken@173
   155
static void music_internal_halt(void);
megastep@7
   156
slouken@29
   157
slouken@29
   158
/* Support for hooking when the music has finished */
slouken@29
   159
static void (*music_finished_hook)(void) = NULL;
slouken@29
   160
slouken@29
   161
void Mix_HookMusicFinished(void (*music_finished)(void))
slouken@29
   162
{
slouken@29
   163
	SDL_LockAudio();
slouken@29
   164
	music_finished_hook = music_finished;
slouken@29
   165
	SDL_UnlockAudio();
slouken@29
   166
}
slouken@29
   167
slouken@29
   168
slouken@0
   169
/* Mixing function */
slouken@0
   170
void music_mixer(void *udata, Uint8 *stream, int len)
slouken@0
   171
{
slouken@173
   172
	if ( music_playing && music_active ) {
megastep@13
   173
		/* Handle fading */
slouken@17
   174
		if ( music_playing->fading != MIX_NO_FADING ) {
slouken@17
   175
			if ( music_playing->fade_step++ < music_playing->fade_steps ) {
slouken@173
   176
				int volume;
slouken@26
   177
				int fade_step = music_playing->fade_step;
slouken@26
   178
				int fade_steps = music_playing->fade_steps;
slouken@17
   179
megastep@4
   180
				if ( music_playing->fading == MIX_FADING_OUT ) {
slouken@173
   181
					volume = (music_volume * (fade_steps-fade_step)) / fade_steps;
slouken@17
   182
				} else { /* Fading in */
slouken@173
   183
					volume = (music_volume * fade_step) / fade_steps;
slouken@17
   184
				}
slouken@173
   185
				music_internal_volume(volume);
slouken@17
   186
			} else {
slouken@17
   187
				if ( music_playing->fading == MIX_FADING_OUT ) {
slouken@173
   188
					music_internal_halt();
slouken@173
   189
					if ( music_finished_hook ) {
slouken@173
   190
						music_finished_hook();
slouken@173
   191
					}
megastep@4
   192
					return;
megastep@4
   193
				}
megastep@4
   194
				music_playing->fading = MIX_NO_FADING;
megastep@4
   195
			}
megastep@4
   196
		}
megastep@13
   197
		/* Restart music if it has to loop */
slouken@173
   198
		if ( !music_internal_playing() ) {
slouken@173
   199
			/* Restart music if it has to loop at a high level */
slouken@29
   200
			if ( music_loops && --music_loops ) {
slouken@213
   201
				Mix_Fading current_fade = music_playing->fading;
slouken@173
   202
				music_internal_play(music_playing, 0.0);
slouken@213
   203
				music_playing->fading = current_fade;
slouken@173
   204
			} else {
slouken@173
   205
				music_internal_halt();
slouken@173
   206
				if ( music_finished_hook ) {
slouken@173
   207
					music_finished_hook();
megastep@11
   208
				}
slouken@173
   209
				return;
megastep@11
   210
			}
megastep@16
   211
		}
slouken@0
   212
		switch (music_playing->type) {
slouken@0
   213
#ifdef CMD_MUSIC
slouken@0
   214
			case MUS_CMD:
slouken@0
   215
				/* The playing is done externally */
slouken@0
   216
				break;
slouken@0
   217
#endif
slouken@0
   218
#ifdef WAV_MUSIC
slouken@0
   219
			case MUS_WAV:
slouken@64
   220
				WAVStream_PlaySome(stream, len);
slouken@0
   221
				break;
slouken@0
   222
#endif
slouken@0
   223
#ifdef MOD_MUSIC
slouken@0
   224
			case MUS_MOD:
slouken@245
   225
				if (current_output_channels > 2) {
slouken@245
   226
					int small_len = 2 * len / current_output_channels;
slouken@245
   227
					int i;
slouken@245
   228
					Uint8 *src, *dst;
slouken@245
   229
slouken@245
   230
					VC_WriteBytes((SBYTE *)stream, small_len);
slouken@245
   231
					/* and extend to len by copying channels */
slouken@245
   232
					src = stream + small_len;
slouken@245
   233
					dst = stream + len;
slouken@245
   234
slouken@245
   235
					switch (current_output_format & 0xFF) {
slouken@245
   236
						case 8:
slouken@245
   237
							for ( i=small_len/2; i; --i ) {
slouken@245
   238
								src -= 2;
slouken@245
   239
								dst -= current_output_channels;
slouken@245
   240
								dst[0] = src[0];
slouken@245
   241
								dst[1] = src[1];
slouken@245
   242
								dst[2] = src[0];
slouken@245
   243
								dst[3] = src[1];
slouken@245
   244
								if (current_output_channels == 6) {
slouken@245
   245
									dst[4] = src[0];
slouken@245
   246
									dst[5] = src[1];
slouken@245
   247
								}
slouken@245
   248
							}
slouken@245
   249
							break;
slouken@245
   250
						case 16:
slouken@245
   251
							for ( i=small_len/4; i; --i ) {
slouken@245
   252
								src -= 4;
slouken@245
   253
								dst -= 2 * current_output_channels;
slouken@245
   254
								dst[0] = src[0];
slouken@245
   255
								dst[1] = src[1];
slouken@245
   256
								dst[2] = src[2];
slouken@245
   257
								dst[3] = src[3];
slouken@245
   258
								dst[4] = src[0];
slouken@245
   259
								dst[5] = src[1];
slouken@245
   260
								dst[6] = src[2];
slouken@245
   261
								dst[7] = src[3];
slouken@245
   262
								if (current_output_channels == 6) {
slouken@245
   263
									dst[8] = src[0];
slouken@245
   264
									dst[9] = src[1];
slouken@245
   265
									dst[10] = src[2];
slouken@245
   266
									dst[11] = src[3];
slouken@245
   267
								}
slouken@245
   268
							}
slouken@245
   269
							break;
slouken@245
   270
					}
slouken@245
   271
slouken@245
   272
slouken@245
   273
slouken@245
   274
				}
slouken@245
   275
				else VC_WriteBytes((SBYTE *)stream, len);
slouken@0
   276
				if ( music_swap8 ) {
slouken@0
   277
					Uint8 *dst;
slouken@42
   278
					int i;
slouken@0
   279
slouken@0
   280
					dst = stream;
slouken@0
   281
					for ( i=len; i; --i ) {
slouken@0
   282
						*dst++ ^= 0x80;
slouken@0
   283
					}
slouken@0
   284
				} else
slouken@0
   285
				if ( music_swap16 ) {
slouken@0
   286
					Uint8 *dst, tmp;
slouken@42
   287
					int i;
slouken@0
   288
slouken@0
   289
					dst = stream;
slouken@0
   290
					for ( i=(len/2); i; --i ) {
slouken@0
   291
						tmp = dst[0];
slouken@0
   292
						dst[0] = dst[1];
slouken@0
   293
						dst[1] = tmp;
slouken@0
   294
						dst += 2;
slouken@0
   295
					}
slouken@0
   296
				}
slouken@0
   297
				break;
slouken@0
   298
#endif
slouken@0
   299
#ifdef MID_MUSIC
slouken@106
   300
#ifdef USE_TIMIDITY_MIDI
slouken@0
   301
			case MUS_MID:
slouken@98
   302
				if ( timidity_ok ) {
slouken@98
   303
					int samples = len / samplesize;
slouken@98
   304
  					Timidity_PlaySome(stream, samples);
slouken@98
   305
				}
slouken@0
   306
				break;
slouken@0
   307
#endif
slouken@106
   308
#endif
slouken@63
   309
#ifdef OGG_MUSIC
slouken@63
   310
			case MUS_OGG:
slouken@63
   311
				OGG_playAudio(music_playing->data.ogg, stream, len);
slouken@63
   312
				break;
slouken@63
   313
#endif
slouken@0
   314
#ifdef MP3_MUSIC
slouken@26
   315
			case MUS_MP3:
slouken@26
   316
				SMPEG_playAudio(music_playing->data.mp3, stream, len);
slouken@0
   317
				break;
slouken@0
   318
#endif
slouken@0
   319
			default:
slouken@0
   320
				/* Unknown music type?? */
slouken@0
   321
				break;
slouken@0
   322
		}
slouken@0
   323
	}
slouken@0
   324
}
slouken@0
   325
slouken@0
   326
/* Initialize the music players with a certain desired audio format */
slouken@0
   327
int open_music(SDL_AudioSpec *mixer)
slouken@0
   328
{
slouken@0
   329
	int music_error;
slouken@0
   330
slouken@0
   331
	music_error = 0;
slouken@0
   332
#ifdef WAV_MUSIC
slouken@0
   333
	if ( WAVStream_Init(mixer) < 0 ) {
slouken@0
   334
		++music_error;
slouken@0
   335
	}
slouken@0
   336
#endif
slouken@0
   337
#ifdef MOD_MUSIC
slouken@0
   338
	/* Set the MikMod music format */
slouken@0
   339
	music_swap8 = 0;
slouken@0
   340
	music_swap16 = 0;
slouken@0
   341
	switch (mixer->format) {
slouken@0
   342
slouken@0
   343
		case AUDIO_U8:
slouken@0
   344
		case AUDIO_S8: {
slouken@0
   345
			if ( mixer->format == AUDIO_S8 ) {
slouken@0
   346
				music_swap8 = 1;
slouken@0
   347
			}
slouken@0
   348
			md_mode = 0;
slouken@0
   349
		}
slouken@0
   350
		break;
slouken@0
   351
slouken@0
   352
		case AUDIO_S16LSB:
slouken@0
   353
		case AUDIO_S16MSB: {
slouken@0
   354
			/* See if we need to correct MikMod mixing */
slouken@0
   355
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@0
   356
			if ( mixer->format == AUDIO_S16MSB ) {
slouken@0
   357
#else
slouken@0
   358
			if ( mixer->format == AUDIO_S16LSB ) {
slouken@0
   359
#endif
slouken@0
   360
				music_swap16 = 1;
slouken@0
   361
			}
slouken@0
   362
			md_mode = DMODE_16BITS;
slouken@0
   363
		}
slouken@0
   364
		break;
slouken@0
   365
slouken@0
   366
		default: {
slouken@26
   367
			Mix_SetError("Unknown hardware audio format");
slouken@0
   368
			++music_error;
slouken@0
   369
		}
slouken@0
   370
	}
slouken@245
   371
	current_output_channels = mixer->channels;
slouken@245
   372
	current_output_format = mixer->format;
slouken@0
   373
	if ( mixer->channels > 1 ) {
slouken@245
   374
		if ( mixer->channels > MAX_OUTPUT_CHANNELS ) {
slouken@26
   375
			Mix_SetError("Hardware uses more channels than mixer");
slouken@0
   376
			++music_error;
slouken@0
   377
		}
slouken@0
   378
		md_mode |= DMODE_STEREO;
slouken@0
   379
	}
slouken@26
   380
	md_mixfreq	 = mixer->freq;
slouken@26
   381
	md_device	  = 0;
slouken@26
   382
	md_volume	  = 96;
slouken@0
   383
	md_musicvolume = 128;
slouken@0
   384
	md_sndfxvolume = 128;
slouken@26
   385
	md_pansep	  = 128;
slouken@26
   386
	md_reverb	  = 0;
slouken@0
   387
	MikMod_RegisterAllLoaders();
slouken@0
   388
	MikMod_RegisterAllDrivers();
slouken@0
   389
	if ( MikMod_Init() ) {
slouken@29
   390
		Mix_SetError("%s", MikMod_strerror(MikMod_errno));
slouken@0
   391
		++music_error;
slouken@0
   392
	}
slouken@0
   393
#endif
slouken@0
   394
#ifdef MID_MUSIC
slouken@106
   395
#ifdef USE_TIMIDITY_MIDI
slouken@142
   396
	samplesize = mixer->size / mixer->samples;
slouken@100
   397
	if ( Timidity_Init(mixer->freq, mixer->format,
slouken@102
   398
	                    mixer->channels, mixer->samples) == 0 ) {
slouken@100
   399
		timidity_ok = 1;
slouken@0
   400
	} else {
slouken@0
   401
		timidity_ok = 0;
slouken@0
   402
	}
slouken@106
   403
#endif
slouken@100
   404
#ifdef USE_NATIVE_MIDI
slouken@106
   405
#ifdef USE_TIMIDITY_MIDI
slouken@100
   406
	native_midi_ok = !timidity_ok;
slouken@106
   407
	if ( native_midi_ok )
slouken@106
   408
#endif
slouken@101
   409
		native_midi_ok = native_midi_detect();
slouken@100
   410
#endif
slouken@0
   411
#endif
slouken@63
   412
#ifdef OGG_MUSIC
slouken@63
   413
	if ( OGG_init(mixer) < 0 ) {
slouken@63
   414
		++music_error;
slouken@63
   415
	}
slouken@63
   416
#endif
slouken@0
   417
#ifdef MP3_MUSIC
slouken@0
   418
	/* Keep a copy of the mixer */
slouken@0
   419
	used_mixer = *mixer;
slouken@0
   420
#endif
slouken@42
   421
	music_playing = NULL;
megastep@7
   422
	music_stopped = 0;
slouken@0
   423
	if ( music_error ) {
slouken@0
   424
		return(-1);
slouken@0
   425
	}
slouken@0
   426
	Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
slouken@0
   427
slouken@26
   428
	/* Calculate the number of ms for each callback */
slouken@44
   429
	ms_per_step = (int) (((float)mixer->samples * 1000.0) / mixer->freq);
slouken@17
   430
slouken@0
   431
	return(0);
slouken@0
   432
}
slouken@0
   433
slouken@125
   434
/* Portable case-insensitive string compare function */
slouken@125
   435
int MIX_string_equals(const char *str1, const char *str2)
slouken@125
   436
{
slouken@125
   437
	while ( *str1 && *str2 ) {
slouken@125
   438
		if ( toupper((unsigned char)*str1) !=
slouken@125
   439
		     toupper((unsigned char)*str2) )
slouken@125
   440
			break;
slouken@125
   441
		++str1;
slouken@125
   442
		++str2;
slouken@125
   443
	}
slouken@125
   444
	return (!*str1 && !*str2);
slouken@125
   445
}
slouken@125
   446
slouken@0
   447
/* Load a music file */
slouken@0
   448
Mix_Music *Mix_LoadMUS(const char *file)
slouken@0
   449
{
slouken@0
   450
	FILE *fp;
slouken@125
   451
	char *ext;
slouken@245
   452
	Uint8 magic[5], moremagic[9];
slouken@0
   453
	Mix_Music *music;
slouken@0
   454
slouken@0
   455
	/* Figure out what kind of file this is */
slouken@0
   456
	fp = fopen(file, "rb");
slouken@0
   457
	if ( (fp == NULL) || !fread(magic, 4, 1, fp) ) {
slouken@0
   458
		if ( fp != NULL ) {
slouken@0
   459
			fclose(fp);
slouken@0
   460
		}
slouken@26
   461
		Mix_SetError("Couldn't read from '%s'", file);
slouken@0
   462
		return(NULL);
slouken@0
   463
	}
slouken@245
   464
	if (!fread(moremagic, 8, 1, fp)) {
slouken@245
   465
		Mix_SetError("Couldn't read from '%s'", file);
slouken@245
   466
		return(NULL);
slouken@245
   467
	}
slouken@0
   468
	magic[4] = '\0';
slouken@245
   469
	moremagic[8] = '\0';
slouken@0
   470
	fclose(fp);
slouken@0
   471
slouken@125
   472
	/* Figure out the file extension, so we can determine the type */
slouken@125
   473
	ext = strrchr(file, '.');
slouken@125
   474
	if ( ext ) ++ext; /* skip the dot in the extension */
slouken@125
   475
slouken@0
   476
	/* Allocate memory for the music structure */
slouken@0
   477
	music = (Mix_Music *)malloc(sizeof(Mix_Music));
slouken@0
   478
	if ( music == NULL ) {
slouken@26
   479
		Mix_SetError("Out of memory");
slouken@0
   480
		return(NULL);
slouken@0
   481
	}
slouken@0
   482
	music->error = 0;
slouken@0
   483
slouken@0
   484
#ifdef CMD_MUSIC
slouken@0
   485
	if ( music_cmd ) {
slouken@0
   486
		music->type = MUS_CMD;
slouken@0
   487
		music->data.cmd = MusicCMD_LoadSong(music_cmd, file);
slouken@0
   488
		if ( music->data.cmd == NULL ) {
slouken@0
   489
			music->error = 1;
slouken@0
   490
		}
slouken@0
   491
	} else
slouken@0
   492
#endif
slouken@0
   493
#ifdef WAV_MUSIC
slouken@0
   494
	/* WAVE files have the magic four bytes "RIFF"
slouken@0
   495
	   AIFF files have the magic 12 bytes "FORM" XXXX "AIFF"
slouken@0
   496
	 */
slouken@135
   497
	if ( (ext && MIX_string_equals(ext, "WAV")) ||
slouken@245
   498
	     ((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) ||
slouken@57
   499
	     (strcmp((char *)magic, "FORM") == 0) ) {
slouken@0
   500
		music->type = MUS_WAV;
slouken@57
   501
		music->data.wave = WAVStream_LoadSong(file, (char *)magic);
slouken@0
   502
		if ( music->data.wave == NULL ) {
slouken@135
   503
		  	Mix_SetError("Unable to load WAV file");
slouken@0
   504
			music->error = 1;
slouken@0
   505
		}
slouken@0
   506
	} else
slouken@0
   507
#endif
slouken@0
   508
#ifdef MID_MUSIC
slouken@0
   509
	/* MIDI files have the magic four bytes "MThd" */
slouken@135
   510
	if ( (ext && MIX_string_equals(ext, "MID")) ||
slouken@135
   511
	     (ext && MIX_string_equals(ext, "MIDI")) ||
slouken@245
   512
	     strcmp((char *)magic, "MThd") == 0  ||
slouken@245
   513
	     ( strcmp((char *)magic, "RIFF") == 0  &&
slouken@245
   514
	  	strcmp((char *)(moremagic+4), "RMID") == 0 ) ) {
slouken@0
   515
		music->type = MUS_MID;
slouken@98
   516
#ifdef USE_NATIVE_MIDI
slouken@98
   517
  		if ( native_midi_ok ) {
slouken@98
   518
  			music->data.nativemidi = native_midi_loadsong((char *)file);
slouken@98
   519
	  		if ( music->data.nativemidi == NULL ) {
slouken@98
   520
		  		Mix_SetError("%s", native_midi_error());
slouken@98
   521
			  	music->error = 1;
slouken@98
   522
			}
slouken@106
   523
	  	} MIDI_ELSE
slouken@98
   524
#endif
slouken@106
   525
#ifdef USE_TIMIDITY_MIDI
slouken@0
   526
		if ( timidity_ok ) {
slouken@0
   527
			music->data.midi = Timidity_LoadSong((char *)file);
slouken@0
   528
			if ( music->data.midi == NULL ) {
slouken@26
   529
				Mix_SetError("%s", Timidity_Error());
slouken@0
   530
				music->error = 1;
slouken@0
   531
			}
slouken@98
   532
		} else {
slouken@26
   533
			Mix_SetError("%s", Timidity_Error());
slouken@0
   534
			music->error = 1;
slouken@0
   535
		}
slouken@106
   536
#endif
slouken@0
   537
	} else
slouken@0
   538
#endif
slouken@63
   539
#ifdef OGG_MUSIC
slouken@63
   540
	/* Ogg Vorbis files have the magic four bytes "OggS" */
slouken@135
   541
	if ( (ext && MIX_string_equals(ext, "OGG")) ||
slouken@133
   542
	     strcmp((char *)magic, "OggS") == 0 ) {
slouken@63
   543
		music->type = MUS_OGG;
slouken@63
   544
		music->data.ogg = OGG_new(file);
slouken@63
   545
		if ( music->data.ogg == NULL ) {
slouken@63
   546
			music->error = 1;
slouken@63
   547
		}
slouken@63
   548
	} else
slouken@63
   549
#endif
slouken@0
   550
#ifdef MP3_MUSIC
slouken@135
   551
	if ( (ext && MIX_string_equals(ext, "MPG")) ||
icculus@186
   552
	     (ext && MIX_string_equals(ext, "MP3")) ||
slouken@135
   553
	     (ext && MIX_string_equals(ext, "MPEG")) ||
slouken@125
   554
	     magic[0]==0xFF && (magic[1]&0xF0)==0xF0) {
slouken@0
   555
		SMPEG_Info info;
slouken@0
   556
		music->type = MUS_MP3;
slouken@0
   557
		music->data.mp3 = SMPEG_new(file, &info, 0);
slouken@0
   558
		if(!info.has_audio){
slouken@26
   559
			Mix_SetError("MPEG file does not have any audio stream.");
slouken@0
   560
			music->error = 1;
slouken@0
   561
		}else{
slouken@0
   562
			SMPEG_actualSpec(music->data.mp3, &used_mixer);
slouken@0
   563
		}
slouken@0
   564
	} else
slouken@0
   565
#endif
slouken@0
   566
#ifdef MOD_MUSIC
slouken@0
   567
	if ( 1 ) {
slouken@0
   568
		music->type = MUS_MOD;
slouken@0
   569
		music->data.module = MikMod_LoadSong((char *)file, 64);
slouken@0
   570
		if ( music->data.module == NULL ) {
slouken@29
   571
			Mix_SetError("%s", MikMod_strerror(MikMod_errno));
slouken@0
   572
			music->error = 1;
slouken@126
   573
		} else {
slouken@126
   574
			/* Stop implicit looping, fade out and other flags. */
slouken@126
   575
			music->data.module->extspd  = 1;
slouken@126
   576
			music->data.module->panflag = 1;
slouken@126
   577
			music->data.module->wrap    = 0;
slouken@126
   578
			music->data.module->loop    = 0;
slouken@129
   579
#if 0 /* Don't set fade out by default - unfortunately there's no real way
slouken@129
   580
         to query the status of the song or set trigger actions.  Hum. */
slouken@126
   581
			music->data.module->fadeout = 1;
slouken@129
   582
#endif
slouken@0
   583
		}
slouken@0
   584
	} else
slouken@0
   585
#endif
slouken@0
   586
	{
slouken@26
   587
		Mix_SetError("Unrecognized music format");
slouken@0
   588
		music->error = 1;
slouken@0
   589
	}
slouken@0
   590
	if ( music->error ) {
slouken@0
   591
		free(music);
slouken@0
   592
		music = NULL;
slouken@0
   593
	}
slouken@0
   594
	return(music);
slouken@0
   595
}
slouken@0
   596
slouken@0
   597
/* Free a music chunk previously loaded */
slouken@0
   598
void Mix_FreeMusic(Mix_Music *music)
slouken@0
   599
{
slouken@0
   600
	if ( music ) {
slouken@173
   601
		/* Stop the music if it's currently playing */
slouken@173
   602
		SDL_LockAudio();
slouken@173
   603
		if ( music == music_playing ) {
slouken@173
   604
			/* Wait for any fade out to finish */
slouken@173
   605
			while ( music->fading == MIX_FADING_OUT ) {
slouken@173
   606
				SDL_UnlockAudio();
slouken@173
   607
				SDL_Delay(100);
slouken@173
   608
				SDL_LockAudio();
slouken@173
   609
			}
slouken@173
   610
			if ( music == music_playing ) {
slouken@173
   611
				music_internal_halt();
megastep@4
   612
			}
slouken@0
   613
		}
slouken@173
   614
		SDL_UnlockAudio();
slouken@0
   615
		switch (music->type) {
slouken@0
   616
#ifdef CMD_MUSIC
slouken@0
   617
			case MUS_CMD:
slouken@0
   618
				MusicCMD_FreeSong(music->data.cmd);
slouken@0
   619
				break;
slouken@0
   620
#endif
slouken@0
   621
#ifdef WAV_MUSIC
slouken@0
   622
			case MUS_WAV:
slouken@0
   623
				WAVStream_FreeSong(music->data.wave);
slouken@0
   624
				break;
slouken@0
   625
#endif
slouken@0
   626
#ifdef MOD_MUSIC
slouken@0
   627
			case MUS_MOD:
slouken@0
   628
				MikMod_FreeSong(music->data.module);
slouken@0
   629
				break;
slouken@0
   630
#endif
slouken@0
   631
#ifdef MID_MUSIC
slouken@0
   632
			case MUS_MID:
slouken@98
   633
#ifdef USE_NATIVE_MIDI
slouken@98
   634
  				if ( native_midi_ok ) {
slouken@98
   635
					native_midi_freesong(music->data.nativemidi);
slouken@106
   636
				} MIDI_ELSE
slouken@98
   637
#endif
slouken@106
   638
#ifdef USE_TIMIDITY_MIDI
slouken@98
   639
				if ( timidity_ok ) {
slouken@98
   640
					Timidity_FreeSong(music->data.midi);
slouken@98
   641
				}
slouken@106
   642
#endif
slouken@0
   643
				break;
slouken@0
   644
#endif
slouken@63
   645
#ifdef OGG_MUSIC
slouken@63
   646
			case MUS_OGG:
slouken@63
   647
				OGG_delete(music->data.ogg);
slouken@63
   648
				break;
slouken@63
   649
#endif
slouken@0
   650
#ifdef MP3_MUSIC
slouken@26
   651
			case MUS_MP3:
slouken@0
   652
				SMPEG_delete(music->data.mp3);
slouken@0
   653
				break;
slouken@0
   654
#endif
slouken@0
   655
			default:
slouken@0
   656
				/* Unknown music type?? */
slouken@0
   657
				break;
slouken@0
   658
		}
slouken@0
   659
		free(music);
slouken@0
   660
	}
slouken@0
   661
}
slouken@0
   662
slouken@177
   663
/* Find out the music format of a mixer music, or the currently playing
slouken@177
   664
   music, if 'music' is NULL.
slouken@177
   665
*/
slouken@177
   666
Mix_MusicType Mix_GetMusicType(const Mix_Music *music)
slouken@177
   667
{
slouken@177
   668
	Mix_MusicType type = MUS_NONE;
slouken@177
   669
slouken@177
   670
	if ( music ) {
slouken@177
   671
		type = music->type;
slouken@177
   672
	} else {
slouken@177
   673
		SDL_LockAudio();
slouken@177
   674
		if ( music_playing ) {
slouken@177
   675
			type = music_playing->type;
slouken@177
   676
		}
slouken@177
   677
		SDL_UnlockAudio();
slouken@177
   678
	}
slouken@177
   679
	return(type);
slouken@177
   680
}
slouken@177
   681
slouken@173
   682
/* Play a music chunk.  Returns 0, or -1 if there was an error.
slouken@173
   683
 */
slouken@173
   684
static int music_internal_play(Mix_Music *music, double position)
slouken@0
   685
{
slouken@173
   686
	int retval = 0;
megastep@10
   687
slouken@173
   688
	/* Note the music we're playing */
slouken@173
   689
	if ( music_playing ) {
slouken@173
   690
		music_internal_halt();
slouken@173
   691
	}
slouken@173
   692
	music_playing = music;
slouken@173
   693
slouken@173
   694
	/* Set the initial volume */
icculus@237
   695
	if ( music->type != MUS_MOD ) {
icculus@237
   696
		music_internal_initialize_volume();
slouken@173
   697
	}
slouken@173
   698
slouken@173
   699
	/* Set up for playback */
slouken@0
   700
	switch (music->type) {
slouken@0
   701
#ifdef CMD_MUSIC
slouken@173
   702
	    case MUS_CMD:
slouken@173
   703
		MusicCMD_Start(music->data.cmd);
slouken@173
   704
		break;
slouken@0
   705
#endif
slouken@0
   706
#ifdef WAV_MUSIC
slouken@173
   707
	    case MUS_WAV:
slouken@173
   708
		WAVStream_Start(music->data.wave);
slouken@173
   709
		break;
slouken@0
   710
#endif
slouken@0
   711
#ifdef MOD_MUSIC
slouken@173
   712
	    case MUS_MOD:
slouken@173
   713
		Player_Start(music->data.module);
icculus@237
   714
		/* Player_SetVolume() does nothing before Player_Start() */
icculus@237
   715
		music_internal_initialize_volume();
slouken@173
   716
		break;
slouken@0
   717
#endif
slouken@0
   718
#ifdef MID_MUSIC
slouken@173
   719
	    case MUS_MID:
slouken@98
   720
#ifdef USE_NATIVE_MIDI
slouken@173
   721
		if ( native_midi_ok ) {
slouken@173
   722
			native_midi_start(music->data.nativemidi);
slouken@173
   723
		} MIDI_ELSE
slouken@98
   724
#endif
slouken@106
   725
#ifdef USE_TIMIDITY_MIDI
slouken@173
   726
		if ( timidity_ok ) {
slouken@173
   727
			Timidity_Start(music->data.midi);
slouken@173
   728
		}
slouken@106
   729
#endif
slouken@173
   730
		break;
slouken@0
   731
#endif
slouken@63
   732
#ifdef OGG_MUSIC
slouken@173
   733
	    case MUS_OGG:
slouken@173
   734
		OGG_play(music->data.ogg);
slouken@173
   735
		break;
slouken@63
   736
#endif
slouken@0
   737
#ifdef MP3_MUSIC
slouken@173
   738
	    case MUS_MP3:
slouken@173
   739
		SMPEG_enableaudio(music->data.mp3,1);
slouken@173
   740
		SMPEG_enablevideo(music->data.mp3,0);
slouken@179
   741
		SMPEG_play(music_playing->data.mp3);
slouken@173
   742
		break;
slouken@0
   743
#endif
slouken@173
   744
	    default:
slouken@173
   745
		Mix_SetError("Can't play unknown music type");
slouken@173
   746
		retval = -1;
slouken@173
   747
		break;
slouken@0
   748
	}
slouken@173
   749
slouken@173
   750
	/* Set the playback position, note any errors if an offset is used */
slouken@173
   751
	if ( retval == 0 ) {
slouken@173
   752
		if ( position > 0.0 ) {
slouken@173
   753
			if ( music_internal_position(position) < 0 ) {
slouken@173
   754
				Mix_SetError("Position not implemented for music type");
slouken@173
   755
				retval = -1;
slouken@173
   756
			}
slouken@173
   757
		} else {
slouken@173
   758
			music_internal_position(0.0);
slouken@173
   759
		}
slouken@173
   760
	}
slouken@173
   761
slouken@173
   762
	/* If the setup failed, we're not playing any music anymore */
slouken@173
   763
	if ( retval < 0 ) {
slouken@173
   764
		music_playing = NULL;
slouken@173
   765
	}
slouken@173
   766
	return(retval);
slouken@173
   767
}
slouken@173
   768
int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
slouken@173
   769
{
slouken@173
   770
	int retval;
slouken@173
   771
slouken@173
   772
	/* Don't play null pointers :-) */
slouken@173
   773
	if ( music == NULL ) {
slouken@173
   774
		Mix_SetError("music parameter was NULL");
slouken@173
   775
		return(-1);
slouken@173
   776
	}
slouken@173
   777
slouken@173
   778
	/* Setup the data */
slouken@173
   779
	if ( ms ) {
slouken@173
   780
		music->fading = MIX_FADING_IN;
slouken@173
   781
	} else {
slouken@173
   782
		music->fading = MIX_NO_FADING;
slouken@173
   783
	}
slouken@173
   784
	music->fade_step = 0;
slouken@173
   785
	music->fade_steps = ms/ms_per_step;
slouken@173
   786
slouken@173
   787
	/* Play the puppy */
slouken@173
   788
	SDL_LockAudio();
slouken@173
   789
	/* If the current music is fading out, wait for the fade to complete */
slouken@173
   790
	while ( music_playing && (music_playing->fading == MIX_FADING_OUT) ) {
slouken@173
   791
		SDL_UnlockAudio();
slouken@173
   792
		SDL_Delay(100);
slouken@173
   793
		SDL_LockAudio();
slouken@173
   794
	}
slouken@173
   795
	music_active = 1;
slouken@173
   796
	music_loops = loops;
slouken@173
   797
	retval = music_internal_play(music, position);
slouken@173
   798
	SDL_UnlockAudio();
slouken@173
   799
slouken@173
   800
	return(retval);
slouken@173
   801
}
slouken@173
   802
int Mix_FadeInMusic(Mix_Music *music, int loops, int ms)
slouken@173
   803
{
slouken@173
   804
	return Mix_FadeInMusicPos(music, loops, ms, 0.0);
slouken@173
   805
}
slouken@173
   806
int Mix_PlayMusic(Mix_Music *music, int loops)
slouken@173
   807
{
slouken@173
   808
	return Mix_FadeInMusicPos(music, loops, 0, 0.0);
megastep@10
   809
}
megastep@10
   810
slouken@173
   811
/* Set the playing music position */
slouken@173
   812
int music_internal_position(double position)
megastep@10
   813
{
slouken@173
   814
	int retval = 0;
slouken@173
   815
slouken@173
   816
	switch (music_playing->type) {
slouken@173
   817
#ifdef MOD_MUSIC
slouken@173
   818
	    case MUS_MOD:
slouken@173
   819
		Player_SetPosition((UWORD)position);
slouken@173
   820
		break;
slouken@173
   821
#endif
slouken@173
   822
#ifdef OGG_MUSIC
slouken@173
   823
	    case MUS_OGG:
slouken@173
   824
		OGG_jump_to_time(music_playing->data.ogg, position);
slouken@173
   825
		break;
slouken@173
   826
#endif
slouken@173
   827
#ifdef MP3_MUSIC
slouken@173
   828
	    case MUS_MP3:
slouken@176
   829
		if ( position > 0.0 ) {
slouken@173
   830
			SMPEG_skip(music_playing->data.mp3, position);
slouken@179
   831
		} else {
slouken@179
   832
			SMPEG_rewind(music_playing->data.mp3);
slouken@179
   833
			SMPEG_play(music_playing->data.mp3);
slouken@173
   834
		}
slouken@173
   835
		break;
slouken@173
   836
#endif
slouken@173
   837
	    default:
slouken@173
   838
		/* TODO: Implement this for other music backends */
slouken@173
   839
		retval = -1;
slouken@173
   840
		break;
megastep@10
   841
	}
slouken@173
   842
	return(retval);
megastep@4
   843
}
slouken@155
   844
int Mix_SetMusicPosition(double position)
slouken@154
   845
{
slouken@173
   846
	int retval;
slouken@173
   847
slouken@173
   848
	SDL_LockAudio();
slouken@173
   849
	if ( music_playing ) {
slouken@173
   850
		retval = music_internal_position(position);
slouken@173
   851
		if ( retval < 0 ) {
slouken@173
   852
			Mix_SetError("Position not implemented for music type");
slouken@154
   853
		}
slouken@173
   854
	} else {
slouken@173
   855
		Mix_SetError("Music isn't playing");
slouken@173
   856
		retval = -1;
slouken@154
   857
	}
slouken@173
   858
	SDL_UnlockAudio();
slouken@154
   859
slouken@173
   860
	return(retval);
slouken@154
   861
}
slouken@154
   862
icculus@237
   863
/* Set the music's initial volume */
icculus@237
   864
static void music_internal_initialize_volume(void)
icculus@237
   865
{
icculus@237
   866
	if ( music_playing->fading == MIX_FADING_IN ) {
icculus@237
   867
		music_internal_volume(0);
icculus@237
   868
	} else {
icculus@237
   869
		music_internal_volume(music_volume);
icculus@237
   870
	}
icculus@237
   871
}
icculus@237
   872
slouken@0
   873
/* Set the music volume */
slouken@173
   874
static void music_internal_volume(int volume)
slouken@173
   875
{
slouken@173
   876
	switch (music_playing->type) {
slouken@173
   877
#ifdef CMD_MUSIC
slouken@173
   878
	    case MUS_CMD:
slouken@173
   879
		MusicCMD_SetVolume(volume);
slouken@173
   880
		break;
slouken@173
   881
#endif
slouken@173
   882
#ifdef WAV_MUSIC
slouken@173
   883
	    case MUS_WAV:
slouken@173
   884
		WAVStream_SetVolume(volume);
slouken@173
   885
		break;
slouken@173
   886
#endif
slouken@173
   887
#ifdef MOD_MUSIC
slouken@173
   888
	    case MUS_MOD:
slouken@173
   889
		Player_SetVolume((SWORD)volume);
slouken@173
   890
		break;
slouken@173
   891
#endif
slouken@173
   892
#ifdef MID_MUSIC
slouken@173
   893
	    case MUS_MID:
slouken@173
   894
#ifdef USE_NATIVE_MIDI
slouken@173
   895
		if ( native_midi_ok ) {
slouken@173
   896
			native_midi_setvolume(volume);
slouken@173
   897
		} MIDI_ELSE
slouken@173
   898
#endif
slouken@173
   899
#ifdef USE_TIMIDITY_MIDI
slouken@173
   900
		if ( timidity_ok ) {
slouken@173
   901
			Timidity_SetVolume(volume);
slouken@173
   902
		}
slouken@173
   903
#endif
slouken@173
   904
		break;
slouken@173
   905
#endif
slouken@173
   906
#ifdef OGG_MUSIC
slouken@173
   907
	    case MUS_OGG:
slouken@173
   908
		OGG_setvolume(music_playing->data.ogg, volume);
slouken@173
   909
		break;
slouken@173
   910
#endif
slouken@173
   911
#ifdef MP3_MUSIC
slouken@173
   912
	    case MUS_MP3:
slouken@173
   913
		SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0));
slouken@173
   914
		break;
slouken@173
   915
#endif
slouken@173
   916
	    default:
slouken@173
   917
		/* Unknown music type?? */
slouken@173
   918
		break;
slouken@173
   919
	}
slouken@173
   920
}
slouken@0
   921
int Mix_VolumeMusic(int volume)
slouken@0
   922
{
slouken@0
   923
	int prev_volume;
slouken@0
   924
slouken@0
   925
	prev_volume = music_volume;
megastep@4
   926
	if ( volume < 0 ) {
slouken@163
   927
		return prev_volume;
megastep@4
   928
	}
megastep@4
   929
	if ( volume > SDL_MIX_MAXVOLUME ) {
megastep@4
   930
		volume = SDL_MIX_MAXVOLUME;
megastep@4
   931
	}
megastep@4
   932
	music_volume = volume;
slouken@173
   933
	SDL_LockAudio();
slouken@173
   934
	if ( music_playing ) {
slouken@173
   935
		music_internal_volume(music_volume);
slouken@0
   936
	}
slouken@173
   937
	SDL_UnlockAudio();
slouken@0
   938
	return(prev_volume);
slouken@0
   939
}
slouken@0
   940
slouken@173
   941
/* Halt playing of music */
slouken@173
   942
static void music_internal_halt(void)
megastep@7
   943
{
megastep@7
   944
	switch (music_playing->type) {
megastep@7
   945
#ifdef CMD_MUSIC
slouken@173
   946
	    case MUS_CMD:
megastep@7
   947
		MusicCMD_Stop(music_playing->data.cmd);
megastep@7
   948
		break;
megastep@7
   949
#endif
megastep@7
   950
#ifdef WAV_MUSIC
slouken@173
   951
	    case MUS_WAV:
megastep@7
   952
		WAVStream_Stop();
megastep@7
   953
		break;
megastep@7
   954
#endif
megastep@7
   955
#ifdef MOD_MUSIC
slouken@173
   956
	    case MUS_MOD:
megastep@7
   957
		Player_Stop();
megastep@7
   958
		break;
megastep@7
   959
#endif
megastep@7
   960
#ifdef MID_MUSIC
slouken@173
   961
	    case MUS_MID:
slouken@98
   962
#ifdef USE_NATIVE_MIDI
slouken@98
   963
		if ( native_midi_ok ) {
slouken@98
   964
			native_midi_stop();
slouken@106
   965
		} MIDI_ELSE
slouken@98
   966
#endif
slouken@106
   967
#ifdef USE_TIMIDITY_MIDI
slouken@98
   968
		if ( timidity_ok ) {
slouken@98
   969
			Timidity_Stop();
slouken@98
   970
		}
slouken@106
   971
#endif
megastep@7
   972
		break;
megastep@7
   973
#endif
slouken@63
   974
#ifdef OGG_MUSIC
slouken@173
   975
	    case MUS_OGG:
slouken@63
   976
		OGG_stop(music_playing->data.ogg);
slouken@63
   977
		break;
slouken@63
   978
#endif
megastep@7
   979
#ifdef MP3_MUSIC
slouken@173
   980
	    case MUS_MP3:
megastep@7
   981
		SMPEG_stop(music_playing->data.mp3);
megastep@7
   982
		break;
megastep@7
   983
#endif
slouken@173
   984
	    default:
megastep@7
   985
		/* Unknown music type?? */
megastep@7
   986
		return;
megastep@7
   987
	}
megastep@7
   988
	music_playing->fading = MIX_NO_FADING;
slouken@17
   989
	music_playing = NULL;
megastep@7
   990
}
slouken@0
   991
int Mix_HaltMusic(void)
slouken@0
   992
{
slouken@173
   993
	SDL_LockAudio();
slouken@173
   994
	if ( music_playing ) {
slouken@173
   995
		music_internal_halt();
slouken@0
   996
	}
slouken@173
   997
	SDL_UnlockAudio();
slouken@173
   998
slouken@0
   999
	return(0);
slouken@0
  1000
}
slouken@0
  1001
megastep@4
  1002
/* Progressively stop the music */
megastep@4
  1003
int Mix_FadeOutMusic(int ms)
megastep@4
  1004
{
slouken@173
  1005
	int retval = 0;
slouken@173
  1006
slouken@173
  1007
	SDL_LockAudio();
slouken@173
  1008
	if ( music_playing && (music_playing->fading == MIX_NO_FADING) ) {
slouken@173
  1009
		music_playing->fading = MIX_FADING_OUT;
slouken@173
  1010
		music_playing->fade_step = 0;
slouken@173
  1011
		music_playing->fade_steps = ms/ms_per_step;
slouken@173
  1012
		retval = 1;
megastep@4
  1013
	}
slouken@173
  1014
	SDL_UnlockAudio();
slouken@173
  1015
slouken@173
  1016
	return(retval);
megastep@4
  1017
}
megastep@4
  1018
megastep@4
  1019
Mix_Fading Mix_FadingMusic(void)
megastep@4
  1020
{
slouken@173
  1021
	Mix_Fading fading = MIX_NO_FADING;
slouken@173
  1022
slouken@173
  1023
	SDL_LockAudio();
slouken@173
  1024
	if ( music_playing ) {
slouken@173
  1025
		fading = music_playing->fading;
slouken@173
  1026
	}
slouken@173
  1027
	SDL_UnlockAudio();
slouken@173
  1028
slouken@173
  1029
	return(fading);
megastep@4
  1030
}
megastep@4
  1031
slouken@0
  1032
/* Pause/Resume the music stream */
slouken@0
  1033
void Mix_PauseMusic(void)
slouken@0
  1034
{
slouken@173
  1035
	music_active = 0;
slouken@0
  1036
}
megastep@4
  1037
slouken@0
  1038
void Mix_ResumeMusic(void)
slouken@0
  1039
{
slouken@173
  1040
	music_active = 1;
slouken@0
  1041
}
slouken@0
  1042
slouken@0
  1043
void Mix_RewindMusic(void)
slouken@0
  1044
{
slouken@173
  1045
	Mix_SetMusicPosition(0.0);
slouken@0
  1046
}
slouken@0
  1047
megastep@13
  1048
int Mix_PausedMusic(void)
megastep@13
  1049
{
slouken@17
  1050
	return (music_active == 0);
megastep@13
  1051
}
megastep@13
  1052
slouken@0
  1053
/* Check the status of the music */
slouken@173
  1054
static int music_internal_playing()
slouken@173
  1055
{
slouken@173
  1056
	int playing = 1;
slouken@173
  1057
	switch (music_playing->type) {
slouken@173
  1058
#ifdef CMD_MUSIC
slouken@173
  1059
	    case MUS_CMD:
slouken@173
  1060
		if (!MusicCMD_Active(music_playing->data.cmd)) {
slouken@173
  1061
			playing = 0;
slouken@173
  1062
		}
slouken@173
  1063
		break;
slouken@173
  1064
#endif
slouken@173
  1065
#ifdef WAV_MUSIC
slouken@173
  1066
	    case MUS_WAV:
slouken@173
  1067
		if ( ! WAVStream_Active() ) {
slouken@173
  1068
			playing = 0;
slouken@173
  1069
		}
slouken@173
  1070
		break;
slouken@173
  1071
#endif
slouken@173
  1072
#ifdef MOD_MUSIC
slouken@173
  1073
	    case MUS_MOD:
slouken@173
  1074
		if ( ! Player_Active() ) {
slouken@173
  1075
			playing = 0;
slouken@173
  1076
		}
slouken@173
  1077
		break;
slouken@173
  1078
#endif
slouken@173
  1079
#ifdef MID_MUSIC
slouken@173
  1080
	    case MUS_MID:
slouken@173
  1081
#ifdef USE_NATIVE_MIDI
slouken@173
  1082
		if ( native_midi_ok ) {
slouken@173
  1083
			if ( ! native_midi_active() )
slouken@173
  1084
				playing = 0;
slouken@173
  1085
		} MIDI_ELSE
slouken@173
  1086
#endif
slouken@173
  1087
#ifdef USE_TIMIDITY_MIDI
slouken@173
  1088
		if ( timidity_ok ) {
slouken@173
  1089
			if ( ! Timidity_Active() )
slouken@173
  1090
				playing = 0;
slouken@173
  1091
		}
slouken@173
  1092
#endif
slouken@173
  1093
		break;
slouken@173
  1094
#endif
slouken@173
  1095
#ifdef OGG_MUSIC
slouken@173
  1096
	    case MUS_OGG:
slouken@173
  1097
		if ( ! OGG_playing(music_playing->data.ogg) ) {
slouken@173
  1098
			playing = 0;
slouken@173
  1099
		}
slouken@173
  1100
		break;
slouken@173
  1101
#endif
slouken@173
  1102
#ifdef MP3_MUSIC
slouken@173
  1103
	    case MUS_MP3:
slouken@173
  1104
		if ( SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING )
slouken@173
  1105
			playing = 0;
slouken@173
  1106
		break;
slouken@173
  1107
#endif
slouken@173
  1108
	    default:
slouken@173
  1109
		playing = 0;
slouken@173
  1110
		break;
slouken@173
  1111
	}
slouken@173
  1112
	return(playing);
slouken@173
  1113
}
megastep@13
  1114
int Mix_PlayingMusic(void)
slouken@0
  1115
{
slouken@173
  1116
	int playing = 0;
slouken@173
  1117
slouken@173
  1118
	SDL_LockAudio();
slouken@173
  1119
	if ( music_playing ) {
slouken@173
  1120
		playing = music_internal_playing();
slouken@0
  1121
	}
slouken@173
  1122
	SDL_UnlockAudio();
slouken@173
  1123
slouken@173
  1124
	return(playing);
slouken@0
  1125
}
slouken@0
  1126
slouken@0
  1127
/* Set the external music playback command */
slouken@0
  1128
int Mix_SetMusicCMD(const char *command)
slouken@0
  1129
{
slouken@0
  1130
	Mix_HaltMusic();
slouken@0
  1131
	if ( music_cmd ) {
slouken@0
  1132
		free(music_cmd);
slouken@0
  1133
		music_cmd = NULL;
slouken@0
  1134
	}
slouken@0
  1135
	if ( command ) {
slouken@0
  1136
		music_cmd = (char *)malloc(strlen(command)+1);
slouken@0
  1137
		if ( music_cmd == NULL ) {
slouken@0
  1138
			return(-1);
slouken@0
  1139
		}
slouken@0
  1140
		strcpy(music_cmd, command);
slouken@0
  1141
	}
slouken@0
  1142
	return(0);
slouken@0
  1143
}
slouken@0
  1144
slouken@154
  1145
int Mix_SetSynchroValue(int i)
slouken@154
  1146
{
slouken@154
  1147
	if ( music_playing && ! music_stopped ) {
slouken@154
  1148
		switch (music_playing->type) {
slouken@154
  1149
#ifdef MOD_MUSIC
slouken@173
  1150
		    case MUS_MOD:
slouken@173
  1151
			if ( ! Player_Active() ) {
slouken@173
  1152
				return(-1);
slouken@173
  1153
			}
slouken@173
  1154
			Player_SetSynchroValue(i);
slouken@173
  1155
			return 0;
slouken@173
  1156
			break;
slouken@154
  1157
#endif
slouken@173
  1158
		    default:
slouken@173
  1159
			return(-1);
slouken@173
  1160
			break;
slouken@154
  1161
		}
slouken@154
  1162
		return(-1);
slouken@154
  1163
	}
slouken@154
  1164
	return(-1);
slouken@154
  1165
}
slouken@154
  1166
slouken@154
  1167
int Mix_GetSynchroValue(void)
slouken@154
  1168
{
slouken@154
  1169
	if ( music_playing && ! music_stopped ) {
slouken@154
  1170
		switch (music_playing->type) {
slouken@154
  1171
#ifdef MOD_MUSIC
slouken@173
  1172
		    case MUS_MOD:
slouken@173
  1173
			if ( ! Player_Active() ) {
slouken@173
  1174
				return(-1);
slouken@173
  1175
			}
slouken@173
  1176
			return Player_GetSynchroValue();
slouken@173
  1177
			break;
slouken@154
  1178
#endif
slouken@173
  1179
		    default:
slouken@173
  1180
			return(-1);
slouken@173
  1181
			break;
slouken@154
  1182
		}
slouken@154
  1183
		return(-1);
slouken@154
  1184
	}
slouken@154
  1185
	return(-1);
slouken@154
  1186
}
slouken@154
  1187
slouken@154
  1188
slouken@0
  1189
/* Uninitialize the music players */
slouken@0
  1190
void close_music(void)
slouken@0
  1191
{
slouken@0
  1192
	Mix_HaltMusic();
slouken@0
  1193
#ifdef CMD_MUSIC
slouken@0
  1194
	Mix_SetMusicCMD(NULL);
slouken@0
  1195
#endif
slouken@0
  1196
#ifdef MOD_MUSIC
slouken@0
  1197
	MikMod_Exit();
slouken@101
  1198
	MikMod_UnregisterAllLoaders();
slouken@101
  1199
	MikMod_UnregisterAllDrivers();
slouken@0
  1200
#endif
slouken@0
  1201
}
slouken@0
  1202
slouken@226
  1203
#ifdef USE_RWOPS
slouken@226
  1204
slouken@226
  1205
Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) {
slouken@226
  1206
	/*Uint8     magic[5]; Apparently there is no way to check if the file is really a MOD,*/
slouken@226
  1207
      /*		    or there are too many formats supported by MikMod or MikMod does */
slouken@226
  1208
      /*		    this check by itself. If someone implements other formats (e.g. MP3) */
slouken@226
  1209
      /*		    the check can be uncommented */
slouken@226
  1210
	Mix_Music *music;
slouken@226
  1211
slouken@226
  1212
      /* Just skip the check */
slouken@226
  1213
	/* Figure out what kind of file this is */
slouken@226
  1214
	/*if (SDL_RWread(rw,magic,1,4)!=4) {
slouken@226
  1215
		Mix_SetError("Couldn't read from RWops");
slouken@226
  1216
		return NULL;
slouken@226
  1217
	}
slouken@226
  1218
	magic[4]='\0';*/
slouken@226
  1219
slouken@226
  1220
	/* Allocate memory for the music structure */
slouken@226
  1221
	music=(Mix_Music *)malloc(sizeof(Mix_Music));
slouken@226
  1222
	if (music==NULL ) {
slouken@226
  1223
		Mix_SetError("Out of memory");
slouken@226
  1224
		return(NULL);
slouken@226
  1225
	}
slouken@226
  1226
	music->error = 0;
slouken@226
  1227
slouken@226
  1228
#ifdef MOD_MUSIC
slouken@226
  1229
	if (1) {
slouken@226
  1230
		music->type=MUS_MOD;
slouken@226
  1231
		music->data.module=MikMod_LoadSongRW(rw,64);
slouken@226
  1232
		if (music->data.module==NULL) {
slouken@226
  1233
			Mix_SetError("%s",MikMod_strerror(MikMod_errno));
slouken@226
  1234
			music->error=1;
slouken@226
  1235
		}
slouken@226
  1236
	} else
slouken@226
  1237
#endif
slouken@226
  1238
	{
slouken@226
  1239
		Mix_SetError("Unrecognized music format");
slouken@226
  1240
		music->error=1;
slouken@226
  1241
	}
slouken@226
  1242
	if (music->error) {
slouken@226
  1243
		free(music);
slouken@226
  1244
		music=NULL;
slouken@226
  1245
	}
slouken@226
  1246
	return(music);
slouken@226
  1247
}
slouken@226
  1248
slouken@226
  1249
#endif /* USE_RWOPS */