Compare commits

...

2 commits

Author SHA1 Message Date
Christopher Snowhill
69533e12c7 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>
2025-05-03 01:56:34 -07:00
Christopher Snowhill
048bc7c30d Converter Node: Change volume scale observer
This should fix an exception being thrown because the observer wasn't
registered, or known to be registered. Only register it when it will be
used, and only unregister it if it was registered.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-03 01:56:29 -07:00
33 changed files with 1452 additions and 1542 deletions

View file

@ -45,6 +45,8 @@
double sampleRatio;
BOOL observersAdded;
float volumeScale;
void *floatBuffer;

View file

@ -66,8 +66,6 @@ static void *kConverterNodeContext = &kConverterNodeContext;
extrapolateBuffer = NULL;
extrapolateBufferSize = 0;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:kConverterNodeContext];
#ifdef LOG_CHAINS
[self initLogFiles];
#endif
@ -76,6 +74,20 @@ static void *kConverterNodeContext = &kConverterNodeContext;
return self;
}
- (void)addObservers {
if(!observersAdded) {
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:(NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew) context:kConverterNodeContext];
observersAdded = YES;
}
}
- (void)removeObservers {
if(observersAdded) {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext];
observersAdded = NO;
}
}
void scale_by_volume(float *buffer, size_t count, float volume) {
if(volume != 1.0) {
size_t unaligned = (uintptr_t)buffer & 15;
@ -334,6 +346,7 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
if(nodeChannelConfig) {
[chunk setChannelConfig:nodeChannelConfig];
}
[self addObservers];
scale_by_volume(floatBuffer, ioNumberPackets / sizeof(float), volumeScale);
[chunk setStreamTimestamp:streamTimestamp];
[chunk setStreamTimeRatio:streamTimeRatio];
@ -488,7 +501,7 @@ static float db_to_scale(float db) {
- (void)dealloc {
DLog(@"Converter dealloc");
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext];
[self removeObservers];
paused = NO;
[self cleanUp];

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 */
{
int32_t seek_sample = data->config.encoder_delay + num_sample;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
// find closest offset to desired sample, and samples to discard after that offset to reach loop
int32_t seek_sample = data->config.encoder_delay + num_sample;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
superframe_number = (seek_sample / superframe_samples); /* closest */
// decoded frames affect each other slightly, so move offset back to make PCM stable
// and equivalent to a full discard loop
int superframe_number = (seek_sample / superframe_samples); // closest
int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe)
if (superframe_back > superframe_number)
superframe_back = superframe_number;
/* decoded frames affect each other slightly, so move offset back to make PCM stable
* and equivalent to a full discard loop */
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
if (superframe_back > superframe_number)
superframe_back = superframe_number;
int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
off_t seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
data->discard = seek_discard; // already includes encoder delay
data->samples_to_discard = seek_discard; /* already includes encoder delay */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
if (v->loop_ch) {
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset;
}
#if 0
//old full discard loop
{
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
data->discard = num_sample;
data->discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
// loop offsets are set during decode; force them to stream start so discard works
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
#endif
}
void free_atrac9(atrac9_codec_data* data) {
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
static int atrac9_parse_config(uint32_t config_data, int* p_sample_rate, int* p_channels, size_t* p_frame_size, size_t* p_samples_per_frame) {
static const int sample_rate_table[16] = {
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@ -266,7 +245,8 @@ fail:
return 0;
}
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) {
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data) {
atrac9_codec_data* data = priv_data;
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
}
@ -276,4 +256,12 @@ size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data) {
return 0;
return bytes / frame_size * samples_per_frame;
}
const codec_info_t atrac9_decoder = {
.sample_type = SFMT_S16, //TODO: decoder doesn't return float (to match Sony's lib apparently)
.decode_frame = decode_frame_atrac9,
.free = free_atrac9,
.reset = reset_atrac9,
.seek = seek_atrac9,
};
#endif

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);
}
/* decode samples of any kind of FFmpeg format */
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
ffmpeg_codec_data* data = vgmstream->codec_data;
while (samples_to_do > 0) {
if (data->samples_consumed < data->samples_filled) {
/* consume samples */
int samples_to_get = (data->samples_filled - data->samples_consumed);
if (data->samples_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_discard)
samples_to_get = data->samples_discard;
data->samples_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do)
samples_to_get = samples_to_do;
copy_samples(data, outbuf, samples_to_get, channels);
samples_to_do -= samples_to_get;
outbuf += samples_to_get * channels;
}
/* mark consumed samples */
data->samples_consumed += samples_to_get;
}
else {
int ok = decode_ffmpeg_frame(data);
if (!ok) goto decode_fail;
}
// FFmpeg can't do PCM24 and upsamples to PCM32, if needed
if (data->fmt == SFMT_S24) {
samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do);
}
return;
}
decode_fail:
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do);
samples_silence_s16(outbuf, channels, samples_to_do);
void remap_audio(ffmpeg_codec_data* data, sbuf_t* sbuf) {
if (!data->channel_remap_set)
return;
switch(sbuf->fmt) {
case SFMT_FLT:
remap_audio_flt(sbuf->buf, sbuf->filled, sbuf->channels, data->channel_remap);
break;
default: // trivial to add but not used
VGM_LOG("FFMPEG: unsupported channel remapping found (implement)\n");
break;
}
}
// ensure output buffer can handle samples, since in theory it could change per call
static bool prepare_sbuf(ffmpeg_codec_data* data, int samples, int channels) {
if (!data->sbuf && data->sbuf_samples >= samples)
return true;
free(data->sbuf);
data->sbuf_samples = samples * 2;
data->sbuf = malloc(data->sbuf_samples * channels * sizeof(float));
if (!data->sbuf) return false;
return true;
}
bool decode_frame_ffmpeg(VGMSTREAM* v) {
decode_state_t* ds = v->decode_state;
ffmpeg_codec_data* data = v->codec_data;
int samples = decode_frame_internal(data);
if (samples < 0)
return false;
if (!prepare_sbuf(data, samples, v->channels))
return false;
copy_samples(data, data->sbuf, samples, v->channels);
sbuf_init(&ds->sbuf, data->fmt, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
remap_audio(data, &ds->sbuf);
if (data->samples_discard) {
ds->discard = data->samples_discard;
data->samples_discard = 0;
}
return true;
}
@ -868,11 +850,7 @@ decode_fail:
/* UTILS */
/* ******************************************** */
void reset_ffmpeg(ffmpeg_codec_data* data) {
seek_ffmpeg(data, 0);
}
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
if (!data) return;
/* Start from 0 and discard samples until sample (slower but not too noticeable).
@ -897,13 +875,11 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
avcodec_flush_buffers(data->codecCtx);
}
data->samples_consumed = 0;
data->samples_filled = 0;
data->samples_discard = num_sample;
data->read_packet = 1;
data->end_of_stream = 0;
data->end_of_audio = 0;
data->read_packet = true;
data->end_of_stream = false;
data->end_of_audio = false;
/* consider skip samples (encoder delay), if manually set */
if (data->skip_samples_set) {
@ -918,6 +894,15 @@ fail:
}
static void seek_ffmpeg(VGMSTREAM* v, int32_t num_sample) {
seek_ffmpeg_internal(v->codec_data, num_sample);
}
static void reset_ffmpeg(void* priv_data) {
seek_ffmpeg_internal(priv_data, 0);
}
static void free_ffmpeg_config(ffmpeg_codec_data* data) {
if (data == NULL)
return;
@ -946,10 +931,11 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) {
av_freep(&(data->buffer));
}
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option
//TODO: avformat_find_stream_info may cause some Win Handle leaks? related to certain option
}
void free_ffmpeg(ffmpeg_codec_data* data) {
void free_ffmpeg(void* priv_data) {
ffmpeg_codec_data* data = priv_data;
if (data == NULL)
return;
@ -1003,9 +989,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
#endif
/* set skip samples with our internal discard */
data->skip_samples_set = 1;
data->samples_discard = skip_samples;
data->skip_samples_set = true;
data->skip_samples = skip_samples;
data->samples_discard = skip_samples;
}
/* returns channel layout if set */
@ -1032,8 +1018,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
/* yet another hack to fix codecs that encode channels in different order and reorder on decoder
* but FFmpeg doesn't do it automatically
* (maybe should be done via mixing, but could clash with other stuff?) */
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
int i;
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels;
#else
@ -1043,10 +1028,12 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
if (channels > 32)
return;
for (i = 0; i < channels; i++) {
for (int i = 0; i < channels; i++) {
data->channel_remap[i] = channel_remap[i];
}
data->channel_remap_set = 1;
VGM_LOG("FFMPEG: channel remapping set\n");
data->channel_remap_set = true;
}
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
@ -1065,7 +1052,7 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
data->force_seek = 1;
data->force_seek = true;
reset_ffmpeg(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->stream_index];
}
@ -1073,7 +1060,16 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
if (!data)
return;
data->invert_floats_set = 1;
data->invert_floats_set = true;
}
// for flac 24-bit, since FFMpeg upsamples to PCM32 (ignored if flac 16-bit)
void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data) {
if (!data)
return;
if (data->fmt != SFMT_S32)
return;
data->fmt = SFMT_S24;
}
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
@ -1131,4 +1127,22 @@ STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
if (!data) return NULL;
return data->sf;
}
static sfmt_t get_sample_type_ffmpeg(VGMSTREAM* v) {
ffmpeg_codec_data* data = v->codec_data;
if (!data)
return SFMT_NONE;
return data->fmt;
}
const codec_info_t ffmpeg_decoder = {
.get_sample_type = get_sample_type_ffmpeg,
.decode_frame = decode_frame_ffmpeg,
.free = free_ffmpeg,
.reset = reset_ffmpeg,
.seek = seek_ffmpeg,
};
#endif

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;
}
/* mark consumed samples */
data->samples_consumed += samples_to_get;
}
else {
int ok = decode_frame_next(stream, data);
if (!ok) goto decode_fail;
}
if (data->discard) {
ds->discard = data->discard;
data->discard = 0;
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("RELIC: decode fail, missing %i samples\n", samples_to_do);
memset(outbuf, 0, samples_to_do * data->channels * sizeof(sample_t));
return true;
}
void reset_relic(relic_codec_data* data) {
static void reset_relic(void* priv_data) {
relic_codec_data* data = priv_data;
if (!data) return;
relic_reset(data->handle);
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_discard = 0;
data->discard = 0;
}
void seek_relic(relic_codec_data* data, int32_t num_sample) {
static void seek_relic(VGMSTREAM* v, int32_t num_sample) {
relic_codec_data* data = v->codec_data;
if (!data) return;
reset_relic(data);
data->samples_discard = num_sample;
}
void free_relic(relic_codec_data* data) {
if (!data) return;
relic_free(data->handle);
free(data);
data->discard = num_sample;
}
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) {
return bytes / channels / (bitrate / 8) * RELIC_SAMPLES_PER_FRAME;
}
const codec_info_t relic_decoder = {
.sample_type = SFMT_F16,
.decode_frame = decode_frame_relic,
.free = free_relic,
.reset = reset_relic,
.seek = seek_relic,
};

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;