Merge pull request #82 from bnnm/wwise-3

Wwise 3 - A Lizard and a Bat, What's Up With That?
This commit is contained in:
Christopher Snowhill 2017-04-13 17:24:57 -07:00 committed by GitHub
commit ebcf4ec8aa
21 changed files with 10780 additions and 104 deletions

View File

@ -27,7 +27,9 @@ CODING_OBJS=coding/adx_decoder.o \
coding/hca_decoder.o \
coding/ffmpeg_decoder.o \
coding/ffmpeg_decoder_utils.o \
coding/fsb_vorbis_decoder.o
coding/fsb_vorbis_decoder.o \
coding/wwise_vorbis_decoder.o \
coding/wwise_vorbis_utils.o
LAYOUT_OBJS=layout/ast_blocked.o \
layout/blocked.o \

View File

@ -32,5 +32,7 @@ libcoding_la_SOURCES += mtaf_decoder.c
libcoding_la_SOURCES += g719_decoder.c
libcoding_la_SOURCES += hca_decoder.c
libcoding_la_SOURCES += fsb_vorbis_decoder.c
libcoding_la_SOURCES += wwise_vorbis_decoder.c
libcoding_la_SOURCES += wwise_vorbis_utils.c
EXTRA_DIST = coding.h g72x_state.h clHCA.h

View File

@ -27,6 +27,8 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp
void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
/* ngc_dsp_decoder */
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -126,6 +128,15 @@ void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_t
void free_fsb_vorbis(vorbis_codec_data *data);
void reset_fsb_vorbis(VGMSTREAM *vgmstream);
void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
/* 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 free_wwise_vorbis(vorbis_codec_data *data);
void reset_wwise_vorbis(VGMSTREAM *vgmstream);
void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
#endif
/* mpeg_decoder */

View File

@ -411,7 +411,14 @@ void free_fsb_vorbis(vorbis_codec_data * data) {
}
void reset_fsb_vorbis(VGMSTREAM *vgmstream) {
seek_fsb_vorbis(vgmstream, 0);
#if FSB_VORBIS_ON
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;
#endif
}
void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
@ -422,7 +429,8 @@ void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
* 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;
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
#endif
}

View File

