From cfc2b7b72039d784a103360691dc94b631bd6bed Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 12 May 2006 06:14:15 +0000 Subject: [PATCH] Added support for dynamically loading Ogg Vorbis library --- CHANGES | 2 + configure.in | 83 ++++++++++++++++++++------------ dynamic_ogg.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ dynamic_ogg.h | 43 +++++++++++++++++ load_ogg.c | 19 +++++--- music_ogg.c | 35 +++++++++----- 6 files changed, 263 insertions(+), 49 deletions(-) create mode 100644 dynamic_ogg.c create mode 100644 dynamic_ogg.h diff --git a/CHANGES b/CHANGES index 507f68c2..c2941bb1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ 1.2.7: +Sam Lantinga - Thu May 11 22:22:43 PDT 2006 + * Added support for dynamically loading Ogg Vorbis library Sam Lantinga - Sun Apr 30 09:01:44 PDT 2006 * Removed automake dependency, to allow Universal binaries on Mac OS X * Added gcc-fat.sh for generating Universal binaries on Mac OS X diff --git a/configure.in b/configure.in index d2b2b439..1409ac17 100644 --- a/configure.in +++ b/configure.in @@ -109,16 +109,16 @@ EXTRA_LDFLAGS="$EXTRA_LDFLAGS $SDL_LIBS" dnl Check command-line options -AC_ARG_ENABLE(music-cmd, -[ --enable-music-cmd support an external music player [default=yes]], - , enable_music_cmd=yes) +AC_ARG_ENABLE([music-cmd], +AC_HELP_STRING([--enable-music-cmd], [support an external music player [[default=yes]]]), + [], [enable_music_cmd=yes]) if test x$enable_music_cmd = xyes; then SOURCES="$SOURCES $srcdir/music_cmd.c" EXTRA_CFLAGS="$EXTRA_CFLAGS -DCMD_MUSIC" fi -AC_ARG_ENABLE(music-wave, -[ --enable-music-wave enable streaming WAVE music [default=yes]], - , enable_music_wave=yes) +AC_ARG_ENABLE([music-wave], +AC_HELP_STRING([--enable-music-wave], [enable streaming WAVE music [[default=yes]]]), + [], [enable_music_wave=yes]) if test x$enable_music_wave = xyes; then SOURCES="$SOURCES $srcdir/wavestream.c" EXTRA_CFLAGS="$EXTRA_CFLAGS -DWAV_MUSIC" @@ -128,9 +128,9 @@ libmikmod_maj=3 libmikmod_min=1 libmikmod_rev=10 libmikmod_ver="$libmikmod_maj.$libmikmod_min.$libmikmod_rev" -AC_ARG_ENABLE(music-libmikmod, -[ --enable-music-libmikmod enable MOD music via external libmikmod [default=no]], - , enable_music_libmikmod=no) +AC_ARG_ENABLE([music-libmikmod], +AC_HELP_STRING([--enable-music-libmikmod], [enable MOD music via external libmikmod [[default=no]]]), + [], [enable_music_libmikmod=no]) if test x$enable_music_libmikmod = xyes; then AC_PATH_PROG(LIBMIKMOD_CONFIG, libmikmod-config, no, [$PATH]) if test "$LIBMIKMOD_CONFIG" != "no" ; then @@ -160,28 +160,28 @@ int main(int argc, char **argv) fi fi -AC_ARG_ENABLE(music-mod, -[ --enable-music-mod enable MOD music via mikmod [default=yes]], -, enable_music_mod=$no_libmikmod) +AC_ARG_ENABLE([music-mod], +AC_HELP_STRING([--enable-music-mod], [enable MOD music via mikmod [[default=yes]]]), + [], [enable_music_mod=$no_libmikmod]) if test x$enable_music_mod = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMOD_MUSIC -I\$(srcdir)/mikmod" SOURCES="$SOURCES $srcdir/mikmod/*.c" fi -AC_ARG_ENABLE(music-midi, -[ --enable-music-midi enable MIDI music via timidity [default=yes]], - , enable_music_midi=yes) +AC_ARG_ENABLE([music-midi], +AC_HELP_STRING([--enable-music-midi], [enable MIDI music via timidity [[default=yes]]]), + [], [enable_music_midi=yes]) if test x$enable_music_midi = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMID_MUSIC" - AC_ARG_ENABLE(music-timidity-midi, -[ --enable-music-timidity-midi enable timidity MIDI output [default=yes]], - , enable_music_timidity_midi=yes) + AC_ARG_ENABLE([music-timidity-midi], +AC_HELP_STRING([--enable-music-timidity-midi], [enable timidity MIDI output [[default=yes]]]), + [], [enable_music_timidity_midi=yes]) if test x$enable_music_timidity_midi = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DUSE_TIMIDITY_MIDI -I\$(srcdir)/timidity" SOURCES="$SOURCES $srcdir/timidity/*.c" fi - AC_ARG_ENABLE(music-native-midi, -[ --enable-music-native-midi enable native MIDI music output [default=yes]], - , enable_music_native_midi=yes) + AC_ARG_ENABLE([music-native-midi], +AC_HELP_STRING([--enable-music-native-midi], [enable native MIDI music output [[default=yes]]]), + [], [enable_music_native_midi=yes]) if test x$enable_music_native_midi = xyes; then use_music_native_midi=no case "$host" in @@ -199,9 +199,9 @@ if test x$enable_music_midi = xyes; then SOURCES="$SOURCES $srcdir/native_midi/*.c" fi fi - AC_ARG_ENABLE(music-native-midi-gpl, -[ --enable-music-native-midi-gpl enable native MIDI on UNIX using GPL code [default=no]], - , enable_music_native_midi_gpl=no) + AC_ARG_ENABLE([music-native-midi-gpl], +AC_HELP_STRING([--enable-music-native-midi-gpl], [enable native MIDI on UNIX using GPL code [[default=no]]]), + [], [enable_music_native_midi_gpl=no]) if test x$enable_music_native_midi_gpl = xyes; then use_music_native_midi_gpl=no case "$host" in @@ -215,9 +215,9 @@ if test x$enable_music_midi = xyes; then fi fi fi -AC_ARG_ENABLE(music-ogg, -[ --enable-music-ogg enable Ogg Vorbis music [default=yes]], - , enable_music_ogg=yes) +AC_ARG_ENABLE([music-ogg], +AC_HELP_STRING([--enable-music-ogg], [enable Ogg Vorbis music [[default=yes]]]), + [], [enable_music_ogg=yes]) if test x$enable_music_ogg = xyes; then AC_MSG_CHECKING(for Ogg Vorbis headers and libraries) have_vorbis=no @@ -229,13 +229,36 @@ if test x$enable_music_ogg = xyes; then ]) AC_MSG_RESULT($have_vorbis) if test x$have_vorbis = xyes; then - SOURCES="$SOURCES $srcdir/load_ogg.c $srcdir/music_ogg.c" + AC_ARG_ENABLE([music-ogg-shared], +AC_HELP_STRING([--enable-music-ogg-shared], [dynamically load Ogg Vorbis support [[default=yes]]]), + [], [enable_music_ogg_shared=yes]) + case "$host" in + *-*-darwin*) # FIXME when Mac OS X ships with Ogg Vorbis + ogg_lib='' + ;; + *-*-cygwin* | *-*-mingw32*) + ogg_lib='vorbisfile.dll' + ;; + *) + for path in /usr/lib /usr/local/lib; do + if test x$ogg_lib = x; then + ogg_lib=[`ls -- $path/libvorbisfile.so.[0-9] 2>/dev/null | sort -r | sed 's/.*\/\(.*\)/\1/; q'`] + fi + done + ;; + esac + SOURCES="$SOURCES $srcdir/*_ogg.c" EXTRA_CFLAGS="$EXTRA_CFLAGS -DOGG_MUSIC" - EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lvorbisfile -lvorbis -logg" + if test x$enable_music_ogg_shared = xyes && test x$ogg_lib != x; then + echo "-- dynamic libvorbisfile -> $ogg_lib" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DOGG_DYNAMIC=\\\"$ogg_lib\\\"" + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lvorbisfile -lvorbis -logg" + fi fi fi AC_ARG_ENABLE(music-mp3, -[ --enable-music-mp3 enable MP3 music via smpeg [default=yes]], +[ --enable-music-mp3 enable MP3 music via smpeg [[default=yes]]], , enable_music_mp3=yes) if test x$enable_music_mp3 = xyes; then SMPEG_VERSION=0.4.3 diff --git a/dynamic_ogg.c b/dynamic_ogg.c new file mode 100644 index 00000000..9adaf56e --- /dev/null +++ b/dynamic_ogg.c @@ -0,0 +1,130 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef OGG_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_ogg.h" + +vorbis_loader vorbis = { + 0, NULL +}; + +#ifdef OGG_DYNAMIC +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.handle = SDL_LoadObject(OGG_DYNAMIC); + if ( vorbis.handle == NULL ) { + return -1; + } + vorbis.ov_clear = + (int (*)(OggVorbis_File *)) + SDL_LoadFunction(vorbis.handle, "ov_clear"); + if ( vorbis.ov_clear == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_info = + (vorbis_info *(*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_info"); + if ( vorbis.ov_info == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_open = + (int (*)(FILE *,OggVorbis_File *,char *,long)) + SDL_LoadFunction(vorbis.handle, "ov_open"); + if ( vorbis.ov_open == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_open_callbacks = + (int (*)(void *, OggVorbis_File *, char *, long, ov_callbacks)) + SDL_LoadFunction(vorbis.handle, "ov_open_callbacks"); + if ( vorbis.ov_open_callbacks == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_pcm_total = + (ogg_int64_t (*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_pcm_total"); + if ( vorbis.ov_pcm_total == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_read = + (long (*)(OggVorbis_File *,char *,int, int,int,int,int *)) + SDL_LoadFunction(vorbis.handle, "ov_read"); + if ( vorbis.ov_read == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_time_seek = + (int (*)(OggVorbis_File *,double)) + SDL_LoadFunction(vorbis.handle, "ov_time_seek"); + if ( vorbis.ov_time_seek == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + } + ++vorbis.loaded; + + return 0; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + SDL_UnloadObject(vorbis.handle); + } + --vorbis.loaded; +} +#else +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.ov_clear = ov_clear; + vorbis.ov_info = ov_info; + vorbis.ov_open = ov_open; + vorbis.ov_open_callbacks = ov_open_callbacks; + vorbis.ov_pcm_total = ov_pcm_total; + vorbis.ov_read = ov_read; + vorbis.ov_time_seek = ov_time_seek; + } + ++vorbis.loaded; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + } + --vorbis.loaded; +} +#endif /* OGG_DYNAMIC */ + +#endif /* OGG_MUSIC */ diff --git a/dynamic_ogg.h b/dynamic_ogg.h new file mode 100644 index 00000000..dd882607 --- /dev/null +++ b/dynamic_ogg.h @@ -0,0 +1,43 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef OGG_MUSIC +#include + +typedef struct { + int loaded; + void *handle; + int (*ov_clear)(OggVorbis_File *vf); + vorbis_info *(*ov_info)(OggVorbis_File *vf,int link); + int (*ov_open)(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); + int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks); + ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i); + long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream); + int (*ov_time_seek)(OggVorbis_File *vf,double pos); +} vorbis_loader; + +extern vorbis_loader vorbis; + +extern int Mix_InitOgg(); +extern void Mix_QuitOgg(); + +#endif diff --git a/load_ogg.c b/load_ogg.c index efd0b6c5..8fa6c54c 100644 --- a/load_ogg.c +++ b/load_ogg.c @@ -27,13 +27,13 @@ #include #include #include -#include #include "SDL_mutex.h" #include "SDL_endian.h" #include "SDL_timer.h" #include "SDL_mixer.h" +#include "dynamic_ogg.h" #include "load_ogg.h" static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) @@ -80,13 +80,16 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */ goto done; + if ( Mix_InitOgg() < 0 ) + goto done; + callbacks.read_func = sdl_read_func; callbacks.seek_func = sdl_seek_func; callbacks.tell_func = sdl_tell_func; callbacks.close_func = freesrc ? sdl_close_func_freesrc : sdl_close_func_nofreesrc; - if (ov_open_callbacks(src, &vf, NULL, 0, callbacks) != 0) + if (vorbis.ov_open_callbacks(src, &vf, NULL, 0, callbacks) != 0) { SDL_SetError("OGG bitstream is not valid Vorbis stream!"); goto done; @@ -94,7 +97,7 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, must_close = 0; - info = ov_info(&vf, -1); + info = vorbis.ov_info(&vf, -1); *audio_buf = NULL; *audio_len = 0; @@ -105,7 +108,7 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, spec->freq = info->rate; spec->samples = 4096; /* buffer size */ - samples = (long)ov_pcm_total(&vf, -1); + samples = (long)vorbis.ov_pcm_total(&vf, -1); *audio_len = spec->size = samples * spec->channels * 2; *audio_buf = malloc(*audio_len); @@ -114,9 +117,9 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, buf = *audio_buf; to_read = *audio_len; - for (read = ov_read(&vf, buf, to_read, 0/*LE*/, 2/*16bit*/, 1/*signed*/, &bitstream); + for (read = vorbis.ov_read(&vf, (char *)buf, to_read, 0/*LE*/, 2/*16bit*/, 1/*signed*/, &bitstream); read > 0; - read = ov_read(&vf, buf, to_read, 0, 2, 1, &bitstream)) + read = vorbis.ov_read(&vf, (char *)buf, to_read, 0, 2, 1, &bitstream)) { if (read == OV_HOLE || read == OV_EBADLINK) break; /* error */ @@ -125,7 +128,7 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, buf += read; } - ov_clear(&vf); + vorbis.ov_clear(&vf); was_error = 0; /* Don't return a buffer that isn't a multiple of samplesize */ @@ -144,6 +147,8 @@ SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, if ( was_error ) spec = NULL; + Mix_QuitOgg(); + return(spec); } /* Mix_LoadOGG_RW */ diff --git a/music_ogg.c b/music_ogg.c index eeb3cb86..d177dd93 100644 --- a/music_ogg.c +++ b/music_ogg.c @@ -31,6 +31,7 @@ #include #include "SDL_mixer.h" +#include "dynamic_ogg.h" #include "music_ogg.h" /* This is the format of the audio mixer data */ @@ -65,20 +66,25 @@ OGG_music *OGG_new(const char *file) OGG_setvolume(music, MIX_MAX_VOLUME); music->section = -1; + if ( Mix_InitOgg() < 0 ) { + return(NULL); + } fp = fopen(file, "rb"); if ( fp == NULL ) { - SDL_SetError("Couldn't open %s", file); free(music); + Mix_QuitOgg(); + SDL_SetError("Couldn't open %s", file); return(NULL); } - if ( ov_open(fp, &music->vf, NULL, 0) < 0 ) { - SDL_SetError("Not an Ogg Vorbis audio stream"); - free(music); + if ( vorbis.ov_open(fp, &music->vf, NULL, 0) < 0 ) { fclose(fp); + free(music); + Mix_QuitOgg(); + SDL_SetError("Not an Ogg Vorbis audio stream"); return(NULL); } } else { - SDL_SetError("Out of memory"); + SDL_OutOfMemory(); } return(music); } @@ -123,14 +129,18 @@ OGG_music *OGG_new_RW(SDL_RWops *rw) OGG_setvolume(music, MIX_MAX_VOLUME); music->section = -1; - if ( ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) { - SDL_SetError("Not an Ogg Vorbis audio stream"); + if ( Mix_InitOgg() < 0 ) { + return(NULL); + } + if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) { free(music); SDL_RWclose(rw); + Mix_QuitOgg(); + SDL_SetError("Not an Ogg Vorbis audio stream"); return(NULL); } } else { - SDL_SetError("Out of memory"); + SDL_OutOfMemory(); } return(music); } @@ -155,7 +165,7 @@ static void OGG_getsome(OGG_music *music) char data[4096]; SDL_AudioCVT *cvt; - len = ov_read(&music->vf, data, sizeof(data), 0, 2, 1, §ion); + len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, §ion); if ( len <= 0 ) { if ( len == 0 ) { music->playing = 0; @@ -166,7 +176,7 @@ static void OGG_getsome(OGG_music *music) if ( section != music->section ) { vorbis_info *vi; - vi = ov_info(&music->vf, -1); + vi = vorbis.ov_info(&music->vf, -1); SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate, mixer.format,mixer.channels,mixer.freq); if ( cvt->buf ) { @@ -232,15 +242,16 @@ void OGG_delete(OGG_music *music) if ( music->cvt.buf ) { free(music->cvt.buf); } - ov_clear(&music->vf); + vorbis.ov_clear(&music->vf); free(music); + Mix_QuitOgg(); } } /* Jump (seek) to a given position (time is in seconds) */ void OGG_jump_to_time(OGG_music *music, double time) { - ov_time_seek( &music->vf, time ); + vorbis.ov_time_seek( &music->vf, time ); } #endif /* OGG_MUSIC */