mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
commit
11950459a8
@ -155,6 +155,7 @@ void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sam
|
||||
void reset_mpeg(VGMSTREAM *vgmstream);
|
||||
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_mpeg(mpeg_codec_data *data);
|
||||
void flush_mpeg(mpeg_codec_data * data);
|
||||
|
||||
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data);
|
||||
void mpeg_set_error_logging(mpeg_codec_data * data, int enable);
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* Various EA ADPCM codecs evolved from CDXA */
|
||||
/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA.
|
||||
* Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */
|
||||
|
||||
/*
|
||||
* Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games):
|
||||
@ -28,7 +29,7 @@ static const int EA_XA_TABLE[20] = {
|
||||
0, -1, -3, -4
|
||||
};
|
||||
|
||||
/* EA XA v2 (inconsistently called EAXA or EA-XA too); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on nibble expand */
|
||||
/* EA XA v2; like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t sample_count;
|
||||
@ -84,7 +85,7 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
|
||||
}
|
||||
}
|
||||
|
||||
/* EA XA v1 stereo (aka "EA ADPCM") */
|
||||
/* EA XA v1 stereo */
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
|
@ -69,12 +69,9 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
|
||||
}
|
||||
|
||||
|
||||
/* unknown encryption */
|
||||
if (data->type == MPEG_AHX && data->config.encryption) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
//todo: test more: this improves the output, but seems formats aren't usually prepared
|
||||
// (and/or the num_samples includes all possible samples in file, so by discarding some it'll reach EOF)
|
||||
#if 0
|
||||
/* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */
|
||||
switch(data->type) {
|
||||
case MPEG_AHX: data->skip_samples = 480; break; /* observed default */
|
||||
@ -82,7 +79,7 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
|
||||
default: break;
|
||||
}
|
||||
data->samples_to_discard = data->skip_samples;
|
||||
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
@ -121,10 +118,10 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
|
||||
}
|
||||
|
||||
/* frame interleave (ie. read 1 data-frame, skip 1 data-frame per stream) */
|
||||
current_interleave = data->config.interleave; /* constant, current_size+current_padding */
|
||||
current_interleave = data->config.interleave; /* constant for multi-stream FSbs */
|
||||
|
||||
VGM_ASSERT(current_interleave != current_data_size+current_padding,
|
||||
"MPEG FSB: non-constant interleave found @ 0x%08lx\n", stream->offset);
|
||||
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
|
||||
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08lx\n", data->streams_size, stream->offset);
|
||||
break;
|
||||
|
||||
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
|
||||
@ -154,7 +151,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
|
||||
|
||||
/* skip interleave once block is done, if defined */
|
||||
if (current_interleave && ((stream->offset - stream->channel_start_offset) % current_interleave == 0)) {
|
||||
stream->offset += current_interleave * (data->ms_size-1); /* skip a block each stream */
|
||||
stream->offset += current_interleave * (data->streams_size-1); /* skip a block each stream */
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#ifdef VGM_USE_MPEG
|
||||
#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414
|
||||
|
||||
static int ahx_decrypt_type08(mpeg_codec_data *data);
|
||||
|
||||
/* writes data to the buffer and moves offsets, transforming AHX frames as needed */
|
||||
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
|
||||
@ -38,23 +39,25 @@ int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data)
|
||||
}
|
||||
|
||||
|
||||
/* read VBR frames with CBR header, 0-fill up to expected size to keep mpg123 happy */
|
||||
/* 0-fill up to expected size to keep mpg123 happy */
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,current_data_size,stream->streamfile);
|
||||
memset(data->buffer + data->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - data->bytes_in_buffer);
|
||||
data->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
|
||||
|
||||
|
||||
/* encryption 0x08 modifies a few bits in the side_data every frame, here we decrypt the buffer */
|
||||
if (data->config.encryption) {
|
||||
VGM_LOG("MPEG AHX: unknown encryption\n");
|
||||
goto fail;
|
||||
/* decrypt if needed */
|
||||
switch(data->config.encryption) {
|
||||
case 0x00: break;
|
||||
case 0x08: ahx_decrypt_type08(data); break;
|
||||
default:
|
||||
VGM_LOG("MPEG AHX: unknown encryption 0x%x\n", data->config.encryption);
|
||||
break; /* garbled frame */
|
||||
}
|
||||
|
||||
|
||||
/* update offsets */
|
||||
stream->offset += current_data_size;
|
||||
if (stream->offset + 0x0c >= file_size)
|
||||
stream->offset = file_size; /* move after 0x0c footer to reach EOF (shouldn't happen normally) */
|
||||
stream->offset = file_size; /* skip 0x0c footer to reach EOF (shouldn't happen normally) */
|
||||
|
||||
|
||||
return 1;
|
||||
@ -62,5 +65,47 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decrypts an AHX type 0x08 (keystring) encrypted frame. Algorithm by Thealexbarney */
|
||||
static int ahx_decrypt_type08(mpeg_codec_data *data) {
|
||||
int i, index, encrypted_bits;
|
||||
uint32_t value;
|
||||
uint16_t current_key;
|
||||
|
||||
/* encryption 0x08 modifies a few bits every frame, here we decrypt and write to data buffer */
|
||||
|
||||
/* derive keystring to 3 primes, using the type 0x08 method, and assign each an index of 1/2/3 (0=no key) */
|
||||
/* (externally done for now, see: https://github.com/Thealexbarney/VGAudio/blob/2.0/src/VGAudio/Codecs/CriAdx/CriAdxKey.cs) */
|
||||
|
||||
/* read 2b from a bitstream offset to decrypt, and use it as an index to get the key.
|
||||
* AHX encrypted bitstream starts at 107b (0x0d*8+3), every frame, and seem to always use index 2 */
|
||||
value = (uint32_t)get_32bitBE(data->buffer + 0x0d);
|
||||
index = (value >> (32-3-2)) & 0x03;
|
||||
switch(index) {
|
||||
case 0: current_key = 0; break;
|
||||
case 1: current_key = data->config.cri_key1; break;
|
||||
case 2: current_key = data->config.cri_key2; break;
|
||||
case 3: current_key = data->config.cri_key3; break;
|
||||
default: goto fail;
|
||||
}
|
||||
|
||||
/* AHX for DC: 16b, normal: 6b (no idea, probably some Layer II field) */
|
||||
encrypted_bits = data->config.cri_type == 0x10 ? 16 : 6;
|
||||
|
||||
/* decrypt next bitstream 2b pairs, up to 16b (max key size):
|
||||
* - read 2b from bitstream (from higher to lower)
|
||||
* - read 2b from key (from lower to higher)
|
||||
* - XOR them to decrypt */
|
||||
for (i = 0; i < encrypted_bits; i+=2) {
|
||||
uint32_t xor_2b = (current_key >> i) & 0x03;
|
||||
value ^= ((xor_2b << (32-3-2-2)) >> i);
|
||||
}
|
||||
|
||||
/* write output */
|
||||
put_32bitBE(data->buffer + 0x0d, value);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
673
src/coding/mpeg_custom_utils_ealayer3.c
Normal file
673
src/coding/mpeg_custom_utils_ealayer3.c
Normal file
@ -0,0 +1,673 @@
|
||||
#include "mpeg_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
||||
/**
|
||||
* Utils to parse EALayer3, an MP3 variant. EALayer3 frames have custom headers (removing unneded bits)
|
||||
* with regular MPEG data and optional PCM blocks. We transform EA-frames to MPEG-frames on the fly
|
||||
* and manually fill the sample PCM sample buffer.
|
||||
*
|
||||
* Layer III MPEG1 uses two granules (data chunks) per frame, while MPEG2/2.5 ("LSF mode") only one. EA-frames
|
||||
* contain one granule, so to reconstruct one MPEG-frame we need two EA-frames (MPEG1) or one (MPEG2).
|
||||
* EALayer v1 and v2 differ in part of the header, but are mostly the same.
|
||||
*
|
||||
* Reverse engineering: https://bitbucket.org/Zenchreal/ealayer3 (ealayer3.exe decoder)
|
||||
* Reference: https://www.mp3-tech.org/programmer/docs/mp3_theory.pdf
|
||||
* https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/mpegaudiodec_template.c#L1306
|
||||
*/
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
#define EALAYER3_EA_FRAME_BUFFER_SIZE 0x1000*4 /* enough for one EA-frame */
|
||||
#define EALAYER3_MAX_GRANULES 2
|
||||
#define EALAYER3_MAX_CHANNELS 2
|
||||
|
||||
/* helper to simulate a bitstream */
|
||||
typedef struct {
|
||||
uint8_t * buf; /* buffer to read/write*/
|
||||
size_t bufsize; /* max size of the buffer */
|
||||
off_t b_off; /* current offset in bits inside the buffer */
|
||||
off_t offset; /* info only */
|
||||
} ealayer3_bitstream;
|
||||
|
||||
/* parsed info from a single EALayer3 frame */
|
||||
typedef struct {
|
||||
/* EALayer3 v1 header */
|
||||
uint32_t v1_pcm_flag;
|
||||
uint32_t v1_pcm_decode_discard;
|
||||
uint32_t v1_pcm_number;
|
||||
|
||||
/* EALayer3 v2 header */
|
||||
uint32_t v2_extended_flag;
|
||||
uint32_t v2_stereo_flag;
|
||||
uint32_t v2_unknown; /* unused? */
|
||||
uint32_t v2_frame_size; /* full size including headers and pcm block */
|
||||
uint32_t v2_mode; /* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3 */
|
||||
uint32_t v2_mode_value; /* samples to use depending on mode */
|
||||
uint32_t v2_pcm_number;
|
||||
uint32_t v2_common_size; /* common header+data size; can be zero */
|
||||
|
||||
/* EALayer3 common header + side info */
|
||||
uint32_t version_index;
|
||||
uint32_t sample_rate_index;
|
||||
uint32_t channel_mode;
|
||||
uint32_t mode_extension;
|
||||
|
||||
uint32_t granule_index; /* 0 = first, 1 = second (for MPEG1, that needs pairs) */
|
||||
uint32_t scfsi[EALAYER3_MAX_CHANNELS]; /* SCaleFactor Selection Info */
|
||||
uint32_t main_data_size[EALAYER3_MAX_CHANNELS]; /* AKA part2_3_length */
|
||||
uint32_t others_1[EALAYER3_MAX_CHANNELS]; /* rest of the side info as-is, divided in 2 */
|
||||
uint32_t others_2[EALAYER3_MAX_CHANNELS];
|
||||
|
||||
/* derived from the above */
|
||||
uint32_t data_offset_b; /* start of the MPEG data */
|
||||
uint32_t pre_size; /* size of the V1/V2 part */
|
||||
uint32_t base_size_b; /* size (bits) of the header+side info, up to data_size */
|
||||
uint32_t data_size_b; /* size (bits) of the main MPEG data up to pcm block; can be zero */
|
||||
uint32_t padding_size_b; /* size (bits) of the padding after base+data */
|
||||
uint32_t common_size; /* size of the common part (base+data+padding) */
|
||||
uint32_t pcm_size; /* size of the pcm block */
|
||||
uint32_t eaframe_size; /* size of all of the above, for convenience */
|
||||
|
||||
int mpeg1; /* flag, as MPEG2/2.5 ("low sample frequency" mode) has some differences */
|
||||
int version;
|
||||
int channels;
|
||||
int sample_rate;
|
||||
|
||||
} ealayer3_frame_info;
|
||||
|
||||
|
||||
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf);
|
||||
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame);
|
||||
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
|
||||
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
|
||||
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os);
|
||||
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf);
|
||||
|
||||
static int r_bits(ealayer3_bitstream * iw, int num_bits, uint32_t * value);
|
||||
static int w_bits(ealayer3_bitstream * ow, int num_bits, uint32_t value);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* init codec from a EALayer3 frame */
|
||||
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
|
||||
int ok;
|
||||
ealayer3_frame_info eaf;
|
||||
ealayer3_bitstream is;
|
||||
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
|
||||
|
||||
//;VGM_LOG("EAFRAME: EALayer3 init at %lx\n", start_offset);
|
||||
|
||||
if (data->type == MPEG_EAL32P || data->type == MPEG_EAL32S)
|
||||
goto fail; /* untested */
|
||||
|
||||
/* get first frame for info */
|
||||
{
|
||||
is.buf = ibuf;
|
||||
is.bufsize = read_streamfile(ibuf,start_offset,EALAYER3_EA_FRAME_BUFFER_SIZE, streamFile); /* reads less at EOF */;
|
||||
is.b_off = 0;
|
||||
|
||||
ok = ealayer3_parse_frame(data, &is, &eaf);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
;VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset);
|
||||
|
||||
*coding_type = coding_MPEG_ealayer3;
|
||||
data->channels_per_frame = eaf.channels;
|
||||
data->samples_per_frame = eaf.mpeg1 ? 1152 : 576;
|
||||
|
||||
/* extra checks */
|
||||
if (!data->channels_per_frame || data->config.channels != data->channels_per_frame){
|
||||
VGM_LOG("MPEG EAL3: unknown %i multichannel layout\n", data->config.channels);
|
||||
goto fail; /* unknown layout */
|
||||
}
|
||||
|
||||
|
||||
/* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* writes data to the buffer and moves offsets, transforming EALayer3 frames */
|
||||
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
|
||||
int ok;
|
||||
off_t current_offset = stream->offset;
|
||||
|
||||
ealayer3_frame_info eaf_0, eaf_1;
|
||||
ealayer3_bitstream is_0, is_1, os;
|
||||
uint8_t ibuf_0[EALAYER3_EA_FRAME_BUFFER_SIZE], ibuf_1[EALAYER3_EA_FRAME_BUFFER_SIZE];
|
||||
|
||||
/* read first frame/granule */
|
||||
{
|
||||
is_0.buf = ibuf_0;
|
||||
is_0.bufsize = read_streamfile(ibuf_0,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
|
||||
is_0.b_off = 0;
|
||||
|
||||
ok = ealayer3_parse_frame(data, &is_0, &eaf_0);
|
||||
if (!ok) goto fail;
|
||||
|
||||
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_0);
|
||||
if (!ok) goto fail;
|
||||
|
||||
stream->offset += eaf_0.eaframe_size;
|
||||
}
|
||||
|
||||
/* get second frame/granule */
|
||||
if (eaf_0.mpeg1) {
|
||||
int granule1_found;
|
||||
do {
|
||||
is_1.buf = ibuf_1;
|
||||
is_1.bufsize = read_streamfile(ibuf_1,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
|
||||
is_1.b_off = 0;
|
||||
|
||||
ok = ealayer3_parse_frame(data, &is_1, &eaf_1);
|
||||
if (!ok) goto fail;
|
||||
|
||||
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_1);
|
||||
if (!ok) goto fail;
|
||||
|
||||
stream->offset += eaf_1.eaframe_size;
|
||||
|
||||
|
||||
/* in V1 sometimes there is a PCM block between two granules, try next */
|
||||
if (eaf_1.v1_pcm_flag == 0xEE)
|
||||
granule1_found = 0;
|
||||
else
|
||||
granule1_found = 1; /* assume it does (bad infinite loops) */
|
||||
}
|
||||
while(!granule1_found);
|
||||
}
|
||||
else {
|
||||
memset(&eaf_1, 0, sizeof(ealayer3_frame_info));
|
||||
}
|
||||
|
||||
/* rebuild EALayer frame to MPEG frame */
|
||||
{
|
||||
os.buf = data->buffer;
|
||||
os.bufsize = data->buffer_size;
|
||||
os.b_off = 0;
|
||||
os.offset = current_offset;
|
||||
|
||||
ok = ealayer3_rebuild_mpeg_frame(&is_0, &eaf_0, &is_1, &eaf_1, &os);
|
||||
if (!ok) goto fail;
|
||||
|
||||
data->bytes_in_buffer = os.b_off / 8; /* wrote full MPEG frame, hopefully */
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
|
||||
int ok;
|
||||
|
||||
memset(eaf, 0, sizeof(ealayer3_frame_info));
|
||||
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame); break;
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = ealayer3_parse_frame_v2(is, eaf); break;
|
||||
default: goto fail;
|
||||
}
|
||||
if (!ok) goto fail;
|
||||
|
||||
|
||||
//;VGM_LOG("EAFRAME: v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf->version, eaf->channels, eaf->sample_rate, eaf->granule_index, eaf->pre_size, eaf->common_size, eaf->pcm_size, eaf->eaframe_size);
|
||||
//if (data->type==MPEG_EAL31) VGM_LOG("EAFRAME v1: pcm=%x, unk=%x, number=%x\n", eaf->v1_pcm_flag, eaf->v1_pcm_unknown, eaf->v1_pcm_number);
|
||||
//else VGM_LOG("EAFRAME v2: stereo=%x, unk=%x, fs=%x, mode=%x, val=%x, number=%x, size=%x\n", eaf->v2_stereo_flag, eaf->v2_unknown, eaf->v2_frame_size, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame) {
|
||||
int ok;
|
||||
|
||||
/* read EA-frame V1 header */
|
||||
r_bits(is, 8,&eaf->v1_pcm_flag);
|
||||
|
||||
eaf->pre_size = 1; /* 8b */
|
||||
|
||||
if (eaf->v1_pcm_flag != 0x00 && eaf->v1_pcm_flag != 0xEE) {
|
||||
VGM_LOG("MPEG EAL3 v1: header not 0x00 or 0xEE\n");
|
||||
goto fail; /* wrong offset? */
|
||||
}
|
||||
|
||||
|
||||
/* check PCM block */
|
||||
if (eaf->v1_pcm_flag == 0xEE) {
|
||||
r_bits(is, 16,&eaf->v1_pcm_decode_discard); /* samples to discard of the next decoded (not PCM block) samples */
|
||||
r_bits(is, 16,&eaf->v1_pcm_number); /* number of PCM samples, can be 0 */
|
||||
|
||||
if (!channels_per_frame) {
|
||||
VGM_LOG("MPEG EAL3 v1: PCM block as first frame\n");
|
||||
goto fail; /* must know from a prev frame */
|
||||
}
|
||||
|
||||
eaf->pre_size += 2+2; /* 16b+16b */
|
||||
eaf->pcm_size = (2*eaf->v1_pcm_number * channels_per_frame);
|
||||
}
|
||||
else {
|
||||
/* read EA-frame common header */
|
||||
ok = ealayer3_parse_frame_common(is, eaf);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
|
||||
int ok;
|
||||
|
||||
/* read EA-frame V2 header */
|
||||
r_bits(is, 1,&eaf->v2_extended_flag);
|
||||
r_bits(is, 1,&eaf->v2_stereo_flag);
|
||||
r_bits(is, 2,&eaf->v2_unknown);
|
||||
r_bits(is, 12,&eaf->v2_frame_size);
|
||||
|
||||
eaf->pre_size = 2; /* 16b */
|
||||
|
||||
if (eaf->v2_extended_flag) {
|
||||
r_bits(is, 2,&eaf->v2_mode);
|
||||
r_bits(is, 10,&eaf->v2_mode_value);
|
||||
r_bits(is, 10,&eaf->v2_pcm_number);
|
||||
r_bits(is, 10,&eaf->v2_common_size);
|
||||
|
||||
eaf->pre_size += 4; /* 32b */
|
||||
}
|
||||
|
||||
/* read EA-frame common header */
|
||||
ok = ealayer3_parse_frame_common(is, eaf);
|
||||
if (!ok) goto fail;
|
||||
|
||||
//todo maybe v2 frames can be PCM-only like v1
|
||||
if (!eaf->channels) {
|
||||
VGM_LOG("MPEG EAL3: v2 frame with no channel number");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels);
|
||||
|
||||
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
|
||||
|
||||
if(eaf->v2_frame_size != eaf->eaframe_size) {
|
||||
VGM_LOG("MPEG EAL3: different v2 frame size vs calculated (0x%x vs 0x%x)\n", eaf->v2_frame_size, eaf->eaframe_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parses a EALayer3 frame (common part) */
|
||||
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
|
||||
/* index tables */
|
||||
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
|
||||
static const int sample_rates[4][4] = { /* [version_index][sample rate index] */
|
||||
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
|
||||
{ -1, -1, -1, -1}, /* reserved */
|
||||
{ 22050, 24000, 16000, -1}, /* MPEG2 */
|
||||
{ 44100, 48000, 32000, -1}, /* MPEG1 */
|
||||
};
|
||||
static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */
|
||||
|
||||
off_t start_b_off = is->b_off;
|
||||
int i;
|
||||
|
||||
/* read main header */
|
||||
r_bits(is, 2,&eaf->version_index);
|
||||
r_bits(is, 2,&eaf->sample_rate_index);
|
||||
r_bits(is, 2,&eaf->channel_mode);
|
||||
r_bits(is, 2,&eaf->mode_extension);
|
||||
|
||||
/* check empty frame */
|
||||
if (eaf->version_index == 0 &&
|
||||
eaf->sample_rate_index == 0 &&
|
||||
eaf->channel_mode == 0 &&
|
||||
eaf->mode_extension == 0) {
|
||||
VGM_LOG("MPEG EAL3: empty frame\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* derived */
|
||||
eaf->version = versions[eaf->version_index];
|
||||
eaf->channels = channels[eaf->channel_mode];
|
||||
eaf->sample_rate = sample_rates[eaf->version_index][eaf->sample_rate_index];
|
||||
eaf->mpeg1 = (eaf->version == 1);
|
||||
|
||||
if (eaf->version == -1 || eaf->sample_rate == -1) {
|
||||
VGM_LOG("MPEG EAL3: illegal header values\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* read side info */
|
||||
r_bits(is, 1,&eaf->granule_index);
|
||||
|
||||
if (eaf->mpeg1 && eaf->granule_index == 1) {
|
||||
for (i = 0; i < eaf->channels; i++) {
|
||||
r_bits(is, 4,&eaf->scfsi[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < eaf->channels; i++) {
|
||||
int others_2_bits = eaf->mpeg1 ? 47-32 : 51-32;
|
||||
|
||||
r_bits(is, 12,&eaf->main_data_size[i]);
|
||||
/* divided in 47b=32+15 (MPEG1) or 51b=32+19 (MPEG2), arbitrarily */
|
||||
r_bits(is, 32,&eaf->others_1[i]);
|
||||
r_bits(is, others_2_bits,&eaf->others_2[i]);
|
||||
}
|
||||
|
||||
|
||||
/* derived */
|
||||
eaf->data_offset_b = is->b_off;
|
||||
|
||||
eaf->base_size_b = (is->b_off - start_b_off); /* header + size info size */
|
||||
|
||||
for (i = 0; i < eaf->channels; i++) { /* data size (can be 0, meaning a micro EA-frame) */
|
||||
eaf->data_size_b += eaf->main_data_size[i];
|
||||
}
|
||||
|
||||
if ((eaf->base_size_b+eaf->data_size_b) % 8) /* aligned to closest 8b */
|
||||
eaf->padding_size_b = 8 - ((eaf->base_size_b+eaf->data_size_b) % 8);
|
||||
|
||||
eaf->common_size = (eaf->base_size_b + eaf->data_size_b + eaf->padding_size_b)/8;
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Converts a EALAYER3 frame to a standard MPEG frame from pre-parsed info */
|
||||
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os) {
|
||||
uint32_t c = 0;
|
||||
int i,j;
|
||||
int expected_bitrate_index, expected_frame_size;
|
||||
|
||||
if (!eaf_0->common_size && !eaf_1->common_size)
|
||||
return 1; /* empty frames, PCM block only */
|
||||
|
||||
/* get bitrate: use max bitrate (320/160) to simplify calcs for now (but some EA-frames use bit reservoir) */
|
||||
expected_bitrate_index = 0x0E;
|
||||
if (eaf_0->mpeg1) { /* 44100=0x414, 48000=0x3C0, 32000=0x5A0 */
|
||||
expected_frame_size = 144l * 320 * 1000l / eaf_0->sample_rate;
|
||||
} else { /* 22050=0x20A, 24000=0x1E0, 16000=0x2D0, 11025=0x414, 12000=0x3C0, 8000=0x5A0 */
|
||||
expected_frame_size = 72l * 160 * 1000l / eaf_0->sample_rate;
|
||||
}
|
||||
|
||||
/* extra checks */
|
||||
if (eaf_0->mpeg1) {
|
||||
if (!eaf_1
|
||||
|| eaf_0->mpeg1 != eaf_1->mpeg1
|
||||
|| eaf_0->version != eaf_1->version
|
||||
|| eaf_0->granule_index == eaf_1->granule_index
|
||||
|| !eaf_0->common_size || !eaf_1->common_size) {
|
||||
VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match at 0x%lx\n", os->offset);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* write MPEG1/2 frame header */
|
||||
w_bits(os, 11, 0x7FF); /* sync */
|
||||
w_bits(os, 2, eaf_0->version_index);
|
||||
w_bits(os, 2, 0x01); /* layer III index */
|
||||
w_bits(os, 1, 1); /* "no CRC" flag */
|
||||
w_bits(os, 4, expected_bitrate_index);
|
||||
w_bits(os, 2, eaf_0->sample_rate_index);
|
||||
w_bits(os, 1, 0); /* padding */
|
||||
w_bits(os, 1, 0); /* private */
|
||||
w_bits(os, 2, eaf_0->channel_mode);
|
||||
w_bits(os, 2, eaf_0->mode_extension);
|
||||
w_bits(os, 1, 1); /* copyrighted */
|
||||
w_bits(os, 1, 1); /* original */
|
||||
w_bits(os, 2, 0); /* emphasis */
|
||||
|
||||
if (eaf_0->mpeg1) {
|
||||
int private_bits = (eaf_0->channels==1 ? 5 : 3);
|
||||
|
||||
/* write MPEG1 side info */
|
||||
w_bits(os, 9, 0); /* main data start (no bit reservoir) */
|
||||
w_bits(os, private_bits, 0);
|
||||
|
||||
for (i = 0; i < eaf_1->channels; i++) {
|
||||
w_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */
|
||||
}
|
||||
for (i = 0; i < eaf_0->channels; i++) { /* granule0 */
|
||||
w_bits(os, 12, eaf_0->main_data_size[i]);
|
||||
w_bits(os, 32, eaf_0->others_1[i]);
|
||||
w_bits(os, 47-32, eaf_0->others_2[i]);
|
||||
}
|
||||
for (i = 0; i < eaf_1->channels; i++) { /* granule1 */
|
||||
w_bits(os, 12, eaf_1->main_data_size[i]);
|
||||
w_bits(os, 32, eaf_1->others_1[i]);
|
||||
w_bits(os, 47-32, eaf_1->others_2[i]);
|
||||
}
|
||||
|
||||
/* write MPEG1 main data */
|
||||
is_0->b_off = eaf_0->data_offset_b;
|
||||
for (i = 0; i < eaf_0->channels; i++) { /* granule0 */
|
||||
for (j = 0; j < eaf_0->main_data_size[i]; j++) {
|
||||
uint32_t c = 0;
|
||||
r_bits(is_0, 1, &c);
|
||||
w_bits(os, 1, c);
|
||||
}
|
||||
}
|
||||
|
||||
is_1->b_off = eaf_1->data_offset_b;
|
||||
for (i = 0; i < eaf_1->channels; i++) { /* granule1 */
|
||||
for (j = 0; j < eaf_1->main_data_size[i]; j++) {
|
||||
r_bits(is_1, 1, &c);
|
||||
w_bits(os, 1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
int private_bits = (eaf_0->channels==1 ? 1 : 2);
|
||||
|
||||
/* write MPEG2 side info */
|
||||
w_bits(os, 8, 0); /* main data start (no bit reservoir) */
|
||||
w_bits(os, private_bits, 0);
|
||||
|
||||
for (i = 0; i < eaf_0->channels; i++) {
|
||||
w_bits(os, 12, eaf_0->main_data_size[i]);
|
||||
w_bits(os, 32, eaf_0->others_1[i]);
|
||||
w_bits(os, 51-32, eaf_0->others_2[i]);
|
||||
}
|
||||
|
||||
/* write MPEG2 main data */
|
||||
is_0->b_off = eaf_0->data_offset_b;
|
||||
for (i = 0; i < eaf_0->channels; i++) {
|
||||
for (j = 0; j < eaf_0->main_data_size[i]; j++) {
|
||||
r_bits(is_0, 1, &c);
|
||||
w_bits(os, 1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* align to closest 8b */
|
||||
if (os->b_off % 8) {
|
||||
int align_bits = 8 - (os->b_off % 8);
|
||||
w_bits(os, align_bits, 0);
|
||||
}
|
||||
|
||||
|
||||
if (os->b_off/8 > expected_frame_size) {
|
||||
VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->offset);
|
||||
//todo bit reservoir! (doesn't seem to affect the output too much)
|
||||
|
||||
//;VGM_LOG("EAFRAME: F0 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_0->version, eaf_0->channels, eaf_0->sample_rate, eaf_0->granule_index, eaf_0->pre_size, eaf_0->common_size, eaf_0->pcm_size, eaf_0->eaframe_size);
|
||||
//;VGM_LOG("EAFRAME: F1 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_1->version, eaf_1->channels, eaf_1->sample_rate, eaf_1->granule_index, eaf_1->pre_size, eaf_1->common_size, eaf_1->pcm_size, eaf_1->eaframe_size);
|
||||
//;VGM_LOGB(os->buf, os->b_off/8, 0);
|
||||
}
|
||||
else {
|
||||
/* fill ancillary data (ignored) */
|
||||
memset(os->buf + os->b_off/8, 0x77, expected_frame_size - os->b_off/8);
|
||||
}
|
||||
|
||||
os->b_off = expected_frame_size*8;
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write PCM block directly to sample buffer (EALayer3 seems to use this as a prefectch of sorts) */
|
||||
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf) {
|
||||
mpeg_custom_stream *ms = data->streams[num_stream];
|
||||
size_t bytes_filled;
|
||||
int i;
|
||||
|
||||
if (!eaf->pcm_size)
|
||||
return 1;
|
||||
|
||||
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
|
||||
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) {
|
||||
VGM_LOG("MPEG EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (eaf->v1_pcm_number) {
|
||||
//;VGM_LOG("pcm discard = %i, number = %i at 0x%lx\n", eaf->v1_pcm_decode_discard, eaf->v1_pcm_number, stream->offset);
|
||||
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%lx\n", eaf->v1_pcm_decode_discard, stream->offset);
|
||||
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%lx\n", eaf->v1_pcm_number, stream->offset);
|
||||
|
||||
/* read + write PCM block samples (always BE) */
|
||||
for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) {
|
||||
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i;
|
||||
int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile);
|
||||
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
|
||||
}
|
||||
ms->samples_filled += eaf->v1_pcm_number;
|
||||
|
||||
/* skip decoded samples as PCM block 'overwrites' them */
|
||||
{
|
||||
size_t decode_to_discard = eaf->v1_pcm_decode_discard;
|
||||
|
||||
//todo should also discard v1_pcm_number, but block layout samples may be exhausted and won't move (maybe new block if offset = new offset detected)
|
||||
/* special meanings */
|
||||
if (decode_to_discard == 576)
|
||||
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
|
||||
|
||||
data->decode_to_discard += decode_to_discard;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* todo V2 (id7 only?) supposed skip modes:
|
||||
* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3
|
||||
*
|
||||
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
|
||||
* D = mode:
|
||||
* E = bytes to discard (mode == 0) or skip (mode == 1 or 2) before outputting the uncompressed samples
|
||||
* (when mode == 3 this is ignored)
|
||||
* F = number of uncompressed sample frames
|
||||
* G = MPEG granule size (can be zero)
|
||||
*
|
||||
* if 0: 576 - E if G == 0 then F
|
||||
* if 1: 576 if G == 0 then F
|
||||
* if 2: 576 if G == 0 then F * 2
|
||||
* if 3: 576
|
||||
*/
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ********************************************* */
|
||||
|
||||
/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */
|
||||
static int r_bits(ealayer3_bitstream * is, int num_bits, uint32_t * value) {
|
||||
off_t off, pos;
|
||||
int i, bit_buf, bit_val;
|
||||
if (num_bits == 0) return 1;
|
||||
if (num_bits > 32 || num_bits < 0 || is->b_off + num_bits > is->bufsize*8) goto fail;
|
||||
|
||||
*value = 0; /* set all bits to 0 */
|
||||
off = is->b_off / 8; /* byte offset */
|
||||
pos = is->b_off % 8; /* bit sub-offset */
|
||||
for (i = 0; i < num_bits; i++) {
|
||||
bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit check for buf */
|
||||
bit_val = (1U << (num_bits-1-i)); /* bit to set in value */
|
||||
|
||||
if (is->buf[off] & bit_buf) /* is bit in buf set? */
|
||||
*value |= bit_val; /* set bit */
|
||||
|
||||
pos++;
|
||||
if (pos%8 == 0) { /* new byte starts */
|
||||
pos = 0;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
is->b_off += num_bits;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */
|
||||
static int w_bits(ealayer3_bitstream * os, int num_bits, uint32_t value) {
|
||||
off_t off, pos;
|
||||
int i, bit_val, bit_buf;
|
||||
if (num_bits == 0) return 1;
|
||||
if (num_bits > 32 || num_bits < 0 || os->b_off + num_bits > os->bufsize*8) goto fail;
|
||||
|
||||
|
||||
off = os->b_off / 8; /* byte offset */
|
||||
pos = os->b_off % 8; /* bit sub-offset */
|
||||
for (i = 0; i < num_bits; i++) {
|
||||
bit_val = (1U << (num_bits-1-i)); /* bit check for value */
|
||||
bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit to set in buf */
|
||||
|
||||
if (value & bit_val) /* is bit in val set? */
|
||||
os->buf[off] |= bit_buf; /* set bit */
|
||||
else
|
||||
os->buf[off] &= ~bit_buf; /* unset bit */
|
||||
|
||||
pos++;
|
||||
if (pos%8 == 0) { /* new byte starts */
|
||||
pos = 0;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
os->b_off += num_bits;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -9,26 +9,18 @@
|
||||
|
||||
/* TODO list for custom decoder
|
||||
* - don't force channel param and get them from frame headers for some types (for MPEG_STANDARD)
|
||||
* - use one stream per channel and advance streams as channels are done in case of streams like 2ch+1ch+1ch+2ch (not seen)
|
||||
* (would also need to change sample_buffer copy)
|
||||
* - improve validation of channels/samples_per_frame between streams
|
||||
* - improve decoded samples to sample buffer copying (very picky with sizes)
|
||||
* - use mpg123 stream_buffer, with flags per stream, and call copy to sample_buffer when all streams have some samples
|
||||
* (so it could handle interleaved VBR frames).
|
||||
* - AHX type 8 encryption
|
||||
* - test encoder delays
|
||||
* - improve error handling
|
||||
* - in case of streams like 2ch+1ch+1ch+2ch (not seen) use one stream per channel and advance streams as channels are done
|
||||
* - validate of channels between streams
|
||||
* - fsb garbage in the first frames
|
||||
*/
|
||||
|
||||
/* mostly arbitrary max values */
|
||||
#define MPEG_DATA_BUFFER_SIZE 0x1000
|
||||
#define MPEG_MAX_CHANNELS 16
|
||||
#define MPEG_MAX_STREAM_FRAMES 10
|
||||
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1) */
|
||||
|
||||
static mpg123_handle * init_mpg123_handle();
|
||||
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream);
|
||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, int num_stream);
|
||||
|
||||
|
||||
/* Inits regular MPEG */
|
||||
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
|
||||
@ -111,7 +103,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
goto fail;
|
||||
|
||||
|
||||
/* reinit, to ignore the reading we've done so far */
|
||||
/* reinit, to ignore the reading done */
|
||||
mpg123_open_feed(main_m);
|
||||
}
|
||||
|
||||
@ -123,10 +115,9 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* Init custom MPEG, with given type and config. */
|
||||
/* Init custom MPEG, with given type and config */
|
||||
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
int stream_frames = 1;
|
||||
int i, ok;
|
||||
|
||||
/* init codec */
|
||||
@ -148,35 +139,30 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
|
||||
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
|
||||
default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break;
|
||||
}
|
||||
if (!ok)
|
||||
goto fail;
|
||||
|
||||
if (channels <= 0 || channels > MPEG_MAX_CHANNELS) goto fail;
|
||||
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
|
||||
if (channels < data->channels_per_frame) goto fail;
|
||||
|
||||
/* init stream decoders (separate as MPEG frames may need N previous frames from their stream to decode) */
|
||||
data->ms_size = channels / data->channels_per_frame;
|
||||
data->ms = calloc(sizeof(mpg123_handle *), data->ms_size);
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
data->ms[i] = init_mpg123_handle();
|
||||
if (!data->ms[i]) goto fail;
|
||||
|
||||
/* init streams */
|
||||
data->streams_size = channels / data->channels_per_frame;
|
||||
data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream*));
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
|
||||
data->streams[i]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/
|
||||
if (!data->streams[i]->m) goto fail;
|
||||
|
||||
/* size could be any value */
|
||||
data->streams[i]->output_buffer_size = sizeof(sample) * data->channels_per_frame * data->samples_per_frame;
|
||||
data->streams[i]->output_buffer = calloc(data->streams[i]->output_buffer_size, sizeof(uint8_t));
|
||||
if (!data->streams[i]->output_buffer) goto fail;
|
||||
}
|
||||
|
||||
if (stream_frames > MPEG_MAX_STREAM_FRAMES) goto fail;
|
||||
|
||||
/* init stream buffer, big enough for one stream and N frames at a time (will be copied to sample buffer) */
|
||||
data->stream_buffer_size = sizeof(sample) * data->channels_per_frame * stream_frames * data->samples_per_frame;
|
||||
data->stream_buffer = calloc(sizeof(uint8_t), data->stream_buffer_size);
|
||||
if (!data->stream_buffer) goto fail;
|
||||
|
||||
/* init sample buffer, big enough for all streams/channels and N frames at a time */
|
||||
data->sample_buffer_size = sizeof(sample) * channels * stream_frames * data->samples_per_frame;
|
||||
data->sample_buffer = calloc(sizeof(uint8_t), data->sample_buffer_size);
|
||||
if (!data->sample_buffer) goto fail;
|
||||
|
||||
|
||||
/* write output */
|
||||
config->interleave = data->config.interleave; /* for FSB */
|
||||
@ -291,188 +277,200 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * dat
|
||||
|
||||
|
||||
/**
|
||||
* Decode custom MPEG, allowing support for: single frames, interleave, mutant frames, multiple streams
|
||||
* (1 frame = 1/2ch so Nch = 2ch*N/2 or 1ch*N or 2ch+1ch+2ch+...), etc.
|
||||
* Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc.
|
||||
*
|
||||
* Decodes samples per each stream and muxes them into a single internal buffer before copying to outbuf
|
||||
* (to make sure channel samples are orderly copied between decode_mpeg calls).
|
||||
* decode_mpeg_custom_stream does the main decoding, while this handles layout and copying samples to output.
|
||||
* 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 * outbuf, int32_t samples_to_do, int channels) {
|
||||
int samples_done = 0, bytes_max, bytes_to_copy;
|
||||
int i, samples_done = 0;
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
int samples_to_copy = -1;
|
||||
|
||||
if (data->bytes_used_in_sample_buffer < data->bytes_in_sample_buffer) {
|
||||
/* copy remaining samples */
|
||||
bytes_to_copy = data->bytes_in_sample_buffer - data->bytes_used_in_sample_buffer;
|
||||
bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels;
|
||||
if (bytes_to_copy > bytes_max)
|
||||
bytes_to_copy = bytes_max;
|
||||
memcpy((uint8_t*)(outbuf+samples_done*channels), data->sample_buffer + data->bytes_used_in_sample_buffer, bytes_to_copy);
|
||||
|
||||
/* update samples copied */
|
||||
data->bytes_used_in_sample_buffer += bytes_to_copy;
|
||||
samples_done += bytes_to_copy / sizeof(sample) / channels;
|
||||
/* find max to copy from all streams (equal for all channels) */
|
||||
for (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;
|
||||
}
|
||||
else {
|
||||
/* fill the internal sample buffer */
|
||||
int i;
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
|
||||
* With multiple offsets it's expected offsets are set up pointing to the first frame of each stream. */
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
switch(data->type) {
|
||||
//case MPEG_LYN:
|
||||
case MPEG_FSB:
|
||||
case MPEG_XVAG:
|
||||
case MPEG_P3D:
|
||||
/* multiple offsets, decodes 1 frame per stream until reaching interleave/block_size and skips it */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
||||
break;
|
||||
|
||||
//case MPEG_EA: //?
|
||||
case MPEG_AWC:
|
||||
/* consecutive streams: multiple offsets, decodes 1 frame per stream */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
||||
break;
|
||||
/* 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;
|
||||
|
||||
default:
|
||||
/* N frames: single offset, decodes all N frames per stream (sample buffer must be big enough for N) */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[0], data, data->ms[i], channels, i);
|
||||
break;
|
||||
for (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;
|
||||
}
|
||||
|
||||
|
||||
if (samples_to_copy > 0) {
|
||||
/* copy stream's samples to outbuf */
|
||||
if (samples_to_copy > samples_to_do - samples_done)
|
||||
samples_to_copy = samples_to_do - samples_done;
|
||||
|
||||
/* mux streams channels (1/2ch) to outbuf (Nch) (ex. 6ch: samples from 2ch+2ch+2ch) */
|
||||
for (i = 0; i < data->streams_size; i++) {
|
||||
mpeg_custom_stream *ms = data->streams[i];
|
||||
int channels_frame = data->channels_per_frame;
|
||||
int fch, s;
|
||||
|
||||
for (fch = 0; fch < channels_frame; fch++) {
|
||||
for (s = 0; s < samples_to_copy; s++) {
|
||||
size_t bytes_used = sizeof(sample)*ms->samples_used*channels_frame;
|
||||
off_t in_offset = sizeof(sample)*s*channels_frame + sizeof(sample)*fch;
|
||||
off_t out_offset = s*channels + i*channels_frame + fch;
|
||||
|
||||
memcpy((uint8_t*)(outbuf+samples_done*channels + out_offset),
|
||||
ms->output_buffer+bytes_used + in_offset,
|
||||
sizeof(sample));
|
||||
}
|
||||
}
|
||||
|
||||
ms->samples_used += samples_to_copy;
|
||||
}
|
||||
|
||||
/* discard (for looping): 'remove' decoded samples from the buffer */
|
||||
if (data->samples_to_discard) {
|
||||
size_t bytes_to_discard = data->samples_to_discard * sizeof(sample) * channels;
|
||||
samples_done += samples_to_copy;
|
||||
}
|
||||
else {
|
||||
/* decode more into stream sample buffers */
|
||||
|
||||
/* 'remove' all buffer at most */
|
||||
if (bytes_to_discard > data->bytes_in_sample_buffer)
|
||||
bytes_to_discard = data->bytes_in_sample_buffer;
|
||||
/* 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 (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);
|
||||
|
||||
/* pretend the samples were used up and readjust discard */
|
||||
data->bytes_used_in_sample_buffer = bytes_to_discard;
|
||||
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
|
||||
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 and muxes samples into a intermediate buffer and moves the stream offsets. */
|
||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream) {
|
||||
size_t bytes_done = 0;
|
||||
/* 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) {
|
||||
size_t bytes_done = 0, bytes_filled, samples_filled;
|
||||
size_t stream_size = get_streamfile_size(stream->streamfile);
|
||||
int rc, ok;
|
||||
mpeg_custom_stream *ms = data->streams[num_stream];
|
||||
|
||||
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used);
|
||||
|
||||
/* wait until samples are depleted, so buffers don't grow too big */
|
||||
if (ms->samples_filled - ms->samples_used > 0) {
|
||||
return; /* common with multi-streams, as they decode at different rates) */
|
||||
}
|
||||
|
||||
/* no samples = reset the counters */
|
||||
ms->samples_filled = 0;
|
||||
ms->samples_used = 0;
|
||||
|
||||
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
|
||||
if (!data->buffer_full && stream->offset >= stream_size) {
|
||||
VGM_LOG("MPEG: EOF found but more data is requested\n");
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
|
||||
/* decode samples from one full-frame (as N data-frames = 1 full-frame) before exiting (to orderly copy to sample buffer) */
|
||||
do {
|
||||
//VGM_LOG("MPEG: new step of stream %i @ 0x%08lx\n", num_stream, stream->offset);
|
||||
|
||||
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
|
||||
if (stream->offset >= stream_size) {
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break; /* continue with other streams */
|
||||
/* read more raw data (could fill the sample buffer too in some cases, namely EALayer3) */
|
||||
if (!data->buffer_full) {
|
||||
//;VGM_LOG("MPEG: reading more raw data\n");
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
|
||||
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
|
||||
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
|
||||
}
|
||||
if (!ok) {
|
||||
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
|
||||
goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */
|
||||
}
|
||||
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", data->bytes_in_buffer, stream->offset);
|
||||
|
||||
/* read more raw data */
|
||||
if (!data->buffer_full) {
|
||||
//VGM_LOG("MPEG: reading more raw data\n");
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_parse_frame_ealayer3(stream, data); break;
|
||||
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
|
||||
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
|
||||
}
|
||||
/* error/EOF, mpg123 can resync in some cases but custom MPEGs wouldn't need that */
|
||||
if (!ok || !data->bytes_in_buffer) {
|
||||
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break; /* continue with other streams */
|
||||
}
|
||||
//VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset off=%lx\n", data->bytes_in_buffer, stream->offset);
|
||||
|
||||
/* parse frame may not touch the buffer (only move offset, or fill the sample buffer) */
|
||||
if (data->bytes_in_buffer) {
|
||||
data->buffer_full = 1;
|
||||
data->buffer_used = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//TODO: in case of EALayer3 with "PCM flag" must put them in buffer
|
||||
if (ea_pcm_flag) {
|
||||
/* write some samples to data->stream_buffer *before* decoding this frame */
|
||||
bytes_done += read_streamfile(data->stream_buffer,offset,num_pcm_samples,stream->streamfile);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
//TODO: FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
|
||||
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
|
||||
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
||||
|
||||
data->buffer_full = 0;
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
||||
if (!data->buffer_used) {
|
||||
//VGM_LOG("MPEG: feed new data and get samples \n");
|
||||
rc = mpg123_decode(m,
|
||||
data->buffer, data->bytes_in_buffer,
|
||||
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
}
|
||||
else {
|
||||
//VGM_LOG("MPEG: get samples from old data\n");
|
||||
rc = mpg123_decode(m,
|
||||
NULL,0,
|
||||
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
||||
&bytes_done);
|
||||
}
|
||||
|
||||
/* not enough raw data, request more */
|
||||
if (rc == MPG123_NEED_MORE) {
|
||||
//VGM_LOG("MPEG: need more raw data to get samples\n");
|
||||
/* (apparently mpg123 can give bytes and request more at the same time and may mess up some calcs, when/how? */
|
||||
VGM_ASSERT(bytes_done > 0, "MPEG: bytes done but decoder requests more data\n");
|
||||
data->buffer_full = 0;
|
||||
continue;
|
||||
}
|
||||
//VGM_LOG("MPEG: got samples, bytes_done=0x%x (fsbs=0x%x)\n", bytes_done, data->stream_buffer_size);
|
||||
|
||||
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
|
||||
/* copy decoded full-frame to intermediate sample buffer, muxing channels
|
||||
* (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */
|
||||
{
|
||||
size_t samples_done;
|
||||
size_t sz = sizeof(sample);
|
||||
int channels_f = data->channels_per_frame;
|
||||
int fch, i;
|
||||
|
||||
samples_done = bytes_done / sz / channels_f;
|
||||
for (fch = 0; fch < channels_f; fch++) { /* channels inside the frame */
|
||||
for (i = 0; i < samples_done; i++) { /* decoded samples */
|
||||
off_t in_offset = sz*i*channels_f + sz*fch;
|
||||
off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch);
|
||||
memcpy(data->sample_buffer + out_offset, data->stream_buffer + in_offset, sz);
|
||||
}
|
||||
}
|
||||
|
||||
data->bytes_in_sample_buffer += bytes_done;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
|
||||
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
|
||||
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
||||
|
||||
data->buffer_full = 0;
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
|
||||
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
||||
if (!data->buffer_used) {
|
||||
//;VGM_LOG("MPEG: feed new data and get samples \n");
|
||||
rc = mpg123_decode(ms->m,
|
||||
data->buffer, data->bytes_in_buffer,
|
||||
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
}
|
||||
else {
|
||||
//;VGM_LOG("MPEG: get samples from old data\n");
|
||||
rc = mpg123_decode(ms->m,
|
||||
NULL, 0,
|
||||
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
|
||||
&bytes_done);
|
||||
}
|
||||
samples_filled = (bytes_done / sizeof(sample) / data->channels_per_frame);
|
||||
|
||||
/* for EALayer3, that discards decoded samples and writes PCM blocks instead */
|
||||
if (data->decode_to_discard) {
|
||||
size_t bytes_to_discard = 0;
|
||||
size_t decode_to_discard = data->decode_to_discard;
|
||||
if (decode_to_discard > samples_filled)
|
||||
decode_to_discard = samples_filled;
|
||||
bytes_to_discard = sizeof(sample)*decode_to_discard*data->channels_per_frame;
|
||||
|
||||
bytes_done -= bytes_to_discard;
|
||||
data->decode_to_discard -= decode_to_discard;
|
||||
ms->samples_used += decode_to_discard;
|
||||
}
|
||||
|
||||
/* if no decoding was done bytes_done will be zero */
|
||||
ms->samples_filled += samples_filled;
|
||||
|
||||
/* not enough raw data, set flag to request more next time
|
||||
* (but only with empty mpg123 buffer, EA blocks wait for all samples decoded before advancing blocks) */
|
||||
if (!bytes_done && rc == MPG123_NEED_MORE) {
|
||||
//;VGM_LOG("MPEG: need more raw data to get samples (bytest_done=%x)\n", bytes_done);
|
||||
data->buffer_full = 0;
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
/* 0-fill but continue with other streams */
|
||||
bytes_filled = ms->samples_filled*data->channels_per_frame*sizeof(sample);
|
||||
memset(ms->output_buffer + bytes_filled, 0, ms->output_buffer_size - bytes_filled);
|
||||
ms->samples_filled = (ms->output_buffer_size / data->channels_per_frame / sizeof(sample));
|
||||
}
|
||||
|
||||
|
||||
@ -489,13 +487,12 @@ void free_mpeg(mpeg_codec_data *data) {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_delete(data->ms[i]);
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_delete(data->streams[i]->m);
|
||||
free(data->streams[i]->output_buffer);
|
||||
free(data->streams[i]);
|
||||
}
|
||||
free(data->ms);
|
||||
|
||||
free(data->stream_buffer);
|
||||
free(data->sample_buffer);
|
||||
free(data->streams);
|
||||
}
|
||||
|
||||
free(data->buffer);
|
||||
@ -520,15 +517,15 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
||||
/* re-start from 0 */
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
|
||||
data->streams[i]->samples_filled = 0;
|
||||
data->streams[i]->samples_used = 0;
|
||||
}
|
||||
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
/* initial delay */
|
||||
data->samples_to_discard = data->skip_samples;
|
||||
data->samples_to_discard = data->skip_samples; /* initial delay */
|
||||
data->decode_to_discard = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,13 +542,14 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
else {
|
||||
int i;
|
||||
/* re-start from 0 */
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
|
||||
data->streams[i]->samples_filled = 0;
|
||||
data->streams[i]->samples_used = 0;
|
||||
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
|
||||
}
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
/* manually discard samples, since we don't really know the correct offset */
|
||||
data->samples_to_discard = num_sample;
|
||||
@ -560,8 +558,35 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
|
||||
data->buffer_full = 0;
|
||||
data->buffer_used = 0;
|
||||
data->decode_to_discard = 0;
|
||||
}
|
||||
|
||||
/* resets mpg123 decoder and its internals (with mpg123_open_feed as mpg123_feedseek won't work) */
|
||||
void flush_mpeg(mpeg_codec_data * data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
/* re-start from 0 */
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_open_feed(data->streams[i]->m);
|
||||
data->streams[i]->samples_filled = 0;
|
||||
data->streams[i]->samples_used = 0;
|
||||
}
|
||||
|
||||
data->samples_to_discard = data->skip_samples; /* initial delay */
|
||||
data->decode_to_discard = 0;
|
||||
}
|
||||
|
||||
data->bytes_in_buffer = 0;
|
||||
data->buffer_full = 0;
|
||||
data->buffer_used = 0;
|
||||
}
|
||||
|
||||
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) */
|
||||
@ -583,15 +608,15 @@ long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
|
||||
}
|
||||
}
|
||||
|
||||
/* disables/enables stderr output, useful for MPEG known to contain recoverable errors */
|
||||
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
|
||||
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
||||
if (!data->custom) {
|
||||
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_param(data->streams[i]->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ typedef struct {
|
||||
int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info);
|
||||
|
||||
int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
|
||||
//int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
|
||||
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
|
||||
|
||||
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
//int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
|
||||
|
||||
#endif/* VGM_USE_MPEG */
|
||||
|
||||
|
@ -469,6 +469,7 @@ static const coding_info coding_info_list[] = {
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
{coding_MPEG_custom, "Custom MPEG Audio"},
|
||||
{coding_MPEG_ealayer3, "EALayer3"},
|
||||
{coding_MPEG_layer1, "MPEG Layer I Audio (MP1)"},
|
||||
{coding_MPEG_layer2, "MPEG Layer II Audio (MP2)"},
|
||||
{coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"},
|
||||
|
@ -5,6 +5,7 @@
|
||||
/* set up for the block at the given offset */
|
||||
void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
int i;
|
||||
int new_schl = 0;
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
uint32_t id;
|
||||
size_t file_size, block_size = 0, block_samples;
|
||||
@ -58,6 +59,10 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
/* Usually there is padding between SCEl and SCHl too (aligned to 0x80) */
|
||||
}
|
||||
|
||||
if (id == 0x5343486C) { /* "SCHl", new subfile */
|
||||
new_schl = 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -114,6 +119,25 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */
|
||||
case coding_MPEG_custom:
|
||||
case coding_MPEG_layer1:
|
||||
case coding_MPEG_layer2:
|
||||
case coding_MPEG_layer3:
|
||||
case coding_MPEG_ealayer3:
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = read_32bit(block_offset + 0x0C,streamFile);
|
||||
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
|
||||
}
|
||||
|
||||
/* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */
|
||||
if (new_schl) {
|
||||
flush_mpeg(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */
|
||||
default:
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
|
@ -1393,6 +1393,10 @@
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_custom_utils_ahx.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_custom_utils_ealayer3.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_decoder.c"
|
||||
|
@ -426,6 +426,7 @@
|
||||
<ClCompile Include="coding\mc3_decoder.c" />
|
||||
<ClCompile Include="coding\mpeg_custom_utils.c" />
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ahx.c" />
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ealayer3.c" />
|
||||
<ClCompile Include="coding\mpeg_decoder.c" />
|
||||
<ClCompile Include="coding\msadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\nds_procyon_decoder.c" />
|
||||
|
@ -814,6 +814,9 @@
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ahx.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ealayer3.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mpeg_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -6,7 +6,7 @@
|
||||
VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channel_count = 1, loop_flag = 0;
|
||||
int channel_count = 1, loop_flag = 0, type;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if ( !check_extensions(streamFile, "ahx") ) goto fail;
|
||||
@ -22,8 +22,8 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
/* check for encoding type (0x10 is AHX for DC with bigger frames, 0x11 is AHX, 0x0N are ADX) */
|
||||
if (read_8bit(0x04,streamFile) != 0x10 &&
|
||||
read_8bit(0x04,streamFile) != 0x11) goto fail;
|
||||
type = read_8bit(0x04,streamFile);
|
||||
if (type != 0x10 && type != 0x11) goto fail;
|
||||
|
||||
/* check for frame size (0 for AHX) */
|
||||
if (read_8bit(0x05,streamFile) != 0) goto fail;
|
||||
@ -52,6 +52,16 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */
|
||||
cfg.cri_type = type;
|
||||
|
||||
if (cfg.encryption) {
|
||||
uint8_t keybuf[6];
|
||||
if (read_key_file(keybuf, 6, streamFile)) {
|
||||
cfg.cri_key1 = get_16bitBE(keybuf+0);
|
||||
cfg.cri_key2 = get_16bitBE(keybuf+2);
|
||||
cfg.cri_key3 = get_16bitBE(keybuf+4);
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg);
|
||||
|
@ -46,6 +46,7 @@
|
||||
#define EA_CODEC2_MT5 0x16
|
||||
#define EA_CODEC2_EALAYER3 0x17
|
||||
#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */
|
||||
//todo #define EA_CODEC2_ATRAC9 0x-- /* supposedly exists */
|
||||
|
||||
#define EA_MAX_CHANNELS 6
|
||||
|
||||
@ -86,7 +87,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
|
||||
/* check extension; exts don't seem enforced by EA's tools, but usually:
|
||||
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
|
||||
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm"))
|
||||
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,ast"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -115,7 +116,7 @@ fail:
|
||||
|
||||
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
|
||||
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
off_t start_offset, header_offset, offset;
|
||||
off_t start_offset, header_offset, offset, table_offset;
|
||||
size_t header_size;
|
||||
ea_header ea;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
@ -128,7 +129,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
|
||||
if (!check_extensions(streamFile,"bnk,sdt,mus"))
|
||||
goto fail;
|
||||
VGM_LOG("1\n");
|
||||
|
||||
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */
|
||||
read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */
|
||||
@ -137,7 +138,7 @@ VGM_LOG("1\n");
|
||||
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
|
||||
else
|
||||
goto fail;
|
||||
VGM_LOG("2\n");
|
||||
|
||||
/* use header size as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
|
||||
read_32bit = read_32bitBE;
|
||||
@ -152,28 +153,34 @@ VGM_LOG("2\n");
|
||||
/* check multi-streams */
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
VGM_LOG("3\n");
|
||||
|
||||
switch(bnk_version) {
|
||||
case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */
|
||||
table_offset = 0x0c;
|
||||
|
||||
header_size = read_32bit(offset + 0x08,streamFile); /* full size */
|
||||
header_offset = offset + 0x0c + 0x04*(target_stream-1) + read_32bit(offset + 0x0c + 0x04*(target_stream-1),streamFile);
|
||||
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
|
||||
break;
|
||||
|
||||
case 0x04: /* mid (last used in PSX banks) */
|
||||
case 0x05: /* late (generated by sx.exe ~v2+) */
|
||||
/* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */
|
||||
table_offset = 0x14;
|
||||
if (read_32bit(offset + table_offset,streamFile) == 0x00)
|
||||
table_offset += 0x4; /* MOH Heroes 2 PSP has an extra empty field, not sure why */
|
||||
|
||||
header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */
|
||||
header_offset = offset + 0x14 + 0x04*(target_stream-1) + read_32bit(offset + 0x14 + 0x04*(target_stream-1),streamFile);
|
||||
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA BNK: unknown version %x\n", bnk_version);
|
||||
goto fail;
|
||||
}
|
||||
VGM_LOG("4\n");
|
||||
|
||||
if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset))
|
||||
goto fail;
|
||||
VGM_LOG("5\n");
|
||||
|
||||
/* fix absolute offsets so it works in next funcs */
|
||||
if (offset) {
|
||||
for (i = 0; i < ea.channels; i++) {
|
||||
@ -338,7 +345,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
|
||||
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
|
||||
VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -609,7 +616,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
|
||||
VGM_LOG("EA SCHl: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -653,7 +660,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break;
|
||||
case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown default version for platform 0x%02x\n", ea->platform);
|
||||
VGM_LOG("EA SCHl: unknown default version for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -667,7 +674,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed
|
||||
case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown default codec1 for platform 0x%02x\n", ea->platform);
|
||||
VGM_LOG("EA SCHl: unknown default codec1 for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -682,7 +689,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break;
|
||||
case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec1 0x%02x\n", ea->codec1);
|
||||
VGM_LOG("EA SCHl: unknown codec1 0x%02x\n", ea->codec1);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -701,7 +708,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break;
|
||||
case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown default codec2 for platform 0x%02x\n", ea->platform);
|
||||
VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -722,7 +729,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
||||
case EA_PLATFORM_PSP: ea->sample_rate = 22050; break;
|
||||
//case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000)
|
||||
default:
|
||||
VGM_LOG("EA: unknown default sample rate for platform 0x%02x\n", ea->platform);
|
||||
VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -510,6 +510,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
|
||||
#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) {
|
||||
@ -698,6 +699,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
|
||||
#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) {
|
||||
@ -979,6 +981,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
#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:
|
||||
@ -1632,6 +1635,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG_custom:
|
||||
case coding_MPEG_ealayer3:
|
||||
case coding_MPEG_layer1:
|
||||
case coding_MPEG_layer2:
|
||||
case coding_MPEG_layer3:
|
||||
@ -1867,6 +1871,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
|
||||
#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) {
|
||||
|
@ -164,6 +164,7 @@ typedef enum {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
coding_MPEG_custom, /* MPEG audio with custom features (MDCT-based) */
|
||||
coding_MPEG_ealayer3, /* EALayer3, custom MPEG frames */
|
||||
coding_MPEG_layer1, /* MP1 MPEG audio (MDCT-based) */
|
||||
coding_MPEG_layer2, /* MP2 MPEG audio (MDCT-based) */
|
||||
coding_MPEG_layer3, /* MP3 MPEG audio (MDCT-based) */
|
||||
@ -795,6 +796,7 @@ typedef enum {
|
||||
VORBIS_WWISE, /* many variations (custom setup, headers and data) */
|
||||
VORBIS_OGL, /* custom packet headers */
|
||||
VORBIS_SK /* "OggS" replaced by "SK" */
|
||||
//VORBIS_LYN /* two interleaved Ogg (including setup, duplicated) */
|
||||
} vorbis_custom_t;
|
||||
|
||||
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
|
||||
@ -832,6 +834,7 @@ typedef struct {
|
||||
|
||||
uint8_t * buffer; /* internal raw data buffer */
|
||||
size_t buffer_size;
|
||||
|
||||
size_t samples_to_discard; /* for looping purposes */
|
||||
int samples_full; /* flag, samples available in vorbis buffers */
|
||||
|
||||
@ -848,8 +851,8 @@ typedef struct {
|
||||
} vorbis_custom_codec_data;
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* Custom MPEG modes, mostly differing in the data layout */
|
||||
typedef enum {
|
||||
MPEG_STANDARD, /* 1 stream */
|
||||
@ -858,21 +861,37 @@ typedef enum {
|
||||
MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */
|
||||
MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */
|
||||
MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */
|
||||
MPEG_EAL31, /* EALayer3 v1, custom frames */
|
||||
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), altered custom frames */
|
||||
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), altered custom frames */
|
||||
MPEG_EAL31, /* EALayer3 v1, custom frames with v1 header */
|
||||
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), custom frames with v2 header */
|
||||
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), custom frames with v2 header */
|
||||
MPEG_LYN, /* N streams of fixed interleave */
|
||||
MPEG_AWC /* N streams in absolute offsets (consecutive) */
|
||||
} mpeg_custom_t;
|
||||
|
||||
/* config for the above modes */
|
||||
typedef struct {
|
||||
int channels; /* max channels */
|
||||
int fsb_padding; /* fsb padding mode */
|
||||
int chunk_size; /* size of a data portion */
|
||||
int interleave; /* size of stream interleave */
|
||||
int encryption; /* encryption mode */
|
||||
/* for AHX */
|
||||
int cri_type;
|
||||
uint16_t cri_key1;
|
||||
uint16_t cri_key2;
|
||||
uint16_t cri_key3;
|
||||
} mpeg_custom_config;
|
||||
|
||||
/* represents a single MPEG stream */
|
||||
typedef struct {
|
||||
mpg123_handle *m; /* MPEG decoder */
|
||||
|
||||
uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */
|
||||
size_t output_buffer_size;
|
||||
size_t samples_filled; /* data in the buffer (in samples) */
|
||||
size_t samples_used; /* data extracted from the buffer */
|
||||
} mpeg_custom_stream;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer; /* internal raw data buffer */
|
||||
size_t buffer_size;
|
||||
@ -891,19 +910,12 @@ typedef struct {
|
||||
mpeg_custom_t type; /* mpeg subtype */
|
||||
mpeg_custom_config config; /* config depending on the mode */
|
||||
|
||||
mpg123_handle **ms; /* custom MPEG decoder array */
|
||||
size_t ms_size;
|
||||
mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */
|
||||
size_t streams_size;
|
||||
|
||||
uint8_t *stream_buffer; /* contains samples from N frames of one stream */
|
||||
size_t stream_buffer_size;
|
||||
uint8_t *sample_buffer; /* contains samples from N frames of all streams/channels */
|
||||
size_t sample_buffer_size;
|
||||
size_t bytes_in_sample_buffer;
|
||||
size_t bytes_used_in_sample_buffer;
|
||||
|
||||
|
||||
size_t samples_to_discard; /* for custom mpeg looping */
|
||||
size_t skip_samples; /* base encoder delay */
|
||||
size_t samples_to_discard; /* for custom mpeg looping */
|
||||
size_t decode_to_discard; /* for EALayer3, that discards decoded samples and writes PCM blocks in their place */
|
||||
|
||||
} mpeg_codec_data;
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user