music.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 09 Nov 2003 20:11:30 +0000
changeset 237 5fb7a859f158
parent 226 3691a375f2e6
child 241 503416fca921
permissions -rw-r--r--
(Courtesy of Jerome Zago on the SDL mailing list...)


+++++ Synopsis:
Mix_FadeInMusic(Music, 0, chunksize) following Mix_VolumeMusic(0) doesn't take
into account the latter for low values of "chunksize" when "Music" is a mod.

+++++ How to reproduce the problem:
#####
$ cat testmusic.c
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <stdlib.h>

int main (int argc, char **argv)
{
if (argc != 4) {
fprintf(stderr, "Usage: %s <music file> <chunksize> <ms>\n", argv[0]);
return 1;
}

if (SDL_Init(SDL_INIT_AUDIO) >= 0) {
if (Mix_OpenAudio(44100, AUDIO_S16, 1, atoi(argv[2])) >= 0) {
Mix_Music* Music = Mix_LoadMUS(argv[1]);
if (Music) {
Mix_VolumeMusic(0);
Mix_FadeInMusic(Music, 0, atoi(argv[3]));
printf("Now playing\n");
SDL_Delay(2000);
Mix_FreeMusic(Music);
}
}
}
SDL_Quit();
return 0;
};

$ cc -g -O2 -Wall -I/usr/include/SDL -D_REENTRANT -L/usr/lib -lSDL \
-lpthread -lSDL_mixer testmusic.c -o testmusic

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 512 10
Now playing
[ I hear the music => BAD ]

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 512 11
Now playing
[ I don't hear the music => GOOD ]

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 1024 22
Now playing
[ I hear the music => BAD ]

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 1024 23
Now playing
[ I don't hear the music => GOOD ]

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 2048 45
Now playing
[ I hear the music => BAD ]

$ ./testmusic /usr/local/share/heroes/mod/heroes01.xm 2048 46
Now playing
[ I don't hear the music => GOOD ]
#####

+++++ Comments
heroes01.xm comes from
http://prdownloads.sourceforge.net/heroes/heroes-sound-tracks-1.0.tar.bz2.
All the other modules I've tested show the same behaviour.

+++++ Analysis (using SDL_mixer source code):
It appears that the lowest value of chunksize for which the volume is taken
into account is equal to ms_per_step = (int) (((float)mixer->samples *
1000.0) / mixer->freq) [file music.c, function open_music()].
This also means that music->fade_steps [function Mix_FadeInMusicPos()] = 0
when and only when the volume isn't taken into account.
In this case, note that music_internal_volume() is never called from
music_mixer(), since (music_playing->fade_step++ < music_playing->fade_steps)
is never verified. This might explain the bug.

+++++ Testing environment:
- Mandrake Linux 9.1 i386
- SDL_mixer 1.2.4 (Mandrake package), 1.2.5 or CVS (both built from scratch)
- Athlon XP 1500+
- SB Live!

More details upon request. Thanks for your time.


--------

This is related to my previous message:
[SDL] Strange behaviour of Mix_FadeInMusic (1.2.5) on mods
http://www.libsdl.org/pipermail/sdl/2003-November/057749.html

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