@ -604,3 +604,57 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
void decode_wwise_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave (configurable size), block-interleave multichannel (ex. if block is 0xD8 in 6ch: 6 blocks of 4+0x20)
int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels;
first_sample = first_sample % block_samples;
//block-interleaved header (1 header per channel block); can be LE or BE
if (first_sample == 0) {
int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
off_t header_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel;
hist1 = read_16bit(header_offset,stream->streamfile);
step_index = read_8bit(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
//must write history from header as last nibble/sample in block is almost always 0 / not encoded
outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing;
first_sample += 1;
samples_to_do -= 1;
}
for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */
off_t byte_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel + 4 + (i-1)/2;
int nibble_shift = ((i-1)&1?4:0); //low nibble first
//last nibble/sample in block is ignored (next header sample contains it)
if (i < block_samples) {
ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
sample_count+=channelspacing;
//todo atenuation: apparently from hcs's analysis Wwise IMA decodes nibbles slightly different, reducing dbs
}
}
//internal interleave: increment offset on complete frame
if (i == block_samples) stream->offset += vgmstream->interleave_block_size;
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
/* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels;
}

View File

@ -63,7 +63,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 mpp123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
VGM_LOG("MPEG: unable to set up mpg123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
goto fail; //todo handle MPG123_DONE?
}
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */
@ -172,7 +172,12 @@ mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t
/* 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;
//VGM_LOG("ch=%i, sr=%i, spf=%i, v=%i, l=%i\n", data->channels_per_frame, data->sample_rate_per_frame, data->samples_per_frame, info.version, info.layer);
//mpg123_handle *main_m = data->m;
//struct mpg123_frameinfo mi;
//mpg123_info(main_m,&mi);
//VGM_LOG("mi.framesize=%x\n", mi.framesize);
/* unlikely, can fixed with bigger buffer or a feed loop */
if (info.frame_size > data->buffer_size)
goto fail;
@ -406,12 +411,16 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
/* decode samples from 1 full frame */
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);
/* 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);
@ -423,6 +432,7 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
/* read more raw data (only 1 frame, to check interleave block end) */
if (!data->buffer_full) {
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile);
//VGM_LOG("read raw: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset);
/* end of stream, fill frame buffer with 0s but continue normally with other streams */
if (!data->bytes_in_buffer) {
@ -438,16 +448,37 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
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 */
}
/*
/// TODO: skip very first frame but add fake silence?
//todo skip only if first frame is fully decodable? (ie.- blank below)
//todo for multich files idem
if (first_frame) {
data->buffer_full = 0;
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;
break;
}
*/
}
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) {
//VGM_LOG("feed unused: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset);
//getchar();
rc = mpg123_decode(m,
data->buffer, data->bytes_in_buffer,
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
&bytes_done);
data->buffer_used = 1;
} else {
//VGM_LOG("feed used: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset);
//getchar();
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
@ -517,6 +548,9 @@ static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, of
data->current_padding = (data->current_frame_size % data->fsb_padding) ?
data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0;
}
//VGM_LOG("off=%lx, ch=%i, sr=%i, spf=%i, fs=%x, v=%i, l=%i\n", offset, info.channels, info.sample_rate, info.frame_samples, info.frame_size, info.version, info.layer);
//getchar();
}
return 1;
@ -651,7 +685,6 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset + input_offset;
}
data->bytes_in_interleave_buffer = 0;
@ -667,13 +700,15 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
/* seek multistream */
if (!data->interleaved) {
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
} else {
int i;
/* re-start from 0 */
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
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;
@ -795,6 +830,9 @@ static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_
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;
}
//int fs2 = (info->frame_samples / 8 * info->bit_rate * 1000l) / info->sample_rate + padding;
//VGM_LOG("fs=%x, fs2=%x, fsp=%i, br=%i\n", info->frame_size, fs2, info->frame_samples, info->bit_rate);
//getchar();
info->coding_type = coding_types[info->version-1][info->layer-1];

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,320 @@
#include "coding.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h>
#define WWISE_VORBIS_ON 1 //todo remove once battle-tested
#if WWISE_VORBIS_ON
#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);
#endif
/**
* 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) {
#if WWISE_VORBIS_ON
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;
if (read_streamfile(data->buffer,offset+header_size,packet_size, streamFile)!=packet_size) goto fail;
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;
if (read_streamfile(data->buffer,offset+header_size,packet_size, streamFile)!=packet_size) goto fail;
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
offset += header_size + packet_size;
/* modified setup packet */ //todo doesn't seem needed, may be copied as -is
header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian);
data->op.bytes = wwise_vorbis_rebuild_setup(data->buffer, data->buffer_size, streamFile, offset, data, big_endian, channels);
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);
#endif
return NULL;
}
/**
* Decodes raw Wwise Vorbis
*/
void decode_wwise_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
#if WWISE_VORBIS_ON
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));
#endif
}
#if WWISE_VORBIS_ON
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;
}
#endif
/* *************************************** */
void free_wwise_vorbis(vorbis_codec_data * data) {
#if WWISE_VORBIS_ON
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);
#endif
}
void reset_wwise_vorbis(VGMSTREAM *vgmstream) {
#if WWISE_VORBIS_ON
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;
#endif
}
void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
#if WWISE_VORBIS_ON
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
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
#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

