VGMStream: Updated libvgmstream code base
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:
Christopher Snowhill 2025-05-03 01:56:03 -07:00
parent 048bc7c30d
commit 69533e12c7
31 changed files with 1434 additions and 1539 deletions

View file

@ -669,6 +669,7 @@
837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */; };
837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; };
8383A62C281203C60062E49E /* s3v.c in Sources */ = {isa = PBXBuildFile; fileRef = 8383A62B281203C50062E49E /* s3v.c */; };
8384EC322DC60DD70037EFFD /* mpeg_custom_info.c in Sources */ = {isa = PBXBuildFile; fileRef = 8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */; };
83852B0B2680247900378854 /* rxws.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B092680247900378854 /* rxws.c */; };
83852B0C2680247900378854 /* ads_midway.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B0A2680247900378854 /* ads_midway.c */; };
8385D4E6245174C700FF8E67 /* diva.c in Sources */ = {isa = PBXBuildFile; fileRef = 8385D4E2245174C600FF8E67 /* diva.c */; };
@ -1624,6 +1625,7 @@
837CEAF023487F2C00E62A4A /* sqex_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_streamfile.h; sourceTree = "<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>"; };
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>"; };
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>"; };
@ -2022,6 +2024,7 @@
83B73C482D8FF17900A57F08 /* mio_decoder.c */,
834F7D6A2C7093EA003AC386 /* mp4_aac_decoder.c */,
834F7D692C7093EA003AC386 /* mpc3_decoder.c */,
8384EC312DC60DD70037EFFD /* mpeg_custom_info.c */,
834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */,
834F7D6B2C7093EA003AC386 /* mpeg_custom_utils_ahx.c */,
834F7D6C2C7093EA003AC386 /* mpeg_custom_utils_ealayer3.c */,
@ -3382,6 +3385,7 @@
8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */,
834F7DC62C7093EA003AC386 /* g721_decoder.c in Sources */,
834F7DA32C7093EA003AC386 /* nwa_lib.c in Sources */,
8384EC322DC60DD70037EFFD /* mpeg_custom_info.c in Sources */,
8306B0EA20984590000302D4 /* caf.c in Sources */,
83AA7F842519C042004C5298 /* svag_snk.c in Sources */,
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */,

View file

@ -74,11 +74,11 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v
LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_title_t* cfg, char* buf, int buf_len) {
if (!buf || !buf_len || !cfg)
if (!buf || !buf_len)
return LIBVGMSTREAM_ERROR_GENERIC;
buf[0] = '\0';
if (!lib || !lib->priv)
if (!lib || !lib->priv || !cfg)
return LIBVGMSTREAM_ERROR_GENERIC;
libvgmstream_priv_t* priv = lib->priv;

View file

@ -16,6 +16,19 @@ extern const codec_info_t mio_decoder;
extern const codec_info_t pcm32_decoder;
extern const codec_info_t pcm24_decoder;
extern const codec_info_t pcmfloat_decoder;
#ifdef VGM_USE_FFMPEG
extern const codec_info_t ffmpeg_decoder;
#endif
#ifdef VGM_USE_ATRAC9
extern const codec_info_t atrac9_decoder;
#endif
#ifdef VGM_USE_CELT
extern const codec_info_t celt_fsb_decoder;
#endif
#ifdef VGM_USE_MPEG
extern const codec_info_t mpeg_decoder;
#endif
extern const codec_info_t relic_decoder;
const codec_info_t* codec_get_info(VGMSTREAM* v) {
switch(v->coding_type) {
@ -50,6 +63,28 @@ const codec_info_t* codec_get_info(VGMSTREAM* v) {
return &pcm24_decoder;
case coding_PCMFLOAT:
return &pcmfloat_decoder;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
return &ffmpeg_decoder;
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return &atrac9_decoder;
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return &celt_fsb_decoder;
#endif
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
return &mpeg_decoder;
#endif
case coding_RELIC:
return &relic_decoder;
default:
return NULL;
}

View file

@ -50,10 +50,6 @@ void decode_free(VGMSTREAM* vgmstream) {
free_circus_vq(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_RELIC) {
free_relic(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) {
free_ice(vgmstream->codec_data);
@ -71,28 +67,12 @@ void decode_free(VGMSTREAM* vgmstream) {
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
free_ffmpeg(vgmstream->codec_data);
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) {
free_mp4_aac(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
free_mpeg(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) {
free_g7221(vgmstream->codec_data);
@ -105,18 +85,6 @@ void decode_free(VGMSTREAM* vgmstream) {
}
#endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
free_atrac9(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
free_celt_fsb(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_ACM) {
free_acm(vgmstream->codec_data);
}
@ -143,10 +111,6 @@ void decode_seek(VGMSTREAM* vgmstream) {
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_RELIC) {
seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) {
seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample);
@ -164,40 +128,12 @@ void decode_seek(VGMSTREAM* vgmstream) {
seek_ea_mt(vgmstream, vgmstream->loop_current_sample);
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
seek_ffmpeg(vgmstream->codec_data, vgmstream->loop_current_sample);
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) {
seek_mp4_aac(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
seek_atrac9(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
seek_celt_fsb(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
seek_mpeg(vgmstream, vgmstream->loop_current_sample);
}
#endif
if (vgmstream->coding_type == coding_NWA) {
seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample);
}
@ -220,10 +156,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
reset_circus_vq(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_RELIC) {
reset_relic(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) {
reset_ice(vgmstream->codec_data);
@ -247,16 +179,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
}
#endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
reset_mpeg(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) {
reset_g7221(vgmstream->codec_data);
@ -269,24 +191,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
}
#endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
reset_atrac9(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
reset_celt_fsb(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
reset_ffmpeg(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_ACM) {
reset_acm(vgmstream->codec_data);
}
@ -342,13 +246,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_PCM24BE:
case coding_PCM32LE:
return 1;
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
#endif
case coding_SDX2:
case coding_SDX2_int:
case coding_CBD2:
@ -464,10 +361,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
#ifdef VGM_USE_G719
case coding_G719:
return 48000/50;
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
return 0;
#endif
case coding_MTAF:
return 128*2;
@ -497,22 +390,12 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
return 0; /* 432, but variable in looped files */
case coding_CIRCUS_VQ:
return 0;
case coding_RELIC:
return 0; /* 512 */
case coding_ICE_RANGE:
case coding_ICE_DCT:
return 0; /* ~100 (range), ~16 (DCT) */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
return mp4_get_samples_per_frame(vgmstream->codec_data);
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return 0; /* varies with config data, usually 256 or 1024 */
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return 0; /* 512? */
#endif
default:
return 0;
@ -679,9 +562,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
#endif
#ifdef VGM_USE_G719
case coding_G719:
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
case coding_MTAF:
return vgmstream->interleave_block_size;
@ -704,7 +584,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
/* UBI_ADPCM: varies per mode? */
/* IMUSE: VBR */
/* EA_MT: VBR, frames of bit counts or PCM frames */
/* ATRAC9: CBR around 0x100-200 */
/* CELT FSB: varies, usually 0x80-100 */
/* TAC: VBR around ~0x200-300 */
default: /* (VBR or managed by decoder) */
@ -1113,18 +992,10 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
case coding_CIRCUS_VQ:
decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
break;
case coding_RELIC:
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do);
break;
case coding_ICE_RANGE:
case coding_ICE_DCT:
decode_ice(vgmstream->codec_data, buffer, samples_to_do);
break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
@ -1316,15 +1187,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
}
break;
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
decode_mpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#ifdef VGM_USE_G7221
case coding_G7221C:
for (ch = 0; ch < vgmstream->channels; ch++) {
@ -1338,16 +1200,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
}
break;
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
decode_atrac9(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
case coding_ACM:
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);

View file

@ -145,6 +145,18 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan
sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) {
if (vgmstream->layout_type == layout_layered) {
layered_layout_data* data = vgmstream->layout_data;
if (data)
return data->fmt;
}
if (vgmstream->layout_type == layout_segmented) {
segmented_layout_data* data = vgmstream->layout_data;
if (data)
return data->fmt;
}
const codec_info_t* codec_info = codec_get_info(vgmstream);
if (codec_info) {
if (codec_info->sample_type)

View file

@ -5,10 +5,10 @@
#include "../util/log.h"
// float-to-int modes
//#define PCM16_ROUNDING_LRINT // potentially faster in some systems/compilers and much slower in others
//#define PCM16_ROUNDING_LRINT // rounding down, potentially faster in some systems/compilers and much slower in others (also affects rounding)
//#define PCM16_ROUNDING_HALF // rounding half + down (vorbis-style), more 'accurate' but slower
#ifdef PCM16_ROUNDING_HALF
#if defined(PCM16_ROUNDING_HALF) || defined(PCM16_ROUNDING_LRINT)
#include <math.h>
#endif
@ -27,7 +27,7 @@
* It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions.
*/
static inline int float_to_int(float val) {
#if PCM16_ROUNDING_LRINT
#ifdef PCM16_ROUNDING_LRINT
return lrintf(val);
#elif defined(_MSC_VER)
return (int)val;
@ -38,7 +38,7 @@ static inline int float_to_int(float val) {
#if 0
static inline int double_to_int(double val) {
#if PCM16_ROUNDING_LRINT
#ifdef PCM16_ROUNDING_LRINT
return lrint(val);
#elif defined(_MSC_VER)
return (int)val;
@ -439,7 +439,7 @@ void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int dst_max)
typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int fade_duration);
#define DEFINE_SBUF_FADE(suffix, buftype) \
#define DEFINE_SBUF_FADE(suffix, buftype, func) \
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
buftype* buf = sbuf->buf; \
int s = start * sbuf->channels; \
@ -447,14 +447,14 @@ typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int
while (s < s_end) { \
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
for (int i = 0; i < sbuf->channels; i++) { \
buf[s] = float_to_int(buf[s] * fadedness); \
buf[s] = func(buf[s] * fadedness); \
s++; \
} \
fade_pos++; \
} \
}
#define DEFINE_SBUF_FD24(suffix, buftype) \
#define DEFINE_SBUF_FD24(suffix, buftype, func) \
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
buftype* buf = sbuf->buf; \
int s = start * sbuf->channels; \
@ -462,17 +462,21 @@ typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int
while (s < s_end) { \
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
for (int i = 0; i < sbuf->channels; i++) { \
put_u24ne(buf + s * 3, float_to_int(get_s24ne(buf + s * 3) * fadedness) ); \
put_u24ne(buf + s * 3, func(get_s24ne(buf + s * 3) * fadedness) ); \
s++; \
} \
fade_pos++; \
} \
}
DEFINE_SBUF_FADE(i16, int16_t);
DEFINE_SBUF_FADE(i32, int32_t);
DEFINE_SBUF_FADE(flt, float);
DEFINE_SBUF_FD24(o24, uint8_t);
// no need to clamp in fade outs
#define CONV_FADE_FLT(x) (x)
#define CONV_FADE_PCM(x) float_to_int(x)
DEFINE_SBUF_FADE(i16, int16_t, CONV_FADE_PCM);
DEFINE_SBUF_FADE(i32, int32_t, CONV_FADE_PCM);
DEFINE_SBUF_FADE(flt, float, CONV_FADE_FLT);
DEFINE_SBUF_FD24(o24, uint8_t, CONV_FADE_PCM);
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) {
//TODO: use interpolated fadedness to improve performance?

View file

@ -1,28 +1,35 @@
#include "coding.h"
#ifdef VGM_USE_ATRAC9
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libatrac9/libatrac9.h"
/* opaque struct */
struct atrac9_codec_data {
uint8_t* data_buffer;
size_t data_buffer_size;
typedef struct {
uint8_t* buf;
int buf_size;
sample_t* sample_buffer;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int samples_to_discard;
int16_t* sbuf;
int discard;
atrac9_config config;
void* handle; /* decoder handle */
Atrac9CodecInfo info; /* decoder info */
};
Atrac9CodecInfo info;
void* handle;
} atrac9_codec_data;
atrac9_codec_data* init_atrac9(atrac9_config* cfg) {
static void free_atrac9(void* priv_data) {
atrac9_codec_data* data = priv_data;
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->buf);
free(data->sbuf);
free(data);
}
void* init_atrac9(atrac9_config* cfg) {
int status;
uint8_t config_data[4];
atrac9_codec_data* data = NULL;
@ -47,16 +54,18 @@ atrac9_codec_data* init_atrac9(atrac9_config* cfg) {
}
/* must hold at least one superframe and its samples */
data->data_buffer_size = data->info.superframeSize;
/* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */
data->data_buffer = calloc(data->data_buffer_size + 0x10, sizeof(uint8_t));
if (!data->data_buffer) goto fail;
/* while ATRAC9 uses float internally, Sony's API only returns PCM16 */
data->sample_buffer = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t));
if (!data->sample_buffer) goto fail;
// must hold at least one superframe and its samples
data->buf_size = data->info.superframeSize;
data->samples_to_discard = cfg->encoder_delay;
// extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though)
data->buf = calloc(data->buf_size + 0x10, sizeof(uint8_t));
if (!data->buf) goto fail;
// while ATRAC9 uses float internally, Sony's API only returns PCM16
data->sbuf = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t));
if (!data->sbuf) goto fail;
data->discard = cfg->encoder_delay;
memcpy(&data->config, cfg, sizeof(atrac9_config));
@ -67,162 +76,132 @@ fail:
return NULL;
}
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
atrac9_codec_data* data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
// read one raw block (superframe) and advance offsets; CBR and around 0x100-200 bytes
static bool read_frame(VGMSTREAM* v) {
VGMSTREAMCHANNEL* vs = &v->ch[0];
atrac9_codec_data* data = v->codec_data;
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
int to_read = data->info.superframeSize;
int bytes = read_streamfile(data->buf, vs->offset, to_read, vs->streamfile);
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
vs->offset += bytes;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample_t));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int iframe, status;
int bytes_used = 0;
uint8_t *buffer = data->data_buffer;
size_t bytes;
data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
/* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) goto decode_fail;
stream->offset += bytes;
/* decode all frames in the superframe block */
for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail;
buffer += bytes_used;
data->samples_filled += data->info.frameSamples;
}
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("ATRAC9: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels);
return (bytes == to_read);
}
void reset_atrac9(atrac9_codec_data* data) {
if (!data) return;
static int decode(VGMSTREAM* v) {
int channels = v->channels;
atrac9_codec_data* data = v->codec_data;
if (!data->handle)
goto fail;
uint8_t* buf = data->buf;
int16_t* sbuf = data->sbuf;
// decode all frames in the superframe block
int samples = 0;
for (int iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
int bytes_used = 0;
int status = Atrac9Decode(data->handle, buf, sbuf, &bytes_used);
if (status < 0) {
VGM_LOG("ATRAC): decode error %i\n", status);
return false;
}
buf += bytes_used;
sbuf += data->info.frameSamples * channels;
samples += data->info.frameSamples;
}
return samples;
}
// ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
// superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples).
static bool decode_frame_atrac9(VGMSTREAM* v) {
bool ok = read_frame(v);
if (!ok)
return false;
int samples = decode(v);
if (samples <= 0)
return false;
decode_state_t* ds = v->decode_state;
atrac9_codec_data* data = v->codec_data;
sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
if (data->discard) {
ds->discard += data->discard;
data->discard = 0;
}
return true;
}
void reset_atrac9(void* priv_data) {
atrac9_codec_data* data = priv_data;
if (!data || !data->handle)
return;
data->discard = data->config.encoder_delay;
#if 0
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
{
int status;
uint8_t config_data[4];
Atrac9ReleaseHandle(data->handle);
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
if (!data->handle) return;
uint8_t config_data[4];
put_u32be(config_data, data->config.config_data);
status = Atrac9InitDecoder(data->handle, config_data);
int status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
}
#endif
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = data->config.encoder_delay;
return;
fail:
return; /* decode calls should fail... */
}
void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) {
atrac9_codec_data* data = vgmstream->codec_data;
void seek_atrac9(VGMSTREAM* v, int32_t num_sample) {
atrac9_codec_data* data = v->codec_data;
if (!data) return;
reset_atrac9(data);
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */
{
// find closest offset to desired sample, and samples to discard after that offset to reach loop
int32_t seek_sample = data->config.encoder_delay + num_sample;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
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 */
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
// decoded frames affect each other slightly, so move offset back to make PCM stable
// and equivalent to a full discard loop
int superframe_number = (seek_sample / superframe_samples); // closest
int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe)
if (superframe_back > superframe_number)
superframe_back = superframe_number;
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
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)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
if (v->loop_ch) {
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset;
}
#if 0
//old full discard loop
{
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
data->discard = num_sample;
data->discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */
// loop offsets are set during decode; force them to stream start so discard works
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
#endif
}
void free_atrac9(atrac9_codec_data* data) {
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
static int atrac9_parse_config(uint32_t config_data, int* p_sample_rate, int* p_channels, size_t* p_frame_size, size_t* p_samples_per_frame) {
static const int sample_rate_table[16] = {
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@ -266,7 +245,8 @@ fail:
return 0;
}
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) {
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data) {
atrac9_codec_data* data = priv_data;
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
}
@ -276,4 +256,12 @@ size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data) {
return 0;
return bytes / frame_size * samples_per_frame;
}
const codec_info_t atrac9_decoder = {
.sample_type = SFMT_S16, //TODO: decoder doesn't return float (to match Sony's lib apparently)
.decode_frame = decode_frame_atrac9,
.free = free_atrac9,
.reset = reset_atrac9,
.seek = seek_atrac9,
};
#endif

View file

@ -1,6 +1,7 @@
#include "coding.h"
#ifdef VGM_USE_CELT
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "celt/celt_fsb.h"
#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */
@ -9,204 +10,25 @@
#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100
#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */
typedef enum { CELT_0_06_1, CELT_0_11_0} celt_lib_t;
/* opaque struct */
struct celt_codec_data {
sample_t* buffer;
typedef struct {
uint8_t buf[FSB_CELT_MAX_DATA_SIZE];
int frame_size; // current size
sample_t* sample_buffer;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int16_t* sbuf;
int samples_to_discard;
int discard;
int channel_mode;
celt_lib_t version;
void* mode_handle;
void* decoder_handle;
};
} celt_codec_data;
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs).
* FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */
celt_codec_data* init_celt_fsb(int channels, celt_lib_t version) {
int error = 0, lib_version = 0;
celt_codec_data* data = NULL;
data = calloc(1, sizeof(celt_codec_data));
if (!data) goto fail;
data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */
data->version = version;
switch(data->version) {
case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */
data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error);
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */
data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error);
if (!data->decoder_handle || error != CELT_OK) goto fail;
break;
default:
goto fail;
}
data->sample_buffer = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(sample_t));
if (!data->sample_buffer) goto fail;
/* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */
return data;
fail:
free_celt_fsb(data);
return NULL;
}
void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
celt_codec_data* data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample_t));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int status;
uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0};
size_t bytes, frame_size;
data->samples_used = 0;
/* FSB DLLs do seem to check this fixed value */
if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) {
goto decode_fail;
}
frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile);
if (frame_size > FSB_CELT_MAX_DATA_SIZE) {
goto decode_fail;
}
/* read and decode one raw block and advance offsets */
bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile);
if (bytes != frame_size) goto decode_fail;
switch(data->version) {
case CELT_0_06_1:
status = celt_decode_0061(data->decoder_handle, data_buffer,bytes, data->sample_buffer);
break;
case CELT_0_11_0:
status = celt_decode_0110(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME);
break;
default:
goto decode_fail;
}
if (status != CELT_OK) goto decode_fail;
stream->offset += 0x04+0x04+frame_size;
data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME;
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels);
}
void reset_celt_fsb(celt_codec_data* data) {
if (!data) return;
/* recreate decoder (mode should not change) */
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle) celt_decoder_destroy_0061(data->decoder_handle);
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0:
if (data->decoder_handle) celt_decoder_destroy_0110(data->decoder_handle);
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL);
if (!data->decoder_handle) goto fail;
break;
default:
goto fail;
}
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) {
celt_codec_data* data = vgmstream->codec_data;
if (!data) return;
reset_celt_fsb(data);
data->samples_to_discard = num_sample;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
void free_celt_fsb(celt_codec_data* data) {
static void free_celt_fsb(void* priv_data) {
celt_codec_data* data = priv_data;
if (!data) return;
switch(data->version) {
@ -224,7 +46,189 @@ void free_celt_fsb(celt_codec_data* data) {
break;
}
free(data->sample_buffer);
free(data->sbuf);
free(data);
}
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs).
* FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */
static void* init_celt_fsb(int channels, celt_lib_t version) {
int error = 0, lib_version = 0;
celt_codec_data* data = NULL;
data = calloc(1, sizeof(celt_codec_data));
if (!data) goto fail;
data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */
data->version = version;
switch(data->version) {
case CELT_0_06_1: // older FSB4 (FMOD ~4.33)
data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error);
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0: // newer FSB4 (FMOD ~4.34), FSB5
data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error);
if (!data->decoder_handle || error != CELT_OK) goto fail;
break;
default:
goto fail;
}
data->sbuf = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(int16_t));
if (!data->sbuf) goto fail;
// ~128 samples of encoder delay, but FMOD DLLs don't discard them?
return data;
fail:
free_celt_fsb(data);
return NULL;
}
void* init_celt_fsb_v1(int channels) {
return init_celt_fsb(channels, CELT_0_06_1);
}
void* init_celt_fsb_v2(int channels) {
return init_celt_fsb(channels, CELT_0_11_0);
}
// read and decode one raw block and advance offsets
static bool read_frame(VGMSTREAM* v) {
VGMSTREAMCHANNEL* vs = &v->ch[0];
celt_codec_data* data = v->codec_data;
// FSB DLLs do seem to check this fixed value
if (read_u32be(vs->offset + 0x00, vs->streamfile) != 0x17C30DF3)
return false;
data->frame_size = read_u32le(vs->offset + 0x04, vs->streamfile);
if (data->frame_size > FSB_CELT_MAX_DATA_SIZE)
return false;
int bytes = read_streamfile(data->buf, vs->offset+0x08, data->frame_size, vs->streamfile);
vs->offset += 0x04 + 0x04 + data->frame_size;
return (bytes == data->frame_size);
}
static int decode(VGMSTREAM* v) {
celt_codec_data* data = v->codec_data;
int samples = FSB_CELT_SAMPLES_PER_FRAME;
int status;
switch(data->version) {
case CELT_0_06_1:
status = celt_decode_0061(data->decoder_handle, data->buf, data->frame_size, data->sbuf);
if (status != CELT_OK)
return -1;
return samples;
case CELT_0_11_0:
status = celt_decode_0110(data->decoder_handle, data->buf, data->frame_size, data->sbuf, samples);
if (status != CELT_OK)
return -1;
return samples;
default:
return -1;
}
}
static bool decode_frame_celt_fsb(VGMSTREAM* v) {
bool ok = read_frame(v);
if (!ok)
return false;
int samples = decode(v);
if (samples <= 0)
return false;
decode_state_t* ds = v->decode_state;
celt_codec_data* data = v->codec_data;
sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
if (data->discard) {
ds->discard += data->discard;
data->discard = 0;
}
return true;
}
void reset_celt_fsb(void* priv_data) {
celt_codec_data* data = priv_data;
if (!data) return;
// recreate decoder (mode should not change)
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle)
celt_decoder_destroy_0061(data->decoder_handle);
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0:
if (data->decoder_handle)
celt_decoder_destroy_0110(data->decoder_handle);
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL);
if (!data->decoder_handle) goto fail;
break;
default:
goto fail;
}
data->discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_celt_fsb(VGMSTREAM* v, int32_t num_sample) {
celt_codec_data* data = v->codec_data;
if (!data) return;
reset_celt_fsb(data);
data->discard = num_sample;
/* loop offsets are set during decode; force them to stream start so discard works */
if (v->loop_ch)
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset;
}
const codec_info_t celt_fsb_decoder = {
.sample_type = SFMT_S16, // decoder doesn't return float
.decode_frame = decode_frame_celt_fsb,
.free = free_celt_fsb,
.reset = reset_celt_fsb,
.seek = seek_celt_fsb,
};
#endif

