From c7aaabf573e1b042165f27f5f55ecd10bf18be2c Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 29 Jun 2019 13:24:53 +0200 Subject: [PATCH] Fix XVAG MPEG encoder delay/gapless looping --- src/coding/mpeg_custom_utils.c | 17 +++++++++++------ src/meta/xvag.c | 21 +++++++++++++++++++-- src/vgmstream.h | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c index f0d62fa3..f0c3a913 100644 --- a/src/coding/mpeg_custom_utils.c +++ b/src/coding/mpeg_custom_utils.c @@ -78,18 +78,23 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m //todo: test more: this improves the output, but seems formats aren't usually prepared // (and/or the num_samples includes all possible samples in file, so by discarding some it'll reach EOF) -#if 0 + /* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */ switch(data->type) { - case MPEG_AHX: data->skip_samples = 480; break; /* observed default */ - case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */ + //case MPEG_AHX: data->skip_samples = 480; break; /* observed default */ + //case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */ + /* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?) * contain audible garbage at the beginning, but it's actually there in-game too */ - case MPEG_FSB: data->skip_samples = 0; - default: break; + //case MPEG_FSB: data->skip_samples = 0; break; + + case MPEG_XVAG: /* set in header and needed for gapless looping */ + data->skip_samples = data->config.skip_samples; break; + default: + break; } data->samples_to_discard = data->skip_samples; -#endif + return 1; fail: diff --git a/src/meta/xvag.c b/src/meta/xvag.c index 1992619e..1fd65850 100644 --- a/src/meta/xvag.c +++ b/src/meta/xvag.c @@ -160,11 +160,28 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail; /* "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, xvag.big_endian, 1)) /*"mpin"*/ goto fail; - cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ + /* all layers/subsongs share the same config; not very useful but for posterity: + * - 0x00: mpeg version + * - 0x04: mpeg layer + * - 0x08: bit rate + * - 0x0c: sample rate + * - 0x10: some version? (0x01-0x03)? + * - 0x14: channels per stream? + * - 0x18: channels per stream or total channels? + * - 0x1c: fixed frame size (always CBR) + * - 0x20: encoder delay (usually but not always 1201) + * - 0x24: number of samples + * - 0x28: some size? + * - 0x2c: ? (0x02) + * - 0x30: ? (0x00, 0x80) + * - 0x34: data size + * (rest is padding) + * */ + cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); + cfg.skip_samples = read_32bit(chunk_offset+0x20,streamFile); cfg.interleave = cfg.chunk_size * xvag.factor; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); diff --git a/src/vgmstream.h b/src/vgmstream.h index 3ee515ad..56c3ab08 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -1030,6 +1030,7 @@ typedef struct { int interleave; /* size of stream interleave */ int encryption; /* encryption mode */ int big_endian; + int skip_samples; /* for AHX */ int cri_type; uint16_t cri_key1;