| /* |
| SDL_mixer: An audio mixer library based on the SDL library |
| Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| |
| /* $Id$ */ |
| |
| #include <ctype.h> |
| #include "SDL_endian.h" |
| #include "SDL_audio.h" |
| #include "SDL_timer.h" |
| |
| #include "SDL_mixer.h" |
| |
| #ifdef CMD_MUSIC |
| #include "music_cmd.h" |
| #endif |
| #ifdef WAV_MUSIC |
| #include "wavestream.h" |
| #endif |
| #ifdef MODPLUG_MUSIC |
| #include "music_modplug.h" |
| #endif |
| #ifdef MOD_MUSIC |
| #include "music_mod.h" |
| #endif |
| #ifdef MID_MUSIC |
| # ifdef USE_TIMIDITY_MIDI |
| # include "timidity/timidity.h" |
| # endif |
| # ifdef USE_FLUIDSYNTH_MIDI |
| # include "fluidsynth.h" |
| # endif |
| # ifdef USE_NATIVE_MIDI |
| # include "native_midi.h" |
| # endif |
| #endif |
| #ifdef OGG_MUSIC |
| #include "music_ogg.h" |
| #endif |
| #ifdef MP3_MUSIC |
| #include "dynamic_mp3.h" |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| #include "music_mad.h" |
| #endif |
| #ifdef FLAC_MUSIC |
| #include "music_flac.h" |
| #endif |
| |
| #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) |
| static SDL_AudioSpec used_mixer; |
| #endif |
| |
| |
| int volatile music_active = 1; |
| static int volatile music_stopped = 0; |
| static int music_loops = 0; |
| static char *music_cmd = NULL; |
| static Mix_Music * volatile music_playing = NULL; |
| static int music_volume = MIX_MAX_VOLUME; |
| |
| struct _Mix_Music { |
| Mix_MusicType type; |
| union { |
| #ifdef CMD_MUSIC |
| MusicCMD *cmd; |
| #endif |
| #ifdef WAV_MUSIC |
| WAVStream *wave; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| modplug_data *modplug; |
| #endif |
| #ifdef MOD_MUSIC |
| struct MODULE *module; |
| #endif |
| #ifdef MID_MUSIC |
| #ifdef USE_TIMIDITY_MIDI |
| MidiSong *midi; |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| FluidSynthMidiSong *fluidsynthmidi; |
| #endif |
| #ifdef USE_NATIVE_MIDI |
| NativeMidiSong *nativemidi; |
| #endif |
| #endif |
| #ifdef OGG_MUSIC |
| OGG_music *ogg; |
| #endif |
| #ifdef MP3_MUSIC |
| SMPEG *mp3; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| mad_data *mp3_mad; |
| #endif |
| #ifdef FLAC_MUSIC |
| FLAC_music *flac; |
| #endif |
| } data; |
| Mix_Fading fading; |
| int fade_step; |
| int fade_steps; |
| int error; |
| }; |
| #ifdef MID_MUSIC |
| #ifdef USE_TIMIDITY_MIDI |
| static int timidity_ok; |
| static int samplesize; |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| static int fluidsynth_ok; |
| #endif |
| #ifdef USE_NATIVE_MIDI |
| static int native_midi_ok; |
| #endif |
| #endif |
| |
| /* Used to calculate fading steps */ |
| static int ms_per_step; |
| |
| /* rcg06042009 report available decoders at runtime. */ |
| static const char **music_decoders = NULL; |
| static int num_decoders = 0; |
| |
| /* Semicolon-separated SoundFont paths */ |
| #ifdef MID_MUSIC |
| char* soundfont_paths = NULL; |
| #endif |
| |
| int Mix_GetNumMusicDecoders(void) |
| { |
| return(num_decoders); |
| } |
| |
| const char *Mix_GetMusicDecoder(int index) |
| { |
| if ((index < 0) || (index >= num_decoders)) { |
| return NULL; |
| } |
| return(music_decoders[index]); |
| } |
| |
| static void add_music_decoder(const char *decoder) |
| { |
| void *ptr = SDL_realloc(music_decoders, (num_decoders + 1) * sizeof (const char **)); |
| if (ptr == NULL) { |
| return; /* oh well, go on without it. */ |
| } |
| music_decoders = (const char **) ptr; |
| music_decoders[num_decoders++] = decoder; |
| } |
| |
| /* Local low-level functions prototypes */ |
| static void music_internal_initialize_volume(void); |
| static void music_internal_volume(int volume); |
| static int music_internal_play(Mix_Music *music, double position); |
| static int music_internal_position(double position); |
| static int music_internal_playing(); |
| static void music_internal_halt(void); |
| |
| |
| /* Support for hooking when the music has finished */ |
| static void (*music_finished_hook)(void) = NULL; |
| |
| void Mix_HookMusicFinished(void (*music_finished)(void)) |
| { |
| SDL_LockAudio(); |
| music_finished_hook = music_finished; |
| SDL_UnlockAudio(); |
| } |
| |
| |
| /* If music isn't playing, halt it if no looping is required, restart it */ |
| /* otherwhise. NOP if the music is playing */ |
| static int music_halt_or_loop (void) |
| { |
| /* Restart music if it has to loop */ |
| |
| if (!music_internal_playing()) |
| { |
| #ifdef USE_NATIVE_MIDI |
| /* Native MIDI handles looping internally */ |
| if (music_playing->type == MUS_MID && native_midi_ok) { |
| music_loops = 0; |
| } |
| #endif |
| |
| /* Restart music if it has to loop at a high level */ |
| if (music_loops) |
| { |
| Mix_Fading current_fade; |
| --music_loops; |
| current_fade = music_playing->fading; |
| music_internal_play(music_playing, 0.0); |
| music_playing->fading = current_fade; |
| } |
| else |
| { |
| music_internal_halt(); |
| if (music_finished_hook) |
| music_finished_hook(); |
| |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| |
| |
| /* Mixing function */ |
| void music_mixer(void *udata, Uint8 *stream, int len) |
| { |
| //printf("music_mixer() called!\n"); |
| int left = 0; |
| |
| if ( music_playing && music_active ) { |
| /* Handle fading */ |
| if ( music_playing->fading != MIX_NO_FADING ) { |
| if ( music_playing->fade_step++ < music_playing->fade_steps ) { |
| int volume; |
| int fade_step = music_playing->fade_step; |
| int fade_steps = music_playing->fade_steps; |
| |
| if ( music_playing->fading == MIX_FADING_OUT ) { |
| volume = (music_volume * (fade_steps-fade_step)) / fade_steps; |
| } else { /* Fading in */ |
| volume = (music_volume * fade_step) / fade_steps; |
| } |
| music_internal_volume(volume); |
| } else { |
| if ( music_playing->fading == MIX_FADING_OUT ) { |
| music_internal_halt(); |
| if ( music_finished_hook ) { |
| music_finished_hook(); |
| } |
| return; |
| } |
| music_playing->fading = MIX_NO_FADING; |
| } |
| } |
| |
| music_halt_or_loop(); |
| if (!music_internal_playing()) |
| return; |
| |
| switch (music_playing->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| /* The playing is done externally */ |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| left = WAVStream_PlaySome(stream, len); |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| left = modplug_playAudio(music_playing->data.modplug, stream, len); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| left = MOD_playAudio(music_playing->data.module, stream, len); |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| /* Native midi is handled asynchronously */ |
| goto skip; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| fluidsynth_playsome(music_playing->data.fluidsynthmidi, stream, len); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| int samples = len / samplesize; |
| Timidity_PlaySome(stream, samples); |
| goto skip; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| |
| left = OGG_playAudio(music_playing->data.ogg, stream, len); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| left = FLAC_playAudio(music_playing->data.flac, stream, len); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| left = (len - smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len)); |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| left = mad_getSamples(music_playing->data.mp3_mad, stream, len); |
| break; |
| #endif |
| default: |
| /* Unknown music type?? */ |
| break; |
| } |
| } |
| |
| |
| skip: |
| /* Handle seamless music looping */ |
| if (left > 0 && left < len) { |
| music_halt_or_loop(); |
| if (music_internal_playing()) |
| music_mixer(udata, stream+(len-left), left); |
| } |
| //printf("sample 0: %d %d", stream[0], stream[1]); |
| } |
| |
| /* Initialize the music players with a certain desired audio format */ |
| int open_music(SDL_AudioSpec *mixer) |
| { |
| #ifdef WAV_MUSIC |
| if ( WAVStream_Init(mixer) == 0 ) { |
| add_music_decoder("WAVE"); |
| } |
| #endif |
| #ifdef MODPLUG_MUSIC |
| if ( modplug_init(mixer) == 0 ) { |
| add_music_decoder("MODPLUG"); |
| } |
| #endif |
| #ifdef MOD_MUSIC |
| if ( MOD_init(mixer) == 0 ) { |
| add_music_decoder("MIKMOD"); |
| } |
| #endif |
| #ifdef MID_MUSIC |
| #ifdef USE_TIMIDITY_MIDI |
| samplesize = mixer->size / mixer->samples; |
| if ( Timidity_Init(mixer->freq, mixer->format, |
| mixer->channels, mixer->samples) == 0 ) { |
| timidity_ok = 1; |
| add_music_decoder("TIMIDITY"); |
| } else { |
| timidity_ok = 0; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_init(mixer) == 0 ) { |
| fluidsynth_ok = 1; |
| add_music_decoder("FLUIDSYNTH"); |
| } else { |
| fluidsynth_ok = 0; |
| } |
| #endif |
| #ifdef USE_NATIVE_MIDI |
| #ifdef USE_FLUIDSYNTH_MIDI |
| native_midi_ok = !fluidsynth_ok; |
| if ( native_midi_ok ) |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| native_midi_ok = !timidity_ok; |
| if ( !native_midi_ok ) { |
| native_midi_ok = (getenv("SDL_NATIVE_MUSIC") != NULL); |
| } |
| if ( native_midi_ok ) |
| #endif |
| native_midi_ok = native_midi_detect(); |
| if ( native_midi_ok ) |
| add_music_decoder("NATIVEMIDI"); |
| #endif |
| #endif |
| #ifdef OGG_MUSIC |
| if ( OGG_init(mixer) == 0 ) { |
| add_music_decoder("OGG"); |
| } |
| #endif |
| #ifdef FLAC_MUSIC |
| if ( FLAC_init(mixer) == 0 ) { |
| add_music_decoder("FLAC"); |
| } |
| #endif |
| #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) |
| /* Keep a copy of the mixer */ |
| used_mixer = *mixer; |
| add_music_decoder("MP3"); |
| #endif |
| |
| music_playing = NULL; |
| music_stopped = 0; |
| Mix_VolumeMusic(SDL_MIX_MAXVOLUME); |
| |
| /* Calculate the number of ms for each callback */ |
| ms_per_step = (int) (((float)mixer->samples * 1000.0) / mixer->freq); |
| |
| return(0); |
| } |
| |
| /* Portable case-insensitive string compare function */ |
| int MIX_string_equals(const char *str1, const char *str2) |
| { |
| while ( *str1 && *str2 ) { |
| if ( toupper((unsigned char)*str1) != |
| toupper((unsigned char)*str2) ) |
| break; |
| ++str1; |
| ++str2; |
| } |
| return (!*str1 && !*str2); |
| } |
| |
| static int detect_mp3(Uint8 *magic) |
| { |
| if ( strncmp((char *)magic, "ID3", 3) == 0 ) { |
| return 1; |
| } |
| |
| /* Detection code lifted from SMPEG */ |
| if(((magic[0] & 0xff) != 0xff) || // No sync bits |
| ((magic[1] & 0xf0) != 0xf0) || // |
| ((magic[2] & 0xf0) == 0x00) || // Bitrate is 0 |
| ((magic[2] & 0xf0) == 0xf0) || // Bitrate is 15 |
| ((magic[2] & 0x0c) == 0x0c) || // Frequency is 3 |
| ((magic[1] & 0x06) == 0x00)) { // Layer is 4 |
| return(0); |
| } |
| return 1; |
| } |
| |
| /* MUS_MOD can't be auto-detected. If no other format was detected, MOD is |
| * assumed and MUS_MOD will be returned, meaning that the format might not |
| * actually be MOD-based. |
| * |
| * Returns MUS_NONE in case of errors. */ |
| static Mix_MusicType detect_music_type(SDL_RWops *rw) |
| { |
| Uint8 magic[5]; |
| Uint8 moremagic[9]; |
| |
| int start = SDL_RWtell(rw); |
| if (SDL_RWread(rw, magic, 1, 4) != 4 || SDL_RWread(rw, moremagic, 1, 8) != 8 ) { |
| Mix_SetError("Couldn't read from RWops"); |
| return MUS_NONE; |
| } |
| SDL_RWseek(rw, start, RW_SEEK_SET); |
| magic[4]='\0'; |
| moremagic[8] = '\0'; |
| |
| /* WAVE files have the magic four bytes "RIFF" |
| AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ |
| if (((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || |
| (strcmp((char *)magic, "FORM") == 0)) { |
| return MUS_WAV; |
| } |
| |
| /* Ogg Vorbis files have the magic four bytes "OggS" */ |
| if (strcmp((char *)magic, "OggS") == 0) { |
| return MUS_OGG; |
| } |
| |
| /* FLAC files have the magic four bytes "fLaC" */ |
| if (strcmp((char *)magic, "fLaC") == 0) { |
| return MUS_FLAC; |
| } |
| |
| /* MIDI files have the magic four bytes "MThd" */ |
| if (strcmp((char *)magic, "MThd") == 0) { |
| return MUS_MID; |
| } |
| |
| if (detect_mp3(magic)) { |
| return MUS_MP3; |
| } |
| |
| /* Assume MOD format. |
| * |
| * Apparently there is no way to check if the file is really a MOD, |
| * or there are too many formats supported by MikMod/ModPlug, or |
| * MikMod/ModPlug does this check by itself. */ |
| return MUS_MOD; |
| } |
| |
| /* Load a music file */ |
| Mix_Music *Mix_LoadMUS(const char *file) |
| { |
| SDL_RWops *rw; |
| Mix_Music *music; |
| Mix_MusicType type; |
| char *ext = strrchr(file, '.'); |
| |
| #ifdef CMD_MUSIC |
| if ( music_cmd ) { |
| /* Allocate memory for the music structure */ |
| music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); |
| if ( music == NULL ) { |
| Mix_SetError("Out of memory"); |
| return(NULL); |
| } |
| music->error = 0; |
| music->type = MUS_CMD; |
| music->data.cmd = MusicCMD_LoadSong(music_cmd, file); |
| if ( music->data.cmd == NULL ) { |
| SDL_free(music); |
| music == NULL; |
| } |
| return music; |
| } |
| #endif |
| |
| rw = SDL_RWFromFile(file, "rb"); |
| if ( rw == NULL ) { |
| Mix_SetError("Couldn't open '%s'", file); |
| return NULL; |
| } |
| |
| /* Use the extension as a first guess on the file type */ |
| type = MUS_NONE; |
| ext = strrchr(file, '.'); |
| /* No need to guard these with #ifdef *_MUSIC stuff, |
| * since we simply call Mix_LoadMUSType_RW() later */ |
| if ( ext ) { |
| ++ext; /* skip the dot in the extension */ |
| if ( MIX_string_equals(ext, "WAV") ) { |
| type = MUS_WAV; |
| } else if ( MIX_string_equals(ext, "MID") || |
| MIX_string_equals(ext, "MIDI") || |
| MIX_string_equals(ext, "KAR") ) { |
| type = MUS_MID; |
| } else if ( MIX_string_equals(ext, "OGG") ) { |
| type = MUS_OGG; |
| } else if ( MIX_string_equals(ext, "FLAC") ) { |
| type = MUS_FLAC; |
| } else if ( MIX_string_equals(ext, "MPG") || |
| MIX_string_equals(ext, "MPEG") || |
| MIX_string_equals(ext, "MP3") || |
| MIX_string_equals(ext, "MAD") ) { |
| type = MUS_MP3; |
| } |
| } |
| if ( type == MUS_NONE ) { |
| type = detect_music_type(rw); |
| } |
| |
| /* We need to know if a specific error occurs; if not, we'll set a |
| * generic one, so we clear the current one. */ |
| Mix_SetError(""); |
| music = Mix_LoadMUSType_RW(rw, type, SDL_TRUE); |
| if ( music == NULL && Mix_GetError()[0] == '\0' ) { |
| SDL_FreeRW(rw); |
| Mix_SetError("Couldn't open '%s'", file); |
| } |
| return music; |
| } |
| |
| Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) |
| { |
| return Mix_LoadMUSType_RW(rw, MUS_NONE, SDL_FALSE); |
| } |
| |
| Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type, int freesrc) |
| { |
| Mix_Music *music; |
| |
| if (!rw) { |
| Mix_SetError("RWops pointer is NULL"); |
| return NULL; |
| } |
| |
| /* If the caller wants auto-detection, figure out what kind of file |
| * this is. */ |
| if (type == MUS_NONE) { |
| if ((type = detect_music_type(rw)) == MUS_NONE) { |
| /* Don't call Mix_SetError() here since detect_music_type() |
| * does that. */ |
| return NULL; |
| } |
| } |
| |
| /* Allocate memory for the music structure */ |
| music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); |
| if (music == NULL ) { |
| Mix_SetError("Out of memory"); |
| return NULL; |
| } |
| music->error = 0; |
| |
| switch (type) { |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| /* The WAVE loader needs the first 4 bytes of the header */ |
| { |
| Uint8 magic[5]; |
| int start = SDL_RWtell(rw); |
| if (SDL_RWread(rw, magic, 1, 4) != 4) { |
| Mix_SetError("Couldn't read from RWops"); |
| return MUS_NONE; |
| } |
| SDL_RWseek(rw, start, RW_SEEK_SET); |
| magic[4] = '\0'; |
| music->type = MUS_WAV; |
| music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic, freesrc); |
| } |
| if (music->data.wave == NULL) { |
| music->error = 1; |
| } |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| music->type = MUS_OGG; |
| music->data.ogg = OGG_new_RW(rw, freesrc); |
| if ( music->data.ogg == NULL ) { |
| music->error = 1; |
| } |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| music->type = MUS_FLAC; |
| music->data.flac = FLAC_new_RW(rw, freesrc); |
| if ( music->data.flac == NULL ) { |
| music->error = 1; |
| } |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| if ( Mix_Init(MIX_INIT_MP3) ) { |
| SMPEG_Info info; |
| music->type = MUS_MP3; |
| music->data.mp3 = smpeg.SMPEG_new_rwops(rw, &info, 0); |
| if ( !info.has_audio ) { |
| Mix_SetError("MPEG file does not have any audio stream."); |
| music->error = 1; |
| } else { |
| smpeg.SMPEG_actualSpec(music->data.mp3, &used_mixer); |
| } |
| } else { |
| music->error = 1; |
| } |
| break; |
| #elif defined(MP3_MAD_MUSIC) |
| case MUS_MP3: |
| music->type = MUS_MP3_MAD; |
| music->data.mp3_mad = mad_openFileRW(rw, &used_mixer, freesrc); |
| if (music->data.mp3_mad == 0) { |
| Mix_SetError("Could not initialize MPEG stream."); |
| music->error = 1; |
| } |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| music->type = MUS_MID; |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| music->data.nativemidi = native_midi_loadsong_RW(rw, freesrc); |
| if ( music->data.nativemidi == NULL ) { |
| Mix_SetError("%s", native_midi_error()); |
| music->error = 1; |
| } |
| break; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw, freesrc); |
| if ( music->data.fluidsynthmidi == NULL ) { |
| music->error = 1; |
| } |
| break; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| music->data.midi = Timidity_LoadSong_RW(rw, freesrc); |
| if ( music->data.midi == NULL ) { |
| Mix_SetError("%s", Timidity_Error()); |
| music->error = 1; |
| } |
| //else |
| //printf("Timidity successfully loaded song!\n"); |
| } else { |
| Mix_SetError("%s", Timidity_Error()); |
| music->error = 1; |
| } |
| #endif |
| break; |
| #endif |
| #if defined(MODPLUG_MUSIC) || defined(MOD_MUSIC) |
| case MUS_MOD: |
| music->error = 1; |
| #ifdef MODPLUG_MUSIC |
| if ( music->error ) { |
| music->type = MUS_MODPLUG; |
| music->data.modplug = modplug_new_RW(rw, freesrc); |
| if ( music->data.modplug ) { |
| music->error = 0; |
| } |
| } |
| #endif |
| #ifdef MOD_MUSIC |
| if ( music->error ) { |
| music->type = MUS_MOD; |
| music->data.module = MOD_new_RW(rw, freesrc); |
| if ( music->data.module ) { |
| music->error = 0; |
| } |
| } |
| #endif |
| break; |
| #endif |
| |
| default: |
| Mix_SetError("Unrecognized music format"); |
| music->error=1; |
| } /* switch (want) */ |
| |
| |
| if (music->error) { |
| SDL_free(music); |
| music=NULL; |
| } |
| return(music); |
| } |
| |
| /* Free a music chunk previously loaded */ |
| void Mix_FreeMusic(Mix_Music *music) |
| { |
| if ( music ) { |
| /* Stop the music if it's currently playing */ |
| SDL_LockAudio(); |
| if ( music == music_playing ) { |
| /* Wait for any fade out to finish */ |
| while ( music->fading == MIX_FADING_OUT ) { |
| SDL_UnlockAudio(); |
| SDL_Delay(100); |
| SDL_LockAudio(); |
| } |
| if ( music == music_playing ) { |
| music_internal_halt(); |
| } |
| } |
| SDL_UnlockAudio(); |
| switch (music->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| MusicCMD_FreeSong(music->data.cmd); |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| WAVStream_FreeSong(music->data.wave); |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| modplug_delete(music->data.modplug); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| MOD_delete(music->data.module); |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| native_midi_freesong(music->data.nativemidi); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| fluidsynth_freesong(music->data.fluidsynthmidi); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| Timidity_FreeSong(music->data.midi); |
| goto skip; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| OGG_delete(music->data.ogg); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| FLAC_delete(music->data.flac); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| smpeg.SMPEG_delete(music->data.mp3); |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| mad_closeFile(music->data.mp3_mad); |
| break; |
| #endif |
| default: |
| /* Unknown music type?? */ |
| break; |
| } |
| |
| skip: |
| SDL_free(music); |
| } |
| } |
| |
| /* Find out the music format of a mixer music, or the currently playing |
| music, if 'music' is NULL. |
| */ |
| Mix_MusicType Mix_GetMusicType(const Mix_Music *music) |
| { |
| Mix_MusicType type = MUS_NONE; |
| |
| if ( music ) { |
| type = music->type; |
| } else { |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| type = music_playing->type; |
| } |
| SDL_UnlockAudio(); |
| } |
| return(type); |
| } |
| |
| /* Play a music chunk. Returns 0, or -1 if there was an error. |
| */ |
| static int music_internal_play(Mix_Music *music, double position) |
| { |
| int retval = 0; |
| |
| #if defined(__MACOSX__) && defined(USE_NATIVE_MIDI) |
| /* This fixes a bug with native MIDI on Mac OS X, where you |
| can't really stop and restart MIDI from the audio callback. |
| */ |
| if ( music == music_playing && music->type == MUS_MID && native_midi_ok ) { |
| /* Just a seek suffices to restart playing */ |
| music_internal_position(position); |
| return 0; |
| } |
| #endif |
| |
| /* Note the music we're playing */ |
| if ( music_playing ) { |
| music_internal_halt(); |
| } |
| music_playing = music; |
| |
| /* Set the initial volume */ |
| if ( music->type != MUS_MOD ) { |
| music_internal_initialize_volume(); |
| } |
| |
| /* Set up for playback */ |
| switch (music->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| MusicCMD_Start(music->data.cmd); |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| WAVStream_Start(music->data.wave); |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| /* can't set volume until file is loaded, so finally set it now */ |
| music_internal_initialize_volume(); |
| modplug_play(music->data.modplug); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| MOD_play(music->data.module); |
| /* Player_SetVolume() does nothing before Player_Start() */ |
| music_internal_initialize_volume(); |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| native_midi_start(music->data.nativemidi, music_loops); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if (fluidsynth_ok ) { |
| fluidsynth_start(music->data.fluidsynthmidi); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| Timidity_Start(music->data.midi); |
| goto skip; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| OGG_play(music->data.ogg); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| FLAC_play(music->data.flac); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| smpeg.SMPEG_enableaudio(music->data.mp3,1); |
| smpeg.SMPEG_enablevideo(music->data.mp3,0); |
| smpeg.SMPEG_play(music_playing->data.mp3); |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| mad_start(music->data.mp3_mad); |
| break; |
| #endif |
| default: |
| Mix_SetError("Can't play unknown music type"); |
| retval = -1; |
| break; |
| } |
| |
| skip: |
| /* Set the playback position, note any errors if an offset is used */ |
| if ( retval == 0 ) { |
| if ( position > 0.0 ) { |
| if ( music_internal_position(position) < 0 ) { |
| Mix_SetError("Position not implemented for music type"); |
| retval = -1; |
| } |
| } else { |
| music_internal_position(0.0); |
| } |
| } |
| |
| /* If the setup failed, we're not playing any music anymore */ |
| if ( retval < 0 ) { |
| music_playing = NULL; |
| } |
| return(retval); |
| } |
| int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) |
| { |
| int retval; |
| |
| if ( ms_per_step == 0 ) { |
| SDL_SetError("Audio device hasn't been opened"); |
| return(-1); |
| } |
| |
| /* Don't play null pointers :-) */ |
| if ( music == NULL ) { |
| Mix_SetError("music parameter was NULL"); |
| return(-1); |
| } |
| |
| /* Setup the data */ |
| if ( ms ) { |
| music->fading = MIX_FADING_IN; |
| } else { |
| music->fading = MIX_NO_FADING; |
| } |
| music->fade_step = 0; |
| music->fade_steps = ms/ms_per_step; |
| |
| /* Play the puppy */ |
| SDL_LockAudio(); |
| /* If the current music is fading out, wait for the fade to complete */ |
| while ( music_playing && (music_playing->fading == MIX_FADING_OUT) ) { |
| SDL_UnlockAudio(); |
| SDL_Delay(100); |
| SDL_LockAudio(); |
| } |
| music_active = 1; |
| if (loops == 1) { |
| /* Loop is the number of times to play the audio */ |
| loops = 0; |
| } |
| music_loops = loops; |
| retval = music_internal_play(music, position); |
| SDL_UnlockAudio(); |
| |
| return(retval); |
| } |
| int Mix_FadeInMusic(Mix_Music *music, int loops, int ms) |
| { |
| return Mix_FadeInMusicPos(music, loops, ms, 0.0); |
| } |
| int Mix_PlayMusic(Mix_Music *music, int loops) |
| { |
| return Mix_FadeInMusicPos(music, loops, 0, 0.0); |
| } |
| |
| /* Set the playing music position */ |
| int music_internal_position(double position) |
| { |
| int retval = 0; |
| |
| switch (music_playing->type) { |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| modplug_jump_to_time(music_playing->data.modplug, position); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| MOD_jump_to_time(music_playing->data.module, position); |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| OGG_jump_to_time(music_playing->data.ogg, position); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| FLAC_jump_to_time(music_playing->data.flac, position); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| smpeg.SMPEG_rewind(music_playing->data.mp3); |
| smpeg.SMPEG_play(music_playing->data.mp3); |
| if ( position > 0.0 ) { |
| smpeg.SMPEG_skip(music_playing->data.mp3, (float)position); |
| } |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| mad_seek(music_playing->data.mp3_mad, position); |
| break; |
| #endif |
| default: |
| /* TODO: Implement this for other music backends */ |
| retval = -1; |
| break; |
| } |
| return(retval); |
| } |
| int Mix_SetMusicPosition(double position) |
| { |
| int retval; |
| |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| retval = music_internal_position(position); |
| if ( retval < 0 ) { |
| Mix_SetError("Position not implemented for music type"); |
| } |
| } else { |
| Mix_SetError("Music isn't playing"); |
| retval = -1; |
| } |
| SDL_UnlockAudio(); |
| |
| return(retval); |
| } |
| |
| /* Set the music's initial volume */ |
| static void music_internal_initialize_volume(void) |
| { |
| if ( music_playing->fading == MIX_FADING_IN ) { |
| music_internal_volume(0); |
| } else { |
| music_internal_volume(music_volume); |
| } |
| } |
| |
| /* Set the music volume */ |
| static void music_internal_volume(int volume) |
| { |
| switch (music_playing->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| MusicCMD_SetVolume(volume); |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| WAVStream_SetVolume(volume); |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| modplug_setvolume(music_playing->data.modplug, volume); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| MOD_setvolume(music_playing->data.module, volume); |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| native_midi_setvolume(volume); |
| return; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| fluidsynth_setvolume(music_playing->data.fluidsynthmidi, volume); |
| return; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| Timidity_SetVolume(volume); |
| return; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| OGG_setvolume(music_playing->data.ogg, volume); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| FLAC_setvolume(music_playing->data.flac, volume); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| smpeg.SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| mad_setVolume(music_playing->data.mp3_mad, volume); |
| break; |
| #endif |
| default: |
| /* Unknown music type?? */ |
| break; |
| } |
| } |
| int Mix_VolumeMusic(int volume) |
| { |
| int prev_volume; |
| |
| prev_volume = music_volume; |
| if ( volume < 0 ) { |
| return prev_volume; |
| } |
| if ( volume > SDL_MIX_MAXVOLUME ) { |
| volume = SDL_MIX_MAXVOLUME; |
| } |
| music_volume = volume; |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| music_internal_volume(music_volume); |
| } |
| SDL_UnlockAudio(); |
| return(prev_volume); |
| } |
| |
| /* Halt playing of music */ |
| static void music_internal_halt(void) |
| { |
| switch (music_playing->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| MusicCMD_Stop(music_playing->data.cmd); |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| WAVStream_Stop(); |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| modplug_stop(music_playing->data.modplug); |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| MOD_stop(music_playing->data.module); |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| native_midi_stop(); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| fluidsynth_stop(music_playing->data.fluidsynthmidi); |
| goto skip; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| Timidity_Stop(); |
| goto skip; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| OGG_stop(music_playing->data.ogg); |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| FLAC_stop(music_playing->data.flac); |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| smpeg.SMPEG_stop(music_playing->data.mp3); |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| mad_stop(music_playing->data.mp3_mad); |
| break; |
| #endif |
| default: |
| /* Unknown music type?? */ |
| return; |
| } |
| |
| skip: |
| music_playing->fading = MIX_NO_FADING; |
| music_playing = NULL; |
| } |
| int Mix_HaltMusic(void) |
| { |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| music_internal_halt(); |
| } |
| SDL_UnlockAudio(); |
| |
| return(0); |
| } |
| |
| /* Progressively stop the music */ |
| int Mix_FadeOutMusic(int ms) |
| { |
| int retval = 0; |
| |
| if ( ms_per_step == 0 ) { |
| SDL_SetError("Audio device hasn't been opened"); |
| return 0; |
| } |
| |
| if (ms <= 0) { /* just halt immediately. */ |
| Mix_HaltMusic(); |
| return 1; |
| } |
| |
| SDL_LockAudio(); |
| if ( music_playing) { |
| int fade_steps = (ms + ms_per_step - 1)/ms_per_step; |
| if ( music_playing->fading == MIX_NO_FADING ) { |
| music_playing->fade_step = 0; |
| } else { |
| int step; |
| int old_fade_steps = music_playing->fade_steps; |
| if ( music_playing->fading == MIX_FADING_OUT ) { |
| step = music_playing->fade_step; |
| } else { |
| step = old_fade_steps |
| - music_playing->fade_step + 1; |
| } |
| music_playing->fade_step = (step * fade_steps) |
| / old_fade_steps; |
| } |
| music_playing->fading = MIX_FADING_OUT; |
| music_playing->fade_steps = fade_steps; |
| retval = 1; |
| } |
| SDL_UnlockAudio(); |
| |
| return(retval); |
| } |
| |
| Mix_Fading Mix_FadingMusic(void) |
| { |
| Mix_Fading fading = MIX_NO_FADING; |
| |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| fading = music_playing->fading; |
| } |
| SDL_UnlockAudio(); |
| |
| return(fading); |
| } |
| |
| /* Pause/Resume the music stream */ |
| void Mix_PauseMusic(void) |
| { |
| music_active = 0; |
| } |
| |
| void Mix_ResumeMusic(void) |
| { |
| music_active = 1; |
| } |
| |
| void Mix_RewindMusic(void) |
| { |
| Mix_SetMusicPosition(0.0); |
| } |
| |
| int Mix_PausedMusic(void) |
| { |
| return (music_active == 0); |
| } |
| |
| /* Check the status of the music */ |
| static int music_internal_playing() |
| { |
| int playing = 1; |
| |
| if (music_playing == NULL) { |
| return 0; |
| } |
| |
| switch (music_playing->type) { |
| #ifdef CMD_MUSIC |
| case MUS_CMD: |
| if (!MusicCMD_Active(music_playing->data.cmd)) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef WAV_MUSIC |
| case MUS_WAV: |
| if ( ! WAVStream_Active() ) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef MODPLUG_MUSIC |
| case MUS_MODPLUG: |
| if ( ! modplug_playing(music_playing->data.modplug) ) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef MOD_MUSIC |
| case MUS_MOD: |
| if ( ! MOD_playing(music_playing->data.module) ) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef MID_MUSIC |
| case MUS_MID: |
| #ifdef USE_NATIVE_MIDI |
| if ( native_midi_ok ) { |
| if ( ! native_midi_active() ) |
| playing = 0; |
| goto skip; |
| } |
| #endif |
| #ifdef USE_FLUIDSYNTH_MIDI |
| if ( fluidsynth_ok ) { |
| if ( ! fluidsynth_active(music_playing->data.fluidsynthmidi) ) |
| playing = 0; |
| goto skip; |
| } |
| #endif |
| #ifdef USE_TIMIDITY_MIDI |
| if ( timidity_ok ) { |
| if ( ! Timidity_Active() ) |
| playing = 0; |
| goto skip; |
| } |
| #endif |
| break; |
| #endif |
| #ifdef OGG_MUSIC |
| case MUS_OGG: |
| if ( ! OGG_playing(music_playing->data.ogg) ) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef FLAC_MUSIC |
| case MUS_FLAC: |
| if ( ! FLAC_playing(music_playing->data.flac) ) { |
| playing = 0; |
| } |
| break; |
| #endif |
| #ifdef MP3_MUSIC |
| case MUS_MP3: |
| if ( smpeg.SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING ) |
| playing = 0; |
| break; |
| #endif |
| #ifdef MP3_MAD_MUSIC |
| case MUS_MP3_MAD: |
| if (!mad_isPlaying(music_playing->data.mp3_mad)) { |
| playing = 0; |
| } |
| break; |
| #endif |
| default: |
| playing = 0; |
| break; |
| } |
| |
| skip: |
| return(playing); |
| } |
| int Mix_PlayingMusic(void) |
| { |
| int playing = 0; |
| |
| SDL_LockAudio(); |
| if ( music_playing ) { |
| playing = music_loops || music_internal_playing(); |
| } |
| SDL_UnlockAudio(); |
| |
| return(playing); |
| } |
| |
| /* Set the external music playback command */ |
| int Mix_SetMusicCMD(const char *command) |
| { |
| Mix_HaltMusic(); |
| if ( music_cmd ) { |
| SDL_free(music_cmd); |
| music_cmd = NULL; |
| } |
| if ( command ) { |
| music_cmd = (char *)SDL_malloc(strlen(command)+1); |
| if ( music_cmd == NULL ) { |
| return(-1); |
| } |
| strcpy(music_cmd, command); |
| } |
| return(0); |
| } |
| |
| int Mix_SetSynchroValue(int i) |
| { |
| /* Not supported by any players at this time */ |
| return(-1); |
| } |
| |
| int Mix_GetSynchroValue(void) |
| { |
| /* Not supported by any players at this time */ |
| return(-1); |
| } |
| |
| |
| /* Uninitialize the music players */ |
| void close_music(void) |
| { |
| Mix_HaltMusic(); |
| #ifdef CMD_MUSIC |
| Mix_SetMusicCMD(NULL); |
| #endif |
| #ifdef MODPLUG_MUSIC |
| modplug_exit(); |
| #endif |
| #ifdef MOD_MUSIC |
| MOD_exit(); |
| #endif |
| #ifdef MID_MUSIC |
| # ifdef USE_TIMIDITY_MIDI |
| Timidity_Close(); |
| # endif |
| #endif |
| |
| /* rcg06042009 report available decoders at runtime. */ |
| SDL_free(music_decoders); |
| music_decoders = NULL; |
| num_decoders = 0; |
| |
| ms_per_step = 0; |
| } |
| |
| int Mix_SetSoundFonts(const char *paths) |
| { |
| #ifdef MID_MUSIC |
| if (soundfont_paths) { |
| SDL_free(soundfont_paths); |
| soundfont_paths = NULL; |
| } |
| |
| if (paths) { |
| if (!(soundfont_paths = SDL_strdup(paths))) { |
| Mix_SetError("Insufficient memory to set SoundFonts"); |
| return 0; |
| } |
| } |
| #endif |
| return 1; |
| } |
| |
| #ifdef MID_MUSIC |
| const char* Mix_GetSoundFonts(void) |
| { |
| const char* force = getenv("SDL_FORCE_SOUNDFONTS"); |
| |
| if (!soundfont_paths || (force && force[0] == '1')) { |
| return getenv("SDL_SOUNDFONTS"); |
| } else { |
| return soundfont_paths; |
| } |
| } |
| |
| int Mix_EachSoundFont(int (*function)(const char*, void*), void *data) |
| { |
| char *context, *path, *paths; |
| const char* cpaths = Mix_GetSoundFonts(); |
| |
| if (!cpaths) { |
| Mix_SetError("No SoundFonts have been requested"); |
| return 0; |
| } |
| |
| if (!(paths = SDL_strdup(cpaths))) { |
| Mix_SetError("Insufficient memory to iterate over SoundFonts"); |
| return 0; |
| } |
| |
| #if defined(__MINGW32__) || defined(__MINGW64__) |
| for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) { |
| #else |
| for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) { |
| #endif |
| if (!function(path, data)) { |
| SDL_free(paths); |
| return 0; |
| } |
| } |
| |
| SDL_free(paths); |
| return 1; |
| } |
| #endif |