mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 19:19:16 +01:00
Merge pull request #149 from bnnm/master
MPC, VXN, SNS/SPS, OGG, FLX, MT
This commit is contained in:
commit
d2f48b2066
11
README.md
11
README.md
@ -65,13 +65,13 @@ supports Winamp plugins you may also use ```in_vgmstream.dll``` instead.
|
||||
|
||||
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts,
|
||||
you need to manually fix it by going to **options > plugins > input > vgmstream**
|
||||
and in the "priority filetypes" put: ```awc,ckd,fsb,genh,msf,p3d,rak,scd,xvag```
|
||||
and in the "priority filetypes" put: ```ahx,asf,awc,ckd,fsb,genh,msf,p3d,rak,scd,txth,xvag```
|
||||
|
||||
### foo_input_vgmstream
|
||||
### foo_input_vgmstream
|
||||
Every file should be installed automatically by the .fb2k-component bundle.
|
||||
|
||||
### Audacious plugin
|
||||
Needs to be manually built. Instructions can be found in the source files.
|
||||
### Audacious plugin
|
||||
Needs to be manually built. Instructions can be found in the build document.
|
||||
|
||||
## Supported codec types
|
||||
Quick list of codecs vgmstream supports, including many obscure ones that
|
||||
@ -108,9 +108,10 @@ are used in few games.
|
||||
- InterPlay ACM
|
||||
- Visual Art's NWA
|
||||
- CRI HCA
|
||||
- Electronic Arts MicroTalk a.k.a. UTK or UMT
|
||||
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
|
||||
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
|
||||
- Electronic Arts EALayer3 v1
|
||||
- Electronic Arts EALayer3
|
||||
- ITU-T G.722.1 (Polycom Siren 7)
|
||||
- ITU-T G.722.1 annex C (Polycom Siren 14)
|
||||
- ITU G.719 annex B (Polycom Siren 22)
|
||||
|
@ -116,6 +116,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("EXA", exa);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FAG", fag);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FFW", ffw);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FILP", filp);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FLX", flx);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FSB", fsb);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("FWAV", fwav);
|
||||
|
||||
@ -185,6 +186,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("MIC", mic);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MIHB", mihb);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MNSTR", mnstr);
|
||||
//"mp4", //common
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MPC", mpc);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MPDSP", mpdsp);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MPDS", mpds);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("MSA", msa);
|
||||
@ -277,6 +279,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("SMPL", smpl);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SND", snd);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SNDS", snds);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SNG", sng);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SNR", snr);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SNS", sns);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SNU", snu);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("SPD", spd);
|
||||
@ -328,6 +331,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("VOI", voi);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("VPK", vpk);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("VS", vs);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("VSF", vsf);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("VXN", vxn);
|
||||
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("WAA", waa);
|
||||
VGMSTREAM_DECLARE_FILE_TYPE("WAC", wac);
|
||||
|
@ -135,6 +135,14 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
/* mc3_decoder */
|
||||
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
|
||||
/* ea_mt_decoder*/
|
||||
ea_mt_codec_data *init_ea_mt(int channel_count, int type);
|
||||
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void reset_ea_mt(VGMSTREAM * vgmstream);
|
||||
void flush_ea_mt(VGMSTREAM *vgmstream);
|
||||
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample);
|
||||
void free_ea_mt(ea_mt_codec_data *data);
|
||||
|
||||
/* hca_decoder */
|
||||
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_hca(VGMSTREAM *vgmstream);
|
||||
@ -169,7 +177,6 @@ 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);
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_G7221
|
||||
|
577
src/coding/ea_mt_decoder.c
Normal file
577
src/coding/ea_mt_decoder.c
Normal file
@ -0,0 +1,577 @@
|
||||
#include "coding.h"
|
||||
|
||||
/* Decodes EA MicroTalk (speech codec) using a copied utkencode lib.
|
||||
* EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same
|
||||
* with different encoding parameters. Later revisions may have PCM blocks (rare).
|
||||
*
|
||||
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
|
||||
* Info: http://wiki.niotso.org/UTK
|
||||
*
|
||||
* The following tries to follow the original code as close as possible, with minimal changes for vgmstream
|
||||
*/
|
||||
|
||||
/* ************************************************************************************************* */
|
||||
#define UTK_BUFFER_SIZE 0x4000
|
||||
|
||||
//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define UTK_MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define UTK_MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max)
|
||||
|
||||
|
||||
/* Note: This struct assumes a member alignment of 4 bytes.
|
||||
** This matters when pitch_lag > 216 on the first subframe of any given frame. */
|
||||
typedef struct UTKContext {
|
||||
uint8_t buffer[UTK_BUFFER_SIZE]; //vgmstream extra
|
||||
STREAMFILE * streamfile; //vgmstream extra
|
||||
off_t offset; //vgmstream extra
|
||||
int samples_filled; //vgmstream extra
|
||||
//FILE *fp; //vgmstream extra
|
||||
const uint8_t *ptr, *end;
|
||||
int parsed_header;
|
||||
unsigned int bits_value;
|
||||
int bits_count;
|
||||
int reduced_bw;
|
||||
int multipulse_thresh;
|
||||
float fixed_gains[64];
|
||||
float rc[12];
|
||||
float synth_history[12];
|
||||
float adapt_cb[324];
|
||||
float decompressed_frame[432];
|
||||
} UTKContext;
|
||||
|
||||
enum {
|
||||
MDL_NORMAL = 0,
|
||||
MDL_LARGEPULSE = 1
|
||||
};
|
||||
|
||||
static const float utk_rc_table[64] = {
|
||||
0.0f,
|
||||
-.99677598476409912109375f, -.99032700061798095703125f, -.983879029750823974609375f, -.977430999279022216796875f,
|
||||
-.970982015132904052734375f, -.964533984661102294921875f, -.958085000514984130859375f, -.9516370296478271484375f,
|
||||
-.930754005908966064453125f, -.904959976673126220703125f, -.879167020320892333984375f, -.853372991085052490234375f,
|
||||
-.827579021453857421875f, -.801786005496978759765625f, -.775991976261138916015625f, -.75019800662994384765625f,
|
||||
-.724404990673065185546875f, -.6986110210418701171875f, -.6706349849700927734375f, -.61904799938201904296875f,
|
||||
-.567460000514984130859375f, -.515873014926910400390625f, -.4642859995365142822265625f, -.4126980006694793701171875f,
|
||||
-.361110985279083251953125f, -.309523999691009521484375f, -.257937014102935791015625f, -.20634900033473968505859375f,
|
||||
-.1547619998455047607421875f, -.10317499935626983642578125f, -.05158700048923492431640625f,
|
||||
0.0f,
|
||||
+.05158700048923492431640625f, +.10317499935626983642578125f, +.1547619998455047607421875f, +.20634900033473968505859375f,
|
||||
+.257937014102935791015625f, +.309523999691009521484375f, +.361110985279083251953125f, +.4126980006694793701171875f,
|
||||
+.4642859995365142822265625f, +.515873014926910400390625f, +.567460000514984130859375f, +.61904799938201904296875f,
|
||||
+.6706349849700927734375f, +.6986110210418701171875f, +.724404990673065185546875f, +.75019800662994384765625f,
|
||||
+.775991976261138916015625f, +.801786005496978759765625f, +.827579021453857421875f, +.853372991085052490234375f,
|
||||
+.879167020320892333984375f, +.904959976673126220703125f, +.930754005908966064453125f, +.9516370296478271484375f,
|
||||
+.958085000514984130859375f, +.964533984661102294921875f, +.970982015132904052734375f, +.977430999279022216796875f,
|
||||
+.983879029750823974609375f, +.99032700061798095703125f, +.99677598476409912109375
|
||||
};
|
||||
|
||||
static const uint8_t utk_codebooks[2][256] = {
|
||||
{ /* normal model */
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2
|
||||
}, { /* large-pulse model */
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3
|
||||
}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int next_model;
|
||||
int code_size;
|
||||
float pulse_value;
|
||||
} utk_commands[29] = {
|
||||
{MDL_LARGEPULSE, 8, 0.0f},
|
||||
{MDL_LARGEPULSE, 7, 0.0f},
|
||||
{MDL_NORMAL, 8, 0.0f},
|
||||
{MDL_NORMAL, 7, 0.0f},
|
||||
{MDL_NORMAL, 2, 0.0f},
|
||||
{MDL_NORMAL, 2, -1.0f},
|
||||
{MDL_NORMAL, 2, +1.0f},
|
||||
{MDL_NORMAL, 3, -1.0f},
|
||||
{MDL_NORMAL, 3, +1.0f},
|
||||
{MDL_LARGEPULSE, 4, -2.0f},
|
||||
{MDL_LARGEPULSE, 4, +2.0f},
|
||||
{MDL_LARGEPULSE, 3, -2.0f},
|
||||
{MDL_LARGEPULSE, 3, +2.0f},
|
||||
{MDL_LARGEPULSE, 5, -3.0f},
|
||||
{MDL_LARGEPULSE, 5, +3.0f},
|
||||
{MDL_LARGEPULSE, 4, -3.0f},
|
||||
{MDL_LARGEPULSE, 4, +3.0f},
|
||||
{MDL_LARGEPULSE, 6, -4.0f},
|
||||
{MDL_LARGEPULSE, 6, +4.0f},
|
||||
{MDL_LARGEPULSE, 5, -4.0f},
|
||||
{MDL_LARGEPULSE, 5, +4.0f},
|
||||
{MDL_LARGEPULSE, 7, -5.0f},
|
||||
{MDL_LARGEPULSE, 7, +5.0f},
|
||||
{MDL_LARGEPULSE, 6, -5.0f},
|
||||
{MDL_LARGEPULSE, 6, +5.0f},
|
||||
{MDL_LARGEPULSE, 8, -6.0f},
|
||||
{MDL_LARGEPULSE, 8, +6.0f},
|
||||
{MDL_LARGEPULSE, 7, -6.0f},
|
||||
{MDL_LARGEPULSE, 7, +6.0f}
|
||||
};
|
||||
|
||||
static int utk_read_byte(UTKContext *ctx)
|
||||
{
|
||||
if (ctx->ptr < ctx->end)
|
||||
return *ctx->ptr++;
|
||||
|
||||
//vgmstream extra: this reads from FILE if static buffer was exhausted, now from a context buffer and STREAMFILE instead
|
||||
if (ctx->streamfile) { //if (ctx->fp) {
|
||||
//static uint8_t buffer[4096];
|
||||
//size_t bytes_copied = fread(buffer, 1, sizeof(buffer), ctx->fp);
|
||||
size_t bytes_copied = read_streamfile(ctx->buffer, ctx->offset, sizeof(ctx->buffer), ctx->streamfile);
|
||||
|
||||
ctx->offset += bytes_copied;
|
||||
if (bytes_copied > 0 && bytes_copied <= sizeof(ctx->buffer)) {
|
||||
ctx->ptr = ctx->buffer;
|
||||
ctx->end = ctx->buffer + bytes_copied;
|
||||
return *ctx->ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t utk_read_i16(UTKContext *ctx)
|
||||
{
|
||||
int x = utk_read_byte(ctx);
|
||||
x = (x << 8) | utk_read_byte(ctx);
|
||||
return x;
|
||||
}
|
||||
|
||||
static int utk_read_bits(UTKContext *ctx, int count)
|
||||
{
|
||||
int ret = ctx->bits_value & ((1 << count) - 1);
|
||||
ctx->bits_value >>= count;
|
||||
ctx->bits_count -= count;
|
||||
|
||||
if (ctx->bits_count < 8) {
|
||||
/* read another byte */
|
||||
ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count;
|
||||
ctx->bits_count += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void utk_parse_header(UTKContext *ctx)
|
||||
{
|
||||
int i;
|
||||
float multiplier;
|
||||
|
||||
ctx->reduced_bw = utk_read_bits(ctx, 1);
|
||||
ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4);
|
||||
ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4));
|
||||
multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f;
|
||||
|
||||
for (i = 1; i < 64; i++)
|
||||
ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier;
|
||||
}
|
||||
|
||||
static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (use_multipulse) {
|
||||
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
|
||||
int model, cmd;
|
||||
model = 0;
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
cmd = utk_codebooks[model][ctx->bits_value & 0xff];
|
||||
model = utk_commands[cmd].next_model;
|
||||
utk_read_bits(ctx, utk_commands[cmd].code_size);
|
||||
|
||||
if (cmd > 3) {
|
||||
/* insert a pulse with magnitude <= 6.0f */
|
||||
out[i] = utk_commands[cmd].pulse_value;
|
||||
i += stride;
|
||||
} else if (cmd > 1) {
|
||||
/* insert between 7 and 70 zeros */
|
||||
int count = 7 + utk_read_bits(ctx, 6);
|
||||
if (i + count * stride > 108)
|
||||
count = (108 - i)/stride;
|
||||
|
||||
while (count > 0) {
|
||||
out[i] = 0.0f;
|
||||
i += stride;
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
/* insert a pulse with magnitude >= 7.0f */
|
||||
int x = 7;
|
||||
|
||||
while (utk_read_bits(ctx, 1))
|
||||
x++;
|
||||
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
x *= -1;
|
||||
|
||||
out[i] = (float)x;
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RELP model: entire residual (excitation) signal is coded explicitly */
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
out[i] = 0.0f;
|
||||
else if (!utk_read_bits(ctx, 1))
|
||||
out[i] = -2.0f;
|
||||
else
|
||||
out[i] = 2.0f;
|
||||
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_to_lpc(const float *rc, float *lpc)
|
||||
{
|
||||
int i, j;
|
||||
float tmp1[12];
|
||||
float tmp2[12];
|
||||
|
||||
for (i = 10; i >= 0; i--)
|
||||
tmp2[1+i] = rc[i];
|
||||
|
||||
tmp2[0] = 1.0f;
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
float x = -tmp2[11] * rc[11];
|
||||
|
||||
for (j = 10; j >= 0; j--) {
|
||||
x -= tmp2[j] * rc[j];
|
||||
tmp2[j+1] = x * rc[j] + tmp2[j];
|
||||
}
|
||||
|
||||
tmp1[i] = tmp2[0] = x;
|
||||
|
||||
for (j = 0; j < i; j++)
|
||||
x -= tmp1[i-1-j] * lpc[j];
|
||||
|
||||
lpc[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks)
|
||||
{
|
||||
int i, j, k;
|
||||
float lpc[12];
|
||||
float *ptr = &ctx->decompressed_frame[offset];
|
||||
|
||||
rc_to_lpc(ctx->rc, lpc);
|
||||
|
||||
for (i = 0; i < num_blocks; i++) {
|
||||
for (j = 0; j < 12; j++) {
|
||||
float x = *ptr;
|
||||
|
||||
for (k = 0; k < j; k++)
|
||||
x += lpc[k] * ctx->synth_history[k-j+12];
|
||||
for (; k < 12; k++)
|
||||
x += lpc[k] * ctx->synth_history[k-j];
|
||||
|
||||
ctx->synth_history[11-j] = x;
|
||||
*ptr++ = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Public functions.
|
||||
*/
|
||||
|
||||
static void utk_decode_frame(UTKContext *ctx)
|
||||
{
|
||||
int i, j;
|
||||
int use_multipulse = 0;
|
||||
float excitation[5+108+5];
|
||||
float rc_delta[12];
|
||||
|
||||
if (!ctx->bits_count) {
|
||||
ctx->bits_value = utk_read_byte(ctx);
|
||||
ctx->bits_count = 8;
|
||||
}
|
||||
|
||||
if (!ctx->parsed_header) {
|
||||
utk_parse_header(ctx);
|
||||
ctx->parsed_header = 1;
|
||||
}
|
||||
|
||||
memset(&excitation[0], 0, 5*sizeof(float));
|
||||
memset(&excitation[5+108], 0, 5*sizeof(float));
|
||||
|
||||
/* read the reflection coefficients */
|
||||
for (i = 0; i < 12; i++) {
|
||||
int idx;
|
||||
if (i == 0) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
if (idx < ctx->multipulse_thresh)
|
||||
use_multipulse = 1;
|
||||
} else if (i < 4) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
} else {
|
||||
idx = 16 + utk_read_bits(ctx, 5);
|
||||
}
|
||||
|
||||
rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f;
|
||||
}
|
||||
|
||||
/* decode four subframes */
|
||||
for (i = 0; i < 4; i++) {
|
||||
int pitch_lag = utk_read_bits(ctx, 8);
|
||||
float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f;
|
||||
float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)];
|
||||
|
||||
if (!ctx->reduced_bw) {
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1);
|
||||
} else {
|
||||
/* residual (excitation) signal is encoded at reduced bandwidth */
|
||||
int align = utk_read_bits(ctx, 1);
|
||||
int zero = utk_read_bits(ctx, 1);
|
||||
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2);
|
||||
|
||||
if (zero) {
|
||||
/* fill the remaining samples with zero
|
||||
** (spectrum is duplicated into high frequencies) */
|
||||
for (j = 0; j < 54; j++)
|
||||
excitation[5+(1-align)+2*j] = 0.0f;
|
||||
} else {
|
||||
/* interpolate the remaining samples
|
||||
** (spectrum is low-pass filtered) */
|
||||
float *ptr = &excitation[5+(1-align)];
|
||||
for (j = 0; j < 108; j += 2)
|
||||
ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f
|
||||
- ptr[j-3] * 0.114591561257839202880859375f
|
||||
+ ptr[j-1] * 0.597385942935943603515625f
|
||||
+ ptr[j+1] * 0.597385942935943603515625f
|
||||
- ptr[j+3] * 0.114591561257839202880859375f
|
||||
+ ptr[j+5] * 0.01803267933428287506103515625f;
|
||||
|
||||
/* scale by 0.5f to give the sinc impulse response unit energy */
|
||||
fixed_gain *= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 108; j++)
|
||||
ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j]
|
||||
+ pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j];
|
||||
}
|
||||
|
||||
for (i = 0; i < 324; i++)
|
||||
ctx->adapt_cb[i] = ctx->decompressed_frame[108+i];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 12; j++)
|
||||
ctx->rc[j] += rc_delta[j];
|
||||
|
||||
utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33);
|
||||
}
|
||||
}
|
||||
|
||||
static void utk_init(UTKContext *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
#if 0 //vgmstream extra: see flush_ea_mt
|
||||
static void utk_set_fp(UTKContext *ctx, FILE *fp)
|
||||
{
|
||||
ctx->fp = fp;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
|
||||
static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end)
|
||||
{
|
||||
ctx->ptr = ptr;
|
||||
ctx->end = end;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** MicroTalk Revision 3 decoding function.
|
||||
*/
|
||||
|
||||
static void utk_rev3_decode_frame(UTKContext *ctx)
|
||||
{
|
||||
int pcm_data_present = (utk_read_byte(ctx) == 0xee);
|
||||
int i;
|
||||
|
||||
utk_decode_frame(ctx);
|
||||
|
||||
/* unread the last 8 bits and reset the bit reader */
|
||||
ctx->ptr--;
|
||||
ctx->bits_count = 0;
|
||||
|
||||
if (pcm_data_present) {
|
||||
/* Overwrite n samples at a given offset in the decoded frame with
|
||||
** raw PCM data. */
|
||||
int offset = utk_read_i16(ctx);
|
||||
int count = utk_read_i16(ctx);
|
||||
|
||||
/* sx.exe does not do any bounds checking or clamping of these two
|
||||
** fields (see 004274D1 in sx.exe v3.01.01), which means a specially
|
||||
** crafted MT5:1 file can crash sx.exe.
|
||||
** We will throw an error instead. */
|
||||
if (offset < 0 || offset > 432) {
|
||||
//fprintf(stderr, "error: invalid PCM offset %d\n", offset);
|
||||
//exit(EXIT_FAILURE);
|
||||
return; //vgmstream extra
|
||||
}
|
||||
if (count < 0 || count > 432 - offset) {
|
||||
//fprintf(stderr, "error: invalid PCM count %d\n", count);
|
||||
//exit(EXIT_FAILURE);
|
||||
return; //vgmstream extra
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************************************************* */
|
||||
|
||||
ea_mt_codec_data *init_ea_mt(int channel_count, int pcm_blocks) {
|
||||
ea_mt_codec_data *data = NULL;
|
||||
int i;
|
||||
|
||||
data = calloc(channel_count, sizeof(ea_mt_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->pcm_blocks = pcm_blocks;
|
||||
data->utk_context_size = channel_count;
|
||||
|
||||
data->utk_context = calloc(channel_count, sizeof(UTKContext*));
|
||||
if (!data->utk_context) goto fail;
|
||||
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
data->utk_context[i] = calloc(1, sizeof(UTKContext));
|
||||
if (!data->utk_context[i]) goto fail;
|
||||
utk_init(data->utk_context[i]);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ea_mt(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
ea_mt_codec_data *data = vgmstream->codec_data;
|
||||
int i, sample_count = 0, frame_samples;
|
||||
UTKContext* ctx = data->utk_context[channel];
|
||||
|
||||
/* Use the above decoder, which expects pointers to read data. Since EA-MT frames aren't
|
||||
* byte-aligned, reading new buffer data is decided by the decoder. When decoding starts
|
||||
* or a SCHl block changes flush_ea_mt must be called to reset the state.
|
||||
* A bit hacky but would need some restructuring otherwise. */
|
||||
|
||||
frame_samples = 432;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* don't decode again if we didn't consume the current frame.
|
||||
* UTKContext saves the sample buffer, and can't re-decode a frame */
|
||||
if (!ctx->samples_filled) {
|
||||
if (data->pcm_blocks)
|
||||
utk_rev3_decode_frame(ctx);
|
||||
else
|
||||
utk_decode_frame(ctx);
|
||||
ctx->samples_filled = 1;
|
||||
}
|
||||
|
||||
/* copy samples */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int x = UTK_ROUND(ctx->decompressed_frame[i]);
|
||||
outbuf[sample_count] = (int16_t)UTK_CLAMP(x, -32768, 32767);
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
if (i == frame_samples)
|
||||
ctx->samples_filled = 0;
|
||||
}
|
||||
|
||||
static void flush_ea_mt_internal(VGMSTREAM *vgmstream, int is_start) {
|
||||
ea_mt_codec_data *data = vgmstream->codec_data;
|
||||
int i;
|
||||
size_t bytes;
|
||||
|
||||
/* the decoder needs to be notified when offsets change */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
UTKContext *ctx = data->utk_context[i];
|
||||
|
||||
ctx->streamfile = vgmstream->ch[i].streamfile;
|
||||
ctx->offset = is_start ? vgmstream->ch[i].channel_start_offset : vgmstream->ch[i].offset;
|
||||
ctx->samples_filled = 0;
|
||||
|
||||
bytes = read_streamfile(ctx->buffer,ctx->offset,sizeof(ctx->buffer),ctx->streamfile);
|
||||
ctx->offset += sizeof(ctx->buffer);
|
||||
ctx->ptr = ctx->buffer;
|
||||
ctx->end = ctx->buffer + bytes;
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void flush_ea_mt(VGMSTREAM *vgmstream) {
|
||||
flush_ea_mt_internal(vgmstream, 0);
|
||||
}
|
||||
|
||||
void reset_ea_mt(VGMSTREAM *vgmstream) {
|
||||
flush_ea_mt_internal(vgmstream, 1);
|
||||
}
|
||||
|
||||
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) {
|
||||
flush_ea_mt_internal(vgmstream, 1);
|
||||
//todo discard loop (though this should be adecuate as probably only uses full loops, if at all)
|
||||
}
|
||||
|
||||
void free_ea_mt(ea_mt_codec_data *data) {
|
||||
int i;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (i = 0; i < data->utk_context_size; i++) {
|
||||
free(data->utk_context[i]);
|
||||
}
|
||||
free(data->utk_context);
|
||||
free(data);
|
||||
}
|
@ -29,59 +29,57 @@ static const int EA_XA_TABLE[20] = {
|
||||
0, -1, -3, -4
|
||||
};
|
||||
|
||||
/* 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?) */
|
||||
/* EA XA v2 (always mono); 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;
|
||||
int32_t coef1, coef2;
|
||||
int i, shift;
|
||||
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
|
||||
int i, sample_count, shift;
|
||||
|
||||
first_sample = first_sample%28;
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
|
||||
channel_offset++;
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_32 = read_16bitBE(stream->offset+channel_offset+0x00,stream->streamfile);
|
||||
stream->adpcm_history2_32 = read_16bitBE(stream->offset+channel_offset+0x02,stream->streamfile);
|
||||
channel_offset += 4;
|
||||
stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset+channel_offset,stream->streamfile);
|
||||
channel_offset+=2;
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* Only increment offset on complete frame */
|
||||
if (channel_offset-stream->channel_start_offset == (2*28)+5)
|
||||
stream->channel_start_offset += (2*28)+5;
|
||||
|
||||
} else { /* ADPCM frame */
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t sample;
|
||||
off_t byte_offset = (stream->offset + channel_offset + i/2);
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */
|
||||
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
|
||||
sample = clamp16(sample);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = sample;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
channel_offset += i/2;
|
||||
|
||||
/* Only increment offset on complete frame */
|
||||
if (channel_offset - stream->channel_start_offset == 0x0F)
|
||||
stream->channel_start_offset += 0x0F;
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,43 +88,42 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
|
||||
int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */
|
||||
|
||||
first_sample = first_sample % 28;
|
||||
int frame_size = 0x1e;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header (coefs ch0+ch1 + shift ch0+ch1) */
|
||||
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
|
||||
channel_offset++;
|
||||
frame_info = read_8bit(stream->offset+0x00,stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0];
|
||||
coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
|
||||
channel_offset++;
|
||||
frame_info = read_8bit(stream->offset+0x01,stream->streamfile);
|
||||
shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8;
|
||||
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t sample;
|
||||
off_t byte_offset = (stream->offset + channel_offset + i);
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x02 + i);
|
||||
int nibble_shift = (hn ? 4 : 0); /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (hn ? sample_byte >> 4 : sample_byte & 0x0F);
|
||||
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
sample = clamp16(sample);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = sample;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
channel_offset += i;
|
||||
|
||||
/* Only increment offset on complete frame */
|
||||
if(channel_offset - stream->channel_start_offset == 0x1E)
|
||||
stream->channel_start_offset += 0x1E;
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
/* EA-XA v1 mono/interleave */
|
||||
@ -134,13 +131,13 @@ void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
|
||||
|
||||
first_sample = first_sample % 28;
|
||||
int frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header (coefs+shift ch0) */
|
||||
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
|
||||
channel_offset++;
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
@ -148,39 +145,38 @@ void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t sample;
|
||||
off_t byte_offset = (stream->offset + channel_offset + i/2);
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */
|
||||
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
sample = clamp16(sample);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = sample;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
channel_offset += i/2;
|
||||
|
||||
/* Only increment offset on complete frame */
|
||||
if(channel_offset - stream->channel_start_offset == 0x0F)
|
||||
stream->channel_start_offset += 0x0F;
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
/* Maxis EA-XA v1 (mono+stereo), differing slightly in the header layout in stereo mode */
|
||||
/* Maxis EA-XA v1 (mono+stereo) with byte-interleave layout in stereo mode */
|
||||
void decode_maxis_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;
|
||||
int i, sample_count, shift;
|
||||
off_t channel_offset = stream->channel_start_offset;
|
||||
int frame_size = channelspacing * 15; /* mono samples have a frame of 15, stereo files have frames of 30 */
|
||||
|
||||
first_sample = first_sample % 28;
|
||||
int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header (coefs+shift ch0 + coefs+shift ch1) */
|
||||
frame_info = read_8bit(channel_offset,stream->streamfile);
|
||||
channel_offset += channelspacing;
|
||||
frame_info = read_8bit(stream->offset + channel,stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
@ -188,27 +184,22 @@ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t sample;
|
||||
off_t byte_offset = (stream->offset + channel_offset);
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2));
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (i&1) ? sample_byte & 0x0F : sample_byte >> 4;
|
||||
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
sample = clamp16(sample);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = sample;
|
||||
|
||||
if(i&1)
|
||||
stream->offset+=channelspacing;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
channel_offset+=i;
|
||||
|
||||
/* Only increment offset on complete frame */
|
||||
if (channel_offset - stream->channel_start_offset == frame_size) {
|
||||
stream->channel_start_offset += frame_size;
|
||||
stream->offset=0;
|
||||
}
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 3 /* only seen up to 3 (Dante's Inferno) */
|
||||
#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 4 /* normally max 3 (Dante's Inferno), ~14 (1 stream) in Burnout Paradise */
|
||||
#define EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK 4 /* XMA2 max is 8ch = 4 * 2ch */
|
||||
#define EAXMA_XMA_PACKET_SIZE 0x800
|
||||
#define EAXMA_XMA_BUFFER_SIZE (EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK * EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK * EAXMA_XMA_PACKET_SIZE)
|
||||
@ -48,7 +48,8 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
|
||||
if (max_packets == 0) goto fail;
|
||||
|
||||
if (max_packets * num_streams * EAXMA_XMA_PACKET_SIZE > EAXMA_XMA_BUFFER_SIZE) {
|
||||
VGM_LOG("EA XMA: block too big at %lx\n", (off_t)real_offset);
|
||||
VGM_LOG("EA XMA: block too big (%i * %i * 0x%x = 0x%x vs max 0x%x) at %lx\n",
|
||||
max_packets,num_streams,EAXMA_XMA_PACKET_SIZE, max_packets*num_streams*EAXMA_XMA_PACKET_SIZE, EAXMA_XMA_BUFFER_SIZE,(off_t)real_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
|
||||
case MPEG_STANDARD:
|
||||
case MPEG_AHX:
|
||||
case MPEG_EA:
|
||||
if (data->channels_per_frame != data->config.channels)
|
||||
if (info.channels != data->config.channels)
|
||||
goto fail; /* no multichannel expected */
|
||||
break;
|
||||
|
||||
@ -89,7 +89,8 @@ fail:
|
||||
|
||||
|
||||
/* writes data to the buffer and moves offsets */
|
||||
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
|
||||
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
|
||||
mpeg_custom_stream *ms = data->streams[num_stream];
|
||||
mpeg_frame_info info;
|
||||
size_t current_data_size = 0;
|
||||
size_t current_padding = 0;
|
||||
@ -137,14 +138,14 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
|
||||
current_data_size = info.frame_size;
|
||||
break;
|
||||
}
|
||||
if (!current_data_size || current_data_size > data->buffer_size) {
|
||||
if (!current_data_size || current_data_size > ms->buffer_size) {
|
||||
VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* read single frame */
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile);
|
||||
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
|
||||
|
||||
|
||||
/* update offsets */
|
||||
|
@ -3,11 +3,12 @@
|
||||
#ifdef VGM_USE_MPEG
|
||||
#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414
|
||||
|
||||
static int ahx_decrypt_type08(mpeg_codec_data *data);
|
||||
static int ahx_decrypt_type08(uint8_t * buffer, mpeg_custom_config *config);
|
||||
|
||||
/* 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) {
|
||||
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
|
||||
/* 0xFFF5E0C0 header: frame size 0x414 (160kbps, 22050Hz) but they actually are much shorter */
|
||||
mpeg_custom_stream *ms = data->streams[num_stream];
|
||||
size_t current_data_size = 0;
|
||||
size_t file_size = get_streamfile_size(stream->streamfile);
|
||||
|
||||
@ -33,22 +34,22 @@ int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data)
|
||||
next_offset++;
|
||||
}
|
||||
}
|
||||
if (!current_data_size || current_data_size > data->buffer_size || current_data_size > MPEG_AHX_EXPECTED_FRAME_SIZE) {
|
||||
if (!current_data_size || current_data_size > ms->buffer_size || current_data_size > MPEG_AHX_EXPECTED_FRAME_SIZE) {
|
||||
VGM_LOG("MPEG AHX: incorrect data_size 0x%x\n", current_data_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset,current_data_size,stream->streamfile);
|
||||
memset(ms->buffer + ms->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - ms->bytes_in_buffer);
|
||||
ms->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
|
||||
|
||||
|
||||
/* decrypt if needed */
|
||||
switch(data->config.encryption) {
|
||||
case 0x00: break;
|
||||
case 0x08: ahx_decrypt_type08(data); break;
|
||||
case 0x08: ahx_decrypt_type08(ms->buffer, &data->config); break;
|
||||
default:
|
||||
VGM_LOG("MPEG AHX: unknown encryption 0x%x\n", data->config.encryption);
|
||||
break; /* garbled frame */
|
||||
@ -66,7 +67,7 @@ fail:
|
||||
}
|
||||
|
||||
/* Decrypts an AHX type 0x08 (keystring) encrypted frame. Algorithm by Thealexbarney */
|
||||
static int ahx_decrypt_type08(mpeg_codec_data *data) {
|
||||
static int ahx_decrypt_type08(uint8_t * buffer, mpeg_custom_config *config) {
|
||||
int i, index, encrypted_bits;
|
||||
uint32_t value;
|
||||
uint16_t current_key;
|
||||
@ -78,18 +79,18 @@ static int ahx_decrypt_type08(mpeg_codec_data *data) {
|
||||
|
||||
/* 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);
|
||||
value = (uint32_t)get_32bitBE(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;
|
||||
case 1: current_key = config->cri_key1; break;
|
||||
case 2: current_key = config->cri_key2; break;
|
||||
case 3: current_key = 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;
|
||||
encrypted_bits = 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)
|
||||
@ -101,7 +102,7 @@ static int ahx_decrypt_type08(mpeg_codec_data *data) {
|
||||
}
|
||||
|
||||
/* write output */
|
||||
put_32bitBE(data->buffer + 0x0d, value);
|
||||
put_32bitBE(buffer + 0x0d, value);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
|
@ -170,7 +170,7 @@ int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data,
|
||||
goto fail;
|
||||
current_data_size = info.frame_size;
|
||||
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile);
|
||||
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
|
||||
|
||||
stream->offset += current_data_size;
|
||||
|
||||
|
@ -7,11 +7,12 @@
|
||||
* 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).
|
||||
* 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). This is only for our decoder, real EALayer3 would decode EA-frames directly.
|
||||
* 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)
|
||||
* Reverse engineering by Zench: 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
|
||||
*/
|
||||
@ -29,7 +30,7 @@ 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 */
|
||||
off_t info_offset; /* for logs */
|
||||
} ealayer3_bitstream;
|
||||
|
||||
/* parsed info from a single EALayer3 frame */
|
||||
@ -38,13 +39,14 @@ typedef struct {
|
||||
uint32_t v1_pcm_flag;
|
||||
uint32_t v1_pcm_decode_discard;
|
||||
uint32_t v1_pcm_number;
|
||||
uint32_t v1_pcm_unknown;
|
||||
|
||||
/* 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; /* discard mode */
|
||||
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 */
|
||||
@ -80,11 +82,12 @@ typedef struct {
|
||||
|
||||
|
||||
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_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame, int is_v1b);
|
||||
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 ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start);
|
||||
|
||||
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);
|
||||
@ -94,17 +97,14 @@ static int w_bits(ealayer3_bitstream * ow, int num_bits, uint32_t value);
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* init codec from a EALayer3 frame */
|
||||
/* init codec from an 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;
|
||||
ealayer3_bitstream is = {0};
|
||||
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 */
|
||||
//;VGM_LOG("init at %lx\n", start_offset);
|
||||
|
||||
/* get first frame for info */
|
||||
{
|
||||
@ -115,20 +115,12 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset,
|
||||
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);
|
||||
VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset); /* untested */
|
||||
|
||||
*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;
|
||||
@ -138,15 +130,20 @@ fail:
|
||||
|
||||
/* 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;
|
||||
mpeg_custom_stream *ms = data->streams[num_stream];
|
||||
int ok, granule_found;
|
||||
off_t info_offset = stream->offset;
|
||||
|
||||
ealayer3_frame_info eaf_0, eaf_1;
|
||||
ealayer3_bitstream is_0, is_1, os;
|
||||
ealayer3_bitstream is_0 = {0}, is_1 = {0}, os = {0};
|
||||
uint8_t ibuf_0[EALAYER3_EA_FRAME_BUFFER_SIZE], ibuf_1[EALAYER3_EA_FRAME_BUFFER_SIZE];
|
||||
|
||||
/* read first frame/granule */
|
||||
{
|
||||
/* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */
|
||||
{
|
||||
//;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset);
|
||||
if (!ealayer3_skip_data(stream, data, num_stream, 1))
|
||||
goto fail;
|
||||
|
||||
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;
|
||||
@ -158,51 +155,54 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *
|
||||
if (!ok) goto fail;
|
||||
|
||||
stream->offset += eaf_0.eaframe_size;
|
||||
}
|
||||
//;VGM_LOG("s%i: get granule0 done at %lx (eaf_size=%x, common_size=%x)\n", num_stream,stream->offset, eaf_0.eaframe_size, eaf_0.common_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;
|
||||
if (!ealayer3_skip_data(stream, data, num_stream, 0))
|
||||
goto fail;
|
||||
|
||||
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;
|
||||
/* get second frame/granule (MPEG1 only) if first granule was found */
|
||||
granule_found = 0;
|
||||
while (eaf_0.common_size && eaf_0.mpeg1 && !granule_found) {
|
||||
//;VGM_LOG("s%i: get granule1 at %lx\n", num_stream,stream->offset);
|
||||
if (!ealayer3_skip_data(stream, data, num_stream, 1))
|
||||
goto fail;
|
||||
|
||||
stream->offset += eaf_1.eaframe_size;
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_1);
|
||||
if (!ok) goto fail;
|
||||
|
||||
stream->offset += eaf_1.eaframe_size;
|
||||
//;VGM_LOG("s%i: get granule0 done at %lx (eaf_size=%x, common_size=%x)\n", num_stream,stream->offset, eaf_0.eaframe_size, eaf_0.common_size);
|
||||
|
||||
if (!ealayer3_skip_data(stream, data, num_stream, 0))
|
||||
goto fail;
|
||||
|
||||
/* in V1a there may be PCM-only frames between granules so read until next one (or parse fails) */
|
||||
if (eaf_1.common_size > 0)
|
||||
granule_found = 1;
|
||||
}
|
||||
|
||||
/* rebuild EALayer frame to MPEG frame */
|
||||
{
|
||||
os.buf = data->buffer;
|
||||
os.bufsize = data->buffer_size;
|
||||
os.buf = ms->buffer;
|
||||
os.bufsize = ms->buffer_size;
|
||||
os.b_off = 0;
|
||||
os.offset = current_offset;
|
||||
os.info_offset = info_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 */
|
||||
ms->bytes_in_buffer = os.b_off / 8; /* wrote full MPEG frame, hopefully */
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
@ -216,28 +216,26 @@ fail:
|
||||
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
|
||||
int ok;
|
||||
|
||||
/* make sure as there is re-parsing in loops */
|
||||
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_EAL31: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame, 0); break;
|
||||
case MPEG_EAL31b: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame, 1); 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) {
|
||||
/* read V1"a" (in SCHl) and V1"b" (in SNS) EALayer3 frame */
|
||||
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame, int is_v1b) {
|
||||
int ok;
|
||||
|
||||
/* read EA-frame V1 header */
|
||||
@ -249,25 +247,30 @@ static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info *
|
||||
VGM_LOG("MPEG EAL3 v1: header not 0x00 or 0xEE\n");
|
||||
goto fail; /* wrong offset? */
|
||||
}
|
||||
if (eaf->v1_pcm_flag == 0xEE && !channels_per_frame) {
|
||||
VGM_LOG("MPEG EAL3 v1: PCM block in first frame\n");
|
||||
goto fail; /* must know from a prev frame */
|
||||
}
|
||||
|
||||
/* read EA-frame common header (v1a PCM blocks don't have EA-frames, while v1b do) */
|
||||
if (is_v1b || eaf->v1_pcm_flag == 0x00) {
|
||||
ok = ealayer3_parse_frame_common(is, eaf);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (is_v1b) { /* extra 32b in v1b */
|
||||
r_bits(is, 32,&eaf->v1_pcm_unknown);
|
||||
eaf->pre_size += 4; /* 32b */
|
||||
VGM_ASSERT(eaf->v1_pcm_unknown != 0, "EA EAL3 v1: v1_pcm_unknown not 0\n");
|
||||
}
|
||||
}
|
||||
|
||||
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
|
||||
@ -277,7 +280,8 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* read V2"PCM" and V2"Spike" EALayer3 frame (exactly the same but V2P seems to have bigger
|
||||
* PCM blocks and maybe smaller frames) */
|
||||
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
|
||||
int ok;
|
||||
|
||||
@ -286,7 +290,7 @@ static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info *
|
||||
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) {
|
||||
@ -294,25 +298,24 @@ static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info *
|
||||
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;
|
||||
if (!eaf->v2_extended_flag || (eaf->v2_extended_flag && eaf->v2_common_size)) {
|
||||
ok = ealayer3_parse_frame_common(is, eaf);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */
|
||||
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with 0x%x\n", eaf->v2_mode, eaf->v2_mode_value);
|
||||
//VGM_ASSERT(eaf->v2_pcm_number > 0, "EA EAL3: v2_pcm_number 0x%x\n", eaf->v2_pcm_number);
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@ -324,7 +327,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* Parses a EALayer3 frame (common part) */
|
||||
/* parses an 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 };
|
||||
@ -393,9 +396,11 @@ static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_in
|
||||
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];
|
||||
}
|
||||
is->b_off += eaf->data_size_b;
|
||||
|
||||
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);
|
||||
is->b_off += eaf->padding_size_b;
|
||||
|
||||
eaf->common_size = (eaf->base_size_b + eaf->data_size_b + eaf->padding_size_b)/8;
|
||||
|
||||
@ -406,36 +411,46 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* Converts a EALAYER3 frame to a standard MPEG frame from pre-parsed info */
|
||||
/* converts an 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;
|
||||
}
|
||||
/* ignore PCM-only frames */
|
||||
if (!eaf_0->common_size)
|
||||
return 1;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (eaf_0->mpeg1 && (!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->info_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* get bitrate: use "free format" (bigger bitrate) to avoid the need of bit reservoir
|
||||
* this feature is in the spec but some decoders may not support it
|
||||
* (free format detection is broken in some MP3 in mpg123 < 1.25.8 but works ok) */
|
||||
expected_bitrate_index = 0x00;
|
||||
if (eaf_0->mpeg1) {
|
||||
expected_frame_size = 144l * (320*2) * 1000l / eaf_0->sample_rate;
|
||||
} else {
|
||||
expected_frame_size = 72l * (160*2) * 1000l / eaf_0->sample_rate;
|
||||
}
|
||||
#if 0
|
||||
/* this uses max official bitrate (320/160) but some frames need more = complex bit reservoir */
|
||||
expected_bitrate_index = 0x0E;
|
||||
if (eaf_0->mpeg1) { /* 320: 44100=0x414, 48000=0x3C0, 32000=0x5A0 */
|
||||
expected_frame_size = 144l * 320 * 1000l / eaf_0->sample_rate;
|
||||
} else { /* 160: 22050=0x20A, 24000=0x1E0, 16000=0x2D0, 11025=0x414, 12000=0x3C0, 8000=0x5A0 */
|
||||
expected_frame_size = 72l * 160 * 1000l / eaf_0->sample_rate;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* write MPEG1/2 frame header */
|
||||
w_bits(os, 11, 0x7FF); /* sync */
|
||||
w_bits(os, 2, eaf_0->version_index);
|
||||
@ -521,12 +536,8 @@ static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_
|
||||
|
||||
|
||||
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);
|
||||
/* bit reservoir! shouldn't happen with free bitrate, otherwise it's hard to fix as needs complex buffering/calcs */
|
||||
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->info_offset);
|
||||
}
|
||||
else {
|
||||
/* fill ancillary data (ignored) */
|
||||
@ -541,7 +552,9 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write PCM block directly to sample buffer (EALayer3 seems to use this as a prefectch of sorts) */
|
||||
/* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts).
|
||||
* Meant to be written inmediatedly, as those PCM are parts that can be found after 1 decoded frame.
|
||||
* (ex. EA-frame_gr0, PCM-frame_0, EA-frame_gr1, PCM-frame_1 actually writes PCM-frame_0+1, decode of EA-frame_gr0+1 + discard part */
|
||||
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;
|
||||
@ -552,13 +565,12 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
@ -576,30 +588,97 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
|
||||
|
||||
//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;
|
||||
|
||||
if (data->type == MPEG_EAL31) {
|
||||
if (decode_to_discard == 576)
|
||||
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
|
||||
}
|
||||
else {
|
||||
if (decode_to_discard == 0) /* seems ok? */
|
||||
decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number;
|
||||
else if (decode_to_discard == 576) /* untested */
|
||||
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
|
||||
}
|
||||
ms->decode_to_discard += decode_to_discard;
|
||||
}
|
||||
}
|
||||
|
||||
if (eaf->v2_extended_flag) {
|
||||
|
||||
if (eaf->v2_pcm_number) {
|
||||
/* read + write PCM block samples (always BE) */
|
||||
for (i = 0; i < eaf->v2_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->v2_pcm_number;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* todo supposed skip modes (only seen 0x00):
|
||||
*
|
||||
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
|
||||
* D = BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3
|
||||
* E = samples 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 (pcm block)
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* modify decoded samples depending on flag */
|
||||
if (eaf->v2_mode == 0x00) {
|
||||
size_t decode_to_discard = eaf->v2_mode_value;
|
||||
|
||||
if (decode_to_discard == 576)
|
||||
decode_to_discard = data->samples_per_frame;//+ eaf->v2_pcm_number;
|
||||
|
||||
ms->decode_to_discard += decode_to_discard;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
*/
|
||||
/* Skip EA-frames from other streams for multichannel (interleaved 1 EA-frame per stream).
|
||||
* Due to EALayer3 being in blocks and other complexities (we can't go past a block) all
|
||||
* streams's offsets should start in the first stream's EA-frame.
|
||||
*
|
||||
* So to properly read one MPEG-frame from a stream we need to:
|
||||
* - skip one EA-frame per previous streams until offset is in current stream's EA-frame
|
||||
* (ie. 1st stream skips 0, 2nd stream skips 1, 3rd stream skips 2)
|
||||
* - read EA-frame (granule0)
|
||||
* - skip one EA-frame per following streams until offset is in first stream's EA-frame
|
||||
* (ie. 1st stream skips 2, 2nd stream skips 1, 3rd stream skips 0)
|
||||
* - repeat again for granule1
|
||||
*/
|
||||
static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) {
|
||||
int ok, i;
|
||||
ealayer3_frame_info eaf;
|
||||
ealayer3_bitstream is = {0};
|
||||
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
|
||||
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
|
||||
|
||||
|
||||
for (i = 0; i < skips; i++) {
|
||||
is.buf = ibuf;
|
||||
is.bufsize = read_streamfile(ibuf,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
|
||||
is.b_off = 0;
|
||||
|
||||
ok = ealayer3_parse_frame(data, &is, &eaf);
|
||||
if (!ok) goto fail;
|
||||
|
||||
stream->offset += eaf.eaframe_size;
|
||||
}
|
||||
//;VGM_LOG("s%i: skipped %i frames, now at %lx\n", num_stream,skips,stream->offset);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
|
@ -13,7 +13,7 @@
|
||||
* - validate of channels between streams
|
||||
*/
|
||||
|
||||
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1) */
|
||||
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) */
|
||||
|
||||
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);
|
||||
@ -101,6 +101,8 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
if (channels_per_frame != channels)
|
||||
goto fail;
|
||||
|
||||
/* 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(main_m);
|
||||
@ -123,11 +125,6 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
|
||||
/* keep around to decode */
|
||||
data->custom = 1;
|
||||
data->type = type;
|
||||
@ -137,6 +134,7 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
|
||||
/* init per subtype */
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL31b:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
|
||||
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break;
|
||||
@ -161,6 +159,11 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
|
||||
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;
|
||||
|
||||
/* one per stream as sometimes mpg123 can't read the whole buffer in one pass */
|
||||
data->streams[i]->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
||||
data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size);
|
||||
if (!data->streams[i]->buffer) goto fail;
|
||||
}
|
||||
|
||||
|
||||
@ -367,7 +370,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
|
||||
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);
|
||||
//;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);
|
||||
|
||||
/* wait until samples are depleted, so buffers don't grow too big */
|
||||
if (ms->samples_filled - ms->samples_used > 0) {
|
||||
@ -379,46 +382,47 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
|
||||
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) {
|
||||
if (!ms->buffer_full && stream->offset >= stream_size) {
|
||||
VGM_LOG("MPEG: EOF found but more data is requested\n");
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
|
||||
/* read more raw data (could fill the sample buffer too in some cases, namely EALayer3) */
|
||||
if (!data->buffer_full) {
|
||||
if (!ms->buffer_full) {
|
||||
//;VGM_LOG("MPEG: reading more raw data\n");
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL31b:
|
||||
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;
|
||||
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break;
|
||||
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
|
||||
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
|
||||
default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); 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);
|
||||
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->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 (ms->bytes_in_buffer) {
|
||||
ms->buffer_full = 1;
|
||||
ms->buffer_used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
if (!ms->buffer_used) {
|
||||
//;VGM_LOG("MPEG: feed new data and get samples\n");
|
||||
rc = mpg123_decode(ms->m,
|
||||
data->buffer, data->bytes_in_buffer,
|
||||
ms->buffer, ms->bytes_in_buffer,
|
||||
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
ms->buffer_used = 1;
|
||||
}
|
||||
else {
|
||||
//;VGM_LOG("MPEG: get samples from old data\n");
|
||||
@ -448,11 +452,12 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
|
||||
/* 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;
|
||||
//;VGM_LOG("MPEG: need more raw data to get samples (bytes_done=%x)\n", bytes_done);
|
||||
ms->buffer_full = 0;
|
||||
}
|
||||
|
||||
|
||||
//;VGM_LOG("MPEG: stream samples now=%i, filled=%i)\n\n", ms->samples_filled, samples_filled);
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
@ -478,6 +483,7 @@ void free_mpeg(mpeg_codec_data *data) {
|
||||
int i;
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_delete(data->streams[i]->m);
|
||||
free(data->streams[i]->buffer);
|
||||
free(data->streams[i]->output_buffer);
|
||||
free(data->streams[i]);
|
||||
}
|
||||
@ -525,6 +531,8 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
/* seek multistream */
|
||||
if (!data->custom) {
|
||||
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
||||
|
||||
/* force first offset as discard-looping needs to start from the beginning */
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
|
||||
}
|
||||
@ -536,7 +544,10 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
data->streams[i]->samples_filled = 0;
|
||||
data->streams[i]->samples_used = 0;
|
||||
data->streams[i]->decode_to_discard = 0;
|
||||
data->streams[i]->buffer_full = 0;
|
||||
data->streams[i]->buffer_used = 0;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@ -567,6 +578,9 @@ void flush_mpeg(mpeg_codec_data * data) {
|
||||
data->streams[i]->samples_filled = 0;
|
||||
data->streams[i]->samples_used = 0;
|
||||
data->streams[i]->decode_to_discard = 0;
|
||||
data->streams[i]->bytes_in_buffer = 0;
|
||||
data->streams[i]->buffer_full = 0;
|
||||
data->streams[i]->buffer_used = 0;
|
||||
}
|
||||
|
||||
data->samples_to_discard = data->skip_samples; /* initial delay */
|
||||
@ -580,23 +594,20 @@ void flush_mpeg(mpeg_codec_data * data) {
|
||||
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
|
||||
/* if not found just return 0 and expect to fail (if used for num_samples) */
|
||||
if (!data->custom) {
|
||||
struct mpg123_frameinfo mi;
|
||||
mpg123_handle *m = data->m;
|
||||
|
||||
if (m == NULL || MPG123_OK != mpg123_info(m, &mi))
|
||||
return 0;
|
||||
|
||||
/* We would need to read all VBR frames headers to count samples */
|
||||
if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used to get an approx
|
||||
if (data->mi.vbr != MPG123_CBR) { //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 * mi.rate * 8 / (mi.bitrate * 1000);
|
||||
return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000);
|
||||
}
|
||||
else {
|
||||
return 0; /* a bit too complex for what is worth */
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* 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) {
|
||||
@ -609,5 +620,5 @@ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -21,8 +21,8 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
|
||||
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_awc(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_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
|
||||
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
|
||||
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
|
||||
int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
|
||||
|
||||
|
@ -50,6 +50,7 @@ vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile,
|
||||
case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break;
|
||||
case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break;
|
||||
case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break;
|
||||
case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(streamFile, start_offset, data); break;
|
||||
default: goto fail;
|
||||
}
|
||||
if(!ok) goto fail;
|
||||
@ -131,6 +132,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
|
||||
case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break;
|
||||
case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break;
|
||||
case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break;
|
||||
case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break;
|
||||
default: goto decode_fail;
|
||||
}
|
||||
if(!ok) {
|
||||
@ -142,8 +144,9 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
|
||||
/* parse the fake ogg packet into a logical vorbis block */
|
||||
rc = vorbis_synthesis(&data->vb,&data->op);
|
||||
if (rc == OV_ENOTAUDIO) {
|
||||
VGM_LOG("Vorbis: not an audio packet @ %lx\n",stream->offset);
|
||||
continue; /* not tested */
|
||||
VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %lx\n",(size_t)data->op.bytes,stream->offset);
|
||||
//VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0);
|
||||
continue; /* seems ok? */
|
||||
} else if (rc != 0) {
|
||||
VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
|
||||
goto decode_fail;
|
||||
|
@ -9,11 +9,13 @@ int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vor
|
||||
int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
|
||||
|
||||
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
|
||||
#endif/* VGM_USE_VORBIS */
|
||||
|
||||
#endif/*_VORBIS_CUSTOM_DECODER_H_ */
|
||||
|
191
src/coding/vorbis_custom_utils_vid1.c
Normal file
191
src/coding/vorbis_custom_utils_vid1.c
Normal file
@ -0,0 +1,191 @@
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* An internal struct to pass around and 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 */
|
||||
} vgm_bitstream;
|
||||
|
||||
static int r_bits(vgm_bitstream * iw, int num_bits, uint32_t * value);
|
||||
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
|
||||
*
|
||||
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
|
||||
*/
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size = 0;
|
||||
|
||||
/* read header packets (id/setup), each with an VID1 header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += packet_size;
|
||||
|
||||
/* generate comment packet */
|
||||
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
/* normal setup packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += packet_size;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
|
||||
/* test block start */
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D && /* "FRAM" */
|
||||
read_32bitBE(stream->offset + 0x20,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
||||
data->block_offset = stream->offset;
|
||||
data->block_size = read_32bitBE(stream->offset + 0x2c,stream->streamfile);
|
||||
stream->offset += 0x34; /* actual start, rest is chunk sizes and maybe granule info */
|
||||
}
|
||||
|
||||
|
||||
/* get packet info the VID1 header */
|
||||
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
|
||||
if (data->op.bytes == 0) {
|
||||
VGM_LOG("VID1 Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset);
|
||||
goto fail; /* EOF or end padding */
|
||||
}
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += data->op.bytes;
|
||||
if (bytes != data->op.bytes) {
|
||||
VGM_LOG("VID1 Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
|
||||
goto fail; /* wrong packet? */
|
||||
}
|
||||
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
|
||||
|
||||
|
||||
/* test block end (weird size calc but seems ok) */
|
||||
if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) {
|
||||
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize) {
|
||||
int bytes = 0x19;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* read header in Vorbis bitpacking format */
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
|
||||
uint8_t ibuf[0x04]; /* header buffer */
|
||||
size_t ibufsize = 0x04; /* header ~max */
|
||||
vgm_bitstream ib = {0};
|
||||
uint32_t size_bits;
|
||||
|
||||
|
||||
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
|
||||
goto fail;
|
||||
ib.buf = ibuf;
|
||||
ib.bufsize = ibufsize;
|
||||
ib.b_off = 0;
|
||||
|
||||
/* read using Vorbis weird LSF */
|
||||
r_bits(&ib, 4,&size_bits);
|
||||
r_bits(&ib, (size_bits+1),(uint32_t*)size);
|
||||
|
||||
/* special meaning, seen in silent frames */
|
||||
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
|
||||
*size = 0x01;
|
||||
}
|
||||
|
||||
/* pad and convert to byte offset */
|
||||
if (ib.b_off % 8)
|
||||
ib.b_off += 8 - (ib.b_off % 8);
|
||||
*offset += (ib.b_off/8);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte.
|
||||
* (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */
|
||||
static int r_bits(vgm_bitstream * ib, 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 || ib->b_off + num_bits > ib->bufsize*8) goto fail;
|
||||
|
||||
*value = 0; /* set all bits to 0 */
|
||||
off = ib->b_off / 8; /* byte offset */
|
||||
pos = ib->b_off % 8; /* bit sub-offset */
|
||||
for (i = 0; i < num_bits; i++) {
|
||||
bit_buf = (1U << pos) & 0xFF; /* bit check for buf */
|
||||
bit_val = (1U << i); /* bit to set in value */
|
||||
|
||||
if (ib->buf[off] & bit_buf) /* is bit in buf set? */
|
||||
*value |= bit_val; /* set bit */
|
||||
|
||||
pos++; /* new byte starts */
|
||||
if (pos%8 == 0) {
|
||||
pos = 0;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
ib->b_off += num_bits;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -110,6 +110,7 @@ static const char* extension_list[] = {
|
||||
"fag",
|
||||
"ffw",
|
||||
"filp",
|
||||
"flx",
|
||||
"fsb",
|
||||
"fwav",
|
||||
|
||||
@ -179,6 +180,7 @@ static const char* extension_list[] = {
|
||||
"mihb",
|
||||
"mnstr",
|
||||
//"mp4", //common
|
||||
"mpc", //FFmpeg, not parsed (musepack)
|
||||
"mpdsp",
|
||||
"mpds",
|
||||
"msa",
|
||||
@ -272,6 +274,7 @@ static const char* extension_list[] = {
|
||||
"snd",
|
||||
"snds",
|
||||
"sng",
|
||||
"snr",
|
||||
"sns",
|
||||
"snu",
|
||||
"spd",
|
||||
@ -317,16 +320,17 @@ static const char* extension_list[] = {
|
||||
"vawx",
|
||||
"vb",
|
||||
"vbk",
|
||||
"vds",
|
||||
"vdm",
|
||||
"vgs",
|
||||
"vgv",
|
||||
"vig",
|
||||
"vds",
|
||||
"vdm",
|
||||
"vms",
|
||||
"voi",
|
||||
"vpk",
|
||||
"vs",
|
||||
"vsf",
|
||||
"vxn",
|
||||
|
||||
"waa",
|
||||
"wac",
|
||||
@ -485,6 +489,8 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_NWA4, "NWA DPCM Level 4"},
|
||||
{coding_NWA5, "NWA DPCM Level 5"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
@ -908,6 +914,12 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_UBI_SB, "Ubisoft SBx header"},
|
||||
{meta_NAAC, "Namco NAAC header"},
|
||||
{meta_EZW, "EZ2DJ EZWAVE header"},
|
||||
{meta_VXN, "Gameloft VXN header"},
|
||||
{meta_EA_SNR_SNS, "Electronic Arts SNR+SNS header"},
|
||||
{meta_EA_SPS, "Electronic Arts SPS header"},
|
||||
{meta_NGC_VID1, "Neversoft VID1 header"},
|
||||
{meta_PC_FLX, "Ultima IX .FLX header"},
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
|
@ -119,6 +119,17 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
|
||||
break;
|
||||
|
||||
/* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */
|
||||
case coding_EA_MT:
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
|
||||
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01;
|
||||
}
|
||||
|
||||
/* flush decoder in every block change */
|
||||
flush_ea_mt(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:
|
||||
@ -161,14 +172,4 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
vgmstream->next_block_offset = block_offset + block_size;
|
||||
vgmstream->current_block_samples = block_samples;
|
||||
vgmstream->current_block_size = 0; /* uses current_block_samples instead */
|
||||
|
||||
|
||||
/* reset channel sub offset for codecs using it */
|
||||
if (vgmstream->coding_type == coding_EA_XA
|
||||
|| vgmstream->coding_type == coding_EA_XA_int
|
||||
|| vgmstream->coding_type == coding_EA_XA_V2) {
|
||||
for(i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].channel_start_offset=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,25 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* known: 0x80 = last block, 0x40, 0x08, 0x04, 0x01 */
|
||||
/* 0x80: last block
|
||||
* 0x40: new block for some codecs?
|
||||
* 0x08: ?
|
||||
* 0x04: new block for some codecs?
|
||||
* 0x01: last block for some codecs?
|
||||
* 0x00: none? */
|
||||
if (block_size & 0xFF000000) {
|
||||
VGM_ASSERT(!(block_size & 0x80000000), "EA SNS: unknown flag found at %lx\n", block_offset);
|
||||
//VGM_ASSERT(!(block_size & 0x80000000), "EA SNS: unknown flag found at %lx\n", block_offset);
|
||||
block_size &= 0x00FFFFFF;
|
||||
}
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = 0x00;
|
||||
vgmstream->ch[i].offset = block_offset + 0x08 + channel_start;
|
||||
|
||||
/* also fix first offset (for EALayer3) */
|
||||
if (block_offset == vgmstream->ch[i].channel_start_offset) {
|
||||
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
|
@ -357,7 +357,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ea_snu.c"
|
||||
RelativePath=".\meta\ea_eaac.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -384,6 +384,10 @@
|
||||
RelativePath=".\meta\ffw.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\flx.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fsb.c"
|
||||
>
|
||||
@ -625,6 +629,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\ngc_ulw.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ngc_vid1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ngca.c"
|
||||
@ -1198,6 +1206,10 @@
|
||||
RelativePath=".\meta\vsf.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\vxn.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\waa_wac_wad_wam.c"
|
||||
>
|
||||
@ -1385,6 +1397,10 @@
|
||||
<File
|
||||
RelativePath=".\coding\coding_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ea_mt_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ea_xa_decoder.c"
|
||||
@ -1550,6 +1566,10 @@
|
||||
RelativePath=".\coding\vorbis_custom_utils_sk.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_vid1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_wwise.c"
|
||||
>
|
||||
|
@ -225,10 +225,11 @@
|
||||
<ClCompile Include="meta\ea_schl.c" />
|
||||
<ClCompile Include="meta\ea_schl_fixed.c" />
|
||||
<ClCompile Include="meta\ea_1snh.c" />
|
||||
<ClCompile Include="meta\ea_snu.c" />
|
||||
<ClCompile Include="meta\ea_eaac.c" />
|
||||
<ClCompile Include="meta\emff.c" />
|
||||
<ClCompile Include="meta\exakt_sc.c" />
|
||||
<ClCompile Include="meta\ffw.c" />
|
||||
<ClCompile Include="meta\flx.c" />
|
||||
<ClCompile Include="meta\fsb.c" />
|
||||
<ClCompile Include="meta\fsb5.c" />
|
||||
<ClCompile Include="meta\gca.c" />
|
||||
@ -287,6 +288,7 @@
|
||||
<ClCompile Include="meta\ngc_tydsp.c" />
|
||||
<ClCompile Include="meta\ngc_ymf.c" />
|
||||
<ClCompile Include="meta\ngc_ulw.c" />
|
||||
<ClCompile Include="meta\ngc_vid1.c" />
|
||||
<ClCompile Include="meta\nub_xma.c" />
|
||||
<ClCompile Include="meta\nwa.c" />
|
||||
<ClCompile Include="meta\ogg_vorbis_file.c" />
|
||||
@ -405,6 +407,7 @@
|
||||
<ClCompile Include="meta\ubi_sb.c" />
|
||||
<ClCompile Include="meta\vs.c" />
|
||||
<ClCompile Include="meta\vsf.c" />
|
||||
<ClCompile Include="meta\vxn.c" />
|
||||
<ClCompile Include="meta\waa_wac_wad_wam.c" />
|
||||
<ClCompile Include="meta\wii_04sw.c" />
|
||||
<ClCompile Include="meta\wii_bns.c" />
|
||||
@ -434,6 +437,7 @@
|
||||
<ClCompile Include="coding\acm_decoder.c" />
|
||||
<ClCompile Include="coding\adx_decoder.c" />
|
||||
<ClCompile Include="coding\aica_decoder.c" />
|
||||
<ClCompile Include="coding\ea_mt_decoder.c" />
|
||||
<ClCompile Include="coding\ea_xa_decoder.c" />
|
||||
<ClCompile Include="coding\ea_xas_decoder.c" />
|
||||
<ClCompile Include="coding\g719_decoder.c" />
|
||||
@ -464,6 +468,7 @@
|
||||
<ClCompile Include="coding\vorbis_custom_utils_fsb.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_utils_ogl.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_utils_sk.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_utils_vid1.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_utils_wwise.c" />
|
||||
<ClCompile Include="coding\ws_decoder.c" />
|
||||
<ClCompile Include="coding\xa_decoder.c" />
|
||||
|
@ -211,7 +211,7 @@
|
||||
<ClCompile Include="meta\ea_1snh.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_snu.c">
|
||||
<ClCompile Include="meta\ea_eaac.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\emff.c">
|
||||
@ -223,6 +223,9 @@
|
||||
<ClCompile Include="meta\ffw.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\flx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\fsb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -376,6 +379,9 @@
|
||||
<ClCompile Include="meta\ngc_ulw.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ngc_vid1.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\nub_xma.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -730,6 +736,9 @@
|
||||
<ClCompile Include="meta\vsf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\vxn.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\waa_wac_wad_wam.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -814,6 +823,9 @@
|
||||
<ClCompile Include="coding\aica_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ea_mt_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ea_xa_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -904,6 +916,9 @@
|
||||
<ClCompile Include="coding\vorbis_custom_utils_sk.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\vorbis_custom_utils_vid1.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\vorbis_custom_utils_wwise.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
253
src/meta/ea_eaac.c
Normal file
253
src/meta/ea_eaac.c
Normal file
@ -0,0 +1,253 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* EAAudioCore formats, EA's current audio middleware */
|
||||
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type);
|
||||
|
||||
/* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamData = NULL;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"snr"))
|
||||
goto fail;
|
||||
|
||||
/* SNR headers normally need an external SNS file, but some have data */
|
||||
if (get_streamfile_size(streamFile) > 0x10) {
|
||||
/* for Burnout Paradise has this, not sure if extension */
|
||||
off_t start_offset = (read_32bitBE(0x0c, streamFile) == 0) ? 0x0c : 0x08;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else {
|
||||
streamData = open_stream_ext(streamFile,"sns");
|
||||
if (!streamData) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamData, 0x00, 0x00, meta_EA_SNR_SNS);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
if (streamData) close_streamfile(streamData);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (streamData) close_streamfile(streamData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .SPS - from EA latest games (~2014), v1 header */
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"sps"))
|
||||
goto fail;
|
||||
|
||||
/* seems to be fixed */
|
||||
if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x48000000)
|
||||
goto fail;
|
||||
|
||||
start_offset = read_8bit(0x03, streamFile);
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x04, start_offset, meta_EA_SPS);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"snu"))
|
||||
goto fail;
|
||||
|
||||
/* EA SNU header (BE/LE depending on platform) */
|
||||
/* 0x00(1): related to sample rate? (03=48000)
|
||||
* 0x01(1): flags/count? (when set has extra block data before start_offset)
|
||||
* 0x02(1): always 0?
|
||||
* 0x03(1): channels? (usually matches but rarely may be 0)
|
||||
* 0x04(4): some size, maybe >>2 ~= number of frames
|
||||
* 0x08(4): start offset
|
||||
* 0x0c(4): some sub-offset? (0x20, found when @0x01 is set) */
|
||||
|
||||
/* use start_offset as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x0000FFFF) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
header_offset = 0x10; /* SNR header */
|
||||
start_offset = read_32bit(0x08,streamFile); /* SPS blocks */
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe).
|
||||
* Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
|
||||
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
|
||||
* Some .SNR include stream data, while .SPS have headers so .SPH is optional. */
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags;
|
||||
uint32_t num_samples, loop_start = 0, loop_end = 0;
|
||||
|
||||
/* EA SNR/SPH header */
|
||||
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0xf;
|
||||
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0xf;
|
||||
channel_config = read_8bit(header_offset + 0x01,streamHead);
|
||||
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead);
|
||||
flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead); /* upper nibble only? */
|
||||
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x00FFFFFF;
|
||||
/* optional, in some headers: 0x08: null? 0x0c: varies (ex. null, full size) */
|
||||
|
||||
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
|
||||
if (version != 0 && version != 1) {
|
||||
VGM_LOG("EA SNS/SPS: unknown version\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset, 0x01: loop? */
|
||||
if (flags != 0x60 && flags != 0x40 && flags != 0x20 && flags != 0x00) {
|
||||
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
|
||||
}
|
||||
|
||||
/* seen in sfx and Dead Space ambient tracks */
|
||||
if (flags & 0x20) {
|
||||
loop_flag = 1;
|
||||
loop_start = 0;
|
||||
loop_end = num_samples;
|
||||
}
|
||||
|
||||
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
|
||||
//channel_count = ((channel_config >> 2) & 0xf) + 1; /* likely, but better fail with unknown values */
|
||||
switch(channel_config) {
|
||||
case 0x00: channel_count = 1; break;
|
||||
case 0x04: channel_count = 2; break;
|
||||
case 0x0c: channel_count = 4; break;
|
||||
case 0x14: channel_count = 6; break;
|
||||
case 0x1c: channel_count = 8; break;
|
||||
default:
|
||||
VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
vgmstream->meta_type = meta_type;
|
||||
|
||||
/* EA decoder list and known internal FourCCs */
|
||||
switch(codec) {
|
||||
|
||||
case 0x02: /* "P6B0": PCM16BE (NBA Jam Wii) */
|
||||
vgmstream->coding_type = coding_PCM16_int;
|
||||
vgmstream->codec_endian = 1;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x03: { /* "EXm0": EA-XMA (Dante's Inferno X360) */
|
||||
uint8_t buf[0x100];
|
||||
int bytes, block_size, block_count;
|
||||
size_t stream_size, virtual_size;
|
||||
ffmpeg_custom_config cfg = {0};
|
||||
|
||||
stream_size = get_streamfile_size(streamData) - start_offset;
|
||||
virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamData);
|
||||
block_size = 0x10000; /* todo unused and not correctly done by the parser */
|
||||
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
cfg.type = FFMPEG_EA_XMA;
|
||||
cfg.virtual_size = virtual_size;
|
||||
cfg.channels = vgmstream->channels;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_config(streamData, buf,bytes, start_offset,stream_size, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
|
||||
vgmstream->coding_type = coding_EA_XAS;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x05: /* "EL31": EALayer3 v1 (Need for Speed: Hot Pursuit PS3) */
|
||||
case 0x06: /* "L32P": EALayer3 v2 "PCM" (Battlefield 1943 PS3) */
|
||||
case 0x07: { /* "L32S": EALayer3 v2 "Spike" (Dante's Inferno PS3) */
|
||||
mpeg_custom_config cfg = {0};
|
||||
off_t mpeg_start_offset = start_offset + 0x08;
|
||||
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 0x00: /* "NONE" (internal 'codec not set' flag) */
|
||||
case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
|
||||
case 0x08: /* ? */
|
||||
case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
|
||||
case 0x0a: /* EATrax (ATRAC9 variant, has deflated fillers a la EA-XMA) */
|
||||
case 0x0b: /* ? */
|
||||
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
|
||||
case 0x0d: /* ? */
|
||||
case 0x0e: /* ? */
|
||||
case 0x0f: /* ? */
|
||||
default:
|
||||
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
if (!vgmstream_open_stream(vgmstream,streamData,start_offset))
|
||||
goto fail;
|
||||
|
||||
if (vgmstream->layout_type == layout_blocked_ea_sns)
|
||||
block_update_ea_sns(start_offset, vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -140,7 +140,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
/* use header size as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x000F0000) { /* todo not very accurate */
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
@ -311,14 +311,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
#ifdef VGM_USE_MPEG
|
||||
case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */
|
||||
case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */
|
||||
mpeg_custom_config cfg;
|
||||
mpeg_custom_config cfg = {0};
|
||||
off_t mpeg_start_offset = is_bnk ?
|
||||
start_offset :
|
||||
get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea);
|
||||
if (!mpeg_start_offset) goto fail;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
@ -326,14 +324,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
}
|
||||
|
||||
case EA_CODEC2_EALAYER3: { /* MP3 variant */
|
||||
mpeg_custom_config cfg;
|
||||
mpeg_custom_config cfg = {0};
|
||||
off_t mpeg_start_offset = is_bnk ?
|
||||
start_offset :
|
||||
get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea);
|
||||
if (!mpeg_start_offset) goto fail;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
@ -343,6 +339,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
|
||||
case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */
|
||||
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
|
||||
vgmstream->coding_type = coding_EA_MT;
|
||||
VGM_LOG("mt: codec=%x, cv=%x, v=%x\n", ea->codec2, ea->codec_version, ea->version);
|
||||
vgmstream->codec_data = init_ea_mt(vgmstream->channels, ea->version == EA_VERSION_V3);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
break;
|
||||
|
||||
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
|
||||
default:
|
||||
VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
|
||||
@ -401,15 +403,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset channel sub offset for codecs using it */
|
||||
if (vgmstream->coding_type == coding_EA_XA
|
||||
|| vgmstream->coding_type == coding_EA_XA_int
|
||||
|| vgmstream->coding_type == coding_EA_XA_V2) {
|
||||
for(i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].channel_start_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* setup first block to update offsets */
|
||||
|
@ -1,186 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2) */
|
||||
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags;
|
||||
uint32_t num_samples, loop_start = 0, loop_end = 0;
|
||||
off_t start_offset, header_offset;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"snu"))
|
||||
goto fail;
|
||||
|
||||
/* EA SNU header (BE/LE depending on platform) */
|
||||
/* 0x00(1): related to sample rate? (03=48000)
|
||||
* 0x01(1): flags/count? (when set has extra block data before start_offset)
|
||||
* 0x02(1): always 0?
|
||||
* 0x03(1): channels? (usually matches but rarely may be 0)
|
||||
* 0x04(4): some size, maybe >>2 ~= number of frames
|
||||
* 0x08(4): start offset
|
||||
* 0x0c(4): some sub-offset? (0x20, found when @0x01 is set) */
|
||||
|
||||
/* use start_offset as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x0000FFFF) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
header_offset = 0x10; /* points to EA SNH/SPH header */
|
||||
start_offset = read_32bit(0x08,streamFile); /* points to EA SNS/SPS blocks */
|
||||
|
||||
|
||||
/* Beyond is the newest EA header (from EAAudioCore library) still generated by sx.exe.
|
||||
* Its audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
|
||||
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
|
||||
* Some .SNR include stream data, while most (all?) .SPS have headers so .SPH is optional. */
|
||||
|
||||
/* EA SNR header */
|
||||
version = (read_8bit(header_offset + 0x00,streamFile) >> 4) & 0xf;
|
||||
codec = (read_8bit(header_offset + 0x00,streamFile) >> 0) & 0xf;
|
||||
channel_config = read_8bit(header_offset + 0x01,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamFile);
|
||||
flags = (uint8_t)read_8bit(header_offset + 0x04,streamFile); /* upper nibble only? */
|
||||
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamFile) & 0x00FFFFFF;
|
||||
/* optional, in some headers:
|
||||
* 0x08: null?
|
||||
* 0x0c: varies (ex. null, full size) */
|
||||
/* headered .SPS start with an id of 0x480000xx (not present in .SPH = not part of the header) */
|
||||
|
||||
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
|
||||
if (version != 0 && version != 1) {
|
||||
VGM_LOG("EA SNS/SPS: unknown version\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* & 0x40: stream asset?, 0x20: full loop?, 0x00: RAM asset?, 0x01: loop? */
|
||||
if (flags != 0x60 && flags != 0x40) {
|
||||
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
|
||||
}
|
||||
|
||||
/* full loop seen in Dead Space ambient tracks */
|
||||
if (flags == 0x60) {
|
||||
VGM_LOG("flg=%x\n",flags);
|
||||
loop_flag = 1;
|
||||
loop_start = 0;
|
||||
loop_end = num_samples;
|
||||
}
|
||||
|
||||
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
|
||||
//channel_count = ((channel_config >> 2) & 0xf) + 1;
|
||||
switch(channel_config) {
|
||||
case 0x00: channel_count = 1; break;
|
||||
case 0x04: channel_count = 2; break;
|
||||
case 0x0c: channel_count = 4; break;
|
||||
case 0x14: channel_count = 6; break;
|
||||
case 0x1c: channel_count = 8; break;
|
||||
default:
|
||||
VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->meta_type = meta_EA_SNU;
|
||||
|
||||
/* EA decoder list and known internal FourCCs */
|
||||
switch(codec) {
|
||||
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
|
||||
vgmstream->coding_type = coding_EA_XAS;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
|
||||
#if 0
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x07: { /* "L32S": EALayer3 v2 "S" (Dante's Inferno PS3) */
|
||||
mpeg_custom_config cfg;
|
||||
off_t mpeg_start_offset = start_offset + 0x08;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL32S, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x03: { /* "EXm0": EA-XMA (Dante's Inferno X360) */
|
||||
uint8_t buf[0x100];
|
||||
int bytes, block_size, block_count;
|
||||
size_t stream_size, virtual_size;
|
||||
ffmpeg_custom_config cfg;
|
||||
|
||||
stream_size = get_streamfile_size(streamFile) - start_offset;
|
||||
virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamFile);
|
||||
block_size = 0x10000; /* todo unused and not correctly done by the parser */
|
||||
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
|
||||
cfg.type = FFMPEG_EA_XMA;
|
||||
cfg.virtual_size = virtual_size;
|
||||
cfg.channels = vgmstream->channels;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,stream_size, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x00: /* "NONE" (internal codec not set flag) */
|
||||
case 0x01: /* not used/reserved? MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
|
||||
case 0x02: /* "P6B0": PCM16BE */
|
||||
case 0x05: /* "EL31": EALayer3 v1 b (with PCM blocks in normal EA-frames?) */
|
||||
case 0x06: /* "L32P": EALayer3 v2 "P" */
|
||||
|
||||
case 0x08: /* ? */
|
||||
case 0x09: /* EASpeex? "Esp0" (libspeex variant) */
|
||||
case 0x0a: /* EATrax (ATRAC9 variant, deflated frames) */
|
||||
case 0x0b: /* ? */
|
||||
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + Opus standard? packet) */
|
||||
case 0x0d: /* ? */
|
||||
case 0x0e: /* ? */
|
||||
case 0x0f: /* ? */
|
||||
default:
|
||||
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
if (vgmstream->layout_type == layout_blocked_ea_sns)
|
||||
block_update_ea_sns(start_offset, vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
107
src/meta/flx.c
Normal file
107
src/meta/flx.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* FLX - from Ultima IX (.FLX is actually an archive format with sometimes sound data, let's support both anyway) */
|
||||
VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, stream_offset = 0;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, codec;
|
||||
int total_streams = 0, target_stream = streamFile->stream_index;
|
||||
|
||||
|
||||
/* check extensions (.flx: name of archive, files inside don't have extensions) */
|
||||
if (!check_extensions(streamFile,"flx"))
|
||||
goto fail;
|
||||
|
||||
/* all spaces up to 0x50 = archive FLX */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x20202020 && read_32bitBE(0x40,streamFile) == 0x20202020) {
|
||||
int i;
|
||||
int entries = read_32bitLE(0x50,streamFile);
|
||||
off_t offset = 0x80;
|
||||
|
||||
if (read_32bitLE(0x54,streamFile) != 0x02
|
||||
|| read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t entry_offset = read_32bitLE(offset + 0x00, streamFile);
|
||||
/* 0x04: stream size */
|
||||
offset += 0x08;
|
||||
|
||||
if (entry_offset != 0x00)
|
||||
total_streams++; /* many entries are empty */
|
||||
if (total_streams == target_stream && stream_offset == 0)
|
||||
stream_offset = entry_offset; /* found but let's keep adding total_streams */
|
||||
}
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
if (stream_offset == 0x00) goto fail;
|
||||
}
|
||||
else {
|
||||
stream_offset = 0x00;
|
||||
}
|
||||
|
||||
if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10)
|
||||
goto fail;
|
||||
data_size = read_32bitLE(stream_offset + 0x28,streamFile);
|
||||
channel_count = read_32bitLE(stream_offset + 0x34,streamFile);
|
||||
codec = read_32bitLE(stream_offset + 0x38,streamFile);
|
||||
loop_flag = (channel_count > 1); /* full seamless repeats in music */
|
||||
start_offset = stream_offset + 0x3c;
|
||||
/* 0x00: id */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile);
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->meta_type = meta_PC_FLX;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* PCM (sfx) */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x01: /* EA-XA (music, sfx) */
|
||||
vgmstream->coding_type = channel_count > 1 ? coding_EA_XA : coding_EA_XA_int;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bitLE(stream_offset + 0x28,streamFile) / 0x0f*channel_count * 28; /* ea_xa_bytes_to_samples */
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
break;
|
||||
|
||||
case 0x02: /* EA-MT (voices) */
|
||||
vgmstream->coding_type = coding_EA_MT;
|
||||
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->num_samples = read_32bitLE(start_offset,streamFile);
|
||||
start_offset += 0x04;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("FLX: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
read_string(vgmstream->stream_name,0x20+1, stream_offset + 0x04,streamFile);
|
||||
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, i;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"xa"))
|
||||
@ -18,30 +18,22 @@ VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) {
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_16bitLE(0x0A,streamFile);
|
||||
start_offset = 0x18;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
start_offset = 0x18;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
|
||||
vgmstream->coding_type = coding_MAXIS_XA;
|
||||
vgmstream->num_samples = read_32bitLE(0x04,streamFile)/2/channel_count;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_MAXIS_XA;
|
||||
vgmstream->coding_type = coding_MAXIS_XA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* open streams */
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
/* fix channel offsets as needed by the codec (could be simplified) */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
vgmstream->ch[i].channel_start_offset = start_offset+i;
|
||||
vgmstream->ch[i].offset = 0;
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
|
@ -693,4 +693,14 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ezw(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_flx(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
75
src/meta/ngc_vid1.c
Normal file
75
src/meta/ngc_vid1.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* VID1 - from Neversoft games (Gun, Tony Hawk's American Wasteland GC) */
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset, header_size;
|
||||
int loop_flag = 0, channel_count;
|
||||
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
/* chunked/blocked format containing video or audio frames */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x56494431) /* "VID1" */
|
||||
goto fail;
|
||||
|
||||
/* find actual header start/size in the chunks */
|
||||
{
|
||||
header_offset = read_32bitBE(0x04, streamFile);
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
start_offset = header_offset + read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
/* videos have VIDH before AUDH, and VIDD in blocks, but aren't parsed ATM */
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x41554448) /* "AUDH" */
|
||||
goto fail;
|
||||
header_size = read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x56415544) /* "VAUD" (Vorbis audio?) */
|
||||
goto fail;
|
||||
header_offset += 0x04;
|
||||
header_size -= 0x10;
|
||||
|
||||
}
|
||||
channel_count = read_8bit(header_offset + 0x04,streamFile);
|
||||
/* other values unknown, maybe related to vorbis (ex. bitrate/encoding modes) */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(header_offset + 0x00, streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(header_offset + 0x1c, streamFile);
|
||||
vgmstream->meta_type = meta_NGC_VID1;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -266,16 +266,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
&& strcasecmp("med",filename_extension(filename))
|
||||
)
|
||||
{
|
||||
if (!strcasecmp("mwv",filename_extension(filename)))
|
||||
if (!strcasecmp("mwv",filename_extension(filename))) {
|
||||
mwv = 1;
|
||||
else if (!strcasecmp("sns",filename_extension(filename)))
|
||||
}
|
||||
else if (!strcasecmp("sns",filename_extension(filename))) {
|
||||
sns = 1;
|
||||
}
|
||||
#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG)
|
||||
else if ( check_extensions(streamFile, "at3,rws") ) /* Renamed .RWS AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP) */
|
||||
/* .RWS: AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP)
|
||||
* .AUD: EA Replay */
|
||||
else if ( check_extensions(streamFile, "at3,rws,aud") ) {
|
||||
at3 = 1;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* check header */
|
||||
|
@ -571,6 +571,22 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Myst IV Demo (2004)(PC) (final game is different) */
|
||||
if (sb->version == 0x00100000 && is_sb0) {
|
||||
sb->section1_entry_size = 0x68;
|
||||
sb->section2_entry_size = 0xa4;
|
||||
|
||||
sb->external_flag_offset = 0x24;
|
||||
sb->num_samples_offset = 0x34;
|
||||
sb->sample_rate_offset = 0x44;
|
||||
sb->channels_offset = 0x4c;
|
||||
sb->stream_type_offset = 0x50;
|
||||
sb->stream_name_offset = 0x54;
|
||||
|
||||
sb->has_internal_names = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prince of Persia: Warrior Within (2004)(PC) */
|
||||
if (sb->version == 0x00120009 && is_sb0) {
|
||||
sb->section1_entry_size = 0x6c;
|
||||
@ -761,5 +777,36 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TMNT (2007)(PS2) */
|
||||
if (sb->version == 0x00190002 && is_sb1) {
|
||||
sb->section1_entry_size = 0x48;
|
||||
sb->section2_entry_size = 0x5c;
|
||||
|
||||
sb->external_flag_offset = 0;
|
||||
sb->channels_offset = 0x28;
|
||||
sb->sample_rate_offset = 0x2c;
|
||||
//sb->num_samples_offset = 0x34 or 0x3c /* varies */
|
||||
sb->extra_name_offset = 0x44;
|
||||
sb->stream_type_offset = 0x48;
|
||||
|
||||
sb->has_extra_name_flag = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TMNT (2007)(GC) */
|
||||
if (sb->version == 0x00190002 && is_sb3) { /* same as 0x00190003 */
|
||||
sb->section1_entry_size = 0x68;
|
||||
sb->section2_entry_size = 0x6c;
|
||||
|
||||
sb->external_flag_offset = 0x28; /* maybe 0x2c */
|
||||
sb->channels_offset = 0x3c;
|
||||
sb->sample_rate_offset = 0x40;
|
||||
sb->num_samples_offset = 0x48;
|
||||
sb->stream_type_offset = 0x5c;
|
||||
sb->extra_name_offset = 0x58;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
102
src/meta/vxn.c
Normal file
102
src/meta/vxn.c
Normal file
@ -0,0 +1,102 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* VXN - from Gameloft mobile games */
|
||||
VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples;
|
||||
off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00;
|
||||
size_t stream_size;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
|
||||
/* check extensions */
|
||||
if (!check_extensions(streamFile,"vxn"))
|
||||
goto fail;
|
||||
|
||||
/* check header/version chunk (RIFF-like format with many custom chunks) */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x566F784E) /* "VoxN" */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x10,streamFile) != get_streamfile_size(streamFile) )
|
||||
goto fail;
|
||||
|
||||
if (!find_chunk_le(streamFile, 0x41666D74,first_offset,0, &chunk_offset,NULL)) /* "Afmt" */
|
||||
goto fail;
|
||||
codec = (uint16_t)read_16bitLE(chunk_offset+0x00, streamFile);
|
||||
channel_count = (uint16_t)read_16bitLE(chunk_offset+0x02, streamFile);
|
||||
sample_rate = read_32bitLE(chunk_offset+0x04, streamFile);
|
||||
block_align = (uint16_t)read_16bitLE(chunk_offset+0x08, streamFile);
|
||||
bits = (uint16_t)read_16bitLE(chunk_offset+0x0a, streamFile);
|
||||
|
||||
/* files are divided into segment subsongs, often a leadout and loop in that order
|
||||
* (the "Plst" and "Rule" chunks may have order info) */
|
||||
if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */
|
||||
goto fail;
|
||||
total_streams = read_32bitLE(chunk_offset+0x00, streamFile);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
|
||||
stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile);
|
||||
stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile);
|
||||
num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile);
|
||||
|
||||
if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */
|
||||
goto fail;
|
||||
start_offset = chunk_offset + stream_offset;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->num_streams = total_streams;
|
||||
|
||||
vgmstream->meta_type = meta_VXN;
|
||||
|
||||
switch (codec) {
|
||||
|
||||
case 0x0002: /* MSADPCM (ex. Asphalt 7) */
|
||||
if (bits != 4) goto fail;
|
||||
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case 0x0011: /* MS-IMA (ex. Asphalt 6) */
|
||||
if (bits != 16) goto fail;
|
||||
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0800: { /* Musepack (ex. Asphalt Xtreme) */
|
||||
ffmpeg_codec_data * ffmpeg_data = NULL;
|
||||
if (bits != 0xFFFF) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("VXN: unknown codec 0x%02x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -365,7 +365,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_pc_ast,
|
||||
init_vgmstream_naac,
|
||||
init_vgmstream_ubi_sb,
|
||||
init_vgmstream_ezw,
|
||||
init_vgmstream_ezw,
|
||||
init_vgmstream_vxn,
|
||||
init_vgmstream_ea_snr_sns,
|
||||
init_vgmstream_ea_sps,
|
||||
init_vgmstream_ngc_vid1,
|
||||
init_vgmstream_flx,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
@ -517,6 +522,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
reset_hca(vgmstream);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_EA_MT) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
if (vgmstream->coding_type==coding_MP4_AAC) {
|
||||
reset_mp4_aac(vgmstream);
|
||||
@ -697,6 +706,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_EA_MT) {
|
||||
free_ea_mt(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
if (vgmstream->coding_type==coding_FFmpeg) {
|
||||
free_ffmpeg(vgmstream->codec_data);
|
||||
@ -1058,9 +1072,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_EA_XA:
|
||||
case coding_EA_XA_int:
|
||||
case coding_EA_XA_V2:
|
||||
return 28;
|
||||
case coding_MAXIS_XA:
|
||||
return 14*vgmstream->channels;
|
||||
case coding_MAXIS_XA:
|
||||
return 28;
|
||||
case coding_EA_XAS:
|
||||
return 128;
|
||||
case coding_WS:
|
||||
@ -1106,11 +1119,13 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_LSF:
|
||||
return 54;
|
||||
case coding_MTAF:
|
||||
return 0x80*2;
|
||||
return 128*2;
|
||||
case coding_MTA2:
|
||||
return 0x80*2;
|
||||
return 128*2;
|
||||
case coding_MC3:
|
||||
return 10;
|
||||
case coding_EA_MT:
|
||||
return 432;
|
||||
case coding_CRI_HCA:
|
||||
return clHCA_samplesPerBlock;
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
@ -1190,6 +1205,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_G721:
|
||||
case coding_SNDS_IMA:
|
||||
case coding_OTNS_IMA:
|
||||
return 0;
|
||||
case coding_UBI_IMA: /* variable (PCM then IMA) */
|
||||
return 0;
|
||||
case coding_NGC_AFC:
|
||||
@ -1215,7 +1231,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_MAXIS_XA:
|
||||
return 0x0F*vgmstream->channels;
|
||||
case coding_EA_XA_V2:
|
||||
return 1; /* the frame is variant in size (ADPCM frames of 0x0F or PCM frames) */
|
||||
return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */
|
||||
case coding_EA_XAS:
|
||||
return 0x4c*vgmstream->channels;
|
||||
case coding_WS:
|
||||
@ -1251,7 +1267,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_MTA2:
|
||||
return 0x90;
|
||||
case coding_MC3:
|
||||
return 4;
|
||||
return 0x04;
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -1843,6 +1861,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
chan);
|
||||
}
|
||||
break;
|
||||
case coding_EA_MT:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do,
|
||||
chan);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1918,6 +1943,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
loop_hca(vgmstream);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_start_sample);
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
if (vgmstream->coding_type==coding_ogg_vorbis) {
|
||||
seek_ogg_vorbis(vgmstream, vgmstream->loop_sample);
|
||||
@ -2514,6 +2543,11 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
||||
}
|
||||
}
|
||||
|
||||
/* EA-MT decoder is a bit finicky and needs this when channel offsets change */
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
flush_ea_mt(vgmstream);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
|
@ -161,6 +161,8 @@ typedef enum {
|
||||
coding_NWA4,
|
||||
coding_NWA5,
|
||||
|
||||
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
|
||||
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
@ -628,7 +630,12 @@ typedef enum {
|
||||
meta_PC_AST, /* Dead Rising (PC) */
|
||||
meta_NAAC, /* Namco AAC (3DS) */
|
||||
meta_UBI_SB, /* Ubisoft banks */
|
||||
meta_EZW, /* EZ2DJ (Arcade) EZWAV */
|
||||
meta_EZW, /* EZ2DJ (Arcade) EZWAV */
|
||||
meta_VXN, /* Gameloft mobile games */
|
||||
meta_EA_SNR_SNS, /* Electronic Arts SNR+SNS (Burnout Paradise) */
|
||||
meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */
|
||||
meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */
|
||||
meta_PC_FLX, /* Ultima IX PC */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
@ -801,11 +808,12 @@ typedef struct {
|
||||
|
||||
/* custom Vorbis modes */
|
||||
typedef enum {
|
||||
VORBIS_FSB, /* simplified/external setup packets, custom packet headers */
|
||||
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_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */
|
||||
VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */
|
||||
VORBIS_OGL, /* Shin'en OGL: custom packet headers */
|
||||
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
|
||||
VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */
|
||||
//VORBIS_LYN /* Ubisoft LyN: two interleaved Ogg (including setup, duplicated) */
|
||||
} vorbis_custom_t;
|
||||
|
||||
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
|
||||
@ -856,6 +864,9 @@ typedef struct {
|
||||
uint8_t prev_blockflag; /* blockflag in the last decoded packet */
|
||||
/* Ogg-style Vorbis: packet within a page */
|
||||
int current_packet;
|
||||
/* reference for page/blocks */
|
||||
off_t block_offset;
|
||||
size_t block_size;
|
||||
|
||||
} vorbis_custom_codec_data;
|
||||
#endif
|
||||
@ -870,9 +881,10 @@ 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 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_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */
|
||||
MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */
|
||||
MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */
|
||||
MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */
|
||||
MPEG_LYN, /* N streams of fixed interleave */
|
||||
MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */
|
||||
} mpeg_custom_t;
|
||||
@ -894,6 +906,12 @@ typedef struct {
|
||||
|
||||
/* 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 */
|
||||
size_t buffer_size;
|
||||
size_t bytes_in_buffer;
|
||||
int buffer_full; /* raw buffer has been filled */
|
||||
int buffer_used; /* raw buffer has been fed to the decoder */
|
||||
mpg123_handle *m; /* MPEG decoder */
|
||||
|
||||
uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */
|
||||
@ -905,16 +923,18 @@ typedef struct {
|
||||
size_t current_size_target; /* max data, until something happens */
|
||||
size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
|
||||
|
||||
|
||||
} mpeg_custom_stream;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer; /* internal raw data buffer */
|
||||
/* regular/single MPEG internals */
|
||||
uint8_t *buffer; /* raw data buffer */
|
||||
size_t buffer_size;
|
||||
size_t bytes_in_buffer;
|
||||
int buffer_full; /* raw buffer has been filled */
|
||||
int buffer_used; /* raw buffer has been fed to the decoder */
|
||||
|
||||
mpg123_handle *m; /* regular/single MPEG decoder */
|
||||
mpg123_handle *m; /* MPEG decoder */
|
||||
struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */
|
||||
|
||||
/* for internal use, assumed to be constant for all frames */
|
||||
int channels_per_frame;
|
||||
@ -1128,6 +1148,12 @@ typedef struct {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int pcm_blocks;
|
||||
int utk_context_size;
|
||||
void** utk_context;
|
||||
} ea_mt_codec_data;
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------*/
|
||||
/* vgmstream "public" API */
|
||||
|
45
test/test.c
45
test/test.c
@ -64,6 +64,7 @@ int main(int argc, char ** argv) {
|
||||
int i,j,k;
|
||||
int opt;
|
||||
/* config */
|
||||
char * infilename = NULL;
|
||||
char * outfilename = NULL;
|
||||
char * outfilename_reset = NULL;
|
||||
int ignore_loop = 0;
|
||||
@ -156,6 +157,15 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
infilename = argv[optind];
|
||||
|
||||
#ifdef WIN32
|
||||
/* make stdout output work with windows */
|
||||
if (play_sdtout) {
|
||||
_setmode(fileno(stdout),_O_BINARY);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (play_forever && !play_sdtout) {
|
||||
fprintf(stderr,"A file of infinite size? Not likely.\n");
|
||||
return 1;
|
||||
@ -166,13 +176,6 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/* make stdout output work with windows */
|
||||
if (play_sdtout) {
|
||||
_setmode(fileno(stdout),_O_BINARY);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ignore_loop && force_loop) {
|
||||
fprintf(stderr,"-e and -i are incompatible\n");
|
||||
return 1;
|
||||
@ -182,25 +185,27 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
if (force_loop && really_force_loop) {
|
||||
fprintf(stderr,"-E and -e are somewhat redundant, are you confused?\n");
|
||||
fprintf(stderr,"-E and -e are incompatible\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* manually init streamfile to pass the stream index */
|
||||
{
|
||||
//s = init_vgmstream(argv[optind]);
|
||||
STREAMFILE *streamFile = open_stdio_streamfile(argv[optind]);
|
||||
if (streamFile) {
|
||||
streamFile->stream_index = stream_index;
|
||||
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
|
||||
close_streamfile(streamFile);
|
||||
//s = init_vgmstream(infilename);
|
||||
STREAMFILE *streamFile = open_stdio_streamfile(infilename);
|
||||
if (!streamFile) {
|
||||
fprintf(stderr,"file %s not found\n",infilename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
streamFile->stream_index = stream_index;
|
||||
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
|
||||
close_streamfile(streamFile);
|
||||
|
||||
if (!vgmstream) {
|
||||
fprintf(stderr,"failed opening %s\n",argv[optind]);
|
||||
return 1;
|
||||
if (!vgmstream) {
|
||||
fprintf(stderr,"failed opening %s\n",infilename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* force only if there aren't already loop points */
|
||||
@ -274,10 +279,10 @@ int main(int argc, char ** argv) {
|
||||
printf("set loop=0\n");
|
||||
}
|
||||
else if (print_metaonly) {
|
||||
printf("metadata for %s\n",argv[optind]);
|
||||
printf("metadata for %s\n",infilename);
|
||||
}
|
||||
else {
|
||||
printf("decoding %s\n",argv[optind]);
|
||||
printf("decoding %s\n",infilename);
|
||||
}
|
||||
}
|
||||
if (!play_sdtout && !print_adxencd && !print_oggenc && !print_batchvar) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user