From d82c61e8b0ff6b7660ec0288c50e6f3e6547ed4a Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Tue, 14 Jul 2009 19:41:52 -0400 Subject: [PATCH] Implement support for libmodplug in Engine.Music. --- configure.ac | 40 ++++++++++++- m4/libmodplug.m4 | 75 +++++++++++++++++++++++++ src/engine/Makefile.inc | 16 +++++- src/engine/modplug-types.h | 34 ++++++++++++ src/engine/music-dummymod.c | 73 ++++++++++++++++++++++++ src/engine/music-modplug.c | 108 ++++++++++++++++++++++++++++++++++++ src/engine/music-module.h | 19 +++++++ src/engine/music.c | 48 ++++++++++------ 8 files changed, 393 insertions(+), 20 deletions(-) create mode 100644 m4/libmodplug.m4 create mode 100644 src/engine/modplug-types.h create mode 100644 src/engine/music-dummymod.c create mode 100644 src/engine/music-modplug.c create mode 100644 src/engine/music-module.h diff --git a/configure.ac b/configure.ac index 468147b..808965b 100644 --- a/configure.ac +++ b/configure.ac @@ -29,8 +29,46 @@ if test x$with_gobject = xyes; then AM_PATH_GLIB_2_0(, [have_gobject=yes], [have_gobject=no], [gobject]) fi -AM_CONDITIONAL([BUILD_UPKG], [test x$have_gobject = xyes]) +AC_ARG_WITH([modlib], + [AS_HELP_STRING([--with-modlib], + [specify which module decoding library to use - one of modplug, none or auto. [default=auto]] + )], + [with_modlib=$withval], + [with_modlib=auto]) + +CHECK_LIBMODPLUG([have_libmodplug=yes], [have_libmodplug=no]) +AC_MSG_CHECKING([which module library to use]) +case $with_modlib in +none) + use_modlib=none +;; +no) + use_modlib=none +;; +modplug) + if test x$have_libmodplug = xyes; then + use_modlib=modplug + else + use_modlib=none + fi +;; +auto) + if test x$have_libmodplug = xyes; then + use_modlib=modplug + else + use_modlib=none + fi +;; +*) + AC_MSG_ERROR([Invalid argument to --with-modlib: $with_modlib]) +;; +esac +AC_MSG_RESULT([$use_modlib]) +AM_CONDITIONAL([USE_LIBMODPLUG], [test x$use_modlib = xmodplug]) +AM_CONDITIONAL([USE_DUMMYMOD], [test x$use_modlib = xnone]) + +AM_CONDITIONAL([BUILD_UPKG], [test x$have_gobject = xyes]) AC_CONFIG_FILES([ Makefile diff --git a/m4/libmodplug.m4 b/m4/libmodplug.m4 new file mode 100644 index 0000000..cd16e8a --- /dev/null +++ b/m4/libmodplug.m4 @@ -0,0 +1,75 @@ +AC_DEFUN([_LIBMODPLUG_TEST], [dnl +AC_CACHE_CHECK([whether libmodplug works], + [libmodplug_cv_works], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl +#include +], [dnl +unsigned char buf@<:@128@:>@ = {0}; +ModPlugFile *f = ModPlug_Load(buf, sizeof buf); +ModPlug_Unload(f); +])], [libmodplug_cv_works=yes], [libmodplug_cv_works=no])]) +]) + +dnl CHECK_LIBMODPLUG([action-if-ok], [action-if-fail]) +AC_DEFUN([CHECK_LIBMODPLUG], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([LIBMODPLUG_CFLAGS], [C compiler flags for libmodplug])dnl +AC_ARG_VAR([LIBMODPLUG_LIBS], [linker flags for libmodplug])dnl + +if test x"$PKG_CONFIG" != x; then + AC_MSG_CHECKING([pkg-config database for libmodplug]) + _PKG_CONFIG([libmodplug_cflags], [cflags], [libmodplug]) + _PKG_CONFIG([libmodplug_libs], [libs], [libmodplug]) + if test x$pkg_failed = xyes; then + errors=`$PKG_CONFIG --errors-to-stdout --print-errors libmodplug` + echo "$errors" >&AS_MESSAGE_LOG_FD + AC_MSG_RESULT([no]) + else + if test x"$LIBMODPLUG_CFLAGS" = x; then + LIBMODPLUG_CFLAGS=$pkg_cv_libmodplug_cflags + fi + if test x"$LIBMODPLUG_LIBS" = x; then + LIBMODPLUG_LIBS=$pkg_cv_libmodplug_libs + fi + + AC_MSG_RESULT([yes]) + fi +fi + +if test x"$LIBMODPLUG_CFLAGS" = x; then + LIBMODPLUG_CFLAGS="-I$includedir/libmodplug" +fi + +if test x"$LIBMODPLUG_LIBS" = x; then + LIBMODPLUG_LIBS=-lmodplug +fi + +AC_LANG_PUSH([C]) +old_cflags=$CFLAGS +old_libs=$LIBS +CFLAGS="$LIBMODPLUG_CFLAGS $CFLAGS" +LIBS="$LIBMODPLUG_LIBS $LIBS" + +_LIBMODPLUG_TEST + +CFLAGS=$old_cflags +LIBS=$old_libs +AC_LANG_POP([C]) + +if test x"$libmodplug_cv_works" = x"yes"; then + AC_SUBST([LIBMODPLUG_CFLAGS]) + AC_SUBST([LIBMODPLUG_LIBS]) + ifelse([$1], [], [true], [$1]) +else + ifelse([$2], [], [AC_MSG_FAILURE([dnl +libmodplug is required. The latest version can be +obtained from http://modplug-xmms.sourceforge.net/. + +If libmodplug is installed but was not found by this configure script, +consider adjusting LIBMODPLUG_CFLAGS and/or LIBMODPLUG_LIBS as necessary. + +If pkg-config is installed, it may help to adjust PKG_CONFIG_PATH +if libmodplug is installed in a non-standard prefix. +])], [$2]) +fi +]) diff --git a/src/engine/Makefile.inc b/src/engine/Makefile.inc index 897ba2b..774f708 100644 --- a/src/engine/Makefile.inc +++ b/src/engine/Makefile.inc @@ -1,7 +1,17 @@ -noinst_HEADERS += engine/music.h engine/texture.h +noinst_HEADERS += engine/music.h engine/texture.h engine/music-module.h lib_LTLIBRARIES += engine.la engine_la_SOURCES = engine/music.c engine/texture.c engine/engine.c engine_la_CFLAGS = $(GLIB_CFLAGS) -engine_la_LDFLAGS = $(GLIB_LIBS) -engine_la_LDFLAGS += -module -avoid-version -export-symbols-regex _LTX_ +engine_la_LIBADD = $(GLIB_LIBS) +engine_la_LDFLAGS = -module -avoid-version -export-symbols-regex _LTX_ + +if USE_DUMMYMOD +engine_la_SOURCES += engine/music-dummymod.c +endif + +if USE_LIBMODPLUG +engine_la_SOURCES += engine/music-modplug.c engine/modplug-types.h +engine_la_CFLAGS += $(LIBMODPLUG_CFLAGS) +engine_la_LIBADD += $(LIBMODPLUG_LIBS) +endif diff --git a/src/engine/modplug-types.h b/src/engine/modplug-types.h new file mode 100644 index 0000000..e75156d --- /dev/null +++ b/src/engine/modplug-types.h @@ -0,0 +1,34 @@ +#ifndef MODPLUG_TYPES_H_ +#define MODPLUG_TYPES_H_ + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + +#endif diff --git a/src/engine/music-dummymod.c b/src/engine/music-dummymod.c new file mode 100644 index 0000000..c2e61b7 --- /dev/null +++ b/src/engine/music-dummymod.c @@ -0,0 +1,73 @@ +#include +#include + +#include "music-module.h" +#include "upkg.h" + +struct music_mod { + struct upkg_file *f; +}; + +int music_mod_init(void) +{ + return 0; +} + +void music_mod_exit(void) +{ +} + +struct music_mod *music_mod_open(struct upkg_file *f) +{ + struct music_mod *m = malloc(sizeof *m); + + if (m) { + m->f = f; + } + + return m; +} + +void music_mod_close(struct music_mod *m) +{ + free(m); +} + +int music_mod_dump(struct music_mod *m, FILE *of) +{ + unsigned char buf[1024]; + size_t rc; + + if (upkg_export_seek(m->f, 0, SEEK_SET) != 0) { + return -1; + } + + while (1) { + rc = upkg_export_read(m->f, buf, sizeof buf); + if (rc == 0) { + if (!m->f->eof) + return -1; + return 0; + } + + if (fwrite(buf, rc, 1, of) != 1) { + if (feof(of)) { + fprintf(stderr, "unexpected end-of-file.\n"); + } else { + perror("fwrite"); + } + return -1; + } + + if (rc < sizeof buf) { + if (!m->f->eof) + return -1; + return 0; + } + } +} + +const char *music_mod_type(struct music_mod *mod) +{ + return "unknown"; +} diff --git a/src/engine/music-modplug.c b/src/engine/music-modplug.c new file mode 100644 index 0000000..e0a0695 --- /dev/null +++ b/src/engine/music-modplug.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include "modplug-types.h" + +#include "music-module.h" +#include "upkg.h" + +struct music_mod { + ModPlugFile *f; + size_t len, alloc; + unsigned char buf[]; +}; + +#define BUF_INIT_SIZE 65536 + +int music_mod_init(void) +{ + ModPlug_Settings settings = { + .mFlags = MODPLUG_ENABLE_OVERSAMPLING, + .mChannels = 2, + .mFrequency = 44100, + .mResamplingMode = MODPLUG_RESAMPLE_SPLINE, + .mLoopCount = -1, + }; + + ModPlug_SetSettings(&settings); + return 0; +} + +void music_mod_exit(void) +{ +} + +static struct music_mod *readfile(struct upkg_file *f) +{ + struct music_mod *m = malloc(sizeof *m + BUF_INIT_SIZE); + if (!m) return NULL; + *m = (struct music_mod) { + .alloc = BUF_INIT_SIZE + }; + + while (1) { + struct music_mod *tmp; + size_t sz = m->alloc - m->len; + + m->len += upkg_export_read(f, m->buf + m->len, sz); + if (m->alloc != m->len) { + if (f->eof) { + tmp = realloc(m, sizeof *m + m->len); + return tmp ? tmp : m; + } + + break; + } + + tmp = realloc(m, sizeof *m + 2*m->alloc); + if (!tmp) + break; + m = tmp; + m->alloc *= 2; + } + + free(m); + return NULL; +} + +struct music_mod *music_mod_open(struct upkg_file *f) +{ + struct music_mod *m = readfile(f); + if (!m) return NULL; + + m->f = ModPlug_Load(m->buf, m->len); + if (!m->f) { + free(m); + return NULL; + } + + return m; +} + +int music_mod_dump(struct music_mod *m, FILE *of) +{ + if (fwrite(m->buf, m->len, 1, of) != 1) + return -1; + return 0; +} + +void music_mod_close(struct music_mod *m) +{ + ModPlug_Unload(m->f); + free(m); +} + +const char *music_mod_type(struct music_mod *m) +{ + int type = ModPlug_GetModuleType(m->f); + + if (type & MOD_TYPE_MOD) + return "mod"; + if (type & MOD_TYPE_S3M) + return "s3m"; + if (type & MOD_TYPE_XM) + return "xm"; + if (type & MOD_TYPE_IT) + return "it"; + return "unknown"; +} diff --git a/src/engine/music-module.h b/src/engine/music-module.h new file mode 100644 index 0000000..d425b9e --- /dev/null +++ b/src/engine/music-module.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_MODULE_H_ +#define MUSIC_MODULE_H_ + +#include "upkg.h" + +struct music_mod; + +/* Initialize the module decoding system. */ +int music_mod_init(void); +/* Shutdown the module decoding system. */ +void music_mod_exit(void); + +struct music_mod *music_mod_open(struct upkg_file *f); +void music_mod_close(struct music_mod *mod); +int music_mod_dump(struct music_mod *mod, FILE *of); + +const char *music_mod_type(struct music_mod *mod); + +#endif diff --git a/src/engine/music.c b/src/engine/music.c index f770662..c562911 100644 --- a/src/engine/music.c +++ b/src/engine/music.c @@ -5,6 +5,7 @@ #include "exportable.h" #include "loadable.h" #include "uobject.h" +#include "music-module.h" #include "music.h" #include "upkg.h" @@ -13,6 +14,7 @@ struct music_priv { struct upkg_file *f; + struct music_mod *mod; unsigned loaded; }; @@ -34,6 +36,11 @@ static int load(GObject *o) if (upkg_export_seek(priv->f, 0, SEEK_SET) != 0) { return -1; } + + priv->mod = music_mod_open(priv->f); + if (!priv->mod) { + return -1; + } } priv->loaded++; @@ -45,35 +52,43 @@ static void unload(GObject *o) struct music_priv *priv = MUSIC_GET_PRIV(o); g_return_if_fail(priv->loaded > 0); - --priv->loaded; + if (--priv->loaded == 0) { + music_mod_close(priv->mod); + } } static int export(GObject *o, FILE *f) { struct music_priv *priv = MUSIC_GET_PRIV(o); - unsigned char buf[1024]; + int rc; - if (!priv->f || upkg_export_seek(priv->f, 0, SEEK_SET) != 0) + if (load(o) != 0) return -1; - while (!priv->f->eof) { - size_t rc = upkg_export_read(priv->f, buf, sizeof buf); - if (rc == 0) { - if (priv->f->eof) break; - return -1; - } + rc = music_mod_dump(priv->mod, f); - if (fwrite(buf, 1, rc, f) != rc) - return -1; - } + unload(o); - return 0; + return rc; } static int export_name(GObject *o, char *buf, size_t n) { struct music_priv *priv = MUSIC_GET_PRIV(o); - return snprintf(buf, n, "%s", priv->f ? priv->f->name : ""); + const char *type; + int rc; + + if (load(o) != 0) { + if (n > 0) *buf = 0; + return 0; + } + + type = music_mod_type(priv->mod); + rc = snprintf(buf, n, "%s.%s", priv->f->name, type); + + unload(o); + + return rc; } static void exportable_init(UObjectExportable *e) @@ -91,7 +106,6 @@ static void loadable_init(UObjectLoadable *l) static int deserialize(UObject *o, struct upkg_file *f) { struct music_priv *priv = MUSIC_GET_PRIV(o); - EngineMusic *m = ENGINE_MUSIC(o); size_t rc, pos = 0, buflen; unsigned char buf[32]; long size; @@ -120,7 +134,6 @@ static int deserialize(UObject *o, struct upkg_file *f) f->base += pos; f->len = size; upkg_export_seek(f, 0, SEEK_SET); - priv->f = f; return 0; @@ -155,8 +168,11 @@ static void engine_music_class_init(EngineMusicClass *class) uo->deserialize = deserialize; go->finalize = engine_music_finalize; + + music_mod_init(); } static void engine_music_class_finalize(EngineMusicClass *class) { + music_mod_exit(); } -- 2.43.2