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