Fix multistream MPEG SCD [Final Fantaxy XIII-2 (PS3)]

This commit is contained in:
bnnm 2017-12-17 16:39:36 +01:00
parent 5cc215b408
commit b65765a56e
4 changed files with 64 additions and 12 deletions

View File

@ -18,6 +18,8 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
data->bitrate_per_frame = info.bit_rate;
data->sample_rate_per_frame = info.sample_rate;
/* extra checks per type */
@ -50,6 +52,7 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
break;
case MPEG_P3D:
case MPEG_SCD:
if (data->config.interleave <= 0)
goto fail; /* needs external fixed size */
break;
@ -127,9 +130,21 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
break;
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
current_data_size = data->config.interleave / 4; /* to ensure we don't feed mpg123 too much */
current_interleave = data->config.interleave; /* fixed interleave (0x400) */
//todo: last block is smaller that interleave, not sure how it's divided
case MPEG_SCD:
current_interleave = data->config.interleave;
#if 0 //todo
/* check if current interleave block is short */
{
off_t block_offset = stream->offset - stream->channel_start_offset;
size_t next_block = data->streams_size*data->config.interleave;
if (data->config.data_size && block_offset + next_block >= data->config.data_size) {
size_t short_interleave = (data->config.data_size % next_block) / data->streams_size;
current_interleave = short_interleave;
}
}
#endif
current_data_size = current_interleave;
break;
default: /* standard frames (CBR or VBR) */
@ -154,6 +169,26 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
/* skip interleave once block is done, if defined */
if (current_interleave && ((stream->offset - stream->channel_start_offset) % current_interleave == 0)) {
stream->offset += current_interleave * (data->streams_size-1); /* skip a block each stream */
#if 0 //todo fix last block interleave
int i;
/* skip other blocks one by one (to handle if next last block has short interleave) */
for (i = 0; i < data->streams_size - 1; i++) {
off_t block_offset = stream->offset - stream->channel_start_offset;
size_t next_block = data->streams_size*data->config.interleave;
if (data->config.data_size && block_offset + next_block >= data->config.data_size) {
/* other stream's data is in last block (short interleave) */
size_t short_interleave = (data->config.data_size % next_block) / data->streams_size;
stream->offset += short_interleave;
}
else {
/* other stream's data is in normal block (regular interleave) */
stream->offset += current_interleave;
}
}
#endif
}
return 1;

View File

@ -383,7 +383,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
if (!ms->buffer_full && stream->offset >= stream_size) {
VGM_LOG("MPEG: EOF found but more data is requested\n");
VGM_LOG("MPEG: EOF found but more data is requested in stream %i\n", num_stream);
goto decode_fail;
}
@ -603,7 +603,12 @@ long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000);
}
else {
return 0; /* a bit too complex for what is worth */
/* needed for SCD */
if (data->streams_size && data->bitrate_per_frame) {
return (int64_t)(bytes / data->streams_size) * data->sample_rate_per_frame * 8 / (data->bitrate_per_frame * 1000);
}
return 0;
}
}

View File

@ -12,9 +12,9 @@ static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, vo
VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset, tables_offset, headers_offset, meta_offset, post_meta_offset, stream_size;
off_t start_offset, tables_offset, headers_offset, meta_offset, post_meta_offset;
int headers_entries;
int32_t loop_start, loop_end;
int32_t stream_size, loop_start, loop_end;
int target_stream = streamFile->stream_index;
int loop_flag = 0, channel_count, codec_id, sample_rate;
@ -80,7 +80,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
meta_offset = read_32bit(headers_offset + (target_stream-1)*4,streamFile);
/** stream header **/
stream_size = read_32bit(meta_offset+0x00, streamFile);
stream_size = read_32bit(meta_offset+0x00,streamFile);
channel_count = read_32bit(meta_offset+0x04,streamFile);
sample_rate = read_32bit(meta_offset+0x08,streamFile);
codec_id = read_32bit(meta_offset+0x0c,streamFile);
@ -200,12 +200,16 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
}
break;
case 0x06: /* OGG [Final Fantasy XIII-2 (PS3), Final Fantasy XIV (PC)] */
case 0x06: /* OGG [Final Fantasy XIII-2 (PC), Final Fantasy XIV (PC)] */
goto fail; /* handled above */
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG [Final Fantasy XIII (PS3)] */
mpeg_codec_data *mpeg_data = NULL;
mpeg_custom_config cfg = {0};
cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)] */
cfg.data_size = stream_size;
/* Drakengard 3, some Kingdom Hearts */
if (vgmstream->sample_rate == 47999)
@ -213,10 +217,13 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
if (vgmstream->sample_rate == 44099)
vgmstream->sample_rate = 44100;
mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
mpeg_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg);
if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data;
vgmstream->layout_type = layout_none;
/* to setup initial interleave in vgmstream_open_stream */
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
vgmstream->num_samples -= vgmstream->num_samples%576;
@ -226,7 +233,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data);
vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576;
}
vgmstream->interleave_block_size = 0;
break;
}
#endif

View File

@ -880,6 +880,7 @@ typedef enum {
MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */
MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */
MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */
MPEG_SCD, /* N streams of fixed interleave (not frame-aligned) */
MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */
MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */
MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */
@ -894,6 +895,7 @@ typedef struct {
int channels; /* max channels */
int fsb_padding; /* fsb padding mode */
int chunk_size; /* size of a data portion */
int data_size; /* playable size */
int interleave; /* size of stream interleave */
int encryption; /* encryption mode */
int big_endian;
@ -939,6 +941,9 @@ typedef struct {
/* for internal use, assumed to be constant for all frames */
int channels_per_frame;
int samples_per_frame;
/* for some calcs */
int bitrate_per_frame;
int sample_rate_per_frame;
/* custom MPEG internals */
int custom; /* flag */