View file

@ -318,13 +318,7 @@ void free_ea_mt(ea_mt_codec_data* data, int channels);
/* relic_decoder */
typedef struct relic_codec_data relic_codec_data;
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate);
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
void reset_relic(relic_codec_data* data);
void seek_relic(relic_codec_data* data, int32_t num_sample);
void free_relic(relic_codec_data* data);
void* init_relic(int channels, int bitrate, int codec_rate);
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
@ -451,15 +445,6 @@ void free_vorbis_custom(void* data);
int32_t vorbis_custom_get_samples(VGMSTREAM* v);
#endif
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
#ifdef VGM_USE_MPEG
/* mpeg_decoder */
@ -506,19 +491,27 @@ typedef struct {
mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels);
mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config* config);
void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_mpeg(mpeg_codec_data* data);
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample);
void free_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header);
bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey);
#endif
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info);
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey);
#endif
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
#ifdef VGM_USE_G7221
@ -570,28 +563,16 @@ typedef struct {
uint32_t config_data; /* ATRAC9 config header */
int encoder_delay; /* initial samples to discard */
} atrac9_config;
typedef struct atrac9_codec_data atrac9_codec_data;
atrac9_codec_data* init_atrac9(atrac9_config* cfg);
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_atrac9(atrac9_codec_data* data);
void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample);
void free_atrac9(atrac9_codec_data* data);
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data);
void* init_atrac9(atrac9_config* cfg);
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data);
size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data);
#endif
#ifdef VGM_USE_CELT
/* celt_fsb_decoder */
typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t;
typedef struct celt_codec_data celt_codec_data;
celt_codec_data* init_celt_fsb(int channels, celt_lib_t version);
void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_celt_fsb(celt_codec_data* data);
void seek_celt_fsb(VGMSTREAM* vgmstream, int32_t num_sample);
void free_celt_fsb(celt_codec_data* data);
void* init_celt_fsb_v1(int channels);
void* init_celt_fsb_v2(int channels);
#endif
@ -610,10 +591,7 @@ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t s
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size);
ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_ffmpeg(ffmpeg_codec_data* data);
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample);
void free_ffmpeg(ffmpeg_codec_data* data);
void free_ffmpeg(void* data);
void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples);
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
@ -621,6 +599,7 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data);
void ffmpeg_set_force_seek(ffmpeg_codec_data* data);
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data);
void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data);
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
@ -739,8 +718,6 @@ size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align);
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels);
size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay);

View file

