#include "../util.h" #include "coding.h" static const int msadpcm_steps[16] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; static const int msadpcm_coefs[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; STREAMFILE *streamfile; int i, frames_in; size_t bytes_per_frame, samples_per_frame; off_t frame_offset; ch1 = &vgmstream->ch[0]; ch2 = &vgmstream->ch[1]; streamfile = ch1->streamfile; /* external interleave (variable size), stereo */ bytes_per_frame = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; frame_offset = ch1->offset + frames_in*bytes_per_frame; /* parse frame header */ if (first_sample == 0) { ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0]; ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1]; ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0]; ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1]; ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile); ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile); ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile); ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile); ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile); ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile); } /* write header samples (needed) */ if (first_sample==0) { 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--; } /* decode nibbles */ for (i = first_sample; i < first_sample+samples_to_do; i++) { int ch; for (ch = 0; ch < 2; ch++) { VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch]; int32_t hist1,hist2, predicted; int sample_nibble = (ch == 0) ? /* L = high nibble first */ get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) : get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile)); hist1 = stream->adpcm_history1_16; hist2 = stream->adpcm_history2_16; predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; predicted = predicted / 256; predicted = predicted + sample_nibble*stream->adpcm_scale; outbuf[0] = clamp16(predicted); stream->adpcm_history2_16 = stream->adpcm_history1_16; stream->adpcm_history1_16 = outbuf[0]; stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; if (stream->adpcm_scale < 0x10) stream->adpcm_scale = 0x10; outbuf++; } } } void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; int i, frames_in; size_t bytes_per_frame, samples_per_frame; off_t frame_offset; /* external interleave (variable size), mono */ bytes_per_frame = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; frame_offset = stream->offset + frames_in*bytes_per_frame; /* parse frame header */ if (first_sample == 0) { stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); } /* write header samples (needed) */ if (first_sample == 0) { outbuf[0] = stream->adpcm_history2_16; outbuf += channelspacing; first_sample++; samples_to_do--; } if (first_sample == 1 && samples_to_do > 0) { outbuf[0] = stream->adpcm_history1_16; outbuf += channelspacing; first_sample++; samples_to_do--; } /* decode nibbles */ for (i = first_sample; i < first_sample+samples_to_do; i++) { int32_t hist1,hist2, predicted; int sample_nibble = (i & 1) ? /* high nibble first */ get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); hist1 = stream->adpcm_history1_16; hist2 = stream->adpcm_history2_16; predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; predicted = predicted / 256; predicted = predicted + sample_nibble*stream->adpcm_scale; outbuf[0] = clamp16(predicted); stream->adpcm_history2_16 = stream->adpcm_history1_16; stream->adpcm_history1_16 = outbuf[0]; stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; if (stream->adpcm_scale < 0x10) stream->adpcm_scale = 0x10; outbuf += channelspacing; } } /* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order * (their tools may convert to float/others but internally it's all PCM16, from debugging). */ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; int i, frames_in; size_t bytes_per_frame, samples_per_frame; off_t frame_offset; /* external interleave (variable size), mono */ bytes_per_frame = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; frame_offset = stream->offset + frames_in*bytes_per_frame; /* parse frame header */ if (first_sample == 0) { stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */ stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); } /* write header samples (needed) */ if (first_sample == 0) { outbuf[0] = stream->adpcm_history2_16; outbuf += channelspacing; first_sample++; samples_to_do--; } if (first_sample == 1 && samples_to_do > 0) { outbuf[0] = stream->adpcm_history1_16; outbuf += channelspacing; first_sample++; samples_to_do--; } /* decode nibbles */ for (i = first_sample; i < first_sample+samples_to_do; i++) { int32_t hist1,hist2, predicted; int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */ get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); hist1 = stream->adpcm_history1_16; hist2 = stream->adpcm_history2_16; predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; predicted = predicted >> 8; /* probably no difference vs MSADPCM */ predicted = predicted + sample_nibble*stream->adpcm_scale; outbuf[0] = clamp16(predicted); stream->adpcm_history2_16 = stream->adpcm_history1_16; stream->adpcm_history1_16 = outbuf[0]; stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; if (stream->adpcm_scale < 0x10) stream->adpcm_scale = 0x10; outbuf += channelspacing; } } long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) { return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels + ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0); }