mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
Merge pull request #114 from bnnm/custom-vorbis-mpeg
Custom Vorbis/MPEG
This commit is contained in:
commit
f9e561e08a
@ -137,34 +137,18 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream);
|
||||
void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_ogg_vorbis(ogg_vorbis_codec_data *data);
|
||||
|
||||
/* fsb_vorbis_decoder */
|
||||
vorbis_codec_data * init_fsb_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, uint32_t setup_id);
|
||||
void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_fsb_vorbis(VGMSTREAM *vgmstream);
|
||||
void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_fsb_vorbis(vorbis_codec_data *data);
|
||||
|
||||
/* wwise_vorbis_decoder */
|
||||
vorbis_codec_data * init_wwise_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp,
|
||||
wwise_setup_type setup_type, wwise_header_type header_type, wwise_packet_type packet_type, int big_endian);
|
||||
void decode_wwise_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_wwise_vorbis(VGMSTREAM *vgmstream);
|
||||
void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_wwise_vorbis(vorbis_codec_data *data);
|
||||
|
||||
/* ogl_vorbis_decoder */
|
||||
vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, off_t * data_start_offset);
|
||||
void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_ogl_vorbis(VGMSTREAM *vgmstream);
|
||||
void seek_ogl_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_ogl_vorbis(vorbis_codec_data *data);
|
||||
/* vorbis_custom_decoder */
|
||||
vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config);
|
||||
void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_vorbis_custom(VGMSTREAM *vgmstream);
|
||||
void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_vorbis_custom(vorbis_custom_codec_data *data);
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* mpeg_decoder */
|
||||
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels);
|
||||
mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, mpeg_interleave_type interleave_type, uint32_t interleave_value);
|
||||
mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count);
|
||||
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config);
|
||||
|
||||
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do);
|
||||
|
@ -1,422 +0,0 @@
|
||||
#include "coding.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */
|
||||
|
||||
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
||||
#include "fsb_vorbis_data.h"
|
||||
#endif
|
||||
|
||||
#define FSB_VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm);
|
||||
static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long);
|
||||
static int vorbis_make_header_comment(uint8_t * buf, size_t bufsize);
|
||||
static int vorbis_make_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
|
||||
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
|
||||
/**
|
||||
* Inits a raw FSB vorbis stream.
|
||||
*
|
||||
* Vorbis packets are stored in .ogg, which is divided into ogg pages/packets, and the first packets contain necessary
|
||||
* vorbis setup. For raw vorbis the setup is stored it elsewhere (i.e.- in the .exe), presumably to shave off some kb
|
||||
* per stream and codec startup time. We'll read the external setup and raw data and decode it with libvorbis.
|
||||
*
|
||||
* FSB references the external setup with the setup_id, and the raw vorbis packets have mini headers with the block size.
|
||||
*
|
||||
* Format info and references from python-fsb5 (https://github.com/HearthSim/python-fsb5) and
|
||||
* fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor).
|
||||
* Also from the official docs (https://www.xiph.org/vorbis/doc/libvorbis/overview.html).
|
||||
*/
|
||||
vorbis_codec_data * init_fsb_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, uint32_t setup_id) {
|
||||
vorbis_codec_data * data = NULL;
|
||||
|
||||
/* init stuff */
|
||||
data = calloc(1,sizeof(vorbis_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = FSB_VORBIS_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
|
||||
/* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks)
|
||||
* libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */
|
||||
vorbis_info_init(&data->vi);
|
||||
vorbis_comment_init(&data->vc);
|
||||
|
||||
data->op.packet = data->buffer;
|
||||
data->op.b_o_s = 1; /* fake headers start */
|
||||
|
||||
data->op.bytes = vorbis_make_header_identification(data->buffer, data->buffer_size, channels, sample_rate, 256, 2048); /* FSB default block sizes */
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
|
||||
data->op.bytes = vorbis_make_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 */
|
||||
|
||||
data->op.bytes = vorbis_make_header_setup(data->buffer, data->buffer_size, setup_id, streamfile);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
|
||||
data->op.b_o_s = 0; /* end of fake headers */
|
||||
|
||||
/* init vorbis global and block state */
|
||||
if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail;
|
||||
if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail;
|
||||
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_fsb_vorbis(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes raw FSB vorbis
|
||||
*/
|
||||
void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
|
||||
vorbis_codec_data * data = vgmstream->codec_data;
|
||||
size_t stream_size = get_streamfile_size(stream->streamfile);
|
||||
//data->op.packet = data->buffer;/* implicit from init */
|
||||
int samples_done = 0;
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
/* extra EOF check for edge cases */
|
||||
if (stream->offset > stream_size) {
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (data->samples_full) { /* read more samples */
|
||||
int samples_to_get;
|
||||
float **pcm;
|
||||
|
||||
/* get PCM samples from libvorbis buffers */
|
||||
samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm);
|
||||
if (!samples_to_get) {
|
||||
data->samples_full = 0; /* request more if empty*/
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data->samples_to_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > data->samples_to_discard)
|
||||
samples_to_get = data->samples_to_discard;
|
||||
data->samples_to_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and convert from Vorbis float pcm to 16bit pcm */
|
||||
if (samples_to_get > samples_to_do - samples_done)
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
pcm_convert_float_to_16(data, outbuf + samples_done * channels, samples_to_get, pcm);
|
||||
samples_done += samples_to_get;
|
||||
}
|
||||
|
||||
/* mark consumed samples from the buffer
|
||||
* (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */
|
||||
vorbis_synthesis_read(&data->vd, samples_to_get);
|
||||
}
|
||||
else { /* read more data */
|
||||
int rc;
|
||||
|
||||
/* this is not actually needed, but feels nicer */
|
||||
data->op.granulepos += samples_to_do;
|
||||
data->op.packetno++;
|
||||
|
||||
/* get next packet size from the FSB 16b header (doesn't count this 16b) */
|
||||
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile);
|
||||
stream->offset += 2;
|
||||
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) {
|
||||
goto decode_fail; /* eof or FSB end padding */
|
||||
}
|
||||
|
||||
/* read raw block */
|
||||
if (read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile) != data->op.bytes) {
|
||||
goto decode_fail; /* wrong packet? */
|
||||
}
|
||||
stream->offset += data->op.bytes;
|
||||
|
||||
/* parse the fake ogg packet into a logical vorbis block */
|
||||
rc = vorbis_synthesis(&data->vb,&data->op);
|
||||
if (rc == OV_ENOTAUDIO) {
|
||||
VGM_LOG("vorbis_synthesis: not audio packet @ %lx\n",stream->offset); getchar();
|
||||
continue; /* not tested */
|
||||
} else if (rc != 0) {
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
/* finally decode the logical block into samples */
|
||||
rc = vorbis_synthesis_blockin(&data->vd,&data->vb);
|
||||
if (rc != 0) {
|
||||
goto decode_fail; /* ? */
|
||||
}
|
||||
|
||||
data->samples_full = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
/* on error just put some 0 samples */
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
}
|
||||
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) {
|
||||
/* mostly from Xiph's decoder_example.c */
|
||||
int i,j;
|
||||
|
||||
/* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc)
|
||||
* to 16 bit signed PCM ints (host order) and interleave + fix clipping */
|
||||
for (i = 0; i < data->vi.channels; i++) {
|
||||
sample *ptr = outbuf + i;
|
||||
float *mono = pcm[i];
|
||||
for (j = 0; j < samples_to_do; j++) {
|
||||
int val = floor(mono[j] * 32767.f + .5f);
|
||||
if (val > 32767) val = 32767;
|
||||
if (val < -32768) val = -32768;
|
||||
|
||||
*ptr = val;
|
||||
ptr += data->vi.channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) {
|
||||
int bytes = 0x1e;
|
||||
uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
/* guetto log2 for allowed blocksizes (2-exp), could be improved */
|
||||
switch(blocksize_long) {
|
||||
case 64: exp_blocksize_0 = 6; break;
|
||||
case 128: exp_blocksize_0 = 7; break;
|
||||
case 256: exp_blocksize_0 = 8; break;
|
||||
case 512: exp_blocksize_0 = 9; break;
|
||||
case 1024: exp_blocksize_0 = 10; break;
|
||||
case 2048: exp_blocksize_0 = 11; break;
|
||||
case 4096: exp_blocksize_0 = 12; break;
|
||||
case 8192: exp_blocksize_0 = 13; break;
|
||||
default: return 0;
|
||||
}
|
||||
switch(blocksize_short) {
|
||||
case 64: exp_blocksize_1 = 6; break;
|
||||
case 128: exp_blocksize_1 = 7; break;
|
||||
case 256: exp_blocksize_1 = 8; break;
|
||||
case 512: exp_blocksize_1 = 9; break;
|
||||
case 1024: exp_blocksize_1 = 10; break;
|
||||
case 2048: exp_blocksize_1 = 11; break;
|
||||
case 4096: exp_blocksize_1 = 12; break;
|
||||
case 8192: exp_blocksize_1 = 13; break;
|
||||
default: return 0;
|
||||
}
|
||||
blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1);
|
||||
|
||||
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_8bit (buf+0x0b, channels); /* audio_channels */
|
||||
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int vorbis_make_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;
|
||||
}
|
||||
|
||||
static int vorbis_make_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
int bytes;
|
||||
|
||||
/* try to load from external files first */
|
||||
bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
/* try to locate from the precompiled list */
|
||||
bytes = load_fvs_array(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
/* not found */
|
||||
VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
STREAMFILE * streamFileSetup = NULL;
|
||||
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
|
||||
/* read "(dir/).fvs_{setup_id}" */
|
||||
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id);
|
||||
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (streamFileSetup) {
|
||||
/* file found, get contents into the buffer */
|
||||
size_t bytes = streamFileSetup->get_size(streamFileSetup);
|
||||
if (bytes > bufsize) goto fail;
|
||||
|
||||
if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes)
|
||||
goto fail;
|
||||
|
||||
streamFileSetup->close(streamFileSetup);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
STREAMFILE * streamFileSetup = NULL;
|
||||
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
|
||||
/* read "(dir/).fvs" */
|
||||
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname);
|
||||
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (streamFileSetup) {
|
||||
/* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */
|
||||
int entries, i;
|
||||
uint32_t offset = 0, size = 0;
|
||||
|
||||
if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */
|
||||
entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */
|
||||
if (entries <= 0) goto fail;
|
||||
|
||||
for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */
|
||||
if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) {
|
||||
offset = read_32bitLE(0x24 + i*0x10, streamFileSetup);
|
||||
size = read_32bitLE(0x28 + i*0x10, streamFileSetup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!size || !offset || size > bufsize) goto fail;
|
||||
|
||||
/* read into buf */
|
||||
if (read_streamfile(buf, offset, size, streamFileSetup) != size)
|
||||
goto fail;
|
||||
|
||||
streamFileSetup->close(streamFileSetup);
|
||||
return size;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
||||
int i, list_length;
|
||||
|
||||
list_length = sizeof(fvs_list) / sizeof(fvs_info);
|
||||
for (i=0; i < list_length; i++) {
|
||||
if (fvs_list[i].id == setup_id) {
|
||||
if (fvs_list[i].size > bufsize) goto fail;
|
||||
/* found: copy data as-is */
|
||||
memcpy(buf,fvs_list[i].setup, fvs_list[i].size);
|
||||
return fvs_list[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
void free_fsb_vorbis(vorbis_codec_data * data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
/* internal decoder cleanp */
|
||||
vorbis_info_clear(&data->vi);
|
||||
vorbis_comment_clear(&data->vc);
|
||||
vorbis_dsp_clear(&data->vd);
|
||||
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void reset_fsb_vorbis(VGMSTREAM *vgmstream) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = 0;
|
||||
}
|
||||
|
||||
void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = num_sample;
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||
}
|
||||
|
||||
#endif
|
249
src/coding/mpeg_custom_utils.c
Normal file
249
src/coding/mpeg_custom_utils.c
Normal file
@ -0,0 +1,249 @@
|
||||
#include "mpeg_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
||||
/* init config and validate per type */
|
||||
int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
|
||||
mpeg_frame_info info;
|
||||
|
||||
|
||||
/* get frame info at offset */
|
||||
if ( !mpeg_get_frame_info(streamFile, start_offset, &info))
|
||||
goto fail;
|
||||
switch(info.layer) {
|
||||
case 1: *coding_type = coding_MPEG_layer1; break;
|
||||
case 2: *coding_type = coding_MPEG_layer2; break;
|
||||
case 3: *coding_type = coding_MPEG_layer3; break;
|
||||
default: goto fail;
|
||||
}
|
||||
data->channels_per_frame = info.channels;
|
||||
data->samples_per_frame = info.frame_samples;
|
||||
|
||||
|
||||
/* extra checks per type */
|
||||
switch(data->type) {
|
||||
case MPEG_XVAG:
|
||||
if (data->config.chunk_size <= 0 || data->config.interleave <= 0)
|
||||
goto fail; /* needs external fixed size */
|
||||
break;
|
||||
|
||||
case MPEG_FSB:
|
||||
if (data->config.fsb_padding != 0
|
||||
&& data->config.fsb_padding != 2
|
||||
&& data->config.fsb_padding != 4
|
||||
&& data->config.fsb_padding != 16)
|
||||
goto fail; /* aligned to closest 0/2/4/16 bytes */
|
||||
|
||||
/* get find interleave to stream offsets are set up externally */
|
||||
{
|
||||
int current_data_size = info.frame_size;
|
||||
int current_padding = 0;
|
||||
if (info.layer == 3 && data->config.fsb_padding) { /* FSB padding for Layer III */
|
||||
current_padding = (current_data_size % data->config.fsb_padding)
|
||||
? data->config.fsb_padding - (current_data_size % data->config.fsb_padding)
|
||||
: 0;
|
||||
}
|
||||
|
||||
data->config.interleave = current_data_size + current_padding; /* should be constant for all stream */
|
||||
}
|
||||
break;
|
||||
|
||||
case MPEG_P3D:
|
||||
if (data->config.interleave <= 0)
|
||||
goto fail; /* needs external fixed size */
|
||||
break;
|
||||
|
||||
case MPEG_LYN:
|
||||
case MPEG_AWC:
|
||||
goto fail; /* not fully implemented */
|
||||
|
||||
case MPEG_STANDARD:
|
||||
case MPEG_AHX:
|
||||
case MPEG_EA:
|
||||
if (data->channels_per_frame != data->config.channels)
|
||||
goto fail; /* no multichannel expected */
|
||||
break;
|
||||
|
||||
default:
|
||||
break; /* nothing special needed */
|
||||
}
|
||||
|
||||
|
||||
/* unknown encryption */
|
||||
if (data->type == MPEG_AHX && data->config.encryption) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */
|
||||
switch(data->type) {
|
||||
case MPEG_AHX: data->skip_samples = 480; break; /* observed default */
|
||||
case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */
|
||||
default: break;
|
||||
}
|
||||
data->samples_to_discard = data->skip_samples;
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* writes data to the buffer and moves offsets */
|
||||
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
|
||||
mpeg_frame_info info;
|
||||
size_t current_data_size = 0;
|
||||
size_t current_padding = 0;
|
||||
size_t current_interleave = 0;
|
||||
|
||||
|
||||
/* Get data size to give to the decoder, per stream. Usually 1 frame at a time,
|
||||
* but doesn't really need to be a full frame (decoder would request more data). */
|
||||
switch(data->type) {
|
||||
|
||||
case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */
|
||||
current_data_size = data->config.chunk_size;
|
||||
current_interleave = data->config.interleave; /* big interleave */
|
||||
break;
|
||||
|
||||
case MPEG_FSB: /* frames with padding + interleave */
|
||||
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
|
||||
goto fail;
|
||||
current_data_size = info.frame_size;
|
||||
|
||||
/* get FSB padding for Layer III (Layer II doesn't use it, and Layer I doesn't seem to be supported) */
|
||||
/* Padding sometimes contains garbage like the next frame header so we can't feed it to mpg123 or it gets confused. */
|
||||
if (info.layer == 3 && data->config.fsb_padding) {
|
||||
current_padding = (current_data_size % data->config.fsb_padding)
|
||||
? data->config.fsb_padding - (current_data_size % data->config.fsb_padding)
|
||||
: 0;
|
||||
}
|
||||
|
||||
/* frame interleave (ie. read 1 data-frame, skip 1 data-frame per stream) */
|
||||
current_interleave = data->config.interleave; /* constant, current_size+current_padding */
|
||||
|
||||
VGM_ASSERT(current_interleave != current_data_size+current_padding,
|
||||
"MPEG FSB: non-constant interleave found @ 0x%08lx\n", stream->offset);
|
||||
break;
|
||||
|
||||
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
|
||||
current_data_size = data->config.interleave / 4; /* to ensure we don't feed mpg123 too much */
|
||||
current_interleave = data->config.interleave; /* fixed interleave (0x400) */
|
||||
//todo: last block is smaller that interleave, not sure how it's divided
|
||||
break;
|
||||
|
||||
default: /* standard frames (CBR or VBR) */
|
||||
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
|
||||
goto fail;
|
||||
current_data_size = info.frame_size;
|
||||
break;
|
||||
}
|
||||
if (!current_data_size || current_data_size > data->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);
|
||||
|
||||
|
||||
/* update offsets */
|
||||
stream->offset += current_data_size + current_padding;
|
||||
|
||||
/* skip interleave once block is done, if defined */
|
||||
if (current_interleave && ((stream->offset - stream->channel_start_offset) % current_interleave == 0)) {
|
||||
stream->offset += current_interleave * (data->ms_size-1); /* skip a block each stream */
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*****************/
|
||||
/* FRAME HELPERS */
|
||||
/*****************/
|
||||
|
||||
/**
|
||||
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow
|
||||
* it's wrong at times (maybe because we use an ancient version) so here we do our thing.
|
||||
*/
|
||||
int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info) {
|
||||
/* index tables */
|
||||
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
|
||||
static const int layers[4] = { -1,3,2,1 };
|
||||
static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */
|
||||
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */
|
||||
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */
|
||||
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */
|
||||
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */
|
||||
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */
|
||||
};
|
||||
static const int sample_rates[4][4] = { /* [version][sample rate index] */
|
||||
{ 44100, 48000, 32000, -1}, /* MPEG1 */
|
||||
{ 22050, 24000, 16000, -1}, /* MPEG2 */
|
||||
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
|
||||
};
|
||||
static const int channels[4] = { 2,2,2, 1 }; /* [channel] */
|
||||
static const int frame_samples[3][3] = { /* [version][layer] */
|
||||
{ 384, 1152, 1152 }, /* MPEG1 */
|
||||
{ 384, 1152, 576 }, /* MPEG2 */
|
||||
{ 384, 1152, 576 } /* MPEG2.5 */
|
||||
};
|
||||
|
||||
uint32_t header;
|
||||
int idx, padding;
|
||||
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
header = read_32bitBE(offset, streamfile);
|
||||
|
||||
if ((header >> 21) != 0x7FF) /* 31-21: sync */
|
||||
goto fail;
|
||||
|
||||
info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */
|
||||
if (info->version <= 0) goto fail;
|
||||
|
||||
info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */
|
||||
if (info->layer <= 0 || info->layer > 3) goto fail;
|
||||
|
||||
//crc = (header >> 16) & 0x1; /* 16: protected by crc? */
|
||||
|
||||
idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1)));
|
||||
info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */
|
||||
if (info->bit_rate <= 0) goto fail;
|
||||
|
||||
info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */
|
||||
if (info->sample_rate <= 0) goto fail;
|
||||
|
||||
padding = (header >> 9) & 0x1; /* 9: padding? */
|
||||
//private = (header >> 8) & 0x1; /* 8: private bit */
|
||||
|
||||
info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */
|
||||
|
||||
//js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */
|
||||
//copyright = (header >> 3) & 0x1; /* 3: copyrighted */
|
||||
//original = (header >> 2) & 0x1; /* 2: original */
|
||||
//emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */
|
||||
|
||||
info->frame_samples = frame_samples[info->version-1][info->layer-1];
|
||||
|
||||
/* calculate frame length (from hcs's fsb_mpeg) */
|
||||
switch (info->frame_samples) {
|
||||
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break;
|
||||
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break;
|
||||
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
66
src/coding/mpeg_custom_utils_ahx.c
Normal file
66
src/coding/mpeg_custom_utils_ahx.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include "mpeg_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414
|
||||
|
||||
|
||||
/* 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) {
|
||||
/* 0xFFF5E0C0 header: frame size 0x414 (160kbps, 22050Hz) but they actually are much shorter */
|
||||
size_t current_data_size = 0;
|
||||
size_t file_size = get_streamfile_size(stream->streamfile);
|
||||
|
||||
/* find actual frame size by looking for the next frame header */
|
||||
{
|
||||
uint32_t current_header = (uint32_t)read_32bitBE(stream->offset, stream->streamfile);
|
||||
off_t next_offset = 0x04;
|
||||
|
||||
while (next_offset <= MPEG_AHX_EXPECTED_FRAME_SIZE) {
|
||||
uint32_t next_header = (uint32_t)read_32bitBE(stream->offset + next_offset, stream->streamfile);
|
||||
|
||||
if (current_header == next_header) {
|
||||
current_data_size = next_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
/* AHXs end in a 0x0c footer (0x41485845 0x28632943 0x52490000 / "AHXE" "(c)C" "RI\0\0") */
|
||||
if (stream->offset + next_offset + 0x0c >= file_size) {
|
||||
current_data_size = next_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
next_offset++;
|
||||
}
|
||||
}
|
||||
if (!current_data_size || current_data_size > data->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;
|
||||
}
|
||||
|
||||
|
||||
/* read VBR frames with CBR header, 0-fill up to expected size to keep mpg123 happy */
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,current_data_size,stream->streamfile);
|
||||
memset(data->buffer + data->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - data->bytes_in_buffer);
|
||||
data->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
|
||||
|
||||
|
||||
/* encryption 0x08 modifies a few bits in the side_data every frame, here we decrypt the buffer */
|
||||
if (data->config.encryption) {
|
||||
VGM_LOG("MPEG AHX: unknown encryption\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* update offsets */
|
||||
stream->offset += current_data_size;
|
||||
if (stream->offset + 0x0c >= file_size)
|
||||
stream->offset = file_size; /* move after 0x0c footer to reach EOF (shouldn't happen normally) */
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -4,31 +4,33 @@
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
#include <mpg123.h>
|
||||
#include "mpeg_decoder.h"
|
||||
|
||||
#define AHX_EXPECTED_FRAME_SIZE 0x414
|
||||
#define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */
|
||||
|
||||
typedef struct {
|
||||
int version;
|
||||
int layer;
|
||||
int bit_rate;
|
||||
int sample_rate;
|
||||
int frame_samples;
|
||||
int frame_size; /* bytes */
|
||||
int channels;
|
||||
coding_t coding_type;
|
||||
} mpeg_frame_info;
|
||||
/* TODO list for custom decoder
|
||||
* - don't force channel param and get them from frame headers for some types (for MPEG_STANDARD)
|
||||
* - use one stream per channel and advance streams as channels are done in case of streams like 2ch+1ch+1ch+2ch (not seen)
|
||||
* (would also need to change sample_buffer copy)
|
||||
* - improve validation of channels/samples_per_frame between streams
|
||||
* - improve decoded samples to sample buffer copying (very picky with sizes)
|
||||
* - use mpg123 stream_buffer, with flags per stream, and call copy to sample_buffer when all streams have some samples
|
||||
* (so it could handle interleaved VBR frames).
|
||||
* - AHX type 8 encryption
|
||||
* - test encoder delays
|
||||
* - improve error handling
|
||||
*/
|
||||
|
||||
/* mostly arbitrary max values */
|
||||
#define MPEG_DATA_BUFFER_SIZE 0x1000
|
||||
#define MPEG_MAX_CHANNELS 16
|
||||
#define MPEG_MAX_STREAM_FRAMES 10
|
||||
|
||||
static mpg123_handle * init_mpg123_handle();
|
||||
static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size);
|
||||
static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset);
|
||||
static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info);
|
||||
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream);
|
||||
|
||||
/**
|
||||
* Inits regular MPEG.
|
||||
*/
|
||||
/* Inits regular MPEG */
|
||||
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
|
||||
@ -36,7 +38,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
@ -64,7 +66,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
rc = mpg123_decode(main_m, data->buffer,data->buffer_size, NULL,0, &bytes_done);
|
||||
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
|
||||
VGM_LOG("MPEG: unable to set up mpg123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
|
||||
goto fail; //todo handle MPG123_DONE?
|
||||
goto fail; //handle MPG123_DONE?
|
||||
}
|
||||
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */
|
||||
VGM_LOG("MPEG: unable to find mpeg data @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
|
||||
@ -86,25 +88,12 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
if ((channels != -1 && channels_per_frame != channels))
|
||||
goto fail;
|
||||
|
||||
if (mi.version == MPG123_1_0 && mi.layer == 1)
|
||||
*coding_type = coding_MPEG1_L1;
|
||||
else if (mi.version == MPG123_1_0 && mi.layer == 2)
|
||||
*coding_type = coding_MPEG1_L2;
|
||||
else if (mi.version == MPG123_1_0 && mi.layer == 3)
|
||||
*coding_type = coding_MPEG1_L3;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 1)
|
||||
*coding_type = coding_MPEG2_L1;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 2)
|
||||
*coding_type = coding_MPEG2_L2;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 3)
|
||||
*coding_type = coding_MPEG2_L3;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 1)
|
||||
*coding_type = coding_MPEG25_L1;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 2)
|
||||
*coding_type = coding_MPEG25_L2;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 3)
|
||||
*coding_type = coding_MPEG25_L3;
|
||||
else goto fail;
|
||||
switch(mi.layer) {
|
||||
case 1: *coding_type = coding_MPEG_layer1; break;
|
||||
case 2: *coding_type = coding_MPEG_layer2; break;
|
||||
case 3: *coding_type = coding_MPEG_layer3; break;
|
||||
default: goto fail;
|
||||
}
|
||||
|
||||
if (mi.layer == 1)
|
||||
samples_per_frame = 384;
|
||||
@ -116,9 +105,11 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
|
||||
samples_per_frame = 576;
|
||||
else goto fail;
|
||||
|
||||
data->sample_rate_per_frame = sample_rate_per_frame;
|
||||
data->channels_per_frame = channels_per_frame;
|
||||
data->samples_per_frame = samples_per_frame;
|
||||
if (channels_per_frame != channels)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* reinit, to ignore the reading we've done so far */
|
||||
mpg123_open_feed(main_m);
|
||||
@ -131,57 +122,42 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init interleaved MPEG (also accepts normal MPEGs, but it's less error tolerant than normal MPEG init).
|
||||
*/
|
||||
mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, mpeg_interleave_type interleave_type, uint32_t interleave_value) {
|
||||
|
||||
/* Init custom MPEG, with given type and config. */
|
||||
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
int stream_frames = 1;
|
||||
int i, ok;
|
||||
|
||||
/* init codec */
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
data->m = init_mpg123_handle();
|
||||
if (!data->m) goto fail;
|
||||
|
||||
/* interleaved setup */
|
||||
{
|
||||
mpeg_frame_info info;
|
||||
int i;
|
||||
/* keep around to decode */
|
||||
data->custom = 1;
|
||||
data->type = type;
|
||||
memcpy(&data->config, config, sizeof(mpeg_custom_config));
|
||||
data->config.channels = channels;
|
||||
|
||||
|
||||
data->interleaved = 1;
|
||||
|
||||
/* get frame info at offset */
|
||||
if ( !mpeg_get_frame_info(streamfile, start_offset, &info) )
|
||||
/* init per subtype */
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
|
||||
default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break;
|
||||
}
|
||||
if (!ok)
|
||||
goto fail;
|
||||
|
||||
*coding_type = info.coding_type;
|
||||
data->channels_per_frame = info.channels;
|
||||
data->sample_rate_per_frame = info.sample_rate;
|
||||
data->samples_per_frame = info.frame_samples;
|
||||
|
||||
data->interleave_type = interleave_type;
|
||||
data->interleave_value = interleave_value;
|
||||
|
||||
/* get frame size from the first frame as it can be used externally to calc interleave */
|
||||
if ( !update_frame_sizes(data, streamfile, start_offset) )
|
||||
goto fail;
|
||||
|
||||
/* unlikely, can fixed with bigger buffer or a feed loop */
|
||||
if (info.frame_size > data->buffer_size)
|
||||
goto fail;
|
||||
|
||||
if (channels < 1 || channels > 32) goto fail; /* arbitrary max */
|
||||
if (channels <= 0 || channels > MPEG_MAX_CHANNELS) goto fail;
|
||||
if (channels < data->channels_per_frame) goto fail;
|
||||
|
||||
//todo improve (less buffers / simplify data->ms)
|
||||
/* Init interleaved audio, which needs separate decoders per stream and frame size stuff.
|
||||
* We still leave data->m as a "base" info/format to simplify some stuff */
|
||||
/* init stream decoders (separate as MPEG frames may need N previous frames from their stream to decode) */
|
||||
data->ms_size = channels / data->channels_per_frame;
|
||||
data->ms = calloc(sizeof(mpg123_handle *), data->ms_size);
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
@ -189,41 +165,21 @@ mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t
|
||||
if (!data->ms[i]) goto fail;
|
||||
}
|
||||
|
||||
data->frame_buffer_size = sizeof(sample) * data->samples_per_frame * data->channels_per_frame;
|
||||
data->frame_buffer = calloc(sizeof(uint8_t), data->frame_buffer_size);
|
||||
if (!data->frame_buffer) goto fail;
|
||||
if (stream_frames > MPEG_MAX_STREAM_FRAMES) goto fail;
|
||||
|
||||
data->interleave_buffer_size = sizeof(sample) * data->samples_per_frame * channels;
|
||||
data->interleave_buffer = calloc(sizeof(uint8_t), data->interleave_buffer_size);
|
||||
if (!data->interleave_buffer) goto fail;
|
||||
}
|
||||
/* init stream buffer, big enough for one stream and N frames at a time (will be copied to sample buffer) */
|
||||
data->stream_buffer_size = sizeof(sample) * data->channels_per_frame * stream_frames * data->samples_per_frame;
|
||||
data->stream_buffer = calloc(sizeof(uint8_t), data->stream_buffer_size);
|
||||
if (!data->stream_buffer) goto fail;
|
||||
|
||||
/* init sample buffer, big enough for all streams/channels and N frames at a time */
|
||||
data->sample_buffer_size = sizeof(sample) * channels * stream_frames * data->samples_per_frame;
|
||||
data->sample_buffer = calloc(sizeof(uint8_t), data->sample_buffer_size);
|
||||
if (!data->sample_buffer) goto fail;
|
||||
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_mpeg(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inits MPEG for AHX, which ignores frame headers.
|
||||
*/
|
||||
mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
|
||||
/* init codec */
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
data->m = init_mpg123_handle();
|
||||
if (!data->m) goto fail;
|
||||
|
||||
/* write output */
|
||||
config->interleave = data->config.interleave; /* for FSB */
|
||||
|
||||
return data;
|
||||
|
||||
@ -249,7 +205,7 @@ static mpg123_handle * init_mpg123_handle() {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); //todo fix gapless
|
||||
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */
|
||||
mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */
|
||||
|
||||
if (mpg123_open_feed(m) != MPG123_OK) {
|
||||
@ -271,11 +227,10 @@ fail:
|
||||
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
mpeg_codec_data * data = (mpeg_codec_data *) vgmstream->codec_data;
|
||||
|
||||
/* MPEGs streams contain one or two channels, so we may only need half VGMSTREAMCHANNELs for offsets */
|
||||
if (data->interleaved) {
|
||||
decode_mpeg_interleave(vgmstream, data, outbuf, samples_to_do, channels);
|
||||
if (!data->custom) {
|
||||
decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
|
||||
} else {
|
||||
decode_mpeg_default(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
|
||||
decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels);
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +238,7 @@ void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do,
|
||||
* Decode anything mpg123 can.
|
||||
* Feeds raw data and extracts decoded samples as needed.
|
||||
*/
|
||||
static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
int samples_done = 0;
|
||||
mpg123_handle *m = data->m;
|
||||
|
||||
@ -315,7 +270,8 @@ static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data
|
||||
(samples_to_do-samples_done)*sizeof(sample)*channels,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rc = mpg123_decode(m,
|
||||
NULL,0,
|
||||
(unsigned char *)(outbuf+samples_done*channels),
|
||||
@ -335,49 +291,59 @@ static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data
|
||||
|
||||
|
||||
/**
|
||||
* Decode interleaved (multichannel) MPEG. Works with mono/stereo too.
|
||||
* Channels (1 or 2), samples and frame size per stream should be constant.
|
||||
* Decode custom MPEG, allowing support for: single frames, interleave, mutant frames, multiple streams
|
||||
* (1 frame = 1/2ch so Nch = 2ch*N/2 or 1ch*N or 2ch+1ch+2ch+...), etc.
|
||||
*
|
||||
* Reads frame 'streams' (ex. 4ch = 1+1+1+1 = 4 streams or 2+2 = 2 streams), decodes
|
||||
* samples per stream and muxes them into a single internal buffer before copying to outbuf
|
||||
* Decodes samples per each stream and muxes them into a single internal buffer before copying to outbuf
|
||||
* (to make sure channel samples are orderly copied between decode_mpeg calls).
|
||||
*
|
||||
* Interleave modes:
|
||||
* - FIXED [XVAG]: fixed block_size per stream (unknown number of samples)
|
||||
* (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc)
|
||||
* - FSB: single frames per stream with padding (block_size is frame_size+padding) [FSB]
|
||||
* (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc)
|
||||
* - P3D: unknown layout at the moment (possibly N
|
||||
* decode_mpeg_custom_stream does the main decoding, while this handles layout and copying samples to output.
|
||||
*/
|
||||
static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
int samples_done = 0, bytes_max, bytes_to_copy;
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
if (data->bytes_used_in_interleave_buffer < data->bytes_in_interleave_buffer) {
|
||||
if (data->bytes_used_in_sample_buffer < data->bytes_in_sample_buffer) {
|
||||
/* copy remaining samples */
|
||||
bytes_to_copy = data->bytes_in_interleave_buffer - data->bytes_used_in_interleave_buffer;
|
||||
bytes_to_copy = data->bytes_in_sample_buffer - data->bytes_used_in_sample_buffer;
|
||||
bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels;
|
||||
if (bytes_to_copy > bytes_max)
|
||||
bytes_to_copy = bytes_max;
|
||||
memcpy((uint8_t*)(outbuf+samples_done*channels), data->interleave_buffer + data->bytes_used_in_interleave_buffer, bytes_to_copy);
|
||||
memcpy((uint8_t*)(outbuf+samples_done*channels), data->sample_buffer + data->bytes_used_in_sample_buffer, bytes_to_copy);
|
||||
|
||||
/* update samples copied */
|
||||
data->bytes_used_in_interleave_buffer += bytes_to_copy;
|
||||
data->bytes_used_in_sample_buffer += bytes_to_copy;
|
||||
samples_done += bytes_to_copy / sizeof(sample) / channels;
|
||||
}
|
||||
else {
|
||||
/* fill the internal sample buffer */
|
||||
int i;
|
||||
data->bytes_in_interleave_buffer = 0;
|
||||
data->bytes_used_in_interleave_buffer = 0;
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
|
||||
* With multiple offsets it's expected offsets are set up pointing to the first frame of each stream. */
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
if (data->interleave_type == MPEG_P3D /* P3D have a strange way to interleave so just use first offset */
|
||||
|| data->interleave_type == MPEG_EA) /* EA MPEG is simply frame by frame normal MPEG */
|
||||
decode_mpeg_interleave_samples(&vgmstream->ch[0], data, data->ms[i], channels, i, vgmstream->interleave_block_size);
|
||||
else
|
||||
decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size);
|
||||
switch(data->type) {
|
||||
//case MPEG_LYN:
|
||||
case MPEG_FSB:
|
||||
case MPEG_XVAG:
|
||||
case MPEG_P3D:
|
||||
/* multiple offsets, decodes 1 frame per stream until reaching interleave/block_size and skips it */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
||||
break;
|
||||
|
||||
//case MPEG_EA: //?
|
||||
case MPEG_AWC:
|
||||
/* consecutive streams: multiple offsets, decodes 1 frame per stream */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* N frames: single offset, decodes all N frames per stream (sample buffer must be big enough for N) */
|
||||
decode_mpeg_custom_stream(&vgmstream->ch[0], data, data->ms[i], channels, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* discard (for looping): 'remove' decoded samples from the buffer */
|
||||
@ -385,115 +351,110 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data
|
||||
size_t bytes_to_discard = data->samples_to_discard * sizeof(sample) * channels;
|
||||
|
||||
/* 'remove' all buffer at most */
|
||||
if (bytes_to_discard > data->bytes_in_interleave_buffer)
|
||||
bytes_to_discard = data->bytes_in_interleave_buffer;
|
||||
if (bytes_to_discard > data->bytes_in_sample_buffer)
|
||||
bytes_to_discard = data->bytes_in_sample_buffer;
|
||||
|
||||
/* pretend the samples were used up and readjust discard */
|
||||
data->bytes_used_in_interleave_buffer = bytes_to_discard;
|
||||
data->bytes_used_in_sample_buffer = bytes_to_discard;
|
||||
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes frames from a stream and muxes the samples into a intermediate buffer.
|
||||
* Skips to the next interleaved block once reaching the stream's block end.
|
||||
*/
|
||||
static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size) {
|
||||
size_t bytes_done;
|
||||
/* Decodes frames from a stream and muxes samples into a intermediate buffer and moves the stream offsets. */
|
||||
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream) {
|
||||
size_t bytes_done = 0;
|
||||
size_t stream_size = get_streamfile_size(stream->streamfile);
|
||||
int rc, ok;
|
||||
|
||||
/* decode samples from 1 full frame */
|
||||
|
||||
/* decode samples from one full-frame (as N data-frames = 1 full-frame) before exiting (to orderly copy to sample buffer) */
|
||||
do {
|
||||
int rc;
|
||||
//int first_frame = stream->offset == stream->channel_start_offset;
|
||||
|
||||
/* padded frame stuff */
|
||||
rc = update_frame_sizes(data, stream->streamfile, stream->offset);
|
||||
/* ignore any errors and continue; mpg123 will probably sync */
|
||||
VGM_ASSERT(rc==0, "MPEG: frame error @ 0x%08lx (prev size=0x%x / padding=0x%x)\n", stream->offset, data->current_frame_size, data->current_padding);
|
||||
//VGM_LOG("off=%lx, st=%i, fs=%x, pd=%x\n", stream->offset, num_stream, data->current_frame_size, data->current_padding);
|
||||
|
||||
//VGM_LOG("MPEG: new step of stream %i @ 0x%08lx\n", num_stream, stream->offset);
|
||||
|
||||
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
|
||||
if (stream->offset > stream_size) {
|
||||
memset(data->frame_buffer, 0, data->frame_buffer_size);
|
||||
bytes_done = data->frame_buffer_size;
|
||||
break;
|
||||
if (stream->offset >= stream_size) {
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break; /* continue with other streams */
|
||||
}
|
||||
|
||||
/* read more raw data (only 1 frame, to check interleave block end) */
|
||||
/* read more raw data */
|
||||
if (!data->buffer_full) {
|
||||
//VGM_LOG("MPEG: reading more raw data\n");
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile);
|
||||
|
||||
/* end of stream, fill frame buffer with 0s but continue normally with other streams */
|
||||
if (!data->bytes_in_buffer) {
|
||||
memset(data->frame_buffer, 0, data->frame_buffer_size);
|
||||
bytes_done = data->frame_buffer_size;
|
||||
break;
|
||||
switch(data->type) {
|
||||
case MPEG_EAL31:
|
||||
case MPEG_EAL32P:
|
||||
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_parse_frame_ealayer3(stream, data); break;
|
||||
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
|
||||
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
|
||||
}
|
||||
/* error/EOF, mpg123 can resync in some cases but custom MPEGs wouldn't need that */
|
||||
if (!ok || !data->bytes_in_buffer) {
|
||||
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break; /* continue with other streams */
|
||||
}
|
||||
//VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset off=%lx\n", data->bytes_in_buffer, stream->offset);
|
||||
|
||||
data->buffer_full = 1;
|
||||
data->buffer_used = 0;
|
||||
|
||||
stream->offset += data->current_frame_size + data->current_padding; /* skip frame + FSB garbage */
|
||||
if (block_size && ((stream->offset - stream->channel_start_offset) % block_size==0)) {
|
||||
stream->offset += block_size * (data->ms_size-1); /* skip a block per stream if block done */
|
||||
}
|
||||
|
||||
#if 0
|
||||
//TODO: skip very first frame but add fake silence? only if first frame is fully decodable?
|
||||
if (data->interleave_type == MPEG_FSB && first_frame) {
|
||||
data->buffer_full = 0;
|
||||
//TODO: in case of EALayer3 with "PCM flag" must put them in buffer
|
||||
if (ea_pcm_flag) {
|
||||
/* write some samples to data->stream_buffer *before* decoding this frame */
|
||||
bytes_done += read_streamfile(data->stream_buffer,offset,num_pcm_samples,stream->streamfile);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
//TODO: FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
|
||||
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
|
||||
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
||||
|
||||
VGM_LOG("skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
||||
memset(data->frame_buffer, 0, data->frame_buffer_size);
|
||||
bytes_done = data->frame_buffer_size;
|
||||
data->buffer_full = 0;
|
||||
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
||||
bytes_done = data->stream_buffer_size;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
||||
if (!data->buffer_used) {
|
||||
//VGM_LOG("MPEG: get samples from new raw data\n");
|
||||
//VGM_LOG("MPEG: feed new data and get samples \n");
|
||||
rc = mpg123_decode(m,
|
||||
data->buffer, data->bytes_in_buffer,
|
||||
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
|
||||
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
} else {
|
||||
//VGM_LOG("MPEG: get samples from old raw data\n");
|
||||
}
|
||||
else {
|
||||
//VGM_LOG("MPEG: get samples from old data\n");
|
||||
rc = mpg123_decode(m,
|
||||
NULL,0,
|
||||
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
|
||||
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
||||
&bytes_done);
|
||||
}
|
||||
|
||||
/* samples per frame should be constant... */
|
||||
if (bytes_done > 0 && bytes_done < data->frame_buffer_size) {
|
||||
VGM_LOG("MPEG: borked frame @ 0x%08lx (%i bytes done, expected %i, rc=%i)\n", stream->offset, bytes_done, data->frame_buffer_size, rc);
|
||||
memset(data->frame_buffer + bytes_done, 0, data->frame_buffer_size - bytes_done);
|
||||
bytes_done = data->frame_buffer_size;
|
||||
}
|
||||
|
||||
/* not enough raw data, request more */
|
||||
if (rc == MPG123_NEED_MORE) {
|
||||
//VGM_LOG("MPEG: need more raw data\n");
|
||||
//VGM_LOG("MPEG: need more raw data to get samples\n");
|
||||
/* (apparently mpg123 can give bytes and request more at the same time and may mess up some calcs, when/how? */
|
||||
VGM_ASSERT(bytes_done > 0, "MPEG: bytes done but decoder requests more data\n");
|
||||
data->buffer_full = 0;
|
||||
continue;
|
||||
}
|
||||
//VGM_LOG("MPEG: got samples, bytes_done=0x%x (fsbs=0x%x)\n", bytes_done, data->stream_buffer_size);
|
||||
|
||||
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
|
||||
/* copy decoded frame to intermediate sample buffer, muxing channels
|
||||
/* copy decoded full-frame to intermediate sample buffer, muxing channels
|
||||
* (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */
|
||||
{
|
||||
size_t samples_done;
|
||||
@ -506,159 +467,14 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
|
||||
for (i = 0; i < samples_done; i++) { /* decoded samples */
|
||||
off_t in_offset = sz*i*channels_f + sz*fch;
|
||||
off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch);
|
||||
memcpy(data->interleave_buffer + out_offset, data->frame_buffer + in_offset, sz);
|
||||
memcpy(data->sample_buffer + out_offset, data->stream_buffer + in_offset, sz);
|
||||
}
|
||||
}
|
||||
|
||||
data->bytes_in_interleave_buffer += bytes_done;
|
||||
data->bytes_in_sample_buffer += bytes_done;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get frame size info for the current frame, needed by FSBs of varying padding.
|
||||
* Padding sometimes contains next frame header so we can't feed it to mpg123 or it gets confused.
|
||||
* Expected to be called at the beginning of a new frame.
|
||||
*/
|
||||
static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) {
|
||||
|
||||
if (data->interleave_type == MPEG_FIXED) { /* frames of fixed size */
|
||||
data->current_frame_size = data->interleave_value;
|
||||
}
|
||||
else if (data->interleave_type == MPEG_FSB) { /* padding between frames */
|
||||
mpeg_frame_info info;
|
||||
|
||||
/* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */
|
||||
if ( !mpeg_get_frame_info(streamfile, offset, &info) )
|
||||
goto fail;
|
||||
data->current_frame_size = info.frame_size;
|
||||
|
||||
/* get FSB padding for MPEG1/2 Layer III (MPEG1 Layer II doesn't use it, and Layer I doesn't seem to be supported) */
|
||||
if (data->interleave_value && info.layer == 3) {
|
||||
data->current_padding = (data->current_frame_size % data->interleave_value)
|
||||
? data->interleave_value - (data->current_frame_size % data->interleave_value)
|
||||
: 0;
|
||||
}
|
||||
|
||||
/* could mess some calcs */
|
||||
VGM_ASSERT(data->sample_rate_per_frame != info.sample_rate || data->samples_per_frame != info.frame_samples,
|
||||
"MPEG: variable frame info found @ 0x%08lx", offset);
|
||||
}
|
||||
else if (data->interleave_type == MPEG_P3D) { /* varying frames size, even though the frame header says 0x120 */
|
||||
uint32_t header = read_32bitBE(offset,streamfile);
|
||||
|
||||
if (read_32bitBE(offset+0x120,streamfile) == header) {
|
||||
data->current_frame_size = 0x120;
|
||||
} else if (read_32bitBE(offset+0xA0,streamfile) == header) {
|
||||
data->current_frame_size = 0xA0;
|
||||
} else if (read_32bitBE(offset+0x1C0,streamfile) == header) {
|
||||
data->current_frame_size = 0x1C0;
|
||||
} else if (read_32bitBE(offset+0x180,streamfile) == header) {
|
||||
data->current_frame_size = 0x180;
|
||||
//} else if (read_32bitBE(offset+0x120,streamfile) == -1) { /* at EOF */
|
||||
// data->current_frame_size = 0x120;
|
||||
} else {
|
||||
VGM_LOG("MPEG: unknown frame size @ %lx, %x\n", offset, read_32bitBE(offset+0x120,streamfile));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if (data->interleave_type == MPEG_EA) { /* straight frame by frame */
|
||||
mpeg_frame_info info;
|
||||
|
||||
/* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */
|
||||
if ( !mpeg_get_frame_info(streamfile, offset, &info) )
|
||||
goto fail;
|
||||
data->current_frame_size = info.frame_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode AHX mono frames.
|
||||
* mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they actually vary and are much shorter
|
||||
*/
|
||||
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do) {
|
||||
int samples_done = 0;
|
||||
const int frame_size = AHX_EXPECTED_FRAME_SIZE;
|
||||
mpg123_handle *m = data->m;
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
size_t bytes_done;
|
||||
int rc;
|
||||
|
||||
/* read more raw data */
|
||||
if (!data->buffer_full) {
|
||||
/* fill buffer up to next frame ending (or file ending) */
|
||||
int bytes_into_header = 0;
|
||||
const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0};
|
||||
off_t frame_offset = 0;
|
||||
|
||||
/* assume that we are starting at a header, skip it and look for the next one */
|
||||
read_streamfile(data->buffer, stream->offset+frame_offset, 4, stream->streamfile);
|
||||
frame_offset += 4;
|
||||
|
||||
do {
|
||||
uint8_t byte;
|
||||
byte = read_8bit(stream->offset+frame_offset,stream->streamfile);
|
||||
data->buffer[frame_offset] = byte;
|
||||
frame_offset++;
|
||||
|
||||
if (byte == header[bytes_into_header]) {
|
||||
bytes_into_header++;
|
||||
} else {
|
||||
/* This might have been the first byte of the header, so
|
||||
* we need to check again.
|
||||
* No need to get more complicated than this, though, since
|
||||
* there are no repeated characters in the search string. */
|
||||
if (bytes_into_header>0) {
|
||||
frame_offset--;
|
||||
}
|
||||
bytes_into_header=0;
|
||||
}
|
||||
|
||||
if (bytes_into_header==4) {
|
||||
break;
|
||||
}
|
||||
} while (frame_offset < frame_size);
|
||||
|
||||
if (bytes_into_header==4)
|
||||
frame_offset-=4;
|
||||
memset(data->buffer+frame_offset,0,frame_size-frame_offset);
|
||||
|
||||
data->buffer_full = 1;
|
||||
data->buffer_used = 0;
|
||||
|
||||
stream->offset += frame_offset;
|
||||
}
|
||||
|
||||
/* feed new raw data to the decoder if needed, copy decodec results to output */
|
||||
if (!data->buffer_used) {
|
||||
rc = mpg123_decode(m,
|
||||
data->buffer,frame_size,
|
||||
(unsigned char *)(outbuf+samples_done),
|
||||
(samples_to_do-samples_done)*sizeof(sample),
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
} else {
|
||||
rc = mpg123_decode(m,
|
||||
NULL,0,
|
||||
(unsigned char *)(outbuf+samples_done),
|
||||
(samples_to_do-samples_done)*sizeof(sample),
|
||||
&bytes_done);
|
||||
}
|
||||
|
||||
/* not enough raw data, request more */
|
||||
if (rc == MPG123_NEED_MORE) {
|
||||
data->buffer_full = 0;
|
||||
}
|
||||
|
||||
/* update copied samples */
|
||||
samples_done += bytes_done/sizeof(sample);/* mono */
|
||||
}
|
||||
}
|
||||
|
||||
/*********/
|
||||
/* UTILS */
|
||||
@ -668,16 +484,18 @@ void free_mpeg(mpeg_codec_data *data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (!data->custom) {
|
||||
mpg123_delete(data->m);
|
||||
if (data->interleaved) {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_delete(data->ms[i]);
|
||||
}
|
||||
free(data->ms);
|
||||
|
||||
free(data->interleave_buffer);
|
||||
free(data->frame_buffer);
|
||||
free(data->stream_buffer);
|
||||
free(data->sample_buffer);
|
||||
}
|
||||
|
||||
free(data->buffer);
|
||||
@ -694,32 +512,37 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
|
||||
off_t input_offset;
|
||||
mpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* reset multistream */ //todo check if stream offsets are properly reset
|
||||
|
||||
if (!data->custom) {
|
||||
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
|
||||
mpg123_feedseek(data->m,0,SEEK_SET,&input_offset);
|
||||
|
||||
/* reset multistream */ //todo check if stream offsets are properly reset
|
||||
if (data->interleaved) {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
||||
}
|
||||
|
||||
data->bytes_in_interleave_buffer = 0;
|
||||
data->bytes_used_in_interleave_buffer = 0;
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
/* initial delay */
|
||||
data->samples_to_discard = data->skip_samples;
|
||||
}
|
||||
}
|
||||
|
||||
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
/* won't work for fake AHX MPEG */
|
||||
off_t input_offset;
|
||||
mpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* seek multistream */
|
||||
if (!data->interleaved) {
|
||||
if (!data->custom) {
|
||||
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
/* re-start from 0 */
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
@ -727,11 +550,12 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
|
||||
}
|
||||
/* manually add skip samples, since we don't really know the correct offset */
|
||||
data->samples_to_discard = num_sample;
|
||||
data->bytes_in_sample_buffer = 0;
|
||||
data->bytes_used_in_sample_buffer = 0;
|
||||
|
||||
data->bytes_in_interleave_buffer = 0;
|
||||
data->bytes_used_in_interleave_buffer = 0;
|
||||
/* manually discard samples, since we don't really know the correct offset */
|
||||
data->samples_to_discard = num_sample;
|
||||
data->samples_to_discard += data->skip_samples;
|
||||
}
|
||||
|
||||
data->buffer_full = 0;
|
||||
@ -740,26 +564,31 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
|
||||
|
||||
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 (MPG123_OK != mpg123_info(m, &mi))
|
||||
if (m == NULL || MPG123_OK != mpg123_info(m, &mi))
|
||||
return 0;
|
||||
|
||||
/* In this case just return 0 and expect to fail (if used for num_samples)
|
||||
* We would need to read the number of frames in some frame header or count them to get samples */
|
||||
if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used
|
||||
/* 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
|
||||
return 0;
|
||||
|
||||
return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000);
|
||||
}
|
||||
else {
|
||||
return 0; /* a bit too complex for what is worth */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disables/enables stderr output, useful for MPEG known to contain recoverable errors
|
||||
*/
|
||||
/* disables/enables stderr output, useful for MPEG known to contain recoverable errors */
|
||||
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
||||
if (!data->custom) {
|
||||
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||
if (data->interleaved) {
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i=0; i < data->ms_size; i++) {
|
||||
mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
||||
@ -767,93 +596,4 @@ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
||||
}
|
||||
}
|
||||
|
||||
/*****************/
|
||||
/* FRAME HELPERS */
|
||||
/*****************/
|
||||
|
||||
/**
|
||||
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow
|
||||
* it's wrong at times (maybe because we use an ancient version) so here we do our thing.
|
||||
*/
|
||||
static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info) {
|
||||
/* index tables */
|
||||
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
|
||||
static const int layers[4] = { -1,3,2,1 };
|
||||
static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */
|
||||
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */
|
||||
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */
|
||||
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */
|
||||
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */
|
||||
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */
|
||||
};
|
||||
static const int sample_rates[4][4] = { /* [version][sample rate index] */
|
||||
{ 44100, 48000, 32000, -1}, /* MPEG1 */
|
||||
{ 22050, 24000, 16000, -1}, /* MPEG2 */
|
||||
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
|
||||
};
|
||||
static const int channels[4] = { 2,2,2, 1 }; /* [channel] */
|
||||
static const int frame_samples[3][3] = { /* [version][layer] */
|
||||
{ 384, 1152, 1152 }, /* MPEG1 */
|
||||
{ 384, 1152, 576 }, /* MPEG2 */
|
||||
{ 384, 1152, 576 } /* MPEG2.5 */
|
||||
};
|
||||
static const coding_t coding_types[3][3] = { /* [version][layer] */
|
||||
{ coding_MPEG1_L1, coding_MPEG1_L2, coding_MPEG1_L3 },
|
||||
{ coding_MPEG2_L1, coding_MPEG2_L2, coding_MPEG2_L3 },
|
||||
{ coding_MPEG25_L1, coding_MPEG25_L2, coding_MPEG25_L3 },
|
||||
};
|
||||
|
||||
uint32_t header;
|
||||
int idx, padding;
|
||||
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
header = read_32bitBE(offset, streamfile);
|
||||
|
||||
if ((header >> 21) != 0x7FF) /* 31-21: sync */
|
||||
goto fail;
|
||||
|
||||
info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */
|
||||
if (info->version <= 0) goto fail;
|
||||
|
||||
info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */
|
||||
if (info->layer <= 0) goto fail;
|
||||
|
||||
//crc = (header >> 16) & 0x1; /* 16: protected by crc? */
|
||||
|
||||
idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1)));
|
||||
info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */
|
||||
if (info->bit_rate <= 0) goto fail;
|
||||
|
||||
info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */
|
||||
if (info->sample_rate <= 0) goto fail;
|
||||
|
||||
padding = (header >> 9) & 0x1; /* 9: padding? */
|
||||
//private = (header >> 8) & 0x1; /* 8: private bit */
|
||||
|
||||
info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */
|
||||
|
||||
//js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */
|
||||
//copyright = (header >> 3) & 0x1; /* 3: copyrighted */
|
||||
//original = (header >> 2) & 0x1; /* 2: original */
|
||||
//emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */
|
||||
|
||||
info->frame_samples = frame_samples[info->version-1][info->layer-1];
|
||||
|
||||
/* calculate frame length (from hcs's fsb_mpeg) */
|
||||
switch (info->frame_samples) {
|
||||
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break;
|
||||
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break;
|
||||
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break;
|
||||
}
|
||||
|
||||
info->coding_type = coding_types[info->version-1][info->layer-1];
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
29
src/coding/mpeg_decoder.h
Normal file
29
src/coding/mpeg_decoder.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef _MPEG_DECODER_H_
|
||||
#define _MPEG_DECODER_H_
|
||||
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* used by mpeg_decoder.c, but scattered in other .c files */
|
||||
#ifdef VGM_USE_MPEG
|
||||
typedef struct {
|
||||
int version;
|
||||
int layer;
|
||||
int bit_rate;
|
||||
int sample_rate;
|
||||
int frame_samples;
|
||||
int frame_size; /* bytes */
|
||||
int channels;
|
||||
} mpeg_frame_info;
|
||||
|
||||
int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info);
|
||||
|
||||
int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
|
||||
//int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
|
||||
|
||||
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
//int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
|
||||
|
||||
#endif/* VGM_USE_MPEG */
|
||||
|
||||
#endif/*_MPEG_DECODER_H_ */
|
@ -2,7 +2,7 @@
|
||||
#define _FSB_VORBIS_DATA_H_
|
||||
|
||||
/**
|
||||
* fvs (FSB vorbis setup) data for fsb_vorbis_decoder.c
|
||||
* fvs (FSB vorbis setup) data for FSB Vorbis.
|
||||
* They are Vorbis setup packets (type 0x05) binaries converted to c-arrays.
|
||||
* Extracted from FMOD by python-fsb5 (https://github.com/HearthSim/python-fsb5).
|
||||
*/
|
@ -2,7 +2,7 @@
|
||||
#define _WWISE_VORBIS_DATA_H_
|
||||
|
||||
/**
|
||||
* Codebook data for wwise_vorbis_decoder.c
|
||||
* Codebook data for Wwise Vorbis.
|
||||
* They are Wwise Vorbis codebook binaries converted to c-arrays.
|
||||
* Extracted from Wwise SDK by ww2ogg. The originals have all codebooks together with an index table at the end.
|
||||
* Here each codebook is an array instead, and the index is converted to a list.
|
@ -1,34 +1,40 @@
|
||||
#include "coding.h"
|
||||
#include <math.h>
|
||||
#include "coding.h"
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#define ogl_vorbis_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */
|
||||
#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm);
|
||||
static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm);
|
||||
|
||||
/**
|
||||
* Inits a raw OGL vorbis stream.
|
||||
* Inits a vorbis stream of some custom variety.
|
||||
*
|
||||
* Vorbis packets are stored in .ogg, which is divided into ogg pages/packets, and the first packets contain necessary
|
||||
* vorbis setup. For raw vorbis the setup is stored it elsewhere (i.e.- in the .exe), presumably to shave off some kb
|
||||
* per stream and codec startup time. We'll read the external setup and raw data and decode it with libvorbis.
|
||||
* Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary
|
||||
* Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere
|
||||
* (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate.
|
||||
* We'll manually read/modify the data and decode it with libvorbis calls.
|
||||
*
|
||||
* OGL simply removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but
|
||||
* the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning).
|
||||
* Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html
|
||||
*/
|
||||
vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, off_t * data_start_offset) {
|
||||
vorbis_codec_data * data = NULL;
|
||||
vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) {
|
||||
vorbis_custom_codec_data * data = NULL;
|
||||
int ok;
|
||||
|
||||
/* init stuff */
|
||||
data = calloc(1,sizeof(vorbis_codec_data));
|
||||
data = calloc(1,sizeof(vorbis_custom_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = ogl_vorbis_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
/* keep around to decode too */
|
||||
data->type = type;
|
||||
memcpy(&data->config, config, sizeof(vorbis_custom_config));
|
||||
|
||||
|
||||
/* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks)
|
||||
* libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */
|
||||
@ -38,35 +44,15 @@ vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t sta
|
||||
data->op.packet = data->buffer;
|
||||
data->op.b_o_s = 1; /* fake headers start */
|
||||
|
||||
{
|
||||
/* read 3 packets with triad (id/comment/setup), each with an OGL header */
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size;
|
||||
|
||||
/* normal identificacion packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* normal comment packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* normal setup packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* data starts after triad */
|
||||
*data_start_offset = offset;
|
||||
/* init header */
|
||||
switch(data->type) {
|
||||
case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(streamFile, start_offset, data); break;
|
||||
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;
|
||||
default: goto fail;
|
||||
}
|
||||
if(!ok) goto fail;
|
||||
|
||||
data->op.b_o_s = 0; /* end of fake headers */
|
||||
|
||||
@ -75,19 +61,21 @@ vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t sta
|
||||
if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail;
|
||||
|
||||
|
||||
/* write output */
|
||||
config->data_start_offset = data->config.data_start_offset;
|
||||
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ogl_vorbis(data);
|
||||
free_vorbis_custom(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes raw OGL vorbis
|
||||
*/
|
||||
void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */
|
||||
void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
|
||||
vorbis_codec_data * data = vgmstream->codec_data;
|
||||
vorbis_custom_codec_data * data = vgmstream->codec_data;
|
||||
size_t stream_size = get_streamfile_size(stream->streamfile);
|
||||
//data->op.packet = data->buffer;/* implicit from init */
|
||||
int samples_done = 0;
|
||||
@ -95,8 +83,8 @@ void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_t
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
/* extra EOF check for edge cases */
|
||||
if (stream->offset > stream_size) {
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
if (stream->offset >= stream_size) {
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -131,37 +119,40 @@ void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_t
|
||||
vorbis_synthesis_read(&data->vd, samples_to_get);
|
||||
}
|
||||
else { /* read more data */
|
||||
int rc;
|
||||
int ok, rc;
|
||||
|
||||
/* this is not actually needed, but feels nicer */
|
||||
data->op.granulepos += samples_to_do;
|
||||
/* not actually needed, but feels nicer */
|
||||
data->op.granulepos += samples_to_do; /* can be changed next if desired */
|
||||
data->op.packetno++;
|
||||
|
||||
/* get next packet size from the OGL 16b header */
|
||||
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2;
|
||||
stream->offset += 2;
|
||||
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) {
|
||||
goto decode_fail; /* eof or end padding */
|
||||
/* read/transform data into the ogg_packet buffer and advance offsets */
|
||||
switch(data->type) {
|
||||
case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break;
|
||||
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;
|
||||
default: goto decode_fail;
|
||||
}
|
||||
if(!ok) {
|
||||
VGM_LOG("Vorbis: cannot parse packet @ around %lx\n",stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
/* read raw block */
|
||||
if (read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile) != data->op.bytes) {
|
||||
goto decode_fail; /* wrong packet? */
|
||||
}
|
||||
stream->offset += data->op.bytes;
|
||||
|
||||
/* parse the fake ogg packet into a logical vorbis block */
|
||||
rc = vorbis_synthesis(&data->vb,&data->op);
|
||||
if (rc == OV_ENOTAUDIO) {
|
||||
VGM_LOG("vorbis_synthesis: not audio packet @ %lx\n",stream->offset); getchar();
|
||||
VGM_LOG("Vorbis: not an audio packet @ %lx\n",stream->offset);
|
||||
continue; /* not tested */
|
||||
} else if (rc != 0) {
|
||||
VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
/* finally decode the logical block into samples */
|
||||
rc = vorbis_synthesis_blockin(&data->vd,&data->vb);
|
||||
if (rc != 0) {
|
||||
VGM_LOG("Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset);
|
||||
goto decode_fail; /* ? */
|
||||
}
|
||||
|
||||
@ -176,9 +167,8 @@ decode_fail:
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
}
|
||||
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) {
|
||||
/* mostly from Xiph's decoder_example.c */
|
||||
/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */
|
||||
static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) {
|
||||
int i,j;
|
||||
|
||||
/* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc)
|
||||
@ -199,7 +189,7 @@ static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, i
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
void free_ogl_vorbis(vorbis_codec_data * data) {
|
||||
void free_vorbis_custom(vorbis_custom_codec_data * data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
@ -212,19 +202,19 @@ void free_ogl_vorbis(vorbis_codec_data * data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void reset_ogl_vorbis(VGMSTREAM *vgmstream) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
void reset_vorbis_custom(VGMSTREAM *vgmstream) {
|
||||
vorbis_custom_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
/* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = 0;
|
||||
}
|
||||
|
||||
void seek_ogl_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
vorbis_custom_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
/* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = num_sample;
|
19
src/coding/vorbis_custom_decoder.h
Normal file
19
src/coding/vorbis_custom_decoder.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef _VORBIS_CUSTOM_DECODER_H_
|
||||
#define _VORBIS_CUSTOM_DECODER_H_
|
||||
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* used by vorbis_custom_decoder.c, but scattered in other .c files */
|
||||
#ifdef VGM_USE_VORBIS
|
||||
int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
|
||||
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_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);
|
||||
#endif/* VGM_USE_VORBIS */
|
||||
|
||||
#endif/*_VORBIS_CUSTOM_DECODER_H_ */
|
275
src/coding/vorbis_custom_utils_fsb.c
Normal file
275
src/coding/vorbis_custom_utils_fsb.c
Normal file
@ -0,0 +1,275 @@
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */
|
||||
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
||||
#include "vorbis_custom_data_fsb.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
|
||||
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* FSB references an external setup packet by the setup_id, and packets have mini headers with the size.
|
||||
*
|
||||
* Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and
|
||||
* fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor).
|
||||
*/
|
||||
int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
vorbis_custom_config cfg = data->config;
|
||||
|
||||
data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
|
||||
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 */
|
||||
|
||||
data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, streamFile);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
/* get next packet size from the FSB 16b header (doesn't count this 16b) */
|
||||
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile);
|
||||
stream->offset += 2;
|
||||
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) {
|
||||
VGM_LOG("FSB Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2);
|
||||
goto fail; /* EOF or end padding */
|
||||
}
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += bytes;
|
||||
if (bytes != data->op.bytes) {
|
||||
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
|
||||
goto fail; /* wrong packet? */
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) {
|
||||
int bytes = 0x1e;
|
||||
uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
/* guetto log2 for allowed blocksizes (2-exp), could be improved */
|
||||
switch(blocksize_long) {
|
||||
case 64: exp_blocksize_0 = 6; break;
|
||||
case 128: exp_blocksize_0 = 7; break;
|
||||
case 256: exp_blocksize_0 = 8; break;
|
||||
case 512: exp_blocksize_0 = 9; break;
|
||||
case 1024: exp_blocksize_0 = 10; break;
|
||||
case 2048: exp_blocksize_0 = 11; break;
|
||||
case 4096: exp_blocksize_0 = 12; break;
|
||||
case 8192: exp_blocksize_0 = 13; break;
|
||||
default: return 0;
|
||||
}
|
||||
switch(blocksize_short) {
|
||||
case 64: exp_blocksize_1 = 6; break;
|
||||
case 128: exp_blocksize_1 = 7; break;
|
||||
case 256: exp_blocksize_1 = 8; break;
|
||||
case 512: exp_blocksize_1 = 9; break;
|
||||
case 1024: exp_blocksize_1 = 10; break;
|
||||
case 2048: exp_blocksize_1 = 11; break;
|
||||
case 4096: exp_blocksize_1 = 12; break;
|
||||
case 8192: exp_blocksize_1 = 13; break;
|
||||
default: return 0;
|
||||
}
|
||||
blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1);
|
||||
|
||||
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_8bit (buf+0x0b, channels); /* audio_channels */
|
||||
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
int bytes;
|
||||
|
||||
/* try to load from external files first */
|
||||
bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
/* try to locate from the precompiled list */
|
||||
bytes = load_fvs_array(buf, bufsize, setup_id, streamFile);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
/* not found */
|
||||
VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
STREAMFILE * streamFileSetup = NULL;
|
||||
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
|
||||
/* read "(dir/).fvs_{setup_id}" */
|
||||
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id);
|
||||
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (streamFileSetup) {
|
||||
/* file found, get contents into the buffer */
|
||||
size_t bytes = streamFileSetup->get_size(streamFileSetup);
|
||||
if (bytes > bufsize) goto fail;
|
||||
|
||||
if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes)
|
||||
goto fail;
|
||||
|
||||
streamFileSetup->close(streamFileSetup);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
STREAMFILE * streamFileSetup = NULL;
|
||||
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
|
||||
/* read "(dir/).fvs" */
|
||||
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname);
|
||||
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (streamFileSetup) {
|
||||
/* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */
|
||||
int entries, i;
|
||||
uint32_t offset = 0, size = 0;
|
||||
|
||||
if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */
|
||||
entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */
|
||||
if (entries <= 0) goto fail;
|
||||
|
||||
for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */
|
||||
if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) {
|
||||
offset = read_32bitLE(0x24 + i*0x10, streamFileSetup);
|
||||
size = read_32bitLE(0x28 + i*0x10, streamFileSetup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!size || !offset || size > bufsize) goto fail;
|
||||
|
||||
/* read into buf */
|
||||
if (read_streamfile(buf, offset, size, streamFileSetup) != size)
|
||||
goto fail;
|
||||
|
||||
streamFileSetup->close(streamFileSetup);
|
||||
return size;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
||||
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
||||
int i, list_length;
|
||||
|
||||
list_length = sizeof(fvs_list) / sizeof(fvs_info);
|
||||
for (i=0; i < list_length; i++) {
|
||||
if (fvs_list[i].id == setup_id) {
|
||||
if (fvs_list[i].size > bufsize) goto fail;
|
||||
/* found: copy data as-is */
|
||||
memcpy(buf,fvs_list[i].setup, fvs_list[i].size);
|
||||
return fvs_list[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
77
src/coding/vorbis_custom_utils_ogl.c
Normal file
77
src/coding/vorbis_custom_utils_ogl.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but
|
||||
* the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning).
|
||||
*/
|
||||
int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size;
|
||||
|
||||
/* read 3 packets with triad (id/comment/setup), each with an OGL header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* normal comment packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* normal setup packet */
|
||||
packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2;
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += 2+packet_size;
|
||||
|
||||
/* data starts after triad */
|
||||
data->config.data_start_offset = offset;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
/* get next packet size from the OGL 16b header (upper 14b) */
|
||||
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2;
|
||||
stream->offset += 2;
|
||||
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) {
|
||||
VGM_LOG("OGL Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2);
|
||||
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("OGL Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
|
||||
goto fail; /* wrong packet? */
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
194
src/coding/vorbis_custom_utils_sk.c
Normal file
194
src/coding/vorbis_custom_utils_sk.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet);
|
||||
static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK"
|
||||
* in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here.
|
||||
*
|
||||
* A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept).
|
||||
*/
|
||||
int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
off_t id_offset = 0, comment_offset = 0, setup_offset = 0;
|
||||
size_t id_size = 0, comment_size = 0, setup_size = 0;
|
||||
int page_packets;
|
||||
|
||||
/* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */
|
||||
|
||||
/* first page has the id packet */
|
||||
if (!get_page_info(streamFile, offset, &id_offset, &id_size, &page_packets, 0)) goto fail;
|
||||
if (page_packets != 1) goto fail;
|
||||
offset = id_offset + id_size;
|
||||
|
||||
/* second page has the comment and setup packets */
|
||||
if (!get_page_info(streamFile, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail;
|
||||
if (page_packets != 2) goto fail;
|
||||
if (!get_page_info(streamFile, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail;
|
||||
if (page_packets != 2) goto fail;
|
||||
offset = comment_offset + comment_size + setup_size;
|
||||
|
||||
|
||||
/* init with all offsets found */
|
||||
data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, id_offset, id_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
|
||||
data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, comment_offset, comment_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, setup_offset, setup_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
|
||||
/* data starts after triad */
|
||||
data->config.data_start_offset = offset;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
VGM_LOG("SK Vorbis: failed to setup init packets @ around 0x%08lx\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
off_t packet_offset = 0;
|
||||
size_t packet_size = 0;
|
||||
int page_packets;
|
||||
|
||||
/* read OggS/SK page and get current packet */
|
||||
if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet)) goto fail;
|
||||
data->current_packet++;
|
||||
|
||||
/* read raw block */
|
||||
data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile);
|
||||
if (data->op.bytes != packet_size) {
|
||||
VGM_LOG("SK Vorbis: read error, 0x%x packet size vs 0x%lx bytes @ %lx\n", packet_size, data->op.bytes, packet_offset);
|
||||
goto fail; /* wrong packet? */
|
||||
}
|
||||
|
||||
|
||||
if (data->current_packet >= page_packets) {
|
||||
/* processed all packets in page, go to next */
|
||||
if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail;
|
||||
stream->offset = packet_offset + packet_size;
|
||||
data->current_packet = 0;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
VGM_LOG("SK Vorbis: failed to parse packet @ around 0x%08lx\n", stream->offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* Get packet info from an Ogg page, from segment/packet N (-1 = all segments)
|
||||
*
|
||||
* Page format:
|
||||
* 0x00(4): capture pattern ("OggS")
|
||||
* 0x01(1): stream structure version
|
||||
* 0x05(1): header type flag
|
||||
* 0x06(8): absolute granule position
|
||||
* 0x0e(4): stream serial number
|
||||
* 0x12(4): page sequence number
|
||||
* 0x16(4): page checksum
|
||||
* 0x1a(1): page segments (total bytes in segment table)
|
||||
* 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF)
|
||||
* 0x--(n): data
|
||||
* Reference: https://xiph.org/ogg/doc/framing.html
|
||||
*/
|
||||
static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet) {
|
||||
off_t table_offset, current_packet_offset, target_packet_offset = 0;
|
||||
size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0;
|
||||
int page_packets = 0;
|
||||
uint8_t segments;
|
||||
int i;
|
||||
|
||||
|
||||
if (read_32bitBE(page_offset+0x00, streamFile) != 0x11534B10) /* \11"SK"\10 */
|
||||
goto fail; /* not a valid page */
|
||||
/* No point on validating other stuff, but they look legal enough (CRC too it seems) */
|
||||
|
||||
segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile);
|
||||
|
||||
table_offset = page_offset + 0x1b;
|
||||
current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */
|
||||
|
||||
/* process segments */
|
||||
for (i = 0; i < segments; i++) {
|
||||
uint8_t segment_size = (uint8_t)read_8bit(table_offset, streamFile);
|
||||
total_packets_size += segment_size;
|
||||
current_packet_size += segment_size;
|
||||
table_offset += 0x01;
|
||||
|
||||
if (segment_size != 0xFF) { /* packet complete */
|
||||
page_packets++;
|
||||
|
||||
if (target_packet+1 == page_packets) {
|
||||
target_packet_offset = current_packet_offset;
|
||||
target_packet_size = current_packet_size;
|
||||
}
|
||||
|
||||
/* keep reading to fill page_packets */
|
||||
current_packet_offset += current_packet_size; /* move to next packet */
|
||||
current_packet_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* < 0 is accepted and returns first offset and all packets sizes */
|
||||
if (target_packet+1 > page_packets) goto fail;
|
||||
if (target_packet < 0) {
|
||||
target_packet_offset = page_offset + 0x1b + segments; /* first */
|
||||
target_packet_size = total_packets_size;
|
||||
}
|
||||
|
||||
if (out_packet_offset) *out_packet_offset = target_packet_offset;
|
||||
if (out_packet_size) *out_packet_size = target_packet_size;
|
||||
if (out_page_packets) *out_page_packets = page_packets;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rebuild a "SK" header packet to a "vorbis" one */
|
||||
static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size) {
|
||||
int bytes;
|
||||
|
||||
if (0x07+packet_size-0x03 > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */
|
||||
if (packet_size-0x03 != bytes) {
|
||||
VGM_LOG("SK Vorbis: packet (size 0x%x) not copied correctly (bytes=%x) @ 0x%lx\n", packet_size, bytes, packet_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0x07+packet_size-0x03;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,11 +1,11 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include "wwise_vorbis_utils.h"
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */
|
||||
|
||||
#if WWISE_VORBIS_USE_PRECOMPILED_WVC
|
||||
#include "wwise_vorbis_data.h"
|
||||
#include "vorbis_custom_data_wwise.h"
|
||||
#endif
|
||||
|
||||
|
||||
@ -13,37 +13,131 @@
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* A internal struct to pass around and simulate a bitstream.
|
||||
/* An internal struct to pass around and simulate a bitstream.
|
||||
* Mainly to keep code cleaner and somewhat closer to ww2ogg */
|
||||
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 */
|
||||
} ww_stream;
|
||||
} ww_bitstream;
|
||||
|
||||
static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian);
|
||||
static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile);
|
||||
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian);
|
||||
static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian);
|
||||
static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels);
|
||||
|
||||
static int codebook_library_copy(ww_stream * ow, ww_stream * iw);
|
||||
static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_size, STREAMFILE *streamFile);
|
||||
static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile);
|
||||
static int tremor_ilog(unsigned int v);
|
||||
static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions);
|
||||
static int ww2ogg_generate_vorbis_packet(ww_bitstream * ow, ww_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian);
|
||||
static int ww2ogg_generate_vorbis_setup(ww_bitstream * ow, ww_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile);
|
||||
static int ww2ogg_codebook_library_copy(ww_bitstream * ow, ww_bitstream * iw);
|
||||
static int ww2ogg_codebook_library_rebuild(ww_bitstream * ow, ww_bitstream * iw, size_t cb_size, STREAMFILE *streamFile);
|
||||
static int ww2ogg_codebook_library_rebuild_by_id(ww_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile);
|
||||
static int ww2ogg_tremor_ilog(unsigned int v);
|
||||
static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions);
|
||||
|
||||
static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile);
|
||||
static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile);
|
||||
static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile);
|
||||
static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_type setup_type);
|
||||
static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type);
|
||||
|
||||
static int r_bits(ww_stream * iw, int num_bits, uint32_t * value);
|
||||
static int w_bits(ww_stream * ow, int num_bits, uint32_t value);
|
||||
static int r_bits(ww_bitstream * iw, int num_bits, uint32_t * value);
|
||||
static int w_bits(ww_bitstream * ow, int num_bits, uint32_t value);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* Wwise stores a reduced setup, and packets have mini headers with the size, and data packets
|
||||
* may reduced as well. The format evolved over time so there are many variations.
|
||||
* The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter.
|
||||
*
|
||||
* Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg).
|
||||
*/
|
||||
int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
size_t header_size, packet_size;
|
||||
vorbis_custom_config cfg = data->config;
|
||||
|
||||
if (cfg.setup_type == HEADER_TRIAD) {
|
||||
/* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */
|
||||
off_t offset = start_offset;
|
||||
|
||||
/* normal identificacion packet */
|
||||
header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += header_size + packet_size;
|
||||
|
||||
/* normal comment packet */
|
||||
header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
offset += header_size + packet_size;
|
||||
|
||||
/* normal setup packet */
|
||||
header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += header_size + packet_size;
|
||||
}
|
||||
else {
|
||||
/* rebuild headers */
|
||||
|
||||
/* new identificacion packet */
|
||||
data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, cfg.blocksize_0_exp, cfg.blocksize_1_exp);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
|
||||
/* new 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 */
|
||||
|
||||
/* rebuild setup packet */
|
||||
data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, cfg.big_endian, cfg.channels);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t header_size, packet_size = 0;
|
||||
|
||||
/* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */
|
||||
header_size = get_packet_header(stream->streamfile, stream->offset, data->config.header_type, (int*)&data->op.granulepos, &packet_size, data->config.big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) {
|
||||
VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian);
|
||||
stream->offset += header_size + packet_size;
|
||||
if (!data->op.bytes || data->op.bytes >= 0xFFFF) {
|
||||
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-header_size-packet_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* loads info from a wwise packet header */
|
||||
int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_type header_type, int * granulepos, size_t * packet_size, int big_endian) {
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
@ -70,8 +164,8 @@ int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_t
|
||||
}
|
||||
|
||||
/* Transforms a Wwise data packet into a real Vorbis one (depending on config) */
|
||||
int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian) {
|
||||
ww_stream ow, iw;
|
||||
static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) {
|
||||
ww_bitstream ow, iw;
|
||||
int rc, granulepos;
|
||||
size_t header_size, packet_size;
|
||||
|
||||
@ -79,7 +173,7 @@ int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *str
|
||||
uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */
|
||||
if (obufsize < ibufsize) goto fail; /* arbitrary expected min */
|
||||
|
||||
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, &granulepos, &packet_size, big_endian);
|
||||
header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > obufsize) goto fail;
|
||||
|
||||
/* load Wwise data into internal buffer */
|
||||
@ -95,7 +189,7 @@ int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *str
|
||||
iw.bufsize = ibufsize;
|
||||
iw.b_off = 0;
|
||||
|
||||
rc = generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian);
|
||||
rc = ww2ogg_generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian);
|
||||
if (!rc) goto fail;
|
||||
|
||||
if (ow.b_off % 8 != 0) {
|
||||
@ -111,8 +205,8 @@ fail:
|
||||
|
||||
|
||||
/* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */
|
||||
int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian, int channels) {
|
||||
ww_stream ow, iw;
|
||||
static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) {
|
||||
ww_bitstream ow, iw;
|
||||
int rc, granulepos;
|
||||
size_t header_size, packet_size;
|
||||
|
||||
@ -121,7 +215,7 @@ int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *stre
|
||||
if (obufsize < ibufsize) goto fail; /* arbitrary expected min */
|
||||
|
||||
/* read Wwise packet header */
|
||||
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, &granulepos, &packet_size, big_endian);
|
||||
header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > ibufsize) goto fail;
|
||||
|
||||
/* load Wwise setup into internal buffer */
|
||||
@ -137,7 +231,7 @@ int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *stre
|
||||
iw.bufsize = ibufsize;
|
||||
iw.b_off = 0;
|
||||
|
||||
rc = generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile);
|
||||
rc = ww2ogg_generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile);
|
||||
if (!rc) goto fail;
|
||||
|
||||
if (ow.b_off % 8 != 0) {
|
||||
@ -151,11 +245,47 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) {
|
||||
int bytes = 0x1e;
|
||||
uint8_t blocksizes;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp);
|
||||
|
||||
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_8bit (buf+0x0b, channels); /* audio_channels */
|
||||
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL WW2OGG STUFF */
|
||||
/* **************************************************************************** */
|
||||
/* The following code was mostly converted from hcs's ww2ogg.
|
||||
/* The following code was mostly and manually converted from hcs's ww2ogg.
|
||||
* Could be simplified but roughly tries to preserve the structure in case fixes have to be backported.
|
||||
*
|
||||
* Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis.
|
||||
@ -165,11 +295,11 @@ fail:
|
||||
|
||||
/* Copy packet as-is or rebuild first byte if mod_packets is used.
|
||||
* (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */
|
||||
static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian) {
|
||||
static int ww2ogg_generate_vorbis_packet(ww_bitstream * ow, ww_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) {
|
||||
int i,granule;
|
||||
size_t header_size, packet_size, data_size;
|
||||
|
||||
header_size = wwise_vorbis_get_header(streamFile,offset, data->header_type, &granule, &packet_size, big_endian);
|
||||
header_size = get_packet_header(streamFile,offset, data->config.header_type, &granule, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > iw->bufsize) goto fail;
|
||||
|
||||
data_size = get_streamfile_size(streamFile);//todo get external data_size
|
||||
@ -183,7 +313,7 @@ static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *st
|
||||
//VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset);
|
||||
|
||||
|
||||
if (data->packet_type == MODIFIED) {
|
||||
if (data->config.packet_type == MODIFIED) {
|
||||
/* rebuild first bits of packet type and window info (for the i-MDCT) */
|
||||
uint32_t packet_type = 0, mode_number = 0, remainder = 0;
|
||||
|
||||
@ -213,14 +343,14 @@ static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *st
|
||||
size_t next_header_size, next_packet_size;
|
||||
int next_granule;
|
||||
|
||||
next_header_size = wwise_vorbis_get_header(streamFile,next_offset, data->header_type, &next_granule, &next_packet_size, big_endian);
|
||||
next_header_size = get_packet_header(streamFile,next_offset, data->config.header_type, &next_granule, &next_packet_size, big_endian);
|
||||
if (!next_header_size) goto fail;
|
||||
|
||||
if (next_packet_size > 0) {
|
||||
/* get next first byte to read next_mode_number */
|
||||
uint32_t next_mode_number;
|
||||
uint8_t nbuf[1];
|
||||
ww_stream nw;
|
||||
ww_bitstream nw;
|
||||
nw.buf = nbuf;
|
||||
nw.bufsize = 1;
|
||||
nw.b_off = 0;
|
||||
@ -279,7 +409,7 @@ fail:
|
||||
|
||||
/* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts.
|
||||
* (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */
|
||||
static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) {
|
||||
static int ww2ogg_generate_vorbis_setup(ww_bitstream * ow, ww_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) {
|
||||
int i,j,k;
|
||||
uint32_t codebook_count = 0, floor_count = 0, residue_count = 0;
|
||||
uint32_t codebook_count_less1 = 0;
|
||||
@ -297,16 +427,16 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da
|
||||
w_bits(ow, 8, codebook_count_less1);
|
||||
codebook_count = codebook_count_less1 + 1;
|
||||
|
||||
if (data->setup_type == FULL_SETUP) {
|
||||
if (data->config.setup_type == FULL_SETUP) {
|
||||
/* rebuild Wwise codebooks: untouched */
|
||||
for (i = 0; i < codebook_count; i++) {
|
||||
if(!codebook_library_copy(ow, iw)) goto fail;
|
||||
if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail;
|
||||
}
|
||||
}
|
||||
else if (data->setup_type == INLINE_CODEBOOKS) {
|
||||
else if (data->config.setup_type == INLINE_CODEBOOKS) {
|
||||
/* rebuild Wwise codebooks: inline in simplified format */
|
||||
for (i = 0; i < codebook_count; i++) {
|
||||
if(!codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail;
|
||||
if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -317,7 +447,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da
|
||||
|
||||
r_bits(iw, 10,&codebook_id);
|
||||
|
||||
rc = codebook_library_rebuild_by_id(ow, codebook_id, data->setup_type, streamFile);
|
||||
rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, streamFile);
|
||||
if (!rc) goto fail;
|
||||
}
|
||||
}
|
||||
@ -330,7 +460,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da
|
||||
w_bits(ow, 16, dummy_time_value);
|
||||
|
||||
|
||||
if (data->setup_type == FULL_SETUP) {
|
||||
if (data->config.setup_type == FULL_SETUP) {
|
||||
/* rest of setup is untouched, copy bits */
|
||||
uint32_t bitly = 0;
|
||||
uint32_t total_bits_read = iw->b_off;
|
||||
@ -553,8 +683,8 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da
|
||||
|
||||
for (j = 0; j < coupling_steps; j++) {
|
||||
uint32_t magnitude = 0, angle = 0;
|
||||
int magnitude_bits = tremor_ilog(channels-1);
|
||||
int angle_bits = tremor_ilog(channels-1);
|
||||
int magnitude_bits = ww2ogg_tremor_ilog(channels-1);
|
||||
int angle_bits = ww2ogg_tremor_ilog(channels-1);
|
||||
|
||||
r_bits(iw, magnitude_bits,&magnitude);
|
||||
w_bits(ow, magnitude_bits, magnitude);
|
||||
@ -619,7 +749,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da
|
||||
mode_count = mode_count_less1 + 1;
|
||||
|
||||
memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */
|
||||
data->mode_bits = tremor_ilog(mode_count-1); /* for mod_packets */
|
||||
data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */
|
||||
|
||||
for (i = 0; i < mode_count; i++) {
|
||||
uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0;
|
||||
@ -668,7 +798,7 @@ fail:
|
||||
|
||||
|
||||
/* copies Vorbis codebooks (untouched, but size uncertain) */
|
||||
static int codebook_library_copy(ww_stream * ow, ww_stream * iw) {
|
||||
static int ww2ogg_codebook_library_copy(ww_bitstream * ow, ww_bitstream * iw) {
|
||||
int i;
|
||||
uint32_t id = 0, dimensions = 0, entries = 0;
|
||||
uint32_t ordered = 0, lookup_type = 0;
|
||||
@ -697,7 +827,7 @@ static int codebook_library_copy(ww_stream * ow, ww_stream * iw) {
|
||||
current_entry = 0;
|
||||
while (current_entry < entries) {
|
||||
uint32_t number = 0;
|
||||
int number_bits = tremor_ilog(entries-current_entry);
|
||||
int number_bits = ww2ogg_tremor_ilog(entries-current_entry);
|
||||
|
||||
r_bits(iw, number_bits,&number);
|
||||
w_bits(ow, number_bits, number);
|
||||
@ -758,7 +888,7 @@ static int codebook_library_copy(ww_stream * ow, ww_stream * iw) {
|
||||
r_bits(iw, 1,&sequence_flag);
|
||||
w_bits(ow, 1, sequence_flag);
|
||||
|
||||
quantvals = tremor_book_maptype1_quantvals(entries, dimensions);
|
||||
quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions);
|
||||
for (i = 0; i < quantvals; i++) {
|
||||
uint32_t val = 0, val_bits = 0;
|
||||
val_bits = value_length+1;
|
||||
@ -784,7 +914,7 @@ fail:
|
||||
|
||||
|
||||
/* rebuilds a Wwise codebook into a Vorbis codebook */
|
||||
static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_size, STREAMFILE *streamFile) {
|
||||
static int ww2ogg_codebook_library_rebuild(ww_bitstream * ow, ww_bitstream * iw, size_t cb_size, STREAMFILE *streamFile) {
|
||||
int i;
|
||||
uint32_t id = 0, dimensions = 0, entries = 0;
|
||||
uint32_t ordered = 0, lookup_type = 0;
|
||||
@ -809,7 +939,7 @@ static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_si
|
||||
current_entry = 0;
|
||||
while (current_entry < entries) {
|
||||
uint32_t number = 0;
|
||||
int number_bits = tremor_ilog(entries-current_entry);
|
||||
int number_bits = ww2ogg_tremor_ilog(entries-current_entry);
|
||||
|
||||
r_bits(iw, number_bits,&number);
|
||||
w_bits(ow, number_bits, number);
|
||||
@ -876,7 +1006,7 @@ static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_si
|
||||
r_bits(iw, 1,&sequence_flag);
|
||||
w_bits(ow, 1, sequence_flag);
|
||||
|
||||
quantvals = tremor_book_maptype1_quantvals(entries, dimensions);
|
||||
quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions);
|
||||
for (i = 0; i < quantvals; i++) {
|
||||
uint32_t val = 0, val_bits = 0;
|
||||
val_bits = value_length+1;
|
||||
@ -908,11 +1038,11 @@ fail:
|
||||
}
|
||||
|
||||
/* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */
|
||||
static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile) {
|
||||
static int ww2ogg_codebook_library_rebuild_by_id(ww_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) {
|
||||
size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */
|
||||
uint8_t ibuf[0x8000]; /* Wwise codebook buffer */
|
||||
size_t cb_size;
|
||||
ww_stream iw;
|
||||
ww_bitstream iw;
|
||||
|
||||
cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, streamFile);
|
||||
if (cb_size == 0) goto fail;
|
||||
@ -921,14 +1051,14 @@ static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id,
|
||||
iw.bufsize = ibufsize;
|
||||
iw.b_off = 0;
|
||||
|
||||
return codebook_library_rebuild(ow, &iw, cb_size, streamFile);
|
||||
return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, streamFile);
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* fixed-point ilog from Xiph's Tremor */
|
||||
static int tremor_ilog(unsigned int v) {
|
||||
static int ww2ogg_tremor_ilog(unsigned int v) {
|
||||
int ret=0;
|
||||
while(v){
|
||||
ret++;
|
||||
@ -937,9 +1067,9 @@ static int tremor_ilog(unsigned int v) {
|
||||
return(ret);
|
||||
}
|
||||
/* quantvals-something from Xiph's Tremor */
|
||||
static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) {
|
||||
static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) {
|
||||
/* get us a starting hint, we'll polish it below */
|
||||
int bits=tremor_ilog(entries);
|
||||
int bits=ww2ogg_tremor_ilog(entries);
|
||||
int vals=entries>>((bits-1)*(dimensions-1)/dimensions);
|
||||
|
||||
while(1){
|
||||
@ -968,7 +1098,7 @@ static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigne
|
||||
/* **************************************************************************** */
|
||||
|
||||
/* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */
|
||||
static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile) {
|
||||
static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) {
|
||||
size_t bytes;
|
||||
|
||||
/* try to load from external file (ignoring type, just use file if found) */
|
||||
@ -1038,7 +1168,7 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_type setup_type) {
|
||||
static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) {
|
||||
#if WWISE_VORBIS_USE_PRECOMPILED_WVC
|
||||
|
||||
/* get pointer to array */
|
||||
@ -1102,7 +1232,7 @@ fail:
|
||||
|
||||
/* 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(ww_stream * iw, int num_bits, uint32_t * value) {
|
||||
static int r_bits(ww_bitstream * iw, int num_bits, uint32_t * value) {
|
||||
off_t off, pos;
|
||||
int i, bit_buf, bit_val;
|
||||
if (num_bits == 0) return 1;
|
||||
@ -1133,7 +1263,7 @@ fail:
|
||||
|
||||
/* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte.
|
||||
* (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/
|
||||
static int w_bits(ww_stream * ow, int num_bits, uint32_t value) {
|
||||
static int w_bits(ww_bitstream * ow, int num_bits, uint32_t value) {
|
||||
off_t off, pos;
|
||||
int i, bit_val, bit_buf;
|
||||
if (num_bits == 0) return 1;
|
@ -1,305 +0,0 @@
|
||||
#include "coding.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#include "wwise_vorbis_utils.h"
|
||||
|
||||
|
||||
#define WWISE_VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm);
|
||||
static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long);
|
||||
static int vorbis_make_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
/**
|
||||
* Inits a raw Wwise vorbis stream.
|
||||
*
|
||||
* Vorbis packets are stored in .ogg, which is divided into ogg pages/packets, and the first packets contain necessary
|
||||
* vorbis setup. For raw vorbis the setup is stored it elsewhere (i.e.- in the .exe), presumably to shave off some kb
|
||||
* per stream and codec startup time. We'll read the external setup and raw data and decode it with libvorbis.
|
||||
*
|
||||
* Wwise stores a reduced setup, and the raw vorbis packets have mini headers with the block size,
|
||||
* and vorbis packets themselves may reduced as well. The format evolved over time so there are some variations.
|
||||
* The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter.
|
||||
*
|
||||
* Format reverse engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg).
|
||||
* Info from the official docs (https://www.xiph.org/vorbis/doc/libvorbis/overview.html).
|
||||
*/
|
||||
vorbis_codec_data * init_wwise_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp,
|
||||
wwise_setup_type setup_type, wwise_header_type header_type, wwise_packet_type packet_type, int big_endian) {
|
||||
vorbis_codec_data * data = NULL;
|
||||
size_t header_size, packet_size;
|
||||
|
||||
/* init stuff */
|
||||
data = calloc(1,sizeof(vorbis_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->buffer_size = WWISE_VORBIS_DEFAULT_BUFFER_SIZE;
|
||||
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
data->setup_type = setup_type;
|
||||
data->header_type = header_type;
|
||||
data->packet_type = packet_type;
|
||||
|
||||
/* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks)
|
||||
* libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */
|
||||
vorbis_info_init(&data->vi);
|
||||
vorbis_comment_init(&data->vc);
|
||||
|
||||
data->op.packet = data->buffer;
|
||||
data->op.b_o_s = 1; /* fake headers start */
|
||||
|
||||
if (setup_type == HEADER_TRIAD) {
|
||||
/* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */
|
||||
off_t offset = start_offset;
|
||||
|
||||
/* normal identificacion packet */
|
||||
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += header_size + packet_size;
|
||||
|
||||
/* normal comment packet */
|
||||
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
offset += header_size + packet_size;
|
||||
|
||||
/* normal setup packet */
|
||||
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += header_size + packet_size;
|
||||
}
|
||||
else {
|
||||
/* rebuild headers */
|
||||
|
||||
/* new identificacion packet */
|
||||
data->op.bytes = vorbis_make_header_identification(data->buffer, data->buffer_size, channels, sample_rate, blocksize_0_exp, blocksize_1_exp);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
|
||||
/* new comment packet */
|
||||
data->op.bytes = vorbis_make_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 */
|
||||
|
||||
/* rebuild setup packet */
|
||||
data->op.bytes = wwise_vorbis_rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, big_endian, channels);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
}
|
||||
|
||||
data->op.b_o_s = 0; /* end of fake headers */
|
||||
|
||||
/* init vorbis global and block state */
|
||||
if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail;
|
||||
if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail;
|
||||
|
||||
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_wwise_vorbis(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes raw Wwise Vorbis
|
||||
*/
|
||||
void decode_wwise_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
|
||||
vorbis_codec_data * data = vgmstream->codec_data;
|
||||
size_t stream_size = get_streamfile_size(stream->streamfile);
|
||||
//data->op.packet = data->buffer;/* implicit from init */
|
||||
int samples_done = 0;
|
||||
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
/* extra EOF check for edge cases */
|
||||
if (stream->offset > stream_size) {
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (data->samples_full) { /* read more samples */
|
||||
int samples_to_get;
|
||||
float **pcm;
|
||||
|
||||
/* get PCM samples from libvorbis buffers */
|
||||
samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm);
|
||||
if (!samples_to_get) {
|
||||
data->samples_full = 0; /* request more if empty*/
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data->samples_to_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > data->samples_to_discard)
|
||||
samples_to_get = data->samples_to_discard;
|
||||
data->samples_to_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and convert from Vorbis float pcm to 16bit pcm */
|
||||
if (samples_to_get > samples_to_do - samples_done)
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
pcm_convert_float_to_16(data, outbuf + samples_done * channels, samples_to_get, pcm);
|
||||
samples_done += samples_to_get;
|
||||
}
|
||||
|
||||
/* mark consumed samples from the buffer
|
||||
* (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */
|
||||
vorbis_synthesis_read(&data->vd, samples_to_get);
|
||||
}
|
||||
else { /* read more data */
|
||||
int rc;
|
||||
size_t header_size, packet_size;
|
||||
|
||||
data->op.packetno++; /* not actually needed, but feels nicer */
|
||||
|
||||
/* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */
|
||||
header_size = wwise_vorbis_get_header(stream->streamfile, stream->offset, data->header_type, (int*)&data->op.granulepos, &packet_size, vgmstream->codec_endian);
|
||||
if (!header_size || packet_size > data->buffer_size) {
|
||||
VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
data->op.bytes = wwise_vorbis_rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, vgmstream->codec_endian);
|
||||
stream->offset += header_size + packet_size; /* update first to avoid infinite loops */
|
||||
if (!data->op.bytes || data->op.bytes >= 0xFFFF) {
|
||||
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
/* parse the fake ogg packet into a logical vorbis block */
|
||||
rc = vorbis_synthesis(&data->vb,&data->op);
|
||||
if (rc == OV_ENOTAUDIO) {
|
||||
VGM_LOG("Wwise Vorbis: not audio packet @ %lx\n",stream->offset);
|
||||
continue; /* bad packet parsing */
|
||||
} else if (rc != 0) {
|
||||
VGM_LOG("Wwise Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
/* finally decode the logical block into samples */
|
||||
rc = vorbis_synthesis_blockin(&data->vd,&data->vb);
|
||||
if (rc != 0) {
|
||||
VGM_LOG("Wwise Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset);
|
||||
goto decode_fail; /* ? */
|
||||
}
|
||||
|
||||
data->samples_full = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
/* on error just put some 0 samples */
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
||||
}
|
||||
|
||||
/* *************************************************** */
|
||||
|
||||
static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) {
|
||||
/* mostly from Xiph's decoder_example.c */
|
||||
int i,j;
|
||||
|
||||
/* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc)
|
||||
* to 16 bit signed PCM ints (host order) and interleave + fix clipping */
|
||||
for (i = 0; i < data->vi.channels; i++) {
|
||||
sample *ptr = outbuf + i;
|
||||
float *mono = pcm[i];
|
||||
for (j = 0; j < samples_to_do; j++) {
|
||||
int val = floor(mono[j] * 32767.f + .5f);
|
||||
if (val > 32767) val = 32767;
|
||||
if (val < -32768) val = -32768;
|
||||
|
||||
*ptr = val;
|
||||
ptr += data->vi.channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) {
|
||||
int bytes = 0x1e;
|
||||
uint8_t blocksizes;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp);
|
||||
|
||||
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_8bit (buf+0x0b, channels); /* audio_channels */
|
||||
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int vorbis_make_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;
|
||||
}
|
||||
|
||||
/* *************************************** */
|
||||
|
||||
void free_wwise_vorbis(vorbis_codec_data * data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
/* internal decoder cleanp */
|
||||
vorbis_info_clear(&data->vi);
|
||||
vorbis_comment_clear(&data->vc);
|
||||
vorbis_dsp_clear(&data->vd);
|
||||
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void reset_wwise_vorbis(VGMSTREAM *vgmstream) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = 0;
|
||||
}
|
||||
|
||||
void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
vorbis_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead.
|
||||
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||
vorbis_synthesis_restart(&data->vd);
|
||||
data->samples_to_discard = num_sample;
|
||||
if (vgmstream->loop_ch) /* this func is only using for looping though */
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,11 +0,0 @@
|
||||
#ifndef _WWISE_VORBIS_UTILS_H_
|
||||
#define _WWISE_VORBIS_UTILS_H_
|
||||
|
||||
#include "coding.h"
|
||||
|
||||
int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_type header_type, int * granulepos, size_t * packet_size, int big_endian);
|
||||
int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian);
|
||||
int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian, int channels);
|
||||
|
||||
|
||||
#endif/*_WWISE_VORBIS_UTILS_H_ */
|
@ -465,21 +465,13 @@ static const coding_info coding_info_list[] = {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{coding_ogg_vorbis, "Ogg Vorbis"},
|
||||
{coding_fsb_vorbis, "FSB Vorbis"},
|
||||
{coding_wwise_vorbis, "Wwise Vorbis"},
|
||||
{coding_ogl_vorbis, "OGL Vorbis"},
|
||||
{coding_VORBIS_custom, "Custom Vorbis"},
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
{coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"},
|
||||
{coding_MPEG1_L1, "MPEG-1 Layer I Audio"},
|
||||
{coding_MPEG1_L2, "MPEG-1 Layer II Audio"},
|
||||
{coding_MPEG1_L3, "MPEG-1 Layer III Audio (MP3)"},
|
||||
{coding_MPEG2_L1, "MPEG-2 Layer I Audio"},
|
||||
{coding_MPEG2_L2, "MPEG-2 Layer II Audio"},
|
||||
{coding_MPEG2_L3, "MPEG-2 Layer III Audio (MP3)"},
|
||||
{coding_MPEG25_L1, "MPEG-2.5 Layer I Audio"},
|
||||
{coding_MPEG25_L2, "MPEG-2.5 Layer II Audio"},
|
||||
{coding_MPEG25_L3, "MPEG-2.5 Layer III Audio (MP3)"},
|
||||
{coding_MPEG_custom, "Custom MPEG Audio"},
|
||||
{coding_MPEG_layer1, "MPEG Layer I Audio (MP1)"},
|
||||
{coding_MPEG_layer2, "MPEG Layer II Audio (MP2)"},
|
||||
{coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"},
|
||||
#endif
|
||||
#ifdef VGM_USE_G7221
|
||||
{coding_G7221, "ITU G.722.1 (Polycom Siren 7)"},
|
||||
@ -538,8 +530,7 @@ static const layout_info layout_info_list[] = {
|
||||
{layout_ogg_vorbis, "Ogg"},
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
{layout_fake_mpeg, "MPEG Audio stream with incorrect frame headers"},
|
||||
{layout_mpeg, "MPEG Audio stream"},
|
||||
{layout_mpeg_custom, "Custom MPEG Audio"},
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -880,6 +871,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_WII_04SW, "Reflections 04SW header"},
|
||||
{meta_TXTH, "TXTH Generic Header"},
|
||||
{meta_EA_BNK, "Electronic Arts BNK header"},
|
||||
{meta_SK_AUD, "Silicon Knights AUD header"},
|
||||
{meta_AHX, "CRI AHX header"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
@ -890,9 +883,6 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_OGG_KOVS, "Ogg Vorbis, KOVS header"},
|
||||
{meta_OGG_PSYCH, "Ogg Vorbis, Psychic Software obfuscation"},
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
{meta_AHX, "CRI AHX header"},
|
||||
#endif
|
||||
#ifdef VGM_USE_MP4V2
|
||||
{meta_MP4, "AAC header"},
|
||||
#endif
|
||||
|
@ -1297,19 +1297,31 @@
|
||||
<File
|
||||
RelativePath=".\coding\coding.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\fsb_vorbis_data.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\g72x_state.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\nwa_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_data_fsb.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_data_wwise.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
@ -1337,10 +1349,6 @@
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\fsb_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\g719_decoder.c"
|
||||
@ -1373,6 +1381,14 @@
|
||||
<File
|
||||
RelativePath=".\coding\mc3_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_custom_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_custom_utils_ahx.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mpeg_decoder.c"
|
||||
@ -1413,10 +1429,6 @@
|
||||
<File
|
||||
RelativePath=".\coding\ogg_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ogl_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\pcm_decoder.c"
|
||||
@ -1438,14 +1450,6 @@
|
||||
RelativePath=".\coding\sdx2_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\wwise_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\wwise_vorbis_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ws_decoder.c"
|
||||
>
|
||||
@ -1454,6 +1458,26 @@
|
||||
RelativePath=".\coding\xa_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_fsb.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_ogl.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_sk.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_custom_utils_wwise.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\ext_libs\clHCA.c"
|
||||
>
|
||||
|
@ -416,7 +416,6 @@
|
||||
<ClCompile Include="coding\adx_decoder.c" />
|
||||
<ClCompile Include="coding\aica_decoder.c" />
|
||||
<ClCompile Include="coding\ea_decoder.c" />
|
||||
<ClCompile Include="coding\fsb_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\g719_decoder.c" />
|
||||
<ClCompile Include="coding\g721_decoder.c" />
|
||||
<ClCompile Include="coding\g7221_decoder.c" />
|
||||
@ -424,6 +423,8 @@
|
||||
<ClCompile Include="coding\ima_decoder.c" />
|
||||
<ClCompile Include="coding\l5_555_decoder.c" />
|
||||
<ClCompile Include="coding\mc3_decoder.c" />
|
||||
<ClCompile Include="coding\mpeg_custom_utils.c" />
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ahx.c" />
|
||||
<ClCompile Include="coding\mpeg_decoder.c" />
|
||||
<ClCompile Include="coding\msadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\nds_procyon_decoder.c" />
|
||||
@ -432,15 +433,17 @@
|
||||
<ClCompile Include="coding\ngc_dtk_decoder.c" />
|
||||
<ClCompile Include="coding\nwa_decoder.c" />
|
||||
<ClCompile Include="coding\ogg_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\ogl_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\pcm_decoder.c" />
|
||||
<ClCompile Include="coding\psv_decoder.c" />
|
||||
<ClCompile Include="coding\psx_decoder.c" />
|
||||
<ClCompile Include="coding\SASSC_decoder.c" />
|
||||
<ClCompile Include="coding\sdx2_decoder.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_decoder.c" />
|
||||
<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_wwise.c" />
|
||||
<ClCompile Include="coding\ws_decoder.c" />
|
||||
<ClCompile Include="coding\wwise_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\wwise_vorbis_utils.c" />
|
||||
<ClCompile Include="coding\xa_decoder.c" />
|
||||
<ClCompile Include="layout\aax_layout.c" />
|
||||
<ClCompile Include="layout\aix_layout.c" />
|
||||
|
@ -790,9 +790,6 @@
|
||||
<ClCompile Include="coding\ea_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\fsb_vorbis_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\g721_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -811,6 +808,12 @@
|
||||
<ClCompile Include="coding\mc3_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mpeg_custom_utils.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mpeg_custom_utils_ahx.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mpeg_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -835,9 +838,6 @@
|
||||
<ClCompile Include="coding\ogg_vorbis_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ogl_vorbis_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\pcm_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -856,10 +856,19 @@
|
||||
<ClCompile Include="coding\ws_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\wwise_vorbis_decoder.c">
|
||||
<ClCompile Include="coding\vorbis_custom_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\wwise_vorbis_utils.c">
|
||||
<ClCompile Include="coding\vorbis_custom_utils_fsb.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\vorbis_custom_utils_ogl.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\vorbis_custom_utils_sk.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\vorbis_custom_utils_wwise.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\xa_decoder.c">
|
||||
|
@ -2,15 +2,11 @@
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
||||
/* AHX is a CRI format which contains an MPEG-2 Layer 2 audio stream.
|
||||
* Although the MPEG frame headers are incorrect... */
|
||||
/* AHX - CRI format mainly for voices, contains MPEG-2 Layer 2 audio with lying frame headers */
|
||||
VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channel_count = 1;
|
||||
int loop_flag = 0;
|
||||
int channel_count = 1, loop_flag = 0;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if ( !check_extensions(streamFile, "ahx") ) goto fail;
|
||||
@ -19,42 +15,52 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail;
|
||||
|
||||
/* get stream offset, check for CRI signature just before */
|
||||
start_offset = (uint16_t)read_16bitBE(2,streamFile) + 4;
|
||||
if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 ||/* "(c" */
|
||||
(uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */
|
||||
) goto fail;
|
||||
start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04;
|
||||
|
||||
/* check for encoding type */
|
||||
/* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is
|
||||
* ADX with exponential scale, 0x11 is AHX */
|
||||
/* Sappharad reports that old AHXs (Sonic Adventure 2) don't have this flag.
|
||||
* When I see one perhaps I can add an exception for those. */
|
||||
if (read_8bit(4,streamFile) != 0x11) goto fail;
|
||||
if ((uint16_t)read_16bitBE(start_offset-0x06,streamFile)!=0x2863 || /* "(c" */
|
||||
(uint32_t)read_32bitBE(start_offset-0x04,streamFile)!=0x29435249) /* ")CRI" */
|
||||
goto fail;
|
||||
|
||||
/* check for encoding type (0x10 is AHX for DC with bigger frames, 0x11 is AHX, 0x0N are ADX) */
|
||||
if (read_8bit(0x04,streamFile) != 0x10 &&
|
||||
read_8bit(0x04,streamFile) != 0x11) goto fail;
|
||||
|
||||
/* check for frame size (0 for AHX) */
|
||||
if (read_8bit(5,streamFile) != 0) goto fail;
|
||||
if (read_8bit(0x05,streamFile) != 0) goto fail;
|
||||
|
||||
/* check for bits per sample? (0 for AHX) */
|
||||
if (read_8bit(6,streamFile) != 0) goto fail;
|
||||
if (read_8bit(0x06,streamFile) != 0) goto fail;
|
||||
|
||||
/* check channel count (only mono AHXs are known) */
|
||||
if (read_8bit(7,streamFile) != 1) goto fail;
|
||||
/* check channel count (only mono AHXs can be created by the encoder) */
|
||||
if (read_8bit(0x07,streamFile) != 1) goto fail;
|
||||
|
||||
/* At this point we almost certainly have an AHX file,
|
||||
* so let's build the VGMSTREAM. */
|
||||
/* check version signature */
|
||||
if (read_8bit(0x12,streamFile) != 0x06) goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitBE(0xc,streamFile);
|
||||
/* This is the One True Samplerate, the MPEG headers lie. */
|
||||
vgmstream->sample_rate = read_32bitBE(8,streamFile);
|
||||
vgmstream->sample_rate = read_32bitBE(0x08,streamFile); /* real sample rate */
|
||||
vgmstream->num_samples = read_32bitBE(0x0c,streamFile); /* doesn't include encoder_delay (handled in decoder) */
|
||||
|
||||
vgmstream->coding_type = coding_fake_MPEG2_L2;
|
||||
vgmstream->layout_type = layout_fake_mpeg;
|
||||
vgmstream->meta_type = meta_AHX;
|
||||
vgmstream->codec_data = init_mpeg_codec_data_ahx(streamFile, start_offset, channel_count);
|
||||
|
||||
{
|
||||
#ifdef VGM_USE_MPEG
|
||||
mpeg_custom_config cfg;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
@ -65,5 +71,3 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@
|
||||
#define EA_CODEC2_XBOXADPCM 0x14
|
||||
#define EA_CODEC2_MT5 0x16
|
||||
#define EA_CODEC2_EALAYER3 0x17
|
||||
#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */
|
||||
|
||||
#define EA_MAX_CHANNELS 6
|
||||
|
||||
@ -127,7 +128,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
|
||||
if (!check_extensions(streamFile,"bnk,sdt,mus"))
|
||||
goto fail;
|
||||
|
||||
VGM_LOG("1\n");
|
||||
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */
|
||||
read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */
|
||||
@ -136,7 +137,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
|
||||
else
|
||||
goto fail;
|
||||
|
||||
VGM_LOG("2\n");
|
||||
/* use header size as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
|
||||
read_32bit = read_32bitBE;
|
||||
@ -151,7 +152,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
/* check multi-streams */
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
|
||||
VGM_LOG("3\n");
|
||||
switch(bnk_version) {
|
||||
case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */
|
||||
header_size = read_32bit(offset + 0x08,streamFile); /* full size */
|
||||
@ -169,10 +170,10 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
VGM_LOG("EA BNK: unknown version %x\n", bnk_version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
VGM_LOG("4\n");
|
||||
if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset))
|
||||
goto fail;
|
||||
|
||||
VGM_LOG("5\n");
|
||||
/* fix absolute offsets so it works in next funcs */
|
||||
if (offset) {
|
||||
for (i = 0; i < ea.channels; i++) {
|
||||
@ -303,21 +304,39 @@ 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;
|
||||
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;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_codec_data_interleaved(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, 0);
|
||||
if (!vgmstream->codec_data) 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;
|
||||
break;
|
||||
}
|
||||
|
||||
case EA_CODEC2_EALAYER3: { /* MP3 variant */
|
||||
mpeg_custom_config cfg;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */
|
||||
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
|
||||
case EA_CODEC2_EALAYER3: /* MP3 variant */
|
||||
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
|
||||
goto fail;
|
||||
@ -810,7 +829,7 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
if (id == 0x5343446C) { /* "SCDl" data block found */
|
||||
off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first channel offset is ok, MPEG channels share offsets */
|
||||
off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
|
||||
return block_offset + 0x0c + ea->channels*0x04 + offset;
|
||||
} else if (id == 0x5343436C) { /* "SCCl" data count found */
|
||||
block_offset += block_size; /* size includes header */
|
||||
|
@ -278,34 +278,25 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
|
||||
if (fsbh.mode & FSOUND_MPEG) {
|
||||
/* FSB3: ?; FSB4: Shatter, Way of the Samurai 3/4, Forza Horizon 1/2, Dragon Age Origins */
|
||||
#if defined(VGM_USE_MPEG)
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
coding_t mpeg_coding_type;
|
||||
int fsb_padding = 0;
|
||||
mpeg_custom_config cfg;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 :
|
||||
(fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
|
||||
(fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0)));
|
||||
|
||||
//VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
|
||||
VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n");
|
||||
|
||||
if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED)
|
||||
fsb_padding = fsbh.numchannels > 2 ? 16 : 2;
|
||||
else if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)
|
||||
fsb_padding = fsbh.numchannels > 2 ? 16 : 4;
|
||||
else /* needed by multichannel with no flags */
|
||||
fsb_padding = fsbh.numchannels > 2 ? 16 : 0;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->coding_type = mpeg_coding_type;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
/* both to setup initial interleave in vgmstream_open_stream */
|
||||
vgmstream->interleave_block_size = cfg.interleave;
|
||||
vgmstream->layout_type = layout_mpeg_custom;
|
||||
|
||||
vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding;
|
||||
//mpeg_set_error_logging(mpeg_data, 0); /* should not be needed anymore with the interleave decoder */
|
||||
|
||||
#elif defined(VGM_USE_FFMPEG)
|
||||
/* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */
|
||||
goto fail;
|
||||
#else
|
||||
goto fail;
|
||||
goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */
|
||||
#endif
|
||||
}
|
||||
else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */
|
||||
|
@ -258,20 +258,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
coding_t mpeg_coding_type;
|
||||
int fsb_padding = 0;
|
||||
mpeg_custom_config cfg;
|
||||
|
||||
fsb_padding = vgmstream->channels > 2 ? 16 : 4; /* observed default */
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
|
||||
|
||||
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->coding_type = mpeg_coding_type;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding;
|
||||
//mpeg_set_error_logging(mpeg_data, 0); /* should not be needed anymore with the interleave decoder */
|
||||
/* both to setup initial interleave in vgmstream_open_stream */
|
||||
vgmstream->interleave_block_size = cfg.interleave;
|
||||
vgmstream->layout_type = layout_mpeg_custom;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -286,10 +283,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS */
|
||||
vgmstream->codec_data = init_fsb_vorbis_codec_data(streamFile, StartOffset, vgmstream->channels, vgmstream->sample_rate,VorbisSetupId);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_fsb_vorbis;
|
||||
vorbis_custom_config cfg;
|
||||
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.sample_rate = vgmstream->sample_rate;
|
||||
cfg.setup_id = VorbisSetupId;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, StartOffset, VORBIS_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
case SDX2: coding = coding_SDX2; break;
|
||||
case DVI_IMA: coding = coding_DVI_IMA; break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MPEG: coding = coding_MPEG1_L3; break; /* we later find out exactly which */
|
||||
case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */
|
||||
#endif
|
||||
case IMA: coding = coding_IMA; break;
|
||||
case AICA: coding = coding_AICA; break;
|
||||
@ -258,8 +258,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG1_L3:
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
case coding_MPEG_layer3:
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &coding, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
|
@ -684,4 +684,6 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) {
|
||||
if (partial_file_size > get_streamfile_size(streamFile)) goto fail;
|
||||
loop_end_sample = num_samples; /* there is no data after num_samples (ie.- it's really num_samples) */
|
||||
|
||||
/* this is actually peeking into the Vorbis id packet */
|
||||
/* actually peeking into the Vorbis id packet */
|
||||
channel_count = read_8bit (0x21,streamFile);
|
||||
sample_rate = read_32bitLE(0x22,streamFile);
|
||||
|
||||
@ -41,10 +41,15 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vgmstream->codec_data = init_ogl_vorbis_codec_data(streamFile, 0x14, &start_offset);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ogl_vorbis;
|
||||
vorbis_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x14, VORBIS_OGL, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
start_offset = cfg.data_start_offset;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
|
@ -95,7 +95,6 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
|
||||
channel_count = read_32bitLE(parse_offset+0x10,streamFile);
|
||||
block_size = read_32bitLE(parse_offset+0x14,streamFile);
|
||||
num_samples = num_samples / channel_count; /* total samples */
|
||||
block_size = block_size * channel_count; /* seems ok? */
|
||||
start_offset = parse_offset+0x18;
|
||||
break;
|
||||
|
||||
@ -141,17 +140,19 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x6D703300: { /* "mp3\0" (PS3) */
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
coding_t mpeg_coding_type;
|
||||
mpeg_custom_config cfg;
|
||||
|
||||
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_P3D, 0);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->coding_type = mpeg_coding_type;
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
cfg.interleave = 0x400;
|
||||
/* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */
|
||||
|
||||
goto fail; //todo: not working right (unknown interleave)
|
||||
//break;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
/* both to setup initial interleave in vgmstream_open_stream */
|
||||
vgmstream->interleave_block_size = cfg.interleave;
|
||||
vgmstream->layout_type = layout_mpeg_custom;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
|
||||
vgmstream->coding_type = ct;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data);
|
||||
vgmstream->num_samples -= vgmstream->num_samples % frame_size;
|
||||
if (loop_flag) {
|
||||
|
@ -82,22 +82,25 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
coding_t mpeg_coding_type;
|
||||
mpeg_custom_config cfg;
|
||||
int fixed_frame_size;
|
||||
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
|
||||
/* "mpin": mpeg info */
|
||||
/* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */
|
||||
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/
|
||||
fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile);
|
||||
|
||||
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FIXED, fixed_frame_size);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->coding_type = mpeg_coding_type;
|
||||
vgmstream->interleave_block_size = fixed_frame_size * multiplier;
|
||||
cfg.chunk_size = fixed_frame_size;
|
||||
cfg.interleave = fixed_frame_size * multiplier;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
/* both to setup initial interleave in vgmstream_open_stream */
|
||||
vgmstream->interleave_block_size = cfg.interleave;
|
||||
vgmstream->layout_type = layout_mpeg_custom;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
75
src/meta/sk_aud.c
Normal file
75
src/meta/sk_aud.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
static int get_sk_num_samples(STREAMFILE *streamFile, off_t start_offset);
|
||||
|
||||
/* AUD/SK - Silicon Knights obfuscated Ogg (cutscene/voices) [Eternal Darkness (GC)] */
|
||||
VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, sample_rate;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"aud"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x11534B10) /* \11"SK"\10 */
|
||||
goto fail;
|
||||
|
||||
/* the format is just mutant Ogg so actually peeking into the Vorbis id packet here */
|
||||
channel_count = read_8bit (0x23,streamFile);
|
||||
sample_rate = read_32bitLE(0x24,streamFile);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = get_sk_num_samples(streamFile, 0);
|
||||
vgmstream->meta_type = meta_SK_AUD;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x00, VORBIS_SK, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
start_offset = cfg.data_start_offset;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
/* SK/Ogg doesn't have num_samples info, manually read total samples */
|
||||
static int get_sk_num_samples(STREAMFILE *streamFile, off_t start_offset) {
|
||||
uint32_t expected_id = 0x11534B10; /* \11"SK"\10 (would read "OggS" by changing the ID) */
|
||||
off_t off = get_streamfile_size(streamFile) - 4-1-1-8-4-4-4;
|
||||
|
||||
/* simplest way is to find last OggS/SK page from stream end */
|
||||
while (off >= start_offset) {
|
||||
uint32_t current_id = read_32bitBE(off, streamFile);
|
||||
if (current_id == expected_id) { /* last packet starts with 0x0004, if more checks are needed */
|
||||
return read_32bitLE(off+4+1+1, streamFile); /* get last granule = total samples (64b but whatevs) */
|
||||
}
|
||||
|
||||
off--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -252,7 +252,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
|
||||
vgmstream->coding_type = ct;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
|
||||
vgmstream->num_samples -= vgmstream->num_samples%576;
|
||||
if (loop_flag) {
|
||||
|
@ -127,7 +127,7 @@ found:
|
||||
case SDX2: coding = coding_SDX2; break;
|
||||
case DVI_IMA: coding = coding_DVI_IMA; break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MPEG: coding = coding_MPEG1_L3; break; /* we later find out exactly which */
|
||||
case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */
|
||||
#endif
|
||||
case IMA: coding = coding_IMA; break;
|
||||
case AICA: coding = coding_AICA; break;
|
||||
@ -272,8 +272,8 @@ found:
|
||||
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG1_L3:
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
case coding_MPEG_layer3:
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.start_offset, &coding, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
|
@ -125,7 +125,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
||||
/* chunks: "MARK" optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
break;
|
||||
|
@ -214,11 +214,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
/* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */
|
||||
off_t vorb_offset, data_offsets, block_offsets;
|
||||
size_t vorb_size, setup_offset, audio_offset;
|
||||
vorbis_custom_config cfg;
|
||||
|
||||
wwise_setup_type setup_type;
|
||||
wwise_header_type header_type;
|
||||
wwise_packet_type packet_type;
|
||||
int blocksize_0_exp = 0, blocksize_1_exp = 0;
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
cfg.channels = ww.channels;
|
||||
cfg.sample_rate = ww.sample_rate;
|
||||
cfg.big_endian = ww.big_endian;
|
||||
|
||||
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */
|
||||
|
||||
@ -231,25 +232,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */
|
||||
data_offsets = 0x18;
|
||||
block_offsets = 0; /* no need, full headers are present */
|
||||
header_type = TYPE_8;
|
||||
packet_type = STANDARD;
|
||||
setup_type = HEADER_TRIAD;
|
||||
cfg.header_type = TYPE_8;
|
||||
cfg.packet_type = STANDARD;
|
||||
cfg.setup_type = HEADER_TRIAD;
|
||||
break;
|
||||
|
||||
//case 0x32: /* ? */
|
||||
case 0x34: /* common (2010~2011) */
|
||||
data_offsets = 0x18;
|
||||
block_offsets = 0x30;
|
||||
header_type = TYPE_6;
|
||||
packet_type = STANDARD;
|
||||
setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
|
||||
cfg.header_type = TYPE_6;
|
||||
cfg.packet_type = STANDARD;
|
||||
cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
|
||||
break;
|
||||
case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */
|
||||
data_offsets = 0x10;
|
||||
block_offsets = 0x28;
|
||||
header_type = TYPE_2;
|
||||
packet_type = MODIFIED;
|
||||
setup_type = EXTERNAL_CODEBOOKS;
|
||||
cfg.header_type = TYPE_2;
|
||||
cfg.packet_type = MODIFIED;
|
||||
cfg.setup_type = EXTERNAL_CODEBOOKS;
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size);
|
||||
@ -260,8 +261,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
setup_offset = read_32bit(vorb_offset + data_offsets + 0x00, streamFile); /* within data (0 = no seek table) */
|
||||
audio_offset = read_32bit(vorb_offset + data_offsets + 0x04, streamFile); /* within data */
|
||||
if (block_offsets) {
|
||||
blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, streamFile); /* small */
|
||||
blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, streamFile); /* big */
|
||||
cfg.blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, streamFile); /* small */
|
||||
cfg.blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, streamFile); /* big */
|
||||
}
|
||||
|
||||
/* detect setup type:
|
||||
@ -274,16 +275,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
|
||||
/* if the setup after header starts with "(data)BCV" it's an inline codebook) */
|
||||
if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */
|
||||
setup_type = FULL_SETUP;
|
||||
cfg.setup_type = FULL_SETUP;
|
||||
}
|
||||
/* if the setup is suspiciously big it's probably trimmed inline codebooks */
|
||||
else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */
|
||||
setup_type = INLINE_CODEBOOKS;
|
||||
cfg.setup_type = INLINE_CODEBOOKS;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp,
|
||||
setup_type,header_type,packet_type, ww.big_endian);
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
else {
|
||||
@ -295,13 +295,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
case 0x30:
|
||||
data_offsets = 0x10;
|
||||
block_offsets = 0x28;
|
||||
header_type = TYPE_2;
|
||||
packet_type = MODIFIED;
|
||||
cfg.header_type = TYPE_2;
|
||||
cfg.packet_type = MODIFIED;
|
||||
|
||||
/* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks
|
||||
* - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too)
|
||||
* - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */
|
||||
setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
break;
|
||||
|
||||
//case 0x2a: /* Rocksmith 2011 X360? */
|
||||
@ -312,34 +312,32 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
vgmstream->num_samples = read_32bit(extra_offset + 0x00, streamFile);
|
||||
setup_offset = read_32bit(extra_offset + data_offsets + 0x00, streamFile); /* within data*/
|
||||
setup_offset = read_32bit(extra_offset + data_offsets + 0x00, streamFile); /* within data */
|
||||
audio_offset = read_32bit(extra_offset + data_offsets + 0x04, streamFile); /* within data */
|
||||
blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, streamFile); /* small */
|
||||
blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */
|
||||
cfg.blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, streamFile); /* small */
|
||||
cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */
|
||||
|
||||
/* Normal packets are used rarely (ex. Oddworld New 'n' Tasty! PSV). They are hard to detect (decoding
|
||||
* will mostly work with garbage results) but we'll try. Setup size and "fmt" bitrate fields may matter too. */
|
||||
if (ww.extra_size == 0x30) {
|
||||
/* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09
|
||||
* (maybe lower spec machines = needs simpler packets) */
|
||||
if (blocksize_0_exp == blocksize_1_exp)
|
||||
packet_type = STANDARD;
|
||||
if (cfg.blocksize_0_exp == cfg.blocksize_1_exp)
|
||||
cfg.packet_type = STANDARD;
|
||||
}
|
||||
|
||||
|
||||
/* try with the selected codebooks */
|
||||
vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp,
|
||||
setup_type,header_type,packet_type, ww.big_endian);
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) {
|
||||
/* codebooks failed: try again with the other type */
|
||||
setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS;
|
||||
vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp,
|
||||
setup_type,header_type,packet_type, ww.big_endian);
|
||||
cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
}
|
||||
vgmstream->coding_type = coding_wwise_vorbis;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_endian = ww.big_endian;
|
||||
|
||||
start_offset = start_offset + audio_offset;
|
||||
|
116
src/vgmstream.c
116
src/vgmstream.c
@ -84,9 +84,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_aifc,
|
||||
init_vgmstream_str_snds,
|
||||
init_vgmstream_ws_aud,
|
||||
#ifdef VGM_USE_MPEG
|
||||
init_vgmstream_ahx,
|
||||
#endif
|
||||
init_vgmstream_ivb,
|
||||
init_vgmstream_amts,
|
||||
init_vgmstream_svs,
|
||||
@ -370,6 +368,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_wii_04sw,
|
||||
init_vgmstream_ea_bnk,
|
||||
init_vgmstream_ea_schl_fixed,
|
||||
init_vgmstream_sk_aud,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
@ -494,16 +493,8 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
reset_ogg_vorbis(vgmstream);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
||||
reset_fsb_vorbis(vgmstream);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||
reset_wwise_vorbis(vgmstream);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_ogl_vorbis) {
|
||||
reset_ogl_vorbis(vgmstream);
|
||||
if (vgmstream->coding_type==coding_VORBIS_custom) {
|
||||
reset_vorbis_custom(vgmstream);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -518,8 +509,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->layout_type==layout_mpeg ||
|
||||
vgmstream->layout_type==layout_fake_mpeg) {
|
||||
if (vgmstream->coding_type==coding_MPEG_custom ||
|
||||
vgmstream->coding_type==coding_MPEG_layer1 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer2 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer3) {
|
||||
reset_mpeg(vgmstream);
|
||||
}
|
||||
#endif
|
||||
@ -678,18 +671,8 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
||||
free_fsb_vorbis(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||
free_wwise_vorbis(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_ogl_vorbis) {
|
||||
free_ogl_vorbis(vgmstream->codec_data);
|
||||
if (vgmstream->coding_type==coding_VORBIS_custom) {
|
||||
free_vorbis_custom(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
#endif
|
||||
@ -714,8 +697,10 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->layout_type==layout_fake_mpeg ||
|
||||
vgmstream->layout_type==layout_mpeg) {
|
||||
if (vgmstream->coding_type==coding_MPEG_custom ||
|
||||
vgmstream->coding_type==coding_MPEG_layer1 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer2 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer3) {
|
||||
free_mpeg(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
@ -911,8 +896,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
||||
case layout_ogg_vorbis:
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case layout_fake_mpeg:
|
||||
case layout_mpeg:
|
||||
case layout_mpeg_custom:
|
||||
#endif
|
||||
case layout_none:
|
||||
render_vgmstream_nolayout(buffer,sample_count,vgmstream);
|
||||
@ -991,21 +975,13 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_ULAW:
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case coding_ogg_vorbis:
|
||||
case coding_fsb_vorbis:
|
||||
case coding_wwise_vorbis:
|
||||
case coding_ogl_vorbis:
|
||||
case coding_VORBIS_custom:
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_fake_MPEG2_L2:
|
||||
case coding_MPEG1_L1:
|
||||
case coding_MPEG1_L2:
|
||||
case coding_MPEG1_L3:
|
||||
case coding_MPEG2_L1:
|
||||
case coding_MPEG2_L2:
|
||||
case coding_MPEG2_L3:
|
||||
case coding_MPEG25_L1:
|
||||
case coding_MPEG25_L2:
|
||||
case coding_MPEG25_L3:
|
||||
case coding_MPEG_custom:
|
||||
case coding_MPEG_layer1:
|
||||
case coding_MPEG_layer2:
|
||||
case coding_MPEG_layer3:
|
||||
#endif
|
||||
case coding_SDX2:
|
||||
case coding_SDX2_int:
|
||||
@ -1526,20 +1502,8 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
vgmstream->channels);
|
||||
break;
|
||||
|
||||
case coding_fsb_vorbis:
|
||||
decode_fsb_vorbis(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
|
||||
case coding_wwise_vorbis:
|
||||
decode_wwise_vorbis(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
|
||||
case coding_ogl_vorbis:
|
||||
decode_ogl_vorbis(vgmstream,
|
||||
case coding_VORBIS_custom:
|
||||
decode_vorbis_custom(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
@ -1667,21 +1631,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_fake_MPEG2_L2:
|
||||
decode_fake_mpeg2_l2(
|
||||
&vgmstream->ch[0],
|
||||
vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do);
|
||||
break;
|
||||
case coding_MPEG1_L1:
|
||||
case coding_MPEG1_L2:
|
||||
case coding_MPEG1_L3:
|
||||
case coding_MPEG2_L1:
|
||||
case coding_MPEG2_L2:
|
||||
case coding_MPEG2_L3:
|
||||
case coding_MPEG25_L1:
|
||||
case coding_MPEG25_L2:
|
||||
case coding_MPEG25_L3:
|
||||
case coding_MPEG_custom:
|
||||
case coding_MPEG_layer1:
|
||||
case coding_MPEG_layer2:
|
||||
case coding_MPEG_layer3:
|
||||
decode_mpeg(
|
||||
vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,
|
||||
@ -1889,16 +1842,8 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
seek_ogg_vorbis(vgmstream, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
||||
seek_fsb_vorbis(vgmstream, vgmstream->loop_start_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||
seek_wwise_vorbis(vgmstream, vgmstream->loop_start_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type==coding_ogl_vorbis) {
|
||||
seek_ogl_vorbis(vgmstream, vgmstream->loop_start_sample);
|
||||
if (vgmstream->coding_type==coding_VORBIS_custom) {
|
||||
seek_vorbis_custom(vgmstream, vgmstream->loop_start_sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1921,8 +1866,11 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->layout_type==layout_mpeg) {
|
||||
seek_mpeg(vgmstream, vgmstream->loop_sample); /* won't work for fake MPEG */
|
||||
if (vgmstream->coding_type==coding_MPEG_custom ||
|
||||
vgmstream->coding_type==coding_MPEG_layer1 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer2 ||
|
||||
vgmstream->coding_type==coding_MPEG_layer3) {
|
||||
seek_mpeg(vgmstream, vgmstream->loop_sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
153
src/vgmstream.h
153
src/vgmstream.h
@ -158,24 +158,15 @@ typedef enum {
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */
|
||||
coding_fsb_vorbis, /* FMOD Vorbis without Ogg layer */
|
||||
coding_wwise_vorbis, /* Audiokinetic Vorbis without Ogg layer */
|
||||
coding_ogl_vorbis, /* Shin'en Vorbis without Ogg layer */
|
||||
coding_ogg_vorbis, /* Xiph Vorbis with Ogg layer (MDCT-based) */
|
||||
coding_VORBIS_custom, /* Xiph Vorbis with custom layer (MDCT-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
coding_fake_MPEG2_L2, /* MPEG-2 Layer 2 (AHX), with lying headers */
|
||||
/* MPEG audio variations (depending on sample rate and other config) */
|
||||
coding_MPEG1_L1, /* MP1 */
|
||||
coding_MPEG1_L2, /* MP2 */
|
||||
coding_MPEG1_L3, /* good ol' MPEG-1 Layer 3 (MP3) */
|
||||
coding_MPEG2_L1,
|
||||
coding_MPEG2_L2,
|
||||
coding_MPEG2_L3,
|
||||
coding_MPEG25_L1,
|
||||
coding_MPEG25_L2,
|
||||
coding_MPEG25_L3,
|
||||
coding_MPEG_custom, /* MPEG audio with custom features (MDCT-based) */
|
||||
coding_MPEG_layer1, /* MP1 MPEG audio (MDCT-based) */
|
||||
coding_MPEG_layer2, /* MP2 MPEG audio (MDCT-based) */
|
||||
coding_MPEG_layer3, /* MP3 MPEG audio (MDCT-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_G7221
|
||||
@ -251,8 +242,7 @@ typedef enum {
|
||||
layout_ogg_vorbis, /* ogg vorbis file */
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
layout_fake_mpeg, /* MPEG audio stream with bad frame headers (AHX) */
|
||||
layout_mpeg, /* proper MPEG audio stream */
|
||||
layout_mpeg_custom, /* usually straight data but may setup offset/interleave somehow */
|
||||
#endif
|
||||
} layout_t;
|
||||
|
||||
@ -494,8 +484,8 @@ typedef enum {
|
||||
meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */
|
||||
meta_DE2, /* Falcom (Gurumin) .de2 */
|
||||
meta_VS, /* Men in Black .vs */
|
||||
meta_FFXI_BGW, /* FFXI BGW */
|
||||
meta_FFXI_SPW, /* FFXI SPW */
|
||||
meta_FFXI_BGW, /* FFXI (PC) BGW */
|
||||
meta_FFXI_SPW, /* FFXI (PC) SPW */
|
||||
meta_STS_WII, /* Shikigami No Shiro 3 STS Audio File */
|
||||
meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */
|
||||
meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */
|
||||
@ -599,8 +589,8 @@ typedef enum {
|
||||
meta_FSTM, // Nintendo Wii U FSTM
|
||||
meta_3DS_IDSP, // Nintendo 3DS/Wii U IDSP
|
||||
meta_KT_WIIBGM, // Koei Tecmo WiiBGM
|
||||
meta_MCA, // Capcom MCA "MADP"
|
||||
meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX
|
||||
meta_MCA, /* Capcom MCA "MADP" */
|
||||
meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */
|
||||
meta_HCA, /* CRI HCA */
|
||||
meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */
|
||||
meta_PS2_VDS_VDM, /* Graffiti Kingdom */
|
||||
@ -623,6 +613,8 @@ typedef enum {
|
||||
meta_PC_XA30, /* Driver - Parallel Lines (PC) */
|
||||
meta_WII_04SW, /* Driver - Parallel Lines (Wii) */
|
||||
meta_TXTH, /* generic text header */
|
||||
meta_SK_AUD, /* Silicon Knights .AUD (Eternal Darkness GC) */
|
||||
meta_AHX, /* CRI AHX header */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
@ -633,9 +625,6 @@ typedef enum {
|
||||
meta_OGG_KOVS, /* Ogg Vorbis with exta header and 0x100 bytes XOR */
|
||||
meta_OGG_PSYCH, /* Ogg Vorbis with all bytes -0x23*/
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
meta_AHX, /* CRI AHX header (same structure as ADX) */
|
||||
#endif
|
||||
#ifdef VGM_USE_MP4V2
|
||||
meta_MP4, /* AAC (iOS) */
|
||||
#endif
|
||||
@ -799,12 +788,41 @@ typedef struct {
|
||||
ogg_vorbis_streamfile ov_streamfile;
|
||||
} ogg_vorbis_codec_data;
|
||||
|
||||
/* config for Wwise Vorbis */
|
||||
typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_type;
|
||||
typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_type;
|
||||
typedef enum { STANDARD, MODIFIED } wwise_packet_type;
|
||||
|
||||
/* any raw Vorbis without Ogg layer */
|
||||
/* 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_custom_t;
|
||||
|
||||
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
|
||||
typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */
|
||||
typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */
|
||||
typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */
|
||||
|
||||
typedef struct {
|
||||
/* to reconstruct init packets */
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int blocksize_0_exp;
|
||||
int blocksize_1_exp;
|
||||
|
||||
uint32_t setup_id; /* external setup */
|
||||
int big_endian; /* flag */
|
||||
|
||||
/* Wwise Vorbis config */
|
||||
wwise_setup_t setup_type;
|
||||
wwise_header_t header_type;
|
||||
wwise_packet_t packet_type;
|
||||
|
||||
/* output (kinda ugly here but to simplify) */
|
||||
off_t data_start_offset;
|
||||
|
||||
} vorbis_custom_config;
|
||||
|
||||
/* custom Vorbis without Ogg layer */
|
||||
typedef struct {
|
||||
vorbis_info vi; /* stream settings */
|
||||
vorbis_comment vc; /* stream comments */
|
||||
@ -817,52 +835,75 @@ typedef struct {
|
||||
size_t samples_to_discard; /* for looping purposes */
|
||||
int samples_full; /* flag, samples available in vorbis buffers */
|
||||
|
||||
/* Wwise Vorbis config */
|
||||
wwise_setup_type setup_type;
|
||||
wwise_header_type header_type;
|
||||
wwise_packet_type packet_type;
|
||||
/* saved data to reconstruct modified packets */
|
||||
vorbis_custom_t type; /* Vorbis subtype */
|
||||
vorbis_custom_config config; /* config depending on the mode */
|
||||
|
||||
/* Wwise Vorbis: saved data to reconstruct modified packets */
|
||||
uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */
|
||||
int mode_bits; /* bits to store mode_number */
|
||||
uint8_t prev_blockflag; /* blockflag in the last decoded packet */
|
||||
/* Ogg-style Vorbis: packet within a page */
|
||||
int current_packet;
|
||||
|
||||
} vorbis_codec_data;
|
||||
} vorbis_custom_codec_data;
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
typedef enum { /*MPEG_NONE,*/ MPEG_FIXED, MPEG_FSB, MPEG_P3D, MPEG_EA } mpeg_interleave_type;
|
||||
|
||||
/* Custom MPEG modes, mostly differing in the data layout */
|
||||
typedef enum {
|
||||
MPEG_STANDARD, /* 1 stream */
|
||||
MPEG_AHX, /* 1 stream with false frame headers */
|
||||
MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */
|
||||
MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */
|
||||
MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */
|
||||
MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */
|
||||
MPEG_EAL31, /* EALayer3 v1, custom frames */
|
||||
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), altered custom frames */
|
||||
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), altered custom frames */
|
||||
MPEG_LYN, /* N streams of fixed interleave */
|
||||
MPEG_AWC /* N streams in absolute offsets (consecutive) */
|
||||
} mpeg_custom_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer; /* raw (coded) data buffer */
|
||||
int channels; /* max channels */
|
||||
int fsb_padding; /* fsb padding mode */
|
||||
int chunk_size; /* size of a data portion */
|
||||
int interleave; /* size of stream interleave */
|
||||
int encryption; /* encryption mode */
|
||||
} mpeg_custom_config;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer; /* internal 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; /* "base" MPEG stream */
|
||||
mpg123_handle *m; /* regular/single MPEG decoder */
|
||||
|
||||
/* base values, assumed to be constant in the file */
|
||||
int sample_rate_per_frame;
|
||||
/* for internal use, assumed to be constant for all frames */
|
||||
int channels_per_frame;
|
||||
size_t samples_per_frame;
|
||||
int samples_per_frame;
|
||||
|
||||
size_t samples_to_discard; /* for interleaved looping */
|
||||
/* custom MPEG internals */
|
||||
int custom; /* flag */
|
||||
mpeg_custom_t type; /* mpeg subtype */
|
||||
mpeg_custom_config config; /* config depending on the mode */
|
||||
|
||||
/* interleaved MPEG internals */
|
||||
int interleaved;
|
||||
mpeg_interleave_type interleave_type; /* flag */
|
||||
uint32_t interleave_value; /* varies with type */
|
||||
|
||||
mpg123_handle **ms; /* array of MPEG streams */
|
||||
mpg123_handle **ms; /* custom MPEG decoder array */
|
||||
size_t ms_size;
|
||||
uint8_t *frame_buffer; /* temp buffer with samples from a single decoded frame */
|
||||
size_t frame_buffer_size;
|
||||
uint8_t *interleave_buffer; /* intermediate buffer with samples from all channels */
|
||||
size_t interleave_buffer_size;
|
||||
size_t bytes_in_interleave_buffer;
|
||||
size_t bytes_used_in_interleave_buffer;
|
||||
|
||||
size_t current_frame_size;
|
||||
size_t current_padding; /* FSB padding between frames */
|
||||
uint8_t *stream_buffer; /* contains samples from N frames of one stream */
|
||||
size_t stream_buffer_size;
|
||||
uint8_t *sample_buffer; /* contains samples from N frames of all streams/channels */
|
||||
size_t sample_buffer_size;
|
||||
size_t bytes_in_sample_buffer;
|
||||
size_t bytes_used_in_sample_buffer;
|
||||
|
||||
|
||||
size_t samples_to_discard; /* for custom mpeg looping */
|
||||
size_t skip_samples; /* base encoder delay */
|
||||
|
||||
} mpeg_codec_data;
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user