mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 16:30:54 +01:00
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:
commit
ebcf4ec8aa
@ -27,7 +27,9 @@ CODING_OBJS=coding/adx_decoder.o \
|
|||||||
coding/hca_decoder.o \
|
coding/hca_decoder.o \
|
||||||
coding/ffmpeg_decoder.o \
|
coding/ffmpeg_decoder.o \
|
||||||
coding/ffmpeg_decoder_utils.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_OBJS=layout/ast_blocked.o \
|
||||||
layout/blocked.o \
|
layout/blocked.o \
|
||||||
|
@ -32,5 +32,7 @@ libcoding_la_SOURCES += mtaf_decoder.c
|
|||||||
libcoding_la_SOURCES += g719_decoder.c
|
libcoding_la_SOURCES += g719_decoder.c
|
||||||
libcoding_la_SOURCES += hca_decoder.c
|
libcoding_la_SOURCES += hca_decoder.c
|
||||||
libcoding_la_SOURCES += fsb_vorbis_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
|
EXTRA_DIST = coding.h g72x_state.h clHCA.h
|
||||||
|
@ -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_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_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_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 */
|
/* ngc_dsp_decoder */
|
||||||
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
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 free_fsb_vorbis(vorbis_codec_data *data);
|
||||||
void reset_fsb_vorbis(VGMSTREAM *vgmstream);
|
void reset_fsb_vorbis(VGMSTREAM *vgmstream);
|
||||||
void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
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
|
#endif
|
||||||
|
|
||||||
/* mpeg_decoder */
|
/* mpeg_decoder */
|
||||||
|
@ -411,7 +411,14 @@ void free_fsb_vorbis(vorbis_codec_data * data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void reset_fsb_vorbis(VGMSTREAM *vgmstream) {
|
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) {
|
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 */
|
* To avoid having to parse different formats we'll just discard until the expected sample */
|
||||||
vorbis_synthesis_restart(&data->vd);
|
vorbis_synthesis_restart(&data->vd);
|
||||||
data->samples_to_discard = num_sample;
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,3 +604,57 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o
|
|||||||
stream->adpcm_history1_32 = hist1;
|
stream->adpcm_history1_32 = hist1;
|
||||||
stream->adpcm_step_index = step_index;
|
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;
|
||||||
|
}
|
||||||
|
@ -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);
|
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) {
|
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?
|
goto fail; //todo handle MPG123_DONE?
|
||||||
}
|
}
|
||||||
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */
|
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 */
|
/* get frame size from the first frame as it can be used externally to calc interleave */
|
||||||
if ( !update_frame_sizes(data, streamfile, start_offset) )
|
if ( !update_frame_sizes(data, streamfile, start_offset) )
|
||||||
goto fail;
|
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 */
|
/* unlikely, can fixed with bigger buffer or a feed loop */
|
||||||
if (info.frame_size > data->buffer_size)
|
if (info.frame_size > data->buffer_size)
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -406,12 +411,16 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
|
|||||||
/* decode samples from 1 full frame */
|
/* decode samples from 1 full frame */
|
||||||
do {
|
do {
|
||||||
int rc;
|
int rc;
|
||||||
|
//int first_frame = stream->offset == stream->channel_start_offset;
|
||||||
|
|
||||||
/* padded frame stuff */
|
/* padded frame stuff */
|
||||||
rc = update_frame_sizes(data, stream->streamfile, stream->offset);
|
rc = update_frame_sizes(data, stream->streamfile, stream->offset);
|
||||||
/* ignore any errors and continue; mpg123 will probably sync */
|
/* 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_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 */
|
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
|
||||||
if (stream->offset > stream_size) {
|
if (stream->offset > stream_size) {
|
||||||
memset(data->frame_buffer, 0, data->frame_buffer_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) */
|
/* read more raw data (only 1 frame, to check interleave block end) */
|
||||||
if (!data->buffer_full) {
|
if (!data->buffer_full) {
|
||||||
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile);
|
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 */
|
/* end of stream, fill frame buffer with 0s but continue normally with other streams */
|
||||||
if (!data->bytes_in_buffer) {
|
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)) {
|
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 */
|
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 */
|
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
||||||
if (!data->buffer_used) {
|
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,
|
rc = mpg123_decode(m,
|
||||||
data->buffer, data->bytes_in_buffer,
|
data->buffer, data->bytes_in_buffer,
|
||||||
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
|
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
|
||||||
&bytes_done);
|
&bytes_done);
|
||||||
data->buffer_used = 1;
|
data->buffer_used = 1;
|
||||||
} else {
|
} 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,
|
rc = mpg123_decode(m,
|
||||||
NULL,0,
|
NULL,0,
|
||||||
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
|
(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->current_padding = (data->current_frame_size % data->fsb_padding) ?
|
||||||
data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0;
|
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;
|
return 1;
|
||||||
@ -651,7 +685,6 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
|
|||||||
int i;
|
int i;
|
||||||
for (i=0; i < data->ms_size; i++) {
|
for (i=0; i < data->ms_size; i++) {
|
||||||
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
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;
|
data->bytes_in_interleave_buffer = 0;
|
||||||
@ -667,13 +700,15 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
|||||||
/* seek multistream */
|
/* seek multistream */
|
||||||
if (!data->interleaved) {
|
if (!data->interleaved) {
|
||||||
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
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 {
|
} else {
|
||||||
int i;
|
int i;
|
||||||
/* re-start from 0 */
|
/* re-start from 0 */
|
||||||
for (i=0; i < data->ms_size; i++) {
|
for (i=0; i < data->ms_size; i++) {
|
||||||
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
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 */
|
/* manually add skip samples, since we don't really know the correct offset */
|
||||||
data->samples_to_discard = num_sample;
|
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 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;
|
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];
|
info->coding_type = coding_types[info->version-1][info->layer-1];
|
||||||
|
|
||||||
|
8747
src/coding/wwise_vorbis_data.h
Normal file
8747
src/coding/wwise_vorbis_data.h
Normal file
File diff suppressed because it is too large
Load Diff
320
src/coding/wwise_vorbis_decoder.c
Normal file
320
src/coding/wwise_vorbis_decoder.c
Normal 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
|
1220
src/coding/wwise_vorbis_utils.c
Normal file
1220
src/coding/wwise_vorbis_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
11
src/coding/wwise_vorbis_utils.h
Normal file
11
src/coding/wwise_vorbis_utils.h
Normal 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_ */
|
@ -420,6 +420,7 @@ static const coding_info coding_info_list[] = {
|
|||||||
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
|
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
|
||||||
{coding_OTNS_IMA, "Omikron: The Nomad Soul 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_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"},
|
||||||
|
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
|
||||||
{coding_WS, "Westwood Studios ADPCM"},
|
{coding_WS, "Westwood Studios ADPCM"},
|
||||||
{coding_ACM, "InterPlay ACM"},
|
{coding_ACM, "InterPlay ACM"},
|
||||||
{coding_NWA0, "NWA DPCM Level 0"},
|
{coding_NWA0, "NWA DPCM Level 0"},
|
||||||
@ -438,6 +439,7 @@ static const coding_info coding_info_list[] = {
|
|||||||
#ifdef VGM_USE_VORBIS
|
#ifdef VGM_USE_VORBIS
|
||||||
{coding_ogg_vorbis, "Ogg Vorbis"},
|
{coding_ogg_vorbis, "Ogg Vorbis"},
|
||||||
{coding_fsb_vorbis, "FSB Vorbis"},
|
{coding_fsb_vorbis, "FSB Vorbis"},
|
||||||
|
{coding_wwise_vorbis, "Wwise Vorbis"},
|
||||||
#endif
|
#endif
|
||||||
#ifdef VGM_USE_MPEG
|
#ifdef VGM_USE_MPEG
|
||||||
{coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"},
|
{coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"},
|
||||||
|
@ -1366,6 +1366,14 @@
|
|||||||
RelativePath=".\coding\sdx2_decoder.c"
|
RelativePath=".\coding\sdx2_decoder.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\coding\wwise_vorbis_decoder.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\coding\wwise_vorbis_utils.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\coding\ws_decoder.c"
|
RelativePath=".\coding\ws_decoder.c"
|
||||||
>
|
>
|
||||||
|
@ -421,6 +421,8 @@
|
|||||||
<ClCompile Include="coding\SASSC_decoder.c" />
|
<ClCompile Include="coding\SASSC_decoder.c" />
|
||||||
<ClCompile Include="coding\sdx2_decoder.c" />
|
<ClCompile Include="coding\sdx2_decoder.c" />
|
||||||
<ClCompile Include="coding\ws_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="coding\xa_decoder.c" />
|
||||||
<ClCompile Include="layout\aax_layout.c" />
|
<ClCompile Include="layout\aax_layout.c" />
|
||||||
<ClCompile Include="layout\aix_layout.c" />
|
<ClCompile Include="layout\aix_layout.c" />
|
||||||
|
@ -808,6 +808,12 @@
|
|||||||
<ClCompile Include="coding\ws_decoder.c">
|
<ClCompile Include="coding\ws_decoder.c">
|
||||||
<Filter>coding\Source Files</Filter>
|
<Filter>coding\Source Files</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="coding\xa_decoder.c">
|
||||||
<Filter>coding\Source Files</Filter>
|
<Filter>coding\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -362,6 +362,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
/* ignorance is bliss */
|
/* ignorance is bliss */
|
||||||
break;
|
break;
|
||||||
@ -687,6 +691,10 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
|
|||||||
if (chunk_size != 4) break;
|
if (chunk_size != 4) break;
|
||||||
//fact_sample_count = read_32bitBE(current_chunk+8, streamFile);
|
//fact_sample_count = read_32bitBE(current_chunk+8, streamFile);
|
||||||
break;
|
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:
|
default:
|
||||||
/* ignorance is bliss */
|
/* ignorance is bliss */
|
||||||
break;
|
break;
|
||||||
|
285
src/meta/wwise.c
285
src/meta/wwise.c
@ -12,6 +12,7 @@ typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9 } wwise_cod
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int big_endian;
|
int big_endian;
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
|
int truncated;
|
||||||
|
|
||||||
/* chunks references */
|
/* chunks references */
|
||||||
off_t fmt_offset;
|
off_t fmt_offset;
|
||||||
@ -46,8 +47,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
/* basic checks */
|
/* basic checks */
|
||||||
/* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july)
|
/* .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 */
|
* .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")) goto fail; /* .xma may be possible? */
|
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma")) goto fail;
|
||||||
|
|
||||||
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
|
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
|
||||||
(read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */
|
(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, 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 (!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 */
|
/* base fmt */
|
||||||
if (ww.fmt_size < 0x12) goto fail;
|
if (ww.fmt_size < 0x12) goto fail;
|
||||||
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
||||||
@ -140,8 +136,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
switch(ww.format) {
|
switch(ww.format) {
|
||||||
case 0x0001: ww.codec = PCM; break; /* older Wwise */
|
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 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
||||||
case 0x0011: ww.codec = IMA; break; /* older Wwise */
|
//case 0x0011: ww.codec = IMA; break; /* older Wwise (used?) */
|
||||||
case 0x0069: ww.codec = IMA; break; /* older Wwise */
|
case 0x0069: ww.codec = IMA; break; /* older Wwise (Spiderman Web of Shadows X360) */
|
||||||
case 0x0161: ww.codec = XWMA; break;
|
case 0x0161: ww.codec = XWMA; break;
|
||||||
case 0x0162: 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 */
|
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;
|
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 */
|
/* build the VGMSTREAM */
|
||||||
vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag);
|
vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag);
|
||||||
@ -186,83 +193,167 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case IMA: /* common */
|
case IMA: /* common */
|
||||||
/* slightly modified MS-IMA.
|
/* slightly modified MS IMA with interleaved sub-blocks and LE/BE header */
|
||||||
* Original research by hcs in ima_rejigger (https://github.com/hcs64/vgm_ripping/tree/master/demux/ima_rejigger5) */
|
|
||||||
#if 0
|
/* 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;
|
if (ww.bits_per_sample != 4) goto fail;
|
||||||
vgmstream->coding_type = coding_WWISE_IMA;
|
vgmstream->coding_type = coding_WWISE_IMA;
|
||||||
vgmstream->layout_type = layout_none;
|
vgmstream->layout_type = layout_none;
|
||||||
vgmstream->interleave_block_size = ww.block_align;
|
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;
|
break;
|
||||||
#endif
|
|
||||||
VGM_LOG("WWISE: IMA found (unsupported)\n");
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
#ifdef VGM_USE_VORBIS
|
#ifdef VGM_USE_VORBIS
|
||||||
case VORBIS: { /* common */
|
case VORBIS: { /* common */
|
||||||
/* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder).
|
/* 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) */
|
off_t vorb_offset, data_offsets, block_offsets;
|
||||||
#if 0
|
size_t vorb_size, setup_offset, audio_offset;
|
||||||
off_t vorb_offset;
|
|
||||||
size_t vorb_size;
|
|
||||||
|
|
||||||
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) */
|
wwise_setup_type setup_type;
|
||||||
int packet_type = 0; /* 1 = standard, 2 = modified vorbis packets */
|
wwise_header_type header_type;
|
||||||
int setup_type = 0; /* 1: triad, 2 = inline codebooks, 3 = external codebooks, 4 = external aoTuV codebooks */
|
wwise_packet_type packet_type;
|
||||||
int blocksize_0_pow = 0, blocksize_1_pow = 0;
|
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"*/
|
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/
|
||||||
/* older Wwise (~2011) */
|
/* older Wwise (~<2012) */
|
||||||
switch (vorb_size) {
|
|
||||||
case 0x28:
|
switch(vorb_size) {
|
||||||
case 0x2A:
|
#if 0
|
||||||
case 0x2C:
|
case 0x28: /* early (~2009), some Divinity II files? */
|
||||||
case 0x32:
|
case 0x2C: /* early (~2009), some EVE Online Apocrypha files? */
|
||||||
case 0x34:
|
data_offsets = 0x18;
|
||||||
default: goto fail;
|
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 {
|
else {
|
||||||
/* newer Wwise (~2012+) */
|
/* newer Wwise (>2012) */
|
||||||
if (ww.extra_size != 0x30) goto fail; //todo some 0x2a exist
|
off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */
|
||||||
|
int is_wem = check_extensions(streamFile,"wem");
|
||||||
|
|
||||||
packet_header_type = 3; /* size 2 */
|
switch(ww.extra_size) {
|
||||||
packet_type = 1; //todo mod packets false on certain configs
|
case 0x30:
|
||||||
setup_type = 4; /* aoTuV */
|
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*/
|
/* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks
|
||||||
/* 0x14 (4): channel config */
|
* - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg
|
||||||
vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, streamFile);
|
* - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */
|
||||||
/* 0x20 (4): config, 0xcb/0xd9/etc */
|
setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||||
/* 0x24 (4): ? samples? */
|
break;
|
||||||
/* 0x28 (4): seek table size (setup packet offset within data) */
|
default:
|
||||||
/* 0x2c (4): setup size (first audio packet offset within data) */
|
//todo apparently some 0x2a + non mod_packets exist
|
||||||
/* 0x30 (2): ? */
|
//todo TYPE_06 possibly detectable by checking setup's granule (should be 0)
|
||||||
/* 0x32 (2): ? */
|
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
|
||||||
/* 0x34 (4): ? */
|
goto fail;
|
||||||
/* 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);
|
|
||||||
|
|
||||||
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->coding_type = coding_wwise_vorbis;
|
||||||
vgmstream->layout_type = layout_none;
|
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;
|
break;
|
||||||
#endif
|
|
||||||
VGM_LOG("WWISE: VORBIS found (unsupported)\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -324,8 +415,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
vgmstream->num_samples = ww.num_samples; /* set while parsing XMAWAVEFORMATs */
|
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) */
|
/* "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");
|
* 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 */
|
/* other chunks: "seek", regular XMA2 seek table */
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -415,3 +507,68 @@ fail:
|
|||||||
close_vgmstream(vgmstream);
|
close_vgmstream(vgmstream);
|
||||||
return NULL;
|
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
|
||||||
|
*/
|
||||||
|
@ -134,7 +134,6 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
|||||||
int big_endian = 0;
|
int big_endian = 0;
|
||||||
enum {
|
enum {
|
||||||
id_RIFF = UINT32_C(0x52494646), /* "RIFF" */
|
id_RIFF = UINT32_C(0x52494646), /* "RIFF" */
|
||||||
id_RIFX = UINT32_C(0x52494658), /* "RIFX" */
|
|
||||||
id_NXMA = UINT32_C(0x786D6100), /* "xma\0" */
|
id_NXMA = UINT32_C(0x786D6100), /* "xma\0" */
|
||||||
id_PASX = UINT32_C(0x50415358), /* "PASX" */
|
id_PASX = UINT32_C(0x50415358), /* "PASX" */
|
||||||
};
|
};
|
||||||
@ -145,7 +144,6 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
|||||||
switch (id) {
|
switch (id) {
|
||||||
case id_RIFF:
|
case id_RIFF:
|
||||||
break;
|
break;
|
||||||
case id_RIFX:
|
|
||||||
case id_NXMA:
|
case id_NXMA:
|
||||||
case id_PASX:
|
case id_PASX:
|
||||||
big_endian = 1;
|
big_endian = 1;
|
||||||
@ -168,7 +166,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
|||||||
xma->file_size = streamFile->get_size(streamFile);
|
xma->file_size = streamFile->get_size(streamFile);
|
||||||
|
|
||||||
/* find offsets */
|
/* 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 current_chunk = 0xc;
|
||||||
off_t fmt_offset = 0, xma2_offset = 0;
|
off_t fmt_offset = 0, xma2_offset = 0;
|
||||||
size_t riff_size = 0, fmt_size = 0, xma2_size = 0;
|
size_t riff_size = 0, fmt_size = 0, xma2_size = 0;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "streamtypes.h"
|
#include "streamtypes.h"
|
||||||
|
|
||||||
int check_sample_rate(int32_t sr) {
|
int check_sample_rate(int32_t sr) {
|
||||||
return !(sr<1000 || sr>96000);
|
return !(sr<300 || sr>96000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * filename_extension(const char * filename) {
|
const char * filename_extension(const char * filename) {
|
||||||
|
@ -479,6 +479,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
|||||||
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
||||||
reset_fsb_vorbis(vgmstream);
|
reset_fsb_vorbis(vgmstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||||
|
reset_wwise_vorbis(vgmstream);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (vgmstream->coding_type==coding_CRI_HCA) {
|
if (vgmstream->coding_type==coding_CRI_HCA) {
|
||||||
hca_codec_data *data = vgmstream->codec_data;
|
hca_codec_data *data = vgmstream->codec_data;
|
||||||
@ -682,6 +686,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||||||
free_fsb_vorbis(vgmstream->codec_data);
|
free_fsb_vorbis(vgmstream->codec_data);
|
||||||
vgmstream->codec_data = NULL;
|
vgmstream->codec_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||||
|
free_wwise_vorbis(vgmstream->codec_data);
|
||||||
|
vgmstream->codec_data = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (vgmstream->coding_type==coding_CRI_HCA) {
|
if (vgmstream->coding_type==coding_CRI_HCA) {
|
||||||
@ -1003,6 +1012,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||||||
#ifdef VGM_USE_VORBIS
|
#ifdef VGM_USE_VORBIS
|
||||||
case coding_ogg_vorbis:
|
case coding_ogg_vorbis:
|
||||||
case coding_fsb_vorbis:
|
case coding_fsb_vorbis:
|
||||||
|
case coding_wwise_vorbis:
|
||||||
#endif
|
#endif
|
||||||
#ifdef VGM_USE_MPEG
|
#ifdef VGM_USE_MPEG
|
||||||
case coding_fake_MPEG2_L2:
|
case coding_fake_MPEG2_L2:
|
||||||
@ -1073,6 +1083,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||||||
return 64;
|
return 64;
|
||||||
case coding_MS_IMA:
|
case coding_MS_IMA:
|
||||||
case coding_RAD_IMA:
|
case coding_RAD_IMA:
|
||||||
|
case coding_WWISE_IMA:
|
||||||
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
|
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
|
||||||
case coding_RAD_IMA_mono:
|
case coding_RAD_IMA_mono:
|
||||||
return 32;
|
return 32;
|
||||||
@ -1162,6 +1173,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||||||
case coding_RAD_IMA:
|
case coding_RAD_IMA:
|
||||||
case coding_NDS_IMA:
|
case coding_NDS_IMA:
|
||||||
case coding_DAT4_IMA:
|
case coding_DAT4_IMA:
|
||||||
|
case coding_WWISE_IMA:
|
||||||
return vgmstream->interleave_block_size;
|
return vgmstream->interleave_block_size;
|
||||||
case coding_RAD_IMA_mono:
|
case coding_RAD_IMA_mono:
|
||||||
return 0x14;
|
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,
|
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||||
vgmstream->channels);
|
vgmstream->channels);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case coding_wwise_vorbis:
|
||||||
|
decode_wwise_vorbis(vgmstream,
|
||||||
|
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||||
|
vgmstream->channels);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case coding_CRI_HCA:
|
case coding_CRI_HCA:
|
||||||
decode_hca(vgmstream->codec_data,
|
decode_hca(vgmstream->codec_data,
|
||||||
@ -1587,6 +1605,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||||||
samples_to_do,chan);
|
samples_to_do,chan);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case coding_WS:
|
||||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||||
@ -1801,6 +1826,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
|||||||
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
if (vgmstream->coding_type==coding_fsb_vorbis) {
|
||||||
seek_fsb_vorbis(vgmstream, vgmstream->loop_start_sample);
|
seek_fsb_vorbis(vgmstream, vgmstream->loop_start_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vgmstream->coding_type==coding_wwise_vorbis) {
|
||||||
|
seek_wwise_vorbis(vgmstream, vgmstream->loop_start_sample);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VGM_USE_FFMPEG
|
#ifdef VGM_USE_FFMPEG
|
||||||
|
@ -121,9 +121,10 @@ typedef enum {
|
|||||||
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
|
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
|
||||||
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
|
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
|
||||||
coding_FSB_IMA, /* FMOD's FSB multichannel 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_MSADPCM, /* Microsoft ADPCM */
|
||||||
|
coding_WS, /* Westwood Studios VBR ADPCM */
|
||||||
coding_AICA, /* Yamaha AICA ADPCM */
|
coding_AICA, /* Yamaha AICA ADPCM */
|
||||||
coding_L5_555, /* Level-5 0x555 ADPCM */
|
coding_L5_555, /* Level-5 0x555 ADPCM */
|
||||||
coding_SASSC, /* Activision EXAKT SASSC DPCM */
|
coding_SASSC, /* Activision EXAKT SASSC DPCM */
|
||||||
@ -150,6 +151,7 @@ typedef enum {
|
|||||||
#ifdef VGM_USE_VORBIS
|
#ifdef VGM_USE_VORBIS
|
||||||
coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */
|
coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */
|
||||||
coding_fsb_vorbis, /* FMOD's Vorbis without Ogg layer */
|
coding_fsb_vorbis, /* FMOD's Vorbis without Ogg layer */
|
||||||
|
coding_wwise_vorbis, /* Audiokinetic's Vorbis without Ogg layer */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VGM_USE_MPEG
|
#ifdef VGM_USE_MPEG
|
||||||
@ -721,6 +723,8 @@ typedef struct {
|
|||||||
off_t loop_next_block_offset; /* saved from next_block_offset */
|
off_t loop_next_block_offset; /* saved from next_block_offset */
|
||||||
|
|
||||||
/* decoder specific */
|
/* 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 */
|
uint8_t xa_channel; /* XA ADPCM: selected channel */
|
||||||
int32_t xa_sector_length; /* XA ADPCM: XA block */
|
int32_t xa_sector_length; /* XA ADPCM: XA block */
|
||||||
uint8_t xa_headerless; /* XA ADPCM: headerless XA block */
|
uint8_t xa_headerless; /* XA ADPCM: headerless XA block */
|
||||||
@ -768,8 +772,13 @@ typedef struct {
|
|||||||
ogg_vorbis_streamfile ov_streamfile;
|
ogg_vorbis_streamfile ov_streamfile;
|
||||||
} ogg_vorbis_codec_data;
|
} 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 */
|
/* any raw Vorbis without Ogg layer */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vorbis_info vi; /* stream settings */
|
vorbis_info vi; /* stream settings */
|
||||||
vorbis_comment vc; /* stream comments */
|
vorbis_comment vc; /* stream comments */
|
||||||
vorbis_dsp_state vd; /* decoder global state */
|
vorbis_dsp_state vd; /* decoder global state */
|
||||||
@ -780,6 +789,16 @@ typedef struct {
|
|||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
size_t samples_to_discard; /* for looping purposes */
|
size_t samples_to_discard; /* for looping purposes */
|
||||||
int samples_full; /* flag, samples available in vorbis buffers */
|
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;
|
} vorbis_codec_data;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
/*
|
/**
|
||||||
** vgmstream for XMPlay
|
* vgmstream for XMPlay
|
||||||
**
|
*/
|
||||||
** 11/11/2009 - started. this is hilariously buggy and doesnt support much yet. [unknownfile]
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
@ -156,7 +154,7 @@ int32_t totalFrames, framesDone, framesLength, framesFade;
|
|||||||
#define APP_NAME "vgmstream plugin"
|
#define APP_NAME "vgmstream plugin"
|
||||||
#define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__
|
#define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__
|
||||||
|
|
||||||
void __stdcall XMPAbout(HWND hwParent) {
|
void __stdcall XMP_About(HWND hwParent) {
|
||||||
MessageBox(hwParent,
|
MessageBox(hwParent,
|
||||||
PLUGIN_DESCRIPTION "\n"
|
PLUGIN_DESCRIPTION "\n"
|
||||||
"by hcs, FastElbja, manakoAT, bxaimc, kode54, and PSXGamerPro1\n\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);
|
,"about xmp-vgmstream",MB_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __stdcall Stop() {
|
void __stdcall XMP_Close() {
|
||||||
close_vgmstream(vgmstream);
|
close_vgmstream(vgmstream);
|
||||||
|
vgmstream = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL __stdcall XMP_CheckFile(const char *filename, XMPFILE file) {
|
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;
|
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);
|
if (file) vgmstream = init_vgmstream_from_xmpfile(file, filename);
|
||||||
else vgmstream = init_vgmstream(filename);
|
else vgmstream = init_vgmstream(filename);
|
||||||
|
|
||||||
@ -220,7 +219,7 @@ DWORD __stdcall LoadVgmStream(const char *filename, XMPFILE file) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __stdcall XMP_Buffer(float* buffer, DWORD bufsize) {
|
DWORD __stdcall XMP_Process(float* buffer, DWORD bufsize) {
|
||||||
INT16 buf[1024];
|
INT16 buf[1024];
|
||||||
UINT32 i, j, todo, done;
|
UINT32 i, j, todo, done;
|
||||||
|
|
||||||
@ -323,45 +322,80 @@ double __stdcall XMP_SetPosition(DWORD pos) {
|
|||||||
return cpos;
|
return cpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __stdcall XMP_GetInfoText(char* txt, char* length) {
|
/* main panel info text (short file info) */
|
||||||
if (txt)
|
void __stdcall XMP_GetInfoText(char* format, char* length) {
|
||||||
sprintf(txt,"vgmstream!");
|
if (!format)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sprintf(format,"vgmstream");
|
||||||
|
/* length is the file time */
|
||||||
}
|
}
|
||||||
|
|
||||||
void __stdcall GetAdditionalFields(char* blerp) {
|
/* info for the "General" window/tab (buf is ~40K) */
|
||||||
sprintf(blerp,"oh god how did this get here I am not good with computers\n");
|
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 int add_extension(int length, char * dst, const char * src);
|
||||||
static void build_extension_list();
|
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 vgmstream_intf = {
|
||||||
XMPIN_FLAG_CANSTREAM,
|
XMPIN_FLAG_CANSTREAM,
|
||||||
"vgmstream for XMPlay",
|
"vgmstream for XMPlay",
|
||||||
working_extension_list,
|
working_extension_list,
|
||||||
XMPAbout,
|
XMP_About,
|
||||||
NULL,
|
NULL,//XMP_Config
|
||||||
XMP_CheckFile,
|
XMP_CheckFile,
|
||||||
XMP_GetFileInfo,
|
XMP_GetFileInfo,
|
||||||
LoadVgmStream,
|
XMP_Open,
|
||||||
Stop,
|
XMP_Close,
|
||||||
NULL,
|
NULL,
|
||||||
XMP_SetFormat,
|
XMP_SetFormat,
|
||||||
XMP_GetTags, // Actually mandatory
|
XMP_GetTags, //(OPTIONAL) --actually mandatory
|
||||||
XMP_GetInfoText,
|
XMP_GetInfoText,
|
||||||
GetAdditionalFields,
|
XMP_GetGeneralInfo,
|
||||||
NULL,
|
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
|
||||||
XMP_SetPosition,
|
XMP_SetPosition,
|
||||||
XMP_GetGranularity,
|
XMP_GetGranularity,
|
||||||
NULL,
|
NULL,
|
||||||
XMP_Buffer,
|
XMP_Process,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
|
Loading…
Reference in New Issue
Block a user