diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c new file mode 100644 index 00000000..394f3888 --- /dev/null +++ b/src/coding/ima_decoder.c @@ -0,0 +1,65 @@ +#include "../util.h" +#include "ima_decoder.h" + +const int32_t ADPCMTable[89] = + +{ + + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 + +}; + +const int IMA_IndexTable[16] = + +{ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i=first_sample; + int32_t sample_count; + int32_t hist1 = stream->adpcm_history1_16; + int step_index = stream->adpcm_step_index; + + if (first_sample==0) { + step_index = read_16bitLE(stream->offset+2,stream->streamfile); + hist1 = read_16bitLE(stream->offset,stream->streamfile); + } + + for (i=first_sample,sample_count=0; ioffset+4+i/2,stream->streamfile) >> (i&1?4:0))&0xf; + int delta; + int step = ADPCMTable[step_index]; + + delta = step >> 3; + if (sample_nibble & 1) delta += step >> 2; + if (sample_nibble & 2) delta += step >> 1; + if (sample_nibble & 4) delta += step; + if (sample_nibble & 8) + outbuf[sample_count] = clamp16(hist1 - delta); + else + outbuf[sample_count] = clamp16(hist1 + delta); + + step_index += IMA_IndexTable[sample_nibble]; + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + hist1 = outbuf[sample_count]; + } + + stream->adpcm_history1_16 = hist1; + stream->adpcm_step_index = step_index; +} diff --git a/src/coding/ima_decoder.h b/src/coding/ima_decoder.h new file mode 100644 index 00000000..8b691cae --- /dev/null +++ b/src/coding/ima_decoder.h @@ -0,0 +1,8 @@ +#include "../vgmstream.h" + +#ifndef _IMA_DECODER_H +#define _IMA_DECODER_H + +void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + +#endif diff --git a/src/layout/interleave.c b/src/layout/interleave.c index 8cea2b23..1f9b5bf7 100644 --- a/src/layout/interleave.c +++ b/src/layout/interleave.c @@ -5,25 +5,34 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA int samples_written=0; int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);; + int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); int samples_this_block; samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; if (vgmstream->layout_type == layout_interleave_shortblock && vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { - samples_this_block = vgmstream->interleave_smallblock_size / frame_size * samples_per_frame; + frame_size = get_vgmstream_shortframe_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); + + samples_this_block = vgmstream->interleave_smallblock_size / frame_size * samples_per_frame; } while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { - samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; + /* we assume that the loop is not back into a short block */ + if (vgmstream->layout_type == layout_interleave_shortblock) { + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; + } continue; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + /*printf("vgmstream_samples_to_do(samples_this_block=%d,samples_per_frame=%d,vgmstream) returns %d\n",samples_this_block,samples_per_frame,samples_to_do);*/ if (samples_written+samples_to_do > sample_count) samples_to_do=sample_count-samples_written; @@ -38,6 +47,8 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA int chan; if (vgmstream->layout_type == layout_interleave_shortblock && vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + frame_size = get_vgmstream_shortframe_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); samples_this_block = vgmstream->interleave_smallblock_size / frame_size * samples_per_frame; for (chan=0;chanchannels;chan++) diff --git a/src/layout/nolayout.h b/src/layout/nolayout.h index 19e400e3..4c6f1482 100644 --- a/src/layout/nolayout.h +++ b/src/layout/nolayout.h @@ -1,5 +1,4 @@ /* - * interleave.h - interleaved layouts */ #include "../streamtypes.h" #include "../vgmstream.h" diff --git a/src/meta/nds_strm.c b/src/meta/nds_strm.c index 6a397c66..dc3764e5 100644 --- a/src/meta/nds_strm.c +++ b/src/meta/nds_strm.c @@ -70,7 +70,7 @@ VGMSTREAM * init_vgmstream_nds_strm(const char * const filename) { vgmstream->interleave_block_size = read_32bitLE(0x30,infile); vgmstream->interleave_smallblock_size = read_32bitLE(0x38,infile); - if (channel_count==1 || coding_type==coding_PCM8 || coding_type==coding_PCM16LE) + if (coding_type==coding_PCM8 || coding_type==coding_PCM16LE) vgmstream->layout_type = layout_none; else vgmstream->layout_type = layout_interleave_shortblock; diff --git a/src/vgmstream.c b/src/vgmstream.c index 2f2c8dfb..a38a2fa8 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -127,11 +127,22 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM16BE: case coding_PCM8: return 1; + case coding_NDS_IMA: + return (vgmstream->interleave_block_size-4)*2; default: return 0; } } +int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return (vgmstream->interleave_smallblock_size-4)*2; + default: + return get_vgmstream_samples_per_frame(vgmstream); + } +} + int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_CRI_ADX: @@ -143,11 +154,22 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 2; case coding_PCM8: return 1; + case coding_NDS_IMA: + return vgmstream->interleave_block_size; default: return 0; } } +int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return vgmstream->interleave_smallblock_size; + default: + return get_vgmstream_frame_size(vgmstream); + } +} + void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample * buffer) { int chan; @@ -188,6 +210,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_NDS_IMA: + for (chan=0;chanchannels;chan++) { + decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; } } @@ -297,6 +326,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream) { case coding_CRI_ADX: printf("CRI ADX 4-bit ADPCM"); break; + case coding_NDS_IMA: + printf("NDS-style 4-bit IMA ADPCM"); + break; default: printf("CANNOT DECODE"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 7b71edc7..653ce996 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -80,6 +80,8 @@ typedef struct { int16_t adpcm_history2_16; /* previous previous sample */ int32_t adpcm_history2_32; }; + + int adpcm_step_index; /* for IMA */ } VGMSTREAMCHANNEL; typedef struct { @@ -149,6 +151,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream); /* number of bytes per frame */ int get_vgmstream_frame_size(VGMSTREAM * vgmstream); +/* in NDS IMA the frame size is the block size, so the last one is short */ +int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream); +int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream); /* Assume that we have written samples_written into the buffer already, and we have samples_to_do consecutive * samples ahead of us. Decode those samples into the buffer. */ diff --git a/test/Makefile b/test/Makefile index ebb979e7..b87fb740 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,6 @@ CFLAGS=-lm -O3 -test: test.c ../src/streamfile.c ../src/vgmstream.c ../src/util.c ../src/meta/adx_header.c ../src/coding/adx_decoder.c ../src/coding/gcdsp_decoder.c ../src/meta/brstm.c ../src/layout/interleave.c ../src/layout/nolayout.c ../src/coding/pcm_decoder.c ../src/meta/nds_strm.c +test: test.c ../src/streamfile.c ../src/vgmstream.c ../src/util.c ../src/meta/adx_header.c ../src/coding/adx_decoder.c ../src/coding/gcdsp_decoder.c ../src/meta/brstm.c ../src/layout/interleave.c ../src/layout/nolayout.c ../src/coding/pcm_decoder.c ../src/meta/nds_strm.c ../src/coding/ima_decoder.c clean: rm test