diff --git a/configure.in b/configure.in index 23e8f61a..5afcb684 100644 --- a/configure.in +++ b/configure.in @@ -19,6 +19,10 @@ PKG_CHECK_MODULES(AUDACIOUS, [audacious >= 1.4.0],, [AC_MSG_ERROR([Cannot find Audacious, have you installed audacious yet?])] ) +PKG_CHECK_MODULES(VORBISFILE, [vorbisfile],, + [AC_MSG_ERROR([Cannot find libvorbisfile])] +) + 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], @@ -26,7 +30,7 @@ PKG_CHECK_MODULES(GTK, [glib-2.0 >= 2.6.0 gtk+-2.0 >= 2.6.0 gthread-2.0 pango], ) CFLAGS="$CFLAGS $AUDACIOUS_CFLAGS" -LIBS="$LIBS $AUDACIOUS_LIBS $GTK_LIBS" +LIBS="$LIBS $AUDACIOUS_LIBS $GTK_LIBS $VORBISFILE_LIBS" plugindir=`pkg-config audacious --variable=plugin_dir` AC_SUBST(plugindir) diff --git a/src/Makefile b/src/Makefile index d58c4dc3..3768454f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,7 +7,8 @@ CODING_OBJS=coding/adx_decoder.o \ coding/pcm_decoder.o \ coding/psx_decoder.o \ coding/xa_decoder.o \ - coding/eaxa_decoder.o + coding/eaxa_decoder.o \ + coding/ogg_vorbis_decoder.o LAYOUT_OBJS=layout/ast_blocked.o \ layout/blocked.o \ @@ -52,7 +53,8 @@ META_OBJS=meta/adx_header.o \ meta/ea_header.o \ meta/ngc_caf.o \ meta/ps2_vpk.o \ - meta/genh.o + meta/genh.o \ + meta/ogg_vorbis_file.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/coding/Makefile.unix.am b/src/coding/Makefile.unix.am index b9484503..8ca2021c 100644 --- a/src/coding/Makefile.unix.am +++ b/src/coding/Makefile.unix.am @@ -4,6 +4,6 @@ AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) AM_MAKEFLAGS=-f Makefile.unix libcoding_la_LDFLAGS = -libcoding_la_SOURCES = adx_decoder.c eaxa_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 +libcoding_la_SOURCES = adx_decoder.c eaxa_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 ogg_vorbis_decoder.c EXTRA_DIST = coding.h g72x_state.h diff --git a/src/coding/coding.h b/src/coding/coding.h index 8e12822f..9d8b8bd9 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -29,4 +29,8 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i void decode_eaxa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +#ifdef VGM_USE_VORBIS +void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +#endif + #endif diff --git a/src/coding/ogg_vorbis_decoder.c b/src/coding/ogg_vorbis_decoder.c new file mode 100644 index 00000000..5f571d1e --- /dev/null +++ b/src/coding/ogg_vorbis_decoder.c @@ -0,0 +1,22 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_VORBIS +#include +#include "coding.h" +#include "../util.h" + +void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { + int samples_done = 0; + OggVorbis_File *ogg_vorbis_file = &data->ogg_vorbis_file; + + do { + long rc = ov_read(ogg_vorbis_file, (char *)(outbuf + samples_done*channels), + (samples_to_do - samples_done)*sizeof(sample)*channels, 0, + sizeof(sample), 1, &data->bitstream); + + if (rc > 0) samples_done += rc/sizeof(sample)/channels; + else return; + } while (samples_done < samples_to_do); +} + +#endif diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 36f922a1..14f9e963 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -4,6 +4,6 @@ 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 ea_header.c gcsw.c halpst.c nds_strm.c ngc_adpdtk.c ngc_caf.c ngc_dsp_std.c ps2_ads.c ps2_exst.c ps2_ild.c ps2_int.c ps2_mib.c ps2_mic.c ps2_npsf.c ps2_pnb.c ps2_rxw.c ps2_str.c ps2_svag.c ps2_vag.c ps2_vpk.c psx_cdxa.c raw.c rs03.c rsf.c rwsd.c psx_gms.c xbox_xwav.c xbox_wavm.c genh.c +libmeta_la_SOURCES = Cstr.c adx_header.c afc_header.c agsc.c ast.c brstm.c ea_header.c gcsw.c halpst.c nds_strm.c ngc_adpdtk.c ngc_caf.c ngc_dsp_std.c ps2_ads.c ps2_exst.c ps2_ild.c ps2_int.c ps2_mib.c ps2_mic.c ps2_npsf.c ps2_pnb.c ps2_rxw.c ps2_str.c ps2_svag.c ps2_vag.c ps2_vpk.c psx_cdxa.c raw.c rs03.c rsf.c rwsd.c psx_gms.c xbox_xwav.c xbox_wavm.c genh.c ogg_vorbis_file.c EXTRA_DIST = meta.h diff --git a/src/meta/meta.h b/src/meta/meta.h index 8ee84b01..5ad73242 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -81,4 +81,6 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile); + #endif diff --git a/src/meta/ogg_vorbis_file.c b/src/meta/ogg_vorbis_file.c new file mode 100644 index 00000000..34e9f6c9 --- /dev/null +++ b/src/meta/ogg_vorbis_file.c @@ -0,0 +1,167 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_VORBIS + +#include "meta.h" +#include "../util.h" +#include + + +#define DEFAULT_BITSTREAM 0 + +static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource) +{ + ogg_vorbis_streamfile * const ov_streamfile = datasource; + size_t items_read; + + size_t bytes_read; + + bytes_read = read_streamfile(ptr, ov_streamfile->offset, size * nmemb, + ov_streamfile->streamfile); + + items_read = bytes_read / size; + + ov_streamfile->offset += items_read * size; + + return items_read; +} + +static int seek_func(void *datasource, ogg_int64_t offset, int whence) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + ogg_int64_t base_offset; + ogg_int64_t new_offset; + + switch (whence) { + case SEEK_SET: + base_offset = 0; + break; + case SEEK_CUR: + base_offset = ov_streamfile->offset; + break; + case SEEK_END: + base_offset = ov_streamfile->size; + break; + default: + return -1; + break; + } + + new_offset = base_offset + offset; + if (new_offset < 0 || new_offset > ov_streamfile->size) { + return -1; + } else { + ov_streamfile->offset = new_offset; + return 0; + } +} + +static long tell_func(void * datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + return ov_streamfile->offset; +} + +/* setting close_func in ov_callbacks to NULL doesn't seem to work */ +int close_func(void * datasource) { + return 0; +} + +/* Ogg Vorbis, by way of libvorbisfile */ + +VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + + ov_callbacks callbacks; + OggVorbis_File temp_ovf; + ogg_vorbis_streamfile temp_streamfile; + + ogg_vorbis_codec_data * data = NULL; + OggVorbis_File *ovf; + int inited_ovf = 0; + vorbis_info *info; + + int loop_flag = 0; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + + /* It is only interesting to use oggs with vgmstream if they are looped. + To prevent such files from being played by other plugins and such they + may be renamed to .logg. This meta reader should still support .ogg, + though. */ + if (strcasecmp("logg",filename_extension(filename)) && + strcasecmp("ogg",filename_extension(filename)) + ) goto fail; + + callbacks.read_func = read_func; + callbacks.seek_func = seek_func; + callbacks.close_func = close_func; + callbacks.tell_func = tell_func; + + temp_streamfile.streamfile = streamFile; + temp_streamfile.offset = 0; + temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); + + /* can we open this as a proper ogg vorbis file? */ + memset(&temp_ovf, 0, sizeof(temp_ovf)); + if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, + 0, callbacks)) goto fail; + /* we have to close this as it has the init_vgmstream meta-reading + STREAMFILE */ + ov_clear(&temp_ovf); + + /* proceed to open a STREAMFILE just for this stream */ + data = calloc(1,sizeof(ogg_vorbis_codec_data)); + if (!data) goto fail; + + data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, + STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!data->ov_streamfile.streamfile) goto fail; + data->ov_streamfile.offset = 0; + data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); + + /* open the ogg vorbis file for real */ + if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, + 0, callbacks)) goto fail; + ovf = &data->ogg_vorbis_file; + inited_ovf = 1; + + /* don't know how to deal with multiple logical streams yet */ + if (ov_streams(ovf) != 1) goto fail; + data->bitstream = DEFAULT_BITSTREAM; + + info = ov_info(ovf,DEFAULT_BITSTREAM); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(info->channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->codec_data = data; + + /* fill in the vital statistics */ + vgmstream->channels = info->channels; + vgmstream->sample_rate = info->rate; + vgmstream->num_samples = ov_pcm_total(ovf,DEFAULT_BITSTREAM); + vgmstream->coding_type = coding_ogg_vorbis; + vgmstream->layout_type = layout_ogg_vorbis; + vgmstream->meta_type = meta_ogg_vorbis; + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (data) { + if (inited_ovf) + ov_clear(&data->ogg_vorbis_file); + if (data->ov_streamfile.streamfile) + close_streamfile(data->ov_streamfile.streamfile); + free(data); + } + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } + return NULL; +} + +#endif diff --git a/src/vgmstream.c b/src/vgmstream.c index f430ebf4..8f4c1cbc 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -55,6 +55,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_caf, init_vgmstream_ps2_vpk, init_vgmstream_genh, + init_vgmstream_ogg_vorbis, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -125,6 +126,16 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { * init_vgmstream_* function doing something tricky and precomputing it. * Otherwise hit_loop will be 0 and it will be copied over anyway when we * really hit the loop start. */ + +#ifdef VGM_USE_VORBIS + if (vgmstream->meta_type==meta_ogg_vorbis) { + ogg_vorbis_codec_data *data = + (ogg_vorbis_codec_data *)(vgmstream->codec_data); + OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); + + ov_pcm_seek(ogg_vorbis_file, 0); + } +#endif } /* simply allocate memory for the VGMSTREAM and its channels */ @@ -209,6 +220,23 @@ void close_vgmstream(VGMSTREAM * vgmstream) { /* the start_vgmstream is considered just data */ if (vgmstream->start_vgmstream) free(vgmstream->start_vgmstream); +#ifdef VGM_USE_VORBIS + if (vgmstream->meta_type==meta_ogg_vorbis) { + ogg_vorbis_codec_data *data = + (ogg_vorbis_codec_data *)(vgmstream->codec_data); + if (vgmstream->codec_data) { + OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); + + + ov_clear(ogg_vorbis_file); + + close_streamfile(data->ov_streamfile.streamfile); + free(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } + } +#endif + free(vgmstream); } @@ -224,6 +252,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_interleave_shortblock: render_vgmstream_interleave(buffer,sample_count,vgmstream); break; + case layout_ogg_vorbis: case layout_dtk_interleave: case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); @@ -247,6 +276,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM16LE: case coding_PCM16BE: case coding_PCM8: + case coding_ogg_vorbis: return 1; case coding_NDS_IMA: return (vgmstream->interleave_block_size-4)*2; @@ -414,6 +444,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; +#ifdef VGM_USE_VORBIS + case coding_ogg_vorbis: + decode_ogg_vorbis(vgmstream->codec_data, + buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream->channels); + break; +#endif } } @@ -479,6 +516,16 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } } #endif + +#ifdef VGM_USE_VORBIS + if (vgmstream->meta_type==meta_ogg_vorbis) { + ogg_vorbis_codec_data *data = + (ogg_vorbis_codec_data *)(vgmstream->codec_data); + OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); + + ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample); + } +#endif /* restore! */ memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); vgmstream->current_sample=vgmstream->loop_sample; diff --git a/src/vgmstream.h b/src/vgmstream.h index 692683d9..35ee5394 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -5,8 +5,13 @@ #ifndef _VGMSTREAM_H #define _VGMSTREAM_H +#define VGM_USE_VORBIS + #include "streamfile.h" #include "coding/g72x_state.h" +#ifdef VGM_USE_VORBIS +#include +#endif /* The encoding type specifies the format the sound data itself takes */ typedef enum { @@ -26,6 +31,9 @@ typedef enum { coding_XA, /* PSX CD-XA */ coding_XBOX, /* XBOX IMA */ coding_EAXA, /* EA/XA ADPCM */ +#ifdef VGM_USE_VORBIS + coding_ogg_vorbis, /* vorbis */ +#endif } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -49,6 +57,9 @@ typedef enum { #endif /* otherwise odd */ layout_dtk_interleave, /* dtk interleaves channels by nibble */ +#ifdef VGM_USE_VORBIS + layout_ogg_vorbis, /* ogg vorbis file */ +#endif } layout_t; /* The meta type specifies how we know what we know about the file. We may know because of a header we read, some of it may have been guessed from filenames, etc. */ @@ -117,6 +128,10 @@ typedef enum { meta_RAW, /* RAW PCM file */ meta_GENH, /* generic header */ + +#ifdef VGM_USE_VORBIS + meta_ogg_vorbis, /* ogg vorbis */ +#endif } meta_t; typedef struct { @@ -198,8 +213,31 @@ typedef struct { uint8_t ea_platform; void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream */ + + /* Data the codec needs for the whole stream. This is for codecs too + * different from vgmstream's structure to be reasonably shoehorned into + * using the ch structures. + * Note also that support must be added for resetting, looping and + * closing for every codec that uses this, as it will not be handled. */ + void * codec_data; } VGMSTREAM; +#ifdef VGM_USE_VORBIS +typedef struct { + STREAMFILE *streamfile; + ogg_int64_t offset; + ogg_int64_t size; +} ogg_vorbis_streamfile; + +typedef struct { + OggVorbis_File ogg_vorbis_file; + int bitstream; + + ogg_vorbis_streamfile ov_streamfile; +} ogg_vorbis_codec_data; +#endif + + /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ VGMSTREAM * init_vgmstream(const char * const filename); diff --git a/test/Makefile b/test/Makefile index b1d4ab98..a1633ef4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,13 +1,12 @@ export SHELL = /bin/sh -export CFLAGS=-Wall -O3 -export LDFLAGS=-lm -L../src -lvgmstream +export CFLAGS=-Wall -ggdb +export LDFLAGS=-lm -L../src -lvgmstream -lvorbisfile export STRIP=strip .PHONY: libvgmstream.a test: libvgmstream.a test.o $(CC) test.o $(LDFLAGS) $(CFLAGS) -o test - $(STRIP) test test.o: test.c $(CC) $(CFLAGS) -c "-DVERSION=\"`../version.sh`\"" test.c -o test.o