vgmstream/src/coding/dsa_decoder.c
2019-03-02 19:23:37 +01:00

53 lines
1.8 KiB
C

#include "coding.h"
static const int dsa_coefs[16] = {
0x0, 0x1999, 0x3333, 0x4CCC,
0x6666, 0x8000, 0x9999, 0xB333,
0xCCCC, 0xE666, 0x10000, 0x11999,
0x13333, 0x18000, 0x1CCCC, 0x21999
};
/* Decodes Ocean DSA ADPCM codec from Last Rites (PC).
* Reverse engineered from daemon1's reverse engineering. */
void decode_dsa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset;
int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
uint8_t header;
int shift, filter;
int32_t hist1 = stream->adpcm_history1_32;
/* external interleave (fixed size), mono */
bytes_per_frame = 0x08;
samples_per_frame = (bytes_per_frame - 0x01) * 2;
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;
header = (uint8_t)read_8bit(frame_offset+0x00,stream->streamfile);
shift = 0x0c - ((header >> 4) & 0xf);
filter = dsa_coefs[header & 0xf];
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile);
sample = i&1 ? /* high nibble first */
(nibbles >> 0) & 0xf :
(nibbles >> 4) & 0xf;
sample = ((int16_t)(sample << 0xC) >> shift); /* 16b sign extend + scale */
sample = sample + ((hist1 * filter) >> 0x10);
outbuf[sample_count] = (sample_t)(sample << 2);
sample_count += channelspacing;
hist1 = sample;
}
stream->adpcm_history1_32 = hist1;
}