diff --git a/src/Makefile b/src/Makefile index 24a67b8a..f6c42894 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ CODING_OBJS=coding/adx_decoder.o \ coding/ws_decoder.o \ coding/mpeg_decoder.o \ coding/acm_decoder.o \ - coding/nwa_decoder.o + coding/nwa_decoder.o \ + coding/msadpcm_decoder.o LAYOUT_OBJS=layout/ast_blocked.o \ layout/blocked.o \ @@ -28,8 +29,9 @@ LAYOUT_OBJS=layout/ast_blocked.o \ layout/ws_aud_blocked.o \ layout/interleave_byte.o \ layout/mus_acm_layout.o \ - layout/aix_layout.o \ - layout/ims_block.o + layout/aix_layout.o \ + layout/ims_block.o \ + layout/de2_blocked.o META_OBJS=meta/adx_header.o \ meta/afc_header.o \ @@ -116,8 +118,9 @@ META_OBJS=meta/adx_header.o \ meta/ngc_tydsp.o \ meta/ngc_vjdsp.o \ meta/xbox_wvs.o \ + meta/xbox_ims.o \ meta/xbox_stma.o \ - meta/xbox_ims.o + meta/de2.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 2860fbdf..ba4e13d5 100644 --- a/src/coding/Makefile.unix.am +++ b/src/coding/Makefile.unix.am @@ -21,5 +21,6 @@ libcoding_la_SOURCES += ws_decoder.c libcoding_la_SOURCES += mpeg_decoder.c libcoding_la_SOURCES += acm_decoder.c libcoding_la_SOURCES += nwa_decoder.c +libcoding_la_SOURCES += msadpcm_decoder. EXTRA_DIST = coding.h g72x_state.h diff --git a/src/coding/coding.h b/src/coding/coding.h index 12ae8db3..4a54fb3a 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -66,4 +66,6 @@ void decode_acm(ACMStream * acm, sample * outbuf, void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); +void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); + #endif diff --git a/src/coding/msadpcm_decoder.c b/src/coding/msadpcm_decoder.c new file mode 100644 index 00000000..685df9bc --- /dev/null +++ b/src/coding/msadpcm_decoder.c @@ -0,0 +1,96 @@ +#include "../util.h" +#include "coding.h" + +/* used to compute next scale */ +static const int ADPCMTable[16] = +{ + 230, 230, 230, 230, + 307, 409, 512, 614, + 768, 614, 512, 409, + 307, 230, 230, 230 +}; + +static const int ADPCMCoeffs[7][2] = +{ + { 256, 0 }, + { 512, -256 }, + { 0, 0 }, + { 192, 64 }, + { 240, 0 }, + { 460, -208 }, + { 392, -232 } +}; + +void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { + VGMSTREAMCHANNEL *ch1,*ch2; + int i; + int framesin; + STREAMFILE *streamfile; + off_t offset; + + framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); + first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); + + ch1 = &vgmstream->ch[0]; + ch2 = &vgmstream->ch[1]; + streamfile = ch1->streamfile; + offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream); + + if (first_sample==0) { + ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; + ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; + ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0]; + ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1]; + ch1->adpcm_scale = read_16bitLE(offset+2,streamfile); + ch2->adpcm_scale = read_16bitLE(offset+4,streamfile); + ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile); + ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile); + ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile); + ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile); + + outbuf[0] = ch1->adpcm_history2_16; + outbuf[1] = ch2->adpcm_history2_16; + + outbuf+=2; + first_sample++; + samples_to_do--; + } + if (first_sample==1 && samples_to_do > 0) { + outbuf[0] = ch1->adpcm_history1_16; + outbuf[1] = ch2->adpcm_history1_16; + + outbuf+=2; + first_sample++; + samples_to_do--; + } + + for (i=first_sample; ich[j]; + int sample_nibble = + (j == 0 ? + get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) : + get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile)) + ); + int32_t hist1,hist2; + int32_t predicted; + + hist1 = ch->adpcm_history1_16; + hist2 = ch->adpcm_history2_16; + predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; + predicted /= 256; + predicted += sample_nibble*ch->adpcm_scale; + outbuf[0] = clamp16(predicted); + ch->adpcm_history2_16 = ch->adpcm_history1_16; + ch->adpcm_history1_16 = outbuf[0]; + ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * + ch->adpcm_scale) / 256; + if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; + + outbuf++; + } + } +} diff --git a/src/layout/Makefile.unix.am b/src/layout/Makefile.unix.am index 881b2557..51953569 100644 --- a/src/layout/Makefile.unix.am +++ b/src/layout/Makefile.unix.am @@ -20,5 +20,6 @@ liblayout_la_SOURCES += interleave_byte.c liblayout_la_SOURCES += mus_acm_layout.c liblayout_la_SOURCES += aix_layout.c liblayout_la_SOURCES += ims_block.c +liblayout_la_SOURCES += de2_layout.c EXTRA_DIST = layout.h diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 26ab299a..e220cbb4 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -81,6 +81,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * break; case layout_matx_blocked: matx_block_update(vgmstream->next_block_offset,vgmstream); + break; + case layout_de2_blocked: + de2_block_update(vgmstream->next_block_offset,vgmstream); break; default: break; diff --git a/src/layout/de2_blocked.c b/src/layout/de2_blocked.c new file mode 100644 index 00000000..b3865787 --- /dev/null +++ b/src/layout/de2_blocked.c @@ -0,0 +1,18 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* set up for the block at the given offset */ +void de2_block_update(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = read_32bitLE( + vgmstream->current_block_offset+4, + vgmstream->ch[0].streamfile)/2/vgmstream->channels; + vgmstream->next_block_offset = block_offset+8+read_32bitLE( + vgmstream->current_block_offset, + vgmstream->ch[0].streamfile); + + for (i=0;ichannels;i++) { + vgmstream->ch[i].offset = vgmstream->current_block_offset + 8; + } +} diff --git a/src/layout/layout.h b/src/layout/layout.h index b82bf2a9..eaa4a68d 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -26,6 +26,8 @@ void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream); void matx_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void de2_block_update(off_t block_offset, VGMSTREAM * vgmstream); + void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 00142a64..f27e5af4 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -242,6 +242,10 @@ RelativePath=".\meta\dc_str.c" > + + @@ -616,6 +620,10 @@ RelativePath=".\coding\mpeg_decoder.c" > + + @@ -688,6 +696,10 @@ RelativePath=".\layout\caf_blocked.c" > + + diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 8fb1c599..157c7737 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -91,5 +91,6 @@ libmeta_la_SOURCES += xbox_wvs.c libmeta_la_SOURCES += ngc_vjdsp.c libmeta_la_SOURCES += xbox_stma.c libmeta_la_SOURCES += xbox_ims.c +libmeta_la_SOURCES += de2.c EXTRA_DIST = meta.h diff --git a/src/meta/de2.c b/src/meta/de2.c new file mode 100644 index 00000000..f1b051eb --- /dev/null +++ b/src/meta/de2.c @@ -0,0 +1,101 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../util.h" + +/* Gurumin .de2 */ +/* A ways into the file we have a fake RIFF header wrapping MS ADPCM */ + +VGMSTREAM * init_vgmstream_de2(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + + off_t riff_off; + int channel_count; + int sample_count; + int sample_rate; + off_t start_offset; + + int loop_flag = 0; + uint32_t data_size; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("de2",filename_extension(filename))) goto fail; + + /* still not sure what this is for, but consistently 0xb */ + if (read_32bitLE(0x04,streamFile)!=0xb) goto fail; + + /* legitimate! really! */ + riff_off = 0x10 + + (read_32bitLE(0x0c,streamFile) ^ read_32bitLE(0x04,streamFile)); + + /* check header */ + if ((uint32_t)read_32bitBE(riff_off+0,streamFile)!=0x52494646) /* "RIFF" */ + goto fail; + /* check for WAVE form */ + if ((uint32_t)read_32bitBE(riff_off+8,streamFile)!=0x57415645) /* "WAVE" */ + goto fail; + /* check for "fmt " */ + if ((uint32_t)read_32bitBE(riff_off+12,streamFile)!=0x666d7420) /* "fmt " */ + goto fail; + /* check for "data" */ + if ((uint32_t)read_32bitBE(riff_off+0x24,streamFile)!=0x64617461) /* "data" */ + goto fail; + /* check for bad fmt chunk size */ + if (read_32bitLE(riff_off+0x10,streamFile)!=0x12) goto fail; + + sample_rate = read_32bitLE(riff_off+0x18,streamFile); + + channel_count = read_16bitLE(riff_off+0x16,streamFile); + if (channel_count != 2) goto fail; + + /* PCM */ + if (read_16bitLE(riff_off+0x14,streamFile) != 1) goto fail; + + /* 16-bit */ + if (read_16bitLE(riff_off+0x20,streamFile) != 4 || + read_16bitLE(riff_off+0x22,streamFile) != 16) goto fail; + + start_offset = riff_off + 0x2c; + data_size = read_32bitLE(riff_off+0x28,streamFile); + + sample_count = data_size/2/channel_count; + + /* build the VGMSTREAM */ + + 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_MSADPCM; + vgmstream->layout_type = layout_de2_blocked; + vgmstream->interleave_block_size = 0x800; + + vgmstream->meta_type = meta_DE2; + + /* 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; + } + } + + /* start me up */ + de2_block_update(start_offset,vgmstream); + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index ea30b49b..ce1d0f74 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -201,4 +201,6 @@ VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_de2(STREAMFILE *streamFile); + #endif diff --git a/src/meta/ngc_tydsp.c b/src/meta/ngc_tydsp.c index af7d47f8..823edd16 100644 --- a/src/meta/ngc_tydsp.c +++ b/src/meta/ngc_tydsp.c @@ -14,9 +14,11 @@ VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE *streamFile) { streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("tydsp",filename_extension(filename))) goto fail; +#if 0 /* check header */ - /* if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ - /* goto fail; */ + if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ + goto fail; +#endif loop_flag = 1; channel_count = 2; diff --git a/src/meta/ngc_vjdsp.c b/src/meta/ngc_vjdsp.c index ea62eb41..4a33e520 100644 --- a/src/meta/ngc_vjdsp.c +++ b/src/meta/ngc_vjdsp.c @@ -14,9 +14,11 @@ VGMSTREAM * init_vgmstream_ngc_vjdsp(STREAMFILE *streamFile) { streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("vjdsp",filename_extension(filename))) goto fail; +#if 0 /* check header */ - /* if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ - /* goto fail; */ + if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ + goto fail; +#endif loop_flag = read_32bitBE(0x14,streamFile); channel_count = read_32bitBE(0x10,streamFile); diff --git a/src/meta/pcm.c b/src/meta/pcm.c index 3cfc2f6a..96d539a2 100644 --- a/src/meta/pcm.c +++ b/src/meta/pcm.c @@ -89,4 +89,4 @@ VGMSTREAM * init_vgmstream_pcm(STREAMFILE *streamFile) { fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; -} \ No newline at end of file +} diff --git a/src/meta/ps2_dxh.c b/src/meta/ps2_dxh.c index 2344eb67..e87e1669 100644 --- a/src/meta/ps2_dxh.c +++ b/src/meta/ps2_dxh.c @@ -9,7 +9,6 @@ VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE *streamFile) { int loop_flag = 0; int channel_count; - int illegal_loopend; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); diff --git a/src/meta/ps2_psh.c b/src/meta/ps2_psh.c index b9262893..5faebb3c 100644 --- a/src/meta/ps2_psh.c +++ b/src/meta/ps2_psh.c @@ -8,7 +8,6 @@ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { char filename[260]; off_t start_offset; uint8_t testBuffer[0x10]; - off_t loopStart = 0; off_t loopEnd = 0; off_t readOffset = 0; size_t fileLength; diff --git a/src/meta/ps2_psw.c b/src/meta/ps2_psw.c index 5653a3a8..67ec9f97 100644 --- a/src/meta/ps2_psw.c +++ b/src/meta/ps2_psw.c @@ -7,7 +7,6 @@ VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE *streamFile) { char filename[260]; off_t start_offset; - int CodecID; int loop_flag = 0; int channel_count; @@ -18,7 +17,6 @@ VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE *streamFile) { /* check header */ if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" */ read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */ - /* read_16bitBE(0x14,streamFile) != 0xFFFF && /* "\FFFF" */ read_32bitBE(0x26,streamFile) != 0x64617461) /* "data" */ goto fail; diff --git a/src/meta/ps2_vas.c b/src/meta/ps2_vas.c index 72061ec5..a24caab7 100644 --- a/src/meta/ps2_vas.c +++ b/src/meta/ps2_vas.c @@ -15,8 +15,10 @@ VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE *streamFile) { if (strcasecmp("vas",filename_extension(filename))) goto fail; /* check header */ - /* if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ - /* goto fail; */ +#if 0 + if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ + goto fail; +#endif loop_flag = (read_32bitLE(0x10,streamFile)!=0); channel_count = 2; diff --git a/src/meta/sdt.c b/src/meta/sdt.c index aaae2b17..3eec1008 100644 --- a/src/meta/sdt.c +++ b/src/meta/sdt.c @@ -14,9 +14,11 @@ VGMSTREAM * init_vgmstream_sdt(STREAMFILE *streamFile) { streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("sdt",filename_extension(filename))) goto fail; +#if 0 /* check header */ - /* if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ - /* goto fail; */ + if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */ + goto fail; +#endif loop_flag = (read_32bitBE(0x04,streamFile)!=0); channel_count = 2; diff --git a/src/meta/xbox_ims.c b/src/meta/xbox_ims.c index 0f0a570e..6ddc01b0 100644 --- a/src/meta/xbox_ims.c +++ b/src/meta/xbox_ims.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../util.h" +#include "../layout/layout.h" /* matx diff --git a/src/vgmstream.c b/src/vgmstream.c index 5676eb46..4551b94f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -114,6 +114,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_xbox_wvs, init_vgmstream_xbox_stma, init_vgmstream_xbox_matx, + init_vgmstream_de2, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -462,6 +463,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_str_snds_blocked: case layout_ws_aud_blocked: case layout_matx_blocked: + case layout_de2_blocked: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: @@ -542,6 +544,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { /* only works if output sample size is 8 bit, which is always is for WS ADPCM */ return vgmstream->ws_output_size; + case coding_MSADPCM: + return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2; default: return 0; } @@ -606,6 +610,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return vgmstream->current_block_size; case coding_INT_DVI_IMA: return 1; + case coding_MSADPCM: + return vgmstream->interleave_block_size; default: return 0; } @@ -859,6 +865,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do ); break; + case coding_MSADPCM: + if (vgmstream->channels == 2) { + decode_msadpcm_stereo(vgmstream, + buffer+samples_written*vgmstream->channels, + vgmstream->samples_into_block, + samples_to_do); + } } } diff --git a/src/vgmstream.h b/src/vgmstream.h index b4e0f4d3..6af8a1b2 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -87,6 +87,8 @@ typedef enum { coding_NWA3, coding_NWA4, coding_NWA5, + + coding_MSADPCM /* Microsoft ADPCM */ } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -110,6 +112,7 @@ typedef enum { layout_str_snds_blocked, layout_ws_aud_blocked, layout_matx_blocked, + layout_de2_blocked, #if 0 layout_strm_blocked, /* */ #endif @@ -268,6 +271,7 @@ typedef enum { meta_KCEY, /* KCEYCOMP */ meta_ACM, /* InterPlay ACM header */ meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */ + meta_DE2, /* Falcom (Gurumin) .de2 */ } meta_t; typedef struct { @@ -291,7 +295,8 @@ typedef struct { int32_t adpcm_history2_32; }; - int adpcm_step_index; /* for IMA */ + int adpcm_step_index; /* for IMA */ + int adpcm_scale; /* for MS ADPCM */ struct g72x_state g72x_state; /* state for G.721 decoder, sort of big but we might as well keep it around */ diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index 8134343c..2ebbdd37 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -173,6 +173,7 @@ char * extension_list[] = { "wvs\0WVS Audio File (*.WVS)\0", "stma\0STMA Audio File (*.STMA)\0", "matx\0MATX Audio File (*.MATX)\0", + "de2\0DE2 Audio File (*.DE2)\0", }; void about(HWND hwndParent) {