From 6823642302c34504f5425b57b23bf4688b71cc9b Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 20:11:42 +0200 Subject: [PATCH 1/9] Fix non-stereo Audacious files --- audacious/plugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audacious/plugin.cc b/audacious/plugin.cc index bb00b8c2..aedd7b73 100644 --- a/audacious/plugin.cc +++ b/audacious/plugin.cc @@ -197,7 +197,7 @@ bool VgmstreamPlugin::play(const char *filename, VFSFile &file) { rate = get_vgmstream_average_bitrate(vgmstream); set_stream_bitrate(rate); - open_audio(FMT_S16_LE, vgmstream->sample_rate, 2); + open_audio(FMT_S16_LE, vgmstream->sample_rate, vgmstream->channels); int fade_samples = vgmstream_cfg.fade_length * vgmstream->sample_rate; while (!check_stop()) { From dc4cee2daf5516cbcc9be8f8e2158fe6a526258a Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 20:12:09 +0200 Subject: [PATCH 2/9] Clean .pos code [Ys I Complete (PC)] --- src/meta/pos.c | 66 +++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/src/meta/pos.c b/src/meta/pos.c index c5ca20e0..86311b81 100644 --- a/src/meta/pos.c +++ b/src/meta/pos.c @@ -1,66 +1,40 @@ -#include #include "meta.h" -#include "../util.h" -#ifdef WIN32 -#define DIRSEP '\\' -#else -#define DIRSEP '/' -#endif - -/* .pos is a tiny file with loop points, and the same base name as a .wav */ +/* .pos - loop points for .wav [Ys I Complete (PC); reused for manual looping] */ VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileWAV = NULL; - char filename[PATH_LIMIT]; - char filenameWAV[PATH_LIMIT]; + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int32_t loop_start, loop_end; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pos",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile,"pos")) + goto fail; + if (get_streamfile_size(streamFile) != 0x08) + goto fail; - /* check for .WAV file */ - strcpy(filenameWAV,filename); - strcpy(filenameWAV+strlen(filenameWAV)-3,"wav"); - - streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWAV) { - /* try again, ucase */ - for (i=strlen(filenameWAV);i>=0&&filenameWAV[i]!=DIRSEP;i--) - filenameWAV[i]=toupper(filenameWAV[i]); - - streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWAV) goto fail; - } + streamData = open_streamfile_by_ext(streamFile, "wav"); + if (!streamData) goto fail; /* let the real initer do the parsing */ - vgmstream = init_vgmstream_riff(streamFileWAV); + vgmstream = init_vgmstream_riff(streamData); if (!vgmstream) goto fail; - close_streamfile(streamFileWAV); - streamFileWAV = NULL; + close_streamfile(streamData); + streamData = NULL; - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; - } + /* install loops (wrong values are validated later) */ + loop_start = read_32bitLE(0x00,streamFile); + loop_end = read_32bitLE(0x04,streamFile); + vgmstream_force_loop(vgmstream, 1, loop_start, loop_end); - vgmstream->loop_start_sample = read_32bitLE(0,streamFile); - vgmstream->loop_end_sample = read_32bitLE(4,streamFile); vgmstream->meta_type = meta_RIFF_WAVE_POS; return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileWAV) close_streamfile(streamFileWAV); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamData); + close_vgmstream(vgmstream); return NULL; } From 1ec9463235c5528abe29a200e6bc51e20d102e47 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 20:12:40 +0200 Subject: [PATCH 3/9] Fix VGM_ASSERT_ONCE --- src/util.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/util.h b/src/util.h index a2c5765f..c5469cf4 100644 --- a/src/util.h +++ b/src/util.h @@ -77,14 +77,14 @@ void concatn(int length, char * dst, const char * src); /* Simple stdout logging for debugging and regression testing purposes. - * Needs C99 variadic macros, uses do..while to force ; as statement */ + * Needs C99 variadic macros, uses do..while to force ";" as statement */ #ifdef VGM_DEBUG_OUTPUT /* equivalent to printf when condition is true */ #define VGM_ASSERT(condition, ...) \ - do { if (condition) printf(__VA_ARGS__); } while (0) + do { if (condition) {printf(__VA_ARGS__);} } while (0) #define VGM_ASSERT_ONCE(condition, ...) \ - do { static int written; if (!written) { if (condition) printf(__VA_ARGS__); written = 1; } } while (0) + do { static int written; if (!written) { if (condition) {printf(__VA_ARGS__); written = 1;} } } while (0) /* equivalent to printf */ #define VGM_LOG(...) \ do { printf(__VA_ARGS__); } while (0) @@ -110,6 +110,7 @@ void concatn(int length, char * dst, const char * src); #else/*VGM_DEBUG_OUTPUT*/ #define VGM_ASSERT(condition, ...) /* nothing */ +#define VGM_ASSERT_ONCE(condition, ...) /* nothing */ #define VGM_LOG(...) /* nothing */ #define VGM_LOG_ONCE(...) /* nothing */ #define VGM_LOGF() /* nothing */ From af70e95877b32e08795251c3f2c60729499780ea Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 20:15:07 +0200 Subject: [PATCH 4/9] Tweak PS-ADPCM decoding [inFamous (PS3)] - Code now more verbose, but easier to understand (by me, at least) - Unify normal and badflags code - Fix hist1 not being properly clamped, though not noticeable - Fix inFamous (PS3) which seemingly wasn't using the extended table but hitting some compiler weirdness? (failed in foobar and worked in test) - Use int math for PSX-cfg, which should be minimally more accurate --- src/coding/coding.h | 7 +- src/coding/psx_decoder.c | 280 ++++++++++++++++----------------------- src/vgmstream.c | 20 +-- 3 files changed, 130 insertions(+), 177 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index b9fb13d1..1cd97def 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -73,13 +73,14 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ -void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); -void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); +/* psv_decoder */ +void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xa_decoder */ void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked); diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index 0fcc3751..63bae82e 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -1,40 +1,24 @@ -#include #include "coding.h" -#include "../util.h" -/* for some algos, maybe closer to the real thing */ -#define VAG_USE_INTEGER_TABLE 0 -/* PS ADPCM table (precalculated divs) */ -static const double VAG_f[16][2] = { +/* PS-ADPCM table, defined as rational numbers (as in the spec) */ +static const double ps_adpcm_coefs_f[5][2] = { { 0.0 , 0.0 }, { 60.0 / 64.0 , 0.0 }, { 115.0 / 64.0 , -52.0 / 64.0 }, { 98.0 / 64.0 , -55.0 / 64.0 }, { 122.0 / 64.0 , -60.0 / 64.0 }, - /* extended table from PPSSPP (PSP emu), found by tests - * (only seen in inFamous PS3, very rare, possibly "SVAG" or "VAG-HE") */ - { 0.0 , 0.0 }, - { 0.0 , 0.0 }, - { 52.0 / 64.0 , 0.0 }, - { 55.0 / 64.0 , -2.0 / 64.0 }, - { 60.0 / 64.0 ,-125.0 / 64.0 }, - { 0.0 , 0.0 }, - { 0.0 , -91.0 / 64.0 }, - { 0.0 , 0.0 }, - { 2.0 / 64.0 ,-216.0 / 64.0 }, - { 125.0 / 64.0 , -6.0 / 64.0 }, - { 0.0 ,-151.0 / 64.0 }, }; -#if VAG_USE_INTEGER_TABLE -/* PS ADPCM table */ -static const int8_t VAG_coefs[5][2] = { + +/* PS-ADPCM table, defined as spec_coef*64 (for int implementations) */ +static const int ps_adpcm_coefs_i[5][2] = { { 0 , 0 }, { 60 , 0 }, { 115 , -52 }, { 98 , -55 }, { 122 , -60 }, - /* extended */ +#if 0 + /* extended table from PPSSPP (PSP emu), found by tests (unused?) */ { 0 , 0 }, { 0 , 0 }, { 52 , 0 }, @@ -46,163 +30,79 @@ static const int8_t VAG_coefs[5][2] = { { 2 ,-216 }, { 125 , -6 }, { 0 ,-151 }, -}; #endif +}; -/** - * Sony's PS ADPCM (sometimes called VAG), decodes 16 bytes into 28 samples. - * The first 2 bytes are a header (shift, predictor, optional flag). - * All variants are the same with minor differences. +/* Decodes Sony's PS-ADPCM (sometimes called SPU-ADPCM or VAG, just "ADPCM" in the SDK docs). + * Very similar to XA ADPCM (see xa_decoder for extended info). * - * Flags: - * 0x0: Nothing - * 0x1: End marker + decode - * 0x2: Loop region - * 0x3: Loop end - * 0x4: Start marker - * 0x5: ? - * 0x6: Loop start - * 0x7: End marker + don't decode - * 0x8+ Not valid + * Some official PC tools decode using float coefs (from the spec), as does this code, but + * consoles/games/libs would vary (PS1 could do it in hardware using BRR/XA's logic, FMOD/PS3 + * may use int math in software, etc). There are inaudible rounding diffs between implementations. + * + * Optional bit flag combinations in the header control the SPU: + * 0x0 (0000): Nothing + * 0x1 (0001): End marker + decode + * 0x2 (0010): Loop region + * 0x3 (0011): Loop end + * 0x4 (0100): Start marker + * 0x6 (0110): Loop start + * 0x7 (0111): End marker + don't decode + * 0x5/8+ (1NNN): Not valid */ -/* default */ -void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - - int predict_nr, shift_factor, sample; - int32_t hist1=stream->adpcm_history1_32; - int32_t hist2=stream->adpcm_history2_32; - - short scale; - int i; - int32_t sample_count; - uint8_t flag; - - int framesin = first_sample/28; - - predict_nr = read_8bit(stream->offset+framesin*16,stream->streamfile) >> 4; - shift_factor = read_8bit(stream->offset+framesin*16,stream->streamfile) & 0xf; - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); /* only lower nibble needed */ - - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - - scale = ((i&1 ? /* odd/even byte */ - sample_byte >> 4 : - sample_byte & 0x0f)<<12); - - sample=(int)((scale >> shift_factor)+hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1]); - } - - outbuf[sample_count] = clamp16(sample); - hist2=hist1; - hist1=sample; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_history2_32=hist2; -} - -/* some games have garbage (?) in their flags, this decoder just ignores that byte */ -void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - - int predict_nr, shift_factor, sample; - int32_t hist1=stream->adpcm_history1_32; - int32_t hist2=stream->adpcm_history2_32; - - short scale; - int i; - int32_t sample_count; - - int framesin = first_sample/28; - - predict_nr = read_8bit(stream->offset+framesin*16,stream->streamfile) >> 4; - shift_factor = read_8bit(stream->offset+framesin*16,stream->streamfile) & 0xf; - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - - scale = ((i&1 ? - sample_byte >> 4 : - sample_byte & 0x0f)<<12); - - sample=(int)((scale >> shift_factor)+hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1]); - - outbuf[sample_count] = clamp16(sample); - hist2=hist1; - hist1=sample; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_history2_32=hist2; -} - - -/* configurable frame size, with no flag - * Found in PS3 Afrika (SGXD type 5) in size 4, FF XI in sizes 3/5/9/41, Blur and James Bond in size 33. */ -void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { - uint8_t predict_nr, shift, byte; - int16_t scale = 0; - - int32_t sample; +/* standard PS-ADPCM (float math version) */ +void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor, flag; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - int i, sample_count, bytes_per_frame, samples_per_frame; - const int header_size = 1; - int framesin; - - bytes_per_frame = frame_size - header_size; - samples_per_frame = bytes_per_frame * 2; - - framesin = first_sample / samples_per_frame; - - /* 1 byte header: predictor = 1st, shift = 2nd */ - byte = (uint8_t)read_8bit(stream->offset+framesin*frame_size+0,stream->streamfile); - predict_nr = byte >> 4; - shift = byte & 0x0f; - + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x10; + samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */ + frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; - if (first_sample & 1) { /* if restarting on a high nibble, read byte first */ - byte = (uint8_t)read_8bit(stream->offset+(framesin*frame_size)+header_size+first_sample/2,stream->streamfile); - } + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + flag = (uint8_t)read_8bit(frame_offset+0x01,stream->streamfile); /* only lower nibble needed */ - for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { - sample = 0; + VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %lx\n", frame_offset); + if (coef_index > 5) /* needed by inFamous (PS3) (maybe it's supposed to use more filters?) */ + coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ - if (predict_nr < 5) { - if (!(i&1)) { /* low nibble first */ - byte = (uint8_t)read_8bit(stream->offset+(framesin*frame_size)+header_size+i/2,stream->streamfile); - scale = (byte & 0x0f); - } else { /* high nibble last */ - scale = byte >> 4; - } - scale = scale << 12; /* shift + sign extend (only if scale is int16_t) */ - /*if (scale > 7) { - scale = scale - 16; - }*/ -#if VAG_USE_INTEGER_TABLE - sample = (scale >> shift) + - (hist1 * VAG_coefs[predict_nr][0] + - hist2 * VAG_coefs[predict_nr][1] ) / 64; -#else - sample = (int)( (scale >> shift) + - (hist1 * VAG_f[predict_nr][0] + - hist2 * VAG_f[predict_nr][1]) ); -#endif + if (is_badflags) /* some games store garbage or extra internal logic in the flags, must be ignored */ + flag = 0; + VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %lx\n", frame_offset); /* meta should set PSX-badflags */ + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample = 0; + + if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */ + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x02+i/2,stream->streamfile); + + new_sample = i&1 ? /* low nibble first */ + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = (int)(new_sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2); + new_sample = clamp16(new_sample); } - outbuf[sample_count] = clamp16(sample); + outbuf[sample_count] = new_sample; + sample_count += channelspacing; + hist2 = hist1; - hist1 = sample; + hist1 = new_sample; } stream->adpcm_history1_32 = hist1; @@ -210,6 +110,58 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha } +/* PS-ADPCM with configurable frame size and no flag (int math version). + * Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc). + * + * Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */ +void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + + /* external interleave (variable size), mono */ + bytes_per_frame = frame_size; + samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 28 */ + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + + VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %lx\n", frame_offset); + if (coef_index > 5) /* needed by Afrika (PS3) (maybe it's supposed to use more filters?) */ + coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample = 0; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile); + + new_sample = i&1 ? /* low nibble first */ + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = new_sample + ((ps_adpcm_coefs_i[coef_index][0]*hist1 + ps_adpcm_coefs_i[coef_index][1]*hist2) >> 6); + new_sample = clamp16(new_sample); + + outbuf[sample_count] = new_sample; + sample_count += channelspacing; + + hist2 = hist1; + hist1 = new_sample; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; +} + size_t ps_bytes_to_samples(size_t bytes, int channels) { return bytes / channels / 0x10 * 28; } diff --git a/src/vgmstream.c b/src/vgmstream.c index c23956fb..17d06784 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1535,21 +1535,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to for (chan=0;chanchannels;chan++) { decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + samples_to_do, 0); } break; case coding_PSX_badflags: for (chan=0;chanchannels;chan++) { - decode_psx_badflags(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; - case coding_HEVAG: - for (chan=0;chanchannels;chan++) { - decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + samples_to_do, 1); } break; case coding_PSX_cfg: @@ -1559,6 +1552,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do, vgmstream->interleave_block_size); } break; + case coding_HEVAG: + for (chan=0;chanchannels;chan++) { + decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_XA: for (chan=0;chanchannels;chan++) { decode_xa(vgmstream,buffer+samples_written*vgmstream->channels+chan, From d4ba9d9d57cc6abc2458ff51ba1e50faf1698b28 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 23:05:20 +0200 Subject: [PATCH 5/9] Fix potential bugs --- src/meta/ea_eaac_streamfile.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index 9436af00..383a83c9 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -125,7 +125,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { return data->total_size; physical_offset = data->start_offset; - max_physical_offset = get_streamfile_size(streamfile) - data->start_offset; + max_physical_offset = get_streamfile_size(streamfile); /* get size of the underlying, non-blocked data */ while (physical_offset < max_physical_offset) { @@ -174,6 +174,11 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { break; /* stop on last block */ } + if (total_size > get_streamfile_size(streamfile)) { + VGM_LOG("EA SCHL: wrong streamfile total_size\n"); + total_size = 0; + } + data->total_size = total_size; return data->total_size; } From 099a4abc6684a3dbbda4209acbc3bd6d83046f51 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 21 Jul 2018 23:34:00 +0200 Subject: [PATCH 6/9] Add EA SCHl ATRAC3plus [The Sims 2 Castaway (PSP), MoH: Heroes 2 PSP)] --- src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/ea_schl.c | 40 +++++-- src/meta/ea_schl_streamfile.h | 182 +++++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 src/meta/ea_schl_streamfile.h diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 8b1517f0..0e6d72c6 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -228,6 +228,10 @@ RelativePath=".\meta\ea_eaac_streamfile.h" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index b83f2a2d..f52a22ee 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -99,6 +99,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index ed956132..a43d4743 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -77,6 +77,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 06bf53a2..11fdb85b 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" +#include "ea_schl_streamfile.h" /* header version */ #define EA_VERSION_NONE -1 @@ -47,7 +48,7 @@ #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 #define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */ -//todo #define EA_CODEC2_ATRAC9 0x-- /* supposedly exists */ + #define EA_MAX_CHANNELS 6 @@ -176,6 +177,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { for (i = 0; i < num_tables; i++) { num_entries = read_8bit(header_table_offset + 0x24, streamFile); base_offset = read_32bit(header_table_offset + 0x2C, streamFile); + if (num_entries == 0xff) goto fail; /* EOF read */ for (j = 0; j < num_entries; j++) { value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, streamFile); @@ -196,6 +198,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { sound_table_offsets[total_sound_tables++] = table_offset; num_sounds = read_32bit(table_offset, streamFile); + if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { entry_offset = table_offset + 0x04 + 0x0C * k; @@ -273,7 +276,7 @@ VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) { off_t schl_offset; STREAMFILE *datFile = NULL, *sthFile = NULL; VGMSTREAM *vgmstream; - int32_t (*read_32bit)(off_t,STREAMFILE*); + //int32_t (*read_32bit)(off_t,STREAMFILE*); int16_t (*read_16bit)(off_t,STREAMFILE*); /* No nice way to validate these so we do what we can */ @@ -297,10 +300,10 @@ VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) { if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail; if (guess_endianness16bit(0x08,streamFile)) { - read_32bit = read_32bitBE; + //read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { - read_32bit = read_32bitLE; + //read_32bit = read_32bitLE; read_16bit = read_16bitLE; } @@ -343,7 +346,7 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ - /* rest is common */ + /* rest is common */ return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, total_streams); fail: @@ -429,6 +432,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ /* special case found in some tests (pcstream had hist, pcbnk no hist, no patch diffs) + * Later console games don't need hist [FIFA 07 (Xbox): V3, NASCAR 06 (Xbox): V2]. * I think this works but what decides if hist is used or not a secret to everybody */ if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version >= EA_VERSION_V1) { ea.codec_version = 0; @@ -491,6 +495,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } vgmstream->num_streams = total_streams; + //vgmstream->stream_size = ; //todo needed for kbps info /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and * favors them over platform's natives (ex. EAXA vs VAG/DSP). @@ -592,13 +597,30 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ break; } - case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */ + case EA_CODEC2_ATRAC3PLUS: { /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */ + STREAMFILE* temp_streamFile = NULL; + + /* remove blocks on reads to feed FFmpeg a clean .at3 */ + temp_streamFile = setup_schl_streamfile(streamFile, ea->codec2, ea->channels, start_offset, 0); + if (!temp_streamFile) goto fail; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + //todo fix encoder delay + vgmstream->codec_data = init_ffmpeg_offset(temp_streamFile, 0x00, get_streamfile_size(temp_streamFile)); + close_streamfile(temp_streamFile); + if (!vgmstream->codec_data) goto fail; + + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + default: VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform); goto fail; } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -644,7 +666,8 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ break; } } - else { + else if (vgmstream->layout_type == layout_blocked_ea_schl) { + /* regular SCHls, except ATRAC3plus */ if (total_streams == 0) { /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); @@ -868,7 +891,6 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be default: VGM_LOG("EA SCHl: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1)); goto fail; - break; } } diff --git a/src/meta/ea_schl_streamfile.h b/src/meta/ea_schl_streamfile.h new file mode 100644 index 00000000..97eb0c12 --- /dev/null +++ b/src/meta/ea_schl_streamfile.h @@ -0,0 +1,182 @@ +#ifndef _EA_SCHL_STREAMFILE_H_ +#define _EA_SCHL_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + /* config */ + int codec; + int channels; + off_t start_offset; + size_t total_size; /* size of the resulting substream */ +} schl_io_data; + + +/* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data. + * physical/logical_offset should always be at the start of a block and only advance when a block is fully done */ +static size_t schl_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, schl_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return total_read; + } + + /* previous offset: re-start as we can't map logical<>physical offsets + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->physical_offset = data->start_offset; + data->logical_offset = 0x00; + } + + /* read doing one EA block at a time */ + while (length > 0) { + size_t to_read, bytes_read; + off_t intrablock_offset, intradata_offset; + uint32_t block_id, block_size, data_size, skip_size; + + block_id = (uint32_t)read_32bitBE(data->physical_offset+0x00,streamfile); + block_size = read_32bitLE(data->physical_offset+0x04,streamfile); /* always LE, hopefully */ + + if (block_id == 0x5343456C) /* "SCEl" */ + break; /* end block (no need to look for more SCHl for codecs needed this custom IO) */ + + if (block_id != 0x5343446C) { /* "SCDl" */ + data->physical_offset += block_size; + continue; /* skip non-data blocks */ + } + + switch(data->codec) { + case 0x1b: /* ATRAC3plus */ + data_size = read_32bitLE(data->physical_offset+0x0c+0x04*data->channels,streamfile); + skip_size = 0x0c+0x04*data->channels+0x04; + break; + default: + return total_read; + } + + /* requested offset is outside current block, try next */ + if (offset >= data->logical_offset + data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + continue; + } + + /* reads could fall in the middle of the block */ + intradata_offset = offset - data->logical_offset; + intrablock_offset = skip_size + intradata_offset; + + /* clamp reads up to this block's end */ + to_read = (data_size - intradata_offset); + if (to_read > length) + to_read = length; + if (to_read == 0) + break; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + break; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + /* block fully read, go next */ + if (intradata_offset + bytes_read == data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + } + } + + return total_read; +} + +static size_t schl_io_size(STREAMFILE *streamfile, schl_io_data* data) { + off_t physical_offset, max_physical_offset; + size_t total_size = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->start_offset; + max_physical_offset = get_streamfile_size(streamfile); + + /* get size of the underlying, non-blocked data */ + while (physical_offset < max_physical_offset) { + uint32_t block_id, block_size, data_size; + + block_id = (uint32_t)read_32bitBE(physical_offset+0x00,streamfile); + block_size = read_32bitLE(physical_offset+0x04,streamfile); /* always LE, hopefully */ + + if (block_id == 0x5343456C) /* "SCEl" */ + break; /* end block (no need to look for more SCHl for codecs needed this custom IO) */ + + if (block_id != 0x5343446C) { /* "SCDl" */ + physical_offset += block_size; + continue; /* skip non-data blocks */ + } + + switch(data->codec) { + case 0x1b: /* ATRAC3plus */ + data_size = read_32bitLE(physical_offset+0x0c+0x04*data->channels,streamfile); + break; + default: + return 0; + } + + physical_offset += block_size; + total_size += data_size; + } + + + if (total_size > get_streamfile_size(streamfile)) { + VGM_LOG("EA SCHL: wrong streamfile total_size\n"); + total_size = 0; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for some blocked SCHl formats, that need clean reads without block headers. + * Basically done to feed FFmpeg clean ATRAC3plus. + */ +static STREAMFILE* setup_schl_streamfile(STREAMFILE *streamFile, int codec, int channels, off_t start_offset, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + schl_io_data io_data = {0}; + size_t io_data_size = sizeof(schl_io_data); + + io_data.codec = codec; + io_data.channels = channels; + io_data.start_offset = start_offset; + io_data.total_size = total_size; /* optional */ + io_data.physical_offset = start_offset; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, schl_io_read,schl_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _EA_SCHL_STREAMFILE_H_ */ From 57b8c6acd3b710a888b2af2bf9f05dfd4c91f75b Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 22 Jul 2018 00:46:08 +0200 Subject: [PATCH 7/9] Add TXTP 'm' channel mapper/swapper Credit to topher-au for the initial idea/code --- src/meta/txtp.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++-- src/vgmstream.c | 20 ++++++++++++++++ src/vgmstream.h | 12 ++++++---- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/meta/txtp.c b/src/meta/txtp.c index a0a8dcf2..995bedf6 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -9,6 +9,8 @@ typedef struct { char filename[TXT_LINE_MAX]; int subsong; uint32_t channel_mask; + int channel_mappings_on; + int channel_mappings[32]; } txtp_entry; typedef struct { @@ -59,6 +61,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->channel_mask = txtp->entry[0].channel_mask; + + vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on; + if(vgmstream->channel_mappings_on) { + for (i = 0; i < 32; i++) { + vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i]; + } + } } else if (txtp->is_layered) { /* layered multi file */ @@ -103,6 +112,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { vgmstream->channel_mask = txtp->entry[0].channel_mask; + vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on; + if (vgmstream->channel_mappings_on) { + for (i = 0; i < 32; i++) { + vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i]; + } + } + vgmstream->layout_data = data_l; } else { @@ -186,6 +202,8 @@ fail: static int add_filename(txtp_header * txtp, char *filename) { int i; uint32_t channel_mask = 0; + int channel_mappings_on = 0; + int channel_mappings[32] = {0}; size_t range_start, range_end; //;VGM_LOG("TXTP: filename=%s\n", filename); @@ -193,7 +211,9 @@ static int add_filename(txtp_header * txtp, char *filename) { /* parse config: * - file.ext#2 = play subsong 2 * - file.ext#2~10 = play subsongs in 2 to 10 range - * - file.ext#c1,2 = play channels 1,2 */ + * - file.ext#c1,2 = play channels 1,2 + * - file.ext#m1-2,3-4 = swaps channels 1<>2 and 3<>4 + */ { char *config; @@ -213,6 +233,7 @@ static int add_filename(txtp_header * txtp, char *filename) { config[0] = '\0'; config++; + //todo: alt long words, s or number=subsong if (config[0] == 'c') { /* mask channels */ @@ -221,7 +242,7 @@ static int add_filename(txtp_header * txtp, char *filename) { config++; channel_mask = 0; while (sscanf(config, "%d%n", &ch,&n) == 1) { - if (ch > 0 && ch < 32) + if (ch > 0 && ch <= 32) channel_mask |= (1 << (ch-1)); config += n; @@ -231,6 +252,35 @@ static int add_filename(txtp_header * txtp, char *filename) { break; }; } + else if (config[0] == 'm') { + /* channel mappings */ + int n, ch_from = 0, ch_to = 0; + + config++; + channel_mappings_on = 1; + + while (config[0] != '\0') { + if (sscanf(config, "%d%n", &ch_from, &n) != 1) + break; + config += n; + if (config[0]== ',' || config[0]== '-') + config++; + else if (config[0] != '\0') + break; + + if (sscanf(config, "%d%n", &ch_to, &n) != 1) + break; + config += n; + if (config[0]== ',' || config[0]== '-') + config++; + else if (config[0] != '\0') + break; + + if (ch_from > 0 && ch_from <= 32 && ch_to > 0 && ch_to <= 32) { + channel_mappings[ch_from-1] = ch_to-1; + } + } + } else { /* subsong range */ int subsong_start = 0, subsong_end = 0; @@ -287,7 +337,16 @@ static int add_filename(txtp_header * txtp, char *filename) { memset(&txtp->entry[txtp->entry_count],0, sizeof(txtp_entry)); strcpy(txtp->entry[txtp->entry_count].filename, filename); + txtp->entry[txtp->entry_count].channel_mask = channel_mask; + + if (channel_mappings_on) { + int ch; + txtp->entry[txtp->entry_count].channel_mappings_on = channel_mappings_on; + for (ch = 0; ch < 32; ch++) { + txtp->entry[txtp->entry_count].channel_mappings[ch] = channel_mappings[ch]; + } + } txtp->entry[txtp->entry_count].subsong = (i+1); txtp->entry_count++; } diff --git a/src/vgmstream.c b/src/vgmstream.c index 17d06784..a1854854 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -959,6 +959,26 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre } + /* swap channels if set, to create custom channel mappings */ + if (vgmstream->channel_mappings_on) { + int ch_from,ch_to,s; + sample temp; + for (s = 0; s < sample_count; s++) { + for (ch_from = 0; ch_from < vgmstream->channels; ch_from++) { + if (ch_from > 32) + continue; + + ch_to = vgmstream->channel_mappings[ch_from]; + if (ch_to < 1 || ch_to > 32 || ch_to > vgmstream->channels-1 || ch_from == ch_to) + continue; + + temp = buffer[s*vgmstream->channels + ch_from]; + buffer[s*vgmstream->channels + ch_from] = buffer[s*vgmstream->channels + ch_to]; + buffer[s*vgmstream->channels + ch_to] = temp; + } + } + } + /* channel bitmask to silence non-set channels (up to 32) * can be used for 'crossfading subsongs' or layered channels, where a set of channels make a song section */ if (vgmstream->channel_mask) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 945f45db..09ab619f 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -747,12 +747,14 @@ typedef struct { layout_t layout_type; /* type of layout for data */ meta_t meta_type; /* how we know the metadata */ - /* subsongs */ - int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ - int stream_index; /* selected stream (also 1-based) */ + /* subsongs and internal config */ + int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ + int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ - size_t stream_size; /* info to properly calculate bitrate */ - uint32_t channel_mask; /* to silence crossfading subsongs/layers */ + size_t stream_size; /* info to properly calculate bitrate */ + uint32_t channel_mask; /* to silence crossfading subsongs/layers */ + int channel_mappings_on; /* channel mappings are active */ + int channel_mappings[32]; /* swap channel "i" with "[i]" */ /* looping */ int loop_flag; /* is this stream looped? */ From 907a0af3df72157e4576924186285a8cdab9d606 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 22 Jul 2018 00:47:00 +0200 Subject: [PATCH 8/9] Add missing nus3 OPUS .opus extension Also remove tabs to fix GCC warnings about mixing spaces and tabs --- src/meta/opus.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/meta/opus.c b/src/meta/opus.c index 06e07f6d..5d010bfd 100644 --- a/src/meta/opus.c +++ b/src/meta/opus.c @@ -87,7 +87,7 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { - STREAMFILE * PSIFile = NULL; + STREAMFILE * PSIFile = NULL; off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0; @@ -95,23 +95,23 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"opus,lopus")) goto fail; - /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ - /* Maybe future Arc System Works games will use this too? */ - PSIFile = open_streamfile_by_ext(streamFile, "psi"); + /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ + /* Maybe future Arc System Works games will use this too? */ + PSIFile = open_streamfile_by_ext(streamFile, "psi"); - offset = 0x00; + offset = 0x00; - if (PSIFile){ - num_samples = read_32bitLE(0x8C, PSIFile); - loop_start = read_32bitLE(0x84, PSIFile); - loop_end = read_32bitLE(0x88, PSIFile); - close_streamfile(PSIFile); - } - else { - num_samples = 0; - loop_start = 0; - loop_end = 0; - } + if (PSIFile){ + num_samples = read_32bitLE(0x8C, PSIFile); + loop_start = read_32bitLE(0x84, PSIFile); + loop_end = read_32bitLE(0x88, PSIFile); + close_streamfile(PSIFile); + } + else { + num_samples = 0; + loop_start = 0; + loop_end = 0; + } return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: @@ -278,17 +278,17 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE *streamFile) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile, "lopus")) + if (!check_extensions(streamFile, "opus,lopus")) goto fail; if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ goto fail; - /* Here's an interesting quirk, OPUS header contains big endian values + /* Here's an interesting quirk, OPUS header contains big endian values while the Nintendo Opus header and data that follows remain little endian as usual */ offset = read_32bitBE(0x20, streamFile); num_samples = read_32bitBE(0x08, streamFile); - /* Check if there's a loop end value to determine loop_flag*/ + /* Check if there's a loop end value to determine loop_flag*/ loop_flag = read_32bitBE(0x18, streamFile); if (loop_flag) { loop_start = read_32bitBE(0x14, streamFile); @@ -314,7 +314,7 @@ VGMSTREAM * init_vgmstream_opus_nlsd(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x09000000) goto fail; - offset = 0x1C; + offset = 0x1C; num_samples = read_32bitLE(0x0C, streamFile); /* Check if there's a loop_end "adjuster" value to determine loop_flag From 4439047fd129044140f0a0c86492bef88accf20f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 22 Jul 2018 00:47:31 +0200 Subject: [PATCH 9/9] Minor doc --- src/coding/asf_decoder.c | 14 +++++++------- src/coding/xa_decoder.c | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/coding/asf_decoder.c b/src/coding/asf_decoder.c index c97d7434..a0e1ba4f 100644 --- a/src/coding/asf_decoder.c +++ b/src/coding/asf_decoder.c @@ -1,13 +1,13 @@ #include "coding.h" -/* Decodec Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform +/* Decodes Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform * looks almost correct, but should reverse engineer asfcodec.adl (DLL) for accuracy. */ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { off_t frame_offset; int i, frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - uint32_t shift, mode; + uint8_t shift, mode; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; @@ -17,12 +17,12 @@ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; - /* parse header */ + /* parse frame header */ frame_offset = stream->offset + bytes_per_frame*frames_in; - shift = (read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; - mode = (read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + mode = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; - /* decoder nibbles */ + /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { int32_t new_sample; uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); @@ -50,7 +50,7 @@ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, } //new_sample = clamp16(new_sample); /* must not */ - new_sample = new_sample & 0xFFFF; /* probably unnecessary */ + new_sample = new_sample & 0xFFFF; /* probably unnecessary and only casting is needed */ outbuf[sample_count] = new_sample; sample_count += channelspacing; diff --git a/src/coding/xa_decoder.c b/src/coding/xa_decoder.c index 088c24bf..d437676d 100644 --- a/src/coding/xa_decoder.c +++ b/src/coding/xa_decoder.c @@ -3,7 +3,7 @@ // todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do // XA coefs int math in different ways (see comments below), not be 100% accurate. -// May be implemented like the SNES/SPC700 BRR (see BSNES' brr.cpp, hardware-tested). +// May be implemented like the SNES/SPC700 BRR. /* XA ADPCM gain values */ static const double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; @@ -35,6 +35,8 @@ static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); } * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. * * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf + * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples + * (bsnes): https://gitlab.com/higan/higan/blob/master/higan/sfc/dsp/brr.cpp */ void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { static int head_table[8] = {0,2,8,10};