Modify SCD/P3D/FSB/XVAG interleave and fix short last interleave

Previously, the streams' offsets needed to be pre-adjusted with the
interleave; now assumes all streams start in the same offset (first
stream).
This simplifies short last interleaves (SCD/P3D) and makes
layout_mpeg_custom unnecessary (also allows theoretical variable-sized
interleaves).
This commit is contained in:
bnnm 2017-12-17 19:25:10 +01:00
parent bd88cc2a65
commit a3b991ac3f
10 changed files with 46 additions and 81 deletions

View File

@ -97,7 +97,9 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
mpeg_frame_info info;
size_t current_data_size = 0;
size_t current_padding = 0;
size_t current_interleave = 0;
size_t current_interleave_pre = 0; /* interleaved data size before current stream */
size_t current_interleave_post = 0; /* interleaved data size after current stream */
size_t current_interleave = 0; /* interleave in this block (usually this + pre + post = interleave*streams = block) */
/* Get data size to give to the decoder, per stream. Usually 1 frame at a time,
@ -105,11 +107,18 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
switch(data->type) {
case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */
current_data_size = data->config.chunk_size;
current_interleave = data->config.interleave; /* big interleave */
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_data_size = data->config.chunk_size;
break;
case MPEG_FSB: /* frames with padding + interleave */
current_interleave = data->config.interleave; /* constant for multi-stream FSbs (1 frame + padding) */
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
goto fail;
current_data_size = info.frame_size;
@ -122,9 +131,6 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
: 0;
}
/* frame interleave (ie. read 1 data-frame, skip 1 data-frame per stream) */
current_interleave = data->config.interleave; /* constant for multi-stream FSbs */
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08lx\n", data->streams_size, stream->offset);
break;
@ -132,18 +138,20 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
case MPEG_SCD:
current_interleave = data->config.interleave;
#if 0 //todo
#if 1
/* 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;
}
if (data->config.data_size && block_offset + next_block >= data->config.data_size)
current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/
}
#endif
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_data_size = current_interleave;
break;
@ -158,39 +166,26 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
goto fail;
}
/* This assumes all streams' offsets start in the first stream, and advances
* the 'full interleaved block' at once, ex:
* start at s0=0x00, s1=0x00, interleave=0x40 (block = 0x40*2=0x80)
* @0x00 read 0x40 of s0, skip 0x40 of s1 (block of 0x80 done) > new offset = 0x80
* @0x00 skip 0x40 of s0, read 0x40 of s1 (block of 0x80 done) > new offset = 0x800
*/
/* read single frame */
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
/* read chunk (skipping other interleaves if needed) */
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset + current_interleave_pre, current_data_size, stream->streamfile);
/* update offsets */
/* update offsets and skip other streams */
stream->offset += current_data_size + current_padding;
/* 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
/* skip rest of block (interleave per stream) once this stream's interleaved data is done, if defined */
if (current_interleave && ((stream->offset - stream->channel_start_offset + current_interleave_pre + current_interleave_post) % current_interleave == 0)) {
stream->offset += current_interleave_pre + current_interleave_post;
}
return 1;
fail:
return 0;

View File

@ -167,9 +167,6 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
}
/* write output */
config->interleave = data->config.interleave; /* for FSB */
return data;
fail:

View File

@ -570,9 +570,6 @@ static const layout_info layout_info_list[] = {
#ifdef VGM_USE_VORBIS
{layout_ogg_vorbis, "Ogg"},
#endif
#ifdef VGM_USE_MPEG
{layout_mpeg_custom, "Custom MPEG Audio"},
#endif
};
static const meta_info meta_info_list[] = {

View File

@ -287,9 +287,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
if (fsbh.mode & FSOUND_MPEG) {
/* FSB3: ?; FSB4: Shatter, Way of the Samurai 3/4, Forza Horizon 1/2, Dragon Age Origins */
#if defined(VGM_USE_MPEG)
mpeg_custom_config cfg;
mpeg_custom_config cfg = {0};
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 :
(fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
(fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0)));
@ -299,10 +298,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
/* both to setup initial interleave in vgmstream_open_stream */
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->layout_type = layout_none;
#else
goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */

View File

@ -265,10 +265,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
/* both to setup initial interleave in vgmstream_open_stream */
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->layout_type = layout_none;
break;
}
#endif

View File

@ -145,18 +145,15 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
#ifdef VGM_USE_MPEG
case 0x6D703300: { /* "mp3\0" (PS3) */
mpeg_custom_config cfg;
mpeg_custom_config cfg = {0};
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.interleave = 0x400;
cfg.data_size = data_size;
/* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
if (!vgmstream->codec_data) goto fail;
/* both to setup initial interleave in vgmstream_open_stream */
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->layout_type = layout_none;
break;
}
#endif

View File

@ -208,7 +208,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
mpeg_codec_data *mpeg_data = NULL;
mpeg_custom_config cfg = {0};
cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)] */
cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */
cfg.data_size = stream_size;
/* Drakengard 3, some Kingdom Hearts */
@ -221,11 +221,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
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;
/* to setup initial interleave in vgmstream_open_stream */
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); //todo test/fix
vgmstream->num_samples -= vgmstream->num_samples%576;
if (loop_flag) {
vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data);

View File

@ -82,25 +82,19 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
#ifdef VGM_USE_MPEG
case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */
mpeg_custom_config cfg;
int fixed_frame_size;
memset(&cfg, 0, sizeof(mpeg_custom_config));
mpeg_custom_config cfg = {0};
/* "mpin": mpeg info */
/* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/
fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile);
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1))
goto fail; /*"mpin"*/
cfg.chunk_size = fixed_frame_size;
cfg.interleave = fixed_frame_size * multiplier;
cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */
cfg.interleave = cfg.chunk_size * multiplier;
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
if (!vgmstream->codec_data) goto fail;
/* both to setup initial interleave in vgmstream_open_stream */
vgmstream->interleave_block_size = cfg.interleave;
vgmstream->layout_type = layout_mpeg_custom;
vgmstream->layout_type = layout_none;
break;
}
#endif

View File

@ -922,9 +922,6 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
break;
#ifdef VGM_USE_VORBIS
case layout_ogg_vorbis:
#endif
#ifdef VGM_USE_MPEG
case layout_mpeg_custom:
#endif
case layout_none:
render_vgmstream_nolayout(buffer,sample_count,vgmstream);

View File

@ -254,9 +254,6 @@ typedef enum {
#ifdef VGM_USE_VORBIS
layout_ogg_vorbis, /* ogg vorbis file */
#endif
#ifdef VGM_USE_MPEG
layout_mpeg_custom, /* usually straight data but may setup offset/interleave somehow */
#endif
} layout_t;
/* The meta type specifies how we know what we know about the file.