diff --git a/src/coding/ngc_dtk_decoder.c b/src/coding/ngc_dtk_decoder.c index 21d18902..b07c6834 100644 --- a/src/coding/ngc_dtk_decoder.c +++ b/src/coding/ngc_dtk_decoder.c @@ -1,51 +1,65 @@ #include "coding.h" #include "../util.h" + +/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i=first_sample; - int32_t sample_count; - - int framesin = first_sample/28; - - uint8_t q = read_8bit(framesin*32+stream->offset+channel,stream->streamfile); + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - first_sample = first_sample%28; - for (i=first_sample,sample_count=0; ioffset+4+i,stream->streamfile); + /* external interleave (fixed size), stereo */ + bytes_per_frame = 0x20; + samples_per_frame = 28; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - int32_t hist=0; + /* parse frame L/R header (repeated at 0x03/04) */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf; + /* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */ + VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %lx\n", frame_offset); - switch (q>>4) - { + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist = 0, new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile); + + /* apply XA filters << 6 */ + switch(coef_index) { case 0: - hist = 0; + hist = 0; // (hist1 * 0) - (hist2 * 0); break; case 1: - hist = (hist1 * 0x3c); + hist = (hist1 * 60); // - (hist2 * 0); break; case 2: - hist = (hist1 * 0x73) - (hist2 * 0x34); + hist = (hist1 * 115) - (hist2 * 52); break; case 3: - hist = (hist1 * 0x62) - (hist2 * 0x37); + hist = (hist1 * 98) - (hist2 * 55); break; } - - hist = (hist+0x20)>>6; + hist = (hist + 32) >> 6; if (hist > 0x1fffff) hist = 0x1fffff; if (hist < -0x200000) hist = -0x200000; + new_sample = (channel==0) ? /* L=low nibble first */ + get_low_nibble_signed(nibbles) : + get_high_nibble_signed(nibbles); + new_sample = (new_sample << 12) >> shift_factor; + new_sample = (new_sample << 6) + hist; + hist2 = hist1; + hist1 = new_sample; - hist1 = ((((channel==0? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte) - ) << 12) >> (q & 0xf)) << 6) + hist; - - outbuf[sample_count] = clamp16(hist1 >> 6); + outbuf[sample_count] = clamp16(new_sample >> 6); + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/src/meta/ngc_adpdtk.c b/src/meta/ngc_adpdtk.c index ab3852c6..c7e4d0b1 100644 --- a/src/meta/ngc_adpdtk.c +++ b/src/meta/ngc_adpdtk.c @@ -2,40 +2,45 @@ #include "meta.h" #include "../util.h" -/* DTK - headerless Nintendo DTK file [Harvest Moon - Another Wonderful Life (GC), XGRA (GC)] */ +/* DTK - headerless Nintendo GC DTK file [Harvest Moon: Another Wonderful Life (GC), XGRA (GC)] */ VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0; - int channel_count = 2, loop_flag = 0; /* always stereo, no loop */ + off_t start_offset; + int channel_count, loop_flag; + /* checks */ - /* dtk: standard [XGRA (GC)], adp: standard [Harvest Moon AWL (GC)], wav/lwav: Alien Hominid (GC) */ + /* .dtk: standard [XGRA (GC)], .adp: standard [Harvest Moon AWL (GC)], .wav/lwav: Alien Hominid (GC) */ if ( !check_extensions(streamFile,"dtk,adp,wav,lwav")) goto fail; - /* files have no header, and the ext is common, so all we can do is look for valid first frames */ - if (check_extensions(streamFile,"adp,wav,lwav")) { + /* check valid frames as files have no header, and .adp/wav are common */ + { int i; for (i = 0; i < 10; i++) { /* try a bunch of frames */ if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) || read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile)) goto fail; + /* header 0x00/01 are repeated in 0x02/03 (for error correction?), + * could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */ } } + /* always stereo, no loop (since it's hardware-decoded and streamed) */ + channel_count = 2; + loop_flag = 0; + start_offset = 0x00; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28; - vgmstream->sample_rate = 48000; + vgmstream->num_samples = get_streamfile_size(streamFile) / 0x20 * 28; + vgmstream->sample_rate = 48000; /* due to a GC hardware defect this may be closer to 48043 */ vgmstream->coding_type = coding_NGC_DTK; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_ADPDTK; - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; @@ -45,4 +50,3 @@ fail: close_vgmstream(vgmstream); return NULL; } -