2008-05-06 05:35:37 +02:00
|
|
|
#include "coding.h"
|
2008-02-04 16:20:20 +01:00
|
|
|
#include "../util.h"
|
|
|
|
|
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
|
|
|
uint8_t frame[0x08] = {0};
|
|
|
|
off_t frame_offset;
|
|
|
|
int i, frames_in, sample_count = 0;
|
|
|
|
size_t bytes_per_frame, samples_per_frame;
|
|
|
|
int coef_index, scale, coef1, coef2;
|
2008-02-04 16:20:20 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_16;
|
|
|
|
int32_t hist2 = stream->adpcm_history2_16;
|
|
|
|
|
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
/* external interleave (fixed size), mono */
|
|
|
|
bytes_per_frame = 0x08;
|
|
|
|
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
|
|
|
|
frames_in = first_sample / samples_per_frame;
|
|
|
|
first_sample = first_sample % samples_per_frame;
|
|
|
|
|
|
|
|
/* parse frame header */
|
|
|
|
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
|
|
|
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
|
|
|
scale = 1 << ((frame[0] >> 0) & 0xf);
|
|
|
|
coef_index = (frame[0] >> 4) & 0xf;
|
|
|
|
|
|
|
|
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs at %x\n", (uint32_t)frame_offset);
|
|
|
|
//if (coef_index > 8) //todo not correctly clamped in original decoder?
|
|
|
|
// coef_index = 8;
|
|
|
|
|
|
|
|
coef1 = stream->adpcm_coef[coef_index*2 + 0];
|
|
|
|
coef2 = stream->adpcm_coef[coef_index*2 + 1];
|
|
|
|
|
|
|
|
|
|
|
|
/* decode nibbles */
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
|
|
|
int32_t sample = 0;
|
|
|
|
uint8_t nibbles = frame[0x01 + i/2];
|
|
|
|
|
|
|
|
sample = i&1 ? /* high nibble first */
|
|
|
|
get_low_nibble_signed(nibbles) :
|
|
|
|
get_high_nibble_signed(nibbles);
|
|
|
|
sample = ((sample * scale) << 11);
|
|
|
|
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
|
|
|
|
sample = clamp16(sample);
|
2008-02-04 16:20:20 +01:00
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
outbuf[sample_count] = sample;
|
|
|
|
sample_count += channelspacing;
|
2008-02-04 16:20:20 +01:00
|
|
|
|
|
|
|
hist2 = hist1;
|
2019-10-05 15:10:40 +02:00
|
|
|
hist1 = sample;
|
2008-02-04 16:20:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
stream->adpcm_history1_16 = hist1;
|
|
|
|
stream->adpcm_history2_16 = hist2;
|
|
|
|
}
|
2008-03-04 08:15:25 +01:00
|
|
|
|
2017-12-06 21:04:04 +01:00
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
/* read from memory rather than a file */
|
|
|
|
static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * frame) {
|
|
|
|
int i, sample_count = 0;
|
|
|
|
size_t bytes_per_frame, samples_per_frame;
|
|
|
|
int coef_index, scale, coef1, coef2;
|
2017-12-06 21:04:04 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_16;
|
|
|
|
int32_t hist2 = stream->adpcm_history2_16;
|
|
|
|
|
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
/* external interleave (fixed size), mono */
|
|
|
|
bytes_per_frame = 0x08;
|
|
|
|
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
|
|
|
|
first_sample = first_sample % samples_per_frame;
|
|
|
|
VGM_ASSERT_ONCE(samples_to_do > samples_per_frame, "DSP: layout error, too many samples\n");
|
|
|
|
|
|
|
|
/* parse frame header */
|
|
|
|
scale = 1 << ((frame[0] >> 0) & 0xf);
|
|
|
|
coef_index = (frame[0] >> 4) & 0xf;
|
|
|
|
|
|
|
|
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs\n");
|
|
|
|
//if (coef_index > 8) //todo not correctly clamped in original decoder?
|
|
|
|
// coef_index = 8;
|
2017-12-06 21:04:04 +01:00
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
coef1 = stream->adpcm_coef[coef_index*2 + 0];
|
|
|
|
coef2 = stream->adpcm_coef[coef_index*2 + 1];
|
|
|
|
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
|
|
|
int32_t sample = 0;
|
|
|
|
uint8_t nibbles = frame[0x01 + i/2];
|
|
|
|
|
|
|
|
sample = i&1 ?
|
|
|
|
get_low_nibble_signed(nibbles) :
|
|
|
|
get_high_nibble_signed(nibbles);
|
|
|
|
sample = ((sample * scale) << 11);
|
|
|
|
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
|
|
|
|
sample = clamp16(sample);
|
|
|
|
|
|
|
|
outbuf[sample_count] = sample;
|
|
|
|
sample_count += channelspacing;
|
2017-12-06 21:04:04 +01:00
|
|
|
|
|
|
|
hist2 = hist1;
|
2019-10-05 15:10:40 +02:00
|
|
|
hist1 = sample;
|
2017-12-06 21:04:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
stream->adpcm_history1_16 = hist1;
|
|
|
|
stream->adpcm_history2_16 = hist2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* decode DSP with byte-interleaved frames (ex. 0x08: 1122112211221122) */
|
2019-03-11 01:34:36 +01:00
|
|
|
void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave) {
|
2019-10-05 15:10:40 +02:00
|
|
|
uint8_t frame[0x08];
|
2017-12-06 21:04:04 +01:00
|
|
|
int i;
|
2019-10-05 15:10:40 +02:00
|
|
|
int frames_in = first_sample / 14;
|
2017-12-06 21:04:04 +01:00
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
for (i = 0; i < 0x08; i++) {
|
2017-12-06 21:04:04 +01:00
|
|
|
/* base + current frame + subint section + subint byte + channel adjust */
|
2019-10-05 15:10:40 +02:00
|
|
|
frame[i] = read_8bit(
|
2017-12-06 21:04:04 +01:00
|
|
|
stream->offset
|
2019-10-05 15:10:40 +02:00
|
|
|
+ frames_in*(0x08*channelspacing)
|
2017-12-06 21:04:04 +01:00
|
|
|
+ i/interleave * interleave * channelspacing
|
|
|
|
+ i%interleave
|
|
|
|
+ interleave * channel, stream->streamfile);
|
|
|
|
}
|
|
|
|
|
2019-10-05 15:10:40 +02:00
|
|
|
decode_ngc_dsp_subint_internal(stream, outbuf, channelspacing, first_sample, samples_to_do, frame);
|
2017-12-06 21:04:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-04 08:15:25 +01:00
|
|
|
/*
|
|
|
|
* The original DSP spec uses nibble counts for loop points, and some
|
2008-03-25 08:27:14 +01:00
|
|
|
* variants don't have a proper sample count, so we (who are interested
|
2008-03-04 08:15:25 +01:00
|
|
|
* in sample counts) need to do this conversion occasionally.
|
|
|
|
*/
|
|
|
|
int32_t dsp_nibbles_to_samples(int32_t nibbles) {
|
|
|
|
int32_t whole_frames = nibbles/16;
|
|
|
|
int32_t remainder = nibbles%16;
|
|
|
|
|
2008-03-25 08:27:14 +01:00
|
|
|
if (remainder>0) return whole_frames*14+remainder-2;
|
|
|
|
else return whole_frames*14;
|
2008-03-04 08:15:25 +01:00
|
|
|
}
|
2017-01-13 23:56:48 +01:00
|
|
|
|
2017-04-07 16:23:43 +02:00
|
|
|
size_t dsp_bytes_to_samples(size_t bytes, int channels) {
|
2019-03-11 01:34:36 +01:00
|
|
|
if (channels <= 0) return 0;
|
2017-04-07 16:23:43 +02:00
|
|
|
return bytes / channels / 8 * 14;
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:56:48 +01:00
|
|
|
|
2017-08-12 18:39:56 +02:00
|
|
|
/* reads DSP coefs built in the streamfile */
|
2017-01-13 23:56:48 +01:00
|
|
|
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
|
2017-01-25 22:20:01 +01:00
|
|
|
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 1);
|
|
|
|
}
|
|
|
|
void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
|
|
|
|
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 0);
|
|
|
|
}
|
|
|
|
void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
|
2017-01-13 23:56:48 +01:00
|
|
|
int ch, i;
|
|
|
|
/* get ADPCM coefs */
|
|
|
|
for (ch=0; ch < vgmstream->channels; ch++) {
|
|
|
|
for (i=0; i < 16; i++) {
|
2017-01-25 22:20:01 +01:00
|
|
|
vgmstream->ch[ch].adpcm_coef[i] = be ?
|
|
|
|
read_16bitBE(offset + ch*spacing + i*2, streamFile) :
|
|
|
|
read_16bitLE(offset + ch*spacing + i*2, streamFile);
|
2017-01-13 23:56:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-12 18:39:56 +02:00
|
|
|
/* reads DSP initial hist built in the streamfile */
|
|
|
|
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
|
|
|
|
dsp_read_hist(vgmstream, streamFile, offset, spacing, 1);
|
|
|
|
}
|
|
|
|
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
|
|
|
|
dsp_read_hist(vgmstream, streamFile, offset, spacing, 0);
|
|
|
|
}
|
|
|
|
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
|
|
|
|
int ch;
|
|
|
|
/* get ADPCM hist */
|
|
|
|
for (ch=0; ch < vgmstream->channels; ch++) {
|
|
|
|
vgmstream->ch[ch].adpcm_history1_16 = be ?
|
|
|
|
read_16bitBE(offset + ch*spacing + 0*2, streamFile) :
|
|
|
|
read_16bitLE(offset + ch*spacing + 0*2, streamFile);;
|
|
|
|
vgmstream->ch[ch].adpcm_history2_16 = be ?
|
|
|
|
read_16bitBE(offset + ch*spacing + 1*2, streamFile) :
|
|
|
|
read_16bitLE(offset + ch*spacing + 1*2, streamFile);;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|