From d209015b6576cbeaa04ea7a66644dbce7a23e8bd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 20 Oct 2017 23:39:04 -0700 Subject: [PATCH] Switched to using SDL_AudioStream in SDL 2.0.7 for better streaming resampling support Also made the following changes: * Moved looping support into the music interfaces for better seamless looping when resampling * Music interfaces always start play from the beginning of the song * Implemented 24-bit and surround sound support for FLAC audio files * Implemented sample dithering in MAD music interface using GPL code * Improved error checking in music interfaces --- CHANGES.txt | 2 + configure | 45 ++-- configure.in | 9 +- mixer.c | 22 +- music.c | 145 ++++++------ music.h | 9 +- music_cmd.c | 14 +- music_flac.c | 572 +++++++++++++++++---------------------------- music_fluidsynth.c | 198 +++++++--------- music_mad.c | 451 ++++++++++++++++++++--------------- music_mikmod.c | 1 + music_modplug.c | 283 ++++++++++------------ music_mpg123.c | 431 +++++++++++++--------------------- music_nativemidi.c | 8 +- music_ogg.c | 376 +++++++++++++++-------------- music_smpeg.c | 70 ++++-- music_timidity.c | 67 ++++-- music_wav.c | 259 ++++++++++---------- 18 files changed, 1416 insertions(+), 1546 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b73b2e4a..bdd4a88d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,6 @@ 2.0.2: +Sam Lantinga - Fri Oct 20 22:04:50 PDT 2017 + * Implemented 24-bit and surround sound support for FLAC audio files Sam Lantinga - Thu Oct 12 21:32:44 PDT 2017 * Updated external libraries flac-1.3.2, libmodplug-0.8.9.0, libogg-1.3.2 and libvorbis-1.3.5 Ryan Gordon - Thu Oct 12 21:29:59 PDT 2017 diff --git a/configure b/configure index f860964e..34b1db01 100755 --- a/configure +++ b/configure @@ -919,6 +919,7 @@ with_smpeg_prefix with_smpeg_exec_prefix enable_smpegtest enable_music_mp3_mad_gpl +enable_music_mp3_mad_gpl_dithering enable_music_mp3_mpg123 enable_music_mp3_mpg123_shared ' @@ -1591,6 +1592,8 @@ Optional Features: --disable-smpegtest Do not try to compile and run a test SMPEG program --enable-music-mp3-mad-gpl enable MP3 music via libmad GPL code [[default=no]] + --enable-music-mp3-mad-gpl-dithering + enable MP3 music via libmad GPL code [[default=yes]] --enable-music-mp3-mpg123 enable MP3 music via libmpg123 [[default=yes]] --enable-music-mp3-mpg123-shared @@ -3927,13 +3930,13 @@ if ${lt_cv_nm_interface+:} false; then : else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3930: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3933: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3933: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3936: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3936: output\"" >&5) + (eval echo "\"\$as_me:3939: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5144,7 +5147,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5147 "configure"' > conftest.$ac_ext + echo '#line 5150 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6974,11 +6977,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6977: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6980: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6981: \$? = $ac_status" >&5 + echo "$as_me:6984: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7313,11 +7316,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7316: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7319: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7320: \$? = $ac_status" >&5 + echo "$as_me:7323: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7418,11 +7421,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7421: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7424: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7425: \$? = $ac_status" >&5 + echo "$as_me:7428: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7473,11 +7476,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7476: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7479: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7480: \$? = $ac_status" >&5 + echo "$as_me:7483: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -9842,7 +9845,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9845 "configure" +#line 9848 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -9938,7 +9941,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9941 "configure" +#line 9944 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11158,7 +11161,7 @@ find_lib() done } -SDL_VERSION=2.0.0 +SDL_VERSION=2.0.7 if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then @@ -12851,6 +12854,13 @@ else enable_music_mp3_mad_gpl=no fi +# Check whether --enable-music-mp3-mad-gpl-dithering was given. +if test "${enable_music_mp3_mad_gpl_dithering+set}" = set; then : + enableval=$enable_music_mp3_mad_gpl_dithering; +else + enable_music_mp3_mad_gpl_dithering=yes +fi + if test x$enable_music_mp3 = xyes -a x$enable_music_mp3_mad_gpl = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmad headers" >&5 $as_echo_n "checking for libmad headers... " >&6; } @@ -12879,6 +12889,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext $as_echo "$have_libmad" >&6; } if test x$have_libmad = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MAD" + if test x$enable_music_mp3_mad_gpl_dithering = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Using GPL libmad and MP3 dithering routines, this build of SDL_mixer is now under the GPL" >&5 +$as_echo "$as_me: WARNING: *** Using GPL libmad and MP3 dithering routines, this build of SDL_mixer is now under the GPL" >&2;} + EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MAD_GPL_DITHERING" + fi EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lmad" else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Unable to find MAD library (http://www.underbit.com/products/mad/)" >&5 diff --git a/configure.in b/configure.in index 5268c54c..af85b38c 100644 --- a/configure.in +++ b/configure.in @@ -188,7 +188,7 @@ find_lib() } dnl Check for SDL -SDL_VERSION=2.0.0 +SDL_VERSION=2.0.7 AM_PATH_SDL2($SDL_VERSION, :, AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) @@ -608,6 +608,9 @@ fi AC_ARG_ENABLE(music-mp3-mad-gpl, AC_HELP_STRING([--enable-music-mp3-mad-gpl], [enable MP3 music via libmad GPL code [[default=no]]]), [], [enable_music_mp3_mad_gpl=no]) +AC_ARG_ENABLE(music-mp3-mad-gpl-dithering, +AC_HELP_STRING([--enable-music-mp3-mad-gpl-dithering], [enable MP3 music via libmad GPL code [[default=yes]]]), + [], [enable_music_mp3_mad_gpl_dithering=yes]) if test x$enable_music_mp3 = xyes -a x$enable_music_mp3_mad_gpl = xyes; then AC_MSG_CHECKING(for libmad headers) have_libmad=no @@ -620,6 +623,10 @@ if test x$enable_music_mp3 = xyes -a x$enable_music_mp3_mad_gpl = xyes; then AC_MSG_RESULT($have_libmad) if test x$have_libmad = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MAD" + if test x$enable_music_mp3_mad_gpl_dithering = xyes; then + AC_MSG_WARN([*** Using GPL libmad and MP3 dithering routines, this build of SDL_mixer is now under the GPL]) + EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MAD_GPL_DITHERING" + fi EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lmad" else AC_MSG_WARN([*** Unable to find MAD library (http://www.underbit.com/products/mad/)]) diff --git a/mixer.c b/mixer.c index 46a6d9c0..547b67cf 100644 --- a/mixer.c +++ b/mixer.c @@ -259,9 +259,7 @@ mix_channels(void *udata, Uint8 *stream, int len) #endif /* Mix the music (must be done before the channels are added) */ - if (music_active || (mix_music != music_mixer)) { - mix_music(music_data, stream, len); - } + mix_music(music_data, stream, len); /* Mix any playing channels... */ sdl_ticks = SDL_GetTicks(); @@ -548,7 +546,6 @@ static SDL_AudioSpec *Mix_LoadMusic_RW(Mix_MusicType music_type, SDL_RWops *src, MusicFragment *first = NULL, *last = NULL, *fragment = NULL; int count = 0; int fragment_size; - int original_volume; *spec = mixer; @@ -579,12 +576,6 @@ static SDL_AudioSpec *Mix_LoadMusic_RW(Mix_MusicType music_type, SDL_RWops *src, break; } - if (interface->api == MIX_MUSIC_SMPEG) { - /* Uh oh, if SMPEG couldn't create anything, it freed the src */ - freesrc = SDL_FALSE; - break; - } - /* Reset the stream for the next decoder */ SDL_RWseek(src, start, RW_SEEK_SET); } @@ -598,14 +589,9 @@ static SDL_AudioSpec *Mix_LoadMusic_RW(Mix_MusicType music_type, SDL_RWops *src, } Mix_LockAudio(); - original_volume = music_volume; - - if (interface->SetVolume) { - interface->SetVolume(music, MIX_MAX_VOLUME); - } if (interface->Play) { - interface->Play(music); + interface->Play(music, 1); } playing = SDL_TRUE; @@ -647,10 +633,6 @@ static SDL_AudioSpec *Mix_LoadMusic_RW(Mix_MusicType music_type, SDL_RWops *src, interface->Stop(music); } - if (interface->SetVolume) { - interface->SetVolume(music, original_volume); - } - if (music) { interface->Delete(music); } diff --git a/music.c b/music.c index 504d0579..d53bc65e 100644 --- a/music.c +++ b/music.c @@ -43,10 +43,9 @@ char *music_cmd = NULL; -int volatile music_active = 1; +static SDL_bool music_active = SDL_TRUE; +static int music_volume = MIX_MAX_VOLUME; static Mix_Music * volatile music_playing = NULL; -int music_loops = 0; -int music_volume = MIX_MAX_VOLUME; SDL_AudioSpec music_spec; struct _Mix_Music { @@ -57,7 +56,6 @@ struct _Mix_Music { Mix_Fading fading; int fade_step; int fade_steps; - int error; }; /* Used to calculate fading steps */ @@ -147,7 +145,7 @@ static void add_music_decoder(const char *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_play(Mix_Music *music, int play_count, double position); static int music_internal_position(double position); static SDL_bool music_internal_playing(void); static void music_internal_halt(void); @@ -163,49 +161,46 @@ void Mix_HookMusicFinished(void (*music_finished)(void)) Mix_UnlockAudio(); } - -/* If music isn't playing, halt it if no looping is required, restart it */ -/* otherwise. NOP if the music is playing */ -static int music_halt_or_loop(void) +/* Convenience function to fill audio and mix at the specified volume + This is called from many music player's GetAudio callback. + */ +int music_pcm_getaudio(void *context, void *data, int bytes, int volume, + int (*GetSome)(void *context, void *data, int bytes, SDL_bool *done)) { - /* Restart music if it has to loop */ + Uint8 *snd = (Uint8 *)data; + Uint8 *dst; + int len = bytes; + SDL_bool done = SDL_FALSE; - if (!music_internal_playing()) - { - /* Native MIDI handles looping internally */ - if (music_playing->interface->api == MIX_MUSIC_NATIVEMIDI) { - music_loops = 0; - } - - /* Restart music if it has to loop at a high level */ - if (music_loops) - { - Mix_Fading current_fade; - if (music_loops > 0) { - --music_loops; - } - current_fade = music_playing->fading; - music_internal_play(music_playing, 0.0); - music_playing->fading = current_fade; + if (volume == MIX_MAX_VOLUME) { + dst = snd; + } else { + dst = SDL_stack_alloc(Uint8, bytes); + } + while (len > 0 && !done) { + int consumed = GetSome(context, dst, len, &done); + if (consumed < 0) { + break; } - else - { - music_internal_halt(); - if (music_finished_hook) - music_finished_hook(); - return 0; + if (volume == MIX_MAX_VOLUME) { + dst += consumed; + } else { + SDL_MixAudioFormat(snd, dst, music_spec.format, (Uint32)consumed, volume); + snd += consumed; } + len -= consumed; } - - return 1; + if (volume != MIX_MAX_VOLUME) { + SDL_stack_free(dst); + } + return len; } - /* Mixing function */ void music_mixer(void *udata, Uint8 *stream, int len) { - if (music_playing && music_active) { + while (music_playing && music_active && len > 0) { /* Handle fading */ if (music_playing->fading != MIX_NO_FADING) { if (music_playing->fade_step++ < music_playing->fade_steps) { @@ -231,23 +226,26 @@ void music_mixer(void *udata, Uint8 *stream, int len) } } - music_halt_or_loop(); - if (!music_internal_playing()) { - return; - } - if (music_playing->interface->GetAudio) { int left = music_playing->interface->GetAudio(music_playing->context, stream, len); - if (left > 0) { + if (left != 0) { + /* Either an error or finished playing with data left */ music_playing->playing = SDL_FALSE; } + if (left > 0) { + stream += (len - left); + len = left; + } else { + len = 0; + } + } else { + len = 0; + } - /* Handle seamless music looping */ - if (left > 0 && left < len) { - music_halt_or_loop(); - if (music_internal_playing()) { - music_mixer(udata, stream+(len-left), left); - } + if (!music_internal_playing()) { + music_internal_halt(); + if (music_finished_hook) { + music_finished_hook(); } } } @@ -523,12 +521,6 @@ Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *src, Mix_MusicType type, int freesrc) return music; } - if (interface->api == MIX_MUSIC_SMPEG) { - /* Uh oh, if SMPEG couldn't create a context, it freed the src */ - freesrc = SDL_FALSE; - break; - } - /* Reset the stream for the next decoder */ SDL_RWseek(src, start, RW_SEEK_SET); } @@ -589,7 +581,7 @@ Mix_MusicType Mix_GetMusicType(const Mix_Music *music) /* Play a music chunk. Returns 0, or -1 if there was an error. */ -static int music_internal_play(Mix_Music *music, double position) +static int music_internal_play(Mix_Music *music, int play_count, double position) { int retval = 0; @@ -615,7 +607,7 @@ static int music_internal_play(Mix_Music *music, double position) music_internal_initialize_volume(); /* Set up for playback */ - retval = music->interface->Play(music->context); + retval = music->interface->Play(music->context, play_count); /* Set the playback position, note any errors if an offset is used */ if (retval == 0) { @@ -631,6 +623,7 @@ static int music_internal_play(Mix_Music *music, double position) /* If the setup failed, we're not playing any music anymore */ if (retval < 0) { + music->playing = SDL_FALSE; music_playing = NULL; } return(retval); @@ -668,13 +661,11 @@ int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) SDL_Delay(100); Mix_LockAudio(); } - music_active = 1; - if (loops == 1) { + if (loops == 0) { /* Loop is the number of times to play the audio */ - loops = 0; + loops = 1; } - music_loops = loops; - retval = music_internal_play(music, position); + retval = music_internal_play(music, loops, position); Mix_UnlockAudio(); return(retval); @@ -833,20 +824,24 @@ Mix_Fading Mix_FadingMusic(void) void Mix_PauseMusic(void) { Mix_LockAudio(); - if (music_playing && music_playing->interface->Pause) { - music_playing->interface->Pause(music_playing->context); + if (music_playing) { + if (music_playing->interface->Pause) { + music_playing->interface->Pause(music_playing->context); + } } - music_active = 0; + music_active = SDL_FALSE; Mix_UnlockAudio(); } void Mix_ResumeMusic(void) { Mix_LockAudio(); - music_active = 1; - if (music_playing && music_playing->interface->Resume) { - music_playing->interface->Resume(music_playing->context); + if (music_playing) { + if (music_playing->interface->Resume) { + music_playing->interface->Resume(music_playing->context); + } } + music_active = SDL_TRUE; Mix_UnlockAudio(); } @@ -857,32 +852,30 @@ void Mix_RewindMusic(void) int Mix_PausedMusic(void) { - return (music_active == 0); + return (music_active == SDL_FALSE); } /* Check the status of the music */ static SDL_bool music_internal_playing(void) { - if (music_playing == NULL) { + if (!music_playing) { return SDL_FALSE; } if (music_playing->interface->IsPlaying) { - return music_playing->interface->IsPlaying(music_playing->context); + music_playing->playing = music_playing->interface->IsPlaying(music_playing->context); } return music_playing->playing; } int Mix_PlayingMusic(void) { - int playing = 0; + SDL_bool playing; Mix_LockAudio(); - if (music_playing) { - playing = music_loops || music_internal_playing(); - } + playing = music_internal_playing(); Mix_UnlockAudio(); - return(playing); + return playing ? 1 : 0; } /* Set the external music playback command */ diff --git a/music.h b/music.h index f595e92c..29bfa5bc 100644 --- a/music.h +++ b/music.h @@ -70,8 +70,8 @@ typedef struct /* Set the volume */ void (*SetVolume)(void *music, int volume); - /* Start playing music */ - int (*Play)(void *music); + /* Start playing music from the beginning with an optional loop count */ + int (*Play)(void *music, int play_count); /* Returns SDL_TRUE if music is still playing */ SDL_bool (*IsPlaying)(void *music); @@ -109,14 +109,13 @@ extern int get_num_music_interfaces(void); extern Mix_MusicInterface *get_music_interface(int index); extern SDL_bool has_music(Mix_MusicType type); extern int open_music(const SDL_AudioSpec *spec); +extern int music_pcm_getaudio(void *context, void *data, int bytes, int volume, + int (*GetSome)(void *context, void *data, int bytes, SDL_bool *done)); extern void music_mixer(void *udata, Uint8 *stream, int len); extern void close_music(void); extern void unload_music(void); extern char *music_cmd; -extern int volatile music_active; -extern int music_loops; -extern int music_volume; extern SDL_AudioSpec music_spec; extern char *soundfont_paths; diff --git a/music_cmd.c b/music_cmd.c index 7f0ac5d7..8c5900d4 100644 --- a/music_cmd.c +++ b/music_cmd.c @@ -44,6 +44,7 @@ typedef struct { char *file; char *cmd; pid_t pid; + int play_count; } MusicCMD; @@ -148,10 +149,11 @@ static char **parse_args(char *command, char *last_arg) } /* Start playback of a given music stream */ -static int MusicCMD_Play(void *context) +static int MusicCMD_Play(void *context, int play_count) { MusicCMD *music = (MusicCMD *)context; + music->play_count = play_count; #ifdef HAVE_FORK music->pid = fork(); #else @@ -204,6 +206,16 @@ static SDL_bool MusicCMD_IsPlaying(void *context) if (kill(music->pid, 0) == 0) { return SDL_TRUE; } + + /* We might want to loop */ + if (music->play_count != 1) { + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + MusicCMD_Play(music, play_count); + return SDL_TRUE; + } } return SDL_FALSE; } diff --git a/music_flac.c b/music_flac.c index 838cd46c..9d270656 100644 --- a/music_flac.c +++ b/music_flac.c @@ -24,6 +24,7 @@ #ifdef MUSIC_FLAC +#include "SDL_assert.h" #include "SDL_loadso.h" #include "music_flac.h" @@ -188,7 +189,7 @@ static int FLAC_Load(void) Mix_SetError("Missing FLAC.framework"); return -1; } -#endif // __MACOSX__ +#endif /* __MACOSX__ */ flac.FLAC__stream_decoder_new = FLAC__stream_decoder_new; flac.FLAC__stream_decoder_delete = FLAC__stream_decoder_delete; @@ -226,51 +227,35 @@ static void FLAC_Unload(void) typedef struct { - FLAC__uint64 sample_size; + int volume; + int play_count; + FLAC__StreamDecoder *flac_decoder; unsigned sample_rate; unsigned channels; unsigned bits_per_sample; - FLAC__uint64 total_samples; - - // the following are used to handle the callback nature of the writer - int max_to_read; - char *data; // pointer to beginning of data array - int data_len; // size of data array - int data_read; // amount of data array used - char *overflow; // pointer to beginning of overflow array - int overflow_len; // size of overflow array - int overflow_read; // amount of overflow array used -} FLAC_Data; - -typedef struct { - SDL_bool playing; - int volume; - int section; - FLAC__StreamDecoder *flac_decoder; - FLAC_Data flac_data; SDL_RWops *src; int freesrc; - SDL_AudioCVT cvt; - int len_available; - Uint8 *snd_available; -} FLAC_music; + SDL_AudioStream *stream; +} FLAC_Music; +static int FLAC_Seek(void *context, double position); + static FLAC__StreamDecoderReadStatus flac_read_music_cb( const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { - FLAC_music *data = (FLAC_music*)client_data; + FLAC_Music *data = (FLAC_Music*)client_data; - // make sure there is something to be reading + /* make sure there is something to be reading */ if (*bytes > 0) { *bytes = SDL_RWread (data->src, buffer, sizeof (FLAC__byte), *bytes); - if (*bytes == 0) { // error or no data was read (EOF) + if (*bytes == 0) { /* error or no data was read (EOF) */ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; - } else { // data was read, continue + } else { /* data was read, continue */ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } } else { @@ -283,9 +268,9 @@ static FLAC__StreamDecoderSeekStatus flac_seek_music_cb( FLAC__uint64 absolute_byte_offset, void *client_data) { - FLAC_music *data = (FLAC_music*)client_data; + FLAC_Music *data = (FLAC_Music*)client_data; - if (SDL_RWseek (data->src, absolute_byte_offset, RW_SEEK_SET) < 0) { + if (SDL_RWseek(data->src, absolute_byte_offset, RW_SEEK_SET) < 0) { return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; } else { return FLAC__STREAM_DECODER_SEEK_STATUS_OK; @@ -297,9 +282,9 @@ static FLAC__StreamDecoderTellStatus flac_tell_music_cb( FLAC__uint64 *absolute_byte_offset, void *client_data) { - FLAC_music *data = (FLAC_music*)client_data; + FLAC_Music *data = (FLAC_Music*)client_data; - Sint64 pos = SDL_RWtell (data->src); + Sint64 pos = SDL_RWtell(data->src); if (pos < 0) { return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; @@ -309,17 +294,17 @@ static FLAC__StreamDecoderTellStatus flac_tell_music_cb( } } -static FLAC__StreamDecoderLengthStatus flac_length_music_cb ( +static FLAC__StreamDecoderLengthStatus flac_length_music_cb( const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { - FLAC_music *data = (FLAC_music*)client_data; + FLAC_Music *data = (FLAC_Music*)client_data; - Sint64 pos = SDL_RWtell (data->src); - Sint64 length = SDL_RWseek (data->src, 0, RW_SEEK_END); + Sint64 pos = SDL_RWtell(data->src); + Sint64 length = SDL_RWseek(data->src, 0, RW_SEEK_END); - if (SDL_RWseek (data->src, pos, RW_SEEK_SET) != pos || length < 0) { + if (SDL_RWseek(data->src, pos, RW_SEEK_SET) != pos || length < 0) { /* there was an error attempting to return the stream to the original * position, or the length was invalid. */ return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; @@ -333,18 +318,18 @@ static FLAC__bool flac_eof_music_cb( const FLAC__StreamDecoder *decoder, void *client_data) { - FLAC_music *data = (FLAC_music*)client_data; + FLAC_Music *data = (FLAC_Music*)client_data; - Sint64 pos = SDL_RWtell (data->src); - Sint64 end = SDL_RWseek (data->src, 0, RW_SEEK_END); + Sint64 pos = SDL_RWtell(data->src); + Sint64 end = SDL_RWseek(data->src, 0, RW_SEEK_END); - // was the original position equal to the end (a.k.a. the seek didn't move)? + /* was the original position equal to the end (a.k.a. the seek didn't move)? */ if (pos == end) { - // must be EOF + /* must be EOF */ return true; } else { - // not EOF, return to the original position - SDL_RWseek (data->src, pos, RW_SEEK_SET); + /* not EOF, return to the original position */ + SDL_RWseek(data->src, pos, RW_SEEK_SET); return false; } } @@ -355,96 +340,81 @@ static FLAC__StreamDecoderWriteStatus flac_write_music_cb( const FLAC__int32 *const buffer[], void *client_data) { - FLAC_music *data = (FLAC_music *)client_data; - size_t i; + FLAC_Music *music = (FLAC_Music *)client_data; + Sint16 *data; + int i, j, channels; + int shift_amount = 0; - if (data->flac_data.total_samples == 0) { - SDL_SetError ("Given FLAC file does not specify its sample count."); + if (!music->stream) { return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } - if (data->flac_data.channels != 2 || - data->flac_data.bits_per_sample != 16) { - SDL_SetError("Current FLAC support is only for 16 bit Stereo files."); + switch (music->bits_per_sample) { + case 16: + shift_amount = 0; + break; + case 20: + shift_amount = 4; + break; + case 24: + shift_amount = 8; + break; + default: + SDL_SetError("FLAC decoder doesn't support %d bits_per_sample", music->bits_per_sample); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } - for (i = 0; i < frame->header.blocksize; i++) { - FLAC__int16 i16; - FLAC__uint16 ui16; - - // make sure we still have at least two bytes that can be read (one for - // each channel) - if (data->flac_data.max_to_read >= 4) { - // does the data block exist? - if (!data->flac_data.data) { - data->flac_data.data_len = data->flac_data.max_to_read; - data->flac_data.data_read = 0; - - // create it - data->flac_data.data = - (char *)SDL_malloc (data->flac_data.data_len); - - if (!data->flac_data.data) { - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - } - } - - i16 = (FLAC__int16)buffer[0][i]; - ui16 = (FLAC__uint16)i16; - - *((data->flac_data.data) + (data->flac_data.data_read++)) = - (char)(ui16); - *((data->flac_data.data) + (data->flac_data.data_read++)) = - (char)(ui16 >> 8); - - i16 = (FLAC__int16)buffer[1][i]; - ui16 = (FLAC__uint16)i16; - - *((data->flac_data.data) + (data->flac_data.data_read++)) = - (char)(ui16); - *((data->flac_data.data) + (data->flac_data.data_read++)) = - (char)(ui16 >> 8); + if (music->channels == 3) { + /* We'll just drop the center channel for now */ + channels = 2; + } else { + channels = music->channels; + } - data->flac_data.max_to_read -= 4; + data = SDL_stack_alloc(Sint16, (frame->header.blocksize * channels)); + if (!data) { + SDL_SetError("Couldn't allocate %d bytes stack memory", (int)(frame->header.blocksize * channels * sizeof(*data))); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if (music->channels == 3) { + Sint16 *dst = data; + for (i = 0; i < frame->header.blocksize; ++i) { + Sint16 FL = (buffer[0][i] >> shift_amount); + Sint16 FR = (buffer[1][i] >> shift_amount); + Sint16 FCmix = (Sint16)((buffer[2][i] >> shift_amount) * 0.5f); + int sample; + + sample = (FL + FCmix); + if (sample > SDL_MAX_SINT16) { + *dst = SDL_MAX_SINT16; + } else if (sample < SDL_MIN_SINT16) { + *dst = SDL_MIN_SINT16; + } else { + *dst = sample; + } + ++dst; - if (data->flac_data.max_to_read < 4) { - // we need to set this so that the read halts from the - // FLAC_getsome function. - data->flac_data.max_to_read = 0; + sample = (FR + FCmix); + if (sample > SDL_MAX_SINT16) { + *dst = SDL_MAX_SINT16; + } else if (sample < SDL_MIN_SINT16) { + *dst = SDL_MIN_SINT16; + } else { + *dst = sample; } - } else { - // we need to write to the overflow - if (!data->flac_data.overflow) { - data->flac_data.overflow_len = (int)(4 * (frame->header.blocksize - i)); - data->flac_data.overflow_read = 0; - - // make it big enough for the rest of the block - data->flac_data.overflow = - (char *)SDL_malloc (data->flac_data.overflow_len); - - if (!data->flac_data.overflow) { - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - } + ++dst; + } + } else { + for (i = 0; i < channels; ++i) { + Sint16 *dst = data + i; + for (j = 0; j < frame->header.blocksize; ++j) { + *dst = (buffer[i][j] >> shift_amount); + dst += channels; } - - i16 = (FLAC__int16)buffer[0][i]; - ui16 = (FLAC__uint16)i16; - - *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = - (char)(ui16); - *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = - (char)(ui16 >> 8); - - i16 = (FLAC__int16)buffer[1][i]; - ui16 = (FLAC__uint16)i16; - - *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = - (char)(ui16); - *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = - (char)(ui16 >> 8); } } + SDL_AudioStreamPut(music->stream, data, (frame->header.blocksize * channels * sizeof(*data))); + SDL_stack_free(data); return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } @@ -454,18 +424,30 @@ static void flac_metadata_music_cb( const FLAC__StreamMetadata *metadata, void *client_data) { - FLAC_music *data = (FLAC_music *)client_data; - - if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { - data->flac_data.sample_rate = metadata->data.stream_info.sample_rate; - data->flac_data.channels = metadata->data.stream_info.channels; - data->flac_data.total_samples = - metadata->data.stream_info.total_samples; - data->flac_data.bits_per_sample = - metadata->data.stream_info.bits_per_sample; - data->flac_data.sample_size = data->flac_data.channels * - ((data->flac_data.bits_per_sample) / 8); + FLAC_Music *music = (FLAC_Music *)client_data; + int channels; + + if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO) { + return; } + + music->sample_rate = metadata->data.stream_info.sample_rate; + music->channels = metadata->data.stream_info.channels; + music->bits_per_sample = metadata->data.stream_info.bits_per_sample; +/*printf("FLAC: Sample rate = %d, channels = %d, bits_per_sample = %d\n", music->sample_rate, music->channels, music->bits_per_sample);*/ + + /* SDL's channel mapping and FLAC channel mapping are the same, + except for 3 channels: SDL is FL FR LFE and FLAC is FL FR FC + */ + if (music->channels == 3) { + channels = 2; + } else { + channels = music->channels; + } + /* We check for NULL stream later when we get data */ + SDL_assert(!music->stream); + music->stream = SDL_NewAudioStream(AUDIO_S16SYS, channels, music->sample_rate, + music_spec.format, music_spec.channels, music_spec.freq); } static void flac_error_music_cb( @@ -473,22 +455,22 @@ static void flac_error_music_cb( FLAC__StreamDecoderErrorStatus status, void *client_data) { - // print an SDL error based on the error status + /* print an SDL error based on the error status */ switch (status) { - case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: - SDL_SetError ("Error processing the FLAC file [LOST_SYNC]."); + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + SDL_SetError("Error processing the FLAC file [LOST_SYNC]."); break; - case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: - SDL_SetError ("Error processing the FLAC file [BAD_HEADER]."); + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + SDL_SetError("Error processing the FLAC file [BAD_HEADER]."); break; - case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: - SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH]."); + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + SDL_SetError("Error processing the FLAC file [CRC_MISMATCH]."); break; - case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: - SDL_SetError ("Error processing the FLAC file [UNPARSEABLE]."); + case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: + SDL_SetError("Error processing the FLAC file [UNPARSEABLE]."); break; - default: - SDL_SetError ("Error processing the FLAC file [UNKNOWN]."); + default: + SDL_SetError("Error processing the FLAC file [UNKNOWN]."); break; } } @@ -496,243 +478,130 @@ static void flac_error_music_cb( /* Load an FLAC stream from an SDL_RWops object */ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc) { - FLAC_music *music; + FLAC_Music *music; int init_stage = 0; int was_error = 1; - if (!Mix_Init(MIX_INIT_FLAC)) { + music = (FLAC_Music *)SDL_calloc(1, sizeof(*music)); + if (!music) { + SDL_OutOfMemory(); return NULL; } - - music = (FLAC_music *)SDL_calloc(1, sizeof (*music)); - if (music) { - /* Initialize the music structure */ - music->volume = MIX_MAX_VOLUME; - music->section = -1; - music->src = src; - music->freesrc = freesrc; - music->flac_data.max_to_read = 0; - music->flac_data.overflow = NULL; - music->flac_data.overflow_len = 0; - music->flac_data.overflow_read = 0; - music->flac_data.data = NULL; - music->flac_data.data_len = 0; - music->flac_data.data_read = 0; - - init_stage++; // stage 1! - - music->flac_decoder = flac.FLAC__stream_decoder_new (); - - if (music->flac_decoder != NULL) { - init_stage++; // stage 2! - - if (flac.FLAC__stream_decoder_init_stream( - music->flac_decoder, - flac_read_music_cb, flac_seek_music_cb, - flac_tell_music_cb, flac_length_music_cb, - flac_eof_music_cb, flac_write_music_cb, - flac_metadata_music_cb, flac_error_music_cb, - music) == FLAC__STREAM_DECODER_INIT_STATUS_OK) { - init_stage++; // stage 3! - - if (flac.FLAC__stream_decoder_process_until_end_of_metadata - (music->flac_decoder)) { - was_error = 0; - } else { - SDL_SetError("FLAC__stream_decoder_process_until_end_of_metadata() failed"); - } + music->src = src; + music->volume = MIX_MAX_VOLUME; + + music->flac_decoder = flac.FLAC__stream_decoder_new(); + if (music->flac_decoder) { + init_stage++; /* stage 1! */ + + if (flac.FLAC__stream_decoder_init_stream( + music->flac_decoder, + flac_read_music_cb, flac_seek_music_cb, + flac_tell_music_cb, flac_length_music_cb, + flac_eof_music_cb, flac_write_music_cb, + flac_metadata_music_cb, flac_error_music_cb, + music) == FLAC__STREAM_DECODER_INIT_STATUS_OK) { + init_stage++; /* stage 2! */ + + if (flac.FLAC__stream_decoder_process_until_end_of_metadata(music->flac_decoder)) { + was_error = 0; } else { - SDL_SetError("FLAC__stream_decoder_init_stream() failed"); + SDL_SetError("FLAC__stream_decoder_process_until_end_of_metadata() failed"); } } else { - SDL_SetError("FLAC__stream_decoder_new() failed"); + SDL_SetError("FLAC__stream_decoder_init_stream() failed"); } + } else { + SDL_SetError("FLAC__stream_decoder_new() failed"); + } - if (was_error) { - switch (init_stage) { - case 3: - flac.FLAC__stream_decoder_finish(music->flac_decoder); - case 2: - flac.FLAC__stream_decoder_delete(music->flac_decoder); - case 1: - case 0: - SDL_free(music); - break; - } - return NULL; + if (was_error) { + switch (init_stage) { + case 2: + flac.FLAC__stream_decoder_finish(music->flac_decoder); + case 1: + flac.FLAC__stream_decoder_delete(music->flac_decoder); + case 0: + SDL_free(music); + break; } - } else { - SDL_OutOfMemory(); + return NULL; } + + music->freesrc = freesrc; return music; } /* Set the volume for an FLAC stream */ static void FLAC_SetVolume(void *context, int volume) { - FLAC_music *music = (FLAC_music *)context; + FLAC_Music *music = (FLAC_Music *)context; music->volume = volume; } /* Start playback of a given FLAC stream */ -static int FLAC_Play(void *context) -{ - FLAC_music *music = (FLAC_music *)context; - music->playing = SDL_TRUE; - return 0; -} - -/* Return non-zero if a stream is currently playing */ -static SDL_bool FLAC_IsPlaying(void *context) +static int FLAC_Play(void *context, int play_count) { - FLAC_music *music = (FLAC_music *)context; - return music->playing; + FLAC_Music *music = (FLAC_Music *)context; + music->play_count = play_count; + return FLAC_Seek(music, 0.0); } /* Read some FLAC stream data and convert it for output */ -static void FLAC_getsome(FLAC_music *music) +static int FLAC_GetSome(void *context, void *data, int bytes, SDL_bool *done) { - SDL_AudioCVT *cvt; - - /* GET AUDIO WAVE DATA */ - // set the max number of characters to read - music->flac_data.max_to_read = 8192; - music->flac_data.data_len = music->flac_data.max_to_read; - music->flac_data.data_read = 0; - if (!music->flac_data.data) { - music->flac_data.data = (char *)SDL_malloc (music->flac_data.data_len); - } - - // we have data to read - while(music->flac_data.max_to_read > 0) { - // first check if there is data in the overflow from before - if (music->flac_data.overflow) { - size_t overflow_len = music->flac_data.overflow_read; - - if (overflow_len > (size_t)music->flac_data.max_to_read) { - size_t overflow_extra_len = overflow_len - - music->flac_data.max_to_read; - - SDL_memcpy (music->flac_data.data+music->flac_data.data_read, - music->flac_data.overflow, music->flac_data.max_to_read); - music->flac_data.data_read += music->flac_data.max_to_read; - SDL_memcpy (music->flac_data.overflow, - music->flac_data.overflow + music->flac_data.max_to_read, - overflow_extra_len); - music->flac_data.overflow_len = (int)overflow_extra_len; - music->flac_data.overflow_read = (int)overflow_extra_len; - music->flac_data.max_to_read = 0; - } else { - SDL_memcpy (music->flac_data.data+music->flac_data.data_read, - music->flac_data.overflow, overflow_len); - music->flac_data.data_read += (int)overflow_len; - SDL_free (music->flac_data.overflow); - music->flac_data.overflow = NULL; - music->flac_data.overflow_len = 0; - music->flac_data.overflow_read = 0; - music->flac_data.max_to_read -= (int)overflow_len; - } - } else { - if (!flac.FLAC__stream_decoder_process_single ( - music->flac_decoder)) { - music->flac_data.max_to_read = 0; - } + FLAC_Music *music = (FLAC_Music *)context; + int filled; - if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) - == FLAC__STREAM_DECODER_END_OF_STREAM) { - music->flac_data.max_to_read = 0; - } - } + filled = SDL_AudioStreamGet(music->stream, data, bytes); + if (filled != 0) { + return filled; } - if (music->flac_data.data_read <= 0) { - if (music->flac_data.data_read == 0) { - music->playing = SDL_FALSE; - } - return; + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; + return 0; } - cvt = &music->cvt; - if (music->section < 0) { - SDL_BuildAudioCVT (cvt, AUDIO_S16, (Uint8)music->flac_data.channels, - (int)music->flac_data.sample_rate, - music_spec.format, - music_spec.channels, - music_spec.freq); - if (cvt->buf) { - SDL_free (cvt->buf); - } - cvt->buf = (Uint8 *)SDL_malloc (music->flac_data.data_len * cvt->len_mult); - music->section = 0; + + if (!flac.FLAC__stream_decoder_process_single(music->flac_decoder)) { + SDL_SetError("FLAC__stream_decoder_process_single() failed"); + return -1; } - if (cvt->buf) { - SDL_memcpy (cvt->buf, music->flac_data.data, music->flac_data.data_read); - if (cvt->needed) { - cvt->len = music->flac_data.data_read; - SDL_ConvertAudio (cvt); + + if (flac.FLAC__stream_decoder_get_state(music->flac_decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) { + if (music->play_count == 1) { + music->play_count = 0; + SDL_AudioStreamFlush(music->stream); } else { - cvt->len_cvt = music->flac_data.data_read; + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (FLAC_Play(music, play_count) < 0) { + return -1; + } } - music->len_available = music->cvt.len_cvt; - music->snd_available = music->cvt.buf; - } else { - SDL_SetError ("Out of memory"); - music->playing = SDL_FALSE; } + return 0; } /* Play some of a stream previously started with FLAC_play() */ static int FLAC_GetAudio(void *context, void *data, int bytes) { - FLAC_music *music = (FLAC_music *)context; - Uint8 *snd = (Uint8 *)data; - int len = bytes; - int mixable; - - while ((len > 0) && music->playing) { - if (!music->len_available) { - FLAC_getsome (music); - } - mixable = len; - if (mixable > music->len_available) { - mixable = music->len_available; - } - if (music->volume == MIX_MAX_VOLUME) { - SDL_memcpy (snd, music->snd_available, mixable); - } else { - SDL_MixAudioFormat(snd, music->snd_available, music_spec.format, mixable, music->volume); - } - music->len_available -= mixable; - music->snd_available += mixable; - len -= mixable; - snd += mixable; - } - - return len; + FLAC_Music *music = (FLAC_Music *)context; + return music_pcm_getaudio(context, data, bytes, music->volume, FLAC_GetSome); } /* Jump (seek) to a given position (position is in seconds) */ static int FLAC_Seek(void *context, double position) { - FLAC_music *music = (FLAC_music *)context; - double seek_sample = music->flac_data.sample_rate * position; + FLAC_Music *music = (FLAC_Music *)context; + double seek_sample = music->sample_rate * position; - // clear data if it has data - if (music->flac_data.data) { - SDL_free (music->flac_data.data); - music->flac_data.data = NULL; - } - - // clear overflow if it has data - if (music->flac_data.overflow) { - SDL_free (music->flac_data.overflow); - music->flac_data.overflow = NULL; - } - - if (!flac.FLAC__stream_decoder_seek_absolute (music->flac_decoder, - (FLAC__uint64)seek_sample)) { - if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) - == FLAC__STREAM_DECODER_SEEK_ERROR) { - flac.FLAC__stream_decoder_flush (music->flac_decoder); + if (!flac.FLAC__stream_decoder_seek_absolute(music->flac_decoder, (FLAC__uint64)seek_sample)) { + if (flac.FLAC__stream_decoder_get_state(music->flac_decoder) == FLAC__STREAM_DECODER_SEEK_ERROR) { + flac.FLAC__stream_decoder_flush(music->flac_decoder); } SDL_SetError("Seeking of FLAC stream failed: libFLAC seek failed."); @@ -741,39 +610,22 @@ static int FLAC_Seek(void *context, double position) return 0; } -/* Stop playback of a stream previously started with FLAC_play() */ -static void FLAC_Stop(void *context) -{ - FLAC_music *music = (FLAC_music *)context; - music->playing = SDL_FALSE; -} - -/* Close the given FLAC_music object */ +/* Close the given FLAC_Music object */ static void FLAC_Delete(void *context) { - FLAC_music *music = (FLAC_music *)context; + FLAC_Music *music = (FLAC_Music *)context; if (music) { if (music->flac_decoder) { - flac.FLAC__stream_decoder_finish (music->flac_decoder); - flac.FLAC__stream_decoder_delete (music->flac_decoder); - } - - if (music->flac_data.data) { - SDL_free (music->flac_data.data); + flac.FLAC__stream_decoder_finish(music->flac_decoder); + flac.FLAC__stream_decoder_delete(music->flac_decoder); } - - if (music->flac_data.overflow) { - SDL_free (music->flac_data.overflow); + if (music->stream) { + SDL_FreeAudioStream(music->stream); } - - if (music->cvt.buf) { - SDL_free (music->cvt.buf); - } - if (music->freesrc) { SDL_RWclose(music->src); } - SDL_free (music); + SDL_free(music); } } @@ -791,12 +643,12 @@ Mix_MusicInterface Mix_MusicInterface_FLAC = NULL, /* CreateFromFile */ FLAC_SetVolume, FLAC_Play, - FLAC_IsPlaying, + NULL, /* IsPlaying */ FLAC_GetAudio, FLAC_Seek, NULL, /* Pause */ NULL, /* Resume */ - FLAC_Stop, + NULL, /* Stop */ FLAC_Delete, NULL, /* Close */ FLAC_Unload, diff --git a/music_fluidsynth.c b/music_fluidsynth.c index dc0373a9..808391ce 100644 --- a/music_fluidsynth.c +++ b/music_fluidsynth.c @@ -117,14 +117,13 @@ static void FLUIDSYNTH_Unload() typedef struct { - SDL_AudioCVT convert; fluid_synth_t *synth; - fluid_player_t* player; -} FluidSynthMidiSong; + fluid_player_t *player; + SDL_AudioStream *stream; + void *buffer; + int buffer_size; +} FLUIDSYNTH_Music; -static Uint16 format; -static Uint8 channels; -static int freq; static int fluidsynth_check_soundfont(const char *path, void *data) { @@ -151,165 +150,134 @@ static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec) if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) { return -1; } - - format = spec->format; - channels = spec->channels; - freq = spec->freq; - return 0; } -static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data) +static FLUIDSYNTH_Music *FLUIDSYNTH_LoadMusic(void *data) { - FluidSynthMidiSong *song; - fluid_settings_t *settings = NULL; - - if ((song = SDL_calloc(1, sizeof(FluidSynthMidiSong)))) { - if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) { - if ((settings = fluidsynth.new_fluid_settings())) { - fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq); - - if ((song->synth = fluidsynth.new_fluid_synth(settings))) { - if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) { - if ((song->player = fluidsynth.new_fluid_player(song->synth))) { - if (function(song, data)) return song; - fluidsynth.delete_fluid_player(song->player); - } else { - Mix_SetError("Failed to create FluidSynth player"); + SDL_RWops *src = (SDL_RWops *)data; + FLUIDSYNTH_Music *music; + fluid_settings_t *settings; + + if ((music = SDL_calloc(1, sizeof(FLUIDSYNTH_Music)))) { + int channels = 2; + if ((music->stream = SDL_NewAudioStream(AUDIO_S16SYS, channels, music_spec.freq, music_spec.format, music_spec.channels, music_spec.freq))) { + music->buffer_size = music_spec.samples * sizeof(Sint16) * channels; + if ((music->buffer = SDL_malloc(music->buffer_size))) { + if ((settings = fluidsynth.new_fluid_settings())) { + fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) music_spec.freq); + + if ((music->synth = fluidsynth.new_fluid_synth(settings))) { + if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) music->synth)) { + if ((music->player = fluidsynth.new_fluid_player(music->synth))) { + void *buffer; + size_t size; + + buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE); + if (buffer) { + if (fluidsynth.fluid_player_add_mem(music->player, buffer, size) == FLUID_OK) { + SDL_free(buffer); + return music; + } else { + Mix_SetError("FluidSynth failed to load in-memory song"); + } + SDL_free(buffer); + } else { + SDL_OutOfMemory(); + } + fluidsynth.delete_fluid_player(music->player); + } else { + Mix_SetError("Failed to create FluidSynth player"); + } } + fluidsynth.delete_fluid_synth(music->synth); + } else { + Mix_SetError("Failed to create FluidSynth synthesizer"); } - fluidsynth.delete_fluid_synth(song->synth); + fluidsynth.delete_fluid_settings(settings); } else { - Mix_SetError("Failed to create FluidSynth synthesizer"); + Mix_SetError("Failed to create FluidSynth settings"); } - fluidsynth.delete_fluid_settings(settings); } else { - Mix_SetError("Failed to create FluidSynth settings"); + SDL_OutOfMemory(); } - } else { - Mix_SetError("Failed to set up audio conversion"); } - SDL_free(song); + SDL_free(music); } else { - Mix_SetError("Insufficient memory for song"); + SDL_OutOfMemory(); } return NULL; } -static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data) -{ - Sint64 offset; - size_t size; - char *buffer; - SDL_RWops *src = (SDL_RWops*) data; - - offset = SDL_RWtell(src); - SDL_RWseek(src, 0, RW_SEEK_END); - size = (size_t)(SDL_RWtell(src) - offset); - SDL_RWseek(src, offset, RW_SEEK_SET); - - if ((buffer = (char*) SDL_malloc(size))) { - if(SDL_RWread(src, buffer, size, 1) == 1) { - if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) { - SDL_free(buffer); - return 1; - } else { - Mix_SetError("FluidSynth failed to load in-memory song"); - } - } else { - Mix_SetError("Failed to read in-memory song"); - } - SDL_free(buffer); - } else { - Mix_SetError("Insufficient memory for song"); - } - return 0; -} - static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc) { - FluidSynthMidiSong *song; + FLUIDSYNTH_Music *music; - song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) src); - if (song && freesrc) { + music = FLUIDSYNTH_LoadMusic(src); + if (music && freesrc) { SDL_RWclose(src); } - return song; + return music; } static void FLUIDSYNTH_SetVolume(void *context, int volume) { - FluidSynthMidiSong *song = (FluidSynthMidiSong *)context; + FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; /* FluidSynth's default is 0.2. Make 1.2 the maximum. */ - fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME)); + fluidsynth.fluid_synth_set_gain(music->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME)); } -static int FLUIDSYNTH_Play(void *context) +static int FLUIDSYNTH_Play(void *context, int play_count) { - FluidSynthMidiSong *song = (FluidSynthMidiSong *)context; - fluidsynth.fluid_player_set_loop(song->player, 1); - fluidsynth.fluid_player_play(song->player); + FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; + fluidsynth.fluid_player_set_loop(music->player, play_count); + fluidsynth.fluid_player_play(music->player); return 0; } static SDL_bool FLUIDSYNTH_IsPlaying(void *context) { - FluidSynthMidiSong *song = (FluidSynthMidiSong *)context; - return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE; + FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; + return fluidsynth.fluid_player_get_status(music->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE; } -static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes) +static int FLUIDSYNTH_GetSome(void *context, void *data, int bytes, SDL_bool *done) { - int result = -1; - int frames = bytes / channels / ((format & 0xFF) / 8); - int src_len = frames * 4; /* 16-bit stereo */ - void *src = dest; - - if (bytes < src_len) { - if (!(src = SDL_malloc(src_len))) { - Mix_SetError("Insufficient memory for audio conversion"); - return result; - } + int filled; + + filled = SDL_AudioStreamGet(music->stream, data, bytes); + if (filled != 0) { + return filled; } - if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) { + /* FIXME: What happens at end of song? */ + if (fluidsynth.fluid_synth_write_s16(music->synth, mixer_spec.samples, music->buffer, 0, 2, music->buffer, 1, 2) != FLUID_OK) { Mix_SetError("Error generating FluidSynth audio"); - goto finish; + return -1; } - - song->convert.buf = src; - song->convert.len = src_len; - - if (SDL_ConvertAudio(&song->convert) < 0) { - Mix_SetError("Error during audio conversion"); - goto finish; + if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) { + return -1; } - - if (src != dest) - SDL_memcpy(dest, src, bytes); - - result = 0; - -finish: - if (src != dest) - SDL_free(src); - - return result; + return 0; +} +static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes) +{ + return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, FLUIDSYNTH_GetSome); } static void FLUIDSYNTH_Stop(void *context) { - FluidSynthMidiSong *song = (FluidSynthMidiSong *)context; - fluidsynth.fluid_player_stop(song->player); + FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; + fluidsynth.fluid_player_stop(music->player); } static void FLUIDSYNTH_Delete(void *context) { - FluidSynthMidiSong *song = (FluidSynthMidiSong *)context; - fluidsynth.delete_fluid_player(song->player); - fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth)); - fluidsynth.delete_fluid_synth(song->synth); - SDL_free(song); + FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; + fluidsynth.delete_fluid_player(music->player); + fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(music->synth)); + fluidsynth.delete_fluid_synth(music->synth); + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH = diff --git a/music_mad.c b/music_mad.c index 3b794d8e..93f76948 100644 --- a/music_mad.c +++ b/music_mad.c @@ -25,84 +25,176 @@ #include "mad.h" + +/* NOTE: The dithering functions are GPL, which should be fine if your + application is GPL (which would need to be true if you enabled + libmad support in SDL_mixer). If you're using libmad under the + commercial license, you need to disable this code. +*/ +/************************ dithering functions ***************************/ + +#ifdef MUSIC_MP3_MAD_GPL_DITHERING + +/* All dithering done here is taken from the GPL'ed xmms-mad plugin. */ + +/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ +/* Any feedback is very welcome. For any question, comments, */ +/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */ +/* matumoto@math.keio.ac.jp */ + +/* Period parameters */ +#define MP3_DITH_N 624 +#define MP3_DITH_M 397 +#define MATRIX_A 0x9908b0df /* constant vector a */ +#define UPPER_MASK 0x80000000 /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffff /* least significant r bits */ + +/* Tempering parameters */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + +static unsigned long mt[MP3_DITH_N]; /* the array for the state vector */ +static int mti=MP3_DITH_N+1; /* mti==MP3_DITH_N+1 means mt[MP3_DITH_N] is not initialized */ + +/* initializing the array with a NONZERO seed */ +static void sgenrand(unsigned long seed) +{ + /* setting initial seeds to mt[MP3_DITH_N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + mt[0]= seed & 0xffffffff; + for (mti=1; mti= MP3_DITH_N) { /* generate MP3_DITH_N words at one time */ + int kk; + + if (mti == MP3_DITH_N+1) /* if sgenrand() has not been called, */ + sgenrand(4357); /* a default initial seed is used */ + + for (kk=0;kk> 1) ^ mag01[y & 0x1]; + } + for (;kk> 1) ^ mag01[y & 0x1]; + } + y = (mt[MP3_DITH_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[MP3_DITH_N-1] = mt[MP3_DITH_M-1] ^ (y >> 1) ^ mag01[y & 0x1]; + + mti = 0; + } + + y = mt[mti++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y ^= TEMPERING_SHIFT_L(y); + + return y; +} + +static long triangular_dither_noise(int nbits) { + /* parameter nbits : the peak-to-peak amplitude desired (in bits) + * use with nbits set to 2 + nber of bits to be trimmed. + * (because triangular is made from two uniformly distributed processes, + * it starts at 2 bits peak-to-peak amplitude) + * see The Theory of Dithered Quantization by Robert Alexander Wannamaker + * for complete proof of why that's optimal + */ + long v = (genrand()/2 - genrand()/2); /* in ]-2^31, 2^31[ */ + long P = 1 << (32 - nbits); /* the power of 2 */ + v /= P; + /* now v in ]-2^(nbits-1), 2^(nbits-1) [ */ + + return v; +} + +#endif /* MUSIC_MP3_MAD_GPL_DITHERING */ + + #define MAD_INPUT_BUFFER_SIZE (5*8192) -#define MAD_OUTPUT_BUFFER_SIZE 8192 enum { MS_input_eof = 0x0001, MS_input_error = 0x0001, - MS_decode_eof = 0x0002, - MS_decode_error = 0x0004, + MS_decode_error = 0x0002, MS_error_flags = 0x000f, - - MS_playing = 0x0100, - MS_cvt_decoded = 0x0200, }; typedef struct { + int play_count; SDL_RWops *src; int freesrc; struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; - int frames_read; mad_timer_t next_frame_start; int volume; int status; - int output_begin, output_end; - SDL_AudioSpec mixer; - SDL_AudioCVT cvt; + SDL_AudioStream *audiostream; unsigned char input_buffer[MAD_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD]; - unsigned char *output_buffer; -} mad_data; +} MAD_Music; +static int MAD_Seek(void *context, double position); + static void *MAD_CreateFromRW(SDL_RWops *src, int freesrc) { - mad_data *mp3_mad; - - mp3_mad = (mad_data *)SDL_calloc(1, sizeof(mad_data)); - if (mp3_mad) { - mp3_mad->src = src; - mp3_mad->freesrc = freesrc; - mad_stream_init(&mp3_mad->stream); - mad_frame_init(&mp3_mad->frame); - mad_synth_init(&mp3_mad->synth); - mad_timer_reset(&mp3_mad->next_frame_start); - mp3_mad->volume = MIX_MAX_VOLUME; - mp3_mad->mixer = music_spec; + MAD_Music *music; + + music = (MAD_Music *)SDL_calloc(1, sizeof(MAD_Music)); + if (!music) { + SDL_OutOfMemory(); + return NULL; } - return mp3_mad; + music->src = src; + music->volume = MIX_MAX_VOLUME; + + mad_stream_init(&music->stream); + mad_frame_init(&music->frame); + mad_synth_init(&music->synth); + mad_timer_reset(&music->next_frame_start); + + music->freesrc = freesrc; + return music; } static void MAD_SetVolume(void *context, int volume) { - mad_data *mp3_mad = (mad_data *)context; - mp3_mad->volume = volume; + MAD_Music *music = (MAD_Music *)context; + music->volume = volume; } /* Starts the playback. */ -static int MAD_Play(void *context) +static int MAD_Play(void *context, int play_count) { - mad_data *mp3_mad = (mad_data *)context; - mp3_mad->status |= MS_playing; - return 0; + MAD_Music *music = (MAD_Music *)context; + music->play_count = play_count; + return MAD_Seek(music, 0.0); } -/* Returns true if the playing is engaged, false otherwise. */ -static SDL_bool MAD_IsPlaying(void *context) +/* Reads the next frame from the file. + Returns true on success or false on failure. + */ +static SDL_bool read_next_frame(MAD_Music *music) { - mad_data *mp3_mad = (mad_data *)context; - return ((mp3_mad->status & MS_playing) != 0); -} - -/* Reads the next frame from the file. Returns true on success or - false on failure. */ -static int -read_next_frame(mad_data *mp3_mad) { - if (mp3_mad->stream.buffer == NULL || - mp3_mad->stream.error == MAD_ERROR_BUFLEN) { + if (music->stream.buffer == NULL || + music->stream.error == MAD_ERROR_BUFLEN) { size_t read_size; size_t remaining; unsigned char *read_start; @@ -110,25 +202,25 @@ read_next_frame(mad_data *mp3_mad) { /* There might be some bytes in the buffer left over from last time. If so, move them down and read more bytes following them. */ - if (mp3_mad->stream.next_frame != NULL) { - remaining = mp3_mad->stream.bufend - mp3_mad->stream.next_frame; - memmove(mp3_mad->input_buffer, mp3_mad->stream.next_frame, remaining); - read_start = mp3_mad->input_buffer + remaining; + if (music->stream.next_frame != NULL) { + remaining = music->stream.bufend - music->stream.next_frame; + memmove(music->input_buffer, music->stream.next_frame, remaining); + read_start = music->input_buffer + remaining; read_size = MAD_INPUT_BUFFER_SIZE - remaining; } else { read_size = MAD_INPUT_BUFFER_SIZE; - read_start = mp3_mad->input_buffer; + read_start = music->input_buffer; remaining = 0; } /* Now read additional bytes from the input file. */ - read_size = SDL_RWread(mp3_mad->src, read_start, 1, read_size); + read_size = SDL_RWread(music->src, read_start, 1, read_size); if (read_size == 0) { - if ((mp3_mad->status & (MS_input_eof | MS_input_error)) == 0) { + if ((music->status & (MS_input_eof | MS_input_error)) == 0) { /* FIXME: how to detect error? */ - mp3_mad->status |= MS_input_eof; + music->status |= MS_input_eof; /* At the end of the file, we must stuff MAD_BUFFER_GUARD number of 0 bytes. */ @@ -138,37 +230,43 @@ read_next_frame(mad_data *mp3_mad) { } /* Now feed those bytes into the libmad stream. */ - mad_stream_buffer(&mp3_mad->stream, mp3_mad->input_buffer, + mad_stream_buffer(&music->stream, music->input_buffer, read_size + remaining); - mp3_mad->stream.error = MAD_ERROR_NONE; + music->stream.error = MAD_ERROR_NONE; } /* Now ask libmad to extract a frame from the data we just put in its buffer. */ - if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) { - if (MAD_RECOVERABLE(mp3_mad->stream.error)) { - return 0; + if (mad_frame_decode(&music->frame, &music->stream)) { + if (MAD_RECOVERABLE(music->stream.error)) { + return SDL_FALSE; - } else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) { - return 0; + } else if (music->stream.error == MAD_ERROR_BUFLEN) { + return SDL_FALSE; } else { - mp3_mad->status |= MS_decode_error; - return 0; + Mix_SetError("mad_frame_decode() failed, corrupt stream?"); + music->status |= MS_decode_error; + return SDL_FALSE; } } - mp3_mad->frames_read++; - mad_timer_add(&mp3_mad->next_frame_start, mp3_mad->frame.header.duration); + mad_timer_add(&music->next_frame_start, music->frame.header.duration); - return 1; + return SDL_TRUE; } /* Scale a MAD sample to 16 bits for output. */ -static signed int -scale(mad_fixed_t sample) { +static Sint16 scale(mad_fixed_t sample) +{ + const int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16; + /* round */ - sample += (1L << (MAD_F_FRACBITS - 16)); + sample += (1L << (n_bits_to_loose - 1)); + +#ifdef MUSIC_MP3_MAD_GPL_DITHERING + sample += triangular_dither_noise(n_bits_to_loose + 1); +#endif /* clip */ if (sample >= MAD_F_ONE) @@ -177,161 +275,132 @@ scale(mad_fixed_t sample) { sample = -MAD_F_ONE; /* quantize */ - return sample >> (MAD_F_FRACBITS + 1 - 16); + return (Sint16)(sample >> n_bits_to_loose); } /* Once the frame has been read, copies its samples into the output buffer. */ -static void -decode_frame(mad_data *mp3_mad) { +static SDL_bool decode_frame(MAD_Music *music) +{ struct mad_pcm *pcm; - unsigned int nchannels, nsamples; + unsigned int i, nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; - unsigned char *out; - int ret; + Sint16 *buffer, *dst; + int result; - mad_synth_frame(&mp3_mad->synth, &mp3_mad->frame); - pcm = &mp3_mad->synth.pcm; + mad_synth_frame(&music->synth, &music->frame); + pcm = &music->synth.pcm; - if ((mp3_mad->status & MS_cvt_decoded) == 0) { - mp3_mad->status |= MS_cvt_decoded; + if (!music->audiostream) { + music->audiostream = SDL_NewAudioStream(AUDIO_S16, pcm->channels, pcm->samplerate, music_spec.format, music_spec.channels, music_spec.freq); + if (!music->audiostream) { + return SDL_FALSE; + } + } - /* The first frame determines some key properties of the stream. - In particular, it tells us enough to set up the convert - structure now. */ - SDL_BuildAudioCVT(&mp3_mad->cvt, AUDIO_S16, pcm->channels, mp3_mad->frame.header.samplerate, mp3_mad->mixer.format, mp3_mad->mixer.channels, mp3_mad->mixer.freq); + nchannels = pcm->channels; + nsamples = pcm->length; + left_ch = pcm->samples[0]; + right_ch = pcm->samples[1]; + buffer = SDL_stack_alloc(Sint16, nsamples*nchannels); + if (!buffer) { + SDL_OutOfMemory(); + return SDL_FALSE; } - if (!mp3_mad->output_buffer) { - size_t sz = MAD_OUTPUT_BUFFER_SIZE; - if (mp3_mad->cvt.len_mult > 1) { - sz *= mp3_mad->cvt.len_mult; + dst = buffer; + if (nchannels == 1) { + for (i = nsamples; i--;) { + *dst++ = scale(*left_ch++); + } + } else { + for (i = nsamples; i--;) { + *dst++ = scale(*left_ch++); + *dst++ = scale(*right_ch++); } - mp3_mad->output_buffer = (unsigned char *) SDL_malloc(sz); } - out = mp3_mad->output_buffer + mp3_mad->output_end; - - /* pcm->samplerate contains the sampling frequency */ - nchannels = pcm->channels; - nsamples = pcm->length; - left_ch = pcm->samples[0]; - right_ch = pcm->samples[1]; - - while (nsamples--) { - signed int sample; + result = SDL_AudioStreamPut(music->audiostream, buffer, (nsamples * nchannels * sizeof(Sint16))); + SDL_stack_free(buffer); - /* output sample(s) in 16-bit signed little-endian PCM */ + if (result < 0) { + return SDL_FALSE; + } + return SDL_TRUE; +} - sample = scale(*left_ch++); - *out++ = ((sample >> 0) & 0xff); - *out++ = ((sample >> 8) & 0xff); +static int MAD_GetSome(void *context, void *data, int bytes, SDL_bool *done) +{ + MAD_Music *music = (MAD_Music *)context; + int filled; - if (nchannels == 2) { - sample = scale(*right_ch++); - *out++ = ((sample >> 0) & 0xff); - *out++ = ((sample >> 8) & 0xff); + if (music->audiostream) { + filled = SDL_AudioStreamGet(music->audiostream, data, bytes); + if (filled != 0) { + return filled; } } - mp3_mad->output_end = out - mp3_mad->output_buffer; - /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/ -} - -static int MAD_GetAudio(void *context, void *data, int bytes) -{ - mad_data *mp3_mad = (mad_data *)context; - Uint8 *stream = (Uint8 *)data; - int len = bytes; - int bytes_remaining; - int num_bytes; - Uint8 *out; - - if ((mp3_mad->status & MS_playing) == 0) { - /* We're not supposed to be playing, so send silence instead. */ - SDL_memset(stream, 0, len); + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; return 0; } - out = stream; - bytes_remaining = len; - while (bytes_remaining > 0) { - if (mp3_mad->output_end == mp3_mad->output_begin) { - /* We need to get a new frame. */ - mp3_mad->output_begin = 0; - mp3_mad->output_end = 0; - if (!read_next_frame(mp3_mad)) { - if ((mp3_mad->status & MS_error_flags) != 0) { - /* Couldn't read a frame; either an error condition or - end-of-file. Stop. */ - SDL_memset(out, 0, bytes_remaining); - mp3_mad->status &= ~MS_playing; - return bytes_remaining; - } - } else { - decode_frame(mp3_mad); - - /* Now convert the frame data to the appropriate format for - output. */ - mp3_mad->cvt.buf = mp3_mad->output_buffer; - mp3_mad->cvt.len = mp3_mad->output_end; - - mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio); - /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/ - SDL_ConvertAudio(&mp3_mad->cvt); - } + if (read_next_frame(music)) { + if (!decode_frame(music)) { + return -1; } + } else { + int play_count = -1; - num_bytes = mp3_mad->output_end - mp3_mad->output_begin; - if (bytes_remaining < num_bytes) { - num_bytes = bytes_remaining; + if (music->status & MS_decode_error) { + return -1; } - if (mp3_mad->volume == MIX_MAX_VOLUME) { - SDL_memcpy(out, mp3_mad->output_buffer + mp3_mad->output_begin, num_bytes); - } else { - SDL_MixAudioFormat(out, mp3_mad->output_buffer + mp3_mad->output_begin, - mp3_mad->mixer.format, num_bytes, mp3_mad->volume); + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (MAD_Play(music, play_count) < 0) { + return -1; } - out += num_bytes; - mp3_mad->output_begin += num_bytes; - bytes_remaining -= num_bytes; } return 0; } +static int MAD_GetAudio(void *context, void *data, int bytes) +{ + MAD_Music *music = (MAD_Music *)context; + return music_pcm_getaudio(context, data, bytes, music->volume, MAD_GetSome); +} static int MAD_Seek(void *context, double position) { - mad_data *mp3_mad = (mad_data *)context; + MAD_Music *music = (MAD_Music *)context; mad_timer_t target; int int_part; int_part = (int)position; mad_timer_set(&target, int_part, (int)((position - int_part) * 1000000), 1000000); - if (mad_timer_compare(mp3_mad->next_frame_start, target) > 0) { + if (mad_timer_compare(music->next_frame_start, target) > 0) { /* In order to seek backwards in a VBR file, we have to rewind and start again from the beginning. This isn't necessary if the file happens to be CBR, of course; in that case we could seek directly to the frame we want. But I leave that little optimization for the future developer who discovers she really needs it. */ - mp3_mad->frames_read = 0; - mad_timer_reset(&mp3_mad->next_frame_start); - mp3_mad->status &= ~MS_error_flags; - mp3_mad->output_begin = 0; - mp3_mad->output_end = 0; + mad_timer_reset(&music->next_frame_start); + music->status &= ~MS_error_flags; - SDL_RWseek(mp3_mad->src, 0, RW_SEEK_SET); + SDL_RWseek(music->src, 0, RW_SEEK_SET); } /* Now we have to skip frames until we come to the right one. Again, only truly necessary if the file is VBR. */ - while (mad_timer_compare(mp3_mad->next_frame_start, target) < 0) { - if (!read_next_frame(mp3_mad)) { - if ((mp3_mad->status & MS_error_flags) != 0) { + while (mad_timer_compare(music->next_frame_start, target) < 0) { + if (!read_next_frame(music)) { + if ((music->status & MS_error_flags) != 0) { /* Couldn't read a frame; either an error condition or end-of-file. Stop. */ - mp3_mad->status &= ~MS_playing; return Mix_SetError("Seek position out of range"); } } @@ -344,25 +413,21 @@ static int MAD_Seek(void *context, double position) return 0; } -/* Stops the playback. */ -static void MAD_Stop(void *context) -{ - mad_data *mp3_mad = (mad_data *)context; - mp3_mad->status &= ~MS_playing; -} - static void MAD_Delete(void *context) { - mad_data *mp3_mad = (mad_data *)context; - mad_stream_finish(&mp3_mad->stream); - mad_frame_finish(&mp3_mad->frame); - mad_synth_finish(&mp3_mad->synth); + MAD_Music *music = (MAD_Music *)context; - if (mp3_mad->freesrc) { - SDL_RWclose(mp3_mad->src); + mad_stream_finish(&music->stream); + mad_frame_finish(&music->frame); + mad_synth_finish(&music->synth); + + if (music->audiostream) { + SDL_FreeAudioStream(music->audiostream); + } + if (music->freesrc) { + SDL_RWclose(music->src); } - SDL_free(mp3_mad->output_buffer); - SDL_free(mp3_mad); + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_MAD = @@ -373,21 +438,21 @@ Mix_MusicInterface Mix_MusicInterface_MAD = SDL_FALSE, SDL_FALSE, - NULL, /* Load */ - NULL, /* Open */ + NULL, /* Load */ + NULL, /* Open */ MAD_CreateFromRW, - NULL, /* CreateFromFile */ + NULL, /* CreateFromFile */ MAD_SetVolume, MAD_Play, - MAD_IsPlaying, + NULL, /* IsPlaying */ MAD_GetAudio, MAD_Seek, - NULL, /* Pause */ - NULL, /* Resume */ - MAD_Stop, + NULL, /* Pause */ + NULL, /* Resume */ + NULL, /* Stop */ MAD_Delete, - NULL, /* Close */ - NULL, /* Unload */ + NULL, /* Close */ + NULL, /* Unload */ }; #endif /* MUSIC_MP3_MAD */ diff --git a/music_mikmod.c b/music_mikmod.c index e1858365..f820a83d 100644 --- a/music_mikmod.c +++ b/music_mikmod.c @@ -20,6 +20,7 @@ */ #ifdef MUSIC_MOD_MIKMOD +#error Implement play_count and audio stream conversion /* This file supports MOD tracker music streams */ diff --git a/music_modplug.c b/music_modplug.c index e2b9cf55..e73bc6cf 100644 --- a/music_modplug.c +++ b/music_modplug.c @@ -49,12 +49,8 @@ static modplug_loader modplug = { }; -static int current_output_channels = 0; -static int music_swap8 = 0; -static int music_swap16 = 0; static ModPlug_Settings settings; - #ifdef MODPLUG_DYNAMIC static int MODPLUG_Load(void) @@ -149,58 +145,49 @@ static void MODPLUG_Unload(void) #endif /* MODPLUG_DYNAMIC */ -static int MODPLUG_Open(const SDL_AudioSpec *spec) +typedef struct { - modplug.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; + int play_count; + ModPlugFile *file; + SDL_AudioStream *stream; + void *buffer; + int buffer_size; +} MODPLUG_Music; - 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; - } +static int MODPLUG_Seek(void *context, double position); +static void MODPLUG_Delete(void *context); +static int MODPLUG_Open(const SDL_AudioSpec *spec) +{ + /* ModPlug supports U8 or S16 audio output */ + modplug.ModPlug_GetSettings(&settings); + settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING; + if (spec->channels == 1) { + settings.mChannels = 1; + } else { + settings.mChannels = 2; } - - 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; + if (SDL_AUDIO_BITSIZE(spec->format) == 8) { + settings.mBits = 8; + } else { + settings.mBits = 16; + } + if (spec->freq >= 44100) { + settings.mFrequency = 44100; + } else if (spec->freq >= 22050) { + settings.mFrequency = 22050; + } else { + settings.mFrequency = 11025; + } + 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.ModPlug_SetSettings(&settings); return 0; } @@ -208,25 +195,45 @@ static int MODPLUG_Open(const SDL_AudioSpec *spec) /* Load a modplug stream from an SDL_RWops object */ void *MODPLUG_CreateFromRW(SDL_RWops *src, int freesrc) { - ModPlugFile *music = NULL; - Sint64 offset; - size_t sz; - char *buf; - - offset = SDL_RWtell(src); - SDL_RWseek(src, 0, RW_SEEK_END); - sz = (size_t)(SDL_RWtell(src) - offset); - SDL_RWseek(src, offset, RW_SEEK_SET); - buf = (char*)SDL_malloc(sz); - if (buf) { - if (SDL_RWread(src, buf, sz, 1) == 1) { - music = modplug.ModPlug_Load(buf, (int)sz); - } - SDL_free(buf); - } else { + MODPLUG_Music *music; + void *buffer; + size_t size; + + music = (MODPLUG_Music *)SDL_calloc(1, sizeof(*music)); + if (!music) { SDL_OutOfMemory(); + return NULL; + } + + music->stream = SDL_NewAudioStream((settings.mBits == 8) ? AUDIO_U8 : AUDIO_S16SYS, settings.mChannels, settings.mFrequency, + music_spec.format, music_spec.channels, music_spec.freq); + if (!music->stream) { + MODPLUG_Delete(music); + return NULL; + } + + music->buffer_size = music_spec.samples * (settings.mBits / 8) * settings.mChannels; + music->buffer = SDL_malloc(music->buffer_size); + if (!music->buffer) { + MODPLUG_Delete(music); + return NULL; } - if (music && freesrc) { + + buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE); + if (buffer) { + music->file = modplug.ModPlug_Load(buffer, (int)size); + if (!music->file) { + Mix_SetError("ModPlug_Load failed"); + } + SDL_free(buffer); + } + + if (!music->file) { + MODPLUG_Delete(music); + return NULL; + } + + if (freesrc) { SDL_RWclose(src); } return music; @@ -235,117 +242,83 @@ void *MODPLUG_CreateFromRW(SDL_RWops *src, int freesrc) /* Set the volume for a modplug stream */ static void MODPLUG_SetVolume(void *context, int volume) { - ModPlugFile *music = (ModPlugFile *)context; - modplug.ModPlug_SetMasterVolume(music, volume*4); + MODPLUG_Music *music = (MODPLUG_Music *)context; + modplug.ModPlug_SetMasterVolume(music->file, volume*4); } /* Start playback of a given modplug stream */ -static int MODPLUG_Play(void *context) +static int MODPLUG_Play(void *context, int play_count) { - ModPlugFile *music = (ModPlugFile *)context; - modplug.ModPlug_Seek(music,0); - return 0; + MODPLUG_Music *music = (MODPLUG_Music *)context; + music->play_count = play_count; + return MODPLUG_Seek(music, 0.0); } /* Play some of a stream previously started with modplug_play() */ -static int MODPLUG_GetAudio(void *context, void *data, int bytes) +static int MODPLUG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { - ModPlugFile *music = (ModPlugFile *)context; - Uint8 *stream = (Uint8 *)data; - int len = bytes; - int consumed; - - if (current_output_channels > 2) { - int small_len = 2 * len / current_output_channels; - int i; - Uint8 *src, *dst; - - i=modplug.ModPlug_Read(music, stream, small_len); - consumed = (i / 2) * current_output_channels; - if(istream, data, bytes); + if (filled != 0) { + return filled; } - if (music_swap8) { - Uint8 *dst; - int i; - dst = stream; - for (i=len; i; --i) { - *dst++ ^= 0x80; + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; + return 0; + } + + amount = modplug.ModPlug_Read(music->file, music->buffer, music->buffer_size); + if (amount > 0) { + if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { + return -1; } - } 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; + } else { + if (music->play_count == 1) { + music->play_count = 0; + SDL_AudioStreamFlush(music->stream); + } else { + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (MODPLUG_Play(music, play_count) < 0) { + return -1; + } } } - return (len-consumed); + return 0; +} +static int MODPLUG_GetAudio(void *context, void *data, int bytes) +{ + return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MODPLUG_GetSome); } /* Jump (seek) to a given position */ static int MODPLUG_Seek(void *context, double position) { - ModPlugFile *music = (ModPlugFile *)context; - modplug.ModPlug_Seek(music,(int)(position*1000)); + MODPLUG_Music *music = (MODPLUG_Music *)context; + modplug.ModPlug_Seek(music->file, (int)(position*1000)); return 0; } /* Close the given modplug stream */ static void MODPLUG_Delete(void *context) { - ModPlugFile *music = (ModPlugFile *)context; - modplug.ModPlug_Unload(music); + MODPLUG_Music *music = (MODPLUG_Music *)context; + if (music->file) { + modplug.ModPlug_Unload(music->file); + } + if (music->stream) { + SDL_FreeAudioStream(music->stream); + } + if (music->buffer) { + SDL_free(music->buffer); + } + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_MODPLUG = diff --git a/music_mpg123.c b/music_mpg123.c index 0bfe4315..5415a4b1 100644 --- a/music_mpg123.c +++ b/music_mpg123.c @@ -25,6 +25,7 @@ #include // For SEEK_SET +#include "SDL_assert.h" #include "SDL_loadso.h" #include "music_mpg123.h" @@ -46,6 +47,7 @@ typedef struct { mpg123_handle *(*mpg123_new)(const char* decoder, int *error); int (*mpg123_open_handle)(mpg123_handle *mh, void *iohandle); const char* (*mpg123_plain_strerror)(int errcode); + void (*mpg123_rates)(const long **list, size_t *number); int (*mpg123_read)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done ); int (*mpg123_replace_reader_handle)( mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) ); off_t (*mpg123_seek)( mpg123_handle *mh, off_t sampleoff, int whence ); @@ -93,6 +95,7 @@ static int MPG123_Load(void) FUNCTION_LOADER(mpg123_new, mpg123_handle *(*)(const char* decoder, int *error)) FUNCTION_LOADER(mpg123_open_handle, int (*)(mpg123_handle *mh, void *iohandle)) FUNCTION_LOADER(mpg123_plain_strerror, const char* (*)(int errcode)) + FUNCTION_LOADER(mpg123_rates, void (*)(const long **list, size_t *number)); FUNCTION_LOADER(mpg123_read, int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done )) FUNCTION_LOADER(mpg123_replace_reader_handle, int (*)( mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) )) FUNCTION_LOADER(mpg123_seek, off_t (*)( mpg123_handle *mh, off_t sampleoff, int whence )) @@ -119,58 +122,37 @@ static void MPG123_Unload(void) typedef struct { + int play_count; SDL_RWops* src; int freesrc; - - SDL_AudioSpec mixer; - - SDL_bool playing; int volume; mpg123_handle* handle; + SDL_AudioStream *stream; + unsigned char *buffer; + size_t buffer_size; +} MPG123_Music; - int gotformat; - SDL_AudioCVT cvt; - Uint8 buf[8192]; - size_t len_available; - Uint8* snd_available; -} mpg_data; -static -int -snd_format_to_mpg123(uint16_t sdl_fmt) -{ - switch (sdl_fmt) - { - case AUDIO_U8: return MPG123_ENC_UNSIGNED_8; - case AUDIO_U16SYS: return MPG123_ENC_UNSIGNED_16; - case AUDIO_S8: return MPG123_ENC_SIGNED_8; - case AUDIO_S16SYS: return MPG123_ENC_SIGNED_16; - case AUDIO_S32SYS: return MPG123_ENC_SIGNED_32; - } - - return -1; -} +static int MPG123_Seek(void *context, double secs); +static void MPG123_Delete(void *context); -static -Uint16 -mpg123_format_to_sdl(int fmt) +static int mpg123_format_to_sdl(int fmt) { switch (fmt) { - case MPG123_ENC_UNSIGNED_8: return AUDIO_U8; - case MPG123_ENC_UNSIGNED_16: return AUDIO_U16SYS; - case MPG123_ENC_SIGNED_8: return AUDIO_S8; - case MPG123_ENC_SIGNED_16: return AUDIO_S16SYS; - case MPG123_ENC_SIGNED_32: return AUDIO_S32SYS; + case MPG123_ENC_SIGNED_8: return AUDIO_S8; + case MPG123_ENC_UNSIGNED_8: return AUDIO_U8; + case MPG123_ENC_SIGNED_16: return AUDIO_S16SYS; + case MPG123_ENC_UNSIGNED_16: return AUDIO_U16SYS; + case MPG123_ENC_SIGNED_32: return AUDIO_S32SYS; + case MPG123_ENC_FLOAT_32: return AUDIO_F32SYS; + default: return -1; } - - return -1; } -static -char const* -mpg123_format_str(int fmt) +/* +static const char *mpg123_format_str(int fmt) { switch (fmt) { @@ -180,46 +162,42 @@ mpg123_format_str(int fmt) f(MPG123_ENC_SIGNED_8) f(MPG123_ENC_SIGNED_16) f(MPG123_ENC_SIGNED_32) + f(MPG123_ENC_FLOAT_32) #undef f } - return "unknown"; } +*/ -static -char const* -mpg_err(mpg123_handle* mpg, int code) +static char const* mpg_err(mpg123_handle* mpg, int result) { char const* err = "unknown error"; - if (mpg && code == MPG123_ERR) { + if (mpg && result == MPG123_ERR) { err = mpg123.mpg123_strerror(mpg); } else { - err = mpg123.mpg123_plain_strerror(code); + err = mpg123.mpg123_plain_strerror(result); } - return err; } /* we're gonna override mpg123's I/O with these wrappers for RWops */ -static -ssize_t rwops_read(void* p, void* dst, size_t n) { +static ssize_t rwops_read(void* p, void* dst, size_t n) +{ return (ssize_t)SDL_RWread((SDL_RWops*)p, dst, 1, n); } -static -off_t rwops_seek(void* p, off_t offset, int whence) { +static off_t rwops_seek(void* p, off_t offset, int whence) +{ return (off_t)SDL_RWseek((SDL_RWops*)p, (Sint64)offset, whence); } -static -void rwops_cleanup(void* p) { +static void rwops_cleanup(void* p) +{ (void)p; /* do nothing, we will free the file later */ } -static int getsome(mpg_data* m); - static int MPG123_Open(const SDL_AudioSpec *spec) { @@ -232,280 +210,191 @@ static int MPG123_Open(const SDL_AudioSpec *spec) static void *MPG123_CreateFromRW(SDL_RWops *src, int freesrc) { - mpg_data* m; + MPG123_Music *music; int result; - int fmt; + const long *rates; + size_t i, num_rates; - m = (mpg_data*)SDL_calloc(1, sizeof(mpg_data)); - if (!m) { - return 0; + music = (MPG123_Music*)SDL_calloc(1, sizeof(*music)); + if (!music) { + return NULL; + } + music->src = src; + music->volume = MIX_MAX_VOLUME; + + /* Just assume 16-bit 2 channel audio for now */ + music->buffer_size = music_spec.samples * sizeof(Sint16) * 2; + music->buffer = (unsigned char *)SDL_malloc(music->buffer_size); + if (!music->buffer) { + MPG123_Delete(music); + SDL_OutOfMemory(); + return NULL; } - m->src = src; - m->freesrc = freesrc; - - m->handle = mpg123.mpg123_new(0, &result); + music->handle = mpg123.mpg123_new(0, &result); if (result != MPG123_OK) { - return 0; + MPG123_Delete(music); + Mix_SetError("mpg123_new failed"); + return NULL; } result = mpg123.mpg123_replace_reader_handle( - m->handle, + music->handle, rwops_read, rwops_seek, rwops_cleanup - ); + ); if (result != MPG123_OK) { - return 0; + MPG123_Delete(music); + Mix_SetError("mpg123_replace_reader_handle: %s", mpg_err(music->handle, result)); + return NULL; } - result = mpg123.mpg123_format_none(m->handle); + result = mpg123.mpg123_format_none(music->handle); if (result != MPG123_OK) { - return 0; - } - - fmt = snd_format_to_mpg123(music_spec.format); - if (fmt == -1) { - return 0; + MPG123_Delete(music); + Mix_SetError("mpg123_format_none: %s", mpg_err(music->handle, result)); + return NULL; } - result = mpg123.mpg123_format(m->handle, music_spec.freq, music_spec.channels, fmt); - if (result != MPG123_OK) { - return 0; + mpg123.mpg123_rates(&rates, &num_rates); + for (i = 0; i < num_rates; ++i) { + const int channels = (MPG123_MONO|MPG123_STEREO); + const int formats = (MPG123_ENC_SIGNED_8 | + MPG123_ENC_UNSIGNED_8 | + MPG123_ENC_SIGNED_16 | + MPG123_ENC_UNSIGNED_16 | + MPG123_ENC_SIGNED_32 | + MPG123_ENC_FLOAT_32); + + mpg123.mpg123_format(music->handle, rates[i], channels, formats); } - result = mpg123.mpg123_open_handle(m->handle, m->src); + result = mpg123.mpg123_open_handle(music->handle, music->src); if (result != MPG123_OK) { - return 0; + MPG123_Delete(music); + Mix_SetError("mpg123_open_handle: %s", mpg_err(music->handle, result)); + return NULL; } - m->volume = MIX_MAX_VOLUME; - m->mixer = music_spec; - - /* hacky: read until we can figure out the format then rewind */ - while (!m->gotformat) - { - if (!getsome(m)) { - return 0; - } - } - - /* rewind */ - mpg123.mpg123_seek(m->handle, 0, SEEK_SET); - - m->len_available = 0; - m->snd_available = m->cvt.buf; - - return m; + music->freesrc = freesrc; + return music; } -static void MPG123_SetVolume(void *context, int volume) { - mpg_data* m = (mpg_data *)context; - m->volume = volume; -} - -static int MPG123_Play(void *context) +static void MPG123_SetVolume(void *context, int volume) { - mpg_data* m = (mpg_data *)context; - m->playing = SDL_TRUE; - return 0; + MPG123_Music *music = (MPG123_Music *)context; + music->volume = volume; } -static SDL_bool MPG123_IsPlaying(void *context) +static int MPG123_Play(void *context, int play_count) { - mpg_data* m = (mpg_data *)context; - return m->playing; + MPG123_Music *music = (MPG123_Music *)context; + music->play_count = play_count; + return MPG123_Seek(music, 0.0); } -/* - updates the convert struct and buffer to match the format queried from - mpg123. -*/ -static int update_format(mpg_data* m) +/* read some mp3 stream data and convert it for output */ +static int MPG123_GetSome(void *context, void *data, int bytes, SDL_bool *done) { - int code; + MPG123_Music *music = (MPG123_Music *)context; + int filled, result; + size_t amount; long rate; - int channels, encoding; - Uint16 sdlfmt; - size_t bufsize; - - m->gotformat = 1; - - code = - mpg123.mpg123_getformat( - m->handle, - &rate, &channels, &encoding - ); - - if (code != MPG123_OK) { - SDL_SetError("mpg123_getformat: %s", mpg_err(m->handle, code)); - return 0; - } - - sdlfmt = mpg123_format_to_sdl(encoding); - if (sdlfmt == (Uint16)-1) - { - SDL_SetError( - "Format %s is not supported by SDL", - mpg123_format_str(encoding) - ); - return 0; - } + int channels, encoding, format; - SDL_BuildAudioCVT( - &m->cvt, - sdlfmt, channels, (int)rate, - m->mixer.format, - m->mixer.channels, - m->mixer.freq - ); - - if (m->cvt.buf) { - SDL_free(m->cvt.buf); + if (music->stream) { + filled = SDL_AudioStreamGet(music->stream, data, bytes); + if (filled != 0) { + return filled; + } } - bufsize = sizeof(m->buf) * m->cvt.len_mult; - m->cvt.buf = SDL_malloc(bufsize); - - if (!m->cvt.buf) - { - SDL_SetError("Out of memory"); - m->playing = SDL_FALSE; + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; return 0; } - return 1; -} - -/* read some mp3 stream data and convert it for output */ -static int getsome(mpg_data* m) -{ - int code; - size_t len; - Uint8* data = m->buf; - size_t cbdata = sizeof(m->buf); - SDL_AudioCVT* cvt = &m->cvt; - - do - { - code = mpg123.mpg123_read(m->handle, data, sizeof(data), &len); - switch (code) - { - case MPG123_NEW_FORMAT: - if (!update_format(m)) { - return 0; - } - break; - - case MPG123_DONE: - m->playing = SDL_FALSE; - break; - case MPG123_OK: - break; - - default: - SDL_SetError("mpg123_read: %s", mpg_err(m->handle, code)); - return 0; + result = mpg123.mpg123_read(music->handle, music->buffer, music->buffer_size, &amount); + switch (result) { + case MPG123_OK: + if (SDL_AudioStreamPut(music->stream, music->buffer, (int)amount) < 0) { + return -1; } - } - while (len && code != MPG123_OK); + break; - SDL_memcpy(cvt->buf, data, cbdata); - - if (cvt->needed) { - /* we need to convert the audio to SDL's format */ - cvt->len = (int)len; - SDL_ConvertAudio(cvt); - } - - else { - /* no conversion needed, just copy */ - cvt->len_cvt = (int)len; - } - - m->len_available = cvt->len_cvt; - m->snd_available = cvt->buf; - - return 1; -} - -static int MPG123_GetAudio(void *context, void *data, int bytes) -{ - mpg_data* m = (mpg_data *)context; - Uint8 *stream = (Uint8 *)data; - int len = bytes; - int mixable; - - while (len > 0 && m->playing) - { - if (!m->len_available) - { - if (!getsome(m)) { - m->playing = SDL_FALSE; - return len; - } + case MPG123_NEW_FORMAT: + result = mpg123.mpg123_getformat(music->handle, &rate, &channels, &encoding); + if (result != MPG123_OK) { + Mix_SetError("mpg123_getformat: %s", mpg_err(music->handle, result)); + return -1; } +/*printf("MPG123 format: %s, channels = %d, rate = %ld\n", mpg123_format_str(encoding), channels, rate);*/ - mixable = len; + format = mpg123_format_to_sdl(encoding); + SDL_assert(format != -1); - if (mixable > (int)m->len_available) { - mixable = (int)m->len_available; - } - - if (m->volume == MIX_MAX_VOLUME) { - SDL_memcpy(stream, m->snd_available, mixable); + music->stream = SDL_NewAudioStream(format, channels, (int)rate, + music_spec.format, music_spec.channels, music_spec.freq); + if (!music->stream) { + return -1; } - - else - { - SDL_MixAudioFormat( - stream, - m->snd_available, - m->mixer.format, - mixable, - m->volume - ); + break; + + case MPG123_DONE: + if (music->play_count == 1) { + music->play_count = 0; + SDL_AudioStreamFlush(music->stream); + } else { + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (MPG123_Play(music, play_count) < 0) { + return -1; + } } - - m->len_available -= mixable; - m->snd_available += mixable; - len -= mixable; - stream += mixable; + break; + default: + Mix_SetError("mpg123_read: %s", mpg_err(music->handle, result)); + return -1; } - - return len; + return 0; +} +static int MPG123_GetAudio(void *context, void *data, int bytes) +{ + MPG123_Music *music = (MPG123_Music *)context; + return music_pcm_getaudio(context, data, bytes, music->volume, MPG123_GetSome); } static int MPG123_Seek(void *context, double secs) { - mpg_data* m = (mpg_data *)context; - off_t offset = (off_t)(m->mixer.freq * secs); + MPG123_Music *music = (MPG123_Music *)context; + off_t offset = (off_t)(music_spec.freq * secs); - if ((offset = mpg123.mpg123_seek(m->handle, offset, SEEK_SET)) < 0) { - return Mix_SetError("mpg123_seek: %s", mpg_err(m->handle, (int)-offset)); + if ((offset = mpg123.mpg123_seek(music->handle, offset, SEEK_SET)) < 0) { + return Mix_SetError("mpg123_seek: %s", mpg_err(music->handle, (int)-offset)); } return 0; } -static void MPG123_Stop(void *context) -{ - mpg_data* m = (mpg_data *)context; - m->playing = SDL_FALSE; -} - static void MPG123_Delete(void *context) { - mpg_data* m = (mpg_data *)context; + MPG123_Music *music = (MPG123_Music *)context; - if (m->freesrc) { - SDL_RWclose(m->src); + if (music->handle) { + mpg123.mpg123_close(music->handle); + mpg123.mpg123_delete(music->handle); } - - if (m->cvt.buf) { - SDL_free(m->cvt.buf); + if (music->stream) { + SDL_FreeAudioStream(music->stream); } - - mpg123.mpg123_close(m->handle); - mpg123.mpg123_delete(m->handle); - SDL_free(m); + if (music->buffer) { + SDL_free(music->buffer); + } + if (music->freesrc) { + SDL_RWclose(music->src); + } + SDL_free(music); } static void MPG123_Close(void) @@ -527,12 +416,12 @@ Mix_MusicInterface Mix_MusicInterface_MPG123 = NULL, /* CreateFromFile */ MPG123_SetVolume, MPG123_Play, - MPG123_IsPlaying, + NULL, /* IsPlaying */ MPG123_GetAudio, MPG123_Seek, NULL, /* Pause */ NULL, /* Resume */ - MPG123_Stop, + NULL, /* Stop */ MPG123_Delete, MPG123_Close, MPG123_Unload diff --git a/music_nativemidi.c b/music_nativemidi.c index 72c3c19c..846f3bd1 100644 --- a/music_nativemidi.c +++ b/music_nativemidi.c @@ -36,10 +36,14 @@ static void *NATIVEMIDI_CreateFromRW(SDL_RWops *src, int freesrc) return music; } -static int NATIVEMIDI_Play(void *context) +static int NATIVEMIDI_Play(void *context, int play_count) { NativeMidiSong *music = (NativeMidiSong *)context; - native_midi_start(music, music_loops); + int loops = play_count; + if (loops > 0) { + --loops; + } + native_midi_start(music, loops); return 0; } diff --git a/music_ogg.c b/music_ogg.c index c1664fa4..1b22a505 100644 --- a/music_ogg.c +++ b/music_ogg.c @@ -204,13 +204,14 @@ static void OGG_Unload(void) typedef struct { SDL_RWops *src; int freesrc; - SDL_bool playing; + int play_count; int volume; OggVorbis_File vf; + vorbis_info vi; int section; - SDL_AudioCVT cvt; - int len_available; - Uint8 *snd_available; + SDL_AudioStream *stream; + char *buffer; + int buffer_size; int loop; ogg_int64_t loop_start; ogg_int64_t loop_end; @@ -218,6 +219,32 @@ typedef struct { ogg_int64_t channels; } OGG_music; + +static int set_ov_error(const char *function, int error) +{ +#define HANDLE_ERROR_CASE(X) case X: Mix_SetError("%s: %s", function, #X); break; + switch (error) { + HANDLE_ERROR_CASE(OV_FALSE); + HANDLE_ERROR_CASE(OV_EOF); + HANDLE_ERROR_CASE(OV_HOLE); + HANDLE_ERROR_CASE(OV_EREAD); + HANDLE_ERROR_CASE(OV_EFAULT); + HANDLE_ERROR_CASE(OV_EIMPL); + HANDLE_ERROR_CASE(OV_EINVAL); + HANDLE_ERROR_CASE(OV_ENOTVORBIS); + HANDLE_ERROR_CASE(OV_EBADHEADER); + HANDLE_ERROR_CASE(OV_EVERSION); + HANDLE_ERROR_CASE(OV_ENOTAUDIO); + HANDLE_ERROR_CASE(OV_EBADPACKET); + HANDLE_ERROR_CASE(OV_EBADLINK); + HANDLE_ERROR_CASE(OV_ENOSEEK); + default: + Mix_SetError("%s: unknown error %d\n", function, error); + break; + } + return -1; +} + static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); @@ -233,99 +260,126 @@ static long sdl_tell_func(void *datasource) return (long)SDL_RWtell((SDL_RWops*)datasource); } +static int OGG_Seek(void *context, double time); +static void OGG_Delete(void *context); + +static int OGG_UpdateSection(OGG_music *music) +{ + vorbis_info *vi; + + vi = vorbis.ov_info(&music->vf, -1); + if (!vi) { + Mix_SetError("ov_info returned NULL"); + return -1; + } + + if (vi->channels == music->vi.channels && vi->rate == music->vi.rate) { + return 0; + } + SDL_memcpy(&music->vi, vi, sizeof(*vi)); + + if (music->buffer) { + SDL_free(music->buffer); + music->buffer = NULL; + } + + if (music->stream) { + SDL_FreeAudioStream(music->stream); + music->stream = NULL; + } + + music->stream = SDL_NewAudioStream(AUDIO_S16, vi->channels, (int)vi->rate, + music_spec.format, music_spec.channels, music_spec.freq); + if (!music->stream) { + return -1; + } + + music->buffer_size = music_spec.samples * sizeof(Sint16) * vi->channels; + music->buffer = (char *)SDL_malloc(music->buffer_size); + if (!music->buffer) { + return -1; + } + return 0; +} + /* Load an OGG stream from an SDL_RWops object */ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) { OGG_music *music; ov_callbacks callbacks; + vorbis_comment *vc; + int isLoopLength = 0, i; + ogg_int64_t fullLength; - SDL_memset(&callbacks, 0, sizeof(callbacks)); + music = (OGG_music *)SDL_calloc(1, sizeof *music); + if (!music) { + SDL_OutOfMemory(); + return NULL; + } + music->src = src; + music->volume = MIX_MAX_VOLUME; + music->section = -1; + music->loop = -1; + music->loop_start = -1; + music->loop_end = 0; + music->loop_len = 0; + + SDL_zero(callbacks); callbacks.read_func = sdl_read_func; callbacks.seek_func = sdl_seek_func; callbacks.tell_func = sdl_tell_func; - music = (OGG_music *)SDL_calloc(1, sizeof *music); - if (music) { - vorbis_info *vi; - vorbis_comment *vc; - int isLoopLength = 0, i; - ogg_int64_t fullLength; - - /* Initialize the music structure */ - music->src = src; - music->freesrc = freesrc; - music->volume = MIX_MAX_VOLUME; - music->section = -1; - - music->loop = -1; - music->loop_start = -1; - music->loop_end = 0; - music->loop_len = 0; - - if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) { - SDL_SetError("Not an Ogg Vorbis audio stream"); - SDL_free(music); - return NULL; - } - - vi = vorbis.ov_info(&music->vf, -1); - music->channels = vi->channels; - - vc = vorbis.ov_comment(&music->vf, -1); - - for (i = 0; i < vc->comments; i++) { - int paramLen = vc->comment_lengths[i] + 1; - char *param = (char *)SDL_malloc((size_t)paramLen); - char *argument = param; - char *value = param; - SDL_memset(param, 0, (size_t)paramLen); - SDL_memcpy(param, vc->user_comments[i], (size_t)vc->comment_lengths[i]); - value = SDL_strchr(param, '='); - if (value == NULL) { - value = param + paramLen - 1; /* set null */ - } else { - *(value++) = '\0'; - } + if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) { + SDL_SetError("Not an Ogg Vorbis audio stream"); + SDL_free(music); + return NULL; + } - #ifdef __USE_ISOC99 - #define A_TO_OGG64(x) (ogg_int64_t)atoll(x) - #else - #define A_TO_OGG64(x) (ogg_int64_t)atol(x) - #endif - - if (SDL_strcasecmp(argument, "LOOPSTART") == 0) - music->loop_start = A_TO_OGG64(value); - else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { - music->loop_len = A_TO_OGG64(value); - isLoopLength = 1; - } - else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { - isLoopLength = 0; - music->loop_end = A_TO_OGG64(value); - } + if (OGG_UpdateSection(music) < 0) { + OGG_Delete(music); + return NULL; + } - #undef A_TO_OGG64 - SDL_free(param); + vc = vorbis.ov_comment(&music->vf, -1); + for (i = 0; i < vc->comments; i++) { + char *param = SDL_strdup(vc->user_comments[i]); + char *argument = param; + char *value = SDL_strchr(param, '='); + if (value == NULL) { + value = param + SDL_strlen(param); + } else { + *(value++) = '\0'; } - if (isLoopLength == 1) - music->loop_end = music->loop_start + music->loop_len; - else - music->loop_len = music->loop_end - music->loop_start; - - fullLength = vorbis.ov_pcm_total(&music->vf, -1); - if (((music->loop_start >= 0) || (music->loop_end > 0)) && - ((music->loop_start < music->loop_end) || (music->loop_end == 0)) && - (music->loop_start < fullLength) && - (music->loop_end <= fullLength)) { - if (music->loop_start < 0) music->loop_start = 0; - if (music->loop_end == 0) music->loop_end = fullLength; - music->loop = 1; + if (SDL_strcasecmp(argument, "LOOPSTART") == 0) + music->loop_start = SDL_strtoull(value, NULL, 0); + else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { + music->loop_len = SDL_strtoull(value, NULL, 0); + isLoopLength = 1; + } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { + isLoopLength = 0; + music->loop_end = SDL_strtoull(value, NULL, 0); } + SDL_free(param); + } + if (isLoopLength == 1) { + music->loop_end = music->loop_start + music->loop_len; } else { - SDL_OutOfMemory(); + music->loop_len = music->loop_end - music->loop_start; + } + + fullLength = vorbis.ov_pcm_total(&music->vf, -1); + if (((music->loop_start >= 0) || (music->loop_end > 0)) && + ((music->loop_start < music->loop_end) || (music->loop_end == 0)) && + (music->loop_start < fullLength) && + (music->loop_end <= fullLength)) { + if (music->loop_start < 0) music->loop_start = 0; + if (music->loop_end == 0) music->loop_end = fullLength; + music->loop = 1; } + + music->freesrc = freesrc; return music; } @@ -337,139 +391,119 @@ static void OGG_SetVolume(void *context, int volume) } /* Start playback of a given OGG stream */ -static int OGG_Play(void *context) +static int OGG_Play(void *context, int play_count) { OGG_music *music = (OGG_music *)context; - music->playing = SDL_TRUE; - return 0; + music->play_count = play_count; + return OGG_Seek(music, 0.0); } -/* Return non-zero if a stream is currently playing */ -static SDL_bool OGG_IsPlaying(void *context) +/* Play some of a stream previously started with OGG_play() */ +static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { OGG_music *music = (OGG_music *)context; - return music->playing; -} - -/* Read some Ogg stream data and convert it for output */ -static void OGG_getsome(OGG_music *music) -{ + SDL_bool looped = SDL_FALSE; + int filled, amount, result; int section; - int len; - char data[4096]; ogg_int64_t pcmPos; - SDL_AudioCVT *cvt; + filled = SDL_AudioStreamGet(music->stream, data, bytes); + if (filled != 0) { + return filled; + } + + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; + return 0; + } + + section = music->section; #ifdef OGG_USE_TREMOR - len = vorbis.ov_read(&music->vf, data, sizeof(data), §ion); + amount = vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, §ion); #else - len = (int)vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, §ion); + amount = (int)vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, 0, 2, 1, §ion); #endif - - pcmPos = vorbis.ov_pcm_tell(&music->vf); - if ((music->loop == 1) && (pcmPos >= music->loop_end)) { - len -= ((pcmPos - music->loop_end) * music->channels) * (long)sizeof(Uint16); - vorbis.ov_pcm_seek(&music->vf, music->loop_start); + if (amount < 0) { + set_ov_error("ov_read", amount); + return -1; } - if (len <= 0) { - if (len == 0) { - music->playing = SDL_FALSE; + if (section != music->section) { + music->section = section; + if (OGG_UpdateSection(music) < 0) { + return -1; } - return; } - cvt = &music->cvt; - if (section != music->section) { - vorbis_info *vi; - vi = vorbis.ov_info(&music->vf, -1); - SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, (int)vi->rate, - music_spec.format, music_spec.channels, music_spec.freq); - if (cvt->buf) { - SDL_free(cvt->buf); + pcmPos = vorbis.ov_pcm_tell(&music->vf); + if ((music->loop == 1) && (pcmPos >= music->loop_end)) { + amount -= ((pcmPos - music->loop_end) * music->channels) * (long)sizeof(Sint16); + result = vorbis.ov_pcm_seek(&music->vf, music->loop_start); + if (result < 0) { + set_ov_error("ov_pcm_seek", result); + return -1; } - cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult); - music->section = section; + looped = SDL_TRUE; } - if (cvt->buf) { - SDL_memcpy(cvt->buf, data, len); - if (cvt->needed) { - cvt->len = len; - SDL_ConvertAudio(cvt); + + if (amount > 0) { + if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { + return -1; + } + } else if (!looped) { + if (music->play_count == 1) { + music->play_count = 0; + SDL_AudioStreamFlush(music->stream); } else { - cvt->len_cvt = len; + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (OGG_Play(music, play_count) < 0) { + return -1; + } } - music->len_available = music->cvt.len_cvt; - music->snd_available = music->cvt.buf; - } else { - SDL_SetError("Out of memory"); - music->playing = SDL_FALSE; } + return 0; } - -/* Play some of a stream previously started with OGG_play() */ static int OGG_GetAudio(void *context, void *data, int bytes) { OGG_music *music = (OGG_music *)context; - Uint8 *snd = (Uint8 *)data; - int len = bytes; - int mixable; - - while ((len > 0) && music->playing) { - if (!music->len_available) { - OGG_getsome(music); - } - mixable = len; - if (mixable > music->len_available) { - mixable = music->len_available; - } - if (music->volume == MIX_MAX_VOLUME) { - SDL_memcpy(snd, music->snd_available, mixable); - } else { - SDL_MixAudioFormat(snd, music->snd_available, music_spec.format, mixable, music->volume); - } - music->len_available -= mixable; - music->snd_available += mixable; - len -= mixable; - snd += mixable; - } - - return len; + return music_pcm_getaudio(context, data, bytes, music->volume, OGG_GetSome); } /* Jump (seek) to a given position (time is in seconds) */ static int OGG_Seek(void *context, double time) { OGG_music *music = (OGG_music *)context; + int result; #ifdef OGG_USE_TREMOR - vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0)); + result = vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0)); #else - vorbis.ov_time_seek(&music->vf, time); + result = vorbis.ov_time_seek(&music->vf, time); #endif + if (result < 0) { + return set_ov_error("ov_time_seek", result); + } return 0; } -/* Stop playback of a stream previously started with OGG_play() */ -static void OGG_Stop(void *context) -{ - OGG_music *music = (OGG_music *)context; - music->playing = SDL_FALSE; -} - /* Close the given OGG stream */ static void OGG_Delete(void *context) { OGG_music *music = (OGG_music *)context; - if (music) { - if (music->cvt.buf) { - SDL_free(music->cvt.buf); - } - if (music->freesrc) { - SDL_RWclose(music->src); - } - vorbis.ov_clear(&music->vf); - SDL_free(music); + vorbis.ov_clear(&music->vf); + if (music->stream) { + SDL_FreeAudioStream(music->stream); + } + if (music->buffer) { + SDL_free(music->buffer); + } + if (music->freesrc) { + SDL_RWclose(music->src); } + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_OGG = @@ -486,12 +520,12 @@ Mix_MusicInterface Mix_MusicInterface_OGG = NULL, /* CreateFromFile */ OGG_SetVolume, OGG_Play, - OGG_IsPlaying, + NULL, /* IsPlaying */ OGG_GetAudio, OGG_Seek, NULL, /* Pause */ NULL, /* Resume */ - OGG_Stop, + NULL, /* Stop */ OGG_Delete, NULL, /* Close */ OGG_Unload, diff --git a/music_smpeg.c b/music_smpeg.c index 70995a90..b5651171 100644 --- a/music_smpeg.c +++ b/music_smpeg.c @@ -210,70 +210,98 @@ static void SMPEG_Unload(void) #endif /* SMPEG_DYNAMIC */ +typedef struct +{ + SMPEG *mp3; + SDL_RWops *src; + int freesrc; +} SMPEG_Music; + static void *SMPEG_CreateFromRW(SDL_RWops *src, int freesrc) { + SMPEG_Music *music; SMPEG_Info info; - SMPEG *mp3 = smpeg.SMPEG_new_rwops(src, &info, freesrc, 0); + + music = (SMPEG_Music *)SDL_calloc(1, sizeof(*music)); + if (!music) { + SDL_OutOfMemory(); + return NULL; + } + music->src = src; + + music->mp3 = smpeg.SMPEG_new_rwops(src, &info, SDL_FALSE, 0); if (!info.has_audio) { Mix_SetError("MPEG file does not have any audio stream."); - smpeg.SMPEG_delete(mp3); + smpeg.SMPEG_delete(music->mp3); + SDL_free(music); return NULL; } smpeg.SMPEG_actualSpec(mp3, &music_spec); - return mp3; + + music->freesrc = freesrc; + return music; } static void SMPEG_SetVolume(void *context, int volume) { - SMPEG *mp3 = (SMPEG *)context; - smpeg.SMPEG_setvolume(mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); + SMPEG_Music *music = (SMPEG_Music *)context; + smpeg.SMPEG_setvolume(music->mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); } static int SMPEG_Play(void *context) { - SMPEG *mp3 = (SMPEG *)context; - smpeg.SMPEG_enableaudio(mp3,1); - smpeg.SMPEG_enablevideo(mp3,0); - smpeg.SMPEG_play(mp3); + SMPEG_Music *music = (SMPEG_Music *)context; + smpeg.SMPEG_enableaudio(music->mp3, 1); + smpeg.SMPEG_enablevideo(music->mp3, 0); + smpeg.SMPEG_rewind(music->mp3); + smpeg.SMPEG_play(music->mp3); return 0; } static SDL_bool SMPEG_IsPlaying(void *context) { - SMPEG *mp3 = (SMPEG *)context; - return smpeg.SMPEG_status(mp3) == SMPEG_PLAYING ? SDL_TRUE : SDL_FALSE; + SMPEG_Music *music = (SMPEG_Music *)context; + return smpeg.SMPEG_status(music->mp3) == SMPEG_PLAYING ? SDL_TRUE : SDL_FALSE; } static int SMPEG_GetAudio(void *context, void *data, int bytes) { - SMPEG *mp3 = (SMPEG *)context; + SMPEG_Music *music = (SMPEG_Music *)context; Uint8 *stream = (Uint8 *)data; int len = bytes; - int left = (len - smpeg.SMPEG_playAudio(mp3, stream, len)); + int left = (len - smpeg.SMPEG_playAudio(music->mp3, stream, len)); + if (left > 0) { + stream += (len - left); return left; } static int SMPEG_Seek(void *context, double position) { - SMPEG *mp3 = (SMPEG *)context; - smpeg.SMPEG_rewind(mp3); - smpeg.SMPEG_play(mp3); + SMPEG_Music *music = (SMPEG_Music *)context; + smpeg.SMPEG_rewind(music->mp3); + smpeg.SMPEG_play(music->mp3); if (position > 0.0) { - smpeg.SMPEG_skip(mp3, (float)position); + smpeg.SMPEG_skip(music->mp3, (float)position); } return 0; } static void SMPEG_Stop(void *context) { - SMPEG *mp3 = (SMPEG *)context; - smpeg.SMPEG_stop(mp3); + SMPEG_Music *music = (SMPEG_Music *)context; + smpeg.SMPEG_stop(music->mp3); } static void SMPEG_Delete(void *context) { - SMPEG *mp3 = (SMPEG *)context; - smpeg.SMPEG_delete(mp3); + SMPEG_Music *music = (SMPEG_Music *)context; + + smpeg.SMPEG_delete(music->mp3); + + if (music->freesrc) { + SDL_RWclose(music->src); + } + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_SMPEG = diff --git a/music_timidity.c b/music_timidity.c index 0e319e7a..e2577e5d 100644 --- a/music_timidity.c +++ b/music_timidity.c @@ -28,6 +28,15 @@ #include "timidity/timidity.h" +typedef struct +{ + int play_count; + MidiSong *song; +} TIMIDITY_Music; + + +static int TIMIDITY_Seek(void *context, double position); + static int TIMIDITY_Open(const SDL_AudioSpec *spec) { return Timidity_Init(); @@ -40,8 +49,21 @@ static void TIMIDITY_Close(void) void *TIMIDITY_CreateFromRW(SDL_RWops *src, int freesrc) { - MidiSong *music = Timidity_LoadSong(src, &music_spec); - if (music && freesrc) { + TIMIDITY_Music *music; + + music = (TIMIDITY_Music *)SDL_calloc(1, sizeof(*music)); + if (!music) { + SDL_OutOfMemory(); + return NULL; + } + + music->song = Timidity_LoadSong(src, &music_spec); + if (!music->song) { + SDL_free(music); + return NULL; + } + + if (freesrc) { SDL_RWclose(src); } return music; @@ -49,38 +71,51 @@ void *TIMIDITY_CreateFromRW(SDL_RWops *src, int freesrc) static void TIMIDITY_SetVolume(void *context, int volume) { - MidiSong *music = (MidiSong *)context; - Timidity_SetVolume(music, volume); + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + Timidity_SetVolume(music->song, volume); } -static int TIMIDITY_Play(void *context) +static int TIMIDITY_Play(void *context, int play_count) { - MidiSong *music = (MidiSong *)context; - Timidity_Start(music); - return 0; + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + music->play_count = play_count; + Timidity_Start(music->song); + return TIMIDITY_Seek(music, 0.0); } static int TIMIDITY_GetAudio(void *context, void *data, int bytes) { - MidiSong *music = (MidiSong *)context; - if (!Timidity_PlaySome(music, data, bytes)) { - /* Nothing consumed, everything left */ - return bytes; + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + if (!Timidity_PlaySome(music->song, data, bytes)) { + if (music->play_count == 1) { + /* We didn't consume anything and we're done */ + music->play_count = 0; + return bytes; + } else { + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (TIMIDITY_Play(music, play_count) < 0) { + return -1; + } + } } return 0; } static int TIMIDITY_Seek(void *context, double position) { - MidiSong *music = (MidiSong *)context; - Timidity_Seek(music, (Uint32)(position * 1000)); + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + Timidity_Seek(music->song, (Uint32)(position * 1000)); return 0; } static void TIMIDITY_Delete(void *context) { - MidiSong *music = (MidiSong *)context; - Timidity_FreeSong(music); + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + Timidity_FreeSong(music->song); + SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_TIMIDITY = diff --git a/music_wav.c b/music_wav.c index 9fffd79a..41491dbf 100644 --- a/music_wav.c +++ b/music_wav.c @@ -39,12 +39,14 @@ typedef struct { SDL_bool freesrc; SDL_AudioSpec spec; int volume; + int play_count; Sint64 start; Sint64 stop; - SDL_AudioCVT cvt; + Uint8 *buffer; + SDL_AudioStream *stream; int numloops; WAVLoopPoint *loops; -} WAVStream; +} WAV_Music; /* Taken with permission from SDL_wave.h, part of the SDL library, @@ -116,84 +118,107 @@ typedef struct { /* Function to load the WAV/AIFF stream */ -static SDL_bool LoadWAVStream(WAVStream *wave); -static SDL_bool LoadAIFFStream(WAVStream *wave); +static SDL_bool LoadWAVMusic(WAV_Music *wave); +static SDL_bool LoadAIFFMusic(WAV_Music *wave); +static void WAV_Delete(void *context); /* Load a WAV stream from the given RWops object */ -static void *WAVStream_CreateFromRW(SDL_RWops *src, int freesrc) +static void *WAV_CreateFromRW(SDL_RWops *src, int freesrc) { - WAVStream *wave; + WAV_Music *music; + Uint32 magic; SDL_bool loaded = SDL_FALSE; - wave = (WAVStream *)SDL_calloc(1, sizeof(*wave)); - if (wave) { - Uint32 magic; - - wave->src = src; - wave->freesrc = freesrc; + music = (WAV_Music *)SDL_calloc(1, sizeof(*music)); + if (!music) { + SDL_OutOfMemory(); + return NULL; + } + music->volume = MIX_MAX_VOLUME; - magic = SDL_ReadLE32(src); - if (magic == RIFF || magic == WAVE) { - loaded = LoadWAVStream(wave); - } else if (magic == FORM) { - loaded = LoadAIFFStream(wave); - } else { - Mix_SetError("Unknown WAVE format"); - } - if (!loaded) { - SDL_free(wave); - return(NULL); - } - SDL_BuildAudioCVT(&wave->cvt, - wave->spec.format, wave->spec.channels, wave->spec.freq, - music_spec.format, music_spec.channels, music_spec.freq); - wave->volume = MIX_MAX_VOLUME; + magic = SDL_ReadLE32(src); + if (magic == RIFF || magic == WAVE) { + loaded = LoadWAVMusic(music); + } else if (magic == FORM) { + loaded = LoadAIFFMusic(music); } else { - SDL_OutOfMemory(); + Mix_SetError("Unknown WAVE format"); + } + if (!loaded) { + SDL_free(music); + return NULL; + } + music->buffer = (Uint8*)SDL_malloc(music->spec.size); + if (!music->buffer) { + WAV_Delete(music); + return NULL; + } + music->stream = SDL_NewAudioStream( + music->spec.format, music->spec.channels, music->spec.freq, + music_spec.format, music_spec.channels, music_spec.freq); + if (!music->stream) { + WAV_Delete(music); + return NULL; } - return wave; + + music->src = src; + music->freesrc = freesrc; + return music; } -static void WAVStream_SetVolume(void *context, int volume) +static void WAV_SetVolume(void *context, int volume) { - WAVStream *wave = (WAVStream *)context; - wave->volume = volume; + WAV_Music *music = (WAV_Music *)context; + music->volume = volume; } /* Start playback of a given WAV stream */ -static int WAVStream_Play(void *context) +static int WAV_Play(void *context, int play_count) { - WAVStream *wave = (WAVStream *)context; + WAV_Music *music = (WAV_Music *)context; int i; - for (i = 0; i < wave->numloops; ++i) { - WAVLoopPoint *loop = &wave->loops[i]; + for (i = 0; i < music->numloops; ++i) { + WAVLoopPoint *loop = &music->loops[i]; loop->active = SDL_TRUE; loop->current_play_count = loop->initial_play_count; } - SDL_RWseek(wave->src, wave->start, RW_SEEK_SET); - return 0; + music->play_count = play_count; + return SDL_RWseek(music->src, music->start, RW_SEEK_SET); } -/* Play some of a stream previously started with WAVStream_Play() */ -static int PlaySome(WAVStream *wave, Uint8 *stream, int len) +/* Play some of a stream previously started with WAV_Play() */ +static int WAV_GetSome(void *context, void *data, int bytes, SDL_bool *done) { + WAV_Music *music = (WAV_Music *)context; Sint64 pos, stop; WAVLoopPoint *loop; Sint64 loop_start; Sint64 loop_stop; + SDL_bool looped = SDL_FALSE; int i; - int consumed; + int filled, amount, result; - pos = SDL_RWtell(wave->src); - stop = wave->stop; + filled = SDL_AudioStreamGet(music->stream, data, bytes); + if (filled != 0) { + return filled; + } + + if (!music->play_count) { + /* All done */ + *done = SDL_TRUE; + return 0; + } + + pos = SDL_RWtell(music->src); + stop = music->stop; loop = NULL; - for (i = 0; i < wave->numloops; ++i) { - loop = &wave->loops[i]; + for (i = 0; i < music->numloops; ++i) { + loop = &music->loops[i]; if (loop->active) { - const int bytes_per_sample = (SDL_AUDIO_BITSIZE(wave->spec.format) / 8) * wave->spec.channels; - loop_start = wave->start + loop->start * bytes_per_sample; - loop_stop = wave->start + (loop->stop + 1) * bytes_per_sample; + const int bytes_per_sample = (SDL_AUDIO_BITSIZE(music->spec.format) / 8) * music->spec.channels; + loop_start = music->start + loop->start * bytes_per_sample; + loop_stop = music->start + (loop->stop + 1) * bytes_per_sample; if (pos >= loop_start && pos < loop_stop) { stop = loop_stop; @@ -203,97 +228,79 @@ static int PlaySome(WAVStream *wave, Uint8 *stream, int len) loop = NULL; } - if (wave->cvt.needed) { - int original_len = (int)((double)len/wave->cvt.len_ratio); - /* Make sure the length is a multiple of the sample size */ - { - const int bytes_per_sample = (SDL_AUDIO_BITSIZE(wave->spec.format) / 8) * wave->spec.channels; - const int alignment_mask = (bytes_per_sample - 1); - original_len &= ~alignment_mask; - } - if (wave->cvt.len != original_len) { - int worksize; - if (wave->cvt.buf != NULL) { - SDL_free(wave->cvt.buf); - } - worksize = original_len*wave->cvt.len_mult; - wave->cvt.buf=(Uint8 *)SDL_malloc(worksize); - if (wave->cvt.buf == NULL) { - return 0; - } - wave->cvt.len = original_len; - } - if ((stop - pos) < original_len) { - original_len = (int)(stop - pos); + amount = music->spec.size; + if ((stop - pos) < amount) { + amount = (int)(stop - pos); + } + amount = (int)SDL_RWread(music->src, music->buffer, 1, amount); + if (amount > 0) { + result = SDL_AudioStreamPut(music->stream, music->buffer, amount); + if (result < 0) { + return -1; } - original_len = (int)SDL_RWread(wave->src, wave->cvt.buf, 1, original_len); - wave->cvt.len = original_len; - SDL_ConvertAudio(&wave->cvt); - SDL_MixAudioFormat(stream, wave->cvt.buf, music_spec.format, wave->cvt.len_cvt, wave->volume); - consumed = wave->cvt.len_cvt; } else { - Uint8 *data; - if ((stop - pos) < len) { - len = (int)(stop - pos); - } - data = SDL_stack_alloc(Uint8, len); - if (data) { - len = (int)SDL_RWread(wave->src, data, 1, len); - SDL_MixAudioFormat(stream, data, music_spec.format, len, wave->volume); - SDL_stack_free(data); - } - consumed = len; + /* We might be looping, continue */ } - if (loop && SDL_RWtell(wave->src) >= stop) { + if (loop && SDL_RWtell(music->src) >= stop) { if (loop->current_play_count == 1) { loop->active = SDL_FALSE; } else { if (loop->current_play_count > 0) { --loop->current_play_count; } - SDL_RWseek(wave->src, loop_start, RW_SEEK_SET); + SDL_RWseek(music->src, loop_start, RW_SEEK_SET); + looped = SDL_TRUE; } } - return consumed; -} -static int WAVStream_GetAudio(void *context, void *data, int bytes) -{ - WAVStream *wave = (WAVStream *)context; - Uint8 *stream = (Uint8 *)data; - int len = bytes; + if (!looped && SDL_RWtell(music->src) >= music->stop) { + if (music->play_count == 1) { + music->play_count = 0; + SDL_AudioStreamFlush(music->stream); + } else { + int play_count = -1; + if (music->play_count > 0) { + play_count = (music->play_count - 1); + } + if (WAV_Play(music, play_count) < 0) { + return -1; + } + } + } - while ((SDL_RWtell(wave->src) < wave->stop) && (len > 0)) { - int consumed = PlaySome(wave, stream, len); - if (!consumed) - break; + /* We'll get called again in the case where we looped or have more data */ + return 0; +} - stream += consumed; - len -= consumed; - } - return len; +static int WAV_GetAudio(void *context, void *data, int bytes) +{ + WAV_Music *music = (WAV_Music *)context; + return music_pcm_getaudio(context, data, bytes, music->volume, WAV_GetSome); } /* Close the given WAV stream */ -static void WAVStream_Delete(void *context) +static void WAV_Delete(void *context) { - WAVStream *wave = (WAVStream *)context; + WAV_Music *music = (WAV_Music *)context; /* Clean up associated data */ - if (wave->loops) { - SDL_free(wave->loops); + if (music->loops) { + SDL_free(music->loops); + } + if (music->stream) { + SDL_FreeAudioStream(music->stream); } - if (wave->cvt.buf) { - SDL_free(wave->cvt.buf); + if (music->buffer) { + SDL_free(music->buffer); } - if (wave->freesrc) { - SDL_RWclose(wave->src); + if (music->freesrc) { + SDL_RWclose(music->src); } - SDL_free(wave); + SDL_free(music); } -static SDL_bool ParseFMT(WAVStream *wave, Uint32 chunk_length) +static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length) { SDL_AudioSpec *spec = &wave->spec; WaveFMT *format; @@ -339,6 +346,10 @@ static SDL_bool ParseFMT(WAVStream *wave, Uint32 chunk_length) } spec->channels = (Uint8) SDL_SwapLE16(format->channels); spec->samples = 4096; /* Good default buffer size */ + /* SDL_CalculateAudioSpec */ + spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8; + spec->size *= spec->channels; + spec->size *= spec->samples; loaded = SDL_TRUE; @@ -347,7 +358,7 @@ static SDL_bool ParseFMT(WAVStream *wave, Uint32 chunk_length) return loaded; } -static SDL_bool ParseDATA(WAVStream *wave, Uint32 chunk_length) +static SDL_bool ParseDATA(WAV_Music *wave, Uint32 chunk_length) { wave->start = SDL_RWtell(wave->src); wave->stop = wave->start + chunk_length; @@ -355,7 +366,7 @@ static SDL_bool ParseDATA(WAVStream *wave, Uint32 chunk_length) return SDL_TRUE; } -static SDL_bool AddLoopPoint(WAVStream *wave, Uint32 play_count, Uint32 start, Uint32 stop) +static SDL_bool AddLoopPoint(WAV_Music *wave, Uint32 play_count, Uint32 start, Uint32 stop) { WAVLoopPoint *loop; WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops)); @@ -375,7 +386,7 @@ static SDL_bool AddLoopPoint(WAVStream *wave, Uint32 play_count, Uint32 start, U return SDL_TRUE; } -static SDL_bool ParseSMPL(WAVStream *wave, Uint32 chunk_length) +static SDL_bool ParseSMPL(WAV_Music *wave, Uint32 chunk_length) { SamplerChunk *chunk; Uint8 *data; @@ -406,7 +417,7 @@ static SDL_bool ParseSMPL(WAVStream *wave, Uint32 chunk_length) return loaded; } -static SDL_bool LoadWAVStream(WAVStream *wave) +static SDL_bool LoadWAVMusic(WAV_Music *wave) { SDL_RWops *src = wave->src; Uint32 chunk_type; @@ -491,7 +502,7 @@ static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) (sanebuf[5] >> 1)) >> (29 - sanebuf[1]); } -static SDL_bool LoadAIFFStream(WAVStream *wave) +static SDL_bool LoadAIFFMusic(WAV_Music *wave) { SDL_RWops *src = wave->src; SDL_AudioSpec *spec = &wave->spec; @@ -604,17 +615,17 @@ Mix_MusicInterface Mix_MusicInterface_WAV = NULL, /* Load */ NULL, /* Open */ - WAVStream_CreateFromRW, + WAV_CreateFromRW, NULL, /* CreateFromFile */ - WAVStream_SetVolume, - WAVStream_Play, + WAV_SetVolume, + WAV_Play, NULL, /* IsPlaying */ - WAVStream_GetAudio, + WAV_GetAudio, NULL, /* Seek */ NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ - WAVStream_Delete, + WAV_Delete, NULL, /* Close */ NULL, /* Unload */ };