mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-16 03:23:19 +01:00
Fix encoder delay/skip samples in: SCD/SGXD/RIFF/MSF ATRAC3/p, SGXD AC3
This commit is contained in:
parent
94b3854a87
commit
6a922be613
@ -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;
|
||||
|
@ -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
|
||||
|
129
src/meta/sgxd.c
129
src/meta/sgxd.c
@ -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,13 +108,18 @@ 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;
|
||||
}
|
||||
|
||||
@ -142,27 +127,34 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x04: { /* ATRAC3plus */
|
||||
at3_riff_info info;
|
||||
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
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;
|
||||
@ -171,12 +163,22 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x06: { /* AC3 */
|
||||
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
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;
|
||||
|
||||
}
|
||||
|
@ -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"*/
|
||||
/* 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_sample_skip = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
} else if (chunk_size == 0xc) {
|
||||
fact_sample_skip = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
}
|
||||
vgmstream->num_samples += fact_sample_skip;
|
||||
vgmstream->loop_start_sample += fact_sample_skip;
|
||||
vgmstream->loop_end_sample += fact_sample_skip;
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples);
|
||||
}
|
||||
/* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user