@ -5,6 +5,8 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include "../base/decode_state.h"
#include "../base/codec_info.h"
/* opaque struct */
struct ffmpeg_codec_data {
@ -26,32 +28,34 @@ struct ffmpeg_codec_data {
int stream_index;
int64_t total_samples; /* may be 0 and innacurate */
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
int channel_remap_set;
bool channel_remap_set;
int channel_remap[32]; /* map of channel > new position */
int invert_floats_set;
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
int force_seek; /* flags for special seeking in faulty formats */
int bad_init;
bool invert_floats_set;
bool skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
bool force_seek; /* flags for special seeking in faulty formats */
bool bad_init;
// FFmpeg context used for metadata
const AVCodec* codec;
/* FFmpeg decoder state */
unsigned char* buffer;
uint8_t* buffer;
AVIOContext* ioCtx;
AVFormatContext* formatCtx;
AVCodecContext* codecCtx;
AVFrame* frame; /* last decoded frame */
AVPacket* packet; /* last read data packet */
int read_packet;
int end_of_stream;
int end_of_audio;
bool read_packet;
bool end_of_stream;
bool end_of_audio;
/* sample state */
int32_t samples_discard;
int32_t samples_consumed;
int32_t samples_filled;
/* other state */
int samples_discard;
sfmt_t fmt;
void* sbuf;
int sbuf_samples;
};
@ -82,25 +86,53 @@ static void g_init_ffmpeg(void) {
}
}
static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) {
int ch_from,ch_to,s;
sample_t temp;
for (s = 0; s < sample_count; s++) {
for (ch_from = 0; ch_from < channels; ch_from++) {
static void remap_audio_flt(float* outbuf, int sample_count, int channels, int* channel_mappings) {
for (int s = 0; s < sample_count; s++) {
for (int ch_from = 0; ch_from < channels; ch_from++) {
if (ch_from > 32)
continue;
ch_to = channel_mappings[ch_from];
int ch_to = channel_mappings[ch_from];
if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to)
continue;
temp = outbuf[s*channels + ch_from];
float temp = outbuf[s*channels + ch_from];
outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to];
outbuf[s*channels + ch_to] = temp;
}
}
}
static sfmt_t convert_sample_type(ffmpeg_codec_data* data) {
switch (data->codecCtx->sample_fmt) {
// used?
case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_U8:
// common
case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S16:
return SFMT_S16;
// PCM32 and FLAC 24-bit (upscaled)
case AV_SAMPLE_FMT_S32P:
case AV_SAMPLE_FMT_S32:
return SFMT_S32;
// transform-based codecs (ATRAC3, AAC, etc)
case AV_SAMPLE_FMT_FLTP:
case AV_SAMPLE_FMT_FLT:
return SFMT_FLT;
// seemingly not used by any codec, only filters
case AV_SAMPLE_FMT_DBLP:
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_S64P:
case AV_SAMPLE_FMT_S64:
default:
return SFMT_NONE;
}
}
/**
* Special patching for FFmpeg's buggy seek code.
*
@ -198,7 +230,6 @@ test_seek:
avcodec_flush_buffers(data->codecCtx);
return 0;
fail:
return -1;
}
@ -357,7 +388,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
if (errcode < 0) goto fail;
/* reset non-zero values */
data->read_packet = 1;
data->read_packet = true;
/* setup other values */
{
@ -377,6 +408,10 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
#endif
data->fmt = convert_sample_type(data);
if (data->fmt == SFMT_NONE)
goto fail;
/* try to guess frames/samples (duration isn't always set) */
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->total_samples < 0)
@ -545,23 +580,23 @@ fail:
return -1;
}
/* decodes a new frame to internal data */
static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
int errcode;
int frame_error = 0;
static int decode_frame_internal(ffmpeg_codec_data* data) {
if (data->bad_init)
return -1;
if (data->bad_init) {
goto fail;
}
/* ignore once file is done (but not on EOF as FFmpeg can output samples until end_of_audio) */
if (/*data->end_of_stream ||*/ data->end_of_audio) {
VGM_LOG("FFMPEG: decode after end of audio\n");
goto fail;
return -1;
}
bool frame_error = false;
/* read data packets until valid is found */
while (data->read_packet && !data->end_of_audio) {
if (!data->end_of_stream) {
@ -569,19 +604,19 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
av_packet_unref(data->packet);
/* read encoded data from demuxer into packet */
errcode = av_read_frame(data->formatCtx, data->packet);
int errcode = av_read_frame(data->formatCtx, data->packet);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
data->end_of_stream = 1; /* no more data to read (but may "drain" samples) */
data->end_of_stream = true; // no more data to read (but may "drain" samples)
}
else {
VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode);
frame_error = 1; //goto fail;
frame_error = true; //goto fail;
}
if (data->formatCtx->pb && data->formatCtx->pb->error) {
VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error);
frame_error = 1; //goto fail;
frame_error = true; //goto fail;
}
}
@ -591,31 +626,31 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
}
/* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */
errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
int errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
if (errcode < 0) {
if (errcode != AVERROR(EAGAIN)) {
VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode);
frame_error = 1; //goto fail;
frame_error = true; //goto fail;
}
}
data->read_packet = 0; /* got data */
data->read_packet = false; // got data
}
/* decode frame samples from sent packet or "drain" samples*/
if (!frame_error) {
/* receive uncompressed sample data from decoded frame */
errcode = avcodec_receive_frame(data->codecCtx, data->frame);
int errcode = avcodec_receive_frame(data->codecCtx, data->frame);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
data->end_of_audio = 1; /* no more audio, file is fully decoded */
data->end_of_audio = true; // no more audio, file is fully decoded
}
else if (errcode == AVERROR(EAGAIN)) {
data->read_packet = 1; /* 0 samples, request more encoded data */
data->read_packet = true; // 0 samples, request more encoded data
}
else {
VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode);
frame_error = 1;//goto fail;
frame_error = true; //goto fail;
}
}
}
@ -623,155 +658,83 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
/* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output
* (ex. BlazBlue X360 022_btl_az.xwb) */
data->samples_consumed = 0;
data->samples_filled = data->frame->nb_samples;
return 1;
fail:
return 0;
}
/* When casting float to int value is simply truncated:
* - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1
* (instead of 1.7 = 2, -1.7 = -2)
*
* Alts for more accurate rounding could be:
* - (int)floor(f32 * 32768.0) //not quite ok negatives
* - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style
* - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest.
*
* Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE
* float requirements, but C99 adds some faster-but-less-precise casting functions
* we try to use (returning "long", though). They work ok without "fast float math" compiler
* flags, but probably should be enabled anyway to ensure no extra IEEE checks are needed.
* MSVC added this in VS2015 (_MSC_VER 1900) but don't seem correctly optimized and is very slow.
*/
static inline int float_to_int(float val) {
#if defined(_MSC_VER)
return (int)val;
#else
return lrintf(val);
#endif
}
static inline int double_to_int(double val) {
#if defined(_MSC_VER)
return (int)val;
#else
return lrint(val); /* returns long tho */
#endif
return data->frame->nb_samples;
}
/* sample copy helpers, using different functions to minimize branches.
*
* in theory, small optimizations like *outbuf++ vs outbuf[i] or alt clamping
* would matter for performance, but in practice aren't very noticeable;
* keep it simple for now until more tests are done.
*
* in normal (interleaved) formats samples are laid out straight
* (ibuf[s*chs+ch], ex. 4ch with 4s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3)
* in "p" (planar) formats samples are in planes per channel
* (ibuf[ch][s], ex. 4ch with 4s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3)
*
* alt float clamping:
* clamp_float(f32)
* int s16 = (int)(f32 * 32768.0f);
* if ((unsigned)(s16 + 0x8000) & 0xFFFF0000)
* s16 = (s16 >> 31) ^ 0x7FFF;
*/
static void samples_silence_s16(sample_t* obuf, int ochs, int samples) {
int s, total_samples = samples * ochs;
for (s = 0; s < total_samples; s++) {
obuf[s] = 0; /* memset'd */
static void samples_u8_to_s16(int16_t* obuf, uint8_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ((int)ibuf[s] - 0x80) << 8;
}
}
static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = ((int)ibuf[skip*ichs + s] - 0x80) << 8;
}
}
static void samples_u8p_to_s16(sample_t* obuf, uint8_t** ibuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ((int)ibuf[ch][skip + s] - 0x80) << 8;
static void samples_u8p_to_s16(int16_t* obuf, uint8_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ((int)ibuf[ch][s] - 0x80) << 8;
}
}
}
static void samples_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */
static void samples_s16_to_s16(int16_t* obuf, int16_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s];
}
}
static void samples_s16p_to_s16(sample_t* obuf, int16_t** ibuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][skip + s];
static void samples_s16p_to_s16(int16_t* obuf, int16_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s];
}
}
}
static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = ibuf[skip*ichs + s] >> 16;
static void samples_s32_to_s32(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[ichs + s];
}
}
static void samples_s32p_to_s16(sample_t* obuf, int32_t** ibuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][skip + s] >> 16;
static void samples_s32p_to_s32(int32_t* obuf, int32_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s];
}
}
}
static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) {
int s, total_samples = samples * ichs;
float scale = invert ? -32768.0f : 32768.0f;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale));
static void samples_flt_to_flt(float* obuf, float* ibuf, int ichs, int samples, bool invert) {
float scale = invert ? -1.0f : 1.0;
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s] * scale;
}
}
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) {
int s, ch;
float scale = invert ? -32768.0f : 32768.0f;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = clamp16(float_to_int(ibuf[ch][skip + s] * scale));
}
}
}
static void samples_dbl_to_s16(sample_t* obuf, double* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(double_to_int(ibuf[skip*ichs + s] * 32768.0));
}
}
static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = clamp16(double_to_int(inbuf[ch][skip + s] * 32768.0));
static void samples_fltp_to_flt(float* obuf, float** ibuf, int ichs, int samples, bool invert) {
float scale = invert ? -1.0f : 1.0;
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s] * scale;
}
}
}
static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do, int max_channels) {
static void samples_s32_to_s24(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s] >> 8;
}
}
static void copy_samples(ffmpeg_codec_data* data, void* sbuf, int samples_to_do, int max_channels) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels;
#else
int channels = data->codecCtx->ch_layout.nb_channels;
#endif
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
void* ibuf;
void* ibuf;
if (is_planar) {
ibuf = data->frame->extended_data;
}
@ -786,81 +749,100 @@ static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_
}
switch (data->codecCtx->sample_fmt) {
/* unused? */
case AV_SAMPLE_FMT_U8P: if (is_planar) { samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_U8:
if (is_planar)
samples_u8p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_u8_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* common */
case AV_SAMPLE_FMT_S16P: if (is_planar) { samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S16:
if (is_planar)
samples_s16p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_s16_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* possibly FLAC and other lossless codecs */
case AV_SAMPLE_FMT_S32P: if (is_planar) { samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_S32P:
case AV_SAMPLE_FMT_S32:
if (is_planar)
samples_s32p_to_s32(sbuf, ibuf, channels, samples_to_do);
else
samples_s32_to_s32(sbuf, ibuf, channels, samples_to_do);
break;
/* mainly MDCT-like codecs (Ogg, AAC, etc) */
case AV_SAMPLE_FMT_FLTP: if (is_planar) { samples_fltp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break; }
// fall through
case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break;
/* possibly PCM64 only (not enabled) */
case AV_SAMPLE_FMT_DBLP: if (is_planar) { samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_FLTP:
case AV_SAMPLE_FMT_FLT:
if (is_planar)
samples_fltp_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
else
samples_flt_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
break;
default:
break;
}
if (data->channel_remap_set)
remap_audio(outbuf, samples_to_do, channels, data->channel_remap);
// FFmpeg can't do PCM24 and upsamples to PCM32, if needed
if (data->fmt == SFMT_S24) {
samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do);
}
}
/* 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;
decode_fail:
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do);
samples_silence_s16(outbuf, channels, samples_to_do);
switch(sbuf->fmt) {
case SFMT_FLT:
remap_audio_flt(sbuf->buf, sbuf->filled, sbuf->channels, data->channel_remap);
break;
default: // trivial to add but not used
VGM_LOG("FFMPEG: unsupported channel remapping found (implement)\n");
break;
}
}
// ensure output buffer can handle samples, since in theory it could change per call
static bool prepare_sbuf(ffmpeg_codec_data* data, int samples, int channels) {
if (!data->sbuf && data->sbuf_samples >= samples)
return true;
free(data->sbuf);
data->sbuf_samples = samples * 2;
data->sbuf = malloc(data->sbuf_samples * channels * sizeof(float));
if (!data->sbuf) return false;
return true;
}
bool decode_frame_ffmpeg(VGMSTREAM* v) {
decode_state_t* ds = v->decode_state;
ffmpeg_codec_data* data = v->codec_data;
int samples = decode_frame_internal(data);
if (samples < 0)
return false;
if (!prepare_sbuf(data, samples, v->channels))
return false;
copy_samples(data, data->sbuf, samples, v->channels);
sbuf_init(&ds->sbuf, data->fmt, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
remap_audio(data, &ds->sbuf);
if (data->samples_discard) {
ds->discard = data->samples_discard;
data->samples_discard = 0;
}
return true;
}
@ -868,11 +850,7 @@ decode_fail:
/* UTILS */
/* ******************************************** */
void reset_ffmpeg(ffmpeg_codec_data* data) {
seek_ffmpeg(data, 0);
}
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
if (!data) return;
/* Start from 0 and discard samples until sample (slower but not too noticeable).
@ -897,13 +875,11 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
avcodec_flush_buffers(data->codecCtx);
}
data->samples_consumed = 0;
data->samples_filled = 0;
data->samples_discard = num_sample;
data->read_packet = 1;
data->end_of_stream = 0;
data->end_of_audio = 0;
data->read_packet = true;
data->end_of_stream = false;
data->end_of_audio = false;
/* consider skip samples (encoder delay), if manually set */
if (data->skip_samples_set) {
@ -918,6 +894,15 @@ fail:
}
static void seek_ffmpeg(VGMSTREAM* v, int32_t num_sample) {
seek_ffmpeg_internal(v->codec_data, num_sample);
}
static void reset_ffmpeg(void* priv_data) {
seek_ffmpeg_internal(priv_data, 0);
}
static void free_ffmpeg_config(ffmpeg_codec_data* data) {
if (data == NULL)
return;
@ -946,10 +931,11 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) {
av_freep(&(data->buffer));
}
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option
//TODO: avformat_find_stream_info may cause some Win Handle leaks? related to certain option
}
void free_ffmpeg(ffmpeg_codec_data* data) {
void free_ffmpeg(void* priv_data) {
ffmpeg_codec_data* data = priv_data;
if (data == NULL)
return;
@ -1003,9 +989,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
#endif
/* set skip samples with our internal discard */
data->skip_samples_set = 1;
data->samples_discard = skip_samples;
data->skip_samples_set = true;
data->skip_samples = skip_samples;
data->samples_discard = skip_samples;
}
/* returns channel layout if set */
@ -1032,8 +1018,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
/* yet another hack to fix codecs that encode channels in different order and reorder on decoder
* but FFmpeg doesn't do it automatically
* (maybe should be done via mixing, but could clash with other stuff?) */
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
int i;
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels;
#else
@ -1043,10 +1028,12 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
if (channels > 32)
return;
for (i = 0; i < channels; i++) {
for (int i = 0; i < channels; i++) {
data->channel_remap[i] = channel_remap[i];
}
data->channel_remap_set = 1;
VGM_LOG("FFMPEG: channel remapping set\n");
data->channel_remap_set = true;
}
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
@ -1065,7 +1052,7 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
data->force_seek = 1;
data->force_seek = true;
reset_ffmpeg(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->stream_index];
}
@ -1073,7 +1060,16 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
if (!data)
return;
data->invert_floats_set = 1;
data->invert_floats_set = true;
}
// for flac 24-bit, since FFMpeg upsamples to PCM32 (ignored if flac 16-bit)
void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data) {
if (!data)
return;
if (data->fmt != SFMT_S32)
return;
data->fmt = SFMT_S24;
}
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
@ -1131,4 +1127,22 @@ STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
if (!data) return NULL;
return data->sf;
}
static sfmt_t get_sample_type_ffmpeg(VGMSTREAM* v) {
ffmpeg_codec_data* data = v->codec_data;
if (!data)
return SFMT_NONE;
return data->fmt;
}
const codec_info_t ffmpeg_decoder = {
.get_sample_type = get_sample_type_ffmpeg,
.decode_frame = decode_frame_ffmpeg,
.free = free_ffmpeg,
.reset = reset_ffmpeg,
.seek = seek_ffmpeg,
};
#endif

