Fix encoder delay/skip samples in: SCD/SGXD/RIFF/MSF ATRAC3/p, SGXD AC3

This commit is contained in:
bnnm 2017-05-20 18:00:27 +02:00
parent 94b3854a87
commit 6a922be613
4 changed files with 91 additions and 118 deletions

View File

@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
/* byte flags, not in MSFv1 or v2
* 0x01/02/04/08: loop marker 0/1/2/3
* 0x10: "resample" loop option (may be active with no 0x01 flag set)
* 0x10: resample options (force 44/48khz)
* 0x20: VBR MP3
* 0x40: joint stereo MP3 (apparently interleaved stereo for other formats)
* 0x80+: (none/reserved) */
@ -104,17 +104,19 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[100];
int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples;
int32_t bytes, block_size, encoder_delay, joint_stereo;
block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels;
encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2)
max_samples = atrac3_bytes_to_samples(data_size, block_size);
joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */
joint_stereo = (codec_id==4); /* interleaved joint stereo (ch must be even) */
/* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent.
* Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter.
* (note that encoder may add a fade-in with looping/resampling enabled but should be skipped) */
encoder_delay = 1162;
vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_size) - encoder_delay;
if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */
vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks
/* make a fake riff so FFmpeg can parse the ATRAC3 */
bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
if (bytes <= 0) goto fail;
@ -124,10 +126,16 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = max_samples;
/* manually set skip_samples if FFmpeg didn't do it */
if (ffmpeg_data->skipSamples <= 0) {
ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay);
}
/* MSF loop/sample values are offsets so trickier to adjust the skip_samples but this seems correct */
if (loop_flag) {
vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size);
vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size);
vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size) /* - encoder_delay*/;
vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size) - encoder_delay;
}
break;

View File

@ -174,7 +174,6 @@ int read_fmt(int big_endian,
case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */
#endif /* defined */
fmt->coding_type = coding_FFmpeg;
fmt->block_size = 2048;
fmt->interleave = 0;
break;
#endif /* VGM_USE_FFMPEG */
@ -408,10 +407,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if ( !ffmpeg_data ) goto fail;
sample_count = ffmpeg_data->totalSamples; /* fact_sample_count */
/* the encoder introduces some garbage (usually silent) samples to skip before the stream
* loop values include the skip samples but fact_sample_count doesn't; add them back to fix some edge loops */
if (fact_sample_skip > 0)
sample_count += fact_sample_skip;
if (at3) {
/* the encoder introduces some garbage (not always silent) samples to skip before the stream */
/* manually set skip_samples if FFmpeg didn't do it */
if (ffmpeg_data->skipSamples <= 0) {
ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip);
}
/* RIFF loop/sample values are absolute (with skip samples), adjust */
if (loop_flag) {
loop_start_offset -= ffmpeg_data->skipSamples;
loop_end_offset -= ffmpeg_data->skipSamples;
}
}
}
break;
#endif

View File

