From 3826cd0f8ed22c7fb7f1a7efb5c13fc1e38a9945 Mon Sep 17 00:00:00 2001 From: Soneek Date: Mon, 11 May 2015 15:02:21 -0700 Subject: [PATCH 1/2] Add LWAV output option to test, fix so that we need at3plusdecoder.dll, not .def in test and winamp builds --- ext_libs/Makefile.mingw | 6 +++--- src/util.c | 23 +++++++++++++++++++++++ src/util.h | 1 + test/Makefile | 2 +- test/Makefile.mingw | 9 +++++---- test/test.c | 18 ++++++++++++++++-- winamp/Makefile | 2 +- 7 files changed, 50 insertions(+), 11 deletions(-) diff --git a/ext_libs/Makefile.mingw b/ext_libs/Makefile.mingw index 21b8ae3f..0e3d609c 100644 --- a/ext_libs/Makefile.mingw +++ b/ext_libs/Makefile.mingw @@ -14,8 +14,8 @@ libg7221_decode.a: libg7221_decode.def libg719_decode.a: libg719_decode.def $(DLLTOOL) -d libg719_decode.def -l libg719_decode.a -at3plusdecoder.a: at3plusdecoder.def - $(DLLTOOL) -d at3plusdecoder.def -l at3plusdecoder.a +libat3plusdecoder.a: at3plusdecoder.def + $(DLLTOOL) -d at3plusdecoder.def -l libat3plusdecoder.a clean: - rm -f libvorbis.a libmpg123-0.a libg7221_decode.a libat3plusdecoder.a + rm -f libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a diff --git a/src/util.c b/src/util.c index 68371461..e3dc5021 100644 --- a/src/util.c +++ b/src/util.c @@ -127,6 +127,29 @@ void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, i put_32bitLE(buf+0x28, (int32_t)bytecount); } +void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end) { + int i; + /* RIFF header */ + memcpy(buf+0, "smpl", 4); + /* size of RIFF */ + put_32bitLE(buf+4, 0x3c); + + for (i = 0; i < 7; i++) + put_32bitLE(buf+8 + i * 4, 0); + + put_32bitLE(buf+36, 1); + + for (i = 0; i < 3; i++) + put_32bitLE(buf+40 + i * 4, 0); + + put_32bitLE(buf+52, loop_start); + put_32bitLE(buf+56, loop_end); + put_32bitLE(buf+60, 0); + put_32bitLE(buf+64, 0); + + +} + void swap_samples_le(sample *buf, int count) { int i; for (i=0;ichannels,toget,outfile); } } - + + if (!play && lwav && s->loop_flag) { // Writing smpl chuck + make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample); + fwrite(buf,1,0x44,outfile); + fseek(outfile, 4, SEEK_SET); + + size_t bytecount = len*s->channels*sizeof(sample); + put_32bitLE((uint8_t*)buf, (int32_t)(bytecount+0x2c+52)); + fwrite(buf,1,0x4,outfile); + } fclose(outfile); outfile = NULL; #ifdef PROFILE_STREAMFILE diff --git a/winamp/Makefile b/winamp/Makefile index cca80428..e4a51d3b 100644 --- a/winamp/Makefile +++ b/winamp/Makefile @@ -13,7 +13,7 @@ export WINDRES=i586-mingw32msvc-windres .PHONY: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a at3plusdecoder.a -in_vgmstream.dll: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a in_vgmstream.c resource.o +in_vgmstream.dll: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a in_vgmstream.c resource.o $(CC) -shared -static-libgcc $(CFLAGS) "-DVERSION=\"`../version.sh`\"" in_vgmstream.c resource.o $(LDFLAGS) -o in_vgmstream.dll $(STRIP) in_vgmstream.dll From 51dfc3339ad4c7704e068e77e56b7b177c1d4368 Mon Sep 17 00:00:00 2001 From: Joseph Gibbs Date: Sat, 16 May 2015 15:30:15 -0700 Subject: [PATCH 2/2] Added support for Xenoblade Chronicles 3D adx --- src/meta/adx_header.c | 301 ++++++++++++++++++++++++------------------ src/vgmstream.c | 3 + src/vgmstream.h | 1 + test/Makefile | 10 +- test/test.c | 29 ++-- winamp/Makefile | 2 +- 6 files changed, 194 insertions(+), 152 deletions(-) diff --git a/src/meta/adx_header.c b/src/meta/adx_header.c index c7d97504..9da93014 100644 --- a/src/meta/adx_header.c +++ b/src/meta/adx_header.c @@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { off_t stream_offset; uint16_t version_signature; int loop_flag=0; - int channel_count; + int channel_count, i, j, channel_header_spacing; int32_t loop_start_sample=0; int32_t loop_end_sample=0; meta_t header_type; @@ -25,160 +25,197 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { char filename[PATH_LIMIT]; int coding_type = coding_CRI_ADX; uint16_t xor_start=0,xor_mult=0,xor_add=0; - + /* Xenoblade Chronicles 3D uses an adx extension as with + the Wii version, but it's actually DSP ADPCM. Adding + this flag to account for it. */ + int xb3d_flag = 0; + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("adx",filename_extension(filename))) goto fail; /* check first 2 bytes */ - if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; + if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) { + if (read_8bit(0,streamFile)!=2)goto fail; + else {xb3d_flag = 1; coding_type = coding_NGC_DSP;} + } + + if (xb3d_flag) { + channel_count = read_32bitLE(0, streamFile); + loop_flag = read_16bitLE(0x6e, streamFile); + channel_header_spacing = 0x34; + } + else { + /* get stream offset, check for CRI signature just before */ + stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; + if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ + (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ + ) goto fail; - /* get stream offset, check for CRI signature just before */ - stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; - if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ - (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ - ) goto fail; + /* check for encoding type */ + /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is + * ADX with exponential scale, 0x11 is AHX */ + if (read_8bit(4,streamFile) != 3) goto fail; - /* check for encoding type */ - /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is - * ADX with exponential scale, 0x11 is AHX */ - if (read_8bit(4,streamFile) != 3) goto fail; + /* check for frame size (only 18 is supported at the moment) */ + if (read_8bit(5,streamFile) != 18) goto fail; - /* check for frame size (only 18 is supported at the moment) */ - if (read_8bit(5,streamFile) != 18) goto fail; + /* check for bits per sample? (only 4 makes sense for ADX) */ + if (read_8bit(6,streamFile) != 4) goto fail; - /* check for bits per sample? (only 4 makes sense for ADX) */ - if (read_8bit(6,streamFile) != 4) goto fail; + /* check version signature, read loop info */ + version_signature = read_16bitBE(0x12,streamFile); + /* encryption */ + if (version_signature == 0x0408) { + if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) + { + coding_type = coding_CRI_ADX_enc_8; + version_signature = 0x0400; + } + } + else if (version_signature == 0x0409) { + if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) + { + coding_type = coding_CRI_ADX_enc_9; + version_signature = 0x0400; + } + } - /* check version signature, read loop info */ - version_signature = read_16bitBE(0x12,streamFile); - /* encryption */ - if (version_signature == 0x0408) { - if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) - { - coding_type = coding_CRI_ADX_enc_8; - version_signature = 0x0400; - } - } - else if (version_signature == 0x0409) { - if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) - { - coding_type = coding_CRI_ADX_enc_9; - version_signature = 0x0400; - } - } + if (version_signature == 0x0300) { /* type 03 */ + header_type = meta_ADX_03; + if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */ + loop_flag = (read_32bitBE(0x18,streamFile) != 0); + loop_start_sample = read_32bitBE(0x1c,streamFile); + //loop_start_offset = read_32bitBE(0x20,streamFile); + loop_end_sample = read_32bitBE(0x24,streamFile); + //loop_end_offset = read_32bitBE(0x28,streamFile); + } + } else if (version_signature == 0x0400) { - if (version_signature == 0x0300) { /* type 03 */ - header_type = meta_ADX_03; - if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */ - loop_flag = (read_32bitBE(0x18,streamFile) != 0); - loop_start_sample = read_32bitBE(0x1c,streamFile); - //loop_start_offset = read_32bitBE(0x20,streamFile); - loop_end_sample = read_32bitBE(0x24,streamFile); - //loop_end_offset = read_32bitBE(0x28,streamFile); - } - } else if (version_signature == 0x0400) { + off_t ainf_info_length=0; - off_t ainf_info_length=0; + if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */ + ainf_info_length = (off_t)read_32bitBE(0x28,streamFile); - if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */ - ainf_info_length = (off_t)read_32bitBE(0x28,streamFile); + header_type = meta_ADX_04; + if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */ + if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE) + loop_flag = 0; + else + loop_flag = (read_32bitBE(0x24,streamFile) != 0); - header_type = meta_ADX_04; - if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */ - if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE) + loop_start_sample = read_32bitBE(0x28,streamFile); + //loop_start_offset = read_32bitBE(0x2c,streamFile); + loop_end_sample = read_32bitBE(0x30,streamFile); + //loop_end_offset = read_32bitBE(0x34,streamFile); + } + } else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */ + header_type = meta_ADX_05; + } else goto fail; /* not a known/supported version signature */ + + /* At this point we almost certainly have an ADX file, + * so let's build the VGMSTREAM. */ + + /* high-pass cutoff frequency, always 500 that I've seen */ + cutoff = (uint16_t)read_16bitBE(0x10,streamFile); + + if (loop_start_sample == 0 && loop_end_sample == 0) { loop_flag = 0; - else - loop_flag = (read_32bitBE(0x24,streamFile) != 0); + } - loop_start_sample = read_32bitBE(0x28,streamFile); - //loop_start_offset = read_32bitBE(0x2c,streamFile); - loop_end_sample = read_32bitBE(0x30,streamFile); - //loop_end_offset = read_32bitBE(0x34,streamFile); - } - } else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */ - header_type = meta_ADX_05; - } else goto fail; /* not a known/supported version signature */ - - /* At this point we almost certainly have an ADX file, - * so let's build the VGMSTREAM. */ - - /* high-pass cutoff frequency, always 500 that I've seen */ - cutoff = (uint16_t)read_16bitBE(0x10,streamFile); - - if (loop_start_sample == 0 && loop_end_sample == 0) { - loop_flag = 0; - } - - channel_count = read_8bit(7,streamFile); + channel_count = read_8bit(7,streamFile); + } vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(0xc,streamFile); - vgmstream->sample_rate = read_32bitBE(8,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - - vgmstream->coding_type = coding_type; - if (channel_count==1) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = header_type; - - vgmstream->interleave_block_size=18; - - /* calculate filter coefficients */ - { - double x,y,z,a,b,c; - - x = cutoff; - y = vgmstream->sample_rate; - z = cos(2.0*M_PI*x/y); - - a = M_SQRT2-z; - b = M_SQRT2-1.0; - c = (a-sqrt((a+b)*(a-b)))/b; - - coef1 = floor(c*8192); - coef2 = floor(c*c*-4096); - } - - { - int i; - STREAMFILE * chstreamfile; - - /* ADX is so tightly interleaved that having two buffers is silly */ - chstreamfile = streamFile->open(streamFile,filename,18*0x400); - if (!chstreamfile) goto fail; - - for (i=0;ich[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - stream_offset+18*i; - - vgmstream->ch[i].adpcm_coef[0] = coef1; - vgmstream->ch[i].adpcm_coef[1] = coef2; - - if (coding_type == coding_CRI_ADX_enc_8 || - coding_type == coding_CRI_ADX_enc_9) - { - int j; - vgmstream->ch[i].adx_channels = channel_count; - vgmstream->ch[i].adx_xor = xor_start; - vgmstream->ch[i].adx_mult = xor_mult; - vgmstream->ch[i].adx_add = xor_add; - - for (j=0;jch[i]); + + if (xb3d_flag) { + for (j=0;jchannels;j++) { + for (i=0;i<16;i++) { + vgmstream->ch[j].adpcm_coef[i]=read_16bitLE(4+j*channel_header_spacing+i*2,streamFile); } } - } + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_type; + vgmstream->meta_type = meta_XB3D_ADX; + vgmstream->sample_rate = read_32bitLE(0x70,streamFile); + vgmstream->num_samples = read_32bitLE(0x74, streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile); + + for (i = 0; ich[i].streamfile = streamFile->open(streamFile, filename, 0x1000); + vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset + = read_32bitLE(0x34+i*channel_header_spacing, streamFile); + if (!vgmstream->ch[i].streamfile) goto fail; + } + + } + else { + vgmstream->num_samples = read_32bitBE(0xc,streamFile); + vgmstream->sample_rate = read_32bitBE(8,streamFile); + /* channels and loop flag are set by allocate_vgmstream */ + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + vgmstream->coding_type = coding_type; + if (channel_count==1) + vgmstream->layout_type = layout_none; + else + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = header_type; + + vgmstream->interleave_block_size=18; + /* calculate filter coefficients */ + { + double x,y,z,a,b,c; + + x = cutoff; + y = vgmstream->sample_rate; + z = cos(2.0*M_PI*x/y); + + a = M_SQRT2-z; + b = M_SQRT2-1.0; + c = (a-sqrt((a+b)*(a-b)))/b; + + coef1 = floor(c*8192); + coef2 = floor(c*c*-4096); + } + + { + int i; + STREAMFILE * chstreamfile; + + /* ADX is so tightly interleaved that having two buffers is silly */ + chstreamfile = streamFile->open(streamFile,filename,18*0x400); + if (!chstreamfile) goto fail; + + for (i=0;ich[i].streamfile = chstreamfile; + + vgmstream->ch[i].channel_start_offset= + vgmstream->ch[i].offset= + stream_offset+18*i; + + vgmstream->ch[i].adpcm_coef[0] = coef1; + vgmstream->ch[i].adpcm_coef[1] = coef2; + + if (coding_type == coding_CRI_ADX_enc_8 || + coding_type == coding_CRI_ADX_enc_9) + { + int j; + vgmstream->ch[i].adx_channels = channel_count; + vgmstream->ch[i].adx_xor = xor_start; + vgmstream->ch[i].adx_mult = xor_mult; + vgmstream->ch[i].adx_add = xor_add; + + for (j=0;jch[i]); + } + } + } + } return vgmstream; /* clean up anything we may have opened */ diff --git a/src/vgmstream.c b/src/vgmstream.c index 260cab10..858c0b5f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -3148,6 +3148,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_MCA: snprintf(temp,TEMPSIZE,"Capcom MCA Header"); break; + case meta_XB3D_ADX: + snprintf(temp, TEMPSIZE,"Xenoblade 3D ADX Header"); + break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 97bbce05..d91e06eb 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -580,6 +580,7 @@ typedef enum { meta_3DS_IDSP, // Nintendo 3DS IDSP meta_G1L, // Tecmo Koei G1L meta_MCA, // Capcom MCA "MADP" + meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX #ifdef VGM_USE_MP4V2 meta_MP4, #endif diff --git a/test/Makefile b/test/Makefile index e5268de3..bec4e98d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,8 @@ export SHELL = /bin/sh -export CFLAGS=-Wall -O3 -I../ext_includes -export LDFLAGS=-L../src -L ../ext_libs -lvgmstream -lvorbisfile -lmpg123-0 -lm +export CFLAGS=-Wall -O3 -I../ext_includes -DUSE_ALLOCA -DVGM_USE_MAIATRAC3PLUS +# -DVGM_USE_G7221 -DVGM_USE_G719 +export LDFLAGS=-L../src -L ../ext_libs -lvgmstream -lvorbisfile -lmpg123 -lat3plusdecoder -lpthread -lm +#-lg719_decode -lg7221_decode -lsiren_decode export CC=gcc export AR=ar export STRIP=strip @@ -8,8 +10,8 @@ export STRIP=strip .PHONY: libvgmstream.a test.exe: libvgmstream.a - $(CC) $(CFLAGS) $(LDFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o test - $(STRIP) test + $(CC) $(CFLAGS) $(LDFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o vgmstream + $(STRIP) vgmstream libvgmstream.a: $(MAKE) -C ../src $@ diff --git a/test/test.c b/test/test.c index f94da3fc..3487c537 100644 --- a/test/test.c +++ b/test/test.c @@ -29,13 +29,12 @@ void usage(const char * name) { " -x: decode and print adxencd command line to encode as ADX\n" " -g: decode and print oggenc command line to encode as OGG\n" " -b: decode and print batch variable commands\n" - " -L: append a smpl chunk and create a looping wav\n" + " -L: append a smpl chunk and create a looping wav\n" " -e: force end-to-end looping\n" " -E: force end-to-end looping even if file has real loop points\n" " -r outfile2.wav: output a second time after resetting\n" " -2 N: only output the Nth (first is 0) set of stereo channels\n" ,name); - } int main(int argc, char ** argv) { @@ -109,9 +108,9 @@ int main(int argc, char ** argv) { case 'E': really_force_loop = 1; break; - case 'L': - lwav = 1; - break; + case 'L': + lwav = 1; + break; case 'r': reset_outfilename = optarg; break; @@ -298,16 +297,16 @@ int main(int argc, char ** argv) { fwrite(buf,sizeof(sample)*s->channels,toget,outfile); } } - - if (!play && lwav && s->loop_flag) { // Writing smpl chuck - make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample); - fwrite(buf,1,0x44,outfile); - fseek(outfile, 4, SEEK_SET); - - size_t bytecount = len*s->channels*sizeof(sample); - put_32bitLE((uint8_t*)buf, (int32_t)(bytecount+0x2c+52)); - fwrite(buf,1,0x4,outfile); - } + + if (!play && lwav && s->loop_flag) { // Writing smpl chuck + make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample); + fwrite(buf,1,0x44,outfile); + fseek(outfile, 4, SEEK_SET); + + size_t bytecount = len*s->channels*sizeof(sample); + put_32bitLE((uint8_t*)buf, (int32_t)(bytecount+0x2c+52)); + fwrite(buf,1,0x4,outfile); + } fclose(outfile); outfile = NULL; #ifdef PROFILE_STREAMFILE diff --git a/winamp/Makefile b/winamp/Makefile index e4a51d3b..670aa4a5 100644 --- a/winamp/Makefile +++ b/winamp/Makefile @@ -35,7 +35,7 @@ libg7221_decode.a: libg719_decode.a: $(MAKE) -C ../ext_libs -f Makefile.mingw $@ -at3plusdecoder.a: +libat3plusdecoder.a: $(MAKE) -C ../ext_libs -f Makefile.mingw $@ clean: