Improve SCHl MP2/EALayer3 V1a gapless

This commit is contained in:
bnnm 2019-11-06 23:06:21 +01:00
parent aa62f671a9
commit 6eecc5303d
3 changed files with 58 additions and 36 deletions

View File

@ -92,6 +92,14 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
data->skip_samples = data->config.skip_samples; break;
case MPEG_STANDARD:
data->skip_samples = data->config.skip_samples; break;
case MPEG_EA:
/* typical MP2 decoder delay, verified vs sx.exe, also SCHl blocks header takes discard
* samples into account (so block_samples+240*2+1 = total frame samples) */
if (info.layer == 2) {
data->skip_samples = 240*2 + 1;
}
/* MP3 probably uses 576 + 528+1 but no known games use it */
break;
default:
break;
}

View File

@ -112,12 +112,16 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset,
ok = ealayer3_parse_frame(data, -1, &ib, &eaf);
if (!ok) goto fail;
}
//;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */
;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */
*coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels;
data->samples_per_frame = eaf.mpeg1 ? 1152 : 576;
/* handled at frame start */
//data->skip_samples = 576 + 529;
//data->samples_to_discard = data->skip_samples;
/* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */
return 1;
@ -133,6 +137,16 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *
ealayer3_frame_t eaf_0, eaf_1;
/* the first frame samples must be discarded (verified vs sx.exe with a file without PCM blocks),
* but we can't set samples_to_discard since PCM blocks would be discarded
* SCHl block samples field takes into account this discard (its value already substracts this) */
if ((data->type == MPEG_EAL31 || data->type == MPEG_EAL31b) && ms->current_size_count == 0) {
/* seems true for MP2/576 frame samples too, though they are rare so it's hard to test */
ms->decode_to_discard += 529 + 576; /* standard MP3 decoder delay + 1 granule samples */
ms->current_size_count++;
}
/* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */
{
//;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset);
@ -317,7 +331,7 @@ static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf,
/* check PCM block */
if (eaf->v1_pcm_flag == 0xEE) {
fill_buf(ib, 32);
r_bits(is, 16,&eaf->v1_offset_samples); /* samples to discard of the next decoded (not PCM block) samples */
r_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */
r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */
eaf->pre_size += 2+2; /* 16b+16b */
@ -672,8 +686,7 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
}
/* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts).
* Meant to be written inmediatedly, as those PCM are parts that can be found after 1 decoded frame.
* (ex. EA-frame_gr0, PCM-frame_0, EA-frame_gr1, PCM-frame_1 actually writes PCM-frame_0+1, decode of EA-frame_gr0+1 + discard part */
* Seems to alter decoded sample buffer to handle encoder delay/padding in a twisted way. */
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf) {
mpeg_custom_stream *ms = data->streams[num_stream];
int channels_per_frame = ms->channels_per_frame;
@ -694,38 +707,34 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
if (eaf->v1_pcm_samples || eaf->v1_offset_samples) {
uint8_t* outbuf = ms->output_buffer + bytes_filled;
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size;
size_t decode_to_discard;
VGM_ASSERT(eaf->v1_offset_samples > 576, "EAL3: big discard %i at 0x%x\n", eaf->v1_offset_samples, (uint32_t)stream->offset);
VGM_ASSERT(eaf->v1_pcm_samples > 0x100, "EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_samples, (uint32_t)stream->offset);
VGM_ASSERT(eaf->v1_offset_samples > 0 && eaf->v1_pcm_samples == 0, "EAL3: offset_samples without pcm_samples\n"); /* not seen but could work */
//;VGM_LOG("EA EAL3 v1: off=%lx, discard=%x, pcm=%i, pcm_o=%lx\n",
// stream->offset, eaf->v1_offset_samples, eaf->v1_pcm_samples, pcm_offset);
//;VGM_LOG("EA EAL3 v1: offset=%lx + %x, offset_samples=%x, pcm_samples=%i, spf=%i\n",
// stream->offset, eaf->pre_size + eaf->common_size, eaf->v1_offset_samples, eaf->v1_pcm_samples, data->samples_per_frame);
/* V1 usually discards + copies samples at the same time
* V1b PCM block is in 'planar' format (ex. NFS:U PS3) */
/* V1b PCM block is in 'planar' format (ex. NFS:U PS3) */
ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile);
ms->samples_filled += eaf->v1_pcm_samples;
/* skip decoded samples as PCM block 'overwrites' them w/ special meanings */
{
size_t decode_to_discard = eaf->v1_offset_samples;
//TODO: we should put samples at offset but most EAL3 use it at first frame, which decodes ok, and rarely
// in the last frame [ex. Celebrity Sports Showdown], which is ~60-80 samples off (could click on segments?)
if (data->type == MPEG_EAL31) {
//todo should also discard v1_pcm_samples, but block layout samples may be exhausted
// and won't move (maybe new block if offset = new offset detected)
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples;
}
else {
VGM_ASSERT(decode_to_discard > 0, "EAL3: found offset_samples in V1b\n");
/* probably (576 or samples_per_frame - eaf->v1_offset_samples) but V1b seems to always use 0 and ~47 samples */
if (decode_to_discard == 0) /* seems ok (ex. comparing NFS:UC PS3 vs PC gets correct waveform this way) */
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; /* musn't discard pcm_number */
}
ms->decode_to_discard += decode_to_discard;
}
/* v1_offset_samples in V1a controls how the PCM block goes in the sample buffer. Value seems to start
* from frame samples end, taking into account that 1st frame discards 576+529 samples.
* ex. with 47 samples:
* - offset 47 puts block at sample 0 (at 576*2-47 w/o 576+529 discard),
* - offset 63 puts block at sample -16 (only 31 samples visible, so 576*2-63 w/o discard),
* - offset 0 seems to cause sample buffer overrun (at 576*2-0 = outside single frame buffer?)
* In V1b seems to works similarly but offset looks adjusted after initial discard (so offset 0 for first frame)
*
* This behaviour matters most in looping sfx (ex. Burnout Paradise), or tracks that start
* without silence (ex. NFS:UG2), and NFS:UC PS3 (EAL3v1b) vs PC (EAXAS) gets correct waveform this way */
decode_to_discard = eaf->v1_pcm_samples;
ms->decode_to_discard += decode_to_discard;
}
if (eaf->v2_extended_flag) {

View File

@ -6,7 +6,6 @@
void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
int new_schl = 0;
size_t block_size, block_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
@ -44,9 +43,21 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
block_samples = 0; /* layout ignores this */
}
/* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
if (block_id == 0x5343486C)
new_schl = 1;
/* "SCHl" start block, when decoding multi files pasted together */
if (block_id == 0x5343486C) {
switch(vgmstream->coding_type) {
case coding_MPEG_custom:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
case coding_MPEG_ealayer3:
/* need to reset MPEG decoder to reset discards and trailing samples in the buffers */
flush_mpeg(vgmstream->codec_data);
break;
default:
break;
}
}
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
if (block_id == 0x00000000)
@ -159,12 +170,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
}
/* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */
if (new_schl) {
flush_mpeg(vgmstream->codec_data);
}
break;
#endif
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */