From 69533e12c7f7dd9da76766266b1a14b7068393ab Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 3 May 2025 01:56:03 -0700 Subject: [PATCH] VGMStream: Updated libvgmstream code base Updated VGMStream to r1980-268-gc32951e9 Signed-off-by: Christopher Snowhill --- .../libvgmstream.xcodeproj/project.pbxproj | 4 + .../vgmstream/src/base/api_helpers.c | 4 +- .../vgmstream/vgmstream/src/base/codec_info.c | 35 ++ .../vgmstream/vgmstream/src/base/decode.c | 148 ----- .../vgmstream/vgmstream/src/base/mixing.c | 12 + .../vgmstream/vgmstream/src/base/sbuf.c | 28 +- .../vgmstream/src/coding/atrac9_decoder.c | 276 ++++----- .../vgmstream/src/coding/celt_fsb_decoder.c | 388 ++++++------ .../vgmstream/vgmstream/src/coding/coding.h | 65 +- .../vgmstream/src/coding/ffmpeg_decoder.c | 486 ++++++++------- .../vgmstream/src/coding/ka1a_decoder.c | 2 +- .../vgmstream/src/coding/libs/relic_lib.c | 92 ++- .../vgmstream/src/coding/libs/relic_lib.h | 5 +- .../vgmstream/src/coding/mpeg_custom_info.c | 253 ++++++++ .../vgmstream/src/coding/mpeg_custom_utils.c | 278 +-------- .../src/coding/mpeg_custom_utils_ahx.c | 6 +- .../src/coding/mpeg_custom_utils_ealayer3.c | 53 +- .../src/coding/mpeg_custom_utils_eamp3.c | 38 +- .../vgmstream/src/coding/mpeg_decoder.c | 575 +++++++++--------- .../vgmstream/src/coding/mpeg_decoder.h | 45 +- .../vgmstream/src/coding/relic_decoder.c | 122 ++-- .../vgmstream/vgmstream/src/layout/layered.c | 12 +- .../vgmstream/vgmstream/src/layout/layout.h | 2 + .../vgmstream/src/layout/segmented.c | 12 +- .../vgmstream/vgmstream/src/meta/astb.c | 2 +- .../vgmstream/vgmstream/src/meta/ffmpeg.c | 4 + Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 8 +- .../vgmstream/vgmstream/src/meta/fsb5.c | 4 +- .../vgmstream/vgmstream/src/meta/fsb_keys.h | 3 +- .../vgmstream/vgmstream/src/meta/redspark.c | 7 +- .../vgmstream/vgmstream/src/meta/rfrm.c | 4 +- 31 files changed, 1434 insertions(+), 1539 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index eb1b0e50d..217c05254 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -669,6 +669,7 @@ 837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */; }; 837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; }; 8383A62C281203C60062E49E /* s3v.c in Sources */ = {isa = PBXBuildFile; fileRef = 8383A62B281203C50062E49E /* s3v.c */; }; + 8384EC322DC60DD70037EFFD /* mpeg_custom_info.c in Sources */ = {isa = PBXBuildFile; fileRef = 8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */; }; 83852B0B2680247900378854 /* rxws.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B092680247900378854 /* rxws.c */; }; 83852B0C2680247900378854 /* ads_midway.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B0A2680247900378854 /* ads_midway.c */; }; 8385D4E6245174C700FF8E67 /* diva.c in Sources */ = {isa = PBXBuildFile; fileRef = 8385D4E2245174C600FF8E67 /* diva.c */; }; @@ -1624,6 +1625,7 @@ 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_streamfile.h; sourceTree = ""; }; 837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = ""; }; 8383A62B281203C50062E49E /* s3v.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s3v.c; sourceTree = ""; }; + 8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_info.c; sourceTree = ""; }; 83852B092680247900378854 /* rxws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rxws.c; sourceTree = ""; }; 83852B0A2680247900378854 /* ads_midway.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads_midway.c; sourceTree = ""; }; 8385D4E2245174C600FF8E67 /* diva.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = diva.c; sourceTree = ""; }; @@ -2022,6 +2024,7 @@ 83B73C482D8FF17900A57F08 /* mio_decoder.c */, 834F7D6A2C7093EA003AC386 /* mp4_aac_decoder.c */, 834F7D692C7093EA003AC386 /* mpc3_decoder.c */, + 8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */, 834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */, 834F7D6B2C7093EA003AC386 /* mpeg_custom_utils_ahx.c */, 834F7D6C2C7093EA003AC386 /* mpeg_custom_utils_ealayer3.c */, @@ -3382,6 +3385,7 @@ 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, 834F7DC62C7093EA003AC386 /* g721_decoder.c in Sources */, 834F7DA32C7093EA003AC386 /* nwa_lib.c in Sources */, + 8384EC322DC60DD70037EFFD /* mpeg_custom_info.c in Sources */, 8306B0EA20984590000302D4 /* caf.c in Sources */, 83AA7F842519C042004C5298 /* svag_snk.c in Sources */, 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/base/api_helpers.c b/Frameworks/vgmstream/vgmstream/src/base/api_helpers.c index e669df4ad..5b802e971 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/api_helpers.c +++ b/Frameworks/vgmstream/vgmstream/src/base/api_helpers.c @@ -74,11 +74,11 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_title_t* cfg, char* buf, int buf_len) { - if (!buf || !buf_len || !cfg) + if (!buf || !buf_len) return LIBVGMSTREAM_ERROR_GENERIC; buf[0] = '\0'; - if (!lib || !lib->priv) + if (!lib || !lib->priv || !cfg) return LIBVGMSTREAM_ERROR_GENERIC; libvgmstream_priv_t* priv = lib->priv; diff --git a/Frameworks/vgmstream/vgmstream/src/base/codec_info.c b/Frameworks/vgmstream/vgmstream/src/base/codec_info.c index da61ae52a..e2f7a95cf 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/codec_info.c +++ b/Frameworks/vgmstream/vgmstream/src/base/codec_info.c @@ -16,6 +16,19 @@ extern const codec_info_t mio_decoder; extern const codec_info_t pcm32_decoder; extern const codec_info_t pcm24_decoder; extern const codec_info_t pcmfloat_decoder; +#ifdef VGM_USE_FFMPEG +extern const codec_info_t ffmpeg_decoder; +#endif +#ifdef VGM_USE_ATRAC9 +extern const codec_info_t atrac9_decoder; +#endif +#ifdef VGM_USE_CELT +extern const codec_info_t celt_fsb_decoder; +#endif +#ifdef VGM_USE_MPEG +extern const codec_info_t mpeg_decoder; +#endif +extern const codec_info_t relic_decoder; const codec_info_t* codec_get_info(VGMSTREAM* v) { switch(v->coding_type) { @@ -50,6 +63,28 @@ const codec_info_t* codec_get_info(VGMSTREAM* v) { return &pcm24_decoder; case coding_PCMFLOAT: return &pcmfloat_decoder; +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + return &ffmpeg_decoder; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return &atrac9_decoder; +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return &celt_fsb_decoder; +#endif +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + case coding_MPEG_ealayer3: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: + return &mpeg_decoder; +#endif + case coding_RELIC: + return &relic_decoder; default: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode.c b/Frameworks/vgmstream/vgmstream/src/base/decode.c index 853e5fa06..a9f80e5f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/base/decode.c @@ -50,10 +50,6 @@ void decode_free(VGMSTREAM* vgmstream) { free_circus_vq(vgmstream->codec_data); } - if (vgmstream->coding_type == coding_RELIC) { - free_relic(vgmstream->codec_data); - } - if (vgmstream->coding_type == coding_ICE_RANGE || vgmstream->coding_type == coding_ICE_DCT) { free_ice(vgmstream->codec_data); @@ -71,28 +67,12 @@ void decode_free(VGMSTREAM* vgmstream) { free_ea_mt(vgmstream->codec_data, vgmstream->channels); } -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - free_ffmpeg(vgmstream->codec_data); - } -#endif - #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) if (vgmstream->coding_type == coding_MP4_AAC) { free_mp4_aac(vgmstream->codec_data); } #endif -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - free_mpeg(vgmstream->codec_data); - } -#endif - #ifdef VGM_USE_G7221 if (vgmstream->coding_type == coding_G7221C) { free_g7221(vgmstream->codec_data); @@ -105,18 +85,6 @@ void decode_free(VGMSTREAM* vgmstream) { } #endif -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - free_atrac9(vgmstream->codec_data); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - free_celt_fsb(vgmstream->codec_data); - } -#endif - if (vgmstream->coding_type == coding_ACM) { free_acm(vgmstream->codec_data); } @@ -143,10 +111,6 @@ void decode_seek(VGMSTREAM* vgmstream) { seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample); } - if (vgmstream->coding_type == coding_RELIC) { - seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample); - } - if (vgmstream->coding_type == coding_ICE_RANGE || vgmstream->coding_type == coding_ICE_DCT) { seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample); @@ -164,40 +128,12 @@ void decode_seek(VGMSTREAM* vgmstream) { seek_ea_mt(vgmstream, vgmstream->loop_current_sample); } -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - seek_ffmpeg(vgmstream->codec_data, vgmstream->loop_current_sample); - } -#endif - #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) if (vgmstream->coding_type == coding_MP4_AAC) { seek_mp4_aac(vgmstream, vgmstream->loop_current_sample); } #endif -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - seek_atrac9(vgmstream, vgmstream->loop_current_sample); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - seek_celt_fsb(vgmstream, vgmstream->loop_current_sample); - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - seek_mpeg(vgmstream, vgmstream->loop_current_sample); - } -#endif - if (vgmstream->coding_type == coding_NWA) { seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample); } @@ -220,10 +156,6 @@ void decode_reset(VGMSTREAM* vgmstream) { reset_circus_vq(vgmstream->codec_data); } - if (vgmstream->coding_type == coding_RELIC) { - reset_relic(vgmstream->codec_data); - } - if (vgmstream->coding_type == coding_ICE_RANGE || vgmstream->coding_type == coding_ICE_DCT) { reset_ice(vgmstream->codec_data); @@ -247,16 +179,6 @@ void decode_reset(VGMSTREAM* vgmstream) { } #endif -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - reset_mpeg(vgmstream->codec_data); - } -#endif - #ifdef VGM_USE_G7221 if (vgmstream->coding_type == coding_G7221C) { reset_g7221(vgmstream->codec_data); @@ -269,24 +191,6 @@ void decode_reset(VGMSTREAM* vgmstream) { } #endif -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - reset_atrac9(vgmstream->codec_data); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - reset_celt_fsb(vgmstream->codec_data); - } -#endif - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - reset_ffmpeg(vgmstream->codec_data); - } -#endif - if (vgmstream->coding_type == coding_ACM) { reset_acm(vgmstream->codec_data); } @@ -342,13 +246,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) { case coding_PCM24BE: case coding_PCM32LE: return 1; -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: -#endif case coding_SDX2: case coding_SDX2_int: case coding_CBD2: @@ -464,10 +361,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) { #ifdef VGM_USE_G719 case coding_G719: return 48000/50; -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - return 0; #endif case coding_MTAF: return 128*2; @@ -497,22 +390,12 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) { return 0; /* 432, but variable in looped files */ case coding_CIRCUS_VQ: return 0; - case coding_RELIC: - return 0; /* 512 */ case coding_ICE_RANGE: case coding_ICE_DCT: return 0; /* ~100 (range), ~16 (DCT) */ #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: return mp4_get_samples_per_frame(vgmstream->codec_data); -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - return 0; /* varies with config data, usually 256 or 1024 */ -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* 512? */ #endif default: return 0; @@ -679,9 +562,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { #endif #ifdef VGM_USE_G719 case coding_G719: -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: #endif case coding_MTAF: return vgmstream->interleave_block_size; @@ -704,7 +584,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { /* UBI_ADPCM: varies per mode? */ /* IMUSE: VBR */ /* EA_MT: VBR, frames of bit counts or PCM frames */ - /* ATRAC9: CBR around 0x100-200 */ /* CELT FSB: varies, usually 0x80-100 */ /* TAC: VBR around ~0x200-300 */ default: /* (VBR or managed by decoder) */ @@ -1113,18 +992,10 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) { case coding_CIRCUS_VQ: decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); break; - case coding_RELIC: - decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do); - break; case coding_ICE_RANGE: case coding_ICE_DCT: decode_ice(vgmstream->codec_data, buffer, samples_to_do); break; -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); - break; -#endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); @@ -1316,15 +1187,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) { } break; -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: - decode_mpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); - break; -#endif #ifdef VGM_USE_G7221 case coding_G7221C: for (ch = 0; ch < vgmstream->channels; ch++) { @@ -1338,16 +1200,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) { decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); } break; -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - decode_atrac9(vgmstream, buffer, samples_to_do, vgmstream->channels); - break; -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels); - break; #endif case coding_ACM: decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixing.c b/Frameworks/vgmstream/vgmstream/src/base/mixing.c index 8a7806a97..dd943f721 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/base/mixing.c @@ -145,6 +145,18 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) { + if (vgmstream->layout_type == layout_layered) { + layered_layout_data* data = vgmstream->layout_data; + if (data) + return data->fmt; + } + + if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* data = vgmstream->layout_data; + if (data) + return data->fmt; + } + const codec_info_t* codec_info = codec_get_info(vgmstream); if (codec_info) { if (codec_info->sample_type) diff --git a/Frameworks/vgmstream/vgmstream/src/base/sbuf.c b/Frameworks/vgmstream/vgmstream/src/base/sbuf.c index b97c80760..a40cb6aaa 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/sbuf.c +++ b/Frameworks/vgmstream/vgmstream/src/base/sbuf.c @@ -5,10 +5,10 @@ #include "../util/log.h" // float-to-int modes -//#define PCM16_ROUNDING_LRINT // potentially faster in some systems/compilers and much slower in others +//#define PCM16_ROUNDING_LRINT // rounding down, potentially faster in some systems/compilers and much slower in others (also affects rounding) //#define PCM16_ROUNDING_HALF // rounding half + down (vorbis-style), more 'accurate' but slower -#ifdef PCM16_ROUNDING_HALF +#if defined(PCM16_ROUNDING_HALF) || defined(PCM16_ROUNDING_LRINT) #include #endif @@ -27,7 +27,7 @@ * It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions. */ static inline int float_to_int(float val) { -#if PCM16_ROUNDING_LRINT +#ifdef PCM16_ROUNDING_LRINT return lrintf(val); #elif defined(_MSC_VER) return (int)val; @@ -38,7 +38,7 @@ static inline int float_to_int(float val) { #if 0 static inline int double_to_int(double val) { -#if PCM16_ROUNDING_LRINT +#ifdef PCM16_ROUNDING_LRINT return lrint(val); #elif defined(_MSC_VER) return (int)val; @@ -439,7 +439,7 @@ void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int dst_max) typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int fade_duration); -#define DEFINE_SBUF_FADE(suffix, buftype) \ +#define DEFINE_SBUF_FADE(suffix, buftype, func) \ static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \ buftype* buf = sbuf->buf; \ int s = start * sbuf->channels; \ @@ -447,14 +447,14 @@ typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int while (s < s_end) { \ float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \ for (int i = 0; i < sbuf->channels; i++) { \ - buf[s] = float_to_int(buf[s] * fadedness); \ + buf[s] = func(buf[s] * fadedness); \ s++; \ } \ fade_pos++; \ } \ } -#define DEFINE_SBUF_FD24(suffix, buftype) \ +#define DEFINE_SBUF_FD24(suffix, buftype, func) \ static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \ buftype* buf = sbuf->buf; \ int s = start * sbuf->channels; \ @@ -462,17 +462,21 @@ typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int while (s < s_end) { \ float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \ for (int i = 0; i < sbuf->channels; i++) { \ - put_u24ne(buf + s * 3, float_to_int(get_s24ne(buf + s * 3) * fadedness) ); \ + put_u24ne(buf + s * 3, func(get_s24ne(buf + s * 3) * fadedness) ); \ s++; \ } \ fade_pos++; \ } \ } -DEFINE_SBUF_FADE(i16, int16_t); -DEFINE_SBUF_FADE(i32, int32_t); -DEFINE_SBUF_FADE(flt, float); -DEFINE_SBUF_FD24(o24, uint8_t); +// no need to clamp in fade outs +#define CONV_FADE_FLT(x) (x) +#define CONV_FADE_PCM(x) float_to_int(x) + +DEFINE_SBUF_FADE(i16, int16_t, CONV_FADE_PCM); +DEFINE_SBUF_FADE(i32, int32_t, CONV_FADE_PCM); +DEFINE_SBUF_FADE(flt, float, CONV_FADE_FLT); +DEFINE_SBUF_FD24(o24, uint8_t, CONV_FADE_PCM); void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { //TODO: use interpolated fadedness to improve performance? diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 7e14359c9..148d354eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -1,28 +1,35 @@ -#include "coding.h" - #ifdef VGM_USE_ATRAC9 +#include "coding.h" +#include "../base/decode_state.h" +#include "../base/codec_info.h" #include "libatrac9/libatrac9.h" /* opaque struct */ -struct atrac9_codec_data { - uint8_t* data_buffer; - size_t data_buffer_size; +typedef struct { + uint8_t* buf; + int buf_size; - sample_t* sample_buffer; - size_t samples_filled; /* number of samples in the buffer */ - size_t samples_used; /* number of samples extracted from the buffer */ - - int samples_to_discard; + int16_t* sbuf; + int discard; atrac9_config config; - - void* handle; /* decoder handle */ - Atrac9CodecInfo info; /* decoder info */ -}; + Atrac9CodecInfo info; + void* handle; +} atrac9_codec_data; -atrac9_codec_data* init_atrac9(atrac9_config* cfg) { +static void free_atrac9(void* priv_data) { + atrac9_codec_data* data = priv_data; + if (!data) return; + + if (data->handle) Atrac9ReleaseHandle(data->handle); + free(data->buf); + free(data->sbuf); + free(data); +} + +void* init_atrac9(atrac9_config* cfg) { int status; uint8_t config_data[4]; atrac9_codec_data* data = NULL; @@ -47,16 +54,18 @@ atrac9_codec_data* init_atrac9(atrac9_config* cfg) { } - /* must hold at least one superframe and its samples */ - data->data_buffer_size = data->info.superframeSize; - /* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */ - data->data_buffer = calloc(data->data_buffer_size + 0x10, sizeof(uint8_t)); - if (!data->data_buffer) goto fail; - /* while ATRAC9 uses float internally, Sony's API only returns PCM16 */ - data->sample_buffer = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t)); - if (!data->sample_buffer) goto fail; + // must hold at least one superframe and its samples + data->buf_size = data->info.superframeSize; - data->samples_to_discard = cfg->encoder_delay; + // extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) + data->buf = calloc(data->buf_size + 0x10, sizeof(uint8_t)); + if (!data->buf) goto fail; + + // while ATRAC9 uses float internally, Sony's API only returns PCM16 + data->sbuf = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t)); + if (!data->sbuf) goto fail; + + data->discard = cfg->encoder_delay; memcpy(&data->config, cfg, sizeof(atrac9_config)); @@ -67,162 +76,132 @@ fail: return NULL; } -void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; - atrac9_codec_data* data = vgmstream->codec_data; - int samples_done = 0; - while (samples_done < samples_to_do) { +// read one raw block (superframe) and advance offsets; CBR and around 0x100-200 bytes +static bool read_frame(VGMSTREAM* v) { + VGMSTREAMCHANNEL* vs = &v->ch[0]; + atrac9_codec_data* data = v->codec_data; - if (data->samples_filled) { /* consume samples */ - int samples_to_get = data->samples_filled; + int to_read = data->info.superframeSize; + int bytes = read_streamfile(data->buf, vs->offset, to_read, vs->streamfile); - if (data->samples_to_discard) { - /* discard samples for looping */ - if (samples_to_get > data->samples_to_discard) - samples_to_get = data->samples_to_discard; - data->samples_to_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; + vs->offset += bytes; - memcpy(outbuf + samples_done*channels, - data->sample_buffer + data->samples_used*channels, - samples_to_get*channels * sizeof(sample_t)); - - samples_done += samples_to_get; - } - - /* mark consumed samples */ - data->samples_used += samples_to_get; - data->samples_filled -= samples_to_get; - } - else { /* decode data */ - int iframe, status; - int bytes_used = 0; - uint8_t *buffer = data->data_buffer; - size_t bytes; - - data->samples_used = 0; - - /* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives - * superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */ - - /* read one raw block (superframe) and advance offsets */ - bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile); - if (bytes != data->data_buffer_size) goto decode_fail; - - stream->offset += bytes; - - /* decode all frames in the superframe block */ - for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) { - status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used); - if (status < 0) goto decode_fail; - - buffer += bytes_used; - data->samples_filled += data->info.frameSamples; - } - } - } - - return; - -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("ATRAC9: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels); + return (bytes == to_read); } -void reset_atrac9(atrac9_codec_data* data) { - if (!data) return; +static int decode(VGMSTREAM* v) { + int channels = v->channels; + atrac9_codec_data* data = v->codec_data; - if (!data->handle) - goto fail; + uint8_t* buf = data->buf; + int16_t* sbuf = data->sbuf; + + // decode all frames in the superframe block + int samples = 0; + for (int iframe = 0; iframe < data->info.framesInSuperframe; iframe++) { + int bytes_used = 0; + + int status = Atrac9Decode(data->handle, buf, sbuf, &bytes_used); + if (status < 0) { + VGM_LOG("ATRAC): decode error %i\n", status); + return false; + } + + buf += bytes_used; + sbuf += data->info.frameSamples * channels; + samples += data->info.frameSamples; + } + + return samples; +} + +// ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives +// superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). +static bool decode_frame_atrac9(VGMSTREAM* v) { + bool ok = read_frame(v); + if (!ok) + return false; + + int samples = decode(v); + if (samples <= 0) + return false; + + decode_state_t* ds = v->decode_state; + atrac9_codec_data* data = v->codec_data; + + sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels); + ds->sbuf.filled = samples; + + if (data->discard) { + ds->discard += data->discard; + data->discard = 0; + } + + return true; +} + +void reset_atrac9(void* priv_data) { + atrac9_codec_data* data = priv_data; + if (!data || !data->handle) + return; + + data->discard = data->config.encoder_delay; #if 0 /* reopen/flush, not needed as superframes decode separatedly and there is no carried state */ { - int status; - uint8_t config_data[4]; - Atrac9ReleaseHandle(data->handle); data->handle = Atrac9GetHandle(); - if (!data->handle) goto fail; + if (!data->handle) return; + uint8_t config_data[4]; put_u32be(config_data, data->config.config_data); - status = Atrac9InitDecoder(data->handle, config_data); + + int status = Atrac9InitDecoder(data->handle, config_data); if (status < 0) goto fail; } #endif - - data->samples_used = 0; - data->samples_filled = 0; - data->samples_to_discard = data->config.encoder_delay; - - return; - -fail: - return; /* decode calls should fail... */ } -void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) { - atrac9_codec_data* data = vgmstream->codec_data; +void seek_atrac9(VGMSTREAM* v, int32_t num_sample) { + atrac9_codec_data* data = v->codec_data; if (!data) return; reset_atrac9(data); - /* find closest offset to desired sample, and samples to discard after that offset to reach loop */ - { - int32_t seek_sample = data->config.encoder_delay + num_sample; - off_t seek_offset; - int32_t seek_discard; - int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe; - size_t superframe_number, superframe_back; + // find closest offset to desired sample, and samples to discard after that offset to reach loop + int32_t seek_sample = data->config.encoder_delay + num_sample; + int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe; - superframe_number = (seek_sample / superframe_samples); /* closest */ + // decoded frames affect each other slightly, so move offset back to make PCM stable + // and equivalent to a full discard loop + int superframe_number = (seek_sample / superframe_samples); // closest + int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe) + if (superframe_back > superframe_number) + superframe_back = superframe_number; - /* decoded frames affect each other slightly, so move offset back to make PCM stable - * and equivalent to a full discard loop */ - superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */ - if (superframe_back > superframe_number) - superframe_back = superframe_number; + int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples); + off_t seek_offset = (superframe_number - superframe_back) * data->info.superframeSize; - seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples); - seek_offset = (superframe_number - superframe_back) * data->info.superframeSize; + data->discard = seek_discard; // already includes encoder delay - data->samples_to_discard = seek_discard; /* already includes encoder delay */ - - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset; + if (v->loop_ch) { + v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset; } #if 0 //old full discard loop - { - data->samples_to_discard = num_sample; - data->samples_to_discard += data->config.encoder_delay; + data->discard = num_sample; + data->discard += data->config.encoder_delay; - /* loop offsets are set during decode; force them to stream start so discard works */ - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; - } + // loop offsets are set during decode; force them to stream start so discard works + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; #endif - } -void free_atrac9(atrac9_codec_data* data) { - if (!data) return; - - if (data->handle) Atrac9ReleaseHandle(data->handle); - free(data->data_buffer); - free(data->sample_buffer); - free(data); -} - - static int atrac9_parse_config(uint32_t config_data, int* p_sample_rate, int* p_channels, size_t* p_frame_size, size_t* p_samples_per_frame) { static const int sample_rate_table[16] = { 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, @@ -266,7 +245,8 @@ fail: return 0; } -size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) { +size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data) { + atrac9_codec_data* data = priv_data; return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); } @@ -276,4 +256,12 @@ size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data) { return 0; return bytes / frame_size * samples_per_frame; } + +const codec_info_t atrac9_decoder = { + .sample_type = SFMT_S16, //TODO: decoder doesn't return float (to match Sony's lib apparently) + .decode_frame = decode_frame_atrac9, + .free = free_atrac9, + .reset = reset_atrac9, + .seek = seek_atrac9, +}; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c index 27308a32d..ace502383 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c @@ -1,6 +1,7 @@ -#include "coding.h" - #ifdef VGM_USE_CELT +#include "coding.h" +#include "../base/decode_state.h" +#include "../base/codec_info.h" #include "celt/celt_fsb.h" #define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */ @@ -9,204 +10,25 @@ #define FSB_CELT_INTERNAL_SAMPLE_RATE 44100 #define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */ +typedef enum { CELT_0_06_1, CELT_0_11_0} celt_lib_t; + /* opaque struct */ -struct celt_codec_data { - sample_t* buffer; +typedef struct { + uint8_t buf[FSB_CELT_MAX_DATA_SIZE]; + int frame_size; // current size - sample_t* sample_buffer; - size_t samples_filled; /* number of samples in the buffer */ - size_t samples_used; /* number of samples extracted from the buffer */ + int16_t* sbuf; - int samples_to_discard; + int discard; int channel_mode; celt_lib_t version; void* mode_handle; void* decoder_handle; -}; +} celt_codec_data; - -/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). - * FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */ - -celt_codec_data* init_celt_fsb(int channels, celt_lib_t version) { - int error = 0, lib_version = 0; - celt_codec_data* data = NULL; - - - data = calloc(1, sizeof(celt_codec_data)); - if (!data) goto fail; - - data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */ - data->version = version; - - switch(data->version) { - case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */ - data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error); - if (!data->mode_handle || error != CELT_OK) goto fail; - - error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); - if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail; - - data->decoder_handle = celt_decoder_create_0061(data->mode_handle); - if (!data->decoder_handle) goto fail; - break; - - case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */ - data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */ - if (!data->mode_handle || error != CELT_OK) goto fail; - - error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); - if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail; - - data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error); - if (!data->decoder_handle || error != CELT_OK) goto fail; - break; - - default: - goto fail; - } - - data->sample_buffer = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(sample_t)); - if (!data->sample_buffer) goto fail; - /* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */ - - return data; - -fail: - free_celt_fsb(data); - return NULL; -} - - -void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; - celt_codec_data* data = vgmstream->codec_data; - int samples_done = 0; - - - while (samples_done < samples_to_do) { - - if (data->samples_filled) { /* consume samples */ - int samples_to_get = data->samples_filled; - - if (data->samples_to_discard) { - /* discard samples for looping */ - if (samples_to_get > data->samples_to_discard) - samples_to_get = data->samples_to_discard; - data->samples_to_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; - - memcpy(outbuf + samples_done*channels, - data->sample_buffer + data->samples_used*channels, - samples_to_get*channels * sizeof(sample_t)); - - samples_done += samples_to_get; - } - - /* mark consumed samples */ - data->samples_used += samples_to_get; - data->samples_filled -= samples_to_get; - } - else { /* decode data */ - int status; - uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0}; - size_t bytes, frame_size; - - - data->samples_used = 0; - - /* FSB DLLs do seem to check this fixed value */ - if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) { - goto decode_fail; - } - - frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile); - if (frame_size > FSB_CELT_MAX_DATA_SIZE) { - goto decode_fail; - } - - /* read and decode one raw block and advance offsets */ - bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile); - if (bytes != frame_size) goto decode_fail; - - switch(data->version) { - case CELT_0_06_1: - status = celt_decode_0061(data->decoder_handle, data_buffer,bytes, data->sample_buffer); - break; - - case CELT_0_11_0: - status = celt_decode_0110(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME); - break; - - default: - goto decode_fail; - } - if (status != CELT_OK) goto decode_fail; - - stream->offset += 0x04+0x04+frame_size; - data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME; - } - } - - return; - -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels); -} - -void reset_celt_fsb(celt_codec_data* data) { - if (!data) return; - - /* recreate decoder (mode should not change) */ - switch(data->version) { - case CELT_0_06_1: - if (data->decoder_handle) celt_decoder_destroy_0061(data->decoder_handle); - - data->decoder_handle = celt_decoder_create_0061(data->mode_handle); - if (!data->decoder_handle) goto fail; - break; - - case CELT_0_11_0: - if (data->decoder_handle) celt_decoder_destroy_0110(data->decoder_handle); - - data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL); - if (!data->decoder_handle) goto fail; - break; - - default: - goto fail; - } - - data->samples_used = 0; - data->samples_filled = 0; - data->samples_to_discard = 0; - - return; -fail: - return; /* decode calls should fail... */ -} - -void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) { - celt_codec_data* data = vgmstream->codec_data; - if (!data) return; - - reset_celt_fsb(data); - - data->samples_to_discard = num_sample; - - /* loop offsets are set during decode; force them to stream start so discard works */ - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; -} - -void free_celt_fsb(celt_codec_data* data) { +static void free_celt_fsb(void* priv_data) { + celt_codec_data* data = priv_data; if (!data) return; switch(data->version) { @@ -224,7 +46,189 @@ void free_celt_fsb(celt_codec_data* data) { break; } - free(data->sample_buffer); + free(data->sbuf); free(data); } + +/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). + * FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */ +static void* init_celt_fsb(int channels, celt_lib_t version) { + int error = 0, lib_version = 0; + celt_codec_data* data = NULL; + + + data = calloc(1, sizeof(celt_codec_data)); + if (!data) goto fail; + + data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */ + data->version = version; + + switch(data->version) { + case CELT_0_06_1: // older FSB4 (FMOD ~4.33) + data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error); + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail; + + data->decoder_handle = celt_decoder_create_0061(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: // newer FSB4 (FMOD ~4.34), FSB5 + data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */ + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail; + + data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error); + if (!data->decoder_handle || error != CELT_OK) goto fail; + break; + + default: + goto fail; + } + + data->sbuf = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(int16_t)); + if (!data->sbuf) goto fail; + + // ~128 samples of encoder delay, but FMOD DLLs don't discard them? + + return data; + +fail: + free_celt_fsb(data); + return NULL; +} + +void* init_celt_fsb_v1(int channels) { + return init_celt_fsb(channels, CELT_0_06_1); +} + +void* init_celt_fsb_v2(int channels) { + return init_celt_fsb(channels, CELT_0_11_0); +} + +// read and decode one raw block and advance offsets +static bool read_frame(VGMSTREAM* v) { + VGMSTREAMCHANNEL* vs = &v->ch[0]; + celt_codec_data* data = v->codec_data; + + // FSB DLLs do seem to check this fixed value + if (read_u32be(vs->offset + 0x00, vs->streamfile) != 0x17C30DF3) + return false; + + data->frame_size = read_u32le(vs->offset + 0x04, vs->streamfile); + if (data->frame_size > FSB_CELT_MAX_DATA_SIZE) + return false; + + int bytes = read_streamfile(data->buf, vs->offset+0x08, data->frame_size, vs->streamfile); + + vs->offset += 0x04 + 0x04 + data->frame_size; + + return (bytes == data->frame_size); +} + +static int decode(VGMSTREAM* v) { + celt_codec_data* data = v->codec_data; + + int samples = FSB_CELT_SAMPLES_PER_FRAME; + int status; + switch(data->version) { + case CELT_0_06_1: + status = celt_decode_0061(data->decoder_handle, data->buf, data->frame_size, data->sbuf); + if (status != CELT_OK) + return -1; + return samples; + + case CELT_0_11_0: + status = celt_decode_0110(data->decoder_handle, data->buf, data->frame_size, data->sbuf, samples); + if (status != CELT_OK) + return -1; + return samples; + + default: + return -1; + } +} + +static bool decode_frame_celt_fsb(VGMSTREAM* v) { + bool ok = read_frame(v); + if (!ok) + return false; + + int samples = decode(v); + if (samples <= 0) + return false; + + decode_state_t* ds = v->decode_state; + celt_codec_data* data = v->codec_data; + + sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels); + ds->sbuf.filled = samples; + + if (data->discard) { + ds->discard += data->discard; + data->discard = 0; + } + + return true; +} + + +void reset_celt_fsb(void* priv_data) { + celt_codec_data* data = priv_data; + if (!data) return; + + // recreate decoder (mode should not change) + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) + celt_decoder_destroy_0061(data->decoder_handle); + + data->decoder_handle = celt_decoder_create_0061(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: + if (data->decoder_handle) + celt_decoder_destroy_0110(data->decoder_handle); + + data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL); + if (!data->decoder_handle) goto fail; + break; + + default: + goto fail; + } + + data->discard = 0; + + return; +fail: + return; /* decode calls should fail... */ +} + +void seek_celt_fsb(VGMSTREAM* v, int32_t num_sample) { + celt_codec_data* data = v->codec_data; + if (!data) return; + + reset_celt_fsb(data); + + data->discard = num_sample; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (v->loop_ch) + v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset; +} + + +const codec_info_t celt_fsb_decoder = { + .sample_type = SFMT_S16, // decoder doesn't return float + .decode_frame = decode_frame_celt_fsb, + .free = free_celt_fsb, + .reset = reset_celt_fsb, + .seek = seek_celt_fsb, +}; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 06c53303d..7f70b1baa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -318,13 +318,7 @@ void free_ea_mt(ea_mt_codec_data* data, int channels); /* relic_decoder */ -typedef struct relic_codec_data relic_codec_data; - -relic_codec_data* init_relic(int channels, int bitrate, int codec_rate); -void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do); -void reset_relic(relic_codec_data* data); -void seek_relic(relic_codec_data* data, int32_t num_sample); -void free_relic(relic_codec_data* data); +void* init_relic(int channels, int bitrate, int codec_rate); int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate); @@ -451,15 +445,6 @@ void free_vorbis_custom(void* data); int32_t vorbis_custom_get_samples(VGMSTREAM* v); #endif -typedef struct { - int version; - int layer; - int bit_rate; - int sample_rate; - int frame_samples; - int frame_size; /* bytes */ - int channels; -} mpeg_frame_info; #ifdef VGM_USE_MPEG /* mpeg_decoder */ @@ -506,19 +491,27 @@ typedef struct { mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels); mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config* config); -void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); -void reset_mpeg(mpeg_codec_data* data); -void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample); -void free_mpeg(mpeg_codec_data* data); int mpeg_get_sample_rate(mpeg_codec_data* data); long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header); +bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey); +#endif + +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; +} mpeg_frame_info; bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info); bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info); -int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey); -#endif +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); +int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr); #ifdef VGM_USE_G7221 @@ -570,28 +563,16 @@ typedef struct { uint32_t config_data; /* ATRAC9 config header */ int encoder_delay; /* initial samples to discard */ } atrac9_config; -typedef struct atrac9_codec_data atrac9_codec_data; -atrac9_codec_data* init_atrac9(atrac9_config* cfg); -void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); -void reset_atrac9(atrac9_codec_data* data); -void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample); -void free_atrac9(atrac9_codec_data* data); -size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data); +void* init_atrac9(atrac9_config* cfg); +size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data); size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data); #endif #ifdef VGM_USE_CELT -/* celt_fsb_decoder */ -typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; -typedef struct celt_codec_data celt_codec_data; - -celt_codec_data* init_celt_fsb(int channels, celt_lib_t version); -void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); -void reset_celt_fsb(celt_codec_data* data); -void seek_celt_fsb(VGMSTREAM* vgmstream, int32_t num_sample); -void free_celt_fsb(celt_codec_data* data); +void* init_celt_fsb_v1(int channels); +void* init_celt_fsb_v2(int channels); #endif @@ -610,10 +591,7 @@ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t s ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size); ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); -void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); -void reset_ffmpeg(ffmpeg_codec_data* data); -void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample); -void free_ffmpeg(ffmpeg_codec_data* data); +void free_ffmpeg(void* data); void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples); uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data); @@ -621,6 +599,7 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap); const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data); void ffmpeg_set_force_seek(ffmpeg_codec_data* data); void ffmpeg_set_invert_floats(ffmpeg_codec_data* data); +void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data); const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key); int32_t ffmpeg_get_samples(ffmpeg_codec_data* data); @@ -739,8 +718,6 @@ size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); -size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); -int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr); int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 182a10c03..516709d25 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -5,6 +5,8 @@ #include #include #include +#include "../base/decode_state.h" +#include "../base/codec_info.h" /* opaque struct */ struct ffmpeg_codec_data { @@ -26,32 +28,34 @@ struct ffmpeg_codec_data { int stream_index; int64_t total_samples; /* may be 0 and innacurate */ int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */ - int channel_remap_set; + bool channel_remap_set; int channel_remap[32]; /* map of channel > new position */ - int invert_floats_set; - int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */ - int force_seek; /* flags for special seeking in faulty formats */ - int bad_init; + bool invert_floats_set; + bool skip_samples_set; /* flag to know skip samples were manually added from vgmstream */ + bool force_seek; /* flags for special seeking in faulty formats */ + bool bad_init; // FFmpeg context used for metadata const AVCodec* codec; /* FFmpeg decoder state */ - unsigned char* buffer; + uint8_t* buffer; AVIOContext* ioCtx; AVFormatContext* formatCtx; AVCodecContext* codecCtx; AVFrame* frame; /* last decoded frame */ AVPacket* packet; /* last read data packet */ - int read_packet; - int end_of_stream; - int end_of_audio; + bool read_packet; + bool end_of_stream; + bool end_of_audio; - /* sample state */ - int32_t samples_discard; - int32_t samples_consumed; - int32_t samples_filled; + /* other state */ + int samples_discard; + + sfmt_t fmt; + void* sbuf; + int sbuf_samples; }; @@ -82,25 +86,53 @@ static void g_init_ffmpeg(void) { } } -static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) { - int ch_from,ch_to,s; - sample_t temp; - for (s = 0; s < sample_count; s++) { - for (ch_from = 0; ch_from < channels; ch_from++) { +static void remap_audio_flt(float* outbuf, int sample_count, int channels, int* channel_mappings) { + for (int s = 0; s < sample_count; s++) { + for (int ch_from = 0; ch_from < channels; ch_from++) { if (ch_from > 32) continue; - ch_to = channel_mappings[ch_from]; + int ch_to = channel_mappings[ch_from]; if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to) continue; - temp = outbuf[s*channels + ch_from]; + float temp = outbuf[s*channels + ch_from]; outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to]; outbuf[s*channels + ch_to] = temp; } } } +static sfmt_t convert_sample_type(ffmpeg_codec_data* data) { + switch (data->codecCtx->sample_fmt) { + // used? + case AV_SAMPLE_FMT_U8P: + case AV_SAMPLE_FMT_U8: + // common + case AV_SAMPLE_FMT_S16P: + case AV_SAMPLE_FMT_S16: + return SFMT_S16; + + // PCM32 and FLAC 24-bit (upscaled) + case AV_SAMPLE_FMT_S32P: + case AV_SAMPLE_FMT_S32: + return SFMT_S32; + + // transform-based codecs (ATRAC3, AAC, etc) + case AV_SAMPLE_FMT_FLTP: + case AV_SAMPLE_FMT_FLT: + return SFMT_FLT; + + // seemingly not used by any codec, only filters + case AV_SAMPLE_FMT_DBLP: + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_S64P: + case AV_SAMPLE_FMT_S64: + default: + return SFMT_NONE; + } +} + /** * Special patching for FFmpeg's buggy seek code. * @@ -198,7 +230,6 @@ test_seek: avcodec_flush_buffers(data->codecCtx); return 0; - fail: return -1; } @@ -357,7 +388,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he if (errcode < 0) goto fail; /* reset non-zero values */ - data->read_packet = 1; + data->read_packet = true; /* setup other values */ { @@ -377,6 +408,10 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); #endif + data->fmt = convert_sample_type(data); + if (data->fmt == SFMT_NONE) + goto fail; + /* try to guess frames/samples (duration isn't always set) */ data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb); if (data->total_samples < 0) @@ -545,23 +580,23 @@ fail: return -1; } + + /* decodes a new frame to internal data */ -static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { - int errcode; - int frame_error = 0; +static int decode_frame_internal(ffmpeg_codec_data* data) { + if (data->bad_init) + return -1; - if (data->bad_init) { - goto fail; - } - /* ignore once file is done (but not on EOF as FFmpeg can output samples until end_of_audio) */ if (/*data->end_of_stream ||*/ data->end_of_audio) { VGM_LOG("FFMPEG: decode after end of audio\n"); - goto fail; + return -1; } + bool frame_error = false; + /* read data packets until valid is found */ while (data->read_packet && !data->end_of_audio) { if (!data->end_of_stream) { @@ -569,19 +604,19 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { av_packet_unref(data->packet); /* read encoded data from demuxer into packet */ - errcode = av_read_frame(data->formatCtx, data->packet); + int errcode = av_read_frame(data->formatCtx, data->packet); if (errcode < 0) { if (errcode == AVERROR_EOF) { - data->end_of_stream = 1; /* no more data to read (but may "drain" samples) */ + data->end_of_stream = true; // no more data to read (but may "drain" samples) } else { VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode); - frame_error = 1; //goto fail; + frame_error = true; //goto fail; } if (data->formatCtx->pb && data->formatCtx->pb->error) { VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error); - frame_error = 1; //goto fail; + frame_error = true; //goto fail; } } @@ -591,31 +626,31 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { } /* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */ - errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet); + int errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet); if (errcode < 0) { if (errcode != AVERROR(EAGAIN)) { VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode); - frame_error = 1; //goto fail; + frame_error = true; //goto fail; } } - data->read_packet = 0; /* got data */ + data->read_packet = false; // got data } /* decode frame samples from sent packet or "drain" samples*/ if (!frame_error) { /* receive uncompressed sample data from decoded frame */ - errcode = avcodec_receive_frame(data->codecCtx, data->frame); + int errcode = avcodec_receive_frame(data->codecCtx, data->frame); if (errcode < 0) { if (errcode == AVERROR_EOF) { - data->end_of_audio = 1; /* no more audio, file is fully decoded */ + data->end_of_audio = true; // no more audio, file is fully decoded } else if (errcode == AVERROR(EAGAIN)) { - data->read_packet = 1; /* 0 samples, request more encoded data */ + data->read_packet = true; // 0 samples, request more encoded data } else { VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode); - frame_error = 1;//goto fail; + frame_error = true; //goto fail; } } } @@ -623,155 +658,83 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { /* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output * (ex. BlazBlue X360 022_btl_az.xwb) */ - - data->samples_consumed = 0; - data->samples_filled = data->frame->nb_samples; - return 1; -fail: - return 0; -} - - -/* When casting float to int value is simply truncated: - * - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1 - * (instead of 1.7 = 2, -1.7 = -2) - * - * Alts for more accurate rounding could be: - * - (int)floor(f32 * 32768.0) //not quite ok negatives - * - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style - * - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f) - * - (((int) (f1 + 32768.5)) - 32768) - * - etc - * but since +-1 isn't really audible we'll just cast as it's the fastest. - * - * Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE - * float requirements, but C99 adds some faster-but-less-precise casting functions - * we try to use (returning "long", though). They work ok without "fast float math" compiler - * flags, but probably should be enabled anyway to ensure no extra IEEE checks are needed. - * MSVC added this in VS2015 (_MSC_VER 1900) but don't seem correctly optimized and is very slow. - */ -static inline int float_to_int(float val) { -#if defined(_MSC_VER) - return (int)val; -#else - return lrintf(val); -#endif -} -static inline int double_to_int(double val) { -#if defined(_MSC_VER) - return (int)val; -#else - return lrint(val); /* returns long tho */ -#endif + return data->frame->nb_samples; } /* sample copy helpers, using different functions to minimize branches. - * - * in theory, small optimizations like *outbuf++ vs outbuf[i] or alt clamping - * would matter for performance, but in practice aren't very noticeable; - * keep it simple for now until more tests are done. * * in normal (interleaved) formats samples are laid out straight * (ibuf[s*chs+ch], ex. 4ch with 4s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3) * in "p" (planar) formats samples are in planes per channel * (ibuf[ch][s], ex. 4ch with 4s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) - * - * alt float clamping: - * clamp_float(f32) - * int s16 = (int)(f32 * 32768.0f); - * if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) - * s16 = (s16 >> 31) ^ 0x7FFF; */ -static void samples_silence_s16(sample_t* obuf, int ochs, int samples) { - int s, total_samples = samples * ochs; - for (s = 0; s < total_samples; s++) { - obuf[s] = 0; /* memset'd */ +static void samples_u8_to_s16(int16_t* obuf, uint8_t* ibuf, int ichs, int samples) { + for (int s = 0; s < samples * ichs; s++) { + obuf[s] = ((int)ibuf[s] - 0x80) << 8; } } - -static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) { - int s, total_samples = samples * ichs; - for (s = 0; s < total_samples; s++) { - obuf[s] = ((int)ibuf[skip*ichs + s] - 0x80) << 8; - } -} -static void samples_u8p_to_s16(sample_t* obuf, uint8_t** ibuf, int ichs, int samples, int skip) { - int s, ch; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - obuf[s*ichs + ch] = ((int)ibuf[ch][skip + s] - 0x80) << 8; +static void samples_u8p_to_s16(int16_t* obuf, uint8_t** ibuf, int ichs, int samples) { + for (int ch = 0; ch < ichs; ch++) { + for (int s = 0; s < samples; s++) { + obuf[s*ichs + ch] = ((int)ibuf[ch][s] - 0x80) << 8; } } } -static void samples_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) { - int s, total_samples = samples * ichs; - for (s = 0; s < total_samples; s++) { - obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */ +static void samples_s16_to_s16(int16_t* obuf, int16_t* ibuf, int ichs, int samples) { + for (int s = 0; s < samples * ichs; s++) { + obuf[s] = ibuf[s]; } } -static void samples_s16p_to_s16(sample_t* obuf, int16_t** ibuf, int ichs, int samples, int skip) { - int s, ch; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - obuf[s*ichs + ch] = ibuf[ch][skip + s]; +static void samples_s16p_to_s16(int16_t* obuf, int16_t** ibuf, int ichs, int samples) { + for (int ch = 0; ch < ichs; ch++) { + for (int s = 0; s < samples; s++) { + obuf[s*ichs + ch] = ibuf[ch][s]; } } } -static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) { - int s, total_samples = samples * ichs; - for (s = 0; s < total_samples; s++) { - obuf[s] = ibuf[skip*ichs + s] >> 16; +static void samples_s32_to_s32(int32_t* obuf, int32_t* ibuf, int ichs, int samples) { + for (int s = 0; s < samples * ichs; s++) { + obuf[s] = ibuf[ichs + s]; } } -static void samples_s32p_to_s16(sample_t* obuf, int32_t** ibuf, int ichs, int samples, int skip) { - int s, ch; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - obuf[s*ichs + ch] = ibuf[ch][skip + s] >> 16; +static void samples_s32p_to_s32(int32_t* obuf, int32_t** ibuf, int ichs, int samples) { + for (int ch = 0; ch < ichs; ch++) { + for (int s = 0; s < samples; s++) { + obuf[s*ichs + ch] = ibuf[ch][s]; } } } -static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) { - int s, total_samples = samples * ichs; - float scale = invert ? -32768.0f : 32768.0f; - for (s = 0; s < total_samples; s++) { - obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale)); +static void samples_flt_to_flt(float* obuf, float* ibuf, int ichs, int samples, bool invert) { + float scale = invert ? -1.0f : 1.0; + for (int s = 0; s < samples * ichs; s++) { + obuf[s] = ibuf[s] * scale; } } -static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) { - int s, ch; - float scale = invert ? -32768.0f : 32768.0f; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - obuf[s*ichs + ch] = clamp16(float_to_int(ibuf[ch][skip + s] * scale)); - } - } -} -static void samples_dbl_to_s16(sample_t* obuf, double* ibuf, int ichs, int samples, int skip) { - int s, total_samples = samples * ichs; - for (s = 0; s < total_samples; s++) { - obuf[s] = clamp16(double_to_int(ibuf[skip*ichs + s] * 32768.0)); - } -} -static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int samples, int skip) { - int s, ch; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - obuf[s*ichs + ch] = clamp16(double_to_int(inbuf[ch][skip + s] * 32768.0)); +static void samples_fltp_to_flt(float* obuf, float** ibuf, int ichs, int samples, bool invert) { + float scale = invert ? -1.0f : 1.0; + for (int ch = 0; ch < ichs; ch++) { + for (int s = 0; s < samples; s++) { + obuf[s*ichs + ch] = ibuf[ch][s] * scale; } } } -static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do, int max_channels) { +static void samples_s32_to_s24(int32_t* obuf, int32_t* ibuf, int ichs, int samples) { + for (int s = 0; s < samples * ichs; s++) { + obuf[s] = ibuf[s] >> 8; + } +} + +static void copy_samples(ffmpeg_codec_data* data, void* sbuf, int samples_to_do, int max_channels) { #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) int channels = data->codecCtx->channels; #else int channels = data->codecCtx->ch_layout.nb_channels; #endif int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1); - void* ibuf; + void* ibuf; if (is_planar) { ibuf = data->frame->extended_data; } @@ -786,81 +749,100 @@ static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_ } switch (data->codecCtx->sample_fmt) { - /* unused? */ - case AV_SAMPLE_FMT_U8P: if (is_planar) { samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } - // fall through - case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; + case AV_SAMPLE_FMT_U8P: + case AV_SAMPLE_FMT_U8: + if (is_planar) + samples_u8p_to_s16(sbuf, ibuf, channels, samples_to_do); + else + samples_u8_to_s16(sbuf, ibuf, channels, samples_to_do); + break; - /* common */ - case AV_SAMPLE_FMT_S16P: if (is_planar) { samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } - // fall through - case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; + case AV_SAMPLE_FMT_S16P: + case AV_SAMPLE_FMT_S16: + if (is_planar) + samples_s16p_to_s16(sbuf, ibuf, channels, samples_to_do); + else + samples_s16_to_s16(sbuf, ibuf, channels, samples_to_do); + break; - /* possibly FLAC and other lossless codecs */ - case AV_SAMPLE_FMT_S32P: if (is_planar) { samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } - // fall through - case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; + case AV_SAMPLE_FMT_S32P: + case AV_SAMPLE_FMT_S32: + if (is_planar) + samples_s32p_to_s32(sbuf, ibuf, channels, samples_to_do); + else + samples_s32_to_s32(sbuf, ibuf, channels, samples_to_do); + break; - /* mainly MDCT-like codecs (Ogg, AAC, etc) */ - case AV_SAMPLE_FMT_FLTP: if (is_planar) { samples_fltp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break; } - // fall through - case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break; - - /* possibly PCM64 only (not enabled) */ - case AV_SAMPLE_FMT_DBLP: if (is_planar) { samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } - // fall through - case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; + case AV_SAMPLE_FMT_FLTP: + case AV_SAMPLE_FMT_FLT: + if (is_planar) + samples_fltp_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set); + else + samples_flt_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set); + break; default: break; } - if (data->channel_remap_set) - remap_audio(outbuf, samples_to_do, channels, data->channel_remap); -} - -/* decode samples of any kind of FFmpeg format */ -void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { - ffmpeg_codec_data* data = vgmstream->codec_data; - - - while (samples_to_do > 0) { - - if (data->samples_consumed < data->samples_filled) { - /* consume samples */ - int samples_to_get = (data->samples_filled - data->samples_consumed); - - if (data->samples_discard) { - /* discard samples for looping */ - if (samples_to_get > data->samples_discard) - samples_to_get = data->samples_discard; - data->samples_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do) - samples_to_get = samples_to_do; - - copy_samples(data, outbuf, samples_to_get, channels); - - samples_to_do -= samples_to_get; - outbuf += samples_to_get * channels; - } - - /* mark consumed samples */ - data->samples_consumed += samples_to_get; - } - else { - int ok = decode_ffmpeg_frame(data); - if (!ok) goto decode_fail; - } + // FFmpeg can't do PCM24 and upsamples to PCM32, if needed + if (data->fmt == SFMT_S24) { + samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do); } - return; +} -decode_fail: - VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do); - samples_silence_s16(outbuf, channels, samples_to_do); +void remap_audio(ffmpeg_codec_data* data, sbuf_t* sbuf) { + if (!data->channel_remap_set) + return; + + switch(sbuf->fmt) { + case SFMT_FLT: + remap_audio_flt(sbuf->buf, sbuf->filled, sbuf->channels, data->channel_remap); + break; + default: // trivial to add but not used + VGM_LOG("FFMPEG: unsupported channel remapping found (implement)\n"); + break; + } +} + +// ensure output buffer can handle samples, since in theory it could change per call +static bool prepare_sbuf(ffmpeg_codec_data* data, int samples, int channels) { + if (!data->sbuf && data->sbuf_samples >= samples) + return true; + + free(data->sbuf); + + data->sbuf_samples = samples * 2; + data->sbuf = malloc(data->sbuf_samples * channels * sizeof(float)); + if (!data->sbuf) return false; + + return true; +} + +bool decode_frame_ffmpeg(VGMSTREAM* v) { + decode_state_t* ds = v->decode_state; + ffmpeg_codec_data* data = v->codec_data; + + int samples = decode_frame_internal(data); + if (samples < 0) + return false; + + if (!prepare_sbuf(data, samples, v->channels)) + return false; + copy_samples(data, data->sbuf, samples, v->channels); + + sbuf_init(&ds->sbuf, data->fmt, data->sbuf, samples, v->channels); + ds->sbuf.filled = samples; + + remap_audio(data, &ds->sbuf); + + if (data->samples_discard) { + ds->discard = data->samples_discard; + data->samples_discard = 0; + } + + return true; } @@ -868,11 +850,7 @@ decode_fail: /* UTILS */ /* ******************************************** */ -void reset_ffmpeg(ffmpeg_codec_data* data) { - seek_ffmpeg(data, 0); -} - -void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) { +static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) { if (!data) return; /* Start from 0 and discard samples until sample (slower but not too noticeable). @@ -897,13 +875,11 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) { avcodec_flush_buffers(data->codecCtx); } - data->samples_consumed = 0; - data->samples_filled = 0; data->samples_discard = num_sample; - data->read_packet = 1; - data->end_of_stream = 0; - data->end_of_audio = 0; + data->read_packet = true; + data->end_of_stream = false; + data->end_of_audio = false; /* consider skip samples (encoder delay), if manually set */ if (data->skip_samples_set) { @@ -918,6 +894,15 @@ fail: } +static void seek_ffmpeg(VGMSTREAM* v, int32_t num_sample) { + seek_ffmpeg_internal(v->codec_data, num_sample); +} + +static void reset_ffmpeg(void* priv_data) { + seek_ffmpeg_internal(priv_data, 0); +} + + static void free_ffmpeg_config(ffmpeg_codec_data* data) { if (data == NULL) return; @@ -946,10 +931,11 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) { av_freep(&(data->buffer)); } - //todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option + //TODO: avformat_find_stream_info may cause some Win Handle leaks? related to certain option } -void free_ffmpeg(ffmpeg_codec_data* data) { +void free_ffmpeg(void* priv_data) { + ffmpeg_codec_data* data = priv_data; if (data == NULL) return; @@ -1003,9 +989,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) { #endif /* set skip samples with our internal discard */ - data->skip_samples_set = 1; - data->samples_discard = skip_samples; + data->skip_samples_set = true; data->skip_samples = skip_samples; + data->samples_discard = skip_samples; } /* returns channel layout if set */ @@ -1032,8 +1018,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) { /* yet another hack to fix codecs that encode channels in different order and reorder on decoder * but FFmpeg doesn't do it automatically * (maybe should be done via mixing, but could clash with other stuff?) */ -void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) { - int i; +void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) { #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) int channels = data->codecCtx->channels; #else @@ -1043,10 +1028,12 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) { if (channels > 32) return; - for (i = 0; i < channels; i++) { + for (int i = 0; i < channels; i++) { data->channel_remap[i] = channel_remap[i]; } - data->channel_remap_set = 1; + + VGM_LOG("FFMPEG: channel remapping set\n"); + data->channel_remap_set = true; } const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) { @@ -1065,7 +1052,7 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) { /* some formats like Smacker are so buggy that any seeking is impossible (even on video players), * or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset). * whatever, we'll just kill and reconstruct FFmpeg's config every time */ - data->force_seek = 1; + data->force_seek = true; reset_ffmpeg(data); /* reset state from trying to seek */ //stream = data->formatCtx->streams[data->stream_index]; } @@ -1073,7 +1060,16 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) { void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) { if (!data) return; - data->invert_floats_set = 1; + data->invert_floats_set = true; +} + +// for flac 24-bit, since FFMpeg upsamples to PCM32 (ignored if flac 16-bit) +void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data) { + if (!data) + return; + if (data->fmt != SFMT_S32) + return; + data->fmt = SFMT_S24; } const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) { @@ -1131,4 +1127,22 @@ STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) { if (!data) return NULL; return data->sf; } + +static sfmt_t get_sample_type_ffmpeg(VGMSTREAM* v) { + ffmpeg_codec_data* data = v->codec_data; + if (!data) + return SFMT_NONE; + + return data->fmt; +} + + +const codec_info_t ffmpeg_decoder = { + .get_sample_type = get_sample_type_ffmpeg, + .decode_frame = decode_frame_ffmpeg, + .free = free_ffmpeg, + .reset = reset_ffmpeg, + .seek = seek_ffmpeg, +}; + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c index bc58cd21d..e7cbe099b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c @@ -84,7 +84,7 @@ static bool read_frame(VGMSTREAM* v) { return true; } -bool decode_frame_ka1a(VGMSTREAM* v) { +static bool decode_frame_ka1a(VGMSTREAM* v) { bool ok = read_frame(v); if (!ok) return false; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.c index 9da438fca..a9ffda852 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.c @@ -1,4 +1,5 @@ #include +#include #include #include #include "relic_lib.h" @@ -14,7 +15,6 @@ extern void relic_mixfft_fft(int n, float* xRe, float* xIm, float* yRe, float* yIm); -#define RELIC_MAX_CHANNELS 2 #define RELIC_MAX_SCALES 6 #define RELIC_BASE_SCALE 10.0f #define RELIC_FREQUENCY_MASKING_FACTOR 1.0f @@ -61,10 +61,9 @@ static const int16_t critical_band_data[RELIC_CRITICAL_BAND_COUNT] = { }; static void init_dct(float* dct, int dct_size) { - int i; - int dct_quarter = dct_size >> 2; + const int dct_quarter = dct_size >> 2; - for (i = 0; i < dct_quarter; i++) { + for (int i = 0; i < dct_quarter; i++) { double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size); dct[i] = sin(temp); dct[dct_quarter + i] = cos(temp); @@ -72,19 +71,17 @@ static void init_dct(float* dct, int dct_size) { } static int apply_idct(const float* freq, float* wave, const float* dct, int dct_size) { - int i; - float factor; float out_re[RELIC_MAX_FFT]; float out_im[RELIC_MAX_FFT]; float in_re[RELIC_MAX_FFT]; float in_im[RELIC_MAX_FFT]; float wave_tmp[RELIC_MAX_SIZE]; - int dct_half = dct_size >> 1; - int dct_quarter = dct_size >> 2; - int dct_3quarter = 3 * (dct_size >> 2); + const int dct_half = dct_size >> 1; + const int dct_quarter = dct_size >> 2; + const int dct_3quarter = 3 * (dct_size >> 2); /* prerotation? */ - for (i = 0; i < dct_quarter; i++) { + for (int i = 0; i < dct_quarter; i++) { float coef1 = freq[2 * i] * 0.5f; float coef2 = freq[dct_half - 1 - 2 * i] * 0.5f; in_re[i] = coef1 * dct[dct_quarter + i] + coef2 * dct[i]; @@ -95,32 +92,31 @@ static int apply_idct(const float* freq, float* wave, const float* dct, int dct_ relic_mixfft_fft(dct_quarter, in_re, in_im, out_re, out_im); /* postrotation, window and reorder? */ - factor = 8.0 / sqrt(dct_size); - for (i = 0; i < dct_quarter; i++) { + float factor = 8.0 / sqrt(dct_size); + for (int i = 0; i < dct_quarter; i++) { float out_re_i = out_re[i]; out_re[i] = (out_re[i] * dct[dct_quarter + i] + out_im[i] * dct[i]) * factor; out_im[i] = (-out_re_i * dct[i] + out_im[i] * dct[dct_quarter + i]) * factor; wave_tmp[i * 2] = out_re[i]; wave_tmp[i * 2 + dct_half] = out_im[i]; } - for (i = 1; i < dct_size; i += 2) { + for (int i = 1; i < dct_size; i += 2) { wave_tmp[i] = -wave_tmp[dct_size - 1 - i]; } /* wave mix thing? */ - for (i = 0; i < dct_3quarter; i++) { + for (int i = 0; i < dct_3quarter; i++) { wave[i] = wave_tmp[dct_quarter + i]; } - for (i = dct_3quarter; i < dct_size; i++) { + for (int i = dct_3quarter; i < dct_size; i++) { wave[i] = -wave_tmp[i - dct_3quarter]; } return 0; } static void decode_frame(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_size) { - int i; float wave_tmp[RELIC_MAX_SIZE]; - int dct_half = dct_size >> 1; + const int dct_half = dct_size >> 1; /* copy for first half(?) */ memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float)); @@ -130,22 +126,19 @@ static void decode_frame(const float* freq1, const float* freq2, float* wave_cur apply_idct(freq2, wave_prv, dct, dct_size); /* overlap and apply window function to filter this block's beginning */ - for (i = 0; i < dct_half; i++) { + for (int i = 0; i < dct_half; i++) { wave_cur[dct_half + i] = wave_tmp[i] * window[i] + wave_cur[dct_half + i] * window[dct_half + i]; wave_prv[i] = wave_prv[i] * window[i] + wave_tmp[dct_half + i] * window[dct_half + i]; } } static void init_window(float *window, int dct_size) { - int i; - - for (i = 0; i < dct_size; i++) { + for (int i = 0; i < dct_size; i++) { window[i] = sin((float)i * (RELIC_PI / dct_size)); } } static void decode_frame_base(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_mode, int samples_mode) { - int i; float wave_tmp[RELIC_MAX_SIZE]; /* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */ @@ -160,7 +153,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav if (dct_mode == RELIC_SIZE_LOW) { /* 128 DCT to 256 samples (repeat sample x2) */ decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW); - for (i = 0; i < 256 - 1; i += 2) { + for (int i = 0; i < 256 - 1; i += 2) { wave_cur[i + 0] = wave_tmp[i >> 1]; wave_cur[i + 1] = wave_tmp[i >> 1]; } @@ -174,7 +167,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav if (dct_mode == RELIC_SIZE_LOW) { /* 128 DCT to 512 samples (repeat sample x4) */ decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW); - for (i = 0; i < 512 - 1; i += 4) { + for (int i = 0; i < 512 - 1; i += 4) { wave_cur[i + 0] = wave_tmp[i >> 2]; wave_cur[i + 1] = wave_tmp[i >> 2]; wave_cur[i + 2] = wave_tmp[i >> 2]; @@ -184,7 +177,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav else if (dct_mode == RELIC_SIZE_MID) { /* 256 DCT to 512 samples (repeat sample x2) */ decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID); - for (i = 0; i < 512 - 1; i += 2) { + for (int i = 0; i < 512 - 1; i += 2) { wave_cur[i + 0] = wave_tmp[i >> 1]; wave_cur[i + 1] = wave_tmp[i >> 1]; } @@ -225,25 +218,22 @@ static int read_sbits(uint8_t bits, uint32_t offset, uint8_t* buf) { } static void init_dequantization(float* scales) { - int i; - scales[0] = RELIC_BASE_SCALE; - for (i = 1; i < RELIC_MAX_SCALES; i++) { + for (int i = 1; i < RELIC_MAX_SCALES; i++) { scales[i] = scales[i - 1] * scales[0]; } - for (i = 0; i < RELIC_MAX_SCALES; i++) { + for (int i = 0; i < RELIC_MAX_SCALES; i++) { scales[i] = RELIC_FREQUENCY_MASKING_FACTOR / (double) ((1 << (i + 1)) - 1) * scales[i]; } } -static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) { +static bool unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) { uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits; - int qv, pos; + int qv; uint8_t ev; uint8_t move; uint32_t bit_offset, max_offset; - int i, j; - int freq_half = freq_size >> 1; + const int freq_half = freq_size >> 1; memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float)); @@ -263,8 +253,8 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, /* read packed exponents indexes for all bands */ if (cb_bits > 0 && ev_bits > 0) { - pos = 0; - for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) { + int pos = 0; + for (int i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) { if (bit_offset + cb_bits > max_offset) goto fail; move = read_ubits(cb_bits, bit_offset, buf); @@ -281,7 +271,7 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, if (pos + 1 >= sizeof(critical_band_data)) goto fail; - for (j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++) { + for (int j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++) { exponents[j] = ev; } } @@ -291,8 +281,8 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, if (freq_half > 0 && ei_bits > 0) { /* read first part */ - pos = 0; - for (i = 0; i < RELIC_MAX_FREQ; i++) { + int pos = 0; + for (int i = 0; i < RELIC_MAX_FREQ; i++) { if (bit_offset + ei_bits > max_offset) goto fail; move = read_ubits(ei_bits, bit_offset, buf); @@ -322,7 +312,7 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, } else { pos = 0; - for (i = 0; i < RELIC_MAX_FREQ; i++) { + for (int i = 0; i < RELIC_MAX_FREQ; i++) { if (bit_offset + ei_bits > max_offset) goto fail; move = read_ubits(ei_bits, bit_offset, buf); @@ -413,12 +403,11 @@ int relic_get_frame_size(relic_handle_t* handle) { } int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel) { - int ok; /* clean extra bytes for bitreader (due to a quirk in the original code it may read outside max frame size) */ memset(buf + handle->frame_size, 0, RELIC_BUFFER_SIZE - handle->frame_size); - ok = unpack_frame(buf, RELIC_BUFFER_SIZE, handle->freq1, handle->freq2, handle->scales, handle->exponents[channel], handle->freq_size); + bool ok = unpack_frame(buf, RELIC_BUFFER_SIZE, handle->freq1, handle->freq2, handle->scales, handle->exponents[channel], handle->freq_size); if (!ok) return ok; decode_frame_base(handle->freq1, handle->freq2, handle->wave_cur[channel], handle->wave_prv[channel], handle->dct, handle->window, handle->dct_mode, handle->samples_mode); @@ -432,13 +421,12 @@ static inline int clamp16(int32_t val) { else return val; } -void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip) { - int s, ch; +void relic_get_pcm16(relic_handle_t* handle, int16_t* sbuf) { int ichs = handle->channels; - for (ch = 0; ch < ichs; ch++) { - for (s = 0; s < samples; s++) { - double d64_sample = handle->wave_cur[ch][skip + s]; + for (int s = 0; s < RELIC_SAMPLES_PER_FRAME; s++) { + for (int ch = 0; ch < ichs; ch++) { + double d64_sample = handle->wave_cur[ch][s]; int pcm_sample = clamp16((int32_t)d64_sample); /* f32 in PCM 32767.0 .. -32768.0 format, original code @@ -446,7 +434,17 @@ void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, i //FQ_BNUM ((float)(1<<26)*(1<<26)*1.5) //rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64)) - outbuf[s*ichs + ch] = pcm_sample; + sbuf[s*ichs + ch] = pcm_sample; + } + } +} + +// original lib always converts to pcm16, this is just a freebie +void relic_get_float(relic_handle_t* handle, float* sbuf) { + int pos = 0; + for (int s = 0; s < RELIC_SAMPLES_PER_FRAME; s++) { + for (int ch = 0; ch < handle->channels; ch++) { + sbuf[pos++] = handle->wave_cur[ch][s]; } } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.h index 763798e09..95836f8b5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/relic_lib.h @@ -5,6 +5,7 @@ #define RELIC_BUFFER_SIZE 0x104 #define RELIC_SAMPLES_PER_FRAME 512 +#define RELIC_MAX_CHANNELS 2 typedef struct relic_handle_t relic_handle_t; @@ -18,6 +19,8 @@ int relic_get_frame_size(relic_handle_t* handle); int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel); -void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip); +void relic_get_pcm16(relic_handle_t* handle, int16_t* sbuf); + +void relic_get_float(relic_handle_t* handle, float* sbuf); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c new file mode 100644 index 000000000..845778968 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c @@ -0,0 +1,253 @@ +#include "coding.h" + +/** + * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow + * is/was wrong at times (maybe only in older versions?) so here we do our thing. + */ +bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) { + /* index tables */ + static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; + static const int layers[4] = { -1,3,2,1 }; + static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */ + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */ + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */ + }; + static const int sample_rates[4][4] = { /* [version][sample rate index] */ + { 44100, 48000, 32000, -1}, /* MPEG1 */ + { 22050, 24000, 16000, -1}, /* MPEG2 */ + { 11025, 12000, 8000, -1}, /* MPEG2.5 */ + }; + static const int channels[4] = { 2,2,2, 1 }; /* [channel] */ + static const int frame_samples[3][3] = { /* [version][layer] */ + { 384, 1152, 1152 }, /* MPEG1 */ + { 384, 1152, 576 }, /* MPEG2 */ + { 384, 1152, 576 } /* MPEG2.5 */ + }; + + int idx, padding; + + + memset(info, 0, sizeof(*info)); + + if ((header >> 21) != 0x7FF) /* 31-21: sync */ + goto fail; + + info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */ + if (info->version <= 0) goto fail; + + info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */ + if (info->layer <= 0 || info->layer > 3) goto fail; + + //crc = (header >> 16) & 0x1; /* 16: protected by crc? */ + + idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1))); + info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */ + if (info->bit_rate <= 0) goto fail; + + info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */ + if (info->sample_rate <= 0) goto fail; + + padding = (header >> 9) & 0x1; /* 9: padding? */ + //private = (header >> 8) & 0x1; /* 8: private bit */ + + info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */ + + //js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */ + //copyright = (header >> 3) & 0x1; /* 3: copyrighted */ + //original = (header >> 2) & 0x1; /* 2: original */ + //emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */ + + info->frame_samples = frame_samples[info->version-1][info->layer-1]; + + /* calculate frame length (from hcs's fsb_mpeg) */ + switch (info->frame_samples) { + case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */ + case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */ + case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */ + default: goto fail; + } + + return true; + +fail: + return false; +} + +bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) { + uint32_t header = read_u32be(offset, sf); + return mpeg_get_frame_info_h(header, info); +} + + +uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header) { + if (!header) + header = read_u32be(offset+0x00, sf); + + /* skip ID3v2 */ + if ((header & 0xFFFFFF00) == get_id32be("ID3\0")) { + size_t frame_size = 0; + uint8_t flags = read_u8(offset+0x05, sf); + /* this is how it's officially read :/ */ + frame_size += read_u8(offset+0x06, sf) << 21; + frame_size += read_u8(offset+0x07, sf) << 14; + frame_size += read_u8(offset+0x08, sf) << 7; + frame_size += read_u8(offset+0x09, sf) << 0; + frame_size += 0x0a; + if (flags & 0x10) /* footer? */ + frame_size += 0x0a; + + return frame_size; + } + + /* skip ID3v1 */ + if ((header & 0xFFFFFF00) == get_id32be("TAG\0")) { + ;VGM_LOG("MPEG: ID3v1 at %x\n", offset); + return 0x80; + } + + return 0; +} + +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) { + off_t offset = start_offset; + off_t max_offset = start_offset + bytes; + int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0; + mpeg_frame_info info; + + if (!sf) + return 0; + + if (max_offset > get_streamfile_size(sf)) + max_offset = get_streamfile_size(sf); + + /* MPEG may use VBR so must read all frames */ + while (offset < max_offset) { + uint32_t header = read_u32be(offset+0x00, sf); + size_t tag_size = mpeg_get_tag_size(sf, offset, header); + if (tag_size) { + offset += tag_size; + continue; + } + + /* regular frame (assumed) */ + if (!mpeg_get_frame_info_h(header, &info)) { + VGM_LOG("MPEG: unknown frame at %lx\n", offset); + break; + } + + /* detect Xing header (disguised as a normal frame) */ + if (frames < 3) { /* should be first after tags */ + /* frame is empty so Xing goes after MPEG side info */ + off_t xing_offset; + if (info.version == 1) + xing_offset = (info.channels == 2 ? 0x20 : 0x11) + 0x04; + else + xing_offset = (info.channels == 2 ? 0x11 : 0x09) + 0x04; + + if (info.frame_size >= xing_offset + 0x78 && + read_u32be(offset + 0x04, sf) == 0 && /* empty frame */ + (read_u32be(offset + xing_offset, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */ + read_u32be(offset + xing_offset, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */ + uint32_t flags = read_u32be(offset + xing_offset + 0x04, sf); + + if (flags & 1) { + uint32_t frame_count = read_u32be(offset + xing_offset + 0x08, sf); + samples = frame_count * info.frame_samples; + } + /* other flags indicate seek table and stuff */ + + ;VGM_LOG("MPEG: found Xing header\n"); + + /* vendor specific */ + if (info.frame_size > xing_offset + 0x78 + 0x24) { + uint32_t sub_id = read_u32be(offset + xing_offset + 0x78, sf); + if (sub_id == get_id32be("LAME") || /* LAME */ + sub_id == get_id32be("Lavc")) { /* FFmpeg */ + if (info.layer == 3) { + uint32_t delays = read_u32be(offset + xing_offset + 0x8C, sf); + encoder_delay = ((delays >> 12) & 0xFFF); + encoder_padding = ((delays >> 0) & 0xFFF); + + encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */ + if (encoder_padding > 528 + 1) + encoder_padding -= (528 + 1); + } + else { + encoder_delay = 240 + 1; + } + } + + /* replay gain and stuff */ + } + + /* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */ + break; /* we got samples */ + } + } + + //TODO: detect "VBRI" header (Fraunhofer encoder) + // https://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#VBRIHeader + + /* could detect VBR/CBR but read frames to remove ID3 end tags */ + + frames++; + offset += info.frame_size; + samples += info.frame_samples; + } + + ;VGM_LOG("MPEG: samples=%i, ed=%i, ep=%i, end=%i\n", samples,encoder_delay,encoder_padding, samples - encoder_delay - encoder_padding); + + //todo return encoder delay + samples = samples - encoder_delay - encoder_padding; + return samples; +} + + +/* variation of the above, for clean streams = no ID3/VBR headers + * (maybe should be fused in a single thing with config, API is kinda messy too) */ +int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr) { + mpeg_frame_info info; + off_t offset = start; + int32_t num_samples = 0, loop_start = 0, loop_end = 0; + + if (!is_vbr) { + /* CBR = quick calcs */ + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + num_samples = size / info.frame_size * info.frame_samples; + if (p_loop_start) + loop_start = *p_loop_start / info.frame_size * info.frame_samples; + if (p_loop_end) + loop_end = *p_loop_end / info.frame_size * info.frame_samples; + } + else { + /* VBR (or unknown) = count frames */ + while (offset < start + size) { + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + if (p_loop_start && *p_loop_start + start == offset) + loop_start = num_samples; + + num_samples += info.frame_samples; + offset += info.frame_size; + + if (p_loop_end && *p_loop_end + start == offset) + loop_end = num_samples; + } + } + + if (p_loop_start) + *p_loop_start = loop_start; + if (p_loop_end) + *p_loop_end = loop_end; + + return num_samples; +fail: + VGM_LOG("MPEG: sample reader failed at %lx\n", offset); + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index cf508a552..6f11d6719 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -18,8 +18,8 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code } data->channels_per_frame = info.channels; data->samples_per_frame = info.frame_samples; - data->bitrate_per_frame = info.bit_rate; - data->sample_rate_per_frame = info.sample_rate; + data->bitrate = info.bit_rate; + data->sample_rate = info.sample_rate; /* extra checks per type */ @@ -134,7 +134,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */ current_interleave = data->config.interleave; /* big interleave */ current_interleave_pre = current_interleave*num_stream; - current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; + current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre; current_data_size = data->config.chunk_size; break; @@ -142,7 +142,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d case MPEG_FSB: /* frames with padding + interleave */ current_interleave = data->config.interleave; /* constant for multi-stream FSbs (1 frame + padding) */ current_interleave_pre = current_interleave*num_stream; - current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; + current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre; if (stream->offset >= data->config.data_size) { VGM_LOG_ONCE("MPEG: fsb overread\n"); @@ -182,8 +182,8 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d } - VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding, - "MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_size, (uint32_t)stream->offset); + VGM_ASSERT(data->streams_count > 1 && current_interleave != current_data_size+current_padding, + "MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_count, (uint32_t)stream->offset); break; case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ @@ -193,14 +193,14 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d /* check if current interleave block is short */ { off_t block_offset = stream->offset - stream->channel_start_offset; - size_t next_block = data->streams_size*data->config.interleave; + size_t next_block = data->streams_count * data->config.interleave; if (data->config.data_size && block_offset + next_block >= data->config.data_size) - current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/ + current_interleave = (data->config.data_size % next_block) / data->streams_count; /* short_interleave*/ } current_interleave_pre = current_interleave*num_stream; - current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; + current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre; current_data_size = current_interleave; break; @@ -215,7 +215,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d return false; current_interleave_pre = current_interleave*num_stream; - current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; + current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre; //VGM_LOG("o=%lx, %i: %x, %x, %x, %x\n", stream->offset, num_stream, ms->current_size_count, current_interleave, current_interleave_pre, current_interleave_post ); current_data_size = current_interleave; @@ -256,261 +256,3 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d return true; } #endif - -//TODO: move to a better place - -/*****************/ -/* FRAME HELPERS */ -/*****************/ - -/** - * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow - * it's wrong at times (maybe because we use an ancient version) so here we do our thing. - */ -bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) { - /* index tables */ - static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; - static const int layers[4] = { -1,3,2,1 }; - static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */ - { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */ - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */ - { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */ - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */ - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */ - }; - static const int sample_rates[4][4] = { /* [version][sample rate index] */ - { 44100, 48000, 32000, -1}, /* MPEG1 */ - { 22050, 24000, 16000, -1}, /* MPEG2 */ - { 11025, 12000, 8000, -1}, /* MPEG2.5 */ - }; - static const int channels[4] = { 2,2,2, 1 }; /* [channel] */ - static const int frame_samples[3][3] = { /* [version][layer] */ - { 384, 1152, 1152 }, /* MPEG1 */ - { 384, 1152, 576 }, /* MPEG2 */ - { 384, 1152, 576 } /* MPEG2.5 */ - }; - - int idx, padding; - - - memset(info, 0, sizeof(*info)); - - if ((header >> 21) != 0x7FF) /* 31-21: sync */ - goto fail; - - info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */ - if (info->version <= 0) goto fail; - - info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */ - if (info->layer <= 0 || info->layer > 3) goto fail; - - //crc = (header >> 16) & 0x1; /* 16: protected by crc? */ - - idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1))); - info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */ - if (info->bit_rate <= 0) goto fail; - - info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */ - if (info->sample_rate <= 0) goto fail; - - padding = (header >> 9) & 0x1; /* 9: padding? */ - //private = (header >> 8) & 0x1; /* 8: private bit */ - - info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */ - - //js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */ - //copyright = (header >> 3) & 0x1; /* 3: copyrighted */ - //original = (header >> 2) & 0x1; /* 2: original */ - //emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */ - - info->frame_samples = frame_samples[info->version-1][info->layer-1]; - - /* calculate frame length (from hcs's fsb_mpeg) */ - switch (info->frame_samples) { - case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */ - case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */ - case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */ - default: goto fail; - } - - return true; - -fail: - return false; -} -bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) { - uint32_t header = read_u32be(offset, sf); - return mpeg_get_frame_info_h(header, info); -} - - -uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header) { - if (!header) - header = read_u32be(offset+0x00, sf); - - /* skip ID3v2 */ - if ((header & 0xFFFFFF00) == get_id32be("ID3\0")) { - size_t frame_size = 0; - uint8_t flags = read_u8(offset+0x05, sf); - /* this is how it's officially read :/ */ - frame_size += read_u8(offset+0x06, sf) << 21; - frame_size += read_u8(offset+0x07, sf) << 14; - frame_size += read_u8(offset+0x08, sf) << 7; - frame_size += read_u8(offset+0x09, sf) << 0; - frame_size += 0x0a; - if (flags & 0x10) /* footer? */ - frame_size += 0x0a; - - return frame_size; - } - - /* skip ID3v1 */ - if ((header & 0xFFFFFF00) == get_id32be("TAG\0")) { - ;VGM_LOG("MPEG: ID3v1 at %x\n", offset); - return 0x80; - } - - return 0; -} - -size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) { - off_t offset = start_offset; - off_t max_offset = start_offset + bytes; - int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0; - mpeg_frame_info info; - - if (!sf) - return 0; - - if (max_offset > get_streamfile_size(sf)) - max_offset = get_streamfile_size(sf); - - /* MPEG may use VBR so must read all frames */ - while (offset < max_offset) { - uint32_t header = read_u32be(offset+0x00, sf); - size_t tag_size = mpeg_get_tag_size(sf, offset, header); - if (tag_size) { - offset += tag_size; - continue; - } - - /* regular frame (assumed) */ - if (!mpeg_get_frame_info_h(header, &info)) { - VGM_LOG("MPEG: unknown frame at %lx\n", offset); - break; - } - - /* detect Xing header (disguised as a normal frame) */ - if (frames < 3) { /* should be first after tags */ - /* frame is empty so Xing goes after MPEG side info */ - off_t xing_offset; - if (info.version == 1) - xing_offset = (info.channels == 2 ? 0x20 : 0x11) + 0x04; - else - xing_offset = (info.channels == 2 ? 0x11 : 0x09) + 0x04; - - if (info.frame_size >= xing_offset + 0x78 && - read_u32be(offset + 0x04, sf) == 0 && /* empty frame */ - (read_u32be(offset + xing_offset, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */ - read_u32be(offset + xing_offset, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */ - uint32_t flags = read_u32be(offset + xing_offset + 0x04, sf); - - if (flags & 1) { - uint32_t frame_count = read_u32be(offset + xing_offset + 0x08, sf); - samples = frame_count * info.frame_samples; - } - /* other flags indicate seek table and stuff */ - - ;VGM_LOG("MPEG: found Xing header\n"); - - /* vendor specific */ - if (info.frame_size > xing_offset + 0x78 + 0x24) { - uint32_t sub_id = read_u32be(offset + xing_offset + 0x78, sf); - if (sub_id == get_id32be("LAME") || /* LAME */ - sub_id == get_id32be("Lavc")) { /* FFmpeg */ - if (info.layer == 3) { - uint32_t delays = read_u32be(offset + xing_offset + 0x8C, sf); - encoder_delay = ((delays >> 12) & 0xFFF); - encoder_padding = ((delays >> 0) & 0xFFF); - - encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */ - if (encoder_padding > 528 + 1) - encoder_padding -= (528 + 1); - } - else { - encoder_delay = 240 + 1; - } - } - - /* replay gain and stuff */ - } - - /* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */ - break; /* we got samples */ - } - } - - //TODO: detect "VBRI" header (Fraunhofer encoder) - // https://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#VBRIHeader - - /* could detect VBR/CBR but read frames to remove ID3 end tags */ - - frames++; - offset += info.frame_size; - samples += info.frame_samples; - } - - ;VGM_LOG("MPEG: samples=%i, ed=%i, ep=%i, end=%i\n", samples,encoder_delay,encoder_padding, samples - encoder_delay - encoder_padding); - - //todo return encoder delay - samples = samples - encoder_delay - encoder_padding; - return samples; -} - - -/* variation of the above, for clean streams = no ID3/VBR headers - * (maybe should be fused in a single thing with config, API is kinda messy too) */ -int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr) { - mpeg_frame_info info; - off_t offset = start; - int32_t num_samples = 0, loop_start = 0, loop_end = 0; - - if (!is_vbr) { - /* CBR = quick calcs */ - if (!mpeg_get_frame_info(sf, offset, &info)) - goto fail; - - num_samples = size / info.frame_size * info.frame_samples; - if (p_loop_start) - loop_start = *p_loop_start / info.frame_size * info.frame_samples; - if (p_loop_end) - loop_end = *p_loop_end / info.frame_size * info.frame_samples; - } - else { - /* VBR (or unknown) = count frames */ - while (offset < start + size) { - if (!mpeg_get_frame_info(sf, offset, &info)) - goto fail; - - if (p_loop_start && *p_loop_start + start == offset) - loop_start = num_samples; - - num_samples += info.frame_samples; - offset += info.frame_size; - - if (p_loop_end && *p_loop_end + start == offset) - loop_end = num_samples; - } - } - - - if (p_loop_start) - *p_loop_start = loop_start; - if (p_loop_end) - *p_loop_end = loop_end; - - return num_samples; -fail: - VGM_LOG("MPEG: sample reader failed at %lx\n", offset); - return 0; -} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c index 5d6224f5f..be8bfa117 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c @@ -189,11 +189,11 @@ fail: #define AHX_KEY_BUFFER 0x2000 -#define AHX_KEY_TEST_FRAMES 20 /* wrong keys may work ok in some frames */ +#define AHX_KEY_TEST_FRAMES 25 /* wrong keys may work ok in some frames */ /* check if current key ends properly in frame syncs */ -int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) { - int bytes; +bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) { + int bytes = 0; uint8_t buf[AHX_KEY_BUFFER]; const int buf_size = sizeof(buf); int pos = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index 60d0af548..c810eff45 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -640,11 +640,8 @@ fail: return 0; } -static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) { - int i, ch; +static void ealayer3_copy_pcm_block(float* sbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) { uint8_t pcm_block[1152 * 2 * 2]; /* assumed max: 1 MPEG frame samples * 16b * max channels */ - size_t pcm_size = pcm_number * 2 * channels_per_frame; - size_t bytes; if (pcm_number == 0) return; @@ -654,7 +651,8 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n return; } - bytes = read_streamfile(pcm_block, pcm_offset, pcm_size, sf); + size_t pcm_size = pcm_number * 2 * channels_per_frame; + int bytes = read_streamfile(pcm_block, pcm_offset, pcm_size, sf); if (bytes != pcm_size) { VGM_LOG("EAL3: incorrect pcm_number %i at %lx\n", pcm_number, pcm_offset); return; @@ -666,24 +664,24 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n if (is_packed) { /* ch0+ch1 packed together */ int pos = 0; - for (i = 0; i < pcm_number * channels_per_frame; i++) { + for (int i = 0; i < pcm_number * channels_per_frame; i++) { int16_t pcm_sample = get_s16be(pcm_block + pos); - put_s16le(outbuf + pos, pcm_sample); + sbuf[i] = pcm_sample / 32767.0f; - pos += sizeof(sample_t); + pos += 0x02; } } else { /* all of ch0 first, then all of ch1 (EAL3 v1b only) */ - int get_pos = 0; - for (ch = 0; ch < channels_per_frame; ch++) { - int put_pos = sizeof(sample_t) * ch; - for (i = 0; i < pcm_number; i++) { - int16_t pcm_sample = get_s16be(pcm_block + get_pos); - put_s16le(outbuf + put_pos, pcm_sample); + int pos = 0; + for (int ch = 0; ch < channels_per_frame; ch++) { + int s = ch; + for (int i = 0; i < pcm_number; i++) { + int16_t pcm_sample = get_s16be(pcm_block + pos); + sbuf[s] = pcm_sample / 32767.0f; - get_pos += sizeof(sample_t); - put_pos += sizeof(sample_t) * channels_per_frame; + pos += 0x02; + s += channels_per_frame; } } } @@ -694,10 +692,11 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n 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; - size_t bytes_filled; + float* sbuf = ms->sbuf; + sbuf += ms->samples_filled * channels_per_frame; - bytes_filled = sizeof(sample_t) * ms->samples_filled * channels_per_frame; + int bytes_filled = sizeof(float) * ms->samples_filled * channels_per_frame; if (bytes_filled + eaf->pcm_size > ms->sbuf_size) { VGM_LOG("EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size); goto fail; @@ -709,11 +708,7 @@ 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->sbuf; off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; - size_t decode_to_discard; - - outbuf += bytes_filled; 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); @@ -723,7 +718,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d // stream->offset, eaf->pre_size + eaf->common_size, eaf->v1_offset_samples, eaf->v1_pcm_samples, data->samples_per_frame); /* 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); + ealayer3_copy_pcm_block(sbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile); ms->samples_filled += eaf->v1_pcm_samples; //TODO: we should put samples at offset but most EAL3 use it at first frame, which decodes ok, and rarely @@ -739,20 +734,16 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d * * 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; + int decode_to_discard = eaf->v1_pcm_samples; ms->decode_to_discard += decode_to_discard; } if (eaf->v2_extended_flag) { - uint8_t* outbuf = ms->sbuf; off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; - size_t usable_samples, decode_to_discard; - - outbuf += bytes_filled; /* V2P usually only copies big PCM, while V2S discards then copies few samples (similar to V1b). * Unlike V1b, both modes seem to use 'packed' PCM block */ - ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v2_pcm_samples, channels_per_frame, 1, stream->streamfile); + ealayer3_copy_pcm_block(sbuf, pcm_offset, eaf->v2_pcm_samples, channels_per_frame, 1, stream->streamfile); ms->samples_filled += eaf->v2_pcm_samples; //;VGM_LOG("EA EAL3 v2: off=%lx, mode=%x, value=%i, pcm=%i, c-size=%x, pcm_o=%lx\n", @@ -761,6 +752,8 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d //todo improve how discarding works since there could exists a subtle-but-unlikely PCM+granule usage //todo test other modes (only seen IGNORE) + int usable_samples, decode_to_discard; + /* get how many samples we can use in this granule + pcm block (thus how many we can't) */ if (eaf->v2_offset_mode == 0x00) { /* IGNORE (looks correct in V2P loops, ex. NFS:W) */ /* offset_samples is usually 0 in V2P (no discard), varies in V2S and may be 576 (full discard). @@ -829,7 +822,7 @@ static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, i int ok, i; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; - int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + int skips = at_start ? num_stream : data->streams_count - 1 - num_stream; /* v1 does multichannel with set offsets */ if (data->type == MPEG_EAL31) diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_eamp3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_eamp3.c index 1db0b224d..fe232ab40 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_eamp3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_eamp3.c @@ -40,8 +40,8 @@ int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_ *coding_type = coding_MPEG_layer3; data->channels_per_frame = data->config.channels; data->samples_per_frame = 1152; - data->bitrate_per_frame = 320; - data->sample_rate_per_frame = 48000; + data->bitrate = 320; + data->sample_rate = 48000; } else { mpeg_frame_info info; @@ -55,8 +55,8 @@ int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_ } data->channels_per_frame = info.channels; data->samples_per_frame = info.frame_samples; - data->bitrate_per_frame = info.bit_rate; - data->sample_rate_per_frame = info.sample_rate; + data->bitrate = info.bit_rate; + data->sample_rate = info.sample_rate; } @@ -124,27 +124,27 @@ fail: } /* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */ -static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf) { +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, eamp3_frame_info* eaf) { mpeg_custom_stream* ms = &data->streams[num_stream]; - size_t bytes_filled; - int i; + float* sbuf = ms->sbuf; + sbuf += ms->samples_filled * data->channels_per_frame; - bytes_filled = sizeof(sample_t) * ms->samples_filled * data->channels_per_frame; + int bytes_filled = sizeof(float) * ms->samples_filled * data->channels_per_frame; if (bytes_filled + eaf->pcm_size > ms->sbuf_size) { VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size); goto fail; } - if (eaf->pcm_number) { /* read + write PCM block samples (always LE) */ - for (i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) { - off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample_t)*i; - int16_t pcm_sample = read_s16le(pcm_offset,stream->streamfile); - uint8_t* sbuf = ms->sbuf; - put_s16le(sbuf + bytes_filled + sizeof(sample_t) * i, pcm_sample); + off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size; + for (int i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) { + int16_t pcm_sample = read_s16le(pcm_offset, stream->streamfile); + + sbuf[i] = pcm_sample / 32767.0f; + pcm_offset += 0x02; } ms->samples_filled += eaf->pcm_number; @@ -161,14 +161,12 @@ fail: } /* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */ -static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { - int ok, i; +static int eamp3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start) { eamp3_frame_info eaf = {0}; - int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + int skips = at_start ? num_stream : data->streams_count - 1 - num_stream; - - for (i = 0; i < skips; i++) { - ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf); + for (int i = 0; i < skips; i++) { + int ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf); if (!ok) goto fail; //;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 6dbd9050d..68f432bac 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -1,17 +1,67 @@ -#include "coding.h" -#include "../util.h" -#include "../vgmstream.h" - #ifdef VGM_USE_MPEG +#include +#include "coding.h" +#include "../vgmstream.h" +#include "../base/decode_state.h" +#include "../base/codec_info.h" #include "mpeg_decoder.h" -#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) */ +#define MPEG_DATA_BUFFER_SIZE 0x1000 // at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) +#define MPEG_MAX_CHANNELS 16 // arbitrary max -static mpg123_handle* init_mpg123_handle(void); -static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); -static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); -static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data* data, int num_stream); + +static void free_mpeg(void* priv_data) { + mpeg_codec_data* data = priv_data; + if (!data) + return; + + if (!data->custom) { + mpg123_delete(data->handle); + } + else { + for (int i = 0; i < data->streams_count; i++) { + if (!data->streams) + continue; + mpg123_delete(data->streams[i].handle); + free(data->streams[i].buffer); + free(data->streams[i].sbuf); + } + free(data->streams); + } + + free(data->buffer); + free(data->sbuf); + free(data); +} + +static mpg123_handle* init_mpg123_handle(void) { + mpg123_handle* handle = NULL; + int rc; + + // in old versions it was needed to call mpg123_init() + if (MPG123_API_VERSION <= 46) + goto fail; + + handle = mpg123_new(NULL, &rc); + if (rc != MPG123_OK) goto fail; + + mpg123_param(handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0); + mpg123_param(handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.0); // wonky support + mpg123_param(handle, MPG123_RESYNC_LIMIT, -1, 0x2000); // just in case, games shouldn't need this +#ifndef VGM_DEBUG_OUTPUT + mpg123_param(handle, MPG123_ADD_FLAGS, MPG123_QUIET, 1); +#endif + + rc = mpg123_open_feed(handle); + if (rc != MPG123_OK) goto fail; + + return handle; + +fail: + mpg123_delete(handle); + return NULL; +} /* Inits regular MPEG */ @@ -26,22 +76,21 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_ data->buffer = calloc(data->buffer_size, sizeof(uint8_t)); if (!data->buffer) goto fail; - data->m = init_mpg123_handle(); - if (!data->m) goto fail; + data->handle = init_mpg123_handle(); + if (!data->handle) goto fail; /* check format */ { - int rc, pos, bytes_read; - size_t bytes_done; + int bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf); + // don't check max as sfx can be smaller than buffer - bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf); - /* don't check max as sfx can be smaller than buffer */ - - /* start_offset should be correct but just in case, read first frame(s) */ - pos = 0; + // start_offset should be correct but just in case, read first frame(s) + int rc; + int pos = 0; do { - rc = mpg123_decode(data->m, data->buffer + pos, bytes_read, NULL,0, &bytes_done); + size_t bytes_done; + rc = mpg123_decode(data->handle, data->buffer + pos, bytes_read, NULL,0, &bytes_done); if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) { VGM_LOG("MPEG: unable to set up mpg123 at start offset\n"); goto fail; //handle MPG123_DONE? @@ -53,23 +102,23 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_ pos++; bytes_read--; - } while (rc != MPG123_NEW_FORMAT); + } + while (rc != MPG123_NEW_FORMAT); } { - size_t samples_per_frame; + int samples_per_frame; long sample_rate_per_frame; int channels_per_frame, encoding; - int rc; - struct mpg123_frameinfo mi; + struct mpg123_frameinfo mi = {0}; /* check first frame header and validate */ - rc = mpg123_getformat(data->m, &sample_rate_per_frame, &channels_per_frame, &encoding); + int rc = mpg123_getformat(data->handle, &sample_rate_per_frame, &channels_per_frame, &encoding); if (rc != MPG123_OK) goto fail; - mpg123_info(data->m, &mi); + mpg123_info(data->handle, &mi); - if (encoding != MPG123_ENC_SIGNED_16) + if (encoding != MPG123_ENC_FLOAT_32) goto fail; if (sample_rate_per_frame != mi.rate) goto fail; @@ -96,14 +145,20 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_ data->channels_per_frame = channels_per_frame; data->samples_per_frame = samples_per_frame; + if (!data->sample_rate) + data->sample_rate = mi.rate; + if (!data->bitrate) + data->bitrate = mi.bitrate; + data->is_vbr = mi.vbr != MPG123_CBR; - /* copy current as open_feed may invalidate until data is fed */ - memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo)); - - /* reinit, to ignore the reading done */ - mpg123_open_feed(data->m); + // reinit, to ignore the reading done + mpg123_open_feed(data->handle); } + data->sbuf_size = sizeof(float) * channels * data->samples_per_frame; + data->sbuf = calloc(data->sbuf_size, sizeof(uint8_t)); + if (!data->sbuf) goto fail; + return data; fail: @@ -142,8 +197,11 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* if (!ok) goto fail; - if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */ - if (channels < data->channels_per_frame) goto fail; + if (channels < 1 || channels > MPEG_MAX_CHANNELS) + goto fail; + if (channels < data->channels_per_frame) + goto fail; + //todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder /* max for some Ubi Lyn */ if (data->default_buffer_size > 0x20000) { @@ -153,24 +211,24 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* /* init streams */ - data->streams_size = channels / data->channels_per_frame; + data->streams_count = channels / data->channels_per_frame; /* 2ch streams + odd channels = last stream must be 1ch */ /* (known channels combos are 2ch+..+2ch, 1ch+..+1ch, or rarely 2ch+..+2ch+1ch in EALayer3) */ if (data->channels_per_frame == 2 && channels % 2) - data->streams_size += 1; + data->streams_count += 1; - data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream)); + data->streams = calloc(data->streams_count, sizeof(mpeg_custom_stream)); if (!data->streams) goto fail; - for (int i = 0; i < data->streams_size; i++) { + for (int i = 0; i < data->streams_count; i++) { //data->streams[i] = calloc(1, sizeof(mpeg_custom_stream)); //if (!data->streams[i]) goto fail; data->streams[i].handle = init_mpg123_handle(); /* decoder not shared as frames depend on prev state */ if (!data->streams[i].handle) goto fail; /* size could be any value */ - data->streams[i].sbuf_size = sizeof(sample_t) * data->channels_per_frame * data->samples_per_frame; + data->streams[i].sbuf_size = sizeof(float) * data->channels_per_frame * data->samples_per_frame; data->streams[i].sbuf = calloc(data->streams[i].sbuf_size, sizeof(uint8_t)); if (!data->streams[i].sbuf) goto fail; @@ -180,10 +238,14 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* if (!data->streams[i].buffer) goto fail; data->streams[i].channels_per_frame = data->channels_per_frame; - if (i + 1 == data->streams_size && data->channels_per_frame == 2 && channels % 2) + if (i + 1 == data->streams_count && data->channels_per_frame == 2 && channels % 2) data->streams[i].channels_per_frame = 1; } + data->sbuf_size = sizeof(float) * channels * data->samples_per_frame; + data->sbuf = calloc(data->sbuf_size, sizeof(uint8_t)); + if (!data->sbuf) goto fail; + return data; fail: @@ -192,60 +254,15 @@ fail: } -static mpg123_handle* init_mpg123_handle(void) { - mpg123_handle* m = NULL; - int rc; - - /* inits a new mpg123 handle */ - m = mpg123_new(NULL, &rc); - if (rc == MPG123_NOT_INITIALIZED) { - /* inits the library if needed */ - if (mpg123_init() != MPG123_OK) - goto fail; - m = mpg123_new(NULL,&rc); - if (rc != MPG123_OK) goto fail; - } - else if (rc != MPG123_OK) { - goto fail; - } - - mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */ - mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x2000); /* just in case, games shouldn't ever need this */ -#ifndef VGM_DEBUG_OUTPUT - mpg123_param(m, MPG123_ADD_FLAGS, MPG123_QUIET, 1); -#endif - - if (mpg123_open_feed(m) != MPG123_OK) { - goto fail; - } - - return m; - -fail: - mpg123_delete(m); - return NULL; -} - - /************/ /* DECODERS */ /************/ -void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { - mpeg_codec_data* data = vgmstream->codec_data; - - if (!data->custom) { - decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels); - } else { - decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels); - } -} - /** * Decode anything mpg123 can. * Feeds raw data and extracts decoded samples as needed. */ -static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) { +static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, float* sbuf, int32_t samples_to_do, int channels) { int samples_done = 0; while (samples_done < samples_to_do) { @@ -259,7 +276,7 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data /* end of stream, fill rest with 0s */ if (data->bytes_in_buffer <= 0) { VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample_t)); + memset(sbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(float)); break; } @@ -269,15 +286,15 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data stream->offset += data->bytes_in_buffer; } - bytes_to_do = (samples_to_do-samples_done) * channels * sizeof(sample_t); + bytes_to_do = (samples_to_do-samples_done) * channels * sizeof(float); /* feed new raw data to the decoder if needed, copy decoded results to output */ if (!data->buffer_used) { - rc = mpg123_decode(data->m, data->buffer, data->bytes_in_buffer, outbuf, bytes_to_do, &bytes_done); + rc = mpg123_decode(data->handle, data->buffer, data->bytes_in_buffer, sbuf, bytes_to_do, &bytes_done); data->buffer_used = true; } else { - rc = mpg123_decode(data->m, NULL,0, outbuf, bytes_to_do, &bytes_done); + rc = mpg123_decode(data->handle, NULL, 0, sbuf, bytes_to_do, &bytes_done); } /* not enough raw data, request more */ @@ -287,92 +304,12 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc); /* update copied samples */ - samples_done += bytes_done / sizeof(sample_t) / channels; - outbuf += bytes_done / sizeof(sample_t); + samples_done += bytes_done / sizeof(float) / channels; + sbuf += bytes_done / sizeof(float); } } -/** - * Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc. - * - * Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode. - . Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space. - */ -static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) { - int samples_done = 0; - - while (samples_done < samples_to_do) { - int samples_to_copy = -1; - - /* find max to copy from all streams (equal for all channels) */ - for (int i = 0; i < data->streams_size; i++) { - size_t samples_in_stream = data->streams[i].samples_filled - data->streams[i].samples_used; - if (samples_to_copy < 0 || samples_in_stream < samples_to_copy) - samples_to_copy = samples_in_stream; - } - - - /* discard if needed (for looping) */ - if (data->samples_to_discard) { - int samples_to_discard = samples_to_copy; - if (samples_to_discard > data->samples_to_discard) - samples_to_discard = data->samples_to_discard; - - for (int i = 0; i < data->streams_size; i++) { - data->streams[i].samples_used += samples_to_discard; - } - data->samples_to_discard -= samples_to_discard; - samples_to_copy -= samples_to_discard; - } - - /* mux streams channels (1/2ch combos) to outbuf (Nch) */ - if (samples_to_copy > 0) { - if (samples_to_copy > samples_to_do - samples_done) - samples_to_copy = samples_to_do - samples_done; - - int ch = 0; - for (int stream = 0; stream < data->streams_size; stream++) { - mpeg_custom_stream* ms = &data->streams[stream]; - sample_t* sbuf = ms->sbuf; - int stream_channels = ms->channels_per_frame; - - for (int stream_ch = 0; stream_ch < stream_channels; stream_ch++) { - for (int s = 0; s < samples_to_copy; s++) { - size_t stream_sample = (ms->samples_used+s)*stream_channels + stream_ch; - size_t buffer_sample = (samples_done+s)*channels + ch; - - outbuf[buffer_sample] = sbuf[stream_sample]; - } - ch++; - } - - ms->samples_used += samples_to_copy; - } - - samples_done += samples_to_copy; - } - else { - /* decode more into stream sample buffers */ - - /* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams) - * With multiple offsets they should already start in the first frame of each stream. */ - for (int i = 0; i < data->streams_size; i++) { - switch(data->type) { - //case MPEG_FSB: - /* same offset: alternate frames between streams (maybe needed for weird layouts?) */ - //decode_mpeg_custom_stream(&vgmstream->ch[0], data, i); - - default: - /* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */ - decode_mpeg_custom_stream(&vgmstream->ch[i], data, i); - break; - } - } - } - } -} - /* Decodes frames from a stream into the stream's sample buffer, feeding mpg123 buffer data. * If not enough data to decode (as N data-frames = 1 full-frame) this will exit but be called again. */ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) { @@ -381,7 +318,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* int rc, ok; mpeg_custom_stream* ms = &data->streams[num_stream]; int channels_per_frame = ms->channels_per_frame; - uint8_t* sbuf = ms->sbuf; + float* sbuf = ms->sbuf; //;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i, buffer_full=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used, ms->buffer_full); @@ -426,32 +363,26 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* } } - - bytes_filled = sizeof(sample_t) * ms->samples_filled * channels_per_frame; + sbuf += ms->samples_filled * channels_per_frame; + bytes_filled = sizeof(float) * ms->samples_filled * channels_per_frame; /* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */ if (!ms->buffer_used) { //;VGM_LOG("MPEG: feed new data and get samples\n"); - rc = mpg123_decode(ms->handle, - ms->buffer, ms->bytes_in_buffer, - sbuf + bytes_filled, ms->sbuf_size - bytes_filled, - &bytes_done); + rc = mpg123_decode(ms->handle, ms->buffer, ms->bytes_in_buffer, sbuf, ms->sbuf_size - bytes_filled, &bytes_done); ms->buffer_used = true; } else { //;VGM_LOG("MPEG: get samples from old data\n"); - rc = mpg123_decode(ms->handle, - NULL, 0, - sbuf + bytes_filled, ms->sbuf_size - bytes_filled, - &bytes_done); + rc = mpg123_decode(ms->handle, NULL, 0, sbuf, ms->sbuf_size - bytes_filled, &bytes_done); } - samples_filled = bytes_done / channels_per_frame / sizeof(sample_t); + samples_filled = bytes_done / channels_per_frame / sizeof(float); /* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */ if (ms->decode_to_discard) { size_t decode_to_discard = ms->decode_to_discard; if (decode_to_discard > samples_filled) decode_to_discard = samples_filled; - size_t bytes_to_discard = sizeof(sample_t) * decode_to_discard * channels_per_frame; + size_t bytes_to_discard = sizeof(float) * decode_to_discard * channels_per_frame; bytes_done -= bytes_to_discard; ms->decode_to_discard -= decode_to_discard; @@ -474,104 +405,115 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* decode_fail: /* 0-fill but continue with other streams */ - bytes_filled = ms->samples_filled * channels_per_frame * sizeof(sample_t); + bytes_filled = ms->samples_filled * channels_per_frame * sizeof(float); memset(sbuf + bytes_filled, 0, ms->sbuf_size - bytes_filled); - ms->samples_filled = (ms->sbuf_size / channels_per_frame / sizeof(sample_t)); + ms->samples_filled = (ms->sbuf_size / channels_per_frame / sizeof(float)); } +/** + * Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc. + * + * Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode. + . Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space. + */ +static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, float* sbuf, int32_t samples_to_do, int channels) { + int samples_done = 0; + + while (samples_done < samples_to_do) { + int samples_to_copy = -1; + + /* find max to copy from all streams (equal for all channels) */ + for (int i = 0; i < data->streams_count; i++) { + size_t samples_in_stream = data->streams[i].samples_filled - data->streams[i].samples_used; + if (samples_to_copy < 0 || samples_in_stream < samples_to_copy) + samples_to_copy = samples_in_stream; + } + + + /* discard if needed (for looping) */ + if (data->samples_to_discard) { + int samples_to_discard = samples_to_copy; + if (samples_to_discard > data->samples_to_discard) + samples_to_discard = data->samples_to_discard; + + for (int i = 0; i < data->streams_count; i++) { + data->streams[i].samples_used += samples_to_discard; + } + data->samples_to_discard -= samples_to_discard; + samples_to_copy -= samples_to_discard; + } + + /* mux streams channels (1/2ch combos) to sbuf (Nch) */ + if (samples_to_copy > 0) { + if (samples_to_copy > samples_to_do - samples_done) + samples_to_copy = samples_to_do - samples_done; + + int ch = 0; + for (int stream = 0; stream < data->streams_count; stream++) { + mpeg_custom_stream* ms = &data->streams[stream]; + int stream_channels = ms->channels_per_frame; + + for (int stream_ch = 0; stream_ch < stream_channels; stream_ch++) { + for (int s = 0; s < samples_to_copy; s++) { + size_t stream_sample = (ms->samples_used+s)*stream_channels + stream_ch; + size_t buffer_sample = (samples_done+s)*channels + ch; + + sbuf[buffer_sample] = ms->sbuf[stream_sample]; + } + ch++; + } + + ms->samples_used += samples_to_copy; + } + + samples_done += samples_to_copy; + } + else { + /* decode more into stream sample buffers */ + + /* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams) + * With multiple offsets they should already start in the first frame of each stream. */ + for (int i = 0; i < data->streams_count; i++) { + switch(data->type) { + //case MPEG_FSB: + /* same offset: alternate frames between streams (maybe needed for weird layouts?) */ + //decode_mpeg_custom_stream(&vgmstream->ch[0], data, i); + + default: + /* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */ + decode_mpeg_custom_stream(&vgmstream->ch[i], data, i); + break; + } + } + } + } +} + +static bool decode_frame_mpeg(VGMSTREAM* v) { + mpeg_codec_data* data = v->codec_data; + decode_state_t* ds = v->decode_state; + + // TODO: needed for EALayer3, that has block with max number of samples (could be handled by reading single frames) + int samples_to_do = ds->samples_left; + if (samples_to_do > data->samples_per_frame) + samples_to_do = data->samples_per_frame; + + if (!data->custom) { + decode_mpeg_standard(&v->ch[0], data, data->sbuf, samples_to_do, v->channels); + } else { + decode_mpeg_custom(v, data, data->sbuf, samples_to_do, v->channels); + } + + sbuf_init_flt(&ds->sbuf, data->sbuf, samples_to_do, v->channels); + ds->sbuf.filled = samples_to_do; + + return true; +} /*********/ /* UTILS */ /*********/ -static void flush_mpeg(mpeg_codec_data* data, int is_loop); - -void free_mpeg(mpeg_codec_data* data) { - if (!data) - return; - - if (!data->custom) { - mpg123_delete(data->m); - } - else { - for (int i = 0; i < data->streams_size; i++) { - if (!data->streams) - continue; - mpg123_delete(data->streams[i].handle); - free(data->streams[i].buffer); - free(data->streams[i].sbuf); - } - free(data->streams); - } - - free(data->buffer); - free(data); - - /* The astute reader will note that a call to mpg123_exit is never - * made. While is is evilly breaking our contract with mpg123, it - * doesn't actually do anything except set the "initialized" flag - * to 0. And if we exit we run the risk of turning it off when - * someone else in another thread is using it. */ -} - -/* seeks stream to 0 */ -void reset_mpeg(mpeg_codec_data* data) { - if (!data) return; - - flush_mpeg(data, 0); - -#if 0 - /* flush_mpeg properly resets mpg123 with mpg123_open_feed, and - * offsets are reset in the VGMSTREAM externally, but for posterity: */ - if (!data->custom) { - off_t input_offset = 0; - mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); - } - else { - off_t input_offset = 0; - int i; - for (i = 0; i < data->streams_size; i++) { - if (!data->streams) - continue; - mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); - } - } -#endif -} - -/* seeks to a point */ -void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) { - mpeg_codec_data* data = vgmstream->codec_data; - if (!data) return; - - - if (!data->custom) { - off_t input_offset = 0; - - mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset); - - /* adjust loop with mpg123's offset (useful?) */ - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset; - } - else { - flush_mpeg(data, 1); - - /* restart from 0 and manually discard samples, since we don't really know the correct offset */ - for (int i = 0; i < data->streams_size; i++) { - //if (!data->streams) - // continue; - //mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); /* already reset */ - - /* force first offset as discard-looping needs to start from the beginning */ - if (vgmstream->loop_ch) - vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset; - } - - data->samples_to_discard += num_sample; - } -} - /* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */ static void flush_mpeg(mpeg_codec_data* data, int is_loop) { if (!data) @@ -579,11 +521,11 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) { if (!data->custom) { /* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */ - mpg123_open_feed(data->m); /* mpg123_feedseek won't work */ + mpg123_open_feed(data->handle); // mpg123_feedseek won't work } else { /* re-start from 0 */ - for (int i = 0; i < data->streams_size; i++) { + for (int i = 0; i < data->streams_count; i++) { if (!data->streams) continue; @@ -609,25 +551,84 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) { data->buffer_used = false; } +void reset_mpeg(void* priv_data) { + mpeg_codec_data* data = priv_data; + if (!data) return; + + flush_mpeg(data, 0); + +#if 0 + /* flush_mpeg properly resets mpg123 with mpg123_open_feed, and + * offsets are reset in the VGMSTREAM externally, but for posterity: */ + if (!data->custom) { + off_t input_offset = 0; + mpg123_feedseek(data->handle,0,SEEK_SET,&input_offset); + } + else { + off_t input_offset = 0; + int i; + for (i = 0; i < data->streams_count; i++) { + if (!data->streams) + continue; + mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); + } + } +#endif +} + +/* seeks to a point */ +static void seek_mpeg(VGMSTREAM* v, int32_t num_sample) { + mpeg_codec_data* data = v->codec_data; + if (!data) return; + + + if (!data->custom) { + off_t input_offset = 0; + + mpg123_feedseek(data->handle, num_sample, SEEK_SET, &input_offset); + + /* adjust loop with mpg123's offset (useful?) */ + if (v->loop_ch) + v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + input_offset; + } + else { + flush_mpeg(data, 1); + + /* restart from 0 and manually discard samples, since we don't really know the correct offset */ + for (int i = 0; i < data->streams_count; i++) { + //if (!data->streams) + // continue; + //mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); /* already reset */ + + /* force first offset as discard-looping needs to start from the beginning */ + if (v->loop_ch) + v->loop_ch[i].offset = v->loop_ch[i].channel_start_offset; + } + + data->samples_to_discard += num_sample; + } +} + + int mpeg_get_sample_rate(mpeg_codec_data* data) { - return data->sample_rate_per_frame; + return data->sample_rate; } long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data) { /* if not found just return 0 and expect to fail (if used for num_samples) */ if (!data->custom) { /* We would need to read all VBR frames headers to count samples */ - if (data->mi.vbr != MPG123_CBR) { //maybe abr_rate could be used to get an approx + if (data->is_vbr) { //maybe abr_rate could be used to get an approx VGM_LOG("MPEG: vbr mp3 can't do bytes_to_samples\n"); return 0; } - return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000); + return (int64_t)bytes * data->sample_rate * 8 / (data->bitrate * 1000); } else { /* needed for SCD */ - if (data->streams_size && data->bitrate_per_frame) { - return (int64_t)(bytes / data->streams_size) * data->sample_rate_per_frame * 8 / (data->bitrate_per_frame * 1000); + if (data->streams_count && data->bitrate) { + return (int64_t)(bytes / data->streams_count) * data->sample_rate * 8 / (data->bitrate * 1000); } return 0; @@ -638,14 +639,22 @@ long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data) { /* disables/enables stderr output, for MPEG known to contain recoverable errors */ void mpeg_set_error_logging(mpeg_codec_data* data, int enable) { if (!data->custom) { - mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); + mpg123_param(data->handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); } else { int i; - for (i=0; i < data->streams_size; i++) { + for (i=0; i < data->streams_count; i++) { mpg123_param(data->streams[i].handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); } } } #endif + +const codec_info_t mpeg_decoder = { + .sample_type = SFMT_FLT, + .decode_frame = decode_frame_mpeg, + .free = free_mpeg, + .reset = reset_mpeg, + .seek = seek_mpeg, +}; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h index 49b0d2e76..e74a14282 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h @@ -7,27 +7,26 @@ /* used by mpeg_decoder.c, but scattered in other .c files */ #ifdef VGM_USE_MPEG -#include /* represents a single MPEG stream */ typedef struct { - /* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ - uint8_t* buffer; /* raw data buffer */ + /* buf per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ + uint8_t* buffer; size_t buffer_size; size_t bytes_in_buffer; - bool buffer_full; /* raw buffer has been filled */ - bool buffer_used; /* raw buffer has been fed to the decoder */ + bool buffer_full; + bool buffer_used; - mpg123_handle* handle; /* MPEG decoder */ + void* handle; - void* sbuf; /* decoded samples from this stream */ - size_t sbuf_size; /* in bytes for mpg123 */ - size_t samples_filled; /* data in the buffer (in samples) */ - size_t samples_used; /* data extracted from the buffer */ + float* sbuf; + int sbuf_size; // in bytes for mpg123 + int samples_filled; + int samples_used; - size_t current_size_count; /* data read (if the parser needs to know) */ - size_t current_size_target; /* max data, until something happens */ - size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ + int current_size_count; /* data read (if the parser needs to know) */ + int current_size_target; /* max data, until something happens */ + int decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ int channels_per_frame; /* for rare cases that streams don't share this */ } mpeg_custom_stream; @@ -35,20 +34,20 @@ typedef struct { struct mpeg_codec_data { /* regular/single MPEG internals */ uint8_t* buffer; /* raw data buffer */ - size_t buffer_size; - size_t bytes_in_buffer; + int buffer_size; + int bytes_in_buffer; bool buffer_full; /* raw buffer has been filled */ bool buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle* m; /* MPEG decoder */ - struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */ + void* handle; // MPEG decoder /* for internal use */ int channels_per_frame; int samples_per_frame; /* for some calcs */ - int bitrate_per_frame; - int sample_rate_per_frame; + int bitrate; + int sample_rate; + bool is_vbr; /* custom MPEG internals */ bool custom; /* flag */ @@ -57,11 +56,13 @@ struct mpeg_codec_data { size_t default_buffer_size; mpeg_custom_stream* streams; /* array of MPEG streams (ex. 2ch+2ch) */ - size_t streams_size; + int streams_count; - size_t skip_samples; /* base encoder delay */ - size_t samples_to_discard; /* for custom mpeg looping */ + int skip_samples; /* base encoder delay */ + int samples_to_discard; /* for custom mpeg looping */ + float* sbuf; // decoded samples from all streams + int sbuf_size; // in bytes for mpg123 }; int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t* coding_type); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c index 3d4c260cb..651a104f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c @@ -1,22 +1,34 @@ #include "coding.h" +#include "../base/decode_state.h" +#include "../base/codec_info.h" #include "libs/relic_lib.h" -//TODO: fix looping -struct relic_codec_data { +typedef struct { relic_handle_t* handle; int channels; int frame_size; - int32_t samples_discard; - int32_t samples_consumed; - int32_t samples_filled; -}; + float fbuf[RELIC_SAMPLES_PER_FRAME * RELIC_MAX_CHANNELS]; + + int32_t discard; +} relic_codec_data; -relic_codec_data* init_relic(int channels, int bitrate, int codec_rate) { +static void free_relic(void* priv_data) { + relic_codec_data* data = priv_data; + if (!data) return; + + relic_free(data->handle); + free(data); +} + +void* init_relic(int channels, int bitrate, int codec_rate) { relic_codec_data* data = NULL; + if (channels > RELIC_MAX_CHANNELS) + goto fail; + data = calloc(1, sizeof(relic_codec_data)); if (!data) goto fail; @@ -32,92 +44,68 @@ fail: return NULL; } -static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) { - int ch; - int bytes; - int ok; +static int decode_frame_channels(VGMSTREAMCHANNEL* stream, relic_codec_data* data) { uint8_t buf[RELIC_BUFFER_SIZE]; - for (ch = 0; ch < data->channels; ch++) { - bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile); - if (bytes != data->frame_size) goto fail; + for (int ch = 0; ch < data->channels; ch++) { + int bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile); + stream->offset += data->frame_size; - ok = relic_decode_frame(data->handle, buf, ch); - if (!ok) goto fail; + if (bytes != data->frame_size) return -1; + + int ok = relic_decode_frame(data->handle, buf, ch); + if (!ok) return -1; } - data->samples_consumed = 0; - data->samples_filled = RELIC_SAMPLES_PER_FRAME; - return 1; -fail: - return 0; + return RELIC_SAMPLES_PER_FRAME; } -void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { +static bool decode_frame_relic(VGMSTREAM* v) { + decode_state_t* ds = v->decode_state; + relic_codec_data* data = v->codec_data; - while (samples_to_do > 0) { + int samples = decode_frame_channels(&v->ch[0], data); + if (samples <= 0) + return false; - if (data->samples_consumed < data->samples_filled) { - /* consume samples */ - int samples_to_get = (data->samples_filled - data->samples_consumed); + relic_get_float(data->handle, data->fbuf); - if (data->samples_discard) { - /* discard samples for looping */ - if (samples_to_get > data->samples_discard) - samples_to_get = data->samples_discard; - data->samples_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do) - samples_to_get = samples_to_do; + sbuf_init_f16(&ds->sbuf, data->fbuf, samples, data->channels); + ds->sbuf.filled = samples; - relic_get_pcm16(data->handle, outbuf, samples_to_get, data->samples_consumed); - - samples_to_do -= samples_to_get; - outbuf += samples_to_get * data->channels; - } - - /* mark consumed samples */ - data->samples_consumed += samples_to_get; - } - else { - int ok = decode_frame_next(stream, data); - if (!ok) goto decode_fail; - } + if (data->discard) { + ds->discard = data->discard; + data->discard = 0; } - return; -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("RELIC: decode fail, missing %i samples\n", samples_to_do); - memset(outbuf, 0, samples_to_do * data->channels * sizeof(sample_t)); + return true; } -void reset_relic(relic_codec_data* data) { +static void reset_relic(void* priv_data) { + relic_codec_data* data = priv_data; if (!data) return; relic_reset(data->handle); - data->samples_filled = 0; - data->samples_consumed = 0; - data->samples_discard = 0; + data->discard = 0; } -void seek_relic(relic_codec_data* data, int32_t num_sample) { +static void seek_relic(VGMSTREAM* v, int32_t num_sample) { + relic_codec_data* data = v->codec_data; if (!data) return; reset_relic(data); - data->samples_discard = num_sample; -} - -void free_relic(relic_codec_data* data) { - if (!data) return; - - relic_free(data->handle); - free(data); + data->discard = num_sample; } int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) { return bytes / channels / (bitrate / 8) * RELIC_SAMPLES_PER_FRAME; } + +const codec_info_t relic_decoder = { + .sample_type = SFMT_F16, + .decode_frame = decode_frame_relic, + .free = free_relic, + .reset = reset_relic, + .seek = seek_relic, +}; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index 8af5f0a8c..27bb0098a 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -129,7 +129,7 @@ fail: bool setup_layout_layered(layered_layout_data* data) { int max_input_channels = 0; int max_output_channels = 0; - int max_sample_size = 0; + sfmt_t max_sample_type = SFMT_NONE; /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ for (int i = 0; i < data->layer_count; i++) { @@ -166,9 +166,9 @@ bool setup_layout_layered(layered_layout_data* data) { #endif } - int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->layers[i]) ); - if (max_sample_size < current_sample_size) - max_sample_size = current_sample_size; + sfmt_t current_sample_type = mixing_get_input_sample_type(data->layers[i]); + if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority + max_sample_type = current_sample_type; /* loops and other values could be mismatched, but should be handled on allocate */ @@ -185,6 +185,10 @@ bool setup_layout_layered(layered_layout_data* data) { if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) return false; + // needed for codecs like FFMpeg where base vgmstream's sample type is unknown + data->fmt = max_sample_type; + int max_sample_size = sfmt_get_sample_size(max_sample_type); + /* create internal buffer big enough for mixing all layers */ free(data->buffer); data->buffer = malloc(VGMSTREAM_LAYER_SAMPLE_BUFFER * max_input_channels * max_sample_size); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index aae900f64..07c520129 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -23,6 +23,7 @@ typedef struct { int input_channels; /* internal buffer channels */ int output_channels; /* resulting channels (after mixing, if applied) */ bool mixed_channels; /* segments have different number of channels */ + sfmt_t fmt; } segmented_layout_data; void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream); @@ -44,6 +45,7 @@ typedef struct { int output_channels; /* resulting channels (after mixing, if applied) */ int external_looping; /* don't loop using per-layer loops, but layout's own looping */ int curr_layer; /* helper */ + sfmt_t fmt; } layered_layout_data; void render_vgmstream_layered(sbuf_t* sbuf, VGMSTREAM* vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 322149279..e817fa069 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -155,7 +155,7 @@ fail: bool setup_layout_segmented(segmented_layout_data* data) { int max_input_channels = 0; int max_output_channels = 0; - int max_sample_size = 0; + sfmt_t max_sample_type = SFMT_NONE; bool mixed_channels = false; /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ @@ -212,9 +212,9 @@ bool setup_layout_segmented(segmented_layout_data* data) { // goto fail; } - int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->segments[i]) ); - if (max_sample_size < current_sample_size) - max_sample_size = current_sample_size; + sfmt_t current_sample_type = mixing_get_input_sample_type(data->segments[i]); + if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority + max_sample_type = current_sample_type; /* init mixing */ mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); @@ -226,6 +226,10 @@ bool setup_layout_segmented(segmented_layout_data* data) { if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) return false; + // needed for codecs like FFMpeg where base vgmstream's sample type is unknown + data->fmt = max_sample_type; + int max_sample_size = sfmt_get_sample_size(max_sample_type); + /* create internal buffer big enough for mixing */ free(data->buffer); data->buffer = malloc(VGMSTREAM_SEGMENT_SAMPLE_BUFFER * max_input_channels * max_sample_size); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/astb.c b/Frameworks/vgmstream/vgmstream/src/meta/astb.c index 7af57ad3e..83feb1fcc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/astb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/astb.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -/* ASTB - found in Dead Rising (X360) */ +/* ASTB - from early MT Framework games [Dead Rising (X360), Lost Planet (X360)] */ VGMSTREAM* init_vgmstream_astb(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset, data_size; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 531c3b9d2..486a39767 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -72,6 +72,10 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) { loop_flag = find_meta_loops(data, &loop_start, &loop_end); } + if (is_id32be(0x00, sf, "fLaC")) { + ffmpeg_set_allow_pcm24(data); + } + /* hack for AAC files (will return 0 samples if not an actual file) */ if (!num_samples && check_extensions(sf, "aac,laac")) { num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf)); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 29c813f2b..20adf3334 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -181,7 +181,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { vgmstream->layout_type = layout_layered; } else { - vgmstream->codec_data = init_celt_fsb(vgmstream->channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1); + vgmstream->codec_data = is_new_lib ? + init_celt_fsb_v2(vgmstream->channels) : + init_celt_fsb_v1(vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_CELT_FSB; vgmstream->layout_type = layout_none; @@ -250,7 +252,9 @@ static layered_layout_data* build_layered_fsb_celt(STREAMFILE* sf, fsb_header_t* data->layers[i]->loop_end_sample = fsb->loop_end; #ifdef VGM_USE_CELT - data->layers[i]->codec_data = init_celt_fsb(layer_channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1); + data->layers[i]->codec_data = is_new_lib ? + init_celt_fsb_v2(layer_channels) : + init_celt_fsb_v1(layer_channels); if (!data->layers[i]->codec_data) goto fail; data->layers[i]->coding_type = coding_CELT_FSB; data->layers[i]->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index adac4acc0..67ab2d5a7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -405,7 +405,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { vgmstream->layout_type = layout_layered; } else { - vgmstream->codec_data = init_celt_fsb(vgmstream->channels, CELT_0_11_0); + vgmstream->codec_data = init_celt_fsb_v2(vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_CELT_FSB; vgmstream->layout_type = layout_none; @@ -591,7 +591,7 @@ static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, STREAMFILE* sb, f switch (fsb5->codec) { #ifdef VGM_USE_CELT case 0x0C: { /* CELT */ - data->layers[i]->codec_data = init_celt_fsb(layer_channels, CELT_0_11_0); + data->layers[i]->codec_data = init_celt_fsb_v2(layer_channels); if (!data->layers[i]->codec_data) goto fail; data->layers[i]->coding_type = coding_CELT_FSB; data->layers[i]->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index e1b2b859d..7247b61ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -65,12 +65,13 @@ static const fsbkey_info fsbkey_list[] = { { MODE_FSB5, FSBKEY_ADD("cbfjZTlUPaZI") }, // JDM: Japanese Drift Master (PC) { MODE_FSB3, FSBKEY_ADD("tkdnsem000") }, // Ys Online: The Call of Solum (PC) [FSB3] (alt key: 2ED62676CEA6B60C0C0C) { MODE_FSB4, FSBKEY_ADD("4DxgpNV3pQLPD6GT7g9Gf6eWU7SXutGQ") }, // Test Drive: Ferrari Racing Legends (PC) - { MODE_FSB4, FSBKEY_ADD("AjaxIsTheGoodestBoy") }, // Hello Kitty: Island Adventure (iOS) + { MODE_FSB5, FSBKEY_ADD("AjaxIsTheGoodestBoy") }, // Hello Kitty: Island Adventure (iOS) { MODE_FSB5, FSBKEY_ADD("resoforce") }, // Rivals of Aether 2 (PC) { MODE_FSB5, FSBKEY_ADD("3cfe772db5b55b806541d3faf894020e") }, // Final Fantasy XV: War for Eos (Android) { MODE_FSB5, FSBKEY_ADD("aj#$kLucf2lh}eqh") }, // Forza Motorsport 2023 (PC) { MODE_FSB4, FSBKEY_ADD("dpdjeoqkr") }, // AirRider CrazyRacing (PC) { MODE_FSB5, FSBKEY_ADD("weareAbsolutelyUnsure2018") }, // Wanderstop (PC) + { MODE_FSB5, FSBKEY_ADD(".xW3uXQ8q79yunvMjL6nahLXts9esEXX2VgetuPCxdLrAjUUbZAmB7R*A6KjW24NU_8ifMZ8TC4Qk@_oEsjsK2QLpAaG-Fy!wYKP") }, // UNBEATABLE Demo (PC) /* some games use a key per file, generated from the filename * (could add all of them but there are a lot of songs, so external .fsbkey are probably better) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c index 421ed103a..0c228dae8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c @@ -111,7 +111,7 @@ static uint32_t decrypt_chunk(uint8_t* buf, int buf_size, uint32_t key) { return key; /* to resume decrypting if needed */ } -#define HEADER_MAX 0x2800 /* seen 0x2420 in one bank */ +#define HEADER_MAX 0x3000 /* seen 0x2BD0 in one bank */ /* header is encrypted except in M&L 3DS so decrypt + handle in buffer; format * base 0x30 header + subheader with tables/headers depending on type */ @@ -259,11 +259,12 @@ static bool parse_header(redspark_header_t* h, STREAMFILE* sf, bool is_new) { coef_offset = target_pos + 0x28; h->channels = 1; - h->loop_flag = (h->loop_start != -1); /* TODO: many files sound kind of odd */ + h->loop_flag = 0; // (h->loop_start != -1); // TODO: many files sound kind of odd if (h->num_samples == 0) h->num_samples = h->loop_end; + // seems correct based on MadWorld, not 100% sure in Imabikisou if (h->sample_rate == 0) - h->sample_rate = 32000; + h->sample_rate = 24000; /* empty entry */ if (h->stream_size == 0) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c b/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c index 4108f591c..b39d39296 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c @@ -145,12 +145,12 @@ VGMSTREAM* init_vgmstream_rfrm(STREAMFILE* sf) { goto fail; version = read_32bitBE(0x18,sf); /* assumed, also at 0x1c */ - if (version == 0x0a) { /* Wii U */ + if (version == 0x0a) { /* DKCTF Wii U */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; big_endian = 1; } - else if (version == 0x12) { /* Switch */ + else if (version == 0x11 || version == 0x12) { /* RS11 and DKCTF Switch */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; big_endian = 0;