From 4814b482fbf15257da1641de0633ad0451acb9e5 Mon Sep 17 00:00:00 2001 From: paladine Date: Sun, 18 May 2008 17:17:49 +0000 Subject: [PATCH] Added initial implement of the audacious plugin for audacious 1.4.x or higher. There are still a few things to do such as add a configuration GUI, but these things are marked via TODO in the source files. I also added the autoconf/automake building system. To generate everything you need, run ./bootstrap in the top level directory, it will generate the configure script. Then run ./configure. It will generate Makefile.unix, so build it via make -f Makefile.unix. Everything should be setup properly, after building, execute 'make -f Makefile.unix install' to install. git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@165 51a99a44-fe44-0410-b1ba-c3e57ba2b86b --- Makefile.unix.am | 7 + bootstrap | 8 + configure.in | 50 +++++ src/Makefile.unix.am | 11 ++ src/coding/Makefile.unix.am | 9 + src/layout/Makefile.unix.am | 9 + src/meta/Makefile.unix.am | 9 + src/vgmstream.c | 4 +- unix/Makefile.unix.am | 10 + unix/config.h | 59 ++++++ unix/data.c | 69 +++++++ unix/gui.c | 31 +++ unix/gui.h | 6 + unix/main.c | 372 ++++++++++++++++++++++++++++++++++++ unix/version.h | 6 + 15 files changed, 658 insertions(+), 2 deletions(-) create mode 100644 Makefile.unix.am create mode 100755 bootstrap create mode 100644 configure.in create mode 100644 src/Makefile.unix.am create mode 100644 src/coding/Makefile.unix.am create mode 100644 src/layout/Makefile.unix.am create mode 100644 src/meta/Makefile.unix.am create mode 100644 unix/Makefile.unix.am create mode 100644 unix/config.h create mode 100644 unix/data.c create mode 100644 unix/gui.c create mode 100644 unix/gui.h create mode 100644 unix/main.c create mode 100644 unix/version.h diff --git a/Makefile.unix.am b/Makefile.unix.am new file mode 100644 index 00000000..81a0f831 --- /dev/null +++ b/Makefile.unix.am @@ -0,0 +1,7 @@ +## Process this file with automake to produce Makefile.in + +AM_MAKEFLAGS=-f Makefile.unix + +SUBDIRS = src unix + +#EXTRA_DIST = include diff --git a/bootstrap b/bootstrap new file mode 100755 index 00000000..a53f5989 --- /dev/null +++ b/bootstrap @@ -0,0 +1,8 @@ +#!/bin/sh + +aclocal +autoheader +automake -a +touch README AUTHORS NEWS ChangeLog +autoconf +libtoolize --copy --force diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..23e8f61a --- /dev/null +++ b/configure.in @@ -0,0 +1,50 @@ +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.53) +AC_INIT(audacious-vgmstream,1.0.0) +AM_INIT_AUTOMAKE(audacious-vgmstream, 1.0.0) +AM_CONFIG_HEADER(unix/config.h) +AM_DISABLE_STATIC + +AC_PROG_CC +AC_PROG_CXX +AM_PROG_CC_STDC +AC_HEADER_STDC +AC_PROG_INSTALL +AM_PROG_LIBTOOL + +AC_PATH_X +AC_PATH_XTRA + +PKG_CHECK_MODULES(AUDACIOUS, [audacious >= 1.4.0],, + [AC_MSG_ERROR([Cannot find Audacious, have you installed audacious yet?])] +) + +dnl Check for GTK/GLib/GThread/Pango + +PKG_CHECK_MODULES(GTK, [glib-2.0 >= 2.6.0 gtk+-2.0 >= 2.6.0 gthread-2.0 pango], + , [AC_MSG_ERROR([Cannot find glib2/gtk2/pango])] +) + +CFLAGS="$CFLAGS $AUDACIOUS_CFLAGS" +LIBS="$LIBS $AUDACIOUS_LIBS $GTK_LIBS" + +plugindir=`pkg-config audacious --variable=plugin_dir` +AC_SUBST(plugindir) + +INPUT_PLUGIN_DIR="Input" +AC_SUBST(INPUT_PLUGIN_DIR) + +#AC_CHECK_HEADERS(regex.h,,AC_MSG_ERROR(regex.h missing)) +#AC_CHECK_FUNCS(regcomp regexec regfree) + +AC_PATH_X +AC_PATH_XTRA + +AC_OUTPUT([ + Makefile.unix + src/Makefile.unix + src/coding/Makefile.unix + src/layout/Makefile.unix + src/meta/Makefile.unix + unix/Makefile.unix +]) diff --git a/src/Makefile.unix.am b/src/Makefile.unix.am new file mode 100644 index 00000000..e117ed26 --- /dev/null +++ b/src/Makefile.unix.am @@ -0,0 +1,11 @@ +noinst_LTLIBRARIES = libvgmstream.la + +AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) +AM_MAKEFLAGS=-f Makefile.unix + +libvgmstream_la_LDFLAGS = coding/libcoding.la layout/liblayout.la meta/libmeta.la +libvgmstream_la_SOURCES = vgmstream.c util.c streamfile.c + +SUBDIRS = coding layout meta + +EXTRA_DIST = pstdint.h streamfile.h streamtypes.h util.h vgmstream.h diff --git a/src/coding/Makefile.unix.am b/src/coding/Makefile.unix.am new file mode 100644 index 00000000..292a6293 --- /dev/null +++ b/src/coding/Makefile.unix.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libcoding.la + +AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) +AM_MAKEFLAGS=-f Makefile.unix + +libcoding_la_LDFLAGS = +libcoding_la_SOURCES = adx_decoder.c g721_decoder.c ima_decoder.c ngc_afc_decoder.c ngc_dsp_decoder.c ngc_dtk_decoder.c pcm_decoder.c psx_decoder.c xa_decoder.c + +EXTRA_DIST = coding.h g72x_state.h diff --git a/src/layout/Makefile.unix.am b/src/layout/Makefile.unix.am new file mode 100644 index 00000000..70cc8f77 --- /dev/null +++ b/src/layout/Makefile.unix.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = liblayout.la + +AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) +AM_MAKEFLAGS=-f Makefile.unix + +liblayout_la_LDFLAGS = +liblayout_la_SOURCES = ast_blocked.c blocked.c halpst_blocked.c interleave.c nolayout.c xa_blocked.c + +EXTRA_DIST = layout.h diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am new file mode 100644 index 00000000..22e25473 --- /dev/null +++ b/src/meta/Makefile.unix.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libmeta.la + +AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) +AM_MAKEFLAGS=-f Makefile.unix + +libmeta_la_LDFLAGS = +libmeta_la_SOURCES = Cstr.c adx_header.c afc_header.c agsc.c ast.c brstm.c gcsw.c halpst.c nds_strm.c ngc_adpdtk.c ngc_dsp_std.c ps2_ads.c ps2_exst.c ps2_int.c ps2_mib.c ps2_mic.c ps2_npsf.c ps2_rxw.c ps2_svag.c ps2_vag.c psx_cdxa.c raw.c rs03.c rsf.c rwsd.c + +EXTRA_DIST = meta.h diff --git a/src/vgmstream.c b/src/vgmstream.c index 378c9c1c..2c85e39e 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -526,12 +526,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { if (vgmstream->layout_type == layout_interleave || vgmstream->layout_type == layout_interleave_shortblock) { snprintf(temp,TEMPSIZE,"interleave: %#x bytes\n", - vgmstream->interleave_block_size); + (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); if (vgmstream->layout_type == layout_interleave_shortblock) { snprintf(temp,TEMPSIZE,"last block interleave: %#x bytes\n", - vgmstream->interleave_smallblock_size); + (int32_t)vgmstream->interleave_smallblock_size); concatn(length,desc,temp); } } diff --git a/unix/Makefile.unix.am b/unix/Makefile.unix.am new file mode 100644 index 00000000..f0ec3b7d --- /dev/null +++ b/unix/Makefile.unix.am @@ -0,0 +1,10 @@ +lib_LTLIBRARIES = libvgmstream.la + +libdir = @plugindir@/@INPUT_PLUGIN_DIR@ + +AM_MAKEFLAGS=-f Makefile.unix +AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/in_cube @GTK_CFLAGS@ +AM_LIBS = + +libvgmstream_la_LDFLAGS = -no-undefined -module -avoid-version -export-symbols-regex get_plugin_info ../src/libvgmstream.la +libvgmstream_la_SOURCES = main.c data.c gui.c diff --git a/unix/config.h b/unix/config.h new file mode 100644 index 00000000..c50bbe1d --- /dev/null +++ b/unix/config.h @@ -0,0 +1,59 @@ +/* unix/config.h. Generated from config.h.in by configure. */ +/* unix/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "audacious-vgmstream" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "audacious-vgmstream" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "audacious-vgmstream 1.0.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "audacious-vgmstream" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.0.0" + +/* Define to 1 if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ diff --git a/unix/data.c b/unix/data.c new file mode 100644 index 00000000..64856260 --- /dev/null +++ b/unix/data.c @@ -0,0 +1,69 @@ +#include + +void vgmstream_init(); +void vgmstream_about(); +void vgmstream_configure(); +void vgmstream_destroy(); +gboolean vgmstream_is_our_file(gchar *pFile); +void vgmstream_play(InputPlayback *context); +void vgmstream_stop(InputPlayback *context); +void vgmstream_pause(InputPlayback *context,gshort paused); +void vgmstream_seek(InputPlayback *context,gint time); +int vgmstream_get_time(InputPlayback *context); +void vgmstream_get_song_info(gchar *pFile,gchar **title,gint *length); +void vgmstream_mseek(InputPlayback *context,gulong ms); +void vgmstream_file_info_box(gchar *pFile); + +gchar *vgmstream_exts [] = { + "adx", + "afc", + "agsc", + "ast", + "brstm", + "hps", + "strm", + "adp", + "rsf", + "dsp", + "gcw", + "ads", + "ss2", + "npsf", + "rwsd", + "xa", + "rxw", + "int", + "sts", + "svag", + "mib", + "mi4", + "mpdsp", + "mic", + "gcm", + "mss", + "raw", + "vag", + /* terminator */ + NULL +}; + + +InputPlugin vgmstream_iplug = { + .description = "VGMStream Decoder", + .init = vgmstream_init, + .about = vgmstream_about, + .configure = vgmstream_configure, + .cleanup = vgmstream_destroy, + .is_our_file = vgmstream_is_our_file, + .play_file = vgmstream_play, + .stop = vgmstream_stop, + .pause = vgmstream_pause, + .seek = vgmstream_seek, + .get_time = vgmstream_get_time, + .get_song_info = vgmstream_get_song_info, + .vfs_extensions = vgmstream_exts, + .mseek = vgmstream_mseek, + .file_info_box = vgmstream_file_info_box, +}; + + diff --git a/unix/gui.c b/unix/gui.c new file mode 100644 index 00000000..2a14808c --- /dev/null +++ b/unix/gui.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include "gui.h" +#include "version.h" +#include +#include + +static GtkWidget *about_box; + +void vgmstream_gui_about() +{ + if (about_box) + { + gdk_window_raise(about_box->window); + return; + } + + about_box = audacious_info_dialog( + (gchar *) "About VGMStream Decoder", + (gchar *) "[ VGMStream Decoder ]\n\n" + "audacious-vgmstream version: " AUDACIOUSVGMSTREAM_VERSION "\n\n" + "audacious-vgmstream written by Todd Jeffreys (http://voidpointer.org/)\n" + "vgmstream written by hcs (http://www.sf.net/projects/vgmstream)", + (gchar *) "OK", + FALSE, NULL, NULL); + gtk_signal_connect(GTK_OBJECT(about_box), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_box); + +} diff --git a/unix/gui.h b/unix/gui.h new file mode 100644 index 00000000..93506e2e --- /dev/null +++ b/unix/gui.h @@ -0,0 +1,6 @@ +#ifndef __GUI__ +#define __GUI__ + +void vgmstream_gui_about(); + +#endif diff --git a/unix/main.c b/unix/main.c new file mode 100644 index 00000000..5d91aa59 --- /dev/null +++ b/unix/main.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "version.h" +#include "../src/vgmstream.h" +#include "gui.h" + +#define TM_QUIT 0 +#define TM_PLAY 1 +#define TM_SEEK 2 + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +extern InputPlugin vgmstream_iplug; +//static CDecoder decoder; +static volatile long decode_seek; +static GThread *decode_thread; +static gint stream_length_samples; +static gint loop_count = 1; +static gint fade_seconds = 0; +static gint fade_delay_seconds = 0; +static gint decode_pos_samples = 0; +static VGMSTREAM *vgmstream = NULL; +static gchar strPlaying[260]; +static InputPlugin *vgmstream_iplist[] = { &vgmstream_iplug, NULL }; + +/* +static gint get_ms_position() +{ + if (vgmstream) + { + return (decode_pos_samples * 1000) / vgmstream->sample_rate; + } + return 0; +} +*/ + +/* TODO - + +Rewrite vgmstream/STREAMFILE so that you can open a VGMSTREAM* by providing +a custom implementation of STREAMFILE. Audacious wants you to use the +VFS functions to do I/O, but VGMSTREAM only supports fopen/fread/etc. +*/ + +/* Here's a function to convert file:// to the actual path */ +static void get_file_name(char *buffer,const char *pfile) +{ + /* unconvert from file:// to regular path */ + gchar *unescaped = g_uri_unescape_string(pfile,NULL); + if (strncmp(unescaped,"file://",7) == 0) + { + strcpy(buffer,unescaped+7); + } + else + { + strcpy(buffer,unescaped); + } + g_free(unescaped); +} + +void vgmstream_mseek(InputPlayback *data,gulong ms); + +#define CLOSE_STREAM() do { \ + if (vgmstream) close_vgmstream(vgmstream); \ + vgmstream = NULL; } while (0) + +SIMPLE_INPUT_PLUGIN(vgmstream,vgmstream_iplist); + +#define DS_EXIT -2 + +void* vgmstream_play_loop(InputPlayback *playback) +{ + int16_t buffer[576*2]; + long l; + gint seek_needed_samples; + gint samples_to_do; + decode_seek = -1; + playback->playing = 1; + playback->eof = 0; + + decode_pos_samples = 0; + + while (playback->playing) + { + // ****************************************** + // Seeking + // ****************************************** + // check thread flags, not my favorite method + if (decode_seek == DS_EXIT) + { + goto exit_thread; + } + else if (decode_seek >= 0) + { + /* compute from ms to samples */ + seek_needed_samples = (long long)decode_seek * vgmstream->sample_rate / 1000L; + if (seek_needed_samples < decode_pos_samples) + { + /* go back in time, reopen file */ + CLOSE_STREAM(); + decode_pos_samples = 0; + vgmstream = init_vgmstream(strPlaying); + if (vgmstream) + { + samples_to_do = seek_needed_samples; + } + else + { + samples_to_do = -1; + // trigger eof + playback->eof = 1; + } + } + else if (decode_pos_samples < seek_needed_samples) + { + /* go forward in time */ + samples_to_do = seek_needed_samples - decode_pos_samples; + } + else + { + /* seek to where we are, how convenient */ + samples_to_do = -1; + } + /* do the actual seeking */ + if (samples_to_do >= 0) + { + while (samples_to_do > 0) + { + l = min(576,samples_to_do); + render_vgmstream(buffer,l,vgmstream); + samples_to_do -= l; + decode_pos_samples += l; + } + playback->output->flush(decode_seek); + // reset eof flag + playback->eof = 0; + } + // reset decode_seek + decode_seek = -1; + } + + // ****************************************** + // Playback + // ****************************************** + if (!playback->eof) + { + // read data and pass onward + samples_to_do = min(576,stream_length_samples - (decode_pos_samples + 576)); + l = (samples_to_do * vgmstream->channels*2); + if (!l) + { + playback->eof = 1; + // will trigger on next run through + } + else + { + // ok we read stuff , pass it on + render_vgmstream(buffer,samples_to_do,vgmstream); + /* TODO fading */ + playback->pass_audio(playback,FMT_S16_LE,vgmstream->channels , l , buffer , &playback->playing ); + + decode_pos_samples += samples_to_do; + } + } + else + { + // at EOF + playback->output->buffer_free(); + playback->output->buffer_free(); + while (playback->output->buffer_playing()) + g_usleep(10000); + playback->playing = 0; + // this effectively ends the loop + } + } + exit_thread: + decode_seek = -1; + playback->playing = 0; + decode_pos_samples = 0; + CLOSE_STREAM(); + return 0; +} + +void vgmstream_about() +{ + vgmstream_gui_about(); +} + +void vgmstream_configure() +{ + /* TODO */ +} + +void vgmstream_init() +{ + +} + +void vgmstream_destroy() +{ + +} + +gboolean vgmstream_is_our_file(char *pFile) +{ + char strFile[260]; + const char *pExt; + gchar **exts; + VGMSTREAM *stream; + + if (!pFile) + return FALSE; + + /* get extension */ + pExt = strrchr(pFile,'.'); + if (!pExt) + return FALSE; + /* skip past period */ + ++pExt; + + get_file_name(strFile,pFile); + + for (exts = vgmstream_iplug.vfs_extensions;*exts;++exts) + { + if (strcasecmp(pExt,*exts) == 0) + { + if ((stream = init_vgmstream(strFile))) + { + close_vgmstream(stream); + return TRUE; + } + } + } + return FALSE; +} + +void vgmstream_mseek(InputPlayback *data,gulong ms) +{ + if (vgmstream) + { + decode_seek = ms; + data->eof = 0; + + while (decode_seek != -1) + g_usleep(10000); + } +} + +void vgmstream_play(InputPlayback *context) +{ + // this is now called in a new thread context + char title[260]; + + get_file_name(title,context->filename); + + vgmstream = init_vgmstream(title); + if (!vgmstream || vgmstream->channels <= 0 || vgmstream->channels > 2) + { + CLOSE_STREAM(); + return; + } + // open the audio device + if (context->output->open_audio(FMT_S16_LE,vgmstream->sample_rate,vgmstream->channels) == 0) + { + CLOSE_STREAM(); + return; + } + /* copy file name */ + strcpy(strPlaying,title); + // set the info + stream_length_samples = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,vgmstream); + gint ms = (stream_length_samples * 1000LL) / vgmstream->sample_rate; + gint rate = vgmstream->sample_rate * 2 * vgmstream->channels; + context->set_params(context,title, + /* length */ ms, + /* rate */rate, + /* freq */vgmstream->sample_rate, + /* n channels */vgmstream->channels); + + decode_thread = g_thread_self(); + context->set_pb_ready(context); + vgmstream_play_loop(context); +} + +void vgmstream_stop(InputPlayback *context) +{ + if (vgmstream) + { + // kill thread + decode_seek = DS_EXIT; + // wait for it to die + g_thread_join(decode_thread); + // close audio output + } + context->output->close_audio(); + // cleanup + CLOSE_STREAM(); +} + +void vgmstream_pause(InputPlayback *context,gshort paused) +{ + context->output->pause(paused); +} + +void vgmstream_seek(InputPlayback *context,gint time) +{ + vgmstream_mseek(context,time * 1000); +} + +int vgmstream_get_time(InputPlayback *context) +{ + if (!vgmstream) + return -2; + + if (!context->playing || + (context->eof && !context->output->buffer_playing())) + return -1; + + return context->output->output_time(); + //return get_ms_position(); +} + +void vgmstream_get_song_info(gchar *pFile,gchar **title,gint *length) +{ + char strTitle[260]; + VGMSTREAM *infostream; + + get_file_name(strTitle,pFile); + + *title = g_strdup(strTitle); + + if ((infostream = init_vgmstream(strTitle))) + { + *length = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,infostream) * 1000LL / infostream->sample_rate; + close_vgmstream(infostream); + } + else + { + *length = 0; + } +} + +void vgmstream_file_info_box(gchar *pFile) +{ + char msg[512]; + char strTitle[260]; + VGMSTREAM *stream; + + get_file_name(strTitle,pFile); + + if ((stream = init_vgmstream(strTitle))) + { + gint sls = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,stream); + gint ms = (sls * 1000LL) / stream->sample_rate; + gint rate = stream->sample_rate * 2 * stream->channels; + + sprintf(msg,"%s\nSample rate: %d\nStereo: %s\nTotal samples: %d\nBits per second: %d\nLength: %f seconds",strTitle,stream->sample_rate,(stream->channels >= 2) ? "yes" : "no",sls,rate,(double)ms / 1000.0); + + close_vgmstream(stream); + + audacious_info_dialog("File information",msg,"OK",FALSE,NULL,NULL); + } +} diff --git a/unix/version.h b/unix/version.h new file mode 100644 index 00000000..3db828de --- /dev/null +++ b/unix/version.h @@ -0,0 +1,6 @@ +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define AUDACIOUSVGMSTREAM_VERSION "1.0.0" + +#endif