From 8826199938690f05850d28df737ef2a99d476757 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 16 Nov 2009 04:40:54 +0000 Subject: [PATCH] Jon Atkins to Sam I added libmodplug as a possible music player defaulted to no in configure for now. libmodplug has higher quality than mikmod, and it seems to be ported to all the major modern OS's (Linux/Win/OSX). --- CHANGES | 4 + Makefile.in | 2 +- SDL_mixer.h | 7 +- configure.in | 23 ++++- music.c | 90 ++++++++++++++++-- music_modplug.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ music_modplug.h | 45 +++++++++ 7 files changed, 398 insertions(+), 11 deletions(-) create mode 100644 music_modplug.c create mode 100644 music_modplug.h diff --git a/CHANGES b/CHANGES index 083ce761..b68fb078 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +1.2.12: +Jon Atkins - Sat Nov 14 13:00:18 PST 2009 + * Added support for libmodplug (disabled by default) + 1.2.11: Sam Lantinga - Sat Nov 14 12:38:01 PST 2009 * Fixed initialization error and crashes if MikMod library isn't available diff --git a/Makefile.in b/Makefile.in index 76fdd3c8..f70fd75b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -38,7 +38,7 @@ VERSION_OBJECTS = @VERSION_OBJECTS@ PLAYWAVE_OBJECTS = @PLAYWAVE_OBJECTS@ PLAYMUS_OBJECTS = @PLAYMUS_OBJECTS@ -DIST = CHANGES COPYING CWProjects.sea.bin MPWmake.sea.bin Makefile.in SDL_mixer.pc.in README SDL_mixer.h SDL_mixer.qpg.in SDL_mixer.spec SDL_mixer.spec.in VisualC.zip Watcom-OS2.zip Xcode.tar.gz acinclude autogen.sh build-scripts configure configure.in dynamic_flac.c dynamic_flac.h dynamic_mod.c dynamic_mod.h dynamic_mp3.c dynamic_mp3.h dynamic_ogg.c dynamic_ogg.h effect_position.c effect_stereoreverse.c effects_internal.c effects_internal.h gcc-fat.sh libmikmod-3.1.12.zip load_aiff.c load_aiff.h load_flac.c load_flac.h load_ogg.c load_ogg.h load_voc.c load_voc.h mixer.c music.c music_cmd.c music_cmd.h music_flac.c music_flac.h music_mad.c music_mad.h music_mod.c music_mod.h music_ogg.c music_ogg.h native_midi native_midi_gpl playmus.c playwave.c timidity wavestream.c wavestream.h version.rc +DIST = CHANGES COPYING CWProjects.sea.bin MPWmake.sea.bin Makefile.in SDL_mixer.pc.in README SDL_mixer.h SDL_mixer.qpg.in SDL_mixer.spec SDL_mixer.spec.in VisualC.zip Watcom-OS2.zip Xcode.tar.gz acinclude autogen.sh build-scripts configure configure.in dynamic_flac.c dynamic_flac.h dynamic_mod.c dynamic_mod.h dynamic_mp3.c dynamic_mp3.h dynamic_ogg.c dynamic_ogg.h effect_position.c effect_stereoreverse.c effects_internal.c effects_internal.h gcc-fat.sh libmikmod-3.1.12.zip load_aiff.c load_aiff.h load_flac.c load_flac.h load_ogg.c load_ogg.h load_voc.c load_voc.h mixer.c music.c music_cmd.c music_cmd.h music_flac.c music_flac.h music_mad.c music_mad.h music_mod.c music_mod.h music_modplug.c music_modplug.h music_ogg.c music_ogg.h native_midi native_midi_gpl playmus.c playwave.c timidity wavestream.c wavestream.h version.rc LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ diff --git a/SDL_mixer.h b/SDL_mixer.h index 0bfdd3e5..8bddc4fb 100644 --- a/SDL_mixer.h +++ b/SDL_mixer.h @@ -122,7 +122,8 @@ typedef enum { MUS_OGG, MUS_MP3, MUS_MP3_MAD, - MUS_FLAC + MUS_FLAC, + MUS_MODPLUG } Mix_MusicType; /* The internal format for a music chunk interpreted via mikmod */ @@ -586,8 +587,8 @@ extern DECLSPEC int SDLCALL Mix_PausedMusic(void); /* Set the current position in the music stream. This returns 0 if successful, or -1 if it failed or isn't implemented. This function is only implemented for MOD music formats (set pattern - order number) and for OGG music (set position in seconds), at the - moment. + order number) and for OGG, FLAC, MP3_MAD, and MODPLUG music (set + position in seconds), at the moment. */ extern DECLSPEC int SDLCALL Mix_SetMusicPosition(double position); diff --git a/configure.in b/configure.in index 7d233877..4a1c5469 100644 --- a/configure.in +++ b/configure.in @@ -271,10 +271,30 @@ return 1; fi else AC_MSG_WARN([*** Unable to find MikMod library (http://mikmod.raphnet.net/)]) - AC_MSG_WARN([MOD support disabled]) fi fi +AC_ARG_ENABLE([music-mod-modplug], +AC_HELP_STRING([--enable-music-mod-modplug], [enable MOD music via modplug [[default=no]]]), + [], [enable_music_mod_modplug=no]) +if test x$enable_music_mod_modplug = xyes; then + PKG_CHECK_MODULES([MODPLUG], [libmodplug >= 0.8.7]) + EXTRA_CFLAGS="$EXTRA_CFLAGS -DMODPLUG_MUSIC $MODPLUG_CFLAGS" + EXTRA_LDFLAGS="$EXTRA_LDFLAGS $MODPLUG_LIBS" +dnl AC_TRY_COMPILE([ +dnl #include "modplug.h" +dnl ],[ +dnl ],[ + have_libmodplug=yes +dnl ]) +else + have_libmodplug=no +fi + +if test x$have_libmikmod != xyes -a x$have_libmodplug != xyes ; then + AC_MSG_WARN([MOD support disabled]) +fi + AC_ARG_ENABLE([music-midi], AC_HELP_STRING([--enable-music-midi], [enable MIDI music via timidity [[default=yes]]]), [], [enable_music_midi=yes]) @@ -519,6 +539,7 @@ if test x$enable_music_mp3_mad_gpl = xyes; then SOURCES="$SOURCES $srcdir/music_mad.c" EXTRA_CFLAGS="$EXTRA_CFLAGS -DMP3_MAD_MUSIC" EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lmad" + SOURCES="$SOURCES $srcdir/music_modplug.c" else AC_MSG_WARN([*** Unable to find MAD library (http://www.underbit.com/products/mad/)]) fi diff --git a/music.c b/music.c index 23ab4997..e9168e1e 100644 --- a/music.c +++ b/music.c @@ -38,6 +38,9 @@ #ifdef WAV_MUSIC #include "wavestream.h" #endif +#ifdef MODPLUG_MUSIC +#include "music_modplug.h" +#endif #ifdef MOD_MUSIC #include "music_mod.h" #endif @@ -88,6 +91,9 @@ struct _Mix_Music { #ifdef WAV_MUSIC WAVStream *wave; #endif +#ifdef MODPLUG_MUSIC + modplug_data *modplug; +#endif #ifdef MOD_MUSIC struct MODULE *module; #endif @@ -253,6 +259,11 @@ void music_mixer(void *udata, Uint8 *stream, int len) 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); @@ -309,6 +320,13 @@ int open_music(SDL_AudioSpec *mixer) add_music_decoder("WAVE"); } #endif +#ifdef MODPLUG_MUSIC + if ( modplug_init(mixer) < 0 ) { + ++music_error; + } else { + add_music_decoder("MODPLUG"); + } +#endif #ifdef MOD_MUSIC if ( MOD_init(mixer) == 0 ) { add_music_decoder("MIKMOD"); @@ -527,6 +545,15 @@ Mix_Music *Mix_LoadMUS(const char *file) } } else #endif +#ifdef MODPLUG_MUSIC + if ( 1 ) { + music->type = MUS_MODPLUG; + music->data.modplug = modplug_new(file); + if ( music->data.modplug == NULL ) { + music->error = 1; + } + } else +#endif #ifdef MOD_MUSIC if ( 1 ) { music->type = MUS_MOD; @@ -576,6 +603,11 @@ void Mix_FreeMusic(Mix_Music *music) 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); @@ -671,6 +703,13 @@ static int music_internal_play(Mix_Music *music, double position) 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); @@ -787,6 +826,11 @@ 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); @@ -867,6 +911,11 @@ static void music_internal_volume(int volume) 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); @@ -945,6 +994,11 @@ static void music_internal_halt(void) 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); @@ -1091,6 +1145,13 @@ static int music_internal_playing() } 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) ) { @@ -1198,6 +1259,9 @@ void close_music(void) #ifdef CMD_MUSIC Mix_SetMusicCMD(NULL); #endif +#ifdef MODPLUG_MUSIC + modplug_exit(); +#endif #ifdef MOD_MUSIC MOD_exit(); #endif @@ -1335,15 +1399,29 @@ Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) #endif } else #endif -#ifdef MOD_MUSIC +#if defined(MODPLUG_MUSIC) || defined(MODPLUG_MUSIC) if (1) { - music->type=MUS_MOD; - music->data.module = MOD_new_RW(rw); - if ( music->data.module == NULL ) { - music->error = 1; + music->error = 1; +#ifdef MODPLUG_MUSIC + if ( music->error ) { + music->type = MUS_MODPLUG; + music->data.modplug = modplug_new_RW(rw); + if ( music->data.modplug ) { + music->error = 0; + } } - } else #endif +#ifdef MOD_MUSIC + if ( music->error ) { + music->type = MUS_MOD; + music->data.module = MOD_new_RW(rw); + if ( music->data.module ) { + music->error = 0; + } + } +#endif + } else +#endif /* MODPLUG_MUSIC || MOD_MUSIC */ { Mix_SetError("Unrecognized music format"); music->error=1; diff --git a/music_modplug.c b/music_modplug.c new file mode 100644 index 00000000..b238f99d --- /dev/null +++ b/music_modplug.c @@ -0,0 +1,238 @@ +#ifdef MODPLUG_MUSIC + +#include "music_modplug.h" + +static int current_output_channels=0; +static int music_swap8=0; +static int music_swap16=0; +static ModPlug_Settings settings; + +int modplug_init(SDL_AudioSpec *spec) +{ + ModPlug_GetSettings(&settings); + settings.mFlags=MODPLUG_ENABLE_OVERSAMPLING; + current_output_channels=spec->channels; + settings.mChannels=spec->channels>1?2:1; + settings.mBits=spec->format&0xFF; + + music_swap8 = 0; + music_swap16 = 0; + + switch(spec->format) + { + case AUDIO_U8: + case AUDIO_S8: { + if ( spec->format == AUDIO_S8 ) { + music_swap8 = 1; + } + settings.mBits=8; + } + break; + + case AUDIO_S16LSB: + case AUDIO_S16MSB: { + /* See if we need to correct MikMod mixing */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + if ( spec->format == AUDIO_S16MSB ) { +#else + if ( spec->format == AUDIO_S16LSB ) { +#endif + music_swap16 = 1; + } + settings.mBits=16; + } + break; + + default: { + Mix_SetError("Unknown hardware audio format"); + return -1; + } + + } + + settings.mFrequency=spec->freq; /*TODO: limit to 11025, 22050, or 44100 ? */ + settings.mResamplingMode=MODPLUG_RESAMPLE_FIR; + settings.mReverbDepth=0; + settings.mReverbDelay=100; + settings.mBassAmount=0; + settings.mBassRange=50; + settings.mSurroundDepth=0; + settings.mSurroundDelay=10; + settings.mLoopCount=0; + ModPlug_SetSettings(&settings); +} + +/* Uninitialize the music players */ +void modplug_exit() +{ +} + +/* Set the volume for a modplug stream */ +void modplug_setvolume(modplug_data *music, int volume) +{ + ModPlug_SetMasterVolume(music->file, volume*4); +} + +/* Load a modplug stream from the given file */ +modplug_data *modplug_new(const char *file) +{ + SDL_RWops *rw; + + rw = SDL_RWFromFile(file, "rb"); + if ( rw == NULL ) { + /* FIXME: Free rw, need to free on delete */ + SDL_SetError("Couldn't open %s", file); + return NULL; + } + return modplug_new_RW(rw); + +} + +/* Load a modplug stream from an SDL_RWops object */ +modplug_data *modplug_new_RW(SDL_RWops *rw) +{ + modplug_data *music=NULL; + long offset,sz; + char *buf=NULL; + + offset = SDL_RWtell(rw); + SDL_RWseek(rw, 0, RW_SEEK_END); + sz = SDL_RWtell(rw)-offset; + SDL_RWseek(rw, offset, RW_SEEK_SET); + buf=(char*)malloc(sz); + if(!buf) + return NULL; + if(SDL_RWread(rw, buf, sz, 1)==1) + { + music=(modplug_data*)malloc(sizeof(modplug_data)); + music->playing=0; + music->file=ModPlug_Load(buf,sz); + if(!music->file) + { + free(music); + music=NULL; + } + } + free(buf); + return music; +} + +/* Start playback of a given modplug stream */ +void modplug_play(modplug_data *music) +{ + ModPlug_Seek(music->file,0); + music->playing=1; +} + +/* Return non-zero if a stream is currently playing */ +int modplug_playing(modplug_data *music) +{ + return music && music->playing; +} + +/* Play some of a stream previously started with modplug_play() */ +int modplug_playAudio(modplug_data *music, Uint8 *stream, int len) +{ + if (current_output_channels > 2) { + int small_len = 2 * len / current_output_channels; + int i; + Uint8 *src, *dst; + + i=ModPlug_Read(music->file, stream, small_len); + if(iplaying=0; + } + /* and extend to len by copying channels */ + src = stream + small_len; + dst = stream + len; + + switch (settings.mBits) { + case 8: + for ( i=small_len/2; i; --i ) { + src -= 2; + dst -= current_output_channels; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[1]; + if (current_output_channels == 6) { + dst[4] = src[0]; + dst[5] = src[1]; + } + } + break; + case 16: + for ( i=small_len/4; i; --i ) { + src -= 4; + dst -= 2 * current_output_channels; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[0]; + dst[5] = src[1]; + dst[6] = src[2]; + dst[7] = src[3]; + if (current_output_channels == 6) { + dst[8] = src[0]; + dst[9] = src[1]; + dst[10] = src[2]; + dst[11] = src[3]; + } + } + break; + } + } else { + int i=ModPlug_Read(music->file, stream, len); + if(iplaying=0; + } + } + if ( music_swap8 ) { + Uint8 *dst; + int i; + + dst = stream; + for ( i=len; i; --i ) { + *dst++ ^= 0x80; + } + } else + if ( music_swap16 ) { + Uint8 *dst, tmp; + int i; + + dst = stream; + for ( i=(len/2); i; --i ) { + tmp = dst[0]; + dst[0] = dst[1]; + dst[1] = tmp; + dst += 2; + } + } + return 0; +} + +/* Stop playback of a stream previously started with modplug_play() */ +void modplug_stop(modplug_data *music) +{ + music->playing=0; +} + +/* Close the given modplug stream */ +void modplug_delete(modplug_data *music) +{ + ModPlug_Unload(music->file); + free(music); +} + +/* Jump (seek) to a given position (time is in seconds) */ +void modplug_jump_to_time(modplug_data *music, double time) +{ + ModPlug_Seek(music->file,(int)(time*1000)); +} + +#endif diff --git a/music_modplug.h b/music_modplug.h new file mode 100644 index 00000000..1c90b673 --- /dev/null +++ b/music_modplug.h @@ -0,0 +1,45 @@ +#ifdef MODPLUG_MUSIC + +#include "modplug.h" +#include "SDL_rwops.h" +#include "SDL_audio.h" +#include "SDL_mixer.h" + +typedef struct { + ModPlugFile *file; + int playing; +} modplug_data; + +int modplug_init(SDL_AudioSpec *mixer); + +/* Uninitialize the music players */ +void modplug_exit(void); + +/* Set the volume for a modplug stream */ +void modplug_setvolume(modplug_data *music, int volume); + +/* Load a modplug stream from the given file */ +modplug_data *modplug_new(const char *file); + +/* Load a modplug stream from an SDL_RWops object */ +modplug_data *modplug_new_RW(SDL_RWops *rw); + +/* Start playback of a given modplug stream */ +void modplug_play(modplug_data *music); + +/* Return non-zero if a stream is currently playing */ +int modplug_playing(modplug_data *music); + +/* Play some of a stream previously started with modplug_play() */ +int modplug_playAudio(modplug_data *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with modplug_play() */ +void modplug_stop(modplug_data *music); + +/* Close the given modplug stream */ +void modplug_delete(modplug_data *music); + +/* Jump (seek) to a given position (time is in seconds) */ +void modplug_jump_to_time(modplug_data *music, double time); + +#endif