mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-01 01:27:20 +01:00
Improve AFC/DTK/FADPCM/PROCYON/LSF/ASF/DSF/XMD/MTAF/MTA2 performance
This commit is contained in:
parent
85ec5e5bab
commit
73e60d64b1
@ -1,55 +1,56 @@
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games.
|
||||
* Algorithm should be accurate (reverse engineered from asfcodec.adl DLL). */
|
||||
void decode_asf(VGMSTREAMCHANNEL * stream, sample * 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 shift, mode;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x11;
|
||||
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;
|
||||
shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
|
||||
mode = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
|
||||
|
||||
/* 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+0x01 + i/2,stream->streamfile);
|
||||
|
||||
new_sample = i&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles):
|
||||
get_high_nibble_signed(nibbles);
|
||||
/* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */
|
||||
new_sample = (new_sample << 4) << (shift + 2);
|
||||
|
||||
/* mode is checked as a flag, so there are 2 modes only, but lower nibble
|
||||
* may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */
|
||||
if (mode & 0x4) { /* ~filters: 2, -1 */
|
||||
new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6;
|
||||
}
|
||||
else { /* ~filters: 1, 0 */
|
||||
new_sample = (new_sample + (hist1 << 6)) >> 6;
|
||||
}
|
||||
|
||||
//new_sample = clamp16(new_sample); /* must not */
|
||||
outbuf[sample_count] = (int16_t)new_sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games.
|
||||
* Reverse engineered from asfcodec.adl DLL. */
|
||||
void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x11] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int shift, mode;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x11;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
//first_sample = first_sample % samples_per_frame; /* for flat layout */
|
||||
|
||||
/* 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 */
|
||||
shift = (frame[0x00] >> 4) & 0xf;
|
||||
mode = (frame[0x00] >> 0) & 0xf;
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
sample = i&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles):
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = (sample << 4) << (shift + 2); /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */
|
||||
|
||||
/* mode is checked as a flag, so there are 2 modes only, but lower nibble
|
||||
* may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */
|
||||
if (mode & 0x4) { /* ~filters: 2, -1 */
|
||||
sample = (sample + (hist1 << 7) - (hist2 << 6)) >> 6;
|
||||
}
|
||||
else { /* ~filters: 1, 0 */
|
||||
sample = (sample + (hist1 << 6)) >> 6;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = (int16_t)sample; /* must not clamp */
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
@ -55,10 +55,10 @@ void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offse
|
||||
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
|
||||
|
||||
/* ngc_dtk_decoder */
|
||||
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
|
||||
/* ngc_afc_decoder */
|
||||
void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* pcm_decoder */
|
||||
void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
@ -138,7 +138,7 @@ size_t yamaha_bytes_to_samples(size_t bytes, int channels);
|
||||
size_t aska_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
/* nds_procyon_decoder */
|
||||
void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* l5_555_decoder */
|
||||
void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
@ -147,28 +147,28 @@ void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
|
||||
void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* lsf_decode */
|
||||
void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* mtaf_decoder */
|
||||
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
|
||||
/* mta2_decoder */
|
||||
void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
|
||||
/* mc3_decoder */
|
||||
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
|
||||
/* fadpcm_decoder */
|
||||
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* asf_decoder */
|
||||
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* dsa_decoder */
|
||||
void decode_dsa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* xmd_decoder */
|
||||
void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
|
||||
void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
|
||||
|
||||
/* derf_decoder */
|
||||
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
@ -183,7 +183,7 @@ void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci
|
||||
size_t oki_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
/* ptadpcm_decoder */
|
||||
void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
|
||||
void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
|
||||
size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size);
|
||||
|
||||
/* ubi_adpcm_decoder */
|
||||
|
@ -1,52 +1,53 @@
|
||||
#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;
|
||||
}
|
||||
#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) {
|
||||
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 index, shift, coef;
|
||||
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; /* for flat layout */
|
||||
|
||||
/* 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 */
|
||||
index = ((frame[0] >> 0) & 0xf);
|
||||
shift = 12 - ((frame[0] >> 4) & 0xf);
|
||||
coef = dsa_coefs[index];
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
sample = i&1 ? /* high nibble first */
|
||||
(nibbles >> 0) & 0xf :
|
||||
(nibbles >> 4) & 0xf;
|
||||
sample = ((int16_t)(sample << 12) >> shift); /* 16b sign extend + scale */
|
||||
sample = sample + ((hist1 * coef) >> 16);
|
||||
|
||||
outbuf[sample_count] = (sample_t)(sample << 2);
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
}
|
||||
|
@ -1,81 +1,78 @@
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* FADPCM table */
|
||||
static const int8_t fadpcm_coefs[8][2] = {
|
||||
{ 0 , 0 },
|
||||
{ 60 , 0 },
|
||||
{ 122 , 60 },
|
||||
{ 115 , 52 },
|
||||
{ 98 , 55 },
|
||||
{ 0 , 0 },
|
||||
{ 0 , 0 },
|
||||
{ 0 , 0 },
|
||||
};
|
||||
|
||||
/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout.
|
||||
* Code/layout could be simplified but tries to emulate FMOD's code.
|
||||
* Algorithm and tables debugged from their PC DLLs (byte-accurate). */
|
||||
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
off_t frame_offset;
|
||||
int i, j, k;
|
||||
int block_samples, frames_in, samples_done = 0, sample_count = 0;
|
||||
uint32_t coefs, shifts;
|
||||
int32_t hist1; //= stream->adpcm_history1_32;
|
||||
int32_t hist2; //= stream->adpcm_history2_32;
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
block_samples = (0x8c - 0xc) * 2;
|
||||
frames_in = first_sample / block_samples;
|
||||
first_sample = first_sample % block_samples;
|
||||
|
||||
frame_offset = stream->offset + 0x8c*frames_in;
|
||||
|
||||
|
||||
/* parse 0xc header (header samples are not written to outbuf) */
|
||||
coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile);
|
||||
shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile);
|
||||
hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile);
|
||||
hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile);
|
||||
|
||||
|
||||
/* decode nibbles, grouped in 8 sets of 0x10 * 0x04 * 2 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
int32_t coef1, coef2, shift, coef_index, shift_factor;
|
||||
off_t group_offset = frame_offset + 0x0c + 0x10*i;
|
||||
|
||||
/* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */
|
||||
coef_index = ((coefs >> i*4) & 0x0f) % 0x07;
|
||||
shift_factor = (shifts >> i*4) & 0x0f;
|
||||
|
||||
coef1 = fadpcm_coefs[coef_index][0];
|
||||
coef2 = fadpcm_coefs[coef_index][1];
|
||||
shift = 0x16 - shift_factor; /* pre-adjust for 32b sign extend */
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile);
|
||||
|
||||
for (k = 0; k < 8; k++) {
|
||||
int32_t new_sample;
|
||||
|
||||
new_sample = (nibbles >> k*4) & 0x0f;
|
||||
new_sample = (new_sample << 28) >> shift; /* 32b sign extend + scale */
|
||||
new_sample = (new_sample - hist2*coef2 + hist1*coef1);
|
||||
new_sample = new_sample >> 6;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = new_sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//stream->adpcm_history1_32 = hist1;
|
||||
//stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* tweaked XA/PSX coefs << 6 */
|
||||
static const int8_t fadpcm_coefs[8][2] = {
|
||||
{ 0, 0 },
|
||||
{ 60, 0 },
|
||||
{ 122, 60 },
|
||||
{ 115, 52 },
|
||||
{ 98, 55 },
|
||||
/* rest is 0s */
|
||||
};
|
||||
|
||||
/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout.
|
||||
* Code/layout could be simplified but tries to emulate FMOD's code.
|
||||
* Algorithm and tables debugged from their PC DLLs (byte-accurate). */
|
||||
void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x8c] = {0};
|
||||
off_t frame_offset;
|
||||
int i, j, k, frames_in, sample_count = 0, samples_done = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
uint32_t coefs, shifts;
|
||||
int32_t hist1; //= stream->adpcm_history1_32;
|
||||
int32_t hist2; //= stream->adpcm_history2_32;
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x8c;
|
||||
samples_per_frame = (bytes_per_frame - 0xc) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse 0xc header (header samples are not written to outbuf) */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coefs = get_u32le(frame + 0x00);
|
||||
shifts = get_u32le(frame + 0x04);
|
||||
hist1 = get_s16le(frame + 0x08);
|
||||
hist2 = get_s16le(frame + 0x0a);
|
||||
|
||||
|
||||
/* decode nibbles, grouped in 8 sets of 0x10 * 0x04 * 2 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
int index, shift, coef1, coef2;
|
||||
|
||||
/* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */
|
||||
index = ((coefs >> i*4) & 0x0f) % 0x07;
|
||||
shift = (shifts >> i*4) & 0x0f;
|
||||
|
||||
coef1 = fadpcm_coefs[index][0];
|
||||
coef2 = fadpcm_coefs[index][1];
|
||||
shift = 22 - shift; /* pre-adjust for 32b sign extend */
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
uint32_t nibbles = get_u32le(frame + 0x0c + 0x10*i + 0x04*j);
|
||||
|
||||
for (k = 0; k < 8; k++) {
|
||||
int32_t sample;
|
||||
|
||||
sample = (nibbles >> k*4) & 0x0f;
|
||||
sample = (sample << 28) >> shift; /* 32b sign extend + scale */
|
||||
sample = (sample - hist2*coef2 + hist1*coef1) >> 6;
|
||||
sample = clamp16(sample);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//stream->adpcm_history1_32 = hist1;
|
||||
//stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
@ -1,50 +1,60 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* lsf ADPCM, as seen in Fastlane Street Racing */
|
||||
|
||||
static const short lsf_coefs[5][2] = {
|
||||
{0x73, -0x34},
|
||||
{0, 0},
|
||||
{0x62, -0x37},
|
||||
{0x3C, 0},
|
||||
{0x7A, -0x3c}
|
||||
/* tweaked XA/PSX coefs << 6 */
|
||||
static const short lsf_coefs[16][2] = {
|
||||
{ 115, -52 },
|
||||
{ 0, 0 },
|
||||
{ 98, -55 },
|
||||
{ 60, 0 },
|
||||
{ 122, -60 },
|
||||
/* rest assumed to be 0s */
|
||||
};
|
||||
|
||||
void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i=first_sample;
|
||||
int32_t sample_count;
|
||||
const int bytes_per_frame = 0x1c;
|
||||
const int samples_per_frame = (bytes_per_frame-1)*2;
|
||||
|
||||
int framesin = first_sample/samples_per_frame;
|
||||
|
||||
uint8_t q = 0xFF - read_8bit(framesin*bytes_per_frame + stream->offset,stream->streamfile);
|
||||
int scale = (q&0xF0)>>4;
|
||||
int coef_idx = q&0x0F;
|
||||
void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x1c] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
int index, shift, coef1, coef2;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_16;
|
||||
int32_t hist2 = stream->adpcm_history2_16;
|
||||
uint8_t header;
|
||||
|
||||
first_sample = first_sample%samples_per_frame;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(framesin*bytes_per_frame+stream->offset+1+i/2,stream->streamfile);
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x1c;
|
||||
samples_per_frame = (bytes_per_frame - 1) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
//first_sample = first_sample % samples_per_frame; /* for flat layout */
|
||||
|
||||
int32_t prediction =
|
||||
(hist1 * lsf_coefs[coef_idx][0] +
|
||||
hist2 * lsf_coefs[coef_idx][1]) / 0x40;
|
||||
/* external interleave (fixed size), mono */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
header = 0xFF - frame[0x00];
|
||||
shift = (header >> 4) & 0xf;
|
||||
index = (header >> 0) & 0xf;
|
||||
coef1 = lsf_coefs[index][0];
|
||||
coef2 = lsf_coefs[index][1];
|
||||
|
||||
prediction += (i&1?
|
||||
get_high_nibble_signed(sample_byte):
|
||||
get_low_nibble_signed(sample_byte)
|
||||
) * (1 << (12-scale));
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
prediction = clamp16(prediction);
|
||||
sample = i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles) :
|
||||
get_low_nibble_signed(nibbles);
|
||||
sample = sample * (1 << (12 - shift));
|
||||
sample = sample + (hist1 * coef1 + hist2 * coef2) / 64; /* >> 6 */
|
||||
sample = clamp16(sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = prediction;
|
||||
|
||||
outbuf[sample_count] = prediction;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16 = hist1;
|
||||
|
@ -1,149 +1,153 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* MTA2 decoder based on:
|
||||
* - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1]
|
||||
* - Solid4 tools: https://github.com/GHzGangster/Drebin
|
||||
*
|
||||
* MTA2 layout:
|
||||
* - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams
|
||||
* ex: 8ch: track0 4ch + track1 4ch + track0 4ch + track1 4ch ...; or 2ch = 1ch track0 + 1ch track1
|
||||
* * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience)
|
||||
* - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4)
|
||||
* ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?)
|
||||
*
|
||||
* Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls
|
||||
* but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and
|
||||
* expects samples_to_do to be block_samples at most (could be simplified, I guess).
|
||||
*/
|
||||
|
||||
/* coefs table (extended XA filters) */
|
||||
static const int mta2_coefs1[8] = {
|
||||
0, 240, 460, 392, 488, 460, 460, 240
|
||||
};
|
||||
static const int mta2_coefs2[8] = {
|
||||
0, 0, -208, -220, -240, -240, -220, -104
|
||||
};
|
||||
/* shift table */
|
||||
static const int mta2_shifts[32] = {
|
||||
256, 335, 438, 573, 749, 979, 1281, 1675,
|
||||
2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327,
|
||||
18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568,
|
||||
160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576
|
||||
};
|
||||
|
||||
/* expands nibble */
|
||||
static short mta2_expand_nibble(int nibble, short hist1, short hist2, int coef_index, int shift_index) {
|
||||
int output;
|
||||
if (nibble > 7) /* sign extend */
|
||||
nibble = nibble - 16;
|
||||
|
||||
output = (hist1 * mta2_coefs1[coef_index] + hist2 * mta2_coefs2[coef_index] + (nibble * mta2_shifts[shift_index]) + 128) >> 8;
|
||||
output = clamp16(output);
|
||||
return (short)output;
|
||||
}
|
||||
|
||||
/* decodes a block for a channel */
|
||||
void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
|
||||
int i, group, row, col;
|
||||
int track_channels = 0, track_channel;
|
||||
|
||||
|
||||
/* track skip */
|
||||
do {
|
||||
int num_track = 0, channel_layout;
|
||||
|
||||
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */
|
||||
num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */
|
||||
/* 0x01(3): num_frame (0=first) */
|
||||
/* 0x04(1): 0? */
|
||||
channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */
|
||||
frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */
|
||||
/* 0x08(8): null */
|
||||
|
||||
|
||||
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset);
|
||||
/* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others)
|
||||
* negative track only happens for truncated files (EOF) */
|
||||
if (frame_size == 0 || num_track < 0) {
|
||||
for (i = 0; i < samples_to_do; i++)
|
||||
outbuf[i * channelspacing] = 0;
|
||||
stream->offset += 0x10;
|
||||
return;
|
||||
}
|
||||
|
||||
track_channels = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((channel_layout >> i) & 0x01)
|
||||
track_channels++;
|
||||
}
|
||||
|
||||
if (track_channels == 0) { /* bad data, avoid div by 0 */
|
||||
VGM_LOG("track_channels 0 at %x\n", (uint32_t)stream->offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */
|
||||
if (channel / track_channels == num_track)
|
||||
break; /* channel belongs to this track */
|
||||
|
||||
/* keep looping for our track */
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
while (1);
|
||||
|
||||
track_channel = channel % track_channels;
|
||||
channel_block_samples = (0x80*2);
|
||||
channel_first_sample = first_sample % (0x80*2);
|
||||
|
||||
|
||||
/* parse channel frame (header 0x04*4 + data 0x20*4) */
|
||||
for (group = 0; group < 4; group++) {
|
||||
short hist2, hist1, coefs, shift, output;
|
||||
int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile);
|
||||
hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
|
||||
hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
|
||||
coefs = (group_header >> 5) & 0x7; /* mid 3b */
|
||||
shift = group_header & 0x1f; /* lower 5b */
|
||||
|
||||
/* write header samples (skips the last 2 group nibbles), like Drebin's decoder
|
||||
* last 2 nibbles and next 2 header hist should match though */
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = hist2;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = hist1;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
for (row = 0; row < 8; row++) {
|
||||
for (col = 0; col < 4*2; col++) {
|
||||
uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile);
|
||||
int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */
|
||||
output = mta2_expand_nibble((nibbles >> nibble_shift) & 0xf, hist1, hist2, coefs, shift);
|
||||
|
||||
/* ignore last 2 nibbles (uses first 2 header samples) */
|
||||
if (row < 7 || col < 3*2) {
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = output;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* block fully done */
|
||||
if (channel_first_sample + samples_done == channel_block_samples) {
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* MTA2 decoder based on:
|
||||
* - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1]
|
||||
* - Solid4 tools: https://github.com/GHzGangster/Drebin
|
||||
* (PS3 probably uses floats, so this may not be 100% accurate)
|
||||
*
|
||||
* MTA2 layout:
|
||||
* - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams
|
||||
* ex: 8ch: track0 4ch + track1 4ch + track0 4ch + track1 4ch ...; or 2ch = 1ch track0 + 1ch track1
|
||||
* * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience)
|
||||
* - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4)
|
||||
* ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?)
|
||||
*
|
||||
* Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls
|
||||
* but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and
|
||||
* expects samples_to_do to be block_samples at most (could be simplified, I guess).
|
||||
*/
|
||||
|
||||
/* tweaked XA/PSX coefs << 8 */
|
||||
static const int16_t mta2_coefs[8][2] = {
|
||||
{ 0, 0 },
|
||||
{ 240, 0 },
|
||||
{ 460, -208 },
|
||||
{ 392, -220 },
|
||||
{ 488, -240 },
|
||||
{ 460, -240 },
|
||||
{ 460, -220 },
|
||||
{ 240, -104 }
|
||||
};
|
||||
|
||||
static const int mta2_scales[32] = {
|
||||
256, 335, 438, 573, 749, 979, 1281, 1675,
|
||||
2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327,
|
||||
18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568,
|
||||
160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576
|
||||
};
|
||||
|
||||
/* decodes a block for a channel */
|
||||
void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x10 + 0x90*8] = {0};
|
||||
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
|
||||
int i, group, row, col;
|
||||
int track_channels = 0, track_channel;
|
||||
|
||||
|
||||
/* track skip */
|
||||
do {
|
||||
int num_track = 0, channel_layout;
|
||||
|
||||
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */
|
||||
read_streamfile(frame, stream->offset, 0x10, stream->streamfile); /* ignore EOF errors */
|
||||
num_track = get_u8 (frame + 0x00); /* 0=first */
|
||||
/* 0x01(3): num_frame (0=first) */
|
||||
/* 0x04(1): 0? */
|
||||
channel_layout = get_u8 (frame + 0x05); /* bitmask, see mta2.c */
|
||||
frame_size = get_u16be(frame + 0x06); /* not including this header */
|
||||
/* 0x08(8): null */
|
||||
|
||||
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset);
|
||||
/* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others)
|
||||
* negative track only happens for truncated files (EOF) */
|
||||
if (frame_size == 0 || num_track < 0) {
|
||||
for (i = 0; i < samples_to_do; i++)
|
||||
outbuf[i * channelspacing] = 0;
|
||||
stream->offset += 0x10;
|
||||
return;
|
||||
}
|
||||
|
||||
track_channels = 0;
|
||||
for (i = 0; i < 8; i++) { /* max 8ch */
|
||||
if ((channel_layout >> i) & 0x01)
|
||||
track_channels++;
|
||||
}
|
||||
|
||||
if (track_channels == 0) { /* bad data, avoid div by 0 */
|
||||
VGM_LOG("MTA2: track_channels 0 at %x\n", (uint32_t)stream->offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */
|
||||
if (channel / track_channels == num_track)
|
||||
break; /* channel belongs to this track */
|
||||
|
||||
/* keep looping for our track */
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
while (1);
|
||||
|
||||
/* parse stuff */
|
||||
read_streamfile(frame + 0x10, stream->offset + 0x10, frame_size, stream->streamfile); /* ignore EOF errors */
|
||||
track_channel = channel % track_channels;
|
||||
channel_block_samples = (0x80*2);
|
||||
channel_first_sample = first_sample % (0x80*2);
|
||||
|
||||
/* parse channel frame (header 0x04*4 + data 0x20*4) */
|
||||
for (group = 0; group < 4; group++) {
|
||||
short hist2, hist1, coefs, scale;
|
||||
uint32_t group_header = get_u32be(frame + 0x10 + track_channel*0x90 + group*0x4);
|
||||
hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
|
||||
hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
|
||||
coefs = (group_header >> 5) & 0x7; /* mid 3b */
|
||||
scale = group_header & 0x1f; /* lower 5b */
|
||||
|
||||
/* write header samples (skips the last 2 group nibbles), like Drebin's decoder
|
||||
* last 2 nibbles and next 2 header hist should match though */
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = hist2;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = hist1;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
/* decode nibbles */
|
||||
for (row = 0; row < 8; row++) {
|
||||
int pos = 0x10 + track_channel*0x90 + 0x10 + group*0x4 + row*0x10;
|
||||
for (col = 0; col < 4*2; col++) {
|
||||
uint8_t nibbles = frame[pos + col/2];
|
||||
int32_t sample;
|
||||
|
||||
sample = col&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles) :
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = sample * mta2_scales[scale];
|
||||
sample = (sample + hist1 * mta2_coefs[coefs][0] + hist2 * mta2_coefs[coefs][1] + 128) >> 8;
|
||||
sample = clamp16(sample);
|
||||
|
||||
/* ignore last 2 nibbles (uses first 2 header samples) */
|
||||
if (row < 7 || col < 3*2) {
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* block fully done */
|
||||
if (channel_first_sample + samples_done == channel_block_samples) {
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
* Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN).
|
||||
*/
|
||||
|
||||
static const int index_table[16] = {
|
||||
static const int mtaf_step_indexes[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
static const int16_t step_size[32][16] = {
|
||||
static const int16_t mtaf_step_sizes[32][16] = {
|
||||
{ 1, 5, 9, 13, 16, 20, 24, 28,
|
||||
-1, -5, -9, -13, -16, -20, -24, -28, },
|
||||
{ 2, 6, 11, 15, 20, 24, 29, 33,
|
||||
@ -81,52 +81,56 @@ static const int16_t step_size[32][16] = {
|
||||
};
|
||||
|
||||
|
||||
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int32_t sample_count;
|
||||
int i;
|
||||
int c = channel%2; /* global channel to track channel */
|
||||
void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x110] = {0};
|
||||
off_t frame_offset;
|
||||
int i, ch, sample_count = 0;
|
||||
size_t bytes_per_frame /*, samples_per_frame*/;
|
||||
int32_t hist = stream->adpcm_history1_16;
|
||||
int32_t step_idx = stream->adpcm_step_index;
|
||||
int32_t step_index = stream->adpcm_step_index;
|
||||
|
||||
|
||||
/* read header when we hit a new track every 0x100 samples */
|
||||
first_sample = first_sample % 0x100;
|
||||
/* special stereo interleave, stereo */
|
||||
bytes_per_frame = 0x10 + 0x80*2;
|
||||
//samples_per_frame = (bytes_per_frame - 0x10) / 2 * 2; /* 256 */
|
||||
ch = channel % 2; /* global channel to track channel */
|
||||
//first_sample = first_sample % samples_per_frame; /* for flat layout */
|
||||
|
||||
/* read frame */
|
||||
frame_offset = stream->offset;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
/* parse frame header when we hit a new track every frame samples */
|
||||
if (first_sample == 0) {
|
||||
/* 0x10 header: track (8b, 0=first), track count (24b, 1=first), step-L, step-R, hist-L, hist-R */
|
||||
int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */
|
||||
int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */
|
||||
step_index = get_s16le(frame + 0x04 + 0x00 + ch*0x02); /* step-L/R */
|
||||
hist = get_s16le(frame + 0x04 + 0x04 + ch*0x04); /* hist-L/R: hist 16bit + empty 16bit */
|
||||
|
||||
VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%x\n", (uint32_t)stream->offset);
|
||||
/* avoid index out of range in corrupt files */
|
||||
if (init_idx < 0) {
|
||||
init_idx = 0;
|
||||
} else if (init_idx > 31) {
|
||||
init_idx = 31;
|
||||
VGM_ASSERT(step_index < 0 || step_index > 31, "MTAF: bad header idx at 0x%x\n", (uint32_t)stream->offset);
|
||||
if (step_index < 0) {
|
||||
step_index = 0;
|
||||
} else if (step_index > 31) {
|
||||
step_index = 31;
|
||||
}
|
||||
|
||||
step_idx = init_idx;
|
||||
hist = init_hist;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x10 + 0x80*ch + i/2];
|
||||
uint8_t nibble = (nibbles >> (!(i&1)?0:4)) & 0xf; /* lower first */
|
||||
|
||||
/* skip to nibble */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t byte = read_8bit(stream->offset + 0x10 + 0x80*c + i/2, stream->streamfile);
|
||||
uint8_t nibble = (byte >> (!(i&1)?0:4)) & 0xf; /* lower first */
|
||||
|
||||
hist = clamp16(hist+step_size[step_idx][nibble]);
|
||||
hist = clamp16(hist + mtaf_step_sizes[step_index][nibble]);
|
||||
outbuf[sample_count] = hist;
|
||||
sample_count += channelspacing;
|
||||
|
||||
step_idx += index_table[nibble];
|
||||
if (step_idx < 0) { /* clip step */
|
||||
step_idx = 0;
|
||||
} else if (step_idx > 31) {
|
||||
step_idx = 31;
|
||||
step_index += mtaf_step_indexes[nibble];
|
||||
if (step_index < 0) {
|
||||
step_index = 0;
|
||||
} else if (step_index > 31) {
|
||||
step_index = 31;
|
||||
}
|
||||
}
|
||||
|
||||
/* update state */
|
||||
stream->adpcm_step_index = step_idx;
|
||||
stream->adpcm_step_index = step_index;
|
||||
stream->adpcm_history1_16 = hist;
|
||||
}
|
||||
|
@ -1,58 +1,62 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */
|
||||
|
||||
static const int8_t proc_coef[5][2] =
|
||||
{
|
||||
{0x00,0x00},
|
||||
{0x3C,0x00},
|
||||
{0x73,0xCC},
|
||||
{0x62,0xC9},
|
||||
{0x7A,0xC4},
|
||||
/* standard XA/PSX coefs << 6 */
|
||||
static const int8_t proc_coefs[16][2] = {
|
||||
{ 0, 0 },
|
||||
{ 60, 0 },
|
||||
{ 115, -52 },
|
||||
{ 98, -55 },
|
||||
{ 122, -60 },
|
||||
/* rest is 0s */
|
||||
};
|
||||
|
||||
void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i=first_sample;
|
||||
int32_t sample_count;
|
||||
|
||||
int framesin = first_sample/30;
|
||||
|
||||
uint8_t header = read_8bit(framesin*16+15+stream->offset,stream->streamfile) ^ 0x80;
|
||||
int scale = 12 - (header & 0xf);
|
||||
int coef_index = (header >> 4) & 0xf;
|
||||
/* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */
|
||||
void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int index, scale, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int32_t coef1;
|
||||
int32_t coef2;
|
||||
uint8_t header;
|
||||
|
||||
if (coef_index > 4) coef_index = 0;
|
||||
coef1 = proc_coef[coef_index][0];
|
||||
coef2 = proc_coef[coef_index][1];
|
||||
first_sample = first_sample%30;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(framesin*16+stream->offset+i/2,stream->streamfile) ^ 0x80;
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* 30 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
|
||||
int32_t sample =
|
||||
(int32_t)
|
||||
(i&1?
|
||||
get_high_nibble_signed(sample_byte):
|
||||
get_low_nibble_signed(sample_byte)
|
||||
) * 64 * 64;
|
||||
/* 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 */
|
||||
header = frame[0x0F] ^ 0x80;
|
||||
scale = 12 - (header & 0xf);
|
||||
index = (header >> 4) & 0xf;
|
||||
coef1 = proc_coefs[index][0];
|
||||
coef2 = proc_coefs[index][1];
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[i/2] ^ 0x80;
|
||||
int32_t sample = 0;
|
||||
|
||||
sample = i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles) :
|
||||
get_low_nibble_signed(nibbles);
|
||||
sample = sample * 64 * 64; /* << 12 */
|
||||
if (scale < 0)
|
||||
{
|
||||
sample <<= -scale;
|
||||
}
|
||||
else
|
||||
sample >>= scale;
|
||||
|
||||
sample = (hist1 * coef1 + hist2 * coef2 + 32) / 64 + (sample * 64);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
hist1 = sample; /* clamp *after* this */
|
||||
|
||||
outbuf[sample_count] = clamp16((sample + 32) / 64) / 64 * 64;
|
||||
outbuf[sample_count] = clamp16((sample + 32) / 64) / 64 * 64;
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
|
@ -1,56 +1,66 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
const short afc_coef[16][2] =
|
||||
{{0,0},
|
||||
{0x0800,0},
|
||||
{0,0x0800},
|
||||
{0x0400,0x0400},
|
||||
{0x1000,0xf800},
|
||||
{0x0e00,0xfa00},
|
||||
{0x0c00,0xfc00},
|
||||
{0x1200,0xf600},
|
||||
{0x1068,0xf738},
|
||||
{0x12c0,0xf704},
|
||||
{0x1400,0xf400},
|
||||
{0x0800,0xf800},
|
||||
{0x0400,0xfc00},
|
||||
{0xfc00,0x0400},
|
||||
{0xfc00,0},
|
||||
{0xf800,0}};
|
||||
static const int16_t afc_coefs[16][2] = {
|
||||
{ 0, 0 },
|
||||
{ 2048, 0 },
|
||||
{ 0, 2048 },
|
||||
{ 1024, 1024 },
|
||||
{ 4096,-2048 },
|
||||
{ 3584,-1536 },
|
||||
{ 3072,-1024 },
|
||||
{ 4608,-2560 },
|
||||
{ 4200,-2248 },
|
||||
{ 4800,-2300 },
|
||||
{ 5120,-3072 },
|
||||
{ 2048,-2048 },
|
||||
{ 1024,-1024 },
|
||||
{-1024, 1024 },
|
||||
{-1024, 0 },
|
||||
{-2048, 0 }
|
||||
};
|
||||
|
||||
void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i=first_sample;
|
||||
int32_t sample_count;
|
||||
|
||||
int framesin = first_sample/16;
|
||||
|
||||
int8_t header = read_8bit(framesin*9+stream->offset,stream->streamfile);
|
||||
int32_t scale = 1 << ((header>>4) & 0xf);
|
||||
int coef_index = (header & 0xf);
|
||||
void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x09] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int index, scale, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_16;
|
||||
int32_t hist2 = stream->adpcm_history2_16;
|
||||
int coef1 = afc_coef[coef_index][0];
|
||||
int coef2 = afc_coef[coef_index][1];
|
||||
/*printf("offset: %x\nscale: %d\nindex: %d (%lf,%lf)\nhist: %d %d\n",
|
||||
(unsigned)stream->offset,scale,coef_index,coef1/2048.0,coef2/2048.0,hist1,hist2);*/
|
||||
|
||||
first_sample = first_sample%16;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(framesin*9+stream->offset+1+i/2,stream->streamfile);
|
||||
/* external interleave, mono */
|
||||
bytes_per_frame = 0x09;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 16 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
|
||||
outbuf[sample_count] = clamp16((
|
||||
(((i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale)<<11) +
|
||||
(coef1 * hist1 + coef2 * hist2))>>11
|
||||
);
|
||||
/*printf("%hd\n",outbuf[sample_count]);*/
|
||||
/* 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] >> 4) & 0xf);
|
||||
index = (frame[0] & 0xf);
|
||||
coef1 = afc_coefs[index][0];
|
||||
coef2 = afc_coefs[index][1];
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
sample = i&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles) :
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = ((sample * scale) << 11);
|
||||
sample = (sample + coef1*hist1 + coef2*hist2) >> 11;
|
||||
|
||||
sample = clamp16(sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16 = hist1;
|
||||
|
@ -2,63 +2,61 @@
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */
|
||||
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
/* standard XA coefs << 6 */
|
||||
static const int8_t dtk_coefs[16][2] = {
|
||||
{ 0, 0 },
|
||||
{ 60, 0 },
|
||||
{ 115, 52 },
|
||||
{ 98, 55 },
|
||||
/* rest assumed to be 0s */
|
||||
};
|
||||
|
||||
/* Nintendo GC Disc TracK streaming ADPCM (similar to XA) */
|
||||
void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x20] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
uint8_t coef_index, shift_factor;
|
||||
int index, shift, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), stereo */
|
||||
bytes_per_frame = 0x20;
|
||||
samples_per_frame = 28;
|
||||
samples_per_frame = (0x20 - 0x04); /* 28 for each channel */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame L/R header (repeated at 0x03/04) */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf;
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
index = (frame[channel] >> 4) & 0xf;
|
||||
shift = (frame[channel] >> 0) & 0xf;
|
||||
coef1 = dtk_coefs[index][0];
|
||||
coef2 = dtk_coefs[index][1];
|
||||
/* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */
|
||||
VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
VGM_ASSERT_ONCE(index > 4 || shift > 12, "DTK: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist = 0, new_sample;
|
||||
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile);
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int sample, hist;
|
||||
uint8_t nibbles = frame[0x04 + i];
|
||||
|
||||
/* apply XA filters << 6 */
|
||||
switch(coef_index) {
|
||||
case 0:
|
||||
hist = 0; // (hist1 * 0) - (hist2 * 0);
|
||||
break;
|
||||
case 1:
|
||||
hist = (hist1 * 60); // - (hist2 * 0);
|
||||
break;
|
||||
case 2:
|
||||
hist = (hist1 * 115) - (hist2 * 52);
|
||||
break;
|
||||
case 3:
|
||||
hist = (hist1 * 98) - (hist2 * 55);
|
||||
break;
|
||||
}
|
||||
hist = (hist + 32) >> 6;
|
||||
if (hist > 0x1fffff) hist = 0x1fffff;
|
||||
if (hist < -0x200000) hist = -0x200000;
|
||||
hist = (hist1*coef1 - hist2*coef2 + 32) >> 6;
|
||||
if (hist > 2097151) hist = 2097151;
|
||||
else if (hist < -2097152) hist = -2097152;
|
||||
|
||||
new_sample = (channel==0) ? /* L=low nibble first */
|
||||
sample = (channel==0) ? /* L=low nibble first */
|
||||
get_low_nibble_signed(nibbles) :
|
||||
get_high_nibble_signed(nibbles);
|
||||
new_sample = (new_sample << 12) >> shift_factor;
|
||||
new_sample = (new_sample << 6) + hist;
|
||||
sample = (sample << 12) >> shift;
|
||||
sample = (sample << 6) + hist;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
hist1 = sample; /* clamp *after* this so hist goes pretty high */
|
||||
|
||||
outbuf[sample_count] = clamp16(new_sample >> 6);
|
||||
outbuf[sample_count] = clamp16(sample >> 6);
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
|
@ -1,98 +1,112 @@
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* a somewhat IMA-like mix of step+next index all in one (maybe an array[][] originally?) */
|
||||
static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */
|
||||
-14, 2, -10, 2, -7, 1, -5, 1, -3, 0, -2, 0, -1, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 3, 0, 5, 1, 7, 1, 10, 2, 14, 2,
|
||||
-28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0,
|
||||
1, 0, 3, 1, 5, 1, 7, 1, 10, 2, 14, 2, 20, 3, 28, 3,
|
||||
-56, 4, -40, 4, -28, 3, -20, 3, -14, 2, -10, 2, -6, 2, -2, 1,
|
||||
2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4,
|
||||
-112, 5, -80, 5, -56, 4, -40, 4, -28, 3, -20, 3, -12, 3, -4, 2,
|
||||
4, 2, 12, 3, 20, 3, 28, 3, 40, 4, 56, 4, 80, 5, 112, 5,
|
||||
-224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3,
|
||||
8, 3, 24, 4, 40, 4, 56, 4, 80, 5, 112, 5, 160, 6, 224, 6,
|
||||
-448, 7, -320, 7, -224, 6, -160, 6, -112, 5, -80, 5, -48, 5, -16, 4,
|
||||
16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7,
|
||||
-896, 8, -640, 8, -448, 7, -320, 7, -224, 6, -160, 6, -96, 6, -32, 5,
|
||||
32, 5, 96, 6, 160, 6, 224, 6, 320, 7, 448, 7, 640, 8, 896, 8,
|
||||
-1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6,
|
||||
64, 6, 192, 7, 320, 7, 448, 7, 640, 8, 896, 8, 1280, 9, 1792, 9,
|
||||
-3584, 10, -2560, 10, -1792, 9, -1280, 9, -896, 8, -640, 8, -384, 8, -128, 7,
|
||||
128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10,
|
||||
-7168, 11, -5120, 11, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -768, 9, -256, 8,
|
||||
256, 8, 768, 9, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 5120, 11, 7168, 11,
|
||||
-14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9,
|
||||
512, 9, 1536, 10, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 10240, 11, 14336, 11,
|
||||
-28672, 11, -20480, 11, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3072, 11, -1024, 10,
|
||||
1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11,
|
||||
/* rest is 0s */
|
||||
};
|
||||
|
||||
/* Platinum "PtADPCM" custom ADPCM for Wwise (reverse engineered from .exes). */
|
||||
void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * 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;
|
||||
int index, step;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = 2 + (frame_size - 0x05) * 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);
|
||||
index = (uint8_t)read_8bit(frame_offset+0x04,stream->streamfile);
|
||||
|
||||
VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
/* 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+0x05 + i/2,stream->streamfile);
|
||||
uint8_t nibble;
|
||||
|
||||
nibble = !(i&1) ? /* low nibble first */
|
||||
(nibbles >> 0) & 0xF :
|
||||
(nibbles >> 4) & 0xF;
|
||||
|
||||
step = ptadpcm_table[2*(nibble + 16*index) + 0];
|
||||
index = ptadpcm_table[2*(nibble + 16*index) + 1];
|
||||
new_sample = clamp16(step + 2*hist1 - hist2);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = new_sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
|
||||
//stream->adpcm_history1_32 = hist1;
|
||||
//stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size) {
|
||||
if (channels <= 0 || frame_size < 0x06) return 0;
|
||||
return (bytes / channels / frame_size) * ((frame_size-0x05) * 2);
|
||||
}
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* a somewhat IMA-like mix of pre-calculated [index][nibble][step,index] all in one */
|
||||
static const int32_t ptadpcm_table[16][16][2] = {
|
||||
{
|
||||
{ -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 0}, { -2, 0}, { -1, 0}, { 0, 0},
|
||||
{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2},
|
||||
}, {
|
||||
{ -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 1}, { -1, 0},
|
||||
{ 1, 0}, { 3, 1}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3},
|
||||
}, {
|
||||
{ -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -6, 2}, { -2, 1},
|
||||
{ 2, 1}, { 6, 2}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4},
|
||||
}, {
|
||||
{ -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -12, 3}, { -4, 2},
|
||||
{ 4, 2}, { 12, 3}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5},
|
||||
}, {
|
||||
{ -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -24, 4}, { -8, 3},
|
||||
{ 8, 3}, { 24, 4}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 6},
|
||||
}, {
|
||||
{ -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -48, 5}, { -16, 4},
|
||||
{ 16, 4}, { 48, 5}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7},
|
||||
}, {
|
||||
{ -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -96, 6}, { -32, 5},
|
||||
{ 32, 5}, { 96, 6}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8},
|
||||
}, {
|
||||
{ -1792, 9}, { -1280, 9}, { -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -192, 7}, { -64, 6},
|
||||
{ 64, 6}, { 192, 7}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8}, { 1280, 9}, { 1792, 9},
|
||||
}, {
|
||||
{ -3584, 10}, { -2560, 10}, { -1792, 9}, { -1280, 9}, { -896, 8}, { -640, 8}, { -384, 8}, { -128, 7},
|
||||
{ 128, 7}, { 384, 8}, { 640, 8}, { 896, 8}, { 1280, 9}, { 1792, 9}, { 2560, 10}, { 3584, 10},
|
||||
}, {
|
||||
{ -7168, 11}, { -5120, 11}, { -3584, 10}, { -2560, 10}, {-1792, 9}, {-1280, 9}, { -768, 9}, { -256, 8},
|
||||
{ 256, 8}, { 768, 9}, { 1280, 9}, { 1792, 9}, { 2560, 10}, { 3584, 10}, { 5120, 11}, { 7168, 11},
|
||||
}, {
|
||||
{-14336, 11}, {-10240, 11}, { -7168, 11}, { -5120, 11}, {-3584, 10}, {-2560, 10}, {-1536, 10}, { -512, 9},
|
||||
{ 512, 9}, { 1536, 10}, { 2560, 10}, { 3584, 10}, { 5120, 11}, { 7168, 11}, {10240, 11}, {14336, 11},
|
||||
}, {
|
||||
{-28672, 11}, {-20480, 11}, {-14336, 11}, {-10240, 11}, {-7168, 11}, {-5120, 11}, {-3072, 11}, {-1024, 10},
|
||||
{ 1024, 10}, { 3072, 11}, { 5120, 11}, { 7168, 11}, {10240, 11}, {14336, 11}, {20480, 11}, {28672, 11},
|
||||
},
|
||||
/* rest is 0s (uses up to index 12) */
|
||||
};
|
||||
|
||||
/* Platinum "PtADPCM" custom ADPCM for Wwise (reverse engineered from .exes). */
|
||||
void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) {
|
||||
uint8_t frame[0x104] = {0};
|
||||
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;
|
||||
int index, nibble, step;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = 2 + (frame_size - 0x05) * 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;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
hist2 = get_s16le(frame + 0x00);
|
||||
hist1 = get_s16le(frame + 0x02);
|
||||
index = frame[0x04];
|
||||
|
||||
VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
/* 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++) {
|
||||
uint8_t nibbles = frame[0x05 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
nibble = !(i&1) ? /* low nibble first */
|
||||
(nibbles >> 0) & 0xF :
|
||||
(nibbles >> 4) & 0xF;
|
||||
|
||||
step = ptadpcm_table[index][nibble][0];
|
||||
index = ptadpcm_table[index][nibble][1];
|
||||
sample = clamp16(step + 2*hist1 - hist2);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
//stream->adpcm_history1_32 = hist1;
|
||||
//stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size) {
|
||||
if (channels <= 0 || frame_size < 0x06) return 0;
|
||||
return (bytes / channels / frame_size) * ((frame_size-0x05) * 2);
|
||||
}
|
||||
|
@ -1,63 +1,65 @@
|
||||
#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;
|
||||
}
|
||||
#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_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) {
|
||||
uint8_t frame[0x15] = {0};
|
||||
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; /* for flat layout */
|
||||
|
||||
/* 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 */
|
||||
hist2 = get_s16le(frame + 0x00);
|
||||
hist1 = get_s16le(frame + 0x02);
|
||||
scale = get_u16le(frame + 0x04); /* 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++) {
|
||||
uint8_t nibbles = frame[0x06 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
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 */
|
||||
sample = (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)sample;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
//stream->adpcm_history1_32 = hist1;
|
||||
//stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
@ -1,67 +1,53 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
/* .AFC - from Nintendo games [Super Mario Sunshine (GC), The Legend of Zelda: Wind Waker (GC)] */
|
||||
VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
int loop_flag;
|
||||
const int channel_count = 2; /* .afc seems to be stereo only */
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("afc",filename_extension(filename))) goto fail;
|
||||
|
||||
/* don't grab AIFF-C with .afc extension */
|
||||
if ((uint32_t)read_32bitBE(0x0,streamFile)==0x464F524D) /* FORM */
|
||||
/* checks */
|
||||
/* .afc: common
|
||||
* .stx: Pikmin (GC) */
|
||||
if (!check_extensions(streamFile, "afc,stx"))
|
||||
goto fail;
|
||||
|
||||
/* we will get a sample rate, that's as close to checking as I think
|
||||
* we can get */
|
||||
if (read_u32be(0x00, streamFile) > get_streamfile_size(streamFile)) /* size without padding */
|
||||
goto fail;
|
||||
|
||||
if (read_u16be(0x0a, streamFile) != 4) /* bps? */
|
||||
goto fail;
|
||||
if (read_u16be(0x0c, streamFile) != 16) /* samples per frame? */
|
||||
goto fail;
|
||||
/* 0x0e: always 0x1E? */
|
||||
|
||||
channel_count = 2;
|
||||
loop_flag = read_s32be(0x10, streamFile);
|
||||
start_offset = 0x20;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
loop_flag = read_32bitBE(0x10,streamFile);
|
||||
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
|
||||
vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile);
|
||||
/* channels and loop flag are set by allocate_vgmstream */
|
||||
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile);
|
||||
vgmstream->meta_type = meta_AFC;
|
||||
vgmstream->num_samples = read_s32be(0x04, streamFile);
|
||||
vgmstream->sample_rate = read_u16be(0x08, streamFile);
|
||||
vgmstream->loop_start_sample = read_s32be(0x14, streamFile);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_AFC;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_AFC;
|
||||
|
||||
/* frame-level interleave (9 bytes) */
|
||||
vgmstream->interleave_block_size = 9;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
STREAMFILE *chstreamfile;
|
||||
int i;
|
||||
|
||||
/* both channels use same buffer, as interleave is so small */
|
||||
chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!chstreamfile) goto fail;
|
||||
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = chstreamfile;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
0x20 + i*vgmstream->interleave_block_size;
|
||||
}
|
||||
}
|
||||
vgmstream->interleave_block_size = 0x09;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,59 +1,47 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* .lsf - Fastlane Street Racing (iPhone) */
|
||||
/* "!n1nj4n" */
|
||||
|
||||
/* .lsf - from Atod games [Fastlane Street Racing (iPhone), Chicane Street Racing prototype (Gizmondo)] */
|
||||
VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
size_t file_size;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
size_t file_size;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("lsf",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x0, streamFile) != 0x216E316E || // "!n1n"
|
||||
read_32bitBE(0x4, streamFile) != 0x6A346E00) // "j4n\0"
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "lsf"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00, streamFile) != 0x216E316E || // "!n1n"
|
||||
read_32bitBE(0x04, streamFile) != 0x6A346E00) // "j4n\0"
|
||||
goto fail;
|
||||
|
||||
/* check size */
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
if (read_32bitLE(0xC, streamFile) + 0x10 != file_size)
|
||||
if (read_32bitLE(0x0C, streamFile) + 0x10 != file_size)
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 1;
|
||||
start_offset = 0x10;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(1,0);
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = (file_size-0x10)/0x1c*0x1b*2;
|
||||
vgmstream->sample_rate = read_32bitLE(0x8, streamFile);
|
||||
|
||||
vgmstream->coding_type = coding_LSF;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_LSF_N1NJ4N;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08, streamFile);
|
||||
vgmstream->num_samples = (file_size-0x10)/0x1c*0x1b*2;
|
||||
vgmstream->coding_type = coding_LSF;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x1c;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[0].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[0].channel_start_offset=
|
||||
vgmstream->ch[0].offset=start_offset;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1,154 +1,135 @@
|
||||
#ifndef _MTA2_STREAMFILE_H_
|
||||
#define _MTA2_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int big_endian;
|
||||
uint32_t target_type;
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} mta2_io_data;
|
||||
|
||||
|
||||
static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
uint32_t block_type, block_size, block_track;
|
||||
|
||||
block_type = read_u32(data->physical_offset+0x00, streamfile); /* subtype and type */
|
||||
block_size = read_u32(data->physical_offset+0x04, streamfile);
|
||||
//block_unk = read_u32(data->physical_offset+0x08, streamfile); /* usually 0 except for 0xF0 'end' block */
|
||||
block_track = read_u32(data->physical_offset+0x0c, streamfile);
|
||||
|
||||
if (block_type != data->target_type || block_size == 0xFFFFFFFF)
|
||||
break;
|
||||
|
||||
data->block_size = block_size;
|
||||
data->skip_size = 0x10;
|
||||
data->data_size = block_size - data->skip_size;
|
||||
/* no audio data (padding block), but write first (header) */
|
||||
if (block_track == 0 && data->logical_offset > 0)
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
mta2_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
/* Handles removing KCE Japan-style blocks in MTA2 streams
|
||||
* (these blocks exist in most KCEJ games and aren't actually related to audio) */
|
||||
static STREAMFILE* setup_mta2_streamfile(STREAMFILE *streamFile, off_t stream_offset, int big_endian, const char* extension) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
mta2_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(mta2_io_data);
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
/* blocks must start with a 'new sub-stream' id */
|
||||
if (read_u32(stream_offset+0x00, streamFile) != 0x00000010)
|
||||
goto fail;
|
||||
|
||||
io_data.target_type = read_u32(stream_offset + 0x0c, streamFile);
|
||||
io_data.stream_offset = stream_offset + 0x10;
|
||||
io_data.stream_size = get_streamfile_size(streamFile) - io_data.stream_offset;
|
||||
io_data.big_endian = big_endian;
|
||||
io_data.logical_offset = -1; /* force phys offset reset */
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mta2_io_read,mta2_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
if (extension) {
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
}
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _MTA2_STREAMFILE_H_ */
|
||||
#ifndef _MTA2_STREAMFILE_H_
|
||||
#define _MTA2_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int big_endian;
|
||||
uint32_t target_type;
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} mta2_io_data;
|
||||
|
||||
|
||||
static size_t mta2_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
;VGM_LOG("IO restart: offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset);
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
uint32_t block_type, block_size, block_track;
|
||||
|
||||
block_type = read_u32(data->physical_offset+0x00, sf); /* subtype and type */
|
||||
block_size = read_u32(data->physical_offset+0x04, sf);
|
||||
//block_unk = read_u32(data->physical_offset+0x08, streamfile); /* usually 0 except for 0xF0 'end' block */
|
||||
block_track = read_u32(data->physical_offset+0x0c, sf);
|
||||
|
||||
if (block_type != data->target_type || block_size == 0xFFFFFFFF)
|
||||
break;
|
||||
|
||||
data->block_size = block_size;
|
||||
data->skip_size = 0x10;
|
||||
data->data_size = block_size - data->skip_size;
|
||||
/* no audio data (padding block), but write first (header) */
|
||||
if (block_track == 0 && data->logical_offset > 0)
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size > 0)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
mta2_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
/* Handles removing KCE Japan-style blocks in MTA2 streams
|
||||
* (these blocks exist in most KCEJ games and aren't actually related to audio) */
|
||||
static STREAMFILE* setup_mta2_streamfile(STREAMFILE *sf, off_t stream_offset, int big_endian, const char *extension) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
mta2_io_data io_data = {0};
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
/* blocks must start with a 'new sub-stream' id */
|
||||
if (read_u32(stream_offset+0x00, sf) != 0x00000010)
|
||||
return NULL;
|
||||
|
||||
io_data.target_type = read_u32(stream_offset + 0x0c, sf);
|
||||
io_data.stream_offset = stream_offset + 0x10;
|
||||
io_data.stream_size = get_streamfile_size(sf) - io_data.stream_offset;
|
||||
io_data.big_endian = big_endian;
|
||||
io_data.logical_offset = -1; /* force phys offset reset */
|
||||
|
||||
/* setup subfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(mta2_io_data), mta2_io_read, mta2_io_size);
|
||||
if (extension)
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, extension);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _MTA2_STREAMFILE_H_ */
|
||||
|
@ -1,54 +1,32 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* sadl (only the Professor Layton interleaved IMA version) */
|
||||
/* sadl - from DS games with Procyon Studio audio driver */
|
||||
VGMSTREAM * init_vgmstream_sadl(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
int coding_type;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("sad",filename_extension(filename))) goto fail;
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "sad"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x7361646c) /* "sadl" */
|
||||
goto fail;
|
||||
|
||||
/* check file size */
|
||||
if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile) )
|
||||
if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
/* check coding type */
|
||||
switch (read_8bit(0x33,streamFile)&0xf0)
|
||||
{
|
||||
case 0x70:
|
||||
coding_type = coding_IMA_int;
|
||||
break;
|
||||
case 0xb0:
|
||||
coding_type = coding_NDS_PROCYON;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = read_8bit(0x31,streamFile);
|
||||
channel_count = read_8bit(0x32,streamFile);
|
||||
start_offset = 0x100;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x100;
|
||||
vgmstream->channels = channel_count;
|
||||
|
||||
switch (read_8bit(0x33,streamFile) & 6)
|
||||
{
|
||||
switch (read_8bit(0x33,streamFile) & 6) {
|
||||
case 4:
|
||||
vgmstream->sample_rate = 32728;
|
||||
break;
|
||||
@ -59,52 +37,37 @@ VGMSTREAM * init_vgmstream_sadl(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_type;
|
||||
|
||||
if (coding_type == coding_IMA_int)
|
||||
vgmstream->num_samples =
|
||||
(read_32bitLE(0x40,streamFile)-start_offset)/channel_count*2;
|
||||
else if (coding_type == coding_NDS_PROCYON)
|
||||
vgmstream->num_samples =
|
||||
(read_32bitLE(0x40,streamFile)-start_offset)/channel_count/16*30;
|
||||
|
||||
vgmstream->interleave_block_size=0x10;
|
||||
|
||||
if (loop_flag)
|
||||
{
|
||||
if (coding_type == coding_IMA_int)
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count*2;
|
||||
else
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count/16*30;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
if (channel_count > 1)
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
else
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_SADL;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
switch(read_8bit(0x33,streamFile) & 0xf0) {
|
||||
case 0x70: /* Ni no Kuni (DS), Professor Layton and the Curious Village (DS), Soma Bringer (DS) */
|
||||
vgmstream->coding_type = coding_IMA_int;
|
||||
|
||||
}
|
||||
vgmstream->num_samples = (read_32bitLE(0x40,streamFile)-start_offset)/channel_count*2;
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count*2;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
break;
|
||||
|
||||
case 0xb0: /* Soma Bringer (DS), Rekishi Taisen Gettenka (DS) */
|
||||
vgmstream->coding_type = coding_NDS_PROCYON;
|
||||
|
||||
vgmstream->num_samples = (read_32bitLE(0x40,streamFile)-start_offset)/channel_count/16*30;
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count/16*30;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user