@ -2,48 +2,27 @@
#include "../coding/coding.h"
/* utils to fix AT3 looping */
typedef struct {
int32_t fact_samples;
int32_t loop_start_sample;
int32_t loop_end_sample;
int32_t skip_samples;
} at3_riff_info;
static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset);
/* Sony's SGB+SGH / SGD / SGX (variations of the same format)
* PS3: Genji (SGX only), Folklore, Afrika, Tokyo Jungle
* PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2
*
* Contains header + chunks, usually:
* WAVE: stream(s) header of ADPCM, AC3, ATRAC3plus, etc
* NAME: stream name(s)
* WSUR, WMRK, BUSS: unknown
* RGND, SEQD: unknown (related to SE)
* Then data, containing the original header if applicable (ex. AT3 RIFF).
* The SGXD header has priority over it (ex. some ATRAC3plus files have 48000 while the data RIFF 44100)
*/
/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in:
* PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle
* PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, data_offset, chunk_offset;
size_t data_size;
int is_sgx, is_sgb;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
/* check extension, case insensitive */
/* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */
if (!check_extensions(streamFile,"sgx,sgd,sgb"))
goto fail;
is_sgx = check_extensions(streamFile,"sgx");
is_sgb = check_extensions(streamFile,"sgb");
//is_sgd = check_extensions(streamFile,"sgd");
/* SGB+SGH: use SGH as header; otherwise use the current file as header */
if (is_sgb) {
@ -69,14 +48,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
}
/* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
/* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */
if (is_sgx) { /* position after chunk+size */
if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */
chunk_offset = 0x18;
} else {
if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */
}
/* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */
/* check multi-streams (usually only SE containers; Puppeteer) */
total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
@ -107,7 +87,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
if (is_sgx) {
stream_offset = 0x0; /* TODO unknown (not seen multi SGX) */
stream_offset = 0x0;
} else{
stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader);
}
@ -128,41 +108,53 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SGXD;
/* needs -1 to match RIFF AT3's loop chunk
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */
if (vgmstream->loop_end_sample > 0)
vgmstream->loop_end_sample -= 1;
switch (type) {
case 0x03: /* PSX ADPCM */
case 0x03: /* PS-ADPCM */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (is_sgx || is_sgb) {
vgmstream->interleave_block_size = 0x10;
} else { //todo this only seems to happen with SFX
} else { /* this only seems to happen with SFX */
vgmstream->interleave_block_size = data_size;
}
break;
#ifdef VGM_USE_FFMPEG
case 0x04: { /* ATRAC3plus */
at3_riff_info info;
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
case 0x04: { /* ATRAC3plus */
ffmpeg_codec_data *ffmpeg_data;
/* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually fix looping due to FFmpeg bugs */
if (loop_flag && get_at3_riff_info(&info, streamFile, start_offset)) {
if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */
/* todo use "skip samples"; for now we just use absolute loop values */
vgmstream->loop_start_sample = info.loop_start_sample;
vgmstream->loop_end_sample = info.loop_end_sample;
vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */
/* manually read skip_samples if FFmpeg didn't do it */
if (ffmpeg_data->skipSamples <= 0) {
off_t chunk_offset;
size_t chunk_size, fact_skip_samples = 0;
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */
goto fail;
if (chunk_size == 0x8) {
fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
} else if (chunk_size == 0xc) {
fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
}
ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples);
}
/* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
break;
}
#endif
case 0x05: /* Short VAG ADPCM */
case 0x05: /* Short PS-ADPCM */
vgmstream->coding_type = coding_PSX_cfg;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4;
@ -170,13 +162,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
break;
#ifdef VGM_USE_FFMPEG
case 0x06: { /* AC3 */
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
case 0x06: { /* AC3 */
ffmpeg_codec_data *ffmpeg_data;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually set skip_samples if FFmpeg didn't do it */
if (ffmpeg_data->skipSamples <= 0) {
/* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples.
* Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */
ffmpeg_set_skip_samples(ffmpeg_data, 256);
}
/* SGXD loop/sample values are relative (without skip samples), no need to adjust */
break;
}
#endif
@ -197,50 +199,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/**
* AT3 RIFF headers have a "skip samples at the beginning" value that the decoder should use,
* and absolute loop values. However the SGXD header loop values assume those samples are skipped.
*
* FFmpeg doesn't support/export this, so we have to manually get the absolute values to fix looping.
*/
static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset) {
off_t chunk_offset;
size_t chunk_size;
memset(info, 0, sizeof(at3_riff_info));
if (read_32bitBE(offset+0x0,streamFile)!=0x52494646 /* "RIFF" */
&& read_32bitBE(offset+0x8,streamFile)!=0x57415645 ) /* "WAVE" */
goto fail;
/*"smpl"*/
if (!find_chunk_le(streamFile, 0x736D706C,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail;
if (read_32bitLE(chunk_offset+0x1C, streamFile)==0
|| read_32bitLE(chunk_offset+0x24+0x4, streamFile)!=0 )
goto fail;
info->loop_start_sample = read_32bitLE(chunk_offset+0x1C+0x8+0x8, streamFile);
info->loop_end_sample = read_32bitLE(chunk_offset+0x1C+0x8+0xc,streamFile);
/*"fact"*/
if (!find_chunk_le(streamFile, 0x66616374,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail;
if (chunk_size == 0x8) {
info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile);
info->skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
} else if (chunk_size == 0xc) {
info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile);
info->skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
} else {
goto fail;
}
/* found */
return 1;
fail:
/* not found */
return 0;
}

View File

@ -1,6 +1,5 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util.h"
/* Square-Enix SCD (FF XIII, XIV) */
@ -358,7 +357,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
int32_t bytes;
/* post_meta_offset+0x00: fmt0x166 header (BE), post_meta_offset+0x34: seek table */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, post_meta_offset,0x34, stream_size, streamFile, 1);
if (bytes <= 0) goto fail;
@ -378,30 +376,33 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
/* ATRAC3plus */ /* Lord of Arcana (PSP) */
{
ffmpeg_codec_data *ffmpeg_data = NULL;
off_t chunk_offset;
size_t chunk_size, fact_sample_skip = 0;
/* full riff header at start_offset/post_meta_offset (same) */
/* full RIFF header at start_offset/post_meta_offset (same) */
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ffmpeg_data->totalSamples;
vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
/* manually find encoder_delay to adjust samples since it's not properly used by FFmpeg */
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; /*"fact"*/
if (chunk_size == 0x8) {
fact_sample_skip = read_32bitLE(chunk_offset+0x4, streamFile);
} else if (chunk_size == 0xc) {
fact_sample_skip = read_32bitLE(chunk_offset+0x8, streamFile);
/* manually read skip_samples if FFmpeg didn't do it */
if (ffmpeg_data->skipSamples <= 0) {
off_t chunk_offset;
size_t chunk_size, fact_skip_samples = 0;
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */
goto fail;
if (chunk_size == 0x8) {
fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
} else if (chunk_size == 0xc) {
fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
}
ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples);
}
vgmstream->num_samples += fact_sample_skip;
vgmstream->loop_start_sample += fact_sample_skip;
vgmstream->loop_end_sample += fact_sample_skip;
/* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
}
break;