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