vgmstream/src/coding/xmd_decoder.c
2018-08-03 00:47:54 +02:00

64 lines
2.5 KiB
C

#include "coding.h"
/* Decodes Konami XMD from Xbox games.
* Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */
void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) {
off_t frame_offset;
int i, frames_in, sample_count = 0, samples_done = 0;
size_t bytes_per_frame, samples_per_frame;
int16_t hist1, hist2;
uint16_t scale;
/* external interleave (variable size), mono */
bytes_per_frame = frame_size;
samples_per_frame = 2 + (frame_size - 0x06) * 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;
hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile);
hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile);
scale = (uint16_t)read_16bitLE(frame_offset+0x04,stream->streamfile); /* scale doesn't go too high though */
/* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist2;
samples_done++;
}
sample_count++;
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist1;
samples_done++;
}
sample_count++;
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x06 + i/2,stream->streamfile);
new_sample = i&1 ? /* low nibble first */
get_high_nibble_signed(nibbles):
get_low_nibble_signed(nibbles);
/* Coefs are based on XA's filter 2 (using those creates hissing in some songs though)
* ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */
new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14;
//new_sample = clamp16(new_sample); /* not needed */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = (int16_t)new_sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = new_sample;
}
//stream->adpcm_history1_32 = hist1;
//stream->adpcm_history2_32 = hist2;
}