Merge pull request #114 from bnnm/custom-vorbis-mpeg

Custom Vorbis/MPEG
This commit is contained in:
Christopher Snowhill 2017-07-29 18:15:26 -07:00 committed by GitHub
commit f9e561e08a
37 changed files with 1837 additions and 1705 deletions

View File

@ -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);

View File

@ -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

View 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

View 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

View File

@ -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
View 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_ */

View File

@ -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).
*/

View File

@ -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.

View File

@ -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;

View 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_ */

View 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

View 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

View 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

View File

@ -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;

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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"
>

View File

@ -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" />

View File

@ -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">

View File

@ -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

View File

@ -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 */

View File

@ -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) */

View File

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

View File

@ -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;

View File

@ -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*/

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

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

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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