View file

@ -84,7 +84,7 @@ static bool read_frame(VGMSTREAM* v) {
return true;
}
bool decode_frame_ka1a(VGMSTREAM* v) {
static bool decode_frame_ka1a(VGMSTREAM* v) {
bool ok = read_frame(v);
if (!ok)
return false;

View file

@ -1,4 +1,5 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include "relic_lib.h"
@ -14,7 +15,6 @@
extern void relic_mixfft_fft(int n, float* xRe, float* xIm, float* yRe, float* yIm);
#define RELIC_MAX_CHANNELS 2
#define RELIC_MAX_SCALES 6
#define RELIC_BASE_SCALE 10.0f
#define RELIC_FREQUENCY_MASKING_FACTOR 1.0f
@ -61,10 +61,9 @@ static const int16_t critical_band_data[RELIC_CRITICAL_BAND_COUNT] = {
};
static void init_dct(float* dct, int dct_size) {
int i;
int dct_quarter = dct_size >> 2;
const int dct_quarter = dct_size >> 2;
for (i = 0; i < dct_quarter; i++) {
for (int i = 0; i < dct_quarter; i++) {
double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size);
dct[i] = sin(temp);
dct[dct_quarter + i] = cos(temp);
@ -72,19 +71,17 @@ static void init_dct(float* dct, int dct_size) {
}
static int apply_idct(const float* freq, float* wave, const float* dct, int dct_size) {
int i;
float factor;
float out_re[RELIC_MAX_FFT];
float out_im[RELIC_MAX_FFT];
float in_re[RELIC_MAX_FFT];
float in_im[RELIC_MAX_FFT];
float wave_tmp[RELIC_MAX_SIZE];
int dct_half = dct_size >> 1;
int dct_quarter = dct_size >> 2;
int dct_3quarter = 3 * (dct_size >> 2);
const int dct_half = dct_size >> 1;
const int dct_quarter = dct_size >> 2;
const int dct_3quarter = 3 * (dct_size >> 2);
/* prerotation? */
for (i = 0; i < dct_quarter; i++) {
for (int i = 0; i < dct_quarter; i++) {
float coef1 = freq[2 * i] * 0.5f;
float coef2 = freq[dct_half - 1 - 2 * i] * 0.5f;
in_re[i] = coef1 * dct[dct_quarter + i] + coef2 * dct[i];
@ -95,32 +92,31 @@ static int apply_idct(const float* freq, float* wave, const float* dct, int dct_
relic_mixfft_fft(dct_quarter, in_re, in_im, out_re, out_im);
/* postrotation, window and reorder? */
factor = 8.0 / sqrt(dct_size);
for (i = 0; i < dct_quarter; i++) {
float factor = 8.0 / sqrt(dct_size);
for (int i = 0; i < dct_quarter; i++) {
float out_re_i = out_re[i];
out_re[i] = (out_re[i] * dct[dct_quarter + i] + out_im[i] * dct[i]) * factor;
out_im[i] = (-out_re_i * dct[i] + out_im[i] * dct[dct_quarter + i]) * factor;
wave_tmp[i * 2] = out_re[i];
wave_tmp[i * 2 + dct_half] = out_im[i];
}
for (i = 1; i < dct_size; i += 2) {
for (int i = 1; i < dct_size; i += 2) {
wave_tmp[i] = -wave_tmp[dct_size - 1 - i];
}
/* wave mix thing? */
for (i = 0; i < dct_3quarter; i++) {
for (int i = 0; i < dct_3quarter; i++) {
wave[i] = wave_tmp[dct_quarter + i];
}
for (i = dct_3quarter; i < dct_size; i++) {
for (int i = dct_3quarter; i < dct_size; i++) {
wave[i] = -wave_tmp[i - dct_3quarter];
}
return 0;
}
static void decode_frame(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_size) {
int i;
float wave_tmp[RELIC_MAX_SIZE];
int dct_half = dct_size >> 1;
const int dct_half = dct_size >> 1;
/* copy for first half(?) */
memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float));
@ -130,22 +126,19 @@ static void decode_frame(const float* freq1, const float* freq2, float* wave_cur
apply_idct(freq2, wave_prv, dct, dct_size);
/* overlap and apply window function to filter this block's beginning */
for (i = 0; i < dct_half; i++) {
for (int i = 0; i < dct_half; i++) {
wave_cur[dct_half + i] = wave_tmp[i] * window[i] + wave_cur[dct_half + i] * window[dct_half + i];
wave_prv[i] = wave_prv[i] * window[i] + wave_tmp[dct_half + i] * window[dct_half + i];
}
}
static void init_window(float *window, int dct_size) {
int i;
for (i = 0; i < dct_size; i++) {
for (int i = 0; i < dct_size; i++) {
window[i] = sin((float)i * (RELIC_PI / dct_size));
}
}
static void decode_frame_base(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_mode, int samples_mode) {
int i;
float wave_tmp[RELIC_MAX_SIZE];
/* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */
@ -160,7 +153,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav
if (dct_mode == RELIC_SIZE_LOW) {
/* 128 DCT to 256 samples (repeat sample x2) */
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
for (i = 0; i < 256 - 1; i += 2) {
for (int i = 0; i < 256 - 1; i += 2) {
wave_cur[i + 0] = wave_tmp[i >> 1];
wave_cur[i + 1] = wave_tmp[i >> 1];
}
@ -174,7 +167,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav
if (dct_mode == RELIC_SIZE_LOW) {
/* 128 DCT to 512 samples (repeat sample x4) */
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
for (i = 0; i < 512 - 1; i += 4) {
for (int i = 0; i < 512 - 1; i += 4) {
wave_cur[i + 0] = wave_tmp[i >> 2];
wave_cur[i + 1] = wave_tmp[i >> 2];
wave_cur[i + 2] = wave_tmp[i >> 2];
@ -184,7 +177,7 @@ static void decode_frame_base(const float* freq1, const float* freq2, float* wav
else if (dct_mode == RELIC_SIZE_MID) {
/* 256 DCT to 512 samples (repeat sample x2) */
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID);
for (i = 0; i < 512 - 1; i += 2) {
for (int i = 0; i < 512 - 1; i += 2) {
wave_cur[i + 0] = wave_tmp[i >> 1];
wave_cur[i + 1] = wave_tmp[i >> 1];
}
@ -225,25 +218,22 @@ static int read_sbits(uint8_t bits, uint32_t offset, uint8_t* buf) {
}
static void init_dequantization(float* scales) {
int i;
scales[0] = RELIC_BASE_SCALE;
for (i = 1; i < RELIC_MAX_SCALES; i++) {
for (int i = 1; i < RELIC_MAX_SCALES; i++) {
scales[i] = scales[i - 1] * scales[0];
}
for (i = 0; i < RELIC_MAX_SCALES; i++) {
for (int i = 0; i < RELIC_MAX_SCALES; i++) {
scales[i] = RELIC_FREQUENCY_MASKING_FACTOR / (double) ((1 << (i + 1)) - 1) * scales[i];
}
}
static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) {
static bool unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) {
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
int qv, pos;
int qv;
uint8_t ev;
uint8_t move;
uint32_t bit_offset, max_offset;
int i, j;
int freq_half = freq_size >> 1;
const int freq_half = freq_size >> 1;
memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float));
@ -263,8 +253,8 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2,
/* read packed exponents indexes for all bands */
if (cb_bits > 0 && ev_bits > 0) {
pos = 0;
for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
int pos = 0;
for (int i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
if (bit_offset + cb_bits > max_offset)
goto fail;
move = read_ubits(cb_bits, bit_offset, buf);
@ -281,7 +271,7 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2,
if (pos + 1 >= sizeof(critical_band_data))
goto fail;
for (j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++) {
for (int j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++) {
exponents[j] = ev;
}
}
@ -291,8 +281,8 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2,
if (freq_half > 0 && ei_bits > 0) {
/* read first part */
pos = 0;
for (i = 0; i < RELIC_MAX_FREQ; i++) {
int pos = 0;
for (int i = 0; i < RELIC_MAX_FREQ; i++) {
if (bit_offset + ei_bits > max_offset)
goto fail;
move = read_ubits(ei_bits, bit_offset, buf);
@ -322,7 +312,7 @@ static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2,
}
else {
pos = 0;
for (i = 0; i < RELIC_MAX_FREQ; i++) {
for (int i = 0; i < RELIC_MAX_FREQ; i++) {
if (bit_offset + ei_bits > max_offset)
goto fail;
move = read_ubits(ei_bits, bit_offset, buf);
@ -413,12 +403,11 @@ int relic_get_frame_size(relic_handle_t* handle) {
}
int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel) {
int ok;
/* clean extra bytes for bitreader (due to a quirk in the original code it may read outside max frame size) */
memset(buf + handle->frame_size, 0, RELIC_BUFFER_SIZE - handle->frame_size);
ok = unpack_frame(buf, RELIC_BUFFER_SIZE, handle->freq1, handle->freq2, handle->scales, handle->exponents[channel], handle->freq_size);
bool ok = unpack_frame(buf, RELIC_BUFFER_SIZE, handle->freq1, handle->freq2, handle->scales, handle->exponents[channel], handle->freq_size);
if (!ok) return ok;
decode_frame_base(handle->freq1, handle->freq2, handle->wave_cur[channel], handle->wave_prv[channel], handle->dct, handle->window, handle->dct_mode, handle->samples_mode);
@ -432,13 +421,12 @@ static inline int clamp16(int32_t val) {
else return val;
}
void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip) {
int s, ch;
void relic_get_pcm16(relic_handle_t* handle, int16_t* sbuf) {
int ichs = handle->channels;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
double d64_sample = handle->wave_cur[ch][skip + s];
for (int s = 0; s < RELIC_SAMPLES_PER_FRAME; s++) {
for (int ch = 0; ch < ichs; ch++) {
double d64_sample = handle->wave_cur[ch][s];
int pcm_sample = clamp16((int32_t)d64_sample);
/* f32 in PCM 32767.0 .. -32768.0 format, original code
@ -446,7 +434,17 @@ void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, i
//FQ_BNUM ((float)(1<<26)*(1<<26)*1.5)
//rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64))
outbuf[s*ichs + ch] = pcm_sample;
sbuf[s*ichs + ch] = pcm_sample;
}
}
}
// original lib always converts to pcm16, this is just a freebie
void relic_get_float(relic_handle_t* handle, float* sbuf) {
int pos = 0;
for (int s = 0; s < RELIC_SAMPLES_PER_FRAME; s++) {
for (int ch = 0; ch < handle->channels; ch++) {
sbuf[pos++] = handle->wave_cur[ch][s];
}
}
}

View file

@ -5,6 +5,7 @@
#define RELIC_BUFFER_SIZE 0x104
#define RELIC_SAMPLES_PER_FRAME 512
#define RELIC_MAX_CHANNELS 2
typedef struct relic_handle_t relic_handle_t;
@ -18,6 +19,8 @@ int relic_get_frame_size(relic_handle_t* handle);
int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel);
void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip);
void relic_get_pcm16(relic_handle_t* handle, int16_t* sbuf);
void relic_get_float(relic_handle_t* handle, float* sbuf);
#endif

View 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;
}

View file

@ -18,8 +18,8 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
data->bitrate_per_frame = info.bit_rate;
data->sample_rate_per_frame = info.sample_rate;
data->bitrate = info.bit_rate;
data->sample_rate = info.sample_rate;
/* extra checks per type */
@ -134,7 +134,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */
current_interleave = data->config.interleave; /* big interleave */
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre;
current_data_size = data->config.chunk_size;
break;
@ -142,7 +142,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
case MPEG_FSB: /* frames with padding + interleave */
current_interleave = data->config.interleave; /* constant for multi-stream FSbs (1 frame + padding) */
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre;
if (stream->offset >= data->config.data_size) {
VGM_LOG_ONCE("MPEG: fsb overread\n");
@ -182,8 +182,8 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
}
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_size, (uint32_t)stream->offset);
VGM_ASSERT(data->streams_count > 1 && current_interleave != current_data_size+current_padding,
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_count, (uint32_t)stream->offset);
break;
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
@ -193,14 +193,14 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
/* check if current interleave block is short */
{
off_t block_offset = stream->offset - stream->channel_start_offset;
size_t next_block = data->streams_size*data->config.interleave;
size_t next_block = data->streams_count * data->config.interleave;
if (data->config.data_size && block_offset + next_block >= data->config.data_size)
current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/
current_interleave = (data->config.data_size % next_block) / data->streams_count; /* short_interleave*/
}
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre;
current_data_size = current_interleave;
break;
@ -215,7 +215,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
return false;
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
current_interleave_post = current_interleave*(data->streams_count-1) - current_interleave_pre;
//VGM_LOG("o=%lx, %i: %x, %x, %x, %x\n", stream->offset, num_stream, ms->current_size_count, current_interleave, current_interleave_pre, current_interleave_post );
current_data_size = current_interleave;
@ -256,261 +256,3 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
return true;
}
#endif
//TODO: move to a better place
/*****************/
/* FRAME HELPERS */
/*****************/
/**
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow
* it's wrong at times (maybe because we use an ancient version) so here we do our thing.
*/
bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) {
/* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int layers[4] = { -1,3,2,1 };
static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */
};
static const int sample_rates[4][4] = { /* [version][sample rate index] */
{ 44100, 48000, 32000, -1}, /* MPEG1 */
{ 22050, 24000, 16000, -1}, /* MPEG2 */
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
};
static const int channels[4] = { 2,2,2, 1 }; /* [channel] */
static const int frame_samples[3][3] = { /* [version][layer] */
{ 384, 1152, 1152 }, /* MPEG1 */
{ 384, 1152, 576 }, /* MPEG2 */
{ 384, 1152, 576 } /* MPEG2.5 */
};
int idx, padding;
memset(info, 0, sizeof(*info));
if ((header >> 21) != 0x7FF) /* 31-21: sync */
goto fail;
info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */
if (info->version <= 0) goto fail;
info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */
if (info->layer <= 0 || info->layer > 3) goto fail;
//crc = (header >> 16) & 0x1; /* 16: protected by crc? */
idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1)));
info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */
if (info->bit_rate <= 0) goto fail;
info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */
if (info->sample_rate <= 0) goto fail;
padding = (header >> 9) & 0x1; /* 9: padding? */
//private = (header >> 8) & 0x1; /* 8: private bit */
info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */
//js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */
//copyright = (header >> 3) & 0x1; /* 3: copyrighted */
//original = (header >> 2) & 0x1; /* 2: original */
//emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */
info->frame_samples = frame_samples[info->version-1][info->layer-1];
/* calculate frame length (from hcs's fsb_mpeg) */
switch (info->frame_samples) {
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */
default: goto fail;
}
return true;
fail:
return false;
}
bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) {
uint32_t header = read_u32be(offset, sf);
return mpeg_get_frame_info_h(header, info);
}
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header) {
if (!header)
header = read_u32be(offset+0x00, sf);
/* skip ID3v2 */
if ((header & 0xFFFFFF00) == get_id32be("ID3\0")) {
size_t frame_size = 0;
uint8_t flags = read_u8(offset+0x05, sf);
/* this is how it's officially read :/ */
frame_size += read_u8(offset+0x06, sf) << 21;
frame_size += read_u8(offset+0x07, sf) << 14;
frame_size += read_u8(offset+0x08, sf) << 7;
frame_size += read_u8(offset+0x09, sf) << 0;
frame_size += 0x0a;
if (flags & 0x10) /* footer? */
frame_size += 0x0a;
return frame_size;
}
/* skip ID3v1 */
if ((header & 0xFFFFFF00) == get_id32be("TAG\0")) {
;VGM_LOG("MPEG: ID3v1 at %x\n", offset);
return 0x80;
}
return 0;
}
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
off_t offset = start_offset;
off_t max_offset = start_offset + bytes;
int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0;
mpeg_frame_info info;
if (!sf)
return 0;
if (max_offset > get_streamfile_size(sf))
max_offset = get_streamfile_size(sf);
/* MPEG may use VBR so must read all frames */
while (offset < max_offset) {
uint32_t header = read_u32be(offset+0x00, sf);
size_t tag_size = mpeg_get_tag_size(sf, offset, header);
if (tag_size) {
offset += tag_size;
continue;
}
/* regular frame (assumed) */
if (!mpeg_get_frame_info_h(header, &info)) {
VGM_LOG("MPEG: unknown frame at %lx\n", offset);
break;
}
/* detect Xing header (disguised as a normal frame) */
if (frames < 3) { /* should be first after tags */
/* frame is empty so Xing goes after MPEG side info */
off_t xing_offset;
if (info.version == 1)
xing_offset = (info.channels == 2 ? 0x20 : 0x11) + 0x04;
else
xing_offset = (info.channels == 2 ? 0x11 : 0x09) + 0x04;
if (info.frame_size >= xing_offset + 0x78 &&
read_u32be(offset + 0x04, sf) == 0 && /* empty frame */
(read_u32be(offset + xing_offset, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */
read_u32be(offset + xing_offset, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */
uint32_t flags = read_u32be(offset + xing_offset + 0x04, sf);
if (flags & 1) {
uint32_t frame_count = read_u32be(offset + xing_offset + 0x08, sf);
samples = frame_count * info.frame_samples;
}
/* other flags indicate seek table and stuff */
;VGM_LOG("MPEG: found Xing header\n");
/* vendor specific */
if (info.frame_size > xing_offset + 0x78 + 0x24) {
uint32_t sub_id = read_u32be(offset + xing_offset + 0x78, sf);
if (sub_id == get_id32be("LAME") || /* LAME */
sub_id == get_id32be("Lavc")) { /* FFmpeg */
if (info.layer == 3) {
uint32_t delays = read_u32be(offset + xing_offset + 0x8C, sf);
encoder_delay = ((delays >> 12) & 0xFFF);
encoder_padding = ((delays >> 0) & 0xFFF);
encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */
if (encoder_padding > 528 + 1)
encoder_padding -= (528 + 1);
}
else {
encoder_delay = 240 + 1;
}
}
/* replay gain and stuff */
}
/* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */
break; /* we got samples */
}
}
//TODO: detect "VBRI" header (Fraunhofer encoder)
// https://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#VBRIHeader
/* could detect VBR/CBR but read frames to remove ID3 end tags */
frames++;
offset += info.frame_size;
samples += info.frame_samples;
}
;VGM_LOG("MPEG: samples=%i, ed=%i, ep=%i, end=%i\n", samples,encoder_delay,encoder_padding, samples - encoder_delay - encoder_padding);
//todo return encoder delay
samples = samples - encoder_delay - encoder_padding;
return samples;
}
/* variation of the above, for clean streams = no ID3/VBR headers
* (maybe should be fused in a single thing with config, API is kinda messy too) */
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr) {
mpeg_frame_info info;
off_t offset = start;
int32_t num_samples = 0, loop_start = 0, loop_end = 0;
if (!is_vbr) {
/* CBR = quick calcs */
if (!mpeg_get_frame_info(sf, offset, &info))
goto fail;
num_samples = size / info.frame_size * info.frame_samples;
if (p_loop_start)
loop_start = *p_loop_start / info.frame_size * info.frame_samples;
if (p_loop_end)
loop_end = *p_loop_end / info.frame_size * info.frame_samples;
}
else {
/* VBR (or unknown) = count frames */
while (offset < start + size) {
if (!mpeg_get_frame_info(sf, offset, &info))
goto fail;
if (p_loop_start && *p_loop_start + start == offset)
loop_start = num_samples;
num_samples += info.frame_samples;
offset += info.frame_size;
if (p_loop_end && *p_loop_end + start == offset)
loop_end = num_samples;
}
}
if (p_loop_start)
*p_loop_start = loop_start;
if (p_loop_end)
*p_loop_end = loop_end;
return num_samples;
fail:
VGM_LOG("MPEG: sample reader failed at %lx\n", offset);
return 0;
}

View file

@ -189,11 +189,11 @@ fail:
#define AHX_KEY_BUFFER 0x2000
#define AHX_KEY_TEST_FRAMES 20 /* wrong keys may work ok in some frames */
#define AHX_KEY_TEST_FRAMES 25 /* wrong keys may work ok in some frames */
/* check if current key ends properly in frame syncs */
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
int bytes;
bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
int bytes = 0;
uint8_t buf[AHX_KEY_BUFFER];
const int buf_size = sizeof(buf);
int pos = 0;

View file

@ -640,11 +640,8 @@ fail:
return 0;
}
static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) {
int i, ch;
static void ealayer3_copy_pcm_block(float* sbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) {
uint8_t pcm_block[1152 * 2 * 2]; /* assumed max: 1 MPEG frame samples * 16b * max channels */
size_t pcm_size = pcm_number * 2 * channels_per_frame;
size_t bytes;
if (pcm_number == 0)
return;
@ -654,7 +651,8 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
return;
}
bytes = read_streamfile(pcm_block, pcm_offset, pcm_size, sf);
size_t pcm_size = pcm_number * 2 * channels_per_frame;
int bytes = read_streamfile(pcm_block, pcm_offset, pcm_size, sf);
if (bytes != pcm_size) {
VGM_LOG("EAL3: incorrect pcm_number %i at %lx\n", pcm_number, pcm_offset);
return;
@ -666,24 +664,24 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
if (is_packed) {
/* ch0+ch1 packed together */
int pos = 0;
for (i = 0; i < pcm_number * channels_per_frame; i++) {
for (int i = 0; i < pcm_number * channels_per_frame; i++) {
int16_t pcm_sample = get_s16be(pcm_block + pos);
put_s16le(outbuf + pos, pcm_sample);
sbuf[i] = pcm_sample / 32767.0f;
pos += sizeof(sample_t);
pos += 0x02;
}
}
else {
/* all of ch0 first, then all of ch1 (EAL3 v1b only) */
int get_pos = 0;
for (ch = 0; ch < channels_per_frame; ch++) {
int put_pos = sizeof(sample_t) * ch;
for (i = 0; i < pcm_number; i++) {
int16_t pcm_sample = get_s16be(pcm_block + get_pos);
put_s16le(outbuf + put_pos, pcm_sample);
int pos = 0;
for (int ch = 0; ch < channels_per_frame; ch++) {
int s = ch;
for (int i = 0; i < pcm_number; i++) {
int16_t pcm_sample = get_s16be(pcm_block + pos);
sbuf[s] = pcm_sample / 32767.0f;
get_pos += sizeof(sample_t);
put_pos += sizeof(sample_t) * channels_per_frame;
pos += 0x02;
s += channels_per_frame;
}
}
}
@ -694,10 +692,11 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, ealayer3_frame_t* eaf) {
mpeg_custom_stream* ms = &data->streams[num_stream];
int channels_per_frame = ms->channels_per_frame;
size_t bytes_filled;
float* sbuf = ms->sbuf;
sbuf += ms->samples_filled * channels_per_frame;
bytes_filled = sizeof(sample_t) * ms->samples_filled * channels_per_frame;
int bytes_filled = sizeof(float) * ms->samples_filled * channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->sbuf_size) {
VGM_LOG("EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
@ -709,11 +708,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
}
if (eaf->v1_pcm_samples || eaf->v1_offset_samples) {
uint8_t* outbuf = ms->sbuf;
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size;
size_t decode_to_discard;
outbuf += bytes_filled;
VGM_ASSERT(eaf->v1_offset_samples > 576, "EAL3: big discard %i at 0x%x\n", eaf->v1_offset_samples, (uint32_t)stream->offset);
VGM_ASSERT(eaf->v1_pcm_samples > 0x100, "EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_samples, (uint32_t)stream->offset);
@ -723,7 +718,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
// stream->offset, eaf->pre_size + eaf->common_size, eaf->v1_offset_samples, eaf->v1_pcm_samples, data->samples_per_frame);
/* V1b PCM block is in 'planar' format (ex. NFS:U PS3) */
ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile);
ealayer3_copy_pcm_block(sbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile);
ms->samples_filled += eaf->v1_pcm_samples;
//TODO: we should put samples at offset but most EAL3 use it at first frame, which decodes ok, and rarely
@ -739,20 +734,16 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
*
* This behaviour matters most in looping sfx (ex. Burnout Paradise), or tracks that start
* without silence (ex. NFS:UG2), and NFS:UC PS3 (EAL3v1b) vs PC (EAXAS) gets correct waveform this way */
decode_to_discard = eaf->v1_pcm_samples;
int decode_to_discard = eaf->v1_pcm_samples;
ms->decode_to_discard += decode_to_discard;
}
if (eaf->v2_extended_flag) {
uint8_t* outbuf = ms->sbuf;
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size;
size_t usable_samples, decode_to_discard;
outbuf += bytes_filled;
/* V2P usually only copies big PCM, while V2S discards then copies few samples (similar to V1b).
* Unlike V1b, both modes seem to use 'packed' PCM block */
ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v2_pcm_samples, channels_per_frame, 1, stream->streamfile);
ealayer3_copy_pcm_block(sbuf, pcm_offset, eaf->v2_pcm_samples, channels_per_frame, 1, stream->streamfile);
ms->samples_filled += eaf->v2_pcm_samples;
//;VGM_LOG("EA EAL3 v2: off=%lx, mode=%x, value=%i, pcm=%i, c-size=%x, pcm_o=%lx\n",
@ -761,6 +752,8 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
//todo improve how discarding works since there could exists a subtle-but-unlikely PCM+granule usage
//todo test other modes (only seen IGNORE)
int usable_samples, decode_to_discard;
/* get how many samples we can use in this granule + pcm block (thus how many we can't) */
if (eaf->v2_offset_mode == 0x00) { /* IGNORE (looks correct in V2P loops, ex. NFS:W) */
/* offset_samples is usually 0 in V2P (no discard), varies in V2S and may be 576 (full discard).
@ -829,7 +822,7 @@ static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, i
int ok, i;
ealayer3_buffer_t ib = {0};
ealayer3_frame_t eaf;
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
int skips = at_start ? num_stream : data->streams_count - 1 - num_stream;
/* v1 does multichannel with set offsets */
if (data->type == MPEG_EAL31)

View file

@ -40,8 +40,8 @@ int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_
*coding_type = coding_MPEG_layer3;
data->channels_per_frame = data->config.channels;
data->samples_per_frame = 1152;
data->bitrate_per_frame = 320;
data->sample_rate_per_frame = 48000;
data->bitrate = 320;
data->sample_rate = 48000;
}
else {
mpeg_frame_info info;
@ -55,8 +55,8 @@ int mpeg_custom_setup_init_eamp3(STREAMFILE* sf, off_t start_offset, mpeg_codec_
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
data->bitrate_per_frame = info.bit_rate;
data->sample_rate_per_frame = info.sample_rate;
data->bitrate = info.bit_rate;
data->sample_rate = info.sample_rate;
}
@ -124,27 +124,27 @@ fail:
}
/* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */
static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf) {
static int eamp3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, eamp3_frame_info* eaf) {
mpeg_custom_stream* ms = &data->streams[num_stream];
size_t bytes_filled;
int i;
float* sbuf = ms->sbuf;
sbuf += ms->samples_filled * data->channels_per_frame;
bytes_filled = sizeof(sample_t) * ms->samples_filled * data->channels_per_frame;
int bytes_filled = sizeof(float) * ms->samples_filled * data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->sbuf_size) {
VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
}
if (eaf->pcm_number) {
/* read + write PCM block samples (always LE) */
for (i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample_t)*i;
int16_t pcm_sample = read_s16le(pcm_offset,stream->streamfile);
uint8_t* sbuf = ms->sbuf;
put_s16le(sbuf + bytes_filled + sizeof(sample_t) * i, pcm_sample);
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size;
for (int i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) {
int16_t pcm_sample = read_s16le(pcm_offset, stream->streamfile);
sbuf[i] = pcm_sample / 32767.0f;
pcm_offset += 0x02;
}
ms->samples_filled += eaf->pcm_number;
@ -161,14 +161,12 @@ fail:
}
/* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */
static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) {
int ok, i;
static int eamp3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start) {
eamp3_frame_info eaf = {0};
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
int skips = at_start ? num_stream : data->streams_count - 1 - num_stream;
for (i = 0; i < skips; i++) {
ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf);
for (int i = 0; i < skips; i++) {
int ok = eamp3_parse_frame(stream->streamfile, stream->offset, data, &eaf);
if (!ok) goto fail;
//;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset);

View file

@ -1,17 +1,67 @@
#include "coding.h"
#include "../util.h"
#include "../vgmstream.h"
#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"
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) */
#define MPEG_DATA_BUFFER_SIZE 0x1000 // at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate)
#define MPEG_MAX_CHANNELS 16 // arbitrary max
static mpg123_handle* init_mpg123_handle(void);
static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data* data, int num_stream);
static void free_mpeg(void* priv_data) {
mpeg_codec_data* data = priv_data;
if (!data)
return;
if (!data->custom) {
mpg123_delete(data->handle);
}
else {
for (int i = 0; i < data->streams_count; i++) {
if (!data->streams)
continue;
mpg123_delete(data->streams[i].handle);
free(data->streams[i].buffer);
free(data->streams[i].sbuf);
}
free(data->streams);
}
free(data->buffer);
free(data->sbuf);
free(data);
}
static mpg123_handle* init_mpg123_handle(void) {
mpg123_handle* handle = NULL;
int rc;
// in old versions it was needed to call mpg123_init()
if (MPG123_API_VERSION <= 46)
goto fail;
handle = mpg123_new(NULL, &rc);
if (rc != MPG123_OK) goto fail;
mpg123_param(handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0);
mpg123_param(handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.0); // wonky support
mpg123_param(handle, MPG123_RESYNC_LIMIT, -1, 0x2000); // just in case, games shouldn't need this
#ifndef VGM_DEBUG_OUTPUT
mpg123_param(handle, MPG123_ADD_FLAGS, MPG123_QUIET, 1);
#endif
rc = mpg123_open_feed(handle);
if (rc != MPG123_OK) goto fail;
return handle;
fail:
mpg123_delete(handle);
return NULL;
}
/* Inits regular MPEG */
@ -26,22 +76,21 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
data->buffer = calloc(data->buffer_size, sizeof(uint8_t));
if (!data->buffer) goto fail;
data->m = init_mpg123_handle();
if (!data->m) goto fail;
data->handle = init_mpg123_handle();
if (!data->handle) goto fail;
/* check format */
{
int rc, pos, bytes_read;
size_t bytes_done;
int bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf);
// don't check max as sfx can be smaller than buffer
bytes_read = read_streamfile(data->buffer, start_offset, data->buffer_size, sf);
/* don't check max as sfx can be smaller than buffer */
/* start_offset should be correct but just in case, read first frame(s) */
pos = 0;
// start_offset should be correct but just in case, read first frame(s)
int rc;
int pos = 0;
do {
rc = mpg123_decode(data->m, data->buffer + pos, bytes_read, NULL,0, &bytes_done);
size_t bytes_done;
rc = mpg123_decode(data->handle, data->buffer + pos, bytes_read, NULL,0, &bytes_done);
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
VGM_LOG("MPEG: unable to set up mpg123 at start offset\n");
goto fail; //handle MPG123_DONE?
@ -53,23 +102,23 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
pos++;
bytes_read--;
} while (rc != MPG123_NEW_FORMAT);
}
while (rc != MPG123_NEW_FORMAT);
}
{
size_t samples_per_frame;
int samples_per_frame;
long sample_rate_per_frame;
int channels_per_frame, encoding;
int rc;
struct mpg123_frameinfo mi;
struct mpg123_frameinfo mi = {0};
/* check first frame header and validate */
rc = mpg123_getformat(data->m, &sample_rate_per_frame, &channels_per_frame, &encoding);
int rc = mpg123_getformat(data->handle, &sample_rate_per_frame, &channels_per_frame, &encoding);
if (rc != MPG123_OK) goto fail;
mpg123_info(data->m, &mi);
mpg123_info(data->handle, &mi);
if (encoding != MPG123_ENC_SIGNED_16)
if (encoding != MPG123_ENC_FLOAT_32)
goto fail;
if (sample_rate_per_frame != mi.rate)
goto fail;
@ -96,14 +145,20 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
data->channels_per_frame = channels_per_frame;
data->samples_per_frame = samples_per_frame;
if (!data->sample_rate)
data->sample_rate = mi.rate;
if (!data->bitrate)
data->bitrate = mi.bitrate;
data->is_vbr = mi.vbr != MPG123_CBR;
/* copy current as open_feed may invalidate until data is fed */
memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo));
/* reinit, to ignore the reading done */
mpg123_open_feed(data->m);
// reinit, to ignore the reading done
mpg123_open_feed(data->handle);
}
data->sbuf_size = sizeof(float) * channels * data->samples_per_frame;
data->sbuf = calloc(data->sbuf_size, sizeof(uint8_t));
if (!data->sbuf) goto fail;
return data;
fail:
@ -142,8 +197,11 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
if (!ok)
goto fail;
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
if (channels < data->channels_per_frame) goto fail;
if (channels < 1 || channels > MPEG_MAX_CHANNELS)
goto fail;
if (channels < data->channels_per_frame)
goto fail;
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
/* max for some Ubi Lyn */
if (data->default_buffer_size > 0x20000) {
@ -153,24 +211,24 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
/* init streams */
data->streams_size = channels / data->channels_per_frame;
data->streams_count = channels / data->channels_per_frame;
/* 2ch streams + odd channels = last stream must be 1ch */
/* (known channels combos are 2ch+..+2ch, 1ch+..+1ch, or rarely 2ch+..+2ch+1ch in EALayer3) */
if (data->channels_per_frame == 2 && channels % 2)
data->streams_size += 1;
data->streams_count += 1;
data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream));
data->streams = calloc(data->streams_count, sizeof(mpeg_custom_stream));
if (!data->streams) goto fail;
for (int i = 0; i < data->streams_size; i++) {
for (int i = 0; i < data->streams_count; i++) {
//data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
//if (!data->streams[i]) goto fail;
data->streams[i].handle = init_mpg123_handle(); /* decoder not shared as frames depend on prev state */
if (!data->streams[i].handle) goto fail;
/* size could be any value */
data->streams[i].sbuf_size = sizeof(sample_t) * data->channels_per_frame * data->samples_per_frame;
data->streams[i].sbuf_size = sizeof(float) * data->channels_per_frame * data->samples_per_frame;
data->streams[i].sbuf = calloc(data->streams[i].sbuf_size, sizeof(uint8_t));
if (!data->streams[i].sbuf) goto fail;
@ -180,10 +238,14 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
if (!data->streams[i].buffer) goto fail;
data->streams[i].channels_per_frame = data->channels_per_frame;
if (i + 1 == data->streams_size && data->channels_per_frame == 2 && channels % 2)
if (i + 1 == data->streams_count && data->channels_per_frame == 2 && channels % 2)
data->streams[i].channels_per_frame = 1;
}
data->sbuf_size = sizeof(float) * channels * data->samples_per_frame;
data->sbuf = calloc(data->sbuf_size, sizeof(uint8_t));
if (!data->sbuf) goto fail;
return data;
fail:
@ -192,60 +254,15 @@ fail:
}
static mpg123_handle* init_mpg123_handle(void) {
mpg123_handle* m = NULL;
int rc;
/* inits a new mpg123 handle */
m = mpg123_new(NULL, &rc);
if (rc == MPG123_NOT_INITIALIZED) {
/* inits the library if needed */
if (mpg123_init() != MPG123_OK)
goto fail;
m = mpg123_new(NULL,&rc);
if (rc != MPG123_OK) goto fail;
}
else if (rc != MPG123_OK) {
goto fail;
}
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */
mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x2000); /* just in case, games shouldn't ever need this */
#ifndef VGM_DEBUG_OUTPUT
mpg123_param(m, MPG123_ADD_FLAGS, MPG123_QUIET, 1);
#endif
if (mpg123_open_feed(m) != MPG123_OK) {
goto fail;
}
return m;
fail:
mpg123_delete(m);
return NULL;
}
/************/
/* DECODERS */
/************/
void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
mpeg_codec_data* data = vgmstream->codec_data;
if (!data->custom) {
decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
} else {
decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels);
}
}
/**
* Decode anything mpg123 can.
* Feeds raw data and extracts decoded samples as needed.
*/
static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, float* sbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
while (samples_done < samples_to_do) {
@ -259,7 +276,7 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data
/* end of stream, fill rest with 0s */
if (data->bytes_in_buffer <= 0) {
VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample_t));
memset(sbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(float));
break;
}
@ -269,15 +286,15 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data
stream->offset += data->bytes_in_buffer;
}
bytes_to_do = (samples_to_do-samples_done) * channels * sizeof(sample_t);
bytes_to_do = (samples_to_do-samples_done) * channels * sizeof(float);
/* feed new raw data to the decoder if needed, copy decoded results to output */
if (!data->buffer_used) {
rc = mpg123_decode(data->m, data->buffer, data->bytes_in_buffer, outbuf, bytes_to_do, &bytes_done);
rc = mpg123_decode(data->handle, data->buffer, data->bytes_in_buffer, sbuf, bytes_to_do, &bytes_done);
data->buffer_used = true;
}
else {
rc = mpg123_decode(data->m, NULL,0, outbuf, bytes_to_do, &bytes_done);
rc = mpg123_decode(data->handle, NULL, 0, sbuf, bytes_to_do, &bytes_done);
}
/* not enough raw data, request more */
@ -287,92 +304,12 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data
VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc);
/* update copied samples */
samples_done += bytes_done / sizeof(sample_t) / channels;
outbuf += bytes_done / sizeof(sample_t);
samples_done += bytes_done / sizeof(float) / channels;
sbuf += bytes_done / sizeof(float);
}
}
/**
* Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc.
*
* Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode.
. Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space.
*/
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
while (samples_done < samples_to_do) {
int samples_to_copy = -1;
/* find max to copy from all streams (equal for all channels) */
for (int i = 0; i < data->streams_size; i++) {
size_t samples_in_stream = data->streams[i].samples_filled - data->streams[i].samples_used;
if (samples_to_copy < 0 || samples_in_stream < samples_to_copy)
samples_to_copy = samples_in_stream;
}
/* discard if needed (for looping) */
if (data->samples_to_discard) {
int samples_to_discard = samples_to_copy;
if (samples_to_discard > data->samples_to_discard)
samples_to_discard = data->samples_to_discard;
for (int i = 0; i < data->streams_size; i++) {
data->streams[i].samples_used += samples_to_discard;
}
data->samples_to_discard -= samples_to_discard;
samples_to_copy -= samples_to_discard;
}
/* mux streams channels (1/2ch combos) to outbuf (Nch) */
if (samples_to_copy > 0) {
if (samples_to_copy > samples_to_do - samples_done)
samples_to_copy = samples_to_do - samples_done;
int ch = 0;
for (int stream = 0; stream < data->streams_size; stream++) {
mpeg_custom_stream* ms = &data->streams[stream];
sample_t* sbuf = ms->sbuf;
int stream_channels = ms->channels_per_frame;
for (int stream_ch = 0; stream_ch < stream_channels; stream_ch++) {
for (int s = 0; s < samples_to_copy; s++) {
size_t stream_sample = (ms->samples_used+s)*stream_channels + stream_ch;
size_t buffer_sample = (samples_done+s)*channels + ch;
outbuf[buffer_sample] = sbuf[stream_sample];
}
ch++;
}
ms->samples_used += samples_to_copy;
}
samples_done += samples_to_copy;
}
else {
/* decode more into stream sample buffers */
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
* With multiple offsets they should already start in the first frame of each stream. */
for (int i = 0; i < data->streams_size; i++) {
switch(data->type) {
//case MPEG_FSB:
/* same offset: alternate frames between streams (maybe needed for weird layouts?) */
//decode_mpeg_custom_stream(&vgmstream->ch[0], data, i);
default:
/* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */
decode_mpeg_custom_stream(&vgmstream->ch[i], data, i);
break;
}
}
}
}
}
/* Decodes frames from a stream into the stream's sample buffer, feeding mpg123 buffer data.
* If not enough data to decode (as N data-frames = 1 full-frame) this will exit but be called again. */
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) {
@ -381,7 +318,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data*
int rc, ok;
mpeg_custom_stream* ms = &data->streams[num_stream];
int channels_per_frame = ms->channels_per_frame;
uint8_t* sbuf = ms->sbuf;
float* sbuf = ms->sbuf;
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i, buffer_full=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used, ms->buffer_full);
@ -426,32 +363,26 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data*
}
}
bytes_filled = sizeof(sample_t) * ms->samples_filled * channels_per_frame;
sbuf += ms->samples_filled * channels_per_frame;
bytes_filled = sizeof(float) * ms->samples_filled * channels_per_frame;
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!ms->buffer_used) {
//;VGM_LOG("MPEG: feed new data and get samples\n");
rc = mpg123_decode(ms->handle,
ms->buffer, ms->bytes_in_buffer,
sbuf + bytes_filled, ms->sbuf_size - bytes_filled,
&bytes_done);
rc = mpg123_decode(ms->handle, ms->buffer, ms->bytes_in_buffer, sbuf, ms->sbuf_size - bytes_filled, &bytes_done);
ms->buffer_used = true;
}
else {
//;VGM_LOG("MPEG: get samples from old data\n");
rc = mpg123_decode(ms->handle,
NULL, 0,
sbuf + bytes_filled, ms->sbuf_size - bytes_filled,
&bytes_done);
rc = mpg123_decode(ms->handle, NULL, 0, sbuf, ms->sbuf_size - bytes_filled, &bytes_done);
}
samples_filled = bytes_done / channels_per_frame / sizeof(sample_t);
samples_filled = bytes_done / channels_per_frame / sizeof(float);
/* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */
if (ms->decode_to_discard) {
size_t decode_to_discard = ms->decode_to_discard;
if (decode_to_discard > samples_filled)
decode_to_discard = samples_filled;
size_t bytes_to_discard = sizeof(sample_t) * decode_to_discard * channels_per_frame;
size_t bytes_to_discard = sizeof(float) * decode_to_discard * channels_per_frame;
bytes_done -= bytes_to_discard;
ms->decode_to_discard -= decode_to_discard;
@ -474,104 +405,115 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data*
decode_fail:
/* 0-fill but continue with other streams */
bytes_filled = ms->samples_filled * channels_per_frame * sizeof(sample_t);
bytes_filled = ms->samples_filled * channels_per_frame * sizeof(float);
memset(sbuf + bytes_filled, 0, ms->sbuf_size - bytes_filled);
ms->samples_filled = (ms->sbuf_size / channels_per_frame / sizeof(sample_t));
ms->samples_filled = (ms->sbuf_size / channels_per_frame / sizeof(float));
}
/**
* Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc.
*
* Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode.
. Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space.
*/
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, float* sbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
while (samples_done < samples_to_do) {
int samples_to_copy = -1;
/* find max to copy from all streams (equal for all channels) */
for (int i = 0; i < data->streams_count; i++) {
size_t samples_in_stream = data->streams[i].samples_filled - data->streams[i].samples_used;
if (samples_to_copy < 0 || samples_in_stream < samples_to_copy)
samples_to_copy = samples_in_stream;
}
/* discard if needed (for looping) */
if (data->samples_to_discard) {
int samples_to_discard = samples_to_copy;
if (samples_to_discard > data->samples_to_discard)
samples_to_discard = data->samples_to_discard;
for (int i = 0; i < data->streams_count; i++) {
data->streams[i].samples_used += samples_to_discard;
}
data->samples_to_discard -= samples_to_discard;
samples_to_copy -= samples_to_discard;
}
/* mux streams channels (1/2ch combos) to sbuf (Nch) */
if (samples_to_copy > 0) {
if (samples_to_copy > samples_to_do - samples_done)
samples_to_copy = samples_to_do - samples_done;
int ch = 0;
for (int stream = 0; stream < data->streams_count; stream++) {
mpeg_custom_stream* ms = &data->streams[stream];
int stream_channels = ms->channels_per_frame;
for (int stream_ch = 0; stream_ch < stream_channels; stream_ch++) {
for (int s = 0; s < samples_to_copy; s++) {
size_t stream_sample = (ms->samples_used+s)*stream_channels + stream_ch;
size_t buffer_sample = (samples_done+s)*channels + ch;
sbuf[buffer_sample] = ms->sbuf[stream_sample];
}
ch++;
}
ms->samples_used += samples_to_copy;
}
samples_done += samples_to_copy;
}
else {
/* decode more into stream sample buffers */
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
* With multiple offsets they should already start in the first frame of each stream. */
for (int i = 0; i < data->streams_count; i++) {
switch(data->type) {
//case MPEG_FSB:
/* same offset: alternate frames between streams (maybe needed for weird layouts?) */
//decode_mpeg_custom_stream(&vgmstream->ch[0], data, i);
default:
/* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */
decode_mpeg_custom_stream(&vgmstream->ch[i], data, i);
break;
}
}
}
}
}
static bool decode_frame_mpeg(VGMSTREAM* v) {
mpeg_codec_data* data = v->codec_data;
decode_state_t* ds = v->decode_state;
// TODO: needed for EALayer3, that has block with max number of samples (could be handled by reading single frames)
int samples_to_do = ds->samples_left;
if (samples_to_do > data->samples_per_frame)
samples_to_do = data->samples_per_frame;
if (!data->custom) {
decode_mpeg_standard(&v->ch[0], data, data->sbuf, samples_to_do, v->channels);
} else {
decode_mpeg_custom(v, data, data->sbuf, samples_to_do, v->channels);
}
sbuf_init_flt(&ds->sbuf, data->sbuf, samples_to_do, v->channels);
ds->sbuf.filled = samples_to_do;
return true;
}
/*********/
/* UTILS */
/*********/
static void flush_mpeg(mpeg_codec_data* data, int is_loop);
void free_mpeg(mpeg_codec_data* data) {
if (!data)
return;
if (!data->custom) {
mpg123_delete(data->m);
}
else {
for (int i = 0; i < data->streams_size; i++) {
if (!data->streams)
continue;
mpg123_delete(data->streams[i].handle);
free(data->streams[i].buffer);
free(data->streams[i].sbuf);
}
free(data->streams);
}
free(data->buffer);
free(data);
/* The astute reader will note that a call to mpg123_exit is never
* made. While is is evilly breaking our contract with mpg123, it
* doesn't actually do anything except set the "initialized" flag
* to 0. And if we exit we run the risk of turning it off when
* someone else in another thread is using it. */
}
/* seeks stream to 0 */
void reset_mpeg(mpeg_codec_data* data) {
if (!data) return;
flush_mpeg(data, 0);
#if 0
/* flush_mpeg properly resets mpg123 with mpg123_open_feed, and
* offsets are reset in the VGMSTREAM externally, but for posterity: */
if (!data->custom) {
off_t input_offset = 0;
mpg123_feedseek(data->m,0,SEEK_SET,&input_offset);
}
else {
off_t input_offset = 0;
int i;
for (i = 0; i < data->streams_size; i++) {
if (!data->streams)
continue;
mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset);
}
}
#endif
}
/* seeks to a point */
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
mpeg_codec_data* data = vgmstream->codec_data;
if (!data) return;
if (!data->custom) {
off_t input_offset = 0;
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
/* adjust loop with mpg123's offset (useful?) */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
}
else {
flush_mpeg(data, 1);
/* restart from 0 and manually discard samples, since we don't really know the correct offset */
for (int i = 0; i < data->streams_size; i++) {
//if (!data->streams)
// continue;
//mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); /* already reset */
/* force first offset as discard-looping needs to start from the beginning */
if (vgmstream->loop_ch)
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
}
data->samples_to_discard += num_sample;
}
}
/* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */
static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
if (!data)
@ -579,11 +521,11 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
if (!data->custom) {
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
mpg123_open_feed(data->m); /* mpg123_feedseek won't work */
mpg123_open_feed(data->handle); // mpg123_feedseek won't work
}
else {
/* re-start from 0 */
for (int i = 0; i < data->streams_size; i++) {
for (int i = 0; i < data->streams_count; i++) {
if (!data->streams)
continue;
@ -609,25 +551,84 @@ static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
data->buffer_used = false;
}
void reset_mpeg(void* priv_data) {
mpeg_codec_data* data = priv_data;
if (!data) return;
flush_mpeg(data, 0);
#if 0
/* flush_mpeg properly resets mpg123 with mpg123_open_feed, and
* offsets are reset in the VGMSTREAM externally, but for posterity: */
if (!data->custom) {
off_t input_offset = 0;
mpg123_feedseek(data->handle,0,SEEK_SET,&input_offset);
}
else {
off_t input_offset = 0;
int i;
for (i = 0; i < data->streams_count; i++) {
if (!data->streams)
continue;
mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset);
}
}
#endif
}
/* seeks to a point */
static void seek_mpeg(VGMSTREAM* v, int32_t num_sample) {
mpeg_codec_data* data = v->codec_data;
if (!data) return;
if (!data->custom) {
off_t input_offset = 0;
mpg123_feedseek(data->handle, num_sample, SEEK_SET, &input_offset);
/* adjust loop with mpg123's offset (useful?) */
if (v->loop_ch)
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + input_offset;
}
else {
flush_mpeg(data, 1);
/* restart from 0 and manually discard samples, since we don't really know the correct offset */
for (int i = 0; i < data->streams_count; i++) {
//if (!data->streams)
// continue;
//mpg123_feedseek(data->streams[i].handle,0,SEEK_SET,&input_offset); /* already reset */
/* force first offset as discard-looping needs to start from the beginning */
if (v->loop_ch)
v->loop_ch[i].offset = v->loop_ch[i].channel_start_offset;
}
data->samples_to_discard += num_sample;
}
}
int mpeg_get_sample_rate(mpeg_codec_data* data) {
return data->sample_rate_per_frame;
return data->sample_rate;
}
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data) {
/* if not found just return 0 and expect to fail (if used for num_samples) */
if (!data->custom) {
/* We would need to read all VBR frames headers to count samples */
if (data->mi.vbr != MPG123_CBR) { //maybe abr_rate could be used to get an approx
if (data->is_vbr) { //maybe abr_rate could be used to get an approx
VGM_LOG("MPEG: vbr mp3 can't do bytes_to_samples\n");
return 0;
}
return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000);
return (int64_t)bytes * data->sample_rate * 8 / (data->bitrate * 1000);
}
else {
/* needed for SCD */
if (data->streams_size && data->bitrate_per_frame) {
return (int64_t)(bytes / data->streams_size) * data->sample_rate_per_frame * 8 / (data->bitrate_per_frame * 1000);
if (data->streams_count && data->bitrate) {
return (int64_t)(bytes / data->streams_count) * data->sample_rate * 8 / (data->bitrate * 1000);
}
return 0;
@ -638,14 +639,22 @@ long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data) {
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
void mpeg_set_error_logging(mpeg_codec_data* data, int enable) {
if (!data->custom) {
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
mpg123_param(data->handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
}
else {
int i;
for (i=0; i < data->streams_size; i++) {
for (i=0; i < data->streams_count; i++) {
mpg123_param(data->streams[i].handle, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
}
}
}
#endif
const codec_info_t mpeg_decoder = {
.sample_type = SFMT_FLT,
.decode_frame = decode_frame_mpeg,
.free = free_mpeg,
.reset = reset_mpeg,
.seek = seek_mpeg,
};
#endif

View file

@ -7,27 +7,26 @@
/* used by mpeg_decoder.c, but scattered in other .c files */
#ifdef VGM_USE_MPEG
#include <mpg123/mpg123.h>
/* represents a single MPEG stream */
typedef struct {
/* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */
uint8_t* buffer; /* raw data buffer */
/* buf per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */
uint8_t* buffer;
size_t buffer_size;
size_t bytes_in_buffer;
bool buffer_full; /* raw buffer has been filled */
bool buffer_used; /* raw buffer has been fed to the decoder */
bool buffer_full;
bool buffer_used;
mpg123_handle* handle; /* MPEG decoder */
void* handle;
void* sbuf; /* decoded samples from this stream */
size_t sbuf_size; /* in bytes for mpg123 */
size_t samples_filled; /* data in the buffer (in samples) */
size_t samples_used; /* data extracted from the buffer */
float* sbuf;
int sbuf_size; // in bytes for mpg123
int samples_filled;
int samples_used;
size_t current_size_count; /* data read (if the parser needs to know) */
size_t current_size_target; /* max data, until something happens */
size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
int current_size_count; /* data read (if the parser needs to know) */
int current_size_target; /* max data, until something happens */
int decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
int channels_per_frame; /* for rare cases that streams don't share this */
} mpeg_custom_stream;
@ -35,20 +34,20 @@ typedef struct {
struct mpeg_codec_data {
/* regular/single MPEG internals */
uint8_t* buffer; /* raw data buffer */
size_t buffer_size;
size_t bytes_in_buffer;
int buffer_size;
int bytes_in_buffer;
bool buffer_full; /* raw buffer has been filled */
bool buffer_used; /* raw buffer has been fed to the decoder */
mpg123_handle* m; /* MPEG decoder */
struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */
void* handle; // MPEG decoder
/* for internal use */
int channels_per_frame;
int samples_per_frame;
/* for some calcs */
int bitrate_per_frame;
int sample_rate_per_frame;
int bitrate;
int sample_rate;
bool is_vbr;
/* custom MPEG internals */
bool custom; /* flag */
@ -57,11 +56,13 @@ struct mpeg_codec_data {
size_t default_buffer_size;
mpeg_custom_stream* streams; /* array of MPEG streams (ex. 2ch+2ch) */
size_t streams_size;
int streams_count;
size_t skip_samples; /* base encoder delay */
size_t samples_to_discard; /* for custom mpeg looping */
int skip_samples; /* base encoder delay */
int samples_to_discard; /* for custom mpeg looping */
float* sbuf; // decoded samples from all streams
int sbuf_size; // in bytes for mpg123
};
int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t* coding_type);

View file

@ -1,22 +1,34 @@
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libs/relic_lib.h"
//TODO: fix looping
struct relic_codec_data {
typedef struct {
relic_handle_t* handle;
int channels;
int frame_size;
int32_t samples_discard;
int32_t samples_consumed;
int32_t samples_filled;
};
float fbuf[RELIC_SAMPLES_PER_FRAME * RELIC_MAX_CHANNELS];
int32_t discard;
} relic_codec_data;
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate) {
static void free_relic(void* priv_data) {
relic_codec_data* data = priv_data;
if (!data) return;
relic_free(data->handle);
free(data);
}
void* init_relic(int channels, int bitrate, int codec_rate) {
relic_codec_data* data = NULL;
if (channels > RELIC_MAX_CHANNELS)
goto fail;
data = calloc(1, sizeof(relic_codec_data));
if (!data) goto fail;
@ -32,92 +44,68 @@ fail:
return NULL;
}
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
int ch;
int bytes;
int ok;
static int decode_frame_channels(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
uint8_t buf[RELIC_BUFFER_SIZE];
for (ch = 0; ch < data->channels; ch++) {
bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
if (bytes != data->frame_size) goto fail;
for (int ch = 0; ch < data->channels; ch++) {
int bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
stream->offset += data->frame_size;
ok = relic_decode_frame(data->handle, buf, ch);
if (!ok) goto fail;
if (bytes != data->frame_size) return -1;
int ok = relic_decode_frame(data->handle, buf, ch);
if (!ok) return -1;
}
data->samples_consumed = 0;
data->samples_filled = RELIC_SAMPLES_PER_FRAME;
return 1;
fail:
return 0;
return RELIC_SAMPLES_PER_FRAME;
}
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
static bool decode_frame_relic(VGMSTREAM* v) {
decode_state_t* ds = v->decode_state;
relic_codec_data* data = v->codec_data;
while (samples_to_do > 0) {
int samples = decode_frame_channels(&v->ch[0], data);
if (samples <= 0)
return false;
if (data->samples_consumed < data->samples_filled) {
/* consume samples */
int samples_to_get = (data->samples_filled - data->samples_consumed);
relic_get_float(data->handle, data->fbuf);
if (data->samples_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_discard)
samples_to_get = data->samples_discard;
data->samples_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do)
samples_to_get = samples_to_do;
sbuf_init_f16(&ds->sbuf, data->fbuf, samples, data->channels);
ds->sbuf.filled = samples;
relic_get_pcm16(data->handle, outbuf, samples_to_get, data->samples_consumed);
samples_to_do -= samples_to_get;
outbuf += samples_to_get * data->channels;
if (data->discard) {
ds->discard = data->discard;
data->discard = 0;
}
/* mark consumed samples */
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));
return true;
}
void reset_relic(relic_codec_data* data) {
static void reset_relic(void* priv_data) {
relic_codec_data* data = priv_data;
if (!data) return;
relic_reset(data->handle);
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_discard = 0;
data->discard = 0;
}
void seek_relic(relic_codec_data* data, int32_t num_sample) {
static void seek_relic(VGMSTREAM* v, int32_t num_sample) {
relic_codec_data* data = v->codec_data;
if (!data) return;
reset_relic(data);
data->samples_discard = num_sample;
}
void free_relic(relic_codec_data* data) {
if (!data) return;
relic_free(data->handle);
free(data);
data->discard = num_sample;
}
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) {
return bytes / channels / (bitrate / 8) * RELIC_SAMPLES_PER_FRAME;
}
const codec_info_t relic_decoder = {
.sample_type = SFMT_F16,
.decode_frame = decode_frame_relic,
.free = free_relic,
.reset = reset_relic,
.seek = seek_relic,
};

View file

@ -129,7 +129,7 @@ fail:
bool setup_layout_layered(layered_layout_data* data) {
int max_input_channels = 0;
int max_output_channels = 0;
int max_sample_size = 0;
sfmt_t max_sample_type = SFMT_NONE;
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
for (int i = 0; i < data->layer_count; i++) {
@ -166,9 +166,9 @@ bool setup_layout_layered(layered_layout_data* data) {
#endif
}
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->layers[i]) );
if (max_sample_size < current_sample_size)
max_sample_size = current_sample_size;
sfmt_t current_sample_type = mixing_get_input_sample_type(data->layers[i]);
if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority
max_sample_type = current_sample_type;
/* loops and other values could be mismatched, but should be handled on allocate */
@ -185,6 +185,10 @@ bool setup_layout_layered(layered_layout_data* data) {
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
return false;
// needed for codecs like FFMpeg where base vgmstream's sample type is unknown
data->fmt = max_sample_type;
int max_sample_size = sfmt_get_sample_size(max_sample_type);
/* create internal buffer big enough for mixing all layers */
free(data->buffer);
data->buffer = malloc(VGMSTREAM_LAYER_SAMPLE_BUFFER * max_input_channels * max_sample_size);

View file

@ -23,6 +23,7 @@ typedef struct {
int input_channels; /* internal buffer channels */
int output_channels; /* resulting channels (after mixing, if applied) */
bool mixed_channels; /* segments have different number of channels */
sfmt_t fmt;
} segmented_layout_data;
void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream);
@ -44,6 +45,7 @@ typedef struct {
int output_channels; /* resulting channels (after mixing, if applied) */
int external_looping; /* don't loop using per-layer loops, but layout's own looping */
int curr_layer; /* helper */
sfmt_t fmt;
} layered_layout_data;
void render_vgmstream_layered(sbuf_t* sbuf, VGMSTREAM* vgmstream);

View file

@ -155,7 +155,7 @@ fail:
bool setup_layout_segmented(segmented_layout_data* data) {
int max_input_channels = 0;
int max_output_channels = 0;
int max_sample_size = 0;
sfmt_t max_sample_type = SFMT_NONE;
bool mixed_channels = false;
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
@ -212,9 +212,9 @@ bool setup_layout_segmented(segmented_layout_data* data) {
// goto fail;
}
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->segments[i]) );
if (max_sample_size < current_sample_size)
max_sample_size = current_sample_size;
sfmt_t current_sample_type = mixing_get_input_sample_type(data->segments[i]);
if (max_sample_type < current_sample_type && max_sample_type != SFMT_FLT) //float has priority
max_sample_type = current_sample_type;
/* init mixing */
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER);
@ -226,6 +226,10 @@ bool setup_layout_segmented(segmented_layout_data* data) {
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
return false;
// needed for codecs like FFMpeg where base vgmstream's sample type is unknown
data->fmt = max_sample_type;
int max_sample_size = sfmt_get_sample_size(max_sample_type);
/* create internal buffer big enough for mixing */
free(data->buffer);
data->buffer = malloc(VGMSTREAM_SEGMENT_SAMPLE_BUFFER * max_input_channels * max_sample_size);

View file

@ -1,7 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
/* ASTB - found in Dead Rising (X360) */
/* ASTB - from early MT Framework games [Dead Rising (X360), Lost Planet (X360)] */
VGMSTREAM* init_vgmstream_astb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, data_size;

View file

@ -72,6 +72,10 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
loop_flag = find_meta_loops(data, &loop_start, &loop_end);
}
if (is_id32be(0x00, sf, "fLaC")) {
ffmpeg_set_allow_pcm24(data);
}
/* hack for AAC files (will return 0 samples if not an actual file) */
if (!num_samples && check_extensions(sf, "aac,laac")) {
num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));

View file

@ -181,7 +181,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
vgmstream->layout_type = layout_layered;
}
else {
vgmstream->codec_data = init_celt_fsb(vgmstream->channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1);
vgmstream->codec_data = is_new_lib ?
init_celt_fsb_v2(vgmstream->channels) :
init_celt_fsb_v1(vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_none;
@ -250,7 +252,9 @@ static layered_layout_data* build_layered_fsb_celt(STREAMFILE* sf, fsb_header_t*
data->layers[i]->loop_end_sample = fsb->loop_end;
#ifdef VGM_USE_CELT
data->layers[i]->codec_data = init_celt_fsb(layer_channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1);
data->layers[i]->codec_data = is_new_lib ?
init_celt_fsb_v2(layer_channels) :
init_celt_fsb_v1(layer_channels);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_CELT_FSB;
data->layers[i]->layout_type = layout_none;

View file

@ -405,7 +405,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
vgmstream->layout_type = layout_layered;
}
else {
vgmstream->codec_data = init_celt_fsb(vgmstream->channels, CELT_0_11_0);
vgmstream->codec_data = init_celt_fsb_v2(vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_none;
@ -591,7 +591,7 @@ static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, STREAMFILE* sb, f
switch (fsb5->codec) {
#ifdef VGM_USE_CELT
case 0x0C: { /* CELT */
data->layers[i]->codec_data = init_celt_fsb(layer_channels, CELT_0_11_0);
data->layers[i]->codec_data = init_celt_fsb_v2(layer_channels);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_CELT_FSB;
data->layers[i]->layout_type = layout_none;

View file

@ -65,12 +65,13 @@ static const fsbkey_info fsbkey_list[] = {
{ MODE_FSB5, FSBKEY_ADD("cbfjZTlUPaZI") }, // JDM: Japanese Drift Master (PC)
{ MODE_FSB3, FSBKEY_ADD("tkdnsem000") }, // Ys Online: The Call of Solum (PC) [FSB3] (alt key: 2ED62676CEA6B60C0C0C)
{ MODE_FSB4, FSBKEY_ADD("4DxgpNV3pQLPD6GT7g9Gf6eWU7SXutGQ") }, // Test Drive: Ferrari Racing Legends (PC)
{ MODE_FSB4, FSBKEY_ADD("AjaxIsTheGoodestBoy") }, // Hello Kitty: Island Adventure (iOS)
{ MODE_FSB5, FSBKEY_ADD("AjaxIsTheGoodestBoy") }, // Hello Kitty: Island Adventure (iOS)
{ MODE_FSB5, FSBKEY_ADD("resoforce") }, // Rivals of Aether 2 (PC)
{ MODE_FSB5, FSBKEY_ADD("3cfe772db5b55b806541d3faf894020e") }, // Final Fantasy XV: War for Eos (Android)
{ MODE_FSB5, FSBKEY_ADD("aj#$kLucf2lh}eqh") }, // Forza Motorsport 2023 (PC)
{ MODE_FSB4, FSBKEY_ADD("dpdjeoqkr") }, // AirRider CrazyRacing (PC)
{ MODE_FSB5, FSBKEY_ADD("weareAbsolutelyUnsure2018") }, // Wanderstop (PC)
{ MODE_FSB5, FSBKEY_ADD(".xW3uXQ8q79yunvMjL6nahLXts9esEXX2VgetuPCxdLrAjUUbZAmB7R*A6KjW24NU_8ifMZ8TC4Qk@_oEsjsK2QLpAaG-Fy!wYKP") }, // UNBEATABLE Demo (PC)
/* some games use a key per file, generated from the filename
* (could add all of them but there are a lot of songs, so external .fsbkey are probably better) */

View file

@ -111,7 +111,7 @@ static uint32_t decrypt_chunk(uint8_t* buf, int buf_size, uint32_t key) {
return key; /* to resume decrypting if needed */
}
#define HEADER_MAX 0x2800 /* seen 0x2420 in one bank */
#define HEADER_MAX 0x3000 /* seen 0x2BD0 in one bank */
/* header is encrypted except in M&L 3DS so decrypt + handle in buffer; format
* base 0x30 header + subheader with tables/headers depending on type */
@ -259,11 +259,12 @@ static bool parse_header(redspark_header_t* h, STREAMFILE* sf, bool is_new) {
coef_offset = target_pos + 0x28;
h->channels = 1;
h->loop_flag = (h->loop_start != -1); /* TODO: many files sound kind of odd */
h->loop_flag = 0; // (h->loop_start != -1); // TODO: many files sound kind of odd
if (h->num_samples == 0)
h->num_samples = h->loop_end;
// seems correct based on MadWorld, not 100% sure in Imabikisou
if (h->sample_rate == 0)
h->sample_rate = 32000;
h->sample_rate = 24000;
/* empty entry */
if (h->stream_size == 0)

View file

@ -145,12 +145,12 @@ VGMSTREAM* init_vgmstream_rfrm(STREAMFILE* sf) {
goto fail;
version = read_32bitBE(0x18,sf); /* assumed, also at 0x1c */
if (version == 0x0a) { /* Wii U */
if (version == 0x0a) { /* DKCTF Wii U */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
big_endian = 1;
}
else if (version == 0x12) { /* Switch */
else if (version == 0x11 || version == 0x12) { /* RS11 and DKCTF Switch */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
big_endian = 0;