Fix EA-MT looping for all cases [SSX3 (PS2)]

This commit is contained in:
bnnm 2018-09-10 13:18:53 +02:00
parent 37d7fe83c4
commit c045d41670
5 changed files with 58 additions and 22 deletions

View File

@ -166,7 +166,8 @@ void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type, int reset_sample);
ea_mt_codec_data *init_ea_mt(int channels, int type);
ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets);
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
void reset_ea_mt(VGMSTREAM * vgmstream);
void flush_ea_mt(VGMSTREAM *vgmstream);

View File

@ -23,10 +23,12 @@ struct ea_mt_codec_data {
STREAMFILE *streamfile;
uint8_t buffer[UTK_BUFFER_SIZE];
off_t offset;
off_t loop_offset;
int loop_sample;
int pcm_blocks;
int samples_filled;
int samples_used;
int reset_sample;
int samples_done;
int samples_discard;
void* utk_context;
@ -34,8 +36,11 @@ struct ea_mt_codec_data {
static size_t ea_mt_read_callback(void *dest, int size, void *arg);
ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks) {
return init_ea_mt_loops(channels, pcm_blocks, 0, NULL);
}
ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks, int reset_sample) {
ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
ea_mt_codec_data *data = NULL;
int i;
@ -48,7 +53,9 @@ ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks, int reset_sample) {
utk_init(data[i].utk_context);
data[i].pcm_blocks = pcm_blocks;
data[i].reset_sample = reset_sample;
data[i].loop_sample = loop_sample;
if (loop_offsets)
data[i].loop_offset = loop_offsets[i];
utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
}
@ -75,9 +82,9 @@ void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, in
int samples_to_get = ch_data->samples_filled;
/* don't go past loop, to reset decoder */
if (ch_data->reset_sample > 0 && ch_data->samples_done < ch_data->reset_sample &&
ch_data->samples_done + samples_to_get > ch_data->reset_sample)
samples_to_get = ch_data->reset_sample - ch_data->samples_done;
if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample &&
ch_data->samples_done + samples_to_get > ch_data->loop_sample)
samples_to_get = ch_data->loop_sample - ch_data->samples_done;
if (ch_data->samples_discard) {
/* discard samples for looping */
@ -106,12 +113,14 @@ void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, in
/* Loops in EA-MT are done with fully separate intro/loop substreams. We must
* notify the decoder when a new substream begins (even with looping disabled). */
if (ch_data->reset_sample > 0 && ch_data->samples_done == ch_data->reset_sample) {
if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) {
ch_data->samples_filled = 0;
ch_data->samples_discard = 0;
/* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */
ch_data->offset = ch_data->loop_offset;
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */
//todo when loop start is < 432 decoder seems to have problems
}
}
else {
@ -146,11 +155,10 @@ static void flush_ea_mt_offsets(VGMSTREAM *vgmstream, int is_start, int samples_
data[i].offset = vgmstream->ch[i].channel_start_offset;
else
data[i].offset = vgmstream->ch[i].offset;
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
if (is_start) {
//utk_reset(ctx); //todo
utk_reset(ctx);
ctx->parsed_header = 0;
data[i].samples_done = 0;
}

View File

@ -90,6 +90,7 @@ typedef struct {
off_t offsets[EA_MAX_CHANNELS];
off_t coefs[EA_MAX_CHANNELS];
off_t loops[EA_MAX_CHANNELS];
int big_endian;
int loop_flag;
@ -594,7 +595,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
}
}
else if (vgmstream->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) {
/* pcstream+gcadpcm with sx.exe v2, this is probably an bug (even with this parts of the wave are off) */
/* pcstream+gcadpcm with sx.exe v2, this is probably a bug (even with this parts of the wave are off) */
int interleave = (vgmstream->num_samples / 14 * 8); /* full interleave */
for (i = 0; i < vgmstream->channels; i++) {
ea->offsets[i] = ea->offsets[0] + interleave*i;
@ -702,8 +703,15 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
use_pcm_blocks = 1;
}
/* make relative loops absolute for the decoder */
if (ea->loop_flag) {
for (i = 0; i < vgmstream->channels; i++) {
ea->loops[i] += ea->offsets[0];
}
}
vgmstream->coding_type = coding_EA_MT;
vgmstream->codec_data = init_ea_mt(vgmstream->channels, use_pcm_blocks, ea->loop_start);
vgmstream->codec_data = init_ea_mt_loops(vgmstream->channels, use_pcm_blocks, ea->loop_start, ea->loops);
if (!vgmstream->codec_data) goto fail;
break;
}
@ -861,6 +869,8 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
uint8_t patch_type = read_8bit(offset,streamFile);
offset++;
//;off_t test_offset = offset;
//;VGM_LOG("EA SCHl: patch=%02x at %lx, value=%x\n", patch_type, offset-1, read_patch(streamFile, &test_offset));
switch(patch_type) {
case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */
if (!is_header_end)
@ -873,7 +883,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x08: /* release envelope (BNK only) */
case 0x09: /* related to playback envelope (BNK only) */
case 0x0A: /* bend range (BNK only) */
case 0x0B: /* unknown (always 0x02) */
case 0x0B: /* bank channels (or, offsets[] size; defaults to 1 if not present, removed in sx.exe v3) */
case 0x0C: /* pan offset (BNK only) */
case 0x0D: /* random pan offset range (BNK only) */
case 0x0E: /* volume (BNK only) */
@ -885,19 +895,17 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x14: /* emdedded user data (free size/value) */
case 0x15: /* unknown, rare (BNK only) [Need for Speed: High Stakes (PS1)] */
case 0x19: /* related to playback envelope (BNK only) */
case 0x1A: /* unknown and very rare (BNK only) [SSX 3 (PS2)] */
case 0x1B: /* unknown (movie only?) */
case 0x1C: /* initial envelope volume (BNK only) */
case 0x1D: /* unknown, rare [NASCAR 06 (Xbox)] */
case 0x1E:
case 0x1E: /* related to ch1? (BNK only) */
case 0x1F:
case 0x20:
case 0x21:
case 0x21: /* related to ch2? (BNK only) */
case 0x22:
case 0x23:
case 0x24: /* master random detune range (BNK only) */
case 0x25: /* unknown */
case 0x26: /* unknown, rare [FIFA 07 (Xbox)] */
read_patch(streamFile, &offset);
break;
@ -933,7 +941,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
ea->loop_start = read_patch(streamFile, &offset);
break;
case 0x87: /* loop end sample */
ea->loop_end = read_patch(streamFile, &offset);
ea->loop_end = read_patch(streamFile, &offset) + 1; /* sx.exe does +1 */
break;
/* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */
@ -981,6 +989,25 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
read_patch(streamFile, &offset);
break;
case 0x1A: /* EA-MT/EA-XA relative loop offset of ch1 */
ea->loops[0] = read_patch(streamFile, &offset);
break;
case 0x26: /* EA-MT/EA-XA relative loop offset of ch2 */
ea->loops[1] = read_patch(streamFile, &offset);
break;
case 0x27: /* EA-MT/EA-XA relative loop offset of ch3 */
ea->loops[2] = read_patch(streamFile, &offset);
break;
case 0x28: /* EA-MT/EA-XA relative loop offset of ch4 */
ea->loops[3] = read_patch(streamFile, &offset);
break;
case 0x29: /* EA-MT/EA-XA relative loop offset of ch5 */
ea->loops[4] = read_patch(streamFile, &offset);
break;
case 0x2a: /* EA-MT/EA-XA relative loop offset of ch6 */
ea->loops[5] = read_patch(streamFile, &offset);
break;
case 0x8A: /* long padding (always 0x00000000) */
case 0x8B: /* also padding? [Need for Speed: Hot Pursuit 2 (PC)] */
case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */

View File

@ -86,7 +86,7 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
case 0x02: /* EA-MT (voices) */
vgmstream->coding_type = coding_EA_MT;
vgmstream->layout_type = layout_none;
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0, 0);
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0);
if (!vgmstream->codec_data) goto fail;
vgmstream->num_samples = read_32bitLE(start_offset,streamFile);

View File

@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_utk(STREAMFILE *streamFile) {
vgmstream->num_samples = read_32bitLE(0x04, streamFile) / 2; /* PCM size */
vgmstream->coding_type = coding_EA_MT;
vgmstream->layout_type = layout_none;
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0, 0);
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0);
if (!vgmstream->codec_data) goto fail;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))