mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-16 19:37:09 +01:00
commit
f2b0a96afc
@ -25,7 +25,8 @@ CODING_OBJS=coding/adx_decoder.o \
|
||||
coding/at3_decoder.o \
|
||||
coding/g719_decoder.o \
|
||||
coding/hca_decoder.o \
|
||||
coding/ffmpeg_decoder.o
|
||||
coding/ffmpeg_decoder.o \
|
||||
coding/ffmpeg_decoder_utils.o
|
||||
|
||||
LAYOUT_OBJS=layout/ast_blocked.o \
|
||||
layout/blocked.o \
|
||||
|
@ -154,17 +154,44 @@ void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_
|
||||
void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
|
||||
#endif
|
||||
|
||||
/* ffmpeg_decoder */
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_ffmpeg(VGMSTREAM *vgmstream);
|
||||
void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
|
||||
void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
|
||||
/* ffmpeg_decoder_utils */
|
||||
int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
|
||||
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay);
|
||||
int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode);
|
||||
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size);
|
||||
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian);
|
||||
int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align);
|
||||
|
||||
/* XMA sample parser info (struct to avoid passing so much stuff, separate for reusing) */
|
||||
typedef struct {
|
||||
int xma_version;
|
||||
int channels;
|
||||
int stream_mode;
|
||||
off_t data_offset;
|
||||
size_t data_size;
|
||||
int loop_flag;
|
||||
/* frame offsets */
|
||||
uint32_t loop_start_b;
|
||||
uint32_t loop_end_b;
|
||||
uint32_t loop_start_subframe;
|
||||
uint32_t loop_end_subframe;
|
||||
|
||||
/* output */
|
||||
int32_t num_samples;
|
||||
int32_t skip_samples;
|
||||
int32_t loop_start_sample;
|
||||
int32_t loop_end_sample;
|
||||
} xma_sample_data;
|
||||
void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -324,207 +324,4 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) {
|
||||
data->skipSamples = skip_samples;
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* FAKE RIFF HELPERS */
|
||||
/* ******************************************** */
|
||||
static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
|
||||
|
||||
/**
|
||||
* Copies a ATRAC3 riff to buf
|
||||
*
|
||||
* returns number of bytes in buf or -1 when buf is not big enough
|
||||
*/
|
||||
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
|
||||
uint16_t codec_ATRAC3 = 0x0270;
|
||||
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x20);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_ATRAC3);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
|
||||
put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */
|
||||
|
||||
put_16bitLE(buf+0x24, 0x0e); /* extra data size */
|
||||
put_16bitLE(buf+0x26, 1); /* unknown, always 1 */
|
||||
put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
|
||||
put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */
|
||||
put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
|
||||
put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
|
||||
put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
|
||||
put_16bitLE(buf+0x32, 0); /* unknown, always 0 */
|
||||
|
||||
memcpy(buf+0x34, "fact", 4);
|
||||
put_32bitLE(buf+0x38, 0x8); /* fact size */
|
||||
put_32bitLE(buf+0x3c, sample_count);
|
||||
put_32bitLE(buf+0x40, encoder_delay);
|
||||
|
||||
memcpy(buf+0x44, "data", 4);
|
||||
put_32bitLE(buf+0x48, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a XMA2 riff to buf
|
||||
*
|
||||
* returns number of bytes in buf or -1 when buf is not big enough
|
||||
*/
|
||||
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) {
|
||||
uint16_t codec_XMA2 = 0x0166;
|
||||
size_t riff_size = 4+4+ 4 + 0x3c + 4+4;
|
||||
size_t bytecount;
|
||||
uint32_t streams = 0;
|
||||
uint32_t speakers = 0; /* see audiodefs.h */
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
bytecount = sample_count * channels * sizeof(sample);
|
||||
|
||||
/* todo untested */
|
||||
switch (channels) {
|
||||
case 1:
|
||||
streams = 1;
|
||||
speakers = 0x00000004; /* FC */
|
||||
break;
|
||||
case 2:
|
||||
streams = 1;
|
||||
speakers = 0x00000001 | 0x00000002; /* FL FR */
|
||||
break;
|
||||
case 3:
|
||||
streams = 3;
|
||||
speakers = 0x00000001 | 0x00000002 | 0x00000004; /* FL FC FR */
|
||||
break;
|
||||
case 4:
|
||||
streams = 2;
|
||||
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020; /* FL FR BL BR */
|
||||
break;
|
||||
case 5:
|
||||
streams = 3;
|
||||
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000004; /* FL C FR BL BR*/
|
||||
break;
|
||||
case 6:
|
||||
streams = 3;
|
||||
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400; /* FL FR BL BR SL SR */
|
||||
break;
|
||||
default:
|
||||
streams = 1;
|
||||
speakers = 0x80000000;
|
||||
break;
|
||||
}
|
||||
|
||||
/*memset(buf,0, sizeof(uint8_t) * fmt_size);*/
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x34);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_XMA2);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
|
||||
put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */
|
||||
put_16bitLE(buf+0x22, sizeof(sample)*8); /* bits per sample */
|
||||
|
||||
put_16bitLE(buf+0x24, 0x22); /* extra data size */
|
||||
put_16bitLE(buf+0x26, streams); /* number of streams */
|
||||
put_32bitLE(buf+0x28, speakers); /* speaker position */
|
||||
put_32bitLE(buf+0x2c, bytecount); /* PCM samples */
|
||||
put_32bitLE(buf+0x30, block_size); /* XMA block size */
|
||||
/* (looping values not set, expected to be handled externally) */
|
||||
put_32bitLE(buf+0x34, 0); /* play begin */
|
||||
put_32bitLE(buf+0x38, 0); /* play length */
|
||||
put_32bitLE(buf+0x3c, 0); /* loop begin */
|
||||
put_32bitLE(buf+0x40, 0); /* loop length */
|
||||
put_8bit(buf+0x44, 0); /* loop count */
|
||||
put_8bit(buf+0x45, 4); /* encoder version */
|
||||
put_16bitLE(buf+0x46, block_count); /* blocks count = entried in seek table */
|
||||
|
||||
memcpy(buf+0x48, "data", 4);
|
||||
put_32bitLE(buf+0x4c, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies a XMA2 riff to buf from a fmt chunk offset
|
||||
*
|
||||
* returns number of bytes in buf or -1 when buf is not big enough
|
||||
*/
|
||||
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) {
|
||||
size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4;
|
||||
uint8_t chunk[100];
|
||||
|
||||
if (buf_size < riff_size || fmt_size > 100)
|
||||
goto fail;
|
||||
if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size)
|
||||
goto fail;
|
||||
|
||||
if (big_endian)
|
||||
ffmpeg_fmt_chunk_swap_endian(chunk, 0x166);
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, fmt_size);/*fmt size*/
|
||||
memcpy(buf+0x14, chunk, fmt_size);
|
||||
|
||||
memcpy(buf+0x14+fmt_size, "data", 4);
|
||||
put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps endianness
|
||||
*
|
||||
* returns 0 on error
|
||||
*/
|
||||
static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
|
||||
if (codec != 0x166)
|
||||
goto fail;
|
||||
|
||||
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
|
||||
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
|
||||
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
|
||||
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
|
||||
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
|
||||
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
|
||||
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
|
||||
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
|
||||
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
|
||||
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
|
||||
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
|
||||
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
|
||||
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
|
||||
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
|
||||
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
|
||||
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
|
||||
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
|
||||
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
497
src/coding/ffmpeg_decoder_utils.c
Normal file
497
src/coding/ffmpeg_decoder_utils.c
Normal file
@ -0,0 +1,497 @@
|
||||
#include "coding.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
#define XMA_CHECK_SKIPS 0
|
||||
#define XMA_BYTES_PER_PACKET 2048
|
||||
#define XMA_SAMPLES_PER_FRAME 512
|
||||
#define XMA_SAMPLES_PER_SUBFRAME 128
|
||||
|
||||
/* ******************************************** */
|
||||
/* INTERNAL UTILS */
|
||||
/* ******************************************** */
|
||||
|
||||
/**
|
||||
* read num_bits (up to 25) from a bit offset.
|
||||
* 25 since we read a 32 bit int, and need to adjust up to 7 bits from the byte-rounded fseek (32-7=25)
|
||||
*/
|
||||
static uint32_t read_bitsBE_b(off_t bit_offset, int num_bits, STREAMFILE *streamFile) {
|
||||
uint32_t num, mask;
|
||||
if (num_bits > 25) return -1; //???
|
||||
|
||||
num = read_32bitBE(bit_offset / 8, streamFile); /* fseek rounded to 8 */
|
||||
num = num << (bit_offset % 8); /* offset adjust (up to 7) */
|
||||
num = num >> (32 - num_bits);
|
||||
mask = 0xffffffff >> (32 - num_bits);
|
||||
|
||||
return num & mask;
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* FAKE RIFF HELPERS */
|
||||
/* ******************************************** */
|
||||
/* All helpers copy a RIFF header to buf and returns the number of bytes in buf or -1 when buf is not big enough */
|
||||
|
||||
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
|
||||
uint16_t codec_ATRAC3 = 0x0270;
|
||||
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x20);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_ATRAC3);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
|
||||
put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */
|
||||
|
||||
put_16bitLE(buf+0x24, 0x0e); /* extra data size */
|
||||
put_16bitLE(buf+0x26, 1); /* unknown, always 1 */
|
||||
put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
|
||||
put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */
|
||||
put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
|
||||
put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
|
||||
put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
|
||||
put_16bitLE(buf+0x32, 0); /* unknown, always 0 */
|
||||
|
||||
memcpy(buf+0x34, "fact", 4);
|
||||
put_32bitLE(buf+0x38, 0x8); /* fact size */
|
||||
put_32bitLE(buf+0x3c, sample_count);
|
||||
put_32bitLE(buf+0x40, encoder_delay);
|
||||
|
||||
memcpy(buf+0x44, "data", 4);
|
||||
put_32bitLE(buf+0x48, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode) {
|
||||
uint16_t codec_XMA1 = 0x0165;
|
||||
size_t riff_size;
|
||||
int streams, i;
|
||||
|
||||
/* stream disposition:
|
||||
* 0: default (ex. 5ch = 2ch + 2ch + 1ch = 3 streams)
|
||||
* 1: lineal (ex. 5ch = 1ch + 1ch + 1ch + 1ch + 1ch = 5 streams), unusual but exists
|
||||
* others: not seen (ex. maybe 5ch = 2ch + 1ch + 1ch + 1ch = 4 streams) */
|
||||
switch(stream_mode) {
|
||||
case 0 : streams = (channels + 1) / 2; break;
|
||||
case 1 : streams = channels; break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
riff_size = 4+4+ 4 + 0x14 + 0x14*streams + 4+4;
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0xc + 0x14*streams);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_XMA1);
|
||||
put_16bitLE(buf+0x16, 16); /* bits per sample */
|
||||
put_16bitLE(buf+0x18, 0x10D6); /* encoder options */
|
||||
put_16bitLE(buf+0x1a, 0); /* largest stream skip (wrong, unneeded) */
|
||||
put_16bitLE(buf+0x1c, streams); /* number of streams */
|
||||
put_8bit (buf+0x1e, 0); /* loop count */
|
||||
put_8bit (buf+0x1f, 2); /* version */
|
||||
|
||||
for (i = 0; i < streams; i++) {
|
||||
int stream_channels;
|
||||
uint32_t speakers;
|
||||
off_t off = 0x20 + 0x14*i;/* stream riff offset */
|
||||
|
||||
if (stream_mode == 1) {
|
||||
/* lineal */
|
||||
stream_channels = 1;
|
||||
switch(i) { /* per stream, values observed */
|
||||
case 0: speakers = 0x0001; break;/* L */
|
||||
case 1: speakers = 0x0002; break;/* R */
|
||||
case 2: speakers = 0x0004; break;/* C */
|
||||
case 3: speakers = 0x0008; break;/* LFE */
|
||||
case 4: speakers = 0x0040; break;/* LB */
|
||||
case 5: speakers = 0x0080; break;/* RB */
|
||||
case 6: speakers = 0x0000; break;/* ? */
|
||||
case 7: speakers = 0x0000; break;/* ? */
|
||||
default: speakers = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* with odd channels the last stream is mono */
|
||||
stream_channels = channels / streams + (channels%2 != 0 && i+1 != streams ? 1 : 0);
|
||||
switch(i) { /* per stream, values from xmaencode */
|
||||
case 0: speakers = stream_channels == 1 ? 0x0001 : 0x0201; break;/* L R */
|
||||
case 1: speakers = stream_channels == 1 ? 0x0004 : 0x0804; break;/* C LFE */
|
||||
case 2: speakers = stream_channels == 1 ? 0x0040 : 0x8040; break;/* LB RB */
|
||||
case 3: speakers = stream_channels == 1 ? 0x0000 : 0x0000; break;/* somehow empty (maybe should use 0x2010 LS RS) */
|
||||
default: speakers = 0;
|
||||
}
|
||||
}
|
||||
|
||||
put_32bitLE(buf+off+0x00, sample_rate*stream_channels / sizeof(sample)); /* average bytes per second (wrong, unneeded) */
|
||||
put_32bitLE(buf+off+0x04, sample_rate);
|
||||
put_32bitLE(buf+off+0x08, 0); /* loop start */
|
||||
put_32bitLE(buf+off+0x0c, 0); /* loop end */
|
||||
put_8bit (buf+off+0x10, 0); /* loop subframe */
|
||||
put_8bit (buf+off+0x11, channels);
|
||||
put_16bitLE(buf+off+0x12, speakers);
|
||||
}
|
||||
|
||||
memcpy(buf+riff_size-4-4, "data", 4);
|
||||
put_32bitLE(buf+riff_size-4, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) {
|
||||
uint16_t codec_XMA2 = 0x0166;
|
||||
size_t riff_size = 4+4+ 4 + 0x3c + 4+4;
|
||||
size_t bytecount;
|
||||
int streams;
|
||||
uint32_t speakers;
|
||||
|
||||
/* info from xma2defs.h, xact3wb.h and audiodefs.h */
|
||||
streams = (channels + 1) / 2;
|
||||
switch (channels) {
|
||||
case 1: speakers = 0x04; break; /* 1.0: FC */
|
||||
case 2: speakers = 0x01 | 0x02; break; /* 2.0: FL FR */
|
||||
case 3: speakers = 0x01 | 0x02 | 0x08; break; /* 2.1: FL FR LF */
|
||||
case 4: speakers = 0x01 | 0x02 | 0x10 | 0x20; break; /* 4.0: FL FR BL BR */
|
||||
case 5: speakers = 0x01 | 0x02 | 0x08 | 0x10 | 0x20; break; /* 4.1: FL FR LF BL BR */
|
||||
case 6: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20; break; /* 5.1: FL FR FC LF BL BR */
|
||||
case 7: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x0100; break; /* 6.1: FL FR FC LF BL BR BC */
|
||||
case 8: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80; break; /* 7.1: FL FR FC LF BL BR FLC FRC */
|
||||
default: speakers = 0; break;
|
||||
}
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
bytecount = sample_count * channels * sizeof(sample);
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x34);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_XMA2);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong unneeded) */
|
||||
put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */
|
||||
put_16bitLE(buf+0x22, 16); /* bits per sample */
|
||||
|
||||
put_16bitLE(buf+0x24, 0x22); /* extra data size */
|
||||
put_16bitLE(buf+0x26, streams); /* number of streams */
|
||||
put_32bitLE(buf+0x28, speakers); /* speaker position */
|
||||
put_32bitLE(buf+0x2c, bytecount); /* PCM samples */
|
||||
put_32bitLE(buf+0x30, block_size); /* XMA block size */
|
||||
/* (looping values not set, expected to be handled externally) */
|
||||
put_32bitLE(buf+0x34, 0); /* play begin */
|
||||
put_32bitLE(buf+0x38, 0); /* play length */
|
||||
put_32bitLE(buf+0x3c, 0); /* loop begin */
|
||||
put_32bitLE(buf+0x40, 0); /* loop length */
|
||||
put_8bit(buf+0x44, 0); /* loop count */
|
||||
put_8bit(buf+0x45, 4); /* encoder version */
|
||||
put_16bitLE(buf+0x46, block_count); /* blocks count = entries in seek table */
|
||||
|
||||
memcpy(buf+0x48, "data", 4);
|
||||
put_32bitLE(buf+0x4c, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) {
|
||||
size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4;
|
||||
uint8_t chunk[100];
|
||||
|
||||
if (buf_size < riff_size || fmt_size > 100)
|
||||
goto fail;
|
||||
if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size)
|
||||
goto fail;
|
||||
|
||||
if (big_endian)
|
||||
ffmpeg_fmt_chunk_swap_endian(chunk, 0x166);
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, fmt_size);/*fmt size*/
|
||||
memcpy(buf+0x14, chunk, fmt_size);
|
||||
|
||||
memcpy(buf+0x14+fmt_size, "data", 4);
|
||||
put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align) {
|
||||
size_t riff_size = 4+4+ 4 + 0x1a + 4+4;
|
||||
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "XWMA", 4);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x12);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, avg_bps); /* average bits per second, somehow vital for XWMA */
|
||||
put_16bitLE(buf+0x20, block_align); /* block align */
|
||||
put_16bitLE(buf+0x22, 16); /* bits per sample */
|
||||
put_16bitLE(buf+0x24, 0); /* unk */
|
||||
/* here goes the "dpds" table, but it's not needed by FFmpeg */
|
||||
|
||||
memcpy(buf+0x26, "data", 4);
|
||||
put_32bitLE(buf+0x2a, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
|
||||
int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
|
||||
if (codec != 0x166)/* XMA2 */
|
||||
goto fail;
|
||||
|
||||
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
|
||||
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
|
||||
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
|
||||
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
|
||||
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
|
||||
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
|
||||
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
|
||||
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
|
||||
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
|
||||
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
|
||||
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
|
||||
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
|
||||
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
|
||||
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
|
||||
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
|
||||
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
|
||||
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
|
||||
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* XMA PARSING */
|
||||
/* ******************************************** */
|
||||
|
||||
/**
|
||||
* Find total and loop samples by reading XMA frame headers.
|
||||
*
|
||||
* A XMA stream is made of packets, each containing N small frames of X samples.
|
||||
* Frames are further divided into subframes for looping purposes.
|
||||
* XMA1 and XMA2 only differ in the packet headers.
|
||||
*/
|
||||
void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) {
|
||||
int frames = 0, samples = 0, loop_start_frame = 0, loop_end_frame = 0, skip_packets;
|
||||
#if XMA_CHECK_SKIPS
|
||||
int start_skip = 0, end_skip = 0, first_start_skip = 0, last_end_skip = 0;
|
||||
#endif
|
||||
uint32_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b;
|
||||
uint64_t offset_b, packet_offset_b, frame_offset_b;
|
||||
size_t size;
|
||||
|
||||
uint32_t packet_size = XMA_BYTES_PER_PACKET;
|
||||
off_t offset = xma->data_offset;
|
||||
uint32_t stream_offset_b = xma->data_offset * 8;
|
||||
|
||||
size = offset + xma->data_size;
|
||||
packet_size_b = packet_size * 8;
|
||||
|
||||
/* if we knew the streams mode then we could read just the first one and adjust samples later
|
||||
* not a big deal but maybe important for skip stuff */
|
||||
//streams = (xma->stream_mode==0 ? (xma->channels + 1) / 2 : xma->channels)
|
||||
skip_packets = 0;
|
||||
|
||||
/* read packets */
|
||||
while (offset < size) {
|
||||
offset_b = offset * 8; /* global offset in bits */
|
||||
offset += packet_size; /* global offset in bytes */
|
||||
|
||||
/* skip packets not owned by the first stream, since we only need samples from it */
|
||||
if (skip_packets && packet_skip_count) {
|
||||
packet_skip_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* XMA1 or XMA2 packet header */
|
||||
if (xma->xma_version == 1) {
|
||||
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
|
||||
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
|
||||
first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside the packet */
|
||||
packet_skip_count = read_bitsBE_b(offset_b+21, 11, streamFile); /* packets to skip for next packet of this stream */
|
||||
} else {
|
||||
//frame_count = read_bitsBE_b(offset_b+0, 6, streamFile); /* frames that begin in this packet */
|
||||
first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside this packet */
|
||||
//packet_metadata = read_bitsBE_b(offset_b+21, 3, streamFile); /* packet_metadata (always 1) */
|
||||
packet_skip_count = read_bitsBE_b(offset_b+24, 8, streamFile); /* packets to skip for next packet of this stream */
|
||||
}
|
||||
|
||||
/* full packet skip */
|
||||
if (packet_skip_count == 0x7FF) {
|
||||
packet_skip_count = 0;
|
||||
continue;
|
||||
}
|
||||
if (packet_skip_count > 255) { /* seen in some (converted?) XMA1 */
|
||||
packet_skip_count = 0;
|
||||
}
|
||||
VGM_ASSERT(packet_skip_count > 10, "XMA: found big packet skip %i\n", packet_skip_count);//a bit unusual...
|
||||
//VGM_LOG("packet: off=%x, ff=%i, ps=%i\n", offset, first_frame_b, packet_skip_b);
|
||||
|
||||
packet_offset_b = 4*8 + first_frame_b; /* packet offset in bits */
|
||||
|
||||
/* read packet frames */
|
||||
while (packet_offset_b < packet_size_b) {
|
||||
frame_offset_b = offset_b + packet_offset_b; /* in bits for aligment stuff */
|
||||
|
||||
//todo not sure if frames or frames+1 (considering skip_samples)
|
||||
if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_start_b)
|
||||
loop_start_frame = frames;
|
||||
if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_end_b)
|
||||
loop_end_frame = frames;
|
||||
|
||||
|
||||
/* XMA1/2 frame header */
|
||||
frame_size_b = read_bitsBE_b(frame_offset_b, 15, streamFile);
|
||||
frame_offset_b += 15;
|
||||
if (frame_size_b == 0) /* observed in some files with empty frames/packets */
|
||||
break;
|
||||
packet_offset_b += frame_size_b; /* including header */
|
||||
|
||||
#if 0
|
||||
{
|
||||
uint32_t frame_config
|
||||
frame_config = read_bitsBE_b(frame_offset_b, 15, streamFile);
|
||||
|
||||
//VGM_LOG(" frame %04i: off_b=%I64x (~0x%I64x), fs_b=%i (~0x%x), fs=%x\n",frames, frame_offset_b, frame_offset_b/8, frame_size_b,frame_size_b/8, frame_config);
|
||||
|
||||
//if (frame_config != 0x7f00) /* "contains all subframes"? */
|
||||
// continue; // todo read packet end bit instead
|
||||
}
|
||||
#endif
|
||||
frame_offset_b += 15;
|
||||
|
||||
if (frame_size_b == 0x7FFF) { /* end packet frame marker */
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// more header stuff (info from FFmpeg)
|
||||
{
|
||||
int flag;
|
||||
|
||||
/* ignore "postproc transform" */
|
||||
if (xma->channels > 1) {
|
||||
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
|
||||
frame_offset_b += 1;
|
||||
if (flag) {
|
||||
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
|
||||
frame_offset_b += 1;
|
||||
if (flag) {
|
||||
frame_offset_b += 1 + 4 * xma->channels*xma->channels; /* 4-something per double channel? */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get start/end skips to get the proper number of samples */ //todo check if first bit =1 means full 512 skip
|
||||
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
|
||||
frame_offset_b += 1;
|
||||
if (flag) {
|
||||
int new_skip;
|
||||
|
||||
/* get start skip */
|
||||
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
|
||||
frame_offset_b += 1;
|
||||
if (flag) {
|
||||
new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
|
||||
frame_offset_b += 10;
|
||||
VGM_ASSERT(start_skip, "XMA: more than one start_skip (%i)\n", new_skip);
|
||||
|
||||
if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */
|
||||
VGM_LOG("XMA: bad start_skip (%i)\n", new_skip);
|
||||
new_skip = XMA_SAMPLES_PER_FRAME;
|
||||
}
|
||||
|
||||
if (frames==0) first_start_skip = new_skip; /* sometimes in the middle */
|
||||
start_skip += new_skip;
|
||||
}
|
||||
|
||||
/* get end skip */
|
||||
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
|
||||
frame_offset_b += 1;
|
||||
if (flag) {
|
||||
new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
|
||||
frame_offset_b += 10;
|
||||
VGM_ASSERT(end_skip, "XMA: more than one end_skip (%i)\n", new_skip);
|
||||
|
||||
if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */
|
||||
VGM_LOG("XMA: bad end_skip (%i)\n", new_skip);
|
||||
new_skip = XMA_SAMPLES_PER_FRAME;
|
||||
}
|
||||
|
||||
last_end_skip = new_skip; /* not seen */
|
||||
end_skip += new_skip;
|
||||
}
|
||||
|
||||
VGM_LOG(" skip: st=%i, ed=%i\n", start_skip, end_skip);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
samples += XMA_SAMPLES_PER_FRAME;
|
||||
frames++;
|
||||
}
|
||||
}
|
||||
|
||||
#if XMA_CHECK_SKIPS
|
||||
//todo this seems to usually work, but not always
|
||||
/* apply skips (not sure why 64, empty samples generated by the decoder not in the file?) */
|
||||
samples = samples + 64 - start_skip;
|
||||
samples = samples + 64 - end_skip;
|
||||
|
||||
xma->skip_samples = 64 + 512; //todo not always correct
|
||||
#endif
|
||||
|
||||
xma->num_samples = samples;
|
||||
|
||||
if (xma->loop_flag && loop_end_frame > loop_start_frame) {
|
||||
xma->loop_start_sample = loop_start_frame * XMA_SAMPLES_PER_FRAME + xma->loop_start_subframe * XMA_SAMPLES_PER_SUBFRAME;
|
||||
xma->loop_end_sample = loop_end_frame * XMA_SAMPLES_PER_FRAME + xma->loop_end_subframe * XMA_SAMPLES_PER_SUBFRAME;
|
||||
#if XMA_CHECK_SKIPS
|
||||
/* maybe this is needed */
|
||||
//xma->loop_start_sample -= xma->skip_samples;
|
||||
//xma->loop_end_sample -= xma->skip_samples;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -602,7 +602,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_FSB4, "FMOD Sample Bank (FSB4) Header"},
|
||||
{meta_FSB5, "FMOD Sample Bank (FSB5) Header"},
|
||||
{meta_RWX, "RWX Header"},
|
||||
{meta_XWB, "XWB WBND Header"},
|
||||
{meta_XWB, "Microsoft XWB Header"},
|
||||
{meta_XA30, "XA30 Header"},
|
||||
{meta_MUSC, "MUSC Header"},
|
||||
{meta_MUSX_V004, "MUSX / Version 004 Header"},
|
||||
|
@ -1258,10 +1258,14 @@
|
||||
RelativePath=".\coding\eaxa_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\g719_decoder.c"
|
||||
>
|
||||
|
@ -124,6 +124,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="coding\at3_decoder.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils.c" />
|
||||
<ClCompile Include="coding\lsf_decoder.c" />
|
||||
<ClCompile Include="coding\mp4_aac_decoder.c" />
|
||||
<ClCompile Include="coding\mtaf_decoder.c" />
|
||||
|
@ -1021,6 +1021,9 @@
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xma.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -137,7 +137,7 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size)
|
||||
/* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped;
|
||||
* it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */
|
||||
if (offset + buf_size > data->size + data->header_size) {
|
||||
buf_size = data->size + data->header_size - offset;
|
||||
buf_size = data->size - offset; /* header "read" is manually inserted later */
|
||||
}
|
||||
|
||||
ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile);
|
||||
|
@ -194,8 +194,6 @@ VGMSTREAM * init_vgmstream_rwx(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xwb(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xwb2(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xa30(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile);
|
||||
|
198
src/meta/xma.c
198
src/meta/xma.c
@ -1,12 +1,9 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
#define ADJUST_SAMPLE_RATE 0
|
||||
#define XMA_BYTES_PER_PACKET 2048
|
||||
#define XMA_SAMPLES_PER_FRAME 512
|
||||
#define XMA_SAMPLES_PER_SUBFRAME 128
|
||||
#define FAKE_RIFF_BUFFER_SIZE 100
|
||||
|
||||
|
||||
@ -24,26 +21,23 @@ typedef struct {
|
||||
uint8_t xma2_version;
|
||||
int needs_header;
|
||||
int force_little_endian; /* FFmpeg can't parse big endian "fmt" chunks */
|
||||
int skip_samples;
|
||||
|
||||
/* info */
|
||||
int channels;
|
||||
int loop_flag;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start_sample;
|
||||
int32_t loop_end_sample;
|
||||
|
||||
int32_t xma1_loop_start_offset_b;
|
||||
int32_t xma1_loop_end_offset_b;
|
||||
int32_t xma1_subframe_data;
|
||||
int32_t loop_start_b;
|
||||
int32_t loop_end_b;
|
||||
int32_t loop_subframe;
|
||||
} xma_header_data;
|
||||
|
||||
|
||||
static int parse_header(xma_header_data * xma, STREAMFILE *streamFile);
|
||||
static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile);
|
||||
static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile);
|
||||
static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * xma, STREAMFILE *streamFile);
|
||||
static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
|
||||
#if ADJUST_SAMPLE_RATE
|
||||
static int get_xma_sample_rate(int32_t general_rate);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* XMA 1/2 (Microsoft)
|
||||
@ -51,7 +45,6 @@ static int get_xma_sample_rate(int32_t general_rate);
|
||||
* Usually in RIFF headers and minor variations.
|
||||
*/
|
||||
VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
char filename[PATH_LIMIT];
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
ffmpeg_codec_data *data = NULL;
|
||||
|
||||
@ -61,12 +54,8 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("xma",filename_extension(filename))
|
||||
&& strcasecmp("xma2",filename_extension(filename)) /* Skullgirls */
|
||||
&& strcasecmp("nps",filename_extension(filename)) /* Beautiful Katamari */
|
||||
&& strcasecmp("past",filename_extension(filename)) /* SoulCalibur II HD */
|
||||
)
|
||||
/* .xma2: Skullgirls, .nps: Beautiful Katamari, .past: SoulCalibur II HD */
|
||||
if ( !check_extensions(streamFile, "xma,xma2,nps,past") )
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -91,24 +80,29 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
/* build VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(data->channels, xma.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
/*vgmstream->channels = data->channels;*/
|
||||
/*vgmstream->loop_flag = loop_flag;*/
|
||||
|
||||
vgmstream->codec_data = data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_FFmpeg;
|
||||
|
||||
vgmstream->sample_rate = data->sampleRate;
|
||||
#if ADJUST_SAMPLE_RATE
|
||||
vgmstream->sample_rate = get_xma_sample_rate(vgmstream->sample_rate);
|
||||
#endif
|
||||
vgmstream->num_samples = xma.num_samples; /* data->totalSamples: XMA1 = not set; XMA2 = not reliable */
|
||||
|
||||
/* fix samples for some formats (data->totalSamples: XMA1 = not set; XMA2 = not reliable) */
|
||||
xma.channels = data->channels;
|
||||
fix_samples(&xma, streamFile);
|
||||
|
||||
vgmstream->num_samples = xma.num_samples;
|
||||
if (vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = xma.loop_start_sample;
|
||||
vgmstream->loop_end_sample = xma.loop_end_sample;
|
||||
}
|
||||
#if 0
|
||||
//not active due to a FFmpeg bug that misses some of the last packet samples and decodes
|
||||
// garbage if asked for more samples (always happens but more apparent with skip_samples active)
|
||||
/* fix encoder delay */
|
||||
if (data->skipSamples==0)
|
||||
ffmpeg_set_skip_samples(data, xma.skip_samples);
|
||||
#endif
|
||||
|
||||
|
||||
return vgmstream;
|
||||
@ -178,7 +172,8 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
size_t riff_size = 0, fmt_size = 0, xma2_size = 0;
|
||||
|
||||
riff_size = read_32bit(4,streamFile);
|
||||
if (riff_size+8 > xma->file_size) goto fail;
|
||||
if (riff_size != xma->file_size && /* some Beautiful Katamari, unsure if bad rip */
|
||||
riff_size+8 > xma->file_size) goto fail;
|
||||
|
||||
while (current_chunk < xma->file_size && current_chunk < riff_size+8) {
|
||||
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
|
||||
@ -229,7 +224,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
else if (id == id_NXMA) { /* Namco (Tekken 6, Galaga Legions DX) */
|
||||
/* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */
|
||||
/* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */
|
||||
uint32_t chunk_type = read_32bit(0xC,streamFile);
|
||||
xma->data_offset = 0x100;
|
||||
xma->data_size = read_32bit(0x14,streamFile);
|
||||
@ -248,7 +243,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
if (xma->data_size + xma->data_offset > xma->file_size) goto fail;
|
||||
}
|
||||
else if (id == id_PASX) { /* SoulCalibur II HD */
|
||||
/* custom header with a "fmt " data chunk inside */
|
||||
/* custom header with a "fmt " data chunk inside */
|
||||
xma->chunk_size = read_32bit(0x08,streamFile);
|
||||
xma->data_size = read_32bit(0x0c,streamFile);
|
||||
xma->chunk_offset = read_32bit(0x10,streamFile);
|
||||
@ -262,18 +257,21 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* find sample data */
|
||||
if (xma->xma2_version) { /* old XMA2 (internally always BE) */
|
||||
xma->loop_start_sample = read_32bitBE(xma->chunk_offset+0x4,streamFile);
|
||||
xma->loop_end_sample = read_32bitBE(xma->chunk_offset+0x8,streamFile);
|
||||
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set */
|
||||
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set, encoder default */
|
||||
|| xma->loop_end_sample;
|
||||
if (xma->xma2_version == 3) {
|
||||
xma->num_samples = read_32bitBE(xma->chunk_offset+0x14,streamFile);
|
||||
/*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x18,streamFile)*/
|
||||
} else {
|
||||
xma->num_samples = read_32bitBE(xma->chunk_offset+0x1C,streamFile);
|
||||
/*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x20,streamFile)*/
|
||||
}
|
||||
/* num_samples is the max samples in the file (apparently not including encoder delay) */
|
||||
/* pcm_samples are original WAV's; not current since samples and sample rate may be adjusted for looping purposes */
|
||||
}
|
||||
else if (xma->fmt_codec == 0x166) { /* pure XMA2 */
|
||||
xma->num_samples = read_32bit(xma->chunk_offset+0x18,streamFile);
|
||||
@ -281,17 +279,16 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
xma->loop_end_sample = xma->loop_start_sample + read_32bit(xma->chunk_offset+0x2C,streamFile);
|
||||
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x30,streamFile) > 0 /* never set in practice */
|
||||
|| xma->loop_end_sample;
|
||||
/* not needed but may affect looping? (sometimes these don't match loop/total samples) */
|
||||
/* play_begin+end = probably pcm_samples (for original sample rate), don't seem to affect anything */
|
||||
/* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */
|
||||
/* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */
|
||||
}
|
||||
else if (xma->fmt_codec == 0x165) { /* pure XMA1 */
|
||||
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0xA,streamFile) > 0;
|
||||
xma->xma1_loop_start_offset_b = read_32bit(xma->chunk_offset+0x14,streamFile);
|
||||
xma->xma1_loop_end_offset_b = read_32bit(xma->chunk_offset+0x18,streamFile);
|
||||
xma->xma1_subframe_data = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile);
|
||||
/* find samples count + loop samples since they are not in the header */
|
||||
parse_xma1_sample_data(xma, streamFile);
|
||||
xma->loop_start_b = read_32bit(xma->chunk_offset+0x14,streamFile);
|
||||
xma->loop_end_b = read_32bit(xma->chunk_offset+0x18,streamFile);
|
||||
xma->loop_subframe = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile);
|
||||
/* num_samples are parsed later */
|
||||
}
|
||||
else { /* unknown chunk */
|
||||
goto fail;
|
||||
@ -304,69 +301,42 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* XMA1: manually find total and loop samples
|
||||
*
|
||||
* A XMA1 stream is made of packets, each containing N frames of X samples, and frame is divided into subframes for looping purposes.
|
||||
* FFmpeg can't get total samples without decoding, so we'll count frames+samples by reading packet headers.
|
||||
*/
|
||||
static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
int frames = 0, loop_frame_start = 0, loop_frame_end = 0, loop_subframe_end, loop_subframe_skip;
|
||||
uint32_t header, first_frame_b, packet_skip_b, frame_size_b, packet_size_b;
|
||||
uint64_t packet_offset_b, frame_offset_b;
|
||||
uint32_t size;
|
||||
static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile) {
|
||||
xma_sample_data xma_sd;
|
||||
|
||||
uint32_t packet_size = XMA_BYTES_PER_PACKET;
|
||||
uint32_t offset = xma->data_offset;
|
||||
uint32_t offset_b = 0;
|
||||
uint32_t stream_offset_b = xma->data_offset * 8;
|
||||
/* for now only XMA1 is fixed, but xmaencode.exe doesn't seem to use
|
||||
* XMA2 sample values in the headers, and the exact number of samples may not be exact.
|
||||
* Also loop values don't seem to need skip_samples. */
|
||||
|
||||
size = offset + xma->data_size;
|
||||
packet_size_b = packet_size*8;
|
||||
|
||||
while (offset < size) { /* stream global offset*/
|
||||
/* XMA1 packet header (32b) = packet_sequence:4, unk:2: frame_offset_in_bits:15, packet_stream_skip_count:11 */
|
||||
header = read_32bitBE(offset, streamFile);
|
||||
first_frame_b = (header >> 11) & 0x7FFF;
|
||||
packet_skip_b = (header) & 0x7FF;
|
||||
|
||||
offset_b = offset * 8;
|
||||
packet_offset_b = 4*8 + first_frame_b;
|
||||
while (packet_offset_b < packet_size_b && packet_skip_b!=0x7FF) { /* internal packet offset + full packet skip (needed?) */
|
||||
frame_offset_b = offset_b + packet_offset_b; /* global offset to packet, in bits for aligment stuff */
|
||||
|
||||
/* XMA1 frame header (32b) = frame_length_in_bits:15, frame_data:17+ */
|
||||
header = read_32bitBE(frame_offset_b/8, streamFile);
|
||||
frame_size_b = (header >> (17 - frame_offset_b % 8)) & 0x7FFF;
|
||||
|
||||
if (frame_size_b == 0) /* observed in some files */
|
||||
break;
|
||||
|
||||
packet_offset_b += frame_size_b;/* including header */
|
||||
|
||||
if (frame_size_b != 0x7FFF) /* end frame marker*/
|
||||
frames++;
|
||||
|
||||
if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_start_offset_b)
|
||||
loop_frame_start = frames;
|
||||
if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_end_offset_b)
|
||||
loop_frame_end = frames;
|
||||
}
|
||||
|
||||
offset += packet_size;
|
||||
if (xma->fmt_codec != 0x165) {
|
||||
return;
|
||||
}
|
||||
|
||||
loop_subframe_end = xma->xma1_subframe_data >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
||||
loop_subframe_skip = xma->xma1_subframe_data & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
|
||||
memset(&xma_sd,0,sizeof(xma_sample_data));
|
||||
|
||||
xma->num_samples = frames * XMA_SAMPLES_PER_FRAME;
|
||||
if (xma->loop_flag) {
|
||||
xma->loop_start_sample = loop_frame_start * XMA_SAMPLES_PER_FRAME + loop_subframe_skip * XMA_SAMPLES_PER_SUBFRAME;
|
||||
xma->loop_end_sample = loop_frame_end * XMA_SAMPLES_PER_FRAME + loop_subframe_end * XMA_SAMPLES_PER_SUBFRAME;
|
||||
}
|
||||
/* call xma parser (copy to its own struct, a bit clunky but oh well...) */
|
||||
xma_sd.xma_version = xma->fmt_codec==0x165 ? 1 : 2;
|
||||
xma_sd.channels = xma->channels;
|
||||
xma_sd.data_offset = xma->data_offset;
|
||||
xma_sd.data_size = xma->data_size;
|
||||
xma_sd.loop_flag = xma->loop_flag;
|
||||
xma_sd.loop_start_b = xma->loop_start_b;
|
||||
xma_sd.loop_end_b = xma->loop_end_b;
|
||||
xma_sd.loop_start_subframe = xma->loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
|
||||
xma_sd.loop_end_subframe = xma->loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
||||
|
||||
xma_get_samples(&xma_sd, streamFile);
|
||||
|
||||
/* and recieve results */
|
||||
xma->num_samples = xma_sd.num_samples;
|
||||
xma->skip_samples = xma_sd.skip_samples;
|
||||
xma->loop_start_sample = xma_sd.loop_start_sample;
|
||||
xma->loop_end_sample = xma_sd.loop_end_sample;
|
||||
/* XMA2 loop/num_samples don't seem to skip_samples */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Recreates a RIFF header that FFmpeg can read since it lacks support for some variations.
|
||||
*
|
||||
@ -406,7 +376,7 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data *
|
||||
internal_size = 4+4+xma->chunk_size;
|
||||
|
||||
if (xma->force_little_endian ) {
|
||||
if ( !fmt_chunk_swap_endian(chunk, xma->fmt_codec) )
|
||||
if ( !ffmpeg_fmt_chunk_swap_endian(chunk, xma->fmt_codec) )
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -434,44 +404,10 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Swaps endianness
|
||||
*
|
||||
* returns 0 on error
|
||||
*/
|
||||
static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
|
||||
if (codec != 0x166)
|
||||
goto fail;
|
||||
|
||||
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
|
||||
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
|
||||
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
|
||||
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
|
||||
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
|
||||
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
|
||||
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
|
||||
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
|
||||
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
|
||||
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
|
||||
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
|
||||
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
|
||||
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
|
||||
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
|
||||
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
|
||||
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
|
||||
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
|
||||
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if ADJUST_SAMPLE_RATE
|
||||
/**
|
||||
* Get real XMA sample rate (from Microsoft docs, apparently info only and not correct for playback).
|
||||
* Get real XMA sample rate (from Microsoft docs).
|
||||
* Info only, not for playback as the encoder adjusts sample rate for looping purposes (sample<>data align).
|
||||
*/
|
||||
static int32_t get_xma_sample_rate(int32_t general_rate) {
|
||||
int32_t xma_rate = 48000; /* default XMA */
|
||||
|
501
src/meta/xwb.c
501
src/meta/xwb.c
@ -1,169 +1,384 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XWB - XACT Wave Bank (Main format on the XBOX console) */
|
||||
/* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */
|
||||
|
||||
#define WAVEBANK_FLAGS_COMPACT 0x00020000 // Bank uses compact format
|
||||
#define WAVEBANKENTRY_FLAGS_IGNORELOOP 0x00000008 // Used internally when the loop region can't be used
|
||||
|
||||
static const int32_t wma_avg_bps_index[] = { /*7*/
|
||||
12000, 24000, 4000, 6000, 8000, 20000, 2500
|
||||
};
|
||||
static const int32_t wma_block_align_index[] = { /*17*/
|
||||
929, 1487, 1280, 2230, 8917, 8192, 4459, 5945, 2304, 1536, 1485, 1008, 2731, 4096, 6827, 5462, 1280
|
||||
};
|
||||
|
||||
|
||||
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA } xact_codec;
|
||||
typedef struct {
|
||||
int little_endian;
|
||||
int xact; /* rough XACT version (1/2/3) */
|
||||
|
||||
int version;
|
||||
|
||||
/* segments */
|
||||
off_t base_offset;
|
||||
size_t base_size;
|
||||
off_t entry_offset;
|
||||
size_t entry_size;
|
||||
off_t data_offset;
|
||||
size_t data_size;
|
||||
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
|
||||
uint32_t base_flags;
|
||||
size_t entry_elem_size;
|
||||
size_t entry_alignment;
|
||||
int streams;
|
||||
|
||||
uint32_t entry_flags;
|
||||
uint32_t format;
|
||||
int tag;
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int block_align;
|
||||
int bits_per_sample;
|
||||
xact_codec codec;
|
||||
|
||||
int loop_flag;
|
||||
uint32_t num_samples;
|
||||
uint32_t loop_start;
|
||||
uint32_t loop_end;
|
||||
uint32_t loop_start_sample;
|
||||
uint32_t loop_end_sample;
|
||||
} xwb_header;
|
||||
|
||||
|
||||
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
|
||||
VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int coding;
|
||||
int coding_flag;
|
||||
int num_samples;
|
||||
int header_start;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
|
||||
off_t start_offset, off, suboff;
|
||||
xwb_header xwb;
|
||||
int target_stream = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* check header */
|
||||
if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* WBND */
|
||||
(read_32bitBE(0x00,streamFile) != 0x444E4257)) /* DNBW */
|
||||
/* basic checks */
|
||||
if (!check_extensions(streamFile,"xwb")) goto fail;
|
||||
|
||||
if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */
|
||||
(read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */
|
||||
goto fail;
|
||||
/* check if the file is been used as container */
|
||||
if (read_32bitBE(0x2C,streamFile) != 0x01000000)
|
||||
goto fail;
|
||||
|
||||
header_start = read_32bitLE(0x10,streamFile);
|
||||
loop_flag = (read_32bitLE(header_start+0x10,streamFile) != 0x0);
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = read_32bitLE(0x20,streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = (((uint32_t)read_32bitLE(header_start+0x4, streamFile)) << 8 >> 12) / 2;
|
||||
|
||||
coding_flag = (uint16_t)(read_16bitLE(header_start+0x2,streamFile));
|
||||
switch (coding_flag) {
|
||||
case 0:
|
||||
coding = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
num_samples = read_32bitLE(header_start+0xC,streamFile)/2/channel_count;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(header_start+10,streamFile)/2/channel_count;
|
||||
vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/2/channel_count;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
coding = coding_XBOX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
num_samples = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(header_start+0x10,streamFile)/36/channel_count*64;
|
||||
vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
memset(&xwb,0,sizeof(xwb_header));
|
||||
|
||||
vgmstream->coding_type = coding;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->meta_type = meta_XWB;
|
||||
xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */
|
||||
if (xwb.little_endian) {
|
||||
read_32bit = read_32bitLE;
|
||||
} else {
|
||||
read_32bit = read_32bitBE;
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
|
||||
if (vgmstream->coding_type == coding_XBOX) {
|
||||
/* xbox interleaving is a little odd */
|
||||
vgmstream->ch[i].channel_start_offset=start_offset;
|
||||
} else {
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
start_offset+vgmstream->interleave_block_size*i;
|
||||
}
|
||||
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset;
|
||||
/* read main header (WAVEBANKHEADER) */
|
||||
xwb.version = read_32bit(0x04, streamFile);
|
||||
if (xwb.version <= 3) {
|
||||
xwb.xact = 1; /* XACT1: XBOX [The King of Fighters 2003] */
|
||||
} else if (xwb.version < 42) {
|
||||
xwb.xact = 2; /* XACT2: early XBOX360 [Kameo, Table Tennis, Blue Dragon], Windows */
|
||||
} else {
|
||||
xwb.xact = 3; /* XACT3: late XBOX360, Windows */
|
||||
}
|
||||
|
||||
/* read segment offsets (SEGIDX) */
|
||||
off = xwb.xact <= 2 ? 0x08 : 0x0c; /* XACT3: 0x04=tool version, 0x08=header version */
|
||||
xwb.base_offset = read_32bit(off+0x00, streamFile);//BANKDATA
|
||||
xwb.base_size = read_32bit(off+0x04, streamFile);
|
||||
xwb.entry_offset= read_32bit(off+0x08, streamFile);//ENTRYMETADATA
|
||||
xwb.entry_size = read_32bit(off+0x0c, streamFile);
|
||||
/* go to last segment (XACT2/3 have 5 segments, XACT1 4) */
|
||||
//0x10: XACT1/2: ENTRYNAMES, XACT3: SEEKTABLES
|
||||
//0x14: XACT1: none (ENTRYWAVEDATA), XACT2: EXTRA, XACT3: ENTRYNAMES
|
||||
suboff = xwb.xact >= 2 ? 0x08+0x08 : 0x08;
|
||||
xwb.data_offset = read_32bit(off+0x10+suboff, streamFile);//ENTRYWAVEDATA
|
||||
xwb.data_size = read_32bit(off+0x10+suboff, streamFile);
|
||||
|
||||
|
||||
/* read base entry (WAVEBANKDATA) */
|
||||
off = xwb.base_offset;
|
||||
xwb.base_flags = (uint32_t)read_32bit(off+0x00, streamFile);
|
||||
xwb.streams = read_32bit(off+0x04, streamFile);
|
||||
/* 0x08 bank_name */
|
||||
suboff = 0x08 + (xwb.xact == 1 ? 0x10 : 0x40);
|
||||
xwb.entry_elem_size = read_32bit(off+suboff+0x00, streamFile);
|
||||
/* suboff+0x04: meta name entry size */
|
||||
xwb.entry_alignment = read_32bit(off+suboff+0x08, streamFile); /* usually 1 dvd sector */
|
||||
xwb.format = read_32bit(off+suboff+0x0c, streamFile); /* compact mode only */
|
||||
/* suboff+0x10: build time 64b (XACT2/3) */
|
||||
|
||||
VGM_ASSERT(xwb.streams > 1, "XWB: multiple streams found (%i entries)\n", xwb.streams);
|
||||
if (target_stream == 0) target_stream = 1; /* auto: default to 1 */
|
||||
if (xwb.streams < 1 || target_stream > xwb.streams) goto fail;
|
||||
|
||||
|
||||
/* read stream entry (WAVEBANKENTRY) */
|
||||
off = xwb.entry_offset + (target_stream-1) * xwb.entry_elem_size;
|
||||
|
||||
if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry */
|
||||
/* offset_in_sectors:21 and sector_alignment_in_bytes:11 */
|
||||
uint32_t entry = (uint32_t)read_32bit(off+0x00, streamFile);
|
||||
xwb.stream_offset = xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF);
|
||||
|
||||
/* find size (up to next entry or data end) */
|
||||
if (xwb.streams > 1) {
|
||||
entry = (uint32_t)read_32bit(off+xwb.entry_size, streamFile);
|
||||
xwb.stream_size = xwb.stream_offset -
|
||||
(xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF));
|
||||
} else {
|
||||
xwb.stream_size = xwb.data_size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t entry_info = (uint32_t)read_32bit(off+0x00, streamFile);
|
||||
if (xwb.xact == 1) {
|
||||
xwb.entry_flags = entry_info;
|
||||
} else {
|
||||
xwb.entry_flags = (entry_info) & 0xF; /*4*/
|
||||
xwb.num_samples = (entry_info >> 4) & 0x0FFFFFFF; /*28*/
|
||||
}
|
||||
xwb.format = (uint32_t)read_32bit(off+0x04, streamFile);
|
||||
xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(off+0x08, streamFile);
|
||||
xwb.stream_size = (uint32_t)read_32bit(off+0x0c, streamFile);
|
||||
|
||||
if (xwb.xact == 1) { //LoopRegion (bytes within data)
|
||||
xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile);
|
||||
xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length
|
||||
} else if (xwb.xact == 2) {//LoopRegion (bytes within data) or XMALoopRegion (bits within data)
|
||||
xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile);
|
||||
xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length (LoopRegion) or offset (XMALoopRegion)
|
||||
} else {//LoopRegion (samples)
|
||||
xwb.loop_start_sample = (uint32_t)read_32bit(off+0x10, streamFile);
|
||||
xwb.loop_end_sample = xwb.loop_start_sample + (uint32_t)read_32bit(off+0x14, streamFile);
|
||||
}
|
||||
|
||||
xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start)
|
||||
&& !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP);
|
||||
}
|
||||
|
||||
|
||||
/* parse format */
|
||||
if (xwb.xact == 1) {
|
||||
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
|
||||
xwb.sample_rate = (xwb.format >> 5) & 0x3FFFFFF; /*26*/
|
||||
xwb.channels = (xwb.format >> 2) & 0x7; /*3*/
|
||||
xwb.tag = (xwb.format) & 0x3; /*2*/
|
||||
}
|
||||
else if (xwb.version <= 34) { /* early XACT2, not sure if includes v35/36 */
|
||||
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
|
||||
xwb.block_align = (xwb.format >> 24) & 0xFF; /*8*/
|
||||
xwb.sample_rate = (xwb.format >> 4) & 0x7FFFF; /*19*/
|
||||
xwb.channels = (xwb.format >> 1) & 0x7; /*3*/
|
||||
xwb.tag = (xwb.format) & 0x1; /*1*/
|
||||
}
|
||||
else {
|
||||
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
|
||||
xwb.block_align = (xwb.format >> 23) & 0xFF; /*8*/
|
||||
xwb.sample_rate = (xwb.format >> 5) & 0x3FFFF; /*18*/
|
||||
xwb.channels = (xwb.format >> 2) & 0x7; /*3*/
|
||||
xwb.tag = (xwb.format) & 0x3; /*2*/
|
||||
}
|
||||
|
||||
/* standardize tag to codec */
|
||||
if (xwb.xact == 1) {
|
||||
switch(xwb.tag){
|
||||
case 0: xwb.codec = PCM; break;
|
||||
case 1: xwb.codec = XBOX_ADPCM; break;
|
||||
case 2: xwb.codec = WMA; break;
|
||||
default: goto fail;
|
||||
}
|
||||
} else if (xwb.xact == 2) {
|
||||
switch(xwb.tag) {
|
||||
case 0: xwb.codec = PCM; break;
|
||||
/* Table Tennis (v34): XMA1, Prey (v38): XMA2, v35/36/37: ? */
|
||||
case 1: xwb.codec = xwb.version <= 34 ? XMA1 : XMA2; break;
|
||||
case 2: xwb.codec = MS_ADPCM; break;
|
||||
default: goto fail;
|
||||
}
|
||||
} else {
|
||||
switch(xwb.tag) {
|
||||
case 0: xwb.codec = PCM; break;
|
||||
case 1: xwb.codec = XMA2; break;
|
||||
case 2: xwb.codec = MS_ADPCM; break;
|
||||
case 3: xwb.codec = XWMA; break;
|
||||
default: goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VGMSTREAM * init_vgmstream_xwb2(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x444E4257) /* WBND */
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitBE(0xA4,streamFile)!=0);
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x800;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 44100;
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->num_samples = read_32bitBE(0xA0,streamFile)/2/channel_count;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitBE(0xA4,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitBE(0xA0,streamFile)/2/channel_count;
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
vgmstream->meta_type = meta_XWB;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
/* fix samples */
|
||||
if ((xwb.xact == 1 || xwb.xact == 2) && xwb.codec == PCM) {
|
||||
xwb.num_samples = xwb.stream_size / 2 / xwb.channels;
|
||||
if (xwb.loop_flag) {
|
||||
xwb.loop_start_sample = (xwb.loop_start) / 2 / xwb.channels;
|
||||
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 2 / xwb.channels;
|
||||
}
|
||||
}
|
||||
else if (xwb.xact == 1 && xwb.codec == XBOX_ADPCM) {
|
||||
xwb.num_samples = xwb.stream_size / 36 / xwb.channels*64;
|
||||
if (xwb.loop_flag) {
|
||||
xwb.loop_start_sample = xwb.loop_start / 36 / xwb.channels*64;
|
||||
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 36 / xwb.channels*64;
|
||||
}
|
||||
}
|
||||
else if (xwb.xact == 2 && xwb.codec == MS_ADPCM && xwb.loop_flag) {
|
||||
int block_size, samples_per_frame;
|
||||
block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
|
||||
samples_per_frame = (block_size-(7-1) * xwb.channels) * 2 / xwb.channels;
|
||||
|
||||
xwb.loop_start_sample = (xwb.loop_start) / block_size / samples_per_frame;
|
||||
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / block_size / samples_per_frame;
|
||||
}
|
||||
else if (xwb.xact == 2 && (xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) {
|
||||
/* need to manually find sample offsets, thanks to Microsoft dumb headers */
|
||||
xma_sample_data xma_sd;
|
||||
memset(&xma_sd,0,sizeof(xma_sample_data));
|
||||
|
||||
xma_sd.xma_version = xwb.codec == XMA1 ? 1 : 2;
|
||||
xma_sd.channels = xwb.channels;
|
||||
xma_sd.data_offset = xwb.stream_offset;
|
||||
xma_sd.data_size = xwb.stream_size;
|
||||
xma_sd.loop_flag = xwb.loop_flag;
|
||||
xma_sd.loop_start_b = xwb.loop_start; /* bit offset in the stream */
|
||||
xma_sd.loop_end_b = (xwb.loop_end >> 4); /*28b */
|
||||
/* XACT adds +1 to the subframe, but this means 0 can't be used? */
|
||||
xma_sd.loop_end_subframe = ((xwb.loop_end >> 2) & 0x3) + 1; /* 2b */
|
||||
xma_sd.loop_start_subframe = ((xwb.loop_end >> 0) & 0x3) + 1; /* 2b */
|
||||
|
||||
xma_get_samples(&xma_sd, streamFile);
|
||||
xwb.loop_start_sample = xma_sd.loop_start_sample;
|
||||
xwb.loop_end_sample = xma_sd.loop_end_sample;
|
||||
// todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't)
|
||||
xwb.loop_start_sample -= 512;
|
||||
xwb.loop_end_sample -= 512;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(xwb.channels,xwb.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = xwb.sample_rate;
|
||||
vgmstream->num_samples = xwb.num_samples;
|
||||
vgmstream->loop_start_sample = xwb.loop_start_sample;
|
||||
vgmstream->loop_end_sample = xwb.loop_end_sample;
|
||||
vgmstream->meta_type = meta_XWB;
|
||||
|
||||
switch(xwb.codec) {
|
||||
case PCM:
|
||||
vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 :
|
||||
(xwb.little_endian ? coding_PCM16LE : coding_PCM16BE);
|
||||
vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none;
|
||||
vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02;
|
||||
break;
|
||||
|
||||
case XBOX_ADPCM:
|
||||
vgmstream->coding_type = coding_XBOX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case MS_ADPCM:
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA1: {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[100];
|
||||
int bytes;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case XMA2: {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[100];
|
||||
int bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x10000; /* XACT default */
|
||||
block_count = xwb.stream_size / block_size + (xwb.stream_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case WMA: { /* WMAudio1 (WMA v1) */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[100];
|
||||
int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
|
||||
|
||||
bps_index = (xwb.block_align >> 5); /* upper 3b bytes-per-second index */ //docs say 2b+6b but are wrong
|
||||
block_index = (xwb.block_align) & 0x1F; /*lower 5b block alignment index */
|
||||
if (bps_index >= 7) goto fail;
|
||||
if (block_index >= 17) goto fail;
|
||||
|
||||
avg_bps = wma_avg_bps_index[bps_index];
|
||||
block_align = wma_block_align_index[block_index];
|
||||
wma_codec = xwb.bits_per_sample ? 0x162 : 0x161; /* 0=WMAudio2, 1=WMAudio3 */
|
||||
|
||||
bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
start_offset = xwb.data_offset;
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,7 +98,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_fsb5,
|
||||
init_vgmstream_rwx,
|
||||
init_vgmstream_xwb,
|
||||
init_vgmstream_xwb2,
|
||||
init_vgmstream_xa30,
|
||||
init_vgmstream_musc,
|
||||
init_vgmstream_musx_v004,
|
||||
|
Loading…
Reference in New Issue
Block a user