mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-20 10:25:53 +01:00
64 lines
2.5 KiB
C
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;
|
|
}
|