VGMStream: Updated libvgmstream code base
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Updated VGMStream to r1980-268-gc32951e9 Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
048bc7c30d
commit
69533e12c7
31 changed files with 1434 additions and 1539 deletions
|
@ -669,6 +669,7 @@
|
||||||
837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */; };
|
837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */; };
|
||||||
837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; };
|
837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; };
|
||||||
8383A62C281203C60062E49E /* s3v.c in Sources */ = {isa = PBXBuildFile; fileRef = 8383A62B281203C50062E49E /* s3v.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 */; };
|
83852B0B2680247900378854 /* rxws.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B092680247900378854 /* rxws.c */; };
|
||||||
83852B0C2680247900378854 /* ads_midway.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B0A2680247900378854 /* ads_midway.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 */; };
|
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 = "<group>"; };
|
837CEAF023487F2C00E62A4A /* sqex_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_streamfile.h; sourceTree = "<group>"; };
|
||||||
837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = "<group>"; };
|
837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = "<group>"; };
|
||||||
8383A62B281203C50062E49E /* s3v.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s3v.c; sourceTree = "<group>"; };
|
8383A62B281203C50062E49E /* s3v.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s3v.c; sourceTree = "<group>"; };
|
||||||
|
8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_info.c; sourceTree = "<group>"; };
|
||||||
83852B092680247900378854 /* rxws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rxws.c; sourceTree = "<group>"; };
|
83852B092680247900378854 /* rxws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rxws.c; sourceTree = "<group>"; };
|
||||||
83852B0A2680247900378854 /* ads_midway.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads_midway.c; sourceTree = "<group>"; };
|
83852B0A2680247900378854 /* ads_midway.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads_midway.c; sourceTree = "<group>"; };
|
||||||
8385D4E2245174C600FF8E67 /* diva.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = diva.c; sourceTree = "<group>"; };
|
8385D4E2245174C600FF8E67 /* diva.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = diva.c; sourceTree = "<group>"; };
|
||||||
|
@ -2022,6 +2024,7 @@
|
||||||
83B73C482D8FF17900A57F08 /* mio_decoder.c */,
|
83B73C482D8FF17900A57F08 /* mio_decoder.c */,
|
||||||
834F7D6A2C7093EA003AC386 /* mp4_aac_decoder.c */,
|
834F7D6A2C7093EA003AC386 /* mp4_aac_decoder.c */,
|
||||||
834F7D692C7093EA003AC386 /* mpc3_decoder.c */,
|
834F7D692C7093EA003AC386 /* mpc3_decoder.c */,
|
||||||
|
8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */,
|
||||||
834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */,
|
834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */,
|
||||||
834F7D6B2C7093EA003AC386 /* mpeg_custom_utils_ahx.c */,
|
834F7D6B2C7093EA003AC386 /* mpeg_custom_utils_ahx.c */,
|
||||||
834F7D6C2C7093EA003AC386 /* mpeg_custom_utils_ealayer3.c */,
|
834F7D6C2C7093EA003AC386 /* mpeg_custom_utils_ealayer3.c */,
|
||||||
|
@ -3382,6 +3385,7 @@
|
||||||
8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */,
|
8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */,
|
||||||
834F7DC62C7093EA003AC386 /* g721_decoder.c in Sources */,
|
834F7DC62C7093EA003AC386 /* g721_decoder.c in Sources */,
|
||||||
834F7DA32C7093EA003AC386 /* nwa_lib.c in Sources */,
|
834F7DA32C7093EA003AC386 /* nwa_lib.c in Sources */,
|
||||||
|
8384EC322DC60DD70037EFFD /* mpeg_custom_info.c in Sources */,
|
||||||
8306B0EA20984590000302D4 /* caf.c in Sources */,
|
8306B0EA20984590000302D4 /* caf.c in Sources */,
|
||||||
83AA7F842519C042004C5298 /* svag_snk.c in Sources */,
|
83AA7F842519C042004C5298 /* svag_snk.c in Sources */,
|
||||||
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */,
|
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */,
|
||||||
|
|
|
@ -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) {
|
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;
|
return LIBVGMSTREAM_ERROR_GENERIC;
|
||||||
|
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
if (!lib || !lib->priv)
|
if (!lib || !lib->priv || !cfg)
|
||||||
return LIBVGMSTREAM_ERROR_GENERIC;
|
return LIBVGMSTREAM_ERROR_GENERIC;
|
||||||
|
|
||||||
libvgmstream_priv_t* priv = lib->priv;
|
libvgmstream_priv_t* priv = lib->priv;
|
||||||
|
|
|
@ -16,6 +16,19 @@ extern const codec_info_t mio_decoder;
|
||||||
extern const codec_info_t pcm32_decoder;
|
extern const codec_info_t pcm32_decoder;
|
||||||
extern const codec_info_t pcm24_decoder;
|
extern const codec_info_t pcm24_decoder;
|
||||||
extern const codec_info_t pcmfloat_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) {
|
const codec_info_t* codec_get_info(VGMSTREAM* v) {
|
||||||
switch(v->coding_type) {
|
switch(v->coding_type) {
|
||||||
|
@ -50,6 +63,28 @@ const codec_info_t* codec_get_info(VGMSTREAM* v) {
|
||||||
return &pcm24_decoder;
|
return &pcm24_decoder;
|
||||||
case coding_PCMFLOAT:
|
case coding_PCMFLOAT:
|
||||||
return &pcmfloat_decoder;
|
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:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,6 @@ void decode_free(VGMSTREAM* vgmstream) {
|
||||||
free_circus_vq(vgmstream->codec_data);
|
free_circus_vq(vgmstream->codec_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_RELIC) {
|
|
||||||
free_relic(vgmstream->codec_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||||
vgmstream->coding_type == coding_ICE_DCT) {
|
vgmstream->coding_type == coding_ICE_DCT) {
|
||||||
free_ice(vgmstream->codec_data);
|
free_ice(vgmstream->codec_data);
|
||||||
|
@ -71,28 +67,12 @@ void decode_free(VGMSTREAM* vgmstream) {
|
||||||
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
|
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 defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||||
if (vgmstream->coding_type == coding_MP4_AAC) {
|
if (vgmstream->coding_type == coding_MP4_AAC) {
|
||||||
free_mp4_aac(vgmstream->codec_data);
|
free_mp4_aac(vgmstream->codec_data);
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#ifdef VGM_USE_G7221
|
||||||
if (vgmstream->coding_type == coding_G7221C) {
|
if (vgmstream->coding_type == coding_G7221C) {
|
||||||
free_g7221(vgmstream->codec_data);
|
free_g7221(vgmstream->codec_data);
|
||||||
|
@ -105,18 +85,6 @@ void decode_free(VGMSTREAM* vgmstream) {
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
if (vgmstream->coding_type == coding_ACM) {
|
||||||
free_acm(vgmstream->codec_data);
|
free_acm(vgmstream->codec_data);
|
||||||
}
|
}
|
||||||
|
@ -143,10 +111,6 @@ void decode_seek(VGMSTREAM* vgmstream) {
|
||||||
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample);
|
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 ||
|
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||||
vgmstream->coding_type == coding_ICE_DCT) {
|
vgmstream->coding_type == coding_ICE_DCT) {
|
||||||
seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample);
|
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);
|
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 defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||||
if (vgmstream->coding_type == coding_MP4_AAC) {
|
if (vgmstream->coding_type == coding_MP4_AAC) {
|
||||||
seek_mp4_aac(vgmstream, vgmstream->loop_current_sample);
|
seek_mp4_aac(vgmstream, vgmstream->loop_current_sample);
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
if (vgmstream->coding_type == coding_NWA) {
|
||||||
seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample);
|
seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||||
}
|
}
|
||||||
|
@ -220,10 +156,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
|
||||||
reset_circus_vq(vgmstream->codec_data);
|
reset_circus_vq(vgmstream->codec_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_RELIC) {
|
|
||||||
reset_relic(vgmstream->codec_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||||
vgmstream->coding_type == coding_ICE_DCT) {
|
vgmstream->coding_type == coding_ICE_DCT) {
|
||||||
reset_ice(vgmstream->codec_data);
|
reset_ice(vgmstream->codec_data);
|
||||||
|
@ -247,16 +179,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#ifdef VGM_USE_G7221
|
||||||
if (vgmstream->coding_type == coding_G7221C) {
|
if (vgmstream->coding_type == coding_G7221C) {
|
||||||
reset_g7221(vgmstream->codec_data);
|
reset_g7221(vgmstream->codec_data);
|
||||||
|
@ -269,24 +191,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
if (vgmstream->coding_type == coding_ACM) {
|
||||||
reset_acm(vgmstream->codec_data);
|
reset_acm(vgmstream->codec_data);
|
||||||
}
|
}
|
||||||
|
@ -342,13 +246,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||||
case coding_PCM24BE:
|
case coding_PCM24BE:
|
||||||
case coding_PCM32LE:
|
case coding_PCM32LE:
|
||||||
return 1;
|
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:
|
||||||
case coding_SDX2_int:
|
case coding_SDX2_int:
|
||||||
case coding_CBD2:
|
case coding_CBD2:
|
||||||
|
@ -464,10 +361,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||||
#ifdef VGM_USE_G719
|
#ifdef VGM_USE_G719
|
||||||
case coding_G719:
|
case coding_G719:
|
||||||
return 48000/50;
|
return 48000/50;
|
||||||
#endif
|
|
||||||
#ifdef VGM_USE_FFMPEG
|
|
||||||
case coding_FFmpeg:
|
|
||||||
return 0;
|
|
||||||
#endif
|
#endif
|
||||||
case coding_MTAF:
|
case coding_MTAF:
|
||||||
return 128*2;
|
return 128*2;
|
||||||
|
@ -497,22 +390,12 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||||
return 0; /* 432, but variable in looped files */
|
return 0; /* 432, but variable in looped files */
|
||||||
case coding_CIRCUS_VQ:
|
case coding_CIRCUS_VQ:
|
||||||
return 0;
|
return 0;
|
||||||
case coding_RELIC:
|
|
||||||
return 0; /* 512 */
|
|
||||||
case coding_ICE_RANGE:
|
case coding_ICE_RANGE:
|
||||||
case coding_ICE_DCT:
|
case coding_ICE_DCT:
|
||||||
return 0; /* ~100 (range), ~16 (DCT) */
|
return 0; /* ~100 (range), ~16 (DCT) */
|
||||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||||
case coding_MP4_AAC:
|
case coding_MP4_AAC:
|
||||||
return mp4_get_samples_per_frame(vgmstream->codec_data);
|
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
|
#endif
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -679,9 +562,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
|
||||||
#endif
|
#endif
|
||||||
#ifdef VGM_USE_G719
|
#ifdef VGM_USE_G719
|
||||||
case coding_G719:
|
case coding_G719:
|
||||||
#endif
|
|
||||||
#ifdef VGM_USE_FFMPEG
|
|
||||||
case coding_FFmpeg:
|
|
||||||
#endif
|
#endif
|
||||||
case coding_MTAF:
|
case coding_MTAF:
|
||||||
return vgmstream->interleave_block_size;
|
return vgmstream->interleave_block_size;
|
||||||
|
@ -704,7 +584,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
|
||||||
/* UBI_ADPCM: varies per mode? */
|
/* UBI_ADPCM: varies per mode? */
|
||||||
/* IMUSE: VBR */
|
/* IMUSE: VBR */
|
||||||
/* EA_MT: VBR, frames of bit counts or PCM frames */
|
/* EA_MT: VBR, frames of bit counts or PCM frames */
|
||||||
/* ATRAC9: CBR around 0x100-200 */
|
|
||||||
/* CELT FSB: varies, usually 0x80-100 */
|
/* CELT FSB: varies, usually 0x80-100 */
|
||||||
/* TAC: VBR around ~0x200-300 */
|
/* TAC: VBR around ~0x200-300 */
|
||||||
default: /* (VBR or managed by decoder) */
|
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:
|
case coding_CIRCUS_VQ:
|
||||||
decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
||||||
break;
|
break;
|
||||||
case coding_RELIC:
|
|
||||||
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do);
|
|
||||||
break;
|
|
||||||
case coding_ICE_RANGE:
|
case coding_ICE_RANGE:
|
||||||
case coding_ICE_DCT:
|
case coding_ICE_DCT:
|
||||||
decode_ice(vgmstream->codec_data, buffer, samples_to_do);
|
decode_ice(vgmstream->codec_data, buffer, samples_to_do);
|
||||||
break;
|
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)
|
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||||
case coding_MP4_AAC:
|
case coding_MP4_AAC:
|
||||||
decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
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;
|
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
|
#ifdef VGM_USE_G7221
|
||||||
case coding_G7221C:
|
case coding_G7221C:
|
||||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
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);
|
decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
|
||||||
}
|
}
|
||||||
break;
|
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
|
#endif
|
||||||
case coding_ACM:
|
case coding_ACM:
|
||||||
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
||||||
|
|
|
@ -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) {
|
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);
|
const codec_info_t* codec_info = codec_get_info(vgmstream);
|
||||||
if (codec_info) {
|
if (codec_info) {
|
||||||
if (codec_info->sample_type)
|
if (codec_info->sample_type)
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
#include "../util/log.h"
|
#include "../util/log.h"
|
||||||
|
|
||||||
// float-to-int modes
|
// 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
|
//#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 <math.h>
|
#include <math.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
* It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions.
|
* It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions.
|
||||||
*/
|
*/
|
||||||
static inline int float_to_int(float val) {
|
static inline int float_to_int(float val) {
|
||||||
#if PCM16_ROUNDING_LRINT
|
#ifdef PCM16_ROUNDING_LRINT
|
||||||
return lrintf(val);
|
return lrintf(val);
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
return (int)val;
|
return (int)val;
|
||||||
|
@ -38,7 +38,7 @@ static inline int float_to_int(float val) {
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static inline int double_to_int(double val) {
|
static inline int double_to_int(double val) {
|
||||||
#if PCM16_ROUNDING_LRINT
|
#ifdef PCM16_ROUNDING_LRINT
|
||||||
return lrint(val);
|
return lrint(val);
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
return (int)val;
|
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);
|
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) { \
|
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
|
||||||
buftype* buf = sbuf->buf; \
|
buftype* buf = sbuf->buf; \
|
||||||
int s = start * sbuf->channels; \
|
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) { \
|
while (s < s_end) { \
|
||||||
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
|
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
|
||||||
for (int i = 0; i < sbuf->channels; i++) { \
|
for (int i = 0; i < sbuf->channels; i++) { \
|
||||||
buf[s] = float_to_int(buf[s] * fadedness); \
|
buf[s] = func(buf[s] * fadedness); \
|
||||||
s++; \
|
s++; \
|
||||||
} \
|
} \
|
||||||
fade_pos++; \
|
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) { \
|
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
|
||||||
buftype* buf = sbuf->buf; \
|
buftype* buf = sbuf->buf; \
|
||||||
int s = start * sbuf->channels; \
|
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) { \
|
while (s < s_end) { \
|
||||||
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
|
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
|
||||||
for (int i = 0; i < sbuf->channels; i++) { \
|
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++; \
|
s++; \
|
||||||
} \
|
} \
|
||||||
fade_pos++; \
|
fade_pos++; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_SBUF_FADE(i16, int16_t);
|
// no need to clamp in fade outs
|
||||||
DEFINE_SBUF_FADE(i32, int32_t);
|
#define CONV_FADE_FLT(x) (x)
|
||||||
DEFINE_SBUF_FADE(flt, float);
|
#define CONV_FADE_PCM(x) float_to_int(x)
|
||||||
DEFINE_SBUF_FD24(o24, uint8_t);
|
|
||||||
|
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) {
|
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) {
|
||||||
//TODO: use interpolated fadedness to improve performance?
|
//TODO: use interpolated fadedness to improve performance?
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
#include "coding.h"
|
|
||||||
|
|
||||||
#ifdef VGM_USE_ATRAC9
|
#ifdef VGM_USE_ATRAC9
|
||||||
|
#include "coding.h"
|
||||||
|
#include "../base/decode_state.h"
|
||||||
|
#include "../base/codec_info.h"
|
||||||
#include "libatrac9/libatrac9.h"
|
#include "libatrac9/libatrac9.h"
|
||||||
|
|
||||||
|
|
||||||
/* opaque struct */
|
/* opaque struct */
|
||||||
struct atrac9_codec_data {
|
typedef struct {
|
||||||
uint8_t* data_buffer;
|
uint8_t* buf;
|
||||||
size_t data_buffer_size;
|
int buf_size;
|
||||||
|
|
||||||
sample_t* sample_buffer;
|
int16_t* sbuf;
|
||||||
size_t samples_filled; /* number of samples in the buffer */
|
int discard;
|
||||||
size_t samples_used; /* number of samples extracted from the buffer */
|
|
||||||
|
|
||||||
int samples_to_discard;
|
|
||||||
|
|
||||||
atrac9_config config;
|
atrac9_config config;
|
||||||
|
Atrac9CodecInfo info;
|
||||||
void* handle; /* decoder handle */
|
void* handle;
|
||||||
Atrac9CodecInfo info; /* decoder info */
|
} 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;
|
int status;
|
||||||
uint8_t config_data[4];
|
uint8_t config_data[4];
|
||||||
atrac9_codec_data* data = NULL;
|
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 */
|
// must hold at least one superframe and its samples
|
||||||
data->data_buffer_size = data->info.superframeSize;
|
data->buf_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;
|
|
||||||
|
|
||||||
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));
|
memcpy(&data->config, cfg, sizeof(atrac9_config));
|
||||||
|
|
||||||
|
@ -67,162 +76,132 @@ fail:
|
||||||
return NULL;
|
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 to_read = data->info.superframeSize;
|
||||||
int samples_to_get = data->samples_filled;
|
int bytes = read_streamfile(data->buf, vs->offset, to_read, vs->streamfile);
|
||||||
|
|
||||||
if (data->samples_to_discard) {
|
vs->offset += bytes;
|
||||||
/* 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,
|
return (bytes == to_read);
|
||||||
data->sample_buffer + data->samples_used*channels,
|
|
||||||
samples_to_get*channels * sizeof(sample_t));
|
|
||||||
|
|
||||||
samples_done += samples_to_get;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mark consumed samples */
|
static int decode(VGMSTREAM* v) {
|
||||||
data->samples_used += samples_to_get;
|
int channels = v->channels;
|
||||||
data->samples_filled -= samples_to_get;
|
atrac9_codec_data* data = v->codec_data;
|
||||||
}
|
|
||||||
else { /* decode data */
|
uint8_t* buf = data->buf;
|
||||||
int iframe, status;
|
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 bytes_used = 0;
|
||||||
uint8_t *buffer = data->data_buffer;
|
|
||||||
size_t bytes;
|
|
||||||
|
|
||||||
data->samples_used = 0;
|
int status = Atrac9Decode(data->handle, buf, sbuf, &bytes_used);
|
||||||
|
if (status < 0) {
|
||||||
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
|
VGM_LOG("ATRAC): decode error %i\n", status);
|
||||||
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
|
return false;
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
decode_fail:
|
data->discard = data->config.encoder_delay;
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_atrac9(atrac9_codec_data* data) {
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
if (!data->handle)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
|
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
|
||||||
{
|
{
|
||||||
int status;
|
|
||||||
uint8_t config_data[4];
|
|
||||||
|
|
||||||
Atrac9ReleaseHandle(data->handle);
|
Atrac9ReleaseHandle(data->handle);
|
||||||
data->handle = Atrac9GetHandle();
|
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);
|
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;
|
if (status < 0) goto fail;
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
void seek_atrac9(VGMSTREAM* v, int32_t num_sample) {
|
||||||
atrac9_codec_data* data = vgmstream->codec_data;
|
atrac9_codec_data* data = v->codec_data;
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
reset_atrac9(data);
|
reset_atrac9(data);
|
||||||
|
|
||||||
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */
|
// 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 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;
|
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
|
||||||
size_t superframe_number, superframe_back;
|
|
||||||
|
|
||||||
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
|
||||||
/* decoded frames affect each other slightly, so move offset back to make PCM stable
|
int superframe_number = (seek_sample / superframe_samples); // closest
|
||||||
* and equivalent to a full discard loop */
|
int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe)
|
||||||
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
|
|
||||||
if (superframe_back > superframe_number)
|
if (superframe_back > superframe_number)
|
||||||
superframe_back = superframe_number;
|
superframe_back = superframe_number;
|
||||||
|
|
||||||
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
|
int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
|
||||||
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
|
off_t seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
|
||||||
|
|
||||||
data->samples_to_discard = seek_discard; /* already includes encoder delay */
|
data->discard = seek_discard; // already includes encoder delay
|
||||||
|
|
||||||
if (vgmstream->loop_ch)
|
if (v->loop_ch) {
|
||||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
|
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
//old full discard loop
|
//old full discard loop
|
||||||
{
|
data->discard = num_sample;
|
||||||
data->samples_to_discard = num_sample;
|
data->discard += data->config.encoder_delay;
|
||||||
data->samples_to_discard += data->config.encoder_delay;
|
|
||||||
|
|
||||||
/* loop offsets are set during decode; force them to stream start so discard works */
|
// loop offsets are set during decode; force them to stream start so discard works
|
||||||
if (vgmstream->loop_ch)
|
if (vgmstream->loop_ch)
|
||||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||||
}
|
|
||||||
#endif
|
#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 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] = {
|
static const int sample_rate_table[16] = {
|
||||||
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
||||||
|
@ -266,7 +245,8 @@ fail:
|
||||||
return 0;
|
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);
|
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 0;
|
||||||
return bytes / frame_size * samples_per_frame;
|
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
|
#endif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "coding.h"
|
|
||||||
|
|
||||||
#ifdef VGM_USE_CELT
|
#ifdef VGM_USE_CELT
|
||||||
|
#include "coding.h"
|
||||||
|
#include "../base/decode_state.h"
|
||||||
|
#include "../base/codec_info.h"
|
||||||
#include "celt/celt_fsb.h"
|
#include "celt/celt_fsb.h"
|
||||||
|
|
||||||
#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */
|
#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_INTERNAL_SAMPLE_RATE 44100
|
||||||
#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */
|
#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 */
|
/* opaque struct */
|
||||||
struct celt_codec_data {
|
typedef struct {
|
||||||
sample_t* buffer;
|
uint8_t buf[FSB_CELT_MAX_DATA_SIZE];
|
||||||
|
int frame_size; // current size
|
||||||
|
|
||||||
sample_t* sample_buffer;
|
int16_t* sbuf;
|
||||||
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;
|
int discard;
|
||||||
|
|
||||||
int channel_mode;
|
int channel_mode;
|
||||||
celt_lib_t version;
|
celt_lib_t version;
|
||||||
void* mode_handle;
|
void* mode_handle;
|
||||||
void* decoder_handle;
|
void* decoder_handle;
|
||||||
};
|
} celt_codec_data;
|
||||||
|
|
||||||
|
static void free_celt_fsb(void* priv_data) {
|
||||||
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs).
|
celt_codec_data* data = priv_data;
|
||||||
* 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) {
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
switch(data->version) {
|
switch(data->version) {
|
||||||
|
@ -224,7 +46,189 @@ void free_celt_fsb(celt_codec_data* data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(data->sample_buffer);
|
free(data->sbuf);
|
||||||
free(data);
|
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
|
#endif
|
||||||
|
|
|
@ -318,13 +318,7 @@ void free_ea_mt(ea_mt_codec_data* data, int channels);
|
||||||
|
|
||||||
|
|
||||||
/* relic_decoder */
|
/* relic_decoder */
|
||||||
typedef struct relic_codec_data relic_codec_data;
|
void* init_relic(int channels, int bitrate, int codec_rate);
|
||||||
|
|
||||||
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);
|
|
||||||
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
|
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);
|
int32_t vorbis_custom_get_samples(VGMSTREAM* v);
|
||||||
#endif
|
#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
|
#ifdef VGM_USE_MPEG
|
||||||
/* mpeg_decoder */
|
/* 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(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);
|
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);
|
int mpeg_get_sample_rate(mpeg_codec_data* data);
|
||||||
long mpeg_bytes_to_samples(long bytes, const 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);
|
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(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
|
||||||
bool mpeg_get_frame_info_h(uint32_t header, 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);
|
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
|
||||||
#endif
|
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
|
#ifdef VGM_USE_G7221
|
||||||
|
@ -570,28 +563,16 @@ typedef struct {
|
||||||
uint32_t config_data; /* ATRAC9 config header */
|
uint32_t config_data; /* ATRAC9 config header */
|
||||||
int encoder_delay; /* initial samples to discard */
|
int encoder_delay; /* initial samples to discard */
|
||||||
} atrac9_config;
|
} atrac9_config;
|
||||||
typedef struct atrac9_codec_data atrac9_codec_data;
|
|
||||||
|
|
||||||
atrac9_codec_data* init_atrac9(atrac9_config* cfg);
|
void* init_atrac9(atrac9_config* cfg);
|
||||||
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
|
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data);
|
||||||
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);
|
|
||||||
size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data);
|
size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef VGM_USE_CELT
|
#ifdef VGM_USE_CELT
|
||||||
/* celt_fsb_decoder */
|
void* init_celt_fsb_v1(int channels);
|
||||||
typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t;
|
void* init_celt_fsb_v2(int channels);
|
||||||
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);
|
|
||||||
#endif
|
#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(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);
|
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 free_ffmpeg(void* data);
|
||||||
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 ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples);
|
void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples);
|
||||||
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
|
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);
|
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data);
|
||||||
void ffmpeg_set_force_seek(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_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);
|
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
|
||||||
|
|
||||||
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
|
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 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 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 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);
|
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libswresample/swresample.h>
|
#include <libswresample/swresample.h>
|
||||||
|
#include "../base/decode_state.h"
|
||||||
|
#include "../base/codec_info.h"
|
||||||
|
|
||||||
/* opaque struct */
|
/* opaque struct */
|
||||||
struct ffmpeg_codec_data {
|
struct ffmpeg_codec_data {
|
||||||
|
@ -26,32 +28,34 @@ struct ffmpeg_codec_data {
|
||||||
int stream_index;
|
int stream_index;
|
||||||
int64_t total_samples; /* may be 0 and innacurate */
|
int64_t total_samples; /* may be 0 and innacurate */
|
||||||
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
|
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 channel_remap[32]; /* map of channel > new position */
|
||||||
int invert_floats_set;
|
bool invert_floats_set;
|
||||||
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
|
bool skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
|
||||||
int force_seek; /* flags for special seeking in faulty formats */
|
bool force_seek; /* flags for special seeking in faulty formats */
|
||||||
int bad_init;
|
bool bad_init;
|
||||||
|
|
||||||
// FFmpeg context used for metadata
|
// FFmpeg context used for metadata
|
||||||
const AVCodec* codec;
|
const AVCodec* codec;
|
||||||
|
|
||||||
/* FFmpeg decoder state */
|
/* FFmpeg decoder state */
|
||||||
unsigned char* buffer;
|
uint8_t* buffer;
|
||||||
AVIOContext* ioCtx;
|
AVIOContext* ioCtx;
|
||||||
AVFormatContext* formatCtx;
|
AVFormatContext* formatCtx;
|
||||||
AVCodecContext* codecCtx;
|
AVCodecContext* codecCtx;
|
||||||
AVFrame* frame; /* last decoded frame */
|
AVFrame* frame; /* last decoded frame */
|
||||||
AVPacket* packet; /* last read data packet */
|
AVPacket* packet; /* last read data packet */
|
||||||
|
|
||||||
int read_packet;
|
bool read_packet;
|
||||||
int end_of_stream;
|
bool end_of_stream;
|
||||||
int end_of_audio;
|
bool end_of_audio;
|
||||||
|
|
||||||
/* sample state */
|
/* other state */
|
||||||
int32_t samples_discard;
|
int samples_discard;
|
||||||
int32_t samples_consumed;
|
|
||||||
int32_t samples_filled;
|
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) {
|
static void remap_audio_flt(float* outbuf, int sample_count, int channels, int* channel_mappings) {
|
||||||
int ch_from,ch_to,s;
|
for (int s = 0; s < sample_count; s++) {
|
||||||
sample_t temp;
|
for (int ch_from = 0; ch_from < channels; ch_from++) {
|
||||||
for (s = 0; s < sample_count; s++) {
|
|
||||||
for (ch_from = 0; ch_from < channels; ch_from++) {
|
|
||||||
if (ch_from > 32)
|
if (ch_from > 32)
|
||||||
continue;
|
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)
|
if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to)
|
||||||
continue;
|
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_from] = outbuf[s*channels + ch_to];
|
||||||
outbuf[s*channels + ch_to] = temp;
|
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.
|
* Special patching for FFmpeg's buggy seek code.
|
||||||
*
|
*
|
||||||
|
@ -198,7 +230,6 @@ test_seek:
|
||||||
avcodec_flush_buffers(data->codecCtx);
|
avcodec_flush_buffers(data->codecCtx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +388,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
||||||
if (errcode < 0) goto fail;
|
if (errcode < 0) goto fail;
|
||||||
|
|
||||||
/* reset non-zero values */
|
/* reset non-zero values */
|
||||||
data->read_packet = 1;
|
data->read_packet = true;
|
||||||
|
|
||||||
/* setup other values */
|
/* 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);
|
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
data->fmt = convert_sample_type(data);
|
||||||
|
if (data->fmt == SFMT_NONE)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
/* try to guess frames/samples (duration isn't always set) */
|
/* try to guess frames/samples (duration isn't always set) */
|
||||||
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
|
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||||
if (data->total_samples < 0)
|
if (data->total_samples < 0)
|
||||||
|
@ -545,23 +580,23 @@ fail:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* decodes a new frame to internal data */
|
/* decodes a new frame to internal data */
|
||||||
static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
|
static int decode_frame_internal(ffmpeg_codec_data* data) {
|
||||||
int errcode;
|
if (data->bad_init)
|
||||||
int frame_error = 0;
|
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) */
|
/* 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) {
|
if (/*data->end_of_stream ||*/ data->end_of_audio) {
|
||||||
VGM_LOG("FFMPEG: decode after end of audio\n");
|
VGM_LOG("FFMPEG: decode after end of audio\n");
|
||||||
goto fail;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool frame_error = false;
|
||||||
|
|
||||||
/* read data packets until valid is found */
|
/* read data packets until valid is found */
|
||||||
while (data->read_packet && !data->end_of_audio) {
|
while (data->read_packet && !data->end_of_audio) {
|
||||||
if (!data->end_of_stream) {
|
if (!data->end_of_stream) {
|
||||||
|
@ -569,19 +604,19 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
|
||||||
av_packet_unref(data->packet);
|
av_packet_unref(data->packet);
|
||||||
|
|
||||||
/* read encoded data from demuxer into 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 < 0) {
|
||||||
if (errcode == AVERROR_EOF) {
|
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 {
|
else {
|
||||||
VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode);
|
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) {
|
if (data->formatCtx->pb && data->formatCtx->pb->error) {
|
||||||
VGM_LOG("FFMPEG: pb error=%i\n", 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) */
|
/* 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 < 0) {
|
||||||
if (errcode != AVERROR(EAGAIN)) {
|
if (errcode != AVERROR(EAGAIN)) {
|
||||||
VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode);
|
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*/
|
/* decode frame samples from sent packet or "drain" samples*/
|
||||||
if (!frame_error) {
|
if (!frame_error) {
|
||||||
/* receive uncompressed sample data from decoded frame */
|
/* 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 < 0) {
|
||||||
if (errcode == AVERROR_EOF) {
|
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)) {
|
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 {
|
else {
|
||||||
VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode);
|
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
|
/* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output
|
||||||
* (ex. BlazBlue X360 022_btl_az.xwb) */
|
* (ex. BlazBlue X360 022_btl_az.xwb) */
|
||||||
|
|
||||||
|
return data->frame->nb_samples;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sample copy helpers, using different functions to minimize branches.
|
/* 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
|
* 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)
|
* (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
|
* 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)
|
* (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) {
|
static void samples_u8_to_s16(int16_t* obuf, uint8_t* ibuf, int ichs, int samples) {
|
||||||
int s, total_samples = samples * ochs;
|
for (int s = 0; s < samples * ichs; s++) {
|
||||||
for (s = 0; s < total_samples; s++) {
|
obuf[s] = ((int)ibuf[s] - 0x80) << 8;
|
||||||
obuf[s] = 0; /* memset'd */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void samples_u8p_to_s16(int16_t* obuf, uint8_t** ibuf, int ichs, int samples) {
|
||||||
static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) {
|
for (int ch = 0; ch < ichs; ch++) {
|
||||||
int s, total_samples = samples * ichs;
|
for (int s = 0; s < samples; s++) {
|
||||||
for (s = 0; s < total_samples; s++) {
|
obuf[s*ichs + ch] = ((int)ibuf[ch][s] - 0x80) << 8;
|
||||||
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_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) {
|
static void samples_s16_to_s16(int16_t* obuf, int16_t* ibuf, int ichs, int samples) {
|
||||||
int s, total_samples = samples * ichs;
|
for (int s = 0; s < samples * ichs; s++) {
|
||||||
for (s = 0; s < total_samples; s++) {
|
obuf[s] = ibuf[s];
|
||||||
obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void samples_s16p_to_s16(sample_t* obuf, int16_t** ibuf, int ichs, int samples, int skip) {
|
static void samples_s16p_to_s16(int16_t* obuf, int16_t** ibuf, int ichs, int samples) {
|
||||||
int s, ch;
|
for (int ch = 0; ch < ichs; ch++) {
|
||||||
for (ch = 0; ch < ichs; ch++) {
|
for (int s = 0; s < samples; s++) {
|
||||||
for (s = 0; s < samples; s++) {
|
obuf[s*ichs + ch] = ibuf[ch][s];
|
||||||
obuf[s*ichs + ch] = ibuf[ch][skip + s];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) {
|
static void samples_s32_to_s32(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
|
||||||
int s, total_samples = samples * ichs;
|
for (int s = 0; s < samples * ichs; s++) {
|
||||||
for (s = 0; s < total_samples; s++) {
|
obuf[s] = ibuf[ichs + s];
|
||||||
obuf[s] = ibuf[skip*ichs + s] >> 16;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void samples_s32p_to_s16(sample_t* obuf, int32_t** ibuf, int ichs, int samples, int skip) {
|
static void samples_s32p_to_s32(int32_t* obuf, int32_t** ibuf, int ichs, int samples) {
|
||||||
int s, ch;
|
for (int ch = 0; ch < ichs; ch++) {
|
||||||
for (ch = 0; ch < ichs; ch++) {
|
for (int s = 0; s < samples; s++) {
|
||||||
for (s = 0; s < samples; s++) {
|
obuf[s*ichs + ch] = ibuf[ch][s];
|
||||||
obuf[s*ichs + ch] = ibuf[ch][skip + s] >> 16;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) {
|
static void samples_flt_to_flt(float* obuf, float* ibuf, int ichs, int samples, bool invert) {
|
||||||
int s, total_samples = samples * ichs;
|
float scale = invert ? -1.0f : 1.0;
|
||||||
float scale = invert ? -32768.0f : 32768.0f;
|
for (int s = 0; s < samples * ichs; s++) {
|
||||||
for (s = 0; s < total_samples; s++) {
|
obuf[s] = ibuf[s] * scale;
|
||||||
obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) {
|
static void samples_fltp_to_flt(float* obuf, float** ibuf, int ichs, int samples, bool invert) {
|
||||||
int s, ch;
|
float scale = invert ? -1.0f : 1.0;
|
||||||
float scale = invert ? -32768.0f : 32768.0f;
|
for (int ch = 0; ch < ichs; ch++) {
|
||||||
for (ch = 0; ch < ichs; ch++) {
|
for (int s = 0; s < samples; s++) {
|
||||||
for (s = 0; s < samples; s++) {
|
obuf[s*ichs + ch] = ibuf[ch][s] * scale;
|
||||||
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 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)
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
|
||||||
int channels = data->codecCtx->channels;
|
int channels = data->codecCtx->channels;
|
||||||
#else
|
#else
|
||||||
int channels = data->codecCtx->ch_layout.nb_channels;
|
int channels = data->codecCtx->ch_layout.nb_channels;
|
||||||
#endif
|
#endif
|
||||||
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
|
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
|
||||||
void* ibuf;
|
|
||||||
|
|
||||||
|
void* ibuf;
|
||||||
if (is_planar) {
|
if (is_planar) {
|
||||||
ibuf = data->frame->extended_data;
|
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) {
|
switch (data->codecCtx->sample_fmt) {
|
||||||
/* unused? */
|
case AV_SAMPLE_FMT_U8P:
|
||||||
case AV_SAMPLE_FMT_U8P: if (is_planar) { samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
|
case AV_SAMPLE_FMT_U8:
|
||||||
// fall through
|
if (is_planar)
|
||||||
case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
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:
|
||||||
case AV_SAMPLE_FMT_S16P: if (is_planar) { samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
|
case AV_SAMPLE_FMT_S16:
|
||||||
// fall through
|
if (is_planar)
|
||||||
case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
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:
|
||||||
case AV_SAMPLE_FMT_S32P: if (is_planar) { samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
|
case AV_SAMPLE_FMT_S32:
|
||||||
// fall through
|
if (is_planar)
|
||||||
case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
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:
|
||||||
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; }
|
case AV_SAMPLE_FMT_FLT:
|
||||||
// fall through
|
if (is_planar)
|
||||||
case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break;
|
samples_fltp_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
|
||||||
|
else
|
||||||
/* possibly PCM64 only (not enabled) */
|
samples_flt_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
|
||||||
case AV_SAMPLE_FMT_DBLP: if (is_planar) { samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
|
break;
|
||||||
// fall through
|
|
||||||
case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->channel_remap_set)
|
// FFmpeg can't do PCM24 and upsamples to PCM32, if needed
|
||||||
remap_audio(outbuf, samples_to_do, channels, data->channel_remap);
|
if (data->fmt == SFMT_S24) {
|
||||||
|
samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remap_audio(ffmpeg_codec_data* data, sbuf_t* sbuf) {
|
||||||
|
if (!data->channel_remap_set)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
decode_fail:
|
switch(sbuf->fmt) {
|
||||||
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do);
|
case SFMT_FLT:
|
||||||
samples_silence_s16(outbuf, channels, samples_to_do);
|
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 */
|
/* UTILS */
|
||||||
/* ******************************************** */
|
/* ******************************************** */
|
||||||
|
|
||||||
void reset_ffmpeg(ffmpeg_codec_data* data) {
|
static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
|
||||||
seek_ffmpeg(data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
/* Start from 0 and discard samples until sample (slower but not too noticeable).
|
/* 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);
|
avcodec_flush_buffers(data->codecCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->samples_consumed = 0;
|
|
||||||
data->samples_filled = 0;
|
|
||||||
data->samples_discard = num_sample;
|
data->samples_discard = num_sample;
|
||||||
|
|
||||||
data->read_packet = 1;
|
data->read_packet = true;
|
||||||
data->end_of_stream = 0;
|
data->end_of_stream = false;
|
||||||
data->end_of_audio = 0;
|
data->end_of_audio = false;
|
||||||
|
|
||||||
/* consider skip samples (encoder delay), if manually set */
|
/* consider skip samples (encoder delay), if manually set */
|
||||||
if (data->skip_samples_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) {
|
static void free_ffmpeg_config(ffmpeg_codec_data* data) {
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return;
|
return;
|
||||||
|
@ -946,10 +931,11 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) {
|
||||||
av_freep(&(data->buffer));
|
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)
|
if (data == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1003,9 +989,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* set skip samples with our internal discard */
|
/* set skip samples with our internal discard */
|
||||||
data->skip_samples_set = 1;
|
data->skip_samples_set = true;
|
||||||
data->samples_discard = skip_samples;
|
|
||||||
data->skip_samples = skip_samples;
|
data->skip_samples = skip_samples;
|
||||||
|
data->samples_discard = skip_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns channel layout if set */
|
/* returns channel layout if set */
|
||||||
|
@ -1033,7 +1019,6 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
|
||||||
* but FFmpeg doesn't do it automatically
|
* but FFmpeg doesn't do it automatically
|
||||||
* (maybe should be done via mixing, but could clash with other stuff?) */
|
* (maybe should be done via mixing, but could clash with other stuff?) */
|
||||||
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
|
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
|
||||||
int i;
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
|
||||||
int channels = data->codecCtx->channels;
|
int channels = data->codecCtx->channels;
|
||||||
#else
|
#else
|
||||||
|
@ -1043,10 +1028,12 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
|
||||||
if (channels > 32)
|
if (channels > 32)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < channels; i++) {
|
for (int i = 0; i < channels; i++) {
|
||||||
data->channel_remap[i] = channel_remap[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) {
|
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),
|
/* 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).
|
* 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 */
|
* 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 */
|
reset_ffmpeg(data); /* reset state from trying to seek */
|
||||||
//stream = data->formatCtx->streams[data->stream_index];
|
//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) {
|
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
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) {
|
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;
|
if (!data) return NULL;
|
||||||
return data->sf;
|
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
|
#endif
|
||||||
|
|
|
@ -84,7 +84,7 @@ static bool read_frame(VGMSTREAM* v) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool decode_frame_ka1a(VGMSTREAM* v) {
|
static bool decode_frame_ka1a(VGMSTREAM* v) {
|
||||||
bool ok = read_frame(v);
|
bool ok = read_frame(v);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "relic_lib.h"
|
#include "relic_lib.h"
|
||||||
|
@ -14,7 +15,6 @@
|
||||||
extern void relic_mixfft_fft(int n, float* xRe, float* xIm, float* yRe, float* yIm);
|
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_MAX_SCALES 6
|
||||||
#define RELIC_BASE_SCALE 10.0f
|
#define RELIC_BASE_SCALE 10.0f
|
||||||
#define RELIC_FREQUENCY_MASKING_FACTOR 1.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) {
|
static void init_dct(float* dct, int dct_size) {
|
||||||
int i;
|
const int dct_quarter = dct_size >> 2;
|
||||||
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);
|
double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size);
|
||||||
dct[i] = sin(temp);
|
dct[i] = sin(temp);
|
||||||
dct[dct_quarter + i] = cos(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) {
|
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_re[RELIC_MAX_FFT];
|
||||||
float out_im[RELIC_MAX_FFT];
|
float out_im[RELIC_MAX_FFT];
|
||||||
float in_re[RELIC_MAX_FFT];
|
float in_re[RELIC_MAX_FFT];
|
||||||
float in_im[RELIC_MAX_FFT];
|
float in_im[RELIC_MAX_FFT];
|
||||||
float wave_tmp[RELIC_MAX_SIZE];
|
float wave_tmp[RELIC_MAX_SIZE];
|
||||||
int dct_half = dct_size >> 1;
|
const int dct_half = dct_size >> 1;
|
||||||
int dct_quarter = dct_size >> 2;
|
const int dct_quarter = dct_size >> 2;
|
||||||
int dct_3quarter = 3 * (dct_size >> 2);
|
const int dct_3quarter = 3 * (dct_size >> 2);
|
||||||
|
|
||||||
/* prerotation? */
|
/* prerotation? */
|
||||||
for (i = 0; i < dct_quarter; i++) {
|
for (int i = 0; i < dct_quarter; i++) {
|
||||||
float coef1 = freq[2 * i] * 0.5f;
|
float coef1 = freq[2 * i] * 0.5f;
|
||||||
float coef2 = freq[dct_half - 1 - 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];
|
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);
|
relic_mixfft_fft(dct_quarter, in_re, in_im, out_re, out_im);
|
||||||
|
|
||||||
/* postrotation, window and reorder? */
|
/* postrotation, window and reorder? */
|
||||||
factor = 8.0 / sqrt(dct_size);
|
float factor = 8.0 / sqrt(dct_size);
|
||||||
for (i = 0; i < dct_quarter; i++) {
|
for (int i = 0; i < dct_quarter; i++) {
|
||||||
float out_re_i = out_re[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_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;
|
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] = out_re[i];
|
||||||
wave_tmp[i * 2 + dct_half] = out_im[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_tmp[i] = -wave_tmp[dct_size - 1 - i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wave mix thing? */
|
/* 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];
|
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];
|
wave[i] = -wave_tmp[i - dct_3quarter];
|
||||||
}
|
}
|
||||||
return 0;
|
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) {
|
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];
|
float wave_tmp[RELIC_MAX_SIZE];
|
||||||
int dct_half = dct_size >> 1;
|
const int dct_half = dct_size >> 1;
|
||||||
|
|
||||||
/* copy for first half(?) */
|
/* copy for first half(?) */
|
||||||
memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float));
|
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);
|
apply_idct(freq2, wave_prv, dct, dct_size);
|
||||||
|
|
||||||
/* overlap and apply window function to filter this block's beginning */
|
/* 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_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];
|
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) {
|
static void init_window(float *window, int dct_size) {
|
||||||
int i;
|
for (int i = 0; i < dct_size; i++) {
|
||||||
|
|
||||||
for (i = 0; i < dct_size; i++) {
|
|
||||||
window[i] = sin((float)i * (RELIC_PI / dct_size));
|
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) {
|
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];
|
float wave_tmp[RELIC_MAX_SIZE];
|
||||||
|
|
||||||
/* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */
|
/* 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) {
|
if (dct_mode == RELIC_SIZE_LOW) {
|
||||||
/* 128 DCT to 256 samples (repeat sample x2) */
|
/* 128 DCT to 256 samples (repeat sample x2) */
|
||||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
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 + 0] = wave_tmp[i >> 1];
|
||||||
wave_cur[i + 1] = 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) {
|
if (dct_mode == RELIC_SIZE_LOW) {
|
||||||
/* 128 DCT to 512 samples (repeat sample x4) */
|
/* 128 DCT to 512 samples (repeat sample x4) */
|
||||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
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 + 0] = wave_tmp[i >> 2];
|
||||||
wave_cur[i + 1] = wave_tmp[i >> 2];
|
wave_cur[i + 1] = wave_tmp[i >> 2];
|
||||||
wave_cur[i + 2] = 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) {
|
else if (dct_mode == RELIC_SIZE_MID) {
|
||||||
/* 256 DCT to 512 samples (repeat sample x2) */
|
/* 256 DCT to 512 samples (repeat sample x2) */
|
||||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID);
|
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 + 0] = wave_tmp[i >> 1];
|
||||||
wave_cur[i + 1] = 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) {
|
static void init_dequantization(float* scales) {
|
||||||
int i;
|
|
||||||
|
|
||||||
scales[0] = RELIC_BASE_SCALE;
|
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];
|
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];
|
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;
|
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
|
||||||
int qv, pos;
|
int qv;
|
||||||
uint8_t ev;
|
uint8_t ev;
|
||||||
uint8_t move;
|
uint8_t move;
|
||||||
uint32_t bit_offset, max_offset;
|
uint32_t bit_offset, max_offset;
|
||||||
int i, j;
|
const int freq_half = freq_size >> 1;
|
||||||
int freq_half = freq_size >> 1;
|
|
||||||
|
|
||||||
|
|
||||||
memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float));
|
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 */
|
/* read packed exponents indexes for all bands */
|
||||||
if (cb_bits > 0 && ev_bits > 0) {
|
if (cb_bits > 0 && ev_bits > 0) {
|
||||||
pos = 0;
|
int pos = 0;
|
||||||
for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
|
for (int i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
|
||||||
if (bit_offset + cb_bits > max_offset)
|
if (bit_offset + cb_bits > max_offset)
|
||||||
goto fail;
|
goto fail;
|
||||||
move = read_ubits(cb_bits, bit_offset, buf);
|
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))
|
if (pos + 1 >= sizeof(critical_band_data))
|
||||||
goto fail;
|
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;
|
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) {
|
if (freq_half > 0 && ei_bits > 0) {
|
||||||
|
|
||||||
/* read first part */
|
/* read first part */
|
||||||
pos = 0;
|
int 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)
|
if (bit_offset + ei_bits > max_offset)
|
||||||
goto fail;
|
goto fail;
|
||||||
move = read_ubits(ei_bits, bit_offset, buf);
|
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 {
|
else {
|
||||||
pos = 0;
|
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)
|
if (bit_offset + ei_bits > max_offset)
|
||||||
goto fail;
|
goto fail;
|
||||||
move = read_ubits(ei_bits, bit_offset, buf);
|
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 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) */
|
/* 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);
|
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;
|
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);
|
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;
|
else return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
int s, ch;
|
|
||||||
int ichs = handle->channels;
|
int ichs = handle->channels;
|
||||||
|
|
||||||
for (ch = 0; ch < ichs; ch++) {
|
for (int s = 0; s < RELIC_SAMPLES_PER_FRAME; s++) {
|
||||||
for (s = 0; s < samples; s++) {
|
for (int ch = 0; ch < ichs; ch++) {
|
||||||
double d64_sample = handle->wave_cur[ch][skip + s];
|
double d64_sample = handle->wave_cur[ch][s];
|
||||||
int pcm_sample = clamp16((int32_t)d64_sample);
|
int pcm_sample = clamp16((int32_t)d64_sample);
|
||||||
|
|
||||||
/* f32 in PCM 32767.0 .. -32768.0 format, original code
|
/* 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)
|
//FQ_BNUM ((float)(1<<26)*(1<<26)*1.5)
|
||||||
//rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64))
|
//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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#define RELIC_BUFFER_SIZE 0x104
|
#define RELIC_BUFFER_SIZE 0x104
|
||||||
#define RELIC_SAMPLES_PER_FRAME 512
|
#define RELIC_SAMPLES_PER_FRAME 512
|
||||||
|
#define RELIC_MAX_CHANNELS 2
|
||||||
|
|
||||||
typedef struct relic_handle_t relic_handle_t;
|
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);
|
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
|
#endif
|
||||||
|
|
253
Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c
Normal file
253
Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_info.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -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->channels_per_frame = info.channels;
|
||||||
data->samples_per_frame = info.frame_samples;
|
data->samples_per_frame = info.frame_samples;
|
||||||
data->bitrate_per_frame = info.bit_rate;
|
data->bitrate = info.bit_rate;
|
||||||
data->sample_rate_per_frame = info.sample_rate;
|
data->sample_rate = info.sample_rate;
|
||||||
|
|
||||||
|
|
||||||
/* extra checks per type */
|
/* 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) */
|
case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */
|
||||||
current_interleave = data->config.interleave; /* big interleave */
|
current_interleave = data->config.interleave; /* big interleave */
|
||||||
current_interleave_pre = current_interleave*num_stream;
|
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;
|
current_data_size = data->config.chunk_size;
|
||||||
break;
|
break;
|
||||||
|
@ -142,7 +142,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
|
||||||
case MPEG_FSB: /* frames with padding + interleave */
|
case MPEG_FSB: /* frames with padding + interleave */
|
||||||
current_interleave = data->config.interleave; /* constant for multi-stream FSbs (1 frame + padding) */
|
current_interleave = data->config.interleave; /* constant for multi-stream FSbs (1 frame + padding) */
|
||||||
current_interleave_pre = current_interleave*num_stream;
|
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) {
|
if (stream->offset >= data->config.data_size) {
|
||||||
VGM_LOG_ONCE("MPEG: fsb overread\n");
|
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,
|
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_size, (uint32_t)stream->offset);
|
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_count, (uint32_t)stream->offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
|
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 */
|
/* check if current interleave block is short */
|
||||||
{
|
{
|
||||||
off_t block_offset = stream->offset - stream->channel_start_offset;
|
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)
|
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_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;
|
current_data_size = current_interleave;
|
||||||
break;
|
break;
|
||||||
|
@ -215,7 +215,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
current_interleave_pre = current_interleave*num_stream;
|
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 );
|
//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;
|
current_data_size = current_interleave;
|
||||||
|
@ -256,261 +256,3 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -189,11 +189,11 @@ fail:
|
||||||
|
|
||||||
|
|
||||||
#define AHX_KEY_BUFFER 0x2000
|
#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 */
|
/* check if current key ends properly in frame syncs */
|
||||||
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
|
bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
|
||||||
int bytes;
|
int bytes = 0;
|
||||||
uint8_t buf[AHX_KEY_BUFFER];
|
uint8_t buf[AHX_KEY_BUFFER];
|
||||||
const int buf_size = sizeof(buf);
|
const int buf_size = sizeof(buf);
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
|
@ -640,11 +640,8 @@ fail:
|
||||||
return 0;
|
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) {
|
static void ealayer3_copy_pcm_block(float* sbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) {
|
||||||
int i, ch;
|
|
||||||
uint8_t pcm_block[1152 * 2 * 2]; /* assumed max: 1 MPEG frame samples * 16b * max channels */
|
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)
|
if (pcm_number == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -654,7 +651,8 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
|
||||||
return;
|
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) {
|
if (bytes != pcm_size) {
|
||||||
VGM_LOG("EAL3: incorrect pcm_number %i at %lx\n", pcm_number, pcm_offset);
|
VGM_LOG("EAL3: incorrect pcm_number %i at %lx\n", pcm_number, pcm_offset);
|
||||||
return;
|
return;
|
||||||
|
@ -666,24 +664,24 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
|
||||||
if (is_packed) {
|
if (is_packed) {
|
||||||
/* ch0+ch1 packed together */
|
/* ch0+ch1 packed together */
|
||||||
int pos = 0;
|
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);
|
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 {
|
else {
|
||||||
/* all of ch0 first, then all of ch1 (EAL3 v1b only) */
|
/* all of ch0 first, then all of ch1 (EAL3 v1b only) */
|
||||||
int get_pos = 0;
|
int pos = 0;
|
||||||
for (ch = 0; ch < channels_per_frame; ch++) {
|
for (int ch = 0; ch < channels_per_frame; ch++) {
|
||||||
int put_pos = sizeof(sample_t) * ch;
|
int s = ch;
|
||||||
for (i = 0; i < pcm_number; i++) {
|
for (int i = 0; i < pcm_number; i++) {
|
||||||
int16_t pcm_sample = get_s16be(pcm_block + get_pos);
|
int16_t pcm_sample = get_s16be(pcm_block + pos);
|
||||||
put_s16le(outbuf + put_pos, pcm_sample);
|
sbuf[s] = pcm_sample / 32767.0f;
|
||||||
|
|
||||||
get_pos += sizeof(sample_t);
|
pos += 0x02;
|
||||||
put_pos += sizeof(sample_t) * channels_per_frame;
|
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) {
|
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];
|
mpeg_custom_stream* ms = &data->streams[num_stream];
|
||||||
int channels_per_frame = ms->channels_per_frame;
|
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) {
|
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);
|
VGM_LOG("EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
|
||||||
goto fail;
|
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) {
|
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;
|
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_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);
|
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);
|
// 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) */
|
/* 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;
|
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
|
//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
|
* 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 */
|
* 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;
|
ms->decode_to_discard += decode_to_discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eaf->v2_extended_flag) {
|
if (eaf->v2_extended_flag) {
|
||||||
uint8_t* outbuf = ms->sbuf;
|
|
||||||
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size;
|
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).
|
/* 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 */
|
* 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;
|
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",
|
//;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 improve how discarding works since there could exists a subtle-but-unlikely PCM+granule usage
|
||||||
//todo test other modes (only seen IGNORE)
|
//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) */
|
/* 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) */
|
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).
|
/* 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;
|
int ok, i;
|
||||||
ealayer3_buffer_t ib = {0};
|
ealayer3_buffer_t ib = {0};
|
||||||
ealayer3_frame_t eaf;
|
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 */
|
/* v1 does multichannel with set offsets */
|
||||||
if (data->type == MPEG_EAL31)
|
if (data->type == MPEG_EAL31)
|
||||||
|
|
|
@ -40,8 +40,8 @@ int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_
|
||||||
*coding_type = coding_MPEG_layer3;
|
*coding_type = coding_MPEG_layer3;
|
||||||
data->channels_per_frame = data->config.channels;
|
data->channels_per_frame = data->config.channels;
|
||||||
data->samples_per_frame = 1152;
|
data->samples_per_frame = 1152;
|
||||||
data->bitrate_per_frame = 320;
|
data->bitrate = 320;
|
||||||
data->sample_rate_per_frame = 48000;
|
data->sample_rate = 48000;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mpeg_frame_info info;
|
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->channels_per_frame = info.channels;
|
||||||
data->samples_per_frame = info.frame_samples;
|
data->samples_per_frame = info.frame_samples;
|
||||||
data->bitrate_per_frame = info.bit_rate;
|
data->bitrate = info.bit_rate;
|
||||||
data->sample_rate_per_frame = info.sample_rate;
|
data->sample_rate = info.sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,25 +126,25 @@ fail:
|
||||||
/* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */
|
/* 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];
|
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) {
|
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);
|
VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (eaf->pcm_number) {
|
if (eaf->pcm_number) {
|
||||||
|
|
||||||
/* read + write PCM block samples (always LE) */
|
/* 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;
|
||||||
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample_t)*i;
|
for (int i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) {
|
||||||
int16_t pcm_sample = read_s16le(pcm_offset, stream->streamfile);
|
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);
|
sbuf[i] = pcm_sample / 32767.0f;
|
||||||
|
pcm_offset += 0x02;
|
||||||
}
|
}
|
||||||
ms->samples_filled += eaf->pcm_number;
|
ms->samples_filled += eaf->pcm_number;
|
||||||
|
|
||||||
|
@ -162,13 +162,11 @@ fail:
|
||||||
|
|
||||||
/* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */
|
/* 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) {
|
static int eamp3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start) {
|
||||||
int ok, i;
|
|
||||||
eamp3_frame_info eaf = {0};
|
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 (int i = 0; i < skips; i++) {
|
||||||
for (i = 0; i < skips; i++) {
|
int ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf);
|
||||||
ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf);
|
|
||||||
if (!ok) goto fail;
|
if (!ok) goto fail;
|
||||||
|
|
||||||
//;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset);
|
//;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset);
|
||||||
|
|
|
@ -1,17 +1,67 @@
|
||||||
#include "coding.h"
|
|
||||||
#include "../util.h"
|
|
||||||
#include "../vgmstream.h"
|
|
||||||
|
|
||||||
#ifdef VGM_USE_MPEG
|
#ifdef VGM_USE_MPEG
|
||||||
|
#include <mpg123.h>
|
||||||
|
#include "coding.h"
|
||||||
|
#include "../vgmstream.h"
|
||||||
|
#include "../base/decode_state.h"
|
||||||
|
#include "../base/codec_info.h"
|
||||||
#include "mpeg_decoder.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 free_mpeg(void* priv_data) {
|
||||||
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
|
mpeg_codec_data* data = priv_data;
|
||||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data* data, int num_stream);
|
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 */
|
/* 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));
|
data->buffer = calloc(data->buffer_size, sizeof(uint8_t));
|
||||||
if (!data->buffer) goto fail;
|
if (!data->buffer) goto fail;
|
||||||
|
|
||||||
data->m = init_mpg123_handle();
|
data->handle = init_mpg123_handle();
|
||||||
if (!data->m) goto fail;
|
if (!data->handle) goto fail;
|
||||||
|
|
||||||
|
|
||||||
/* check format */
|
/* check format */
|
||||||
{
|
{
|
||||||
int rc, pos, bytes_read;
|
int bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf);
|
||||||
size_t bytes_done;
|
// don't check max as sfx can be smaller than buffer
|
||||||
|
|
||||||
bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf);
|
// start_offset should be correct but just in case, read first frame(s)
|
||||||
/* don't check max as sfx can be smaller than buffer */
|
int rc;
|
||||||
|
int pos = 0;
|
||||||
/* start_offset should be correct but just in case, read first frame(s) */
|
|
||||||
pos = 0;
|
|
||||||
do {
|
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) {
|
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
|
||||||
VGM_LOG("MPEG: unable to set up mpg123 at start offset\n");
|
VGM_LOG("MPEG: unable to set up mpg123 at start offset\n");
|
||||||
goto fail; //handle MPG123_DONE?
|
goto fail; //handle MPG123_DONE?
|
||||||
|
@ -53,23 +102,23 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
|
||||||
|
|
||||||
pos++;
|
pos++;
|
||||||
bytes_read--;
|
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;
|
long sample_rate_per_frame;
|
||||||
int channels_per_frame, encoding;
|
int channels_per_frame, encoding;
|
||||||
int rc;
|
struct mpg123_frameinfo mi = {0};
|
||||||
struct mpg123_frameinfo mi;
|
|
||||||
|
|
||||||
/* check first frame header and validate */
|
/* 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;
|
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;
|
goto fail;
|
||||||
if (sample_rate_per_frame != mi.rate)
|
if (sample_rate_per_frame != mi.rate)
|
||||||
goto fail;
|
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->channels_per_frame = channels_per_frame;
|
||||||
data->samples_per_frame = samples_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 */
|
// reinit, to ignore the reading done
|
||||||
memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo));
|
mpg123_open_feed(data->handle);
|
||||||
|
|
||||||
/* reinit, to ignore the reading done */
|
|
||||||
mpg123_open_feed(data->m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return data;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -142,8 +197,11 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
|
||||||
if (!ok)
|
if (!ok)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
|
if (channels < 1 || channels > MPEG_MAX_CHANNELS)
|
||||||
if (channels < data->channels_per_frame) goto fail;
|
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
|
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
|
||||||
/* max for some Ubi Lyn */
|
/* max for some Ubi Lyn */
|
||||||
if (data->default_buffer_size > 0x20000) {
|
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 */
|
/* 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 */
|
/* 2ch streams + odd channels = last stream must be 1ch */
|
||||||
/* (known channels combos are 2ch+..+2ch, 1ch+..+1ch, or rarely 2ch+..+2ch+1ch in EALayer3) */
|
/* (known channels combos are 2ch+..+2ch, 1ch+..+1ch, or rarely 2ch+..+2ch+1ch in EALayer3) */
|
||||||
if (data->channels_per_frame == 2 && channels % 2)
|
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;
|
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));
|
//data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
|
||||||
//if (!data->streams[i]) goto fail;
|
//if (!data->streams[i]) goto fail;
|
||||||
data->streams[i].handle = init_mpg123_handle(); /* decoder not shared as frames depend on prev state */
|
data->streams[i].handle = init_mpg123_handle(); /* decoder not shared as frames depend on prev state */
|
||||||
if (!data->streams[i].handle) goto fail;
|
if (!data->streams[i].handle) goto fail;
|
||||||
|
|
||||||
/* size could be any value */
|
/* 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));
|
data->streams[i].sbuf = calloc(data->streams[i].sbuf_size, sizeof(uint8_t));
|
||||||
if (!data->streams[i].sbuf) goto fail;
|
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;
|
if (!data->streams[i].buffer) goto fail;
|
||||||
|
|
||||||
data->streams[i].channels_per_frame = data->channels_per_frame;
|
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->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;
|
return data;
|
||||||
|
|
||||||
fail:
|
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 */
|
/* 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.
|
* Decode anything mpg123 can.
|
||||||
* Feeds raw data and extracts decoded samples as needed.
|
* 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;
|
int samples_done = 0;
|
||||||
while (samples_done < samples_to_do) {
|
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 */
|
/* end of stream, fill rest with 0s */
|
||||||
if (data->bytes_in_buffer <= 0) {
|
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));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,15 +286,15 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data
|
||||||
stream->offset += data->bytes_in_buffer;
|
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 */
|
/* feed new raw data to the decoder if needed, copy decoded results to output */
|
||||||
if (!data->buffer_used) {
|
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;
|
data->buffer_used = true;
|
||||||
}
|
}
|
||||||
else {
|
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 */
|
/* 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);
|
VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc);
|
||||||
|
|
||||||
/* update copied samples */
|
/* update copied samples */
|
||||||
samples_done += bytes_done / sizeof(sample_t) / channels;
|
samples_done += bytes_done / sizeof(float) / channels;
|
||||||
outbuf += bytes_done / sizeof(sample_t);
|
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.
|
/* 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. */
|
* 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) {
|
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;
|
int rc, ok;
|
||||||
mpeg_custom_stream* ms = &data->streams[num_stream];
|
mpeg_custom_stream* ms = &data->streams[num_stream];
|
||||||
int channels_per_frame = ms->channels_per_frame;
|
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);
|
//;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*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sbuf += ms->samples_filled * channels_per_frame;
|
||||||
bytes_filled = sizeof(sample_t) * 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 */
|
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
||||||
if (!ms->buffer_used) {
|
if (!ms->buffer_used) {
|
||||||
//;VGM_LOG("MPEG: feed new data and get samples\n");
|
//;VGM_LOG("MPEG: feed new data and get samples\n");
|
||||||
rc = mpg123_decode(ms->handle,
|
rc = mpg123_decode(ms->handle, ms->buffer, ms->bytes_in_buffer, sbuf, ms->sbuf_size - bytes_filled, &bytes_done);
|
||||||
ms->buffer, ms->bytes_in_buffer,
|
|
||||||
sbuf + bytes_filled, ms->sbuf_size - bytes_filled,
|
|
||||||
&bytes_done);
|
|
||||||
ms->buffer_used = true;
|
ms->buffer_used = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//;VGM_LOG("MPEG: get samples from old data\n");
|
//;VGM_LOG("MPEG: get samples from old data\n");
|
||||||
rc = mpg123_decode(ms->handle,
|
rc = mpg123_decode(ms->handle, NULL, 0, sbuf, ms->sbuf_size - bytes_filled, &bytes_done);
|
||||||
NULL, 0,
|
|
||||||
sbuf + bytes_filled, 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) */
|
/* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */
|
||||||
if (ms->decode_to_discard) {
|
if (ms->decode_to_discard) {
|
||||||
size_t decode_to_discard = ms->decode_to_discard;
|
size_t decode_to_discard = ms->decode_to_discard;
|
||||||
if (decode_to_discard > samples_filled)
|
if (decode_to_discard > samples_filled)
|
||||||
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;
|
bytes_done -= bytes_to_discard;
|
||||||
ms->decode_to_discard -= decode_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:
|
decode_fail:
|
||||||
/* 0-fill but continue with other streams */
|
/* 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);
|
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 */
|
/* 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 */
|
/* 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) {
|
static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
|
||||||
if (!data)
|
if (!data)
|
||||||
|
@ -579,11 +521,11 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
|
||||||
|
|
||||||
if (!data->custom) {
|
if (!data->custom) {
|
||||||
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
|
/* 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 {
|
else {
|
||||||
/* re-start from 0 */
|
/* 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)
|
if (!data->streams)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -609,25 +551,84 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
|
||||||
data->buffer_used = false;
|
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) {
|
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) {
|
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 not found just return 0 and expect to fail (if used for num_samples) */
|
||||||
if (!data->custom) {
|
if (!data->custom) {
|
||||||
/* We would need to read all VBR frames headers to count samples */
|
/* 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");
|
VGM_LOG("MPEG: vbr mp3 can't do bytes_to_samples\n");
|
||||||
return 0;
|
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 {
|
else {
|
||||||
/* needed for SCD */
|
/* needed for SCD */
|
||||||
if (data->streams_size && data->bitrate_per_frame) {
|
if (data->streams_count && data->bitrate) {
|
||||||
return (int64_t)(bytes / data->streams_size) * data->sample_rate_per_frame * 8 / (data->bitrate_per_frame * 1000);
|
return (int64_t)(bytes / data->streams_count) * data->sample_rate * 8 / (data->bitrate * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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 */
|
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
|
||||||
void mpeg_set_error_logging(mpeg_codec_data* data, int enable) {
|
void mpeg_set_error_logging(mpeg_codec_data* data, int enable) {
|
||||||
if (!data->custom) {
|
if (!data->custom) {
|
||||||
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
mpg123_param(data->handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int i;
|
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);
|
mpg123_param(data->streams[i].handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
|
@ -7,27 +7,26 @@
|
||||||
|
|
||||||
/* used by mpeg_decoder.c, but scattered in other .c files */
|
/* used by mpeg_decoder.c, but scattered in other .c files */
|
||||||
#ifdef VGM_USE_MPEG
|
#ifdef VGM_USE_MPEG
|
||||||
#include <mpg123/mpg123.h>
|
|
||||||
|
|
||||||
/* represents a single MPEG stream */
|
/* represents a single MPEG stream */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */
|
/* buf per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */
|
||||||
uint8_t* buffer; /* raw data buffer */
|
uint8_t* buffer;
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
size_t bytes_in_buffer;
|
size_t bytes_in_buffer;
|
||||||
bool buffer_full; /* raw buffer has been filled */
|
bool buffer_full;
|
||||||
bool buffer_used; /* raw buffer has been fed to the decoder */
|
bool buffer_used;
|
||||||
|
|
||||||
mpg123_handle* handle; /* MPEG decoder */
|
void* handle;
|
||||||
|
|
||||||
void* sbuf; /* decoded samples from this stream */
|
float* sbuf;
|
||||||
size_t sbuf_size; /* in bytes for mpg123 */
|
int sbuf_size; // in bytes for mpg123
|
||||||
size_t samples_filled; /* data in the buffer (in samples) */
|
int samples_filled;
|
||||||
size_t samples_used; /* data extracted from the buffer */
|
int samples_used;
|
||||||
|
|
||||||
size_t current_size_count; /* data read (if the parser needs to know) */
|
int current_size_count; /* data read (if the parser needs to know) */
|
||||||
size_t current_size_target; /* max data, until something happens */
|
int current_size_target; /* max data, until something happens */
|
||||||
size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
|
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 */
|
int channels_per_frame; /* for rare cases that streams don't share this */
|
||||||
} mpeg_custom_stream;
|
} mpeg_custom_stream;
|
||||||
|
@ -35,20 +34,20 @@ typedef struct {
|
||||||
struct mpeg_codec_data {
|
struct mpeg_codec_data {
|
||||||
/* regular/single MPEG internals */
|
/* regular/single MPEG internals */
|
||||||
uint8_t* buffer; /* raw data buffer */
|
uint8_t* buffer; /* raw data buffer */
|
||||||
size_t buffer_size;
|
int buffer_size;
|
||||||
size_t bytes_in_buffer;
|
int bytes_in_buffer;
|
||||||
bool buffer_full; /* raw buffer has been filled */
|
bool buffer_full; /* raw buffer has been filled */
|
||||||
bool buffer_used; /* raw buffer has been fed to the decoder */
|
bool buffer_used; /* raw buffer has been fed to the decoder */
|
||||||
|
|
||||||
mpg123_handle* m; /* MPEG decoder */
|
void* handle; // MPEG decoder
|
||||||
struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */
|
|
||||||
|
|
||||||
/* for internal use */
|
/* for internal use */
|
||||||
int channels_per_frame;
|
int channels_per_frame;
|
||||||
int samples_per_frame;
|
int samples_per_frame;
|
||||||
/* for some calcs */
|
/* for some calcs */
|
||||||
int bitrate_per_frame;
|
int bitrate;
|
||||||
int sample_rate_per_frame;
|
int sample_rate;
|
||||||
|
bool is_vbr;
|
||||||
|
|
||||||
/* custom MPEG internals */
|
/* custom MPEG internals */
|
||||||
bool custom; /* flag */
|
bool custom; /* flag */
|
||||||
|
@ -57,11 +56,13 @@ struct mpeg_codec_data {
|
||||||
|
|
||||||
size_t default_buffer_size;
|
size_t default_buffer_size;
|
||||||
mpeg_custom_stream* streams; /* array of MPEG streams (ex. 2ch+2ch) */
|
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 */
|
int skip_samples; /* base encoder delay */
|
||||||
size_t samples_to_discard; /* for custom mpeg looping */
|
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);
|
int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t* coding_type);
|
||||||
|
|
|
@ -1,22 +1,34 @@
|
||||||
#include "coding.h"
|
#include "coding.h"
|
||||||
|
#include "../base/decode_state.h"
|
||||||
|
#include "../base/codec_info.h"
|
||||||
#include "libs/relic_lib.h"
|
#include "libs/relic_lib.h"
|
||||||
|
|
||||||
//TODO: fix looping
|
|
||||||
|
|
||||||
struct relic_codec_data {
|
typedef struct {
|
||||||
relic_handle_t* handle;
|
relic_handle_t* handle;
|
||||||
int channels;
|
int channels;
|
||||||
int frame_size;
|
int frame_size;
|
||||||
|
|
||||||
int32_t samples_discard;
|
float fbuf[RELIC_SAMPLES_PER_FRAME * RELIC_MAX_CHANNELS];
|
||||||
int32_t samples_consumed;
|
|
||||||
int32_t samples_filled;
|
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;
|
relic_codec_data* data = NULL;
|
||||||
|
|
||||||
|
if (channels > RELIC_MAX_CHANNELS)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
data = calloc(1, sizeof(relic_codec_data));
|
data = calloc(1, sizeof(relic_codec_data));
|
||||||
if (!data) goto fail;
|
if (!data) goto fail;
|
||||||
|
|
||||||
|
@ -32,92 +44,68 @@ fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
|
static int decode_frame_channels(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
|
||||||
int ch;
|
|
||||||
int bytes;
|
|
||||||
int ok;
|
|
||||||
uint8_t buf[RELIC_BUFFER_SIZE];
|
uint8_t buf[RELIC_BUFFER_SIZE];
|
||||||
|
|
||||||
for (ch = 0; ch < data->channels; ch++) {
|
for (int ch = 0; ch < data->channels; ch++) {
|
||||||
bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
|
int bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
|
||||||
if (bytes != data->frame_size) goto fail;
|
|
||||||
stream->offset += data->frame_size;
|
stream->offset += data->frame_size;
|
||||||
|
|
||||||
ok = relic_decode_frame(data->handle, buf, ch);
|
if (bytes != data->frame_size) return -1;
|
||||||
if (!ok) goto fail;
|
|
||||||
|
int ok = relic_decode_frame(data->handle, buf, ch);
|
||||||
|
if (!ok) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->samples_consumed = 0;
|
return RELIC_SAMPLES_PER_FRAME;
|
||||||
data->samples_filled = RELIC_SAMPLES_PER_FRAME;
|
|
||||||
return 1;
|
|
||||||
fail:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
relic_get_float(data->handle, data->fbuf);
|
||||||
/* consume samples */
|
|
||||||
int samples_to_get = (data->samples_filled - data->samples_consumed);
|
|
||||||
|
|
||||||
if (data->samples_discard) {
|
sbuf_init_f16(&ds->sbuf, data->fbuf, samples, data->channels);
|
||||||
/* discard samples for looping */
|
ds->sbuf.filled = samples;
|
||||||
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;
|
|
||||||
|
|
||||||
relic_get_pcm16(data->handle, outbuf, samples_to_get, data->samples_consumed);
|
if (data->discard) {
|
||||||
|
ds->discard = data->discard;
|
||||||
samples_to_do -= samples_to_get;
|
data->discard = 0;
|
||||||
outbuf += samples_to_get * data->channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mark consumed samples */
|
return true;
|
||||||
data->samples_consumed += samples_to_get;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int ok = decode_frame_next(stream, data);
|
|
||||||
if (!ok) goto decode_fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_relic(relic_codec_data* data) {
|
static void reset_relic(void* priv_data) {
|
||||||
|
relic_codec_data* data = priv_data;
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
relic_reset(data->handle);
|
relic_reset(data->handle);
|
||||||
data->samples_filled = 0;
|
data->discard = 0;
|
||||||
data->samples_consumed = 0;
|
|
||||||
data->samples_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;
|
if (!data) return;
|
||||||
|
|
||||||
reset_relic(data);
|
reset_relic(data);
|
||||||
data->samples_discard = num_sample;
|
data->discard = num_sample;
|
||||||
}
|
|
||||||
|
|
||||||
void free_relic(relic_codec_data* data) {
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
relic_free(data->handle);
|
|
||||||
free(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) {
|
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) {
|
||||||
return bytes / channels / (bitrate / 8) * RELIC_SAMPLES_PER_FRAME;
|
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,
|
||||||
|
};
|
||||||
|
|
|
@ -129,7 +129,7 @@ fail:
|
||||||
bool setup_layout_layered(layered_layout_data* data) {
|
bool setup_layout_layered(layered_layout_data* data) {
|
||||||
int max_input_channels = 0;
|
int max_input_channels = 0;
|
||||||
int max_output_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) */
|
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
|
||||||
for (int i = 0; i < data->layer_count; i++) {
|
for (int i = 0; i < data->layer_count; i++) {
|
||||||
|
@ -166,9 +166,9 @@ bool setup_layout_layered(layered_layout_data* data) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->layers[i]) );
|
sfmt_t current_sample_type = mixing_get_input_sample_type(data->layers[i]);
|
||||||
if (max_sample_size < current_sample_size)
|
if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority
|
||||||
max_sample_size = current_sample_size;
|
max_sample_type = current_sample_type;
|
||||||
|
|
||||||
/* loops and other values could be mismatched, but should be handled on allocate */
|
/* 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)
|
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
|
||||||
return false;
|
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 */
|
/* create internal buffer big enough for mixing all layers */
|
||||||
free(data->buffer);
|
free(data->buffer);
|
||||||
data->buffer = malloc(VGMSTREAM_LAYER_SAMPLE_BUFFER * max_input_channels * max_sample_size);
|
data->buffer = malloc(VGMSTREAM_LAYER_SAMPLE_BUFFER * max_input_channels * max_sample_size);
|
||||||
|
|
|
@ -23,6 +23,7 @@ typedef struct {
|
||||||
int input_channels; /* internal buffer channels */
|
int input_channels; /* internal buffer channels */
|
||||||
int output_channels; /* resulting channels (after mixing, if applied) */
|
int output_channels; /* resulting channels (after mixing, if applied) */
|
||||||
bool mixed_channels; /* segments have different number of channels */
|
bool mixed_channels; /* segments have different number of channels */
|
||||||
|
sfmt_t fmt;
|
||||||
} segmented_layout_data;
|
} segmented_layout_data;
|
||||||
|
|
||||||
void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream);
|
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 output_channels; /* resulting channels (after mixing, if applied) */
|
||||||
int external_looping; /* don't loop using per-layer loops, but layout's own looping */
|
int external_looping; /* don't loop using per-layer loops, but layout's own looping */
|
||||||
int curr_layer; /* helper */
|
int curr_layer; /* helper */
|
||||||
|
sfmt_t fmt;
|
||||||
} layered_layout_data;
|
} layered_layout_data;
|
||||||
|
|
||||||
void render_vgmstream_layered(sbuf_t* sbuf, VGMSTREAM* vgmstream);
|
void render_vgmstream_layered(sbuf_t* sbuf, VGMSTREAM* vgmstream);
|
||||||
|
|
|
@ -155,7 +155,7 @@ fail:
|
||||||
bool setup_layout_segmented(segmented_layout_data* data) {
|
bool setup_layout_segmented(segmented_layout_data* data) {
|
||||||
int max_input_channels = 0;
|
int max_input_channels = 0;
|
||||||
int max_output_channels = 0;
|
int max_output_channels = 0;
|
||||||
int max_sample_size = 0;
|
sfmt_t max_sample_type = SFMT_NONE;
|
||||||
bool mixed_channels = false;
|
bool mixed_channels = false;
|
||||||
|
|
||||||
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
|
/* 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;
|
// goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->segments[i]) );
|
sfmt_t current_sample_type = mixing_get_input_sample_type(data->segments[i]);
|
||||||
if (max_sample_size < current_sample_size)
|
if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority
|
||||||
max_sample_size = current_sample_size;
|
max_sample_type = current_sample_type;
|
||||||
|
|
||||||
/* init mixing */
|
/* init mixing */
|
||||||
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER);
|
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)
|
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
|
||||||
return false;
|
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 */
|
/* create internal buffer big enough for mixing */
|
||||||
free(data->buffer);
|
free(data->buffer);
|
||||||
data->buffer = malloc(VGMSTREAM_SEGMENT_SAMPLE_BUFFER * max_input_channels * max_sample_size);
|
data->buffer = malloc(VGMSTREAM_SEGMENT_SAMPLE_BUFFER * max_input_channels * max_sample_size);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "../coding/coding.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* init_vgmstream_astb(STREAMFILE* sf) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
off_t start_offset, data_size;
|
off_t start_offset, data_size;
|
||||||
|
|
|
@ -72,6 +72,10 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||||
loop_flag = find_meta_loops(data, &loop_start, &loop_end);
|
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) */
|
/* hack for AAC files (will return 0 samples if not an actual file) */
|
||||||
if (!num_samples && check_extensions(sf, "aac,laac")) {
|
if (!num_samples && check_extensions(sf, "aac,laac")) {
|
||||||
num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));
|
num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));
|
||||||
|
|
|
@ -181,7 +181,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
||||||
vgmstream->layout_type = layout_layered;
|
vgmstream->layout_type = layout_layered;
|
||||||
}
|
}
|
||||||
else {
|
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;
|
if (!vgmstream->codec_data) goto fail;
|
||||||
vgmstream->coding_type = coding_CELT_FSB;
|
vgmstream->coding_type = coding_CELT_FSB;
|
||||||
vgmstream->layout_type = layout_none;
|
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;
|
data->layers[i]->loop_end_sample = fsb->loop_end;
|
||||||
|
|
||||||
#ifdef VGM_USE_CELT
|
#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;
|
if (!data->layers[i]->codec_data) goto fail;
|
||||||
data->layers[i]->coding_type = coding_CELT_FSB;
|
data->layers[i]->coding_type = coding_CELT_FSB;
|
||||||
data->layers[i]->layout_type = layout_none;
|
data->layers[i]->layout_type = layout_none;
|
||||||
|
|
|
@ -405,7 +405,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
|
||||||
vgmstream->layout_type = layout_layered;
|
vgmstream->layout_type = layout_layered;
|
||||||
}
|
}
|
||||||
else {
|
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;
|
if (!vgmstream->codec_data) goto fail;
|
||||||
vgmstream->coding_type = coding_CELT_FSB;
|
vgmstream->coding_type = coding_CELT_FSB;
|
||||||
vgmstream->layout_type = layout_none;
|
vgmstream->layout_type = layout_none;
|
||||||
|
@ -591,7 +591,7 @@ static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, STREAMFILE* sb, f
|
||||||
switch (fsb5->codec) {
|
switch (fsb5->codec) {
|
||||||
#ifdef VGM_USE_CELT
|
#ifdef VGM_USE_CELT
|
||||||
case 0x0C: { /* 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;
|
if (!data->layers[i]->codec_data) goto fail;
|
||||||
data->layers[i]->coding_type = coding_CELT_FSB;
|
data->layers[i]->coding_type = coding_CELT_FSB;
|
||||||
data->layers[i]->layout_type = layout_none;
|
data->layers[i]->layout_type = layout_none;
|
||||||
|
|
|
@ -65,12 +65,13 @@ static const fsbkey_info fsbkey_list[] = {
|
||||||
{ MODE_FSB5, FSBKEY_ADD("cbfjZTlUPaZI") }, // JDM: Japanese Drift Master (PC)
|
{ 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_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("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("resoforce") }, // Rivals of Aether 2 (PC)
|
||||||
{ MODE_FSB5, FSBKEY_ADD("3cfe772db5b55b806541d3faf894020e") }, // Final Fantasy XV: War for Eos (Android)
|
{ MODE_FSB5, FSBKEY_ADD("3cfe772db5b55b806541d3faf894020e") }, // Final Fantasy XV: War for Eos (Android)
|
||||||
{ MODE_FSB5, FSBKEY_ADD("aj#$kLucf2lh}eqh") }, // Forza Motorsport 2023 (PC)
|
{ MODE_FSB5, FSBKEY_ADD("aj#$kLucf2lh}eqh") }, // Forza Motorsport 2023 (PC)
|
||||||
{ MODE_FSB4, FSBKEY_ADD("dpdjeoqkr") }, // AirRider CrazyRacing (PC)
|
{ MODE_FSB4, FSBKEY_ADD("dpdjeoqkr") }, // AirRider CrazyRacing (PC)
|
||||||
{ MODE_FSB5, FSBKEY_ADD("weareAbsolutelyUnsure2018") }, // Wanderstop (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
|
/* 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) */
|
* (could add all of them but there are a lot of songs, so external .fsbkey are probably better) */
|
||||||
|
|
|
@ -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 */
|
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
|
/* header is encrypted except in M&L 3DS so decrypt + handle in buffer; format
|
||||||
* base 0x30 header + subheader with tables/headers depending on type */
|
* 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;
|
coef_offset = target_pos + 0x28;
|
||||||
|
|
||||||
h->channels = 1;
|
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)
|
if (h->num_samples == 0)
|
||||||
h->num_samples = h->loop_end;
|
h->num_samples = h->loop_end;
|
||||||
|
// seems correct based on MadWorld, not 100% sure in Imabikisou
|
||||||
if (h->sample_rate == 0)
|
if (h->sample_rate == 0)
|
||||||
h->sample_rate = 32000;
|
h->sample_rate = 24000;
|
||||||
|
|
||||||
/* empty entry */
|
/* empty entry */
|
||||||
if (h->stream_size == 0)
|
if (h->stream_size == 0)
|
||||||
|
|
|
@ -145,12 +145,12 @@ VGMSTREAM* init_vgmstream_rfrm(STREAMFILE* sf) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
version = read_32bitBE(0x18,sf); /* assumed, also at 0x1c */
|
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_32bit = read_32bitBE;
|
||||||
read_16bit = read_16bitBE;
|
read_16bit = read_16bitBE;
|
||||||
big_endian = 1;
|
big_endian = 1;
|
||||||
}
|
}
|
||||||
else if (version == 0x12) { /* Switch */
|
else if (version == 0x11 || version == 0x12) { /* RS11 and DKCTF Switch */
|
||||||
read_32bit = read_32bitLE;
|
read_32bit = read_32bitLE;
|
||||||
read_16bit = read_16bitLE;
|
read_16bit = read_16bitLE;
|
||||||
big_endian = 0;
|
big_endian = 0;
|
||||||
|
|
Loading…
Reference in a new issue