diff --git a/readme.txt b/readme.txt index 68e89af9..d2a59ef0 100644 --- a/readme.txt +++ b/readme.txt @@ -101,6 +101,8 @@ File types supported by this version of vgmstream: - .ivb (PS2 ADPCM) - .amts (GC DSP ADPCM) - .svs (PS2 ADPCM) +- .wav (8/16 bit PCM) +- .pos (8/16 bit PCM) Enjoy! -hcs diff --git a/src/Makefile b/src/Makefile index d1ef4933..11f085c5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -67,7 +67,9 @@ META_OBJS=meta/adx_header.o \ meta/ws_aud.o \ meta/ahx.o \ meta/ivb.o \ - meta/svs.o + meta/svs.o \ + meta/riff.o \ + meta/pos.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 682bc7f5..6b0c03d7 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -366,6 +366,14 @@ RelativePath=".\meta\svs.c" > + + + + +#include "meta.h" +#include "../util.h" + +#ifdef WIN32 +#define DIRSEP '\\' +#else +#define DIRSEP '/' +#endif + +/* .pos is a tiny file with loop points, and the same base name as a .wav */ + +VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) { + + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamFileWAV = NULL; + char filename[260]; + char filenameWAV[260]; + + int i; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("pos",filename_extension(filename))) goto fail; + + /* check for .WAV file */ + strcpy(filenameWAV,filename); + strcpy(filenameWAV+strlen(filenameWAV)-3,"wav"); + for (i=strlen(filenameWAV);i>=0&&filenameWAV[i]!=DIRSEP;i--) + filenameWAV[i]=toupper(filenameWAV[i]); + + streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!streamFileWAV) goto fail; + + /* let the real initer do the parsing */ + vgmstream = init_vgmstream_riff(streamFileWAV); + if (!vgmstream) goto fail; + + /* install loops */ + if (!vgmstream->loop_flag) { + vgmstream->loop_flag = 1; + vgmstream->loop_ch = calloc(vgmstream->channels, + sizeof(VGMSTREAMCHANNEL)); + if (!vgmstream->loop_ch) goto fail; + } + + vgmstream->loop_start_sample = read_32bitLE(0,streamFile); + vgmstream->loop_end_sample = read_32bitLE(4,streamFile); + vgmstream->meta_type = meta_RIFF_WAVE_POS; + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (streamFileWAV) close_streamfile(streamFileWAV); + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/riff.c b/src/meta/riff.c new file mode 100644 index 00000000..a719a58f --- /dev/null +++ b/src/meta/riff.c @@ -0,0 +1,154 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../util.h" + +/* Resource Interchange File Format */ +/* only the bare minimum needed to read PCM wavs */ + +VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + + off_t file_size = -1; + int channel_count = 0; + int sample_count = 0; + int sample_rate = 0; + int coding_type = -1; + off_t start_offset = -1; + int interleave = -1; + + int loop_flag = 0; + int32_t loop_start = -1; + int32_t loop_end = -1; + uint32_t riff_size; + uint32_t data_size = 0; + + int FormatChunkFound = 0; + int DataChunkFound = 0; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("wav",filename_extension(filename))) goto fail; + + /* check header */ + if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494646) /* "RIFF" */ + goto fail; + /* check for WAVE form */ + if ((uint32_t)read_32bitBE(8,streamFile)!=0x57415645) /* "WAVE" */ + goto fail; + + riff_size = read_32bitLE(4,streamFile); + file_size = get_streamfile_size(streamFile); + + /* check for tructated RIFF */ + if (file_size < riff_size+8) goto fail; + + /* read through chunks to verify format and find metadata */ + { + off_t current_chunk = 0xc; /* start with first chunk */ + + while (current_chunk < file_size) { + uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + + if (current_chunk+8+chunk_size > file_size) goto fail; + + switch(chunk_type) { + case 0x666d7420: /* "fmt " */ + /* only one per file */ + if (FormatChunkFound) goto fail; + FormatChunkFound = 1; + + sample_rate = read_32bitLE(current_chunk+0x0c,streamFile); + channel_count = read_16bitLE(current_chunk+0x0a,streamFile); + + switch (read_16bitLE(current_chunk+0x8,streamFile)) { + case 1: /* PCM */ + switch (read_16bitLE(current_chunk+0x16,streamFile)) { + case 16: + coding_type = coding_PCM16LE; + interleave = 2; + break; + case 8: + coding_type = coding_PCM8; + interleave = 1; + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case 0x64617461: /* data */ + /* at most one per file */ + if (DataChunkFound) goto fail; + DataChunkFound = 1; + + start_offset = current_chunk + 8; + data_size = chunk_size; + break; + default: + /* ignorance is bliss */ + break; + } + + current_chunk += 8+chunk_size; + } + } + + if (!FormatChunkFound || !DataChunkFound) goto fail; + + switch (coding_type) { + case coding_PCM16LE: + sample_count = data_size/2/channel_count; + break; + case coding_PCM8: + sample_count = data_size/channel_count; + break; + } + + /* build the VGMSTREAM */ + + printf("channel_count = %d, loop_flag = %d\n",channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = sample_count; + vgmstream->sample_rate = sample_rate; + + vgmstream->coding_type = coding_type; + if (channel_count > 1) + vgmstream->layout_type = layout_interleave; + else + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = interleave; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->meta_type = meta_RIFF_WAVE; + + /* open the file, set up each channel */ + { + int i; + + vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, + STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!vgmstream->ch[0].streamfile) goto fail; + + for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; + vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = + start_offset+i*interleave; + } + } + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 10165576..be6d1324 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -69,6 +69,8 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ivb, init_vgmstream_amts, init_vgmstream_svs, + init_vgmstream_riff, + init_vgmstream_pos, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -1106,6 +1108,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_PS2_SVS: snprintf(temp,TEMPSIZE,"Square SVS header"); break; + case meta_RIFF_WAVE: + snprintf(temp,TEMPSIZE,"RIFF WAVE header"); + break; + case meta_RIFF_WAVE_POS: + snprintf(temp,TEMPSIZE,"RIFF WAVE header and .pos for looping"); + break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 325048ff..7e4381d1 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -180,6 +180,8 @@ typedef enum { #ifdef VGM_USE_MPEG meta_AHX, /* CRI AHX header (same structure as ADX) */ #endif + meta_RIFF_WAVE, /* RIFF, for WAVs */ + meta_RIFF_WAVE_POS, /* .wav + .pos for looping */ } meta_t; typedef struct { diff --git a/unix/data.c b/unix/data.c index 0342fab2..a2ce1cca 100644 --- a/unix/data.c +++ b/unix/data.c @@ -67,6 +67,7 @@ gchar *vgmstream_exts [] = { "ivb", "amts", "svs", + "pos", /* terminator */ NULL }; diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index 462cd53c..6284d497 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -125,6 +125,7 @@ char * extension_list[] = { "ivb\0IVB Audio File (*.IVB)\0", "amts\0AMTS Audio File (*.AMTS)\0", "svs\0SVS Audio File (*.SVS)\0", + "pos\0POS Audio File (*.POS)\0", }; void about(HWND hwndParent) {