@ -420,6 +420,7 @@ static const coding_info coding_info_list[] = {
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
{coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"},
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
{coding_WS, "Westwood Studios ADPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA0, "NWA DPCM Level 0"},
@ -438,6 +439,7 @@ 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"},
#endif
#ifdef VGM_USE_MPEG
{coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"},

View File

@ -1366,6 +1366,14 @@
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"
>

View File

@ -421,6 +421,8 @@
<ClCompile Include="coding\SASSC_decoder.c" />
<ClCompile Include="coding\sdx2_decoder.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

@ -808,6 +808,12 @@
<ClCompile Include="coding\ws_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\wwise_vorbis_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\wwise_vorbis_utils.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\xa_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

View File

@ -362,6 +362,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
}
break;
case 0x4A554E4B: /* JUNK */
/* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice).
* To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. */
goto fail;
default:
/* ignorance is bliss */
break;
@ -687,6 +691,10 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
if (chunk_size != 4) break;
//fact_sample_count = read_32bitBE(current_chunk+8, streamFile);
break;
case 0x4A554E4B: /* JUNK */
/* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice).
* To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. */
goto fail;
default:
/* ignorance is bliss */
break;

View File

@ -12,6 +12,7 @@ typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9 } wwise_cod
typedef struct {
int big_endian;
size_t file_size;
int truncated;
/* chunks references */
off_t fmt_offset;
@ -46,8 +47,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* basic checks */
/* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july)
* .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360) are used only in older Wwise */
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg")) goto fail; /* .xma may be possible? */
* .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360), .xma (ex. Tron Evolution X360) are used in older Wwise */
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma")) goto fail;
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
(read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */
@ -87,11 +88,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) goto fail; /*"fmt "*/
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) goto fail; /*"data"*/
if (ww.data_size > ww.file_size) {
VGM_LOG("WWISE: bad data size (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size);
goto fail;
}
/* base fmt */
if (ww.fmt_size < 0x12) goto fail;
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
@ -140,8 +136,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
switch(ww.format) {
case 0x0001: ww.codec = PCM; break; /* older Wwise */
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
case 0x0011: ww.codec = IMA; break; /* older Wwise */
case 0x0069: ww.codec = IMA; break; /* older Wwise */
//case 0x0011: ww.codec = IMA; break; /* older Wwise (used?) */
case 0x0069: ww.codec = IMA; break; /* older Wwise (Spiderman Web of Shadows X360) */
case 0x0161: ww.codec = XWMA; break;
case 0x0162: ww.codec = XWMA; break;
case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */
@ -161,6 +157,17 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
ww.codec = DSP;
}
/* Some Wwise files (ex. Oddworld PSV, Bayonetta 2 WiiU sfx) are truncated mirrors of another file.
* They come in RAM banks, probably to play the beginning while the rest of the real stream loads.
* We'll add basic support to avoid complaints of this or that .wem not playing */
if (ww.data_size > ww.file_size) {
//VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size);
if (ww.codec == IMA || ww.codec == VORBIS) /* only seen in those, probably other exist */
ww.truncated = 1;
else
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag);
@ -186,83 +193,167 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
break;
case IMA: /* common */
/* slightly modified MS-IMA.
* Original research by hcs in ima_rejigger (https://github.com/hcs64/vgm_ripping/tree/master/demux/ima_rejigger5) */
#if 0
/* slightly modified MS IMA with interleaved sub-blocks and LE/BE header */
/* Wwise uses common codecs (ex. 0x0002 MSADPCM) so this parser should go AFTER riff.c avoid misdetection */
if (ww.bits_per_sample != 4) goto fail;
vgmstream->coding_type = coding_WWISE_IMA;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = ww.block_align;
vgmstream->codec_endian = ww.big_endian;
vgmstream->num_samples = (ww.data_size / ww.block_align) * (ww.block_align - 4 * vgmstream->channels) * 2 /vgmstream->channels;//todo ms_ima_bytes_to_samples
if (ww.truncated) /* enough to get real samples */
ww.data_size = ww.file_size - ww.data_offset;
vgmstream->num_samples = ms_ima_bytes_to_samples(ww.data_size, ww.block_align, ww.channels);
break;
#endif
VGM_LOG("WWISE: IMA found (unsupported)\n");
goto fail;
#ifdef VGM_USE_VORBIS
case VORBIS: { /* common */
/* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder).
* Original research by hcs in ww2ogg (https://github.com/hcs64/ww2ogg) */
#if 0
off_t vorb_offset;
size_t vorb_size;
/* 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;
int packet_header_type = 0; /* 1 = size 8 (4-granule + 4-size), 2 = size 6 (4-granule + 2-size) or 3 = size 2 (2-size) */
int packet_type = 0; /* 1 = standard, 2 = modified vorbis packets */
int setup_type = 0; /* 1: triad, 2 = inline codebooks, 3 = external codebooks, 4 = external aoTuV codebooks */
int blocksize_0_pow = 0, blocksize_1_pow = 0;
wwise_setup_type setup_type;
wwise_header_type header_type;
wwise_packet_type packet_type;
int blocksize_0_exp = 0, blocksize_1_exp = 0;
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail;
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */
/* autodetect format */
/* autodetect format (field are mostly common, see the end of the file) */
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/
/* older Wwise (~2011) */
switch (vorb_size) {
case 0x28:
case 0x2A:
case 0x2C:
case 0x32:
case 0x34:
default: goto fail;
/* older Wwise (~<2012) */
switch(vorb_size) {
#if 0
case 0x28: /* early (~2009), some Divinity II files? */
case 0x2C: /* early (~2009), some EVE Online Apocrypha files? */
data_offsets = 0x18;
block_offsets = 0; /* no need, full headers are present */
header_type = TYPE_8;
packet_type = STANDARD;
setup_type = HEADER_TRIAD;
break;
case 0x32: /* ? */
#endif
case 0x34: /* common (2010~2011) */
data_offsets = 0x18;
block_offsets = 0x30;
header_type = TYPE_6;
packet_type = STANDARD;
/* setup_type: autodetect later */
break;
case 0x2a: /* infamous 2 (mid 2011) */
data_offsets = 0x10;
block_offsets = 0x28;
header_type = TYPE_2;
packet_type = MODIFIED;
setup_type = EXTERNAL_CODEBOOKS;
break;
default:
VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size);
goto fail;
}
vgmstream->num_samples = read_32bit(vorb_offset + 0x00, 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 */
}
/* detect setup type:
* - full inline: ~2009, ex. The King of Fighters XII X360, The Saboteur PC
* - trimmed inline: ~2010, ex. Army of Two: 40 days X360 (some multiplayer files)
* - external: ~2010, ex. Assassin's Creed Brotherhood X360, Dead Nation X360 */
if (vorb_size == 0x34) {
size_t setup_size = (uint16_t)read_16bit(start_offset + setup_offset, streamFile);
uint32_t id = (uint32_t)read_32bitBE(start_offset + setup_offset + 0x06, 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;
}
/* 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;
}
else {
setup_type = EXTERNAL_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);
if (!vgmstream->codec_data) goto fail;
}
else {
/* newer Wwise (~2012+) */
if (ww.extra_size != 0x30) goto fail; //todo some 0x2a exist
/* newer Wwise (>2012) */
off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */
int is_wem = check_extensions(streamFile,"wem");
packet_header_type = 3; /* size 2 */
packet_type = 1; //todo mod packets false on certain configs
setup_type = 4; /* aoTuV */
switch(ww.extra_size) {
case 0x30:
data_offsets = 0x10;
block_offsets = 0x28;
header_type = TYPE_2;
packet_type = MODIFIED;
//packet_type = STANDARD;
/* 0x12 (2): unk (00,10,18) not related to seek table*/
/* 0x14 (4): channel config */
vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, streamFile);
/* 0x20 (4): config, 0xcb/0xd9/etc */
/* 0x24 (4): ? samples? */
/* 0x28 (4): seek table size (setup packet offset within data) */
/* 0x2c (4): setup size (first audio packet offset within data) */
/* 0x30 (2): ? */
/* 0x32 (2): ? */
/* 0x34 (4): ? */
/* 0x38 (4): ? */
/* 0x3c (4): uid */ //todo same as external crc32?
blocksize_0_pow = read_8bit(ww.fmt_offset + 0x40, streamFile);
blocksize_1_pow = read_8bit(ww.fmt_offset + 0x41, streamFile);
/* 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
* - 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 */
break;
default:
//todo apparently some 0x2a + non mod_packets exist
//todo TYPE_06 possibly detectable by checking setup's granule (should be 0)
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
goto fail;
}
goto fail;
vgmstream->num_samples = read_32bit(extra_offset + 0x00, streamFile);
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 */
/* 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;
}
/* 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);
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);
if (!vgmstream->codec_data) goto fail;
}
}
vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset, vgmstream->channels, vgmstream->sample_rate);//pass endianness too
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_wwise_vorbis;
vgmstream->layout_type = layout_none;
vgmstream->codec_endian = ww.big_endian;
start_offset = start_offset + audio_offset;
/* Vorbis is VBR so this is very approximate, meh */
if (ww.truncated)
vgmstream->num_samples = 500;// vgmstream->num_samples * (ww.file_size - start_offset) / ww.data_size;
break;
#endif
VGM_LOG("WWISE: VORBIS found (unsupported)\n");
goto fail;
}
#endif
@ -324,8 +415,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
vgmstream->num_samples = ww.num_samples; /* set while parsing XMAWAVEFORMATs */
/* "XMAc": rare Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) */
VGM_ASSERT(find_chunk(streamFile, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n");
/* "XMAc": rare Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data)
* Can appear even in the file doesn't loop, maybe it's meant to be the playable physical region */
//VGM_ASSERT(find_chunk(streamFile, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n");
/* other chunks: "seek", regular XMA2 seek table */
break;
@ -415,3 +507,68 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* VORBIS FORMAT RESEARCH */
/*
- old format
"fmt" size 0x28, extra size 0x16 / size 0x18, extra size 0x06
0x12 (2): flag? (00,10,18): not related to seek table, codebook type, chunk count, looping
0x14 (4): channel config
0x18-24 (16): ? (fixed: 0x01000000 00001000 800000AA 00389B71) [removed when extra size is 0x06]
"vorb" size 0x34
0x00 (4): num_samples
0x04 (4): skip samples?
0x08 (4): ? (small if loop, 0 otherwise)
0x0c (4): loop offset after seek table+setup (offset after setup if file doesn't loop)
0x10 (4): ? (small, 0..~0x400)
0x14 (4): approximate data size without seek table? (almost setup+packets)
0x18 (4): setup_offset within data (0 = no seek table)
0x1c (4): audio_offset within data
0x20 (4): biggest packet size (not including header)?
0x24 (4): ? (mid, 0~0x5000)
0x28 (4): ? (mid, 0~0x5000)
0x2c (4): parent bank/event id? (shared by several .wem a game, but not all need to share it)
0x30 (1): blocksize_1_exp (small)
0x31 (1): blocksize_0_exp (large)
0x32 (2): empty
"vorb" size 0x2a
0x00 (4): num_samples
0x04 (4): loop offset after seek table+setup (offset after setup if file doesn't loop)
0x08 (4): data size without seek table (setup+packets)
0x0c (2): ? (small, 0..~0x400)
0x10 (4): setup_offset within data (0 = no seek table)
0x14 (4): audio_offset within data
0x18 (2): ? (small, 0..~0x400)
0x1a (2): ? (small, N..~0x100)
0x1c (4): ? (mid, 0~0x5000)
0x20 (4): ? (mid, 0~0x5000)
0x24 (4): parent bank/event id? (shared by several .wem a game, but not all need to share it)
0x28 (1): blocksize_1_exp (small)
0x29 (1): blocksize_0_exp (large)
- new format:
"fmt" size 0x42, extra size 0x30
0x12 (2): flag? (00,10,18): not related to seek table, codebook type, chunk count, looping, etc
0x14 (4): channel config
0x18 (4): num_samples
0x1c (4): loop offset after seek table+setup (offset after setup if file doesn't loop)
0x20 (4): data size without seek table (setup+packets)
0x24 (2): ?1 (small, 0..~0x400)
0x26 (2): ?2 (small, N..~0x100): not related to seek table, codebook type, chunk count, looping, packet size, samples, etc
0x28 (4): setup offset within data (0 = no seek table)
0x2c (4): audio offset within data
0x30 (2): biggest packet size (not including header)
0x32 (2): ?4 (small, 0..~0x100): may be same than ?2 (something related to the avg bitrate?)
0x34 (4): bitrate config? (mid, 0~0x5000)
0x38 (4): bitrate config? (mid, 0~0x5000) (2 byte with max/min?)
0x40 (1): blocksize_1_exp (small)
0x41 (1): blocksize_0_exp (large)
Wwise encoder options, unknown fields above may be reflect these:
https://www.audiokinetic.com/library/edge/?source=Help&id=vorbis_encoder_parameters
*/

View File

@ -134,7 +134,6 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
int big_endian = 0;
enum {
id_RIFF = UINT32_C(0x52494646), /* "RIFF" */
id_RIFX = UINT32_C(0x52494658), /* "RIFX" */
id_NXMA = UINT32_C(0x786D6100), /* "xma\0" */
id_PASX = UINT32_C(0x50415358), /* "PASX" */
};
@ -145,7 +144,6 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
switch (id) {
case id_RIFF:
break;
case id_RIFX:
case id_NXMA:
case id_PASX:
big_endian = 1;
@ -168,7 +166,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
xma->file_size = streamFile->get_size(streamFile);
/* find offsets */
if (id == id_RIFF || id == id_RIFX) { /* regular RIFF header / RIFX (BE, wwsize?) */
if (id == id_RIFF) { /* regular RIFF header */
off_t current_chunk = 0xc;
off_t fmt_offset = 0, xma2_offset = 0;
size_t riff_size = 0, fmt_size = 0, xma2_size = 0;

View File

@ -3,7 +3,7 @@
#include "streamtypes.h"
int check_sample_rate(int32_t sr) {
return !(sr<1000 || sr>96000);
return !(sr<300 || sr>96000);
}
const char * filename_extension(const char * filename) {

View File

@ -479,6 +479,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
if (vgmstream->coding_type==coding_fsb_vorbis) {
reset_fsb_vorbis(vgmstream);
}
if (vgmstream->coding_type==coding_wwise_vorbis) {
reset_wwise_vorbis(vgmstream);
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
hca_codec_data *data = vgmstream->codec_data;
@ -682,6 +686,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
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;
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
@ -1003,6 +1012,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_VORBIS
case coding_ogg_vorbis:
case coding_fsb_vorbis:
case coding_wwise_vorbis:
#endif
#ifdef VGM_USE_MPEG
case coding_fake_MPEG2_L2:
@ -1073,6 +1083,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 64;
case coding_MS_IMA:
case coding_RAD_IMA:
case coding_WWISE_IMA:
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
case coding_RAD_IMA_mono:
return 32;
@ -1162,6 +1173,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_RAD_IMA:
case coding_NDS_IMA:
case coding_DAT4_IMA:
case coding_WWISE_IMA:
return vgmstream->interleave_block_size;
case coding_RAD_IMA_mono:
return 0x14;
@ -1487,6 +1499,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
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;
#endif
case coding_CRI_HCA:
decode_hca(vgmstream->codec_data,
@ -1587,6 +1605,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_WWISE_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_wwise_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}
break;
case coding_WS:
for (chan=0;chan<vgmstream->channels;chan++) {
@ -1801,6 +1826,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
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);
}
#endif
#ifdef VGM_USE_FFMPEG

View File

@ -121,9 +121,10 @@ typedef enum {
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */
coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_MSADPCM, /* Microsoft ADPCM */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_AICA, /* Yamaha AICA ADPCM */
coding_L5_555, /* Level-5 0x555 ADPCM */
coding_SASSC, /* Activision EXAKT SASSC DPCM */
@ -150,6 +151,7 @@ typedef enum {
#ifdef VGM_USE_VORBIS
coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */
coding_fsb_vorbis, /* FMOD's Vorbis without Ogg layer */
coding_wwise_vorbis, /* Audiokinetic's Vorbis without Ogg layer */
#endif
#ifdef VGM_USE_MPEG
@ -721,6 +723,8 @@ typedef struct {
off_t loop_next_block_offset; /* saved from next_block_offset */
/* decoder specific */
int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */
uint8_t xa_channel; /* XA ADPCM: selected channel */
int32_t xa_sector_length; /* XA ADPCM: XA block */
uint8_t xa_headerless; /* XA ADPCM: headerless XA block */
@ -768,8 +772,13 @@ 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 */
typedef struct {
typedef struct {
vorbis_info vi; /* stream settings */
vorbis_comment vc; /* stream comments */
vorbis_dsp_state vd; /* decoder global state */
@ -780,6 +789,16 @@ typedef struct {
size_t buffer_size;
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 */
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 */
} vorbis_codec_data;
#endif

View File

@ -1,8 +1,6 @@
/*
** vgmstream for XMPlay
**
** 11/11/2009 - started. this is hilariously buggy and doesnt support much yet. [unknownfile]
*/
/**
* vgmstream for XMPlay
*/
#include <windows.h>
#include <windowsx.h>
@ -156,7 +154,7 @@ int32_t totalFrames, framesDone, framesLength, framesFade;
#define APP_NAME "vgmstream plugin"
#define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__
void __stdcall XMPAbout(HWND hwParent) {
void __stdcall XMP_About(HWND hwParent) {
MessageBox(hwParent,
PLUGIN_DESCRIPTION "\n"
"by hcs, FastElbja, manakoAT, bxaimc, kode54, and PSXGamerPro1\n\n"
@ -164,8 +162,9 @@ void __stdcall XMPAbout(HWND hwParent) {
,"about xmp-vgmstream",MB_OK);
}
void __stdcall Stop() {
void __stdcall XMP_Close() {
close_vgmstream(vgmstream);
vgmstream = NULL;
}
BOOL __stdcall XMP_CheckFile(const char *filename, XMPFILE file) {
@ -200,7 +199,7 @@ DWORD __stdcall XMP_GetFileInfo(const char *filename, XMPFILE file, float **leng
return 1;
}
DWORD __stdcall LoadVgmStream(const char *filename, XMPFILE file) {
DWORD __stdcall XMP_Open(const char *filename, XMPFILE file) {
if (file) vgmstream = init_vgmstream_from_xmpfile(file, filename);
else vgmstream = init_vgmstream(filename);
@ -220,7 +219,7 @@ DWORD __stdcall LoadVgmStream(const char *filename, XMPFILE file) {
return 1;
}
DWORD __stdcall XMP_Buffer(float* buffer, DWORD bufsize) {
DWORD __stdcall XMP_Process(float* buffer, DWORD bufsize) {
INT16 buf[1024];
UINT32 i, j, todo, done;
@ -323,45 +322,80 @@ double __stdcall XMP_SetPosition(DWORD pos) {
return cpos;
}
void __stdcall XMP_GetInfoText(char* txt, char* length) {
if (txt)
sprintf(txt,"vgmstream!");
/* main panel info text (short file info) */
void __stdcall XMP_GetInfoText(char* format, char* length) {
if (!format)
return;
sprintf(format,"vgmstream");
/* length is the file time */
}
void __stdcall GetAdditionalFields(char* blerp) {
sprintf(blerp,"oh god how did this get here I am not good with computers\n");
/* info for the "General" window/tab (buf is ~40K) */
void __stdcall XMP_GetGeneralInfo(char* buf) {
int i;
char description[1024];
if (!buf)
return;
if (!vgmstream)
return;
description[0] = '\0';
describe_vgmstream(vgmstream,description,sizeof(description));
/* tags are divided with a tab and lines with carriage return so we'll do some guetto fixin' */
for (i = 0; i < 1024; i++) {
int tag_done = 0;
if (description[i] == '\0')
break;
if (description[i] == ':' && !tag_done) { /* to ignore multiple ':' in a line*/
description[i] = ' ';
description[i+1] = '\t';
tag_done = 1;
}
if (description[i] == '\n') {
description[i] = '\r';
tag_done = 0;
}
}
sprintf(buf,"vgmstream\t\r%s\r", description);
}
/* XMPlay extension list, only needed to associate extensions in Windows */
/* todo: as of v3.8.2.17, any more than 1024 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
#define EXTENSION_LIST_SIZE 1024 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
static int add_extension(int length, char * dst, const char * src);
static void build_extension_list();
/* XMPlay extension list, only needed to associate extensions in Windows */
/* todo: as of v3.8.2.17, any more than ~1000 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
#define EXTENSION_LIST_SIZE 1000 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
/* plugin defs, see xmpin.h */
XMPIN vgmstream_intf = {
XMPIN_FLAG_CANSTREAM,
"vgmstream for XMPlay",
working_extension_list,
XMPAbout,
NULL,
XMP_About,
NULL,//XMP_Config
XMP_CheckFile,
XMP_GetFileInfo,
LoadVgmStream,
Stop,
XMP_Open,
XMP_Close,
NULL,
XMP_SetFormat,
XMP_GetTags, // Actually mandatory
XMP_GetTags, //(OPTIONAL) --actually mandatory
XMP_GetInfoText,
GetAdditionalFields,
NULL,
XMP_GetGeneralInfo,
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
XMP_SetPosition,
XMP_GetGranularity,
NULL,
XMP_Buffer,
XMP_Process,
NULL,
NULL,
NULL,