Updated vgmstream

This commit is contained in:
Chris Moeller 2015-05-16 18:58:14 -07:00
parent 65a1ecef0d
commit 959a43b2de
6 changed files with 199 additions and 132 deletions

View file

@ -17,6 +17,8 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t sa
if (rc > 0) samples_done += rc/sizeof(sample)/channels; if (rc > 0) samples_done += rc/sizeof(sample)/channels;
else return; else return;
} while (samples_done < samples_to_do); } while (samples_done < samples_to_do);
swap_samples_le(outbuf, samples_to_do*channels);
} }
#endif #endif

View file

@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
off_t stream_offset; off_t stream_offset;
uint16_t version_signature; uint16_t version_signature;
int loop_flag=0; int loop_flag=0;
int channel_count; int channel_count, i, j, channel_header_spacing;
int32_t loop_start_sample=0; int32_t loop_start_sample=0;
int32_t loop_end_sample=0; int32_t loop_end_sample=0;
meta_t header_type; meta_t header_type;
@ -25,160 +25,197 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
int coding_type = coding_CRI_ADX; int coding_type = coding_CRI_ADX;
uint16_t xor_start=0,xor_mult=0,xor_add=0; uint16_t xor_start=0,xor_mult=0,xor_add=0;
/* Xenoblade Chronicles 3D uses an adx extension as with
the Wii version, but it's actually DSP ADPCM. Adding
this flag to account for it. */
int xb3d_flag = 0;
/* check extension, case insensitive */ /* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("adx",filename_extension(filename))) goto fail; if (strcasecmp("adx",filename_extension(filename))) goto fail;
/* check first 2 bytes */ /* check first 2 bytes */
if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) {
if (read_8bit(0,streamFile)!=2)goto fail;
else {xb3d_flag = 1; coding_type = coding_NGC_DSP;}
}
if (xb3d_flag) {
channel_count = read_32bitLE(0, streamFile);
loop_flag = read_16bitLE(0x6e, streamFile);
channel_header_spacing = 0x34;
}
else {
/* get stream offset, check for CRI signature just before */
stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4;
if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */
(uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */
) goto fail;
/* get stream offset, check for CRI signature just before */ /* check for encoding type */
stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is
if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ * ADX with exponential scale, 0x11 is AHX */
(uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ if (read_8bit(4,streamFile) != 3) goto fail;
) goto fail;
/* check for encoding type */ /* check for frame size (only 18 is supported at the moment) */
/* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is if (read_8bit(5,streamFile) != 18) goto fail;
* ADX with exponential scale, 0x11 is AHX */
if (read_8bit(4,streamFile) != 3) goto fail;
/* check for frame size (only 18 is supported at the moment) */ /* check for bits per sample? (only 4 makes sense for ADX) */
if (read_8bit(5,streamFile) != 18) goto fail; if (read_8bit(6,streamFile) != 4) goto fail;
/* check for bits per sample? (only 4 makes sense for ADX) */ /* check version signature, read loop info */
if (read_8bit(6,streamFile) != 4) goto fail; version_signature = read_16bitBE(0x12,streamFile);
/* encryption */
if (version_signature == 0x0408) {
if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add))
{
coding_type = coding_CRI_ADX_enc_8;
version_signature = 0x0400;
}
}
else if (version_signature == 0x0409) {
if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add))
{
coding_type = coding_CRI_ADX_enc_9;
version_signature = 0x0400;
}
}
/* check version signature, read loop info */ if (version_signature == 0x0300) { /* type 03 */
version_signature = read_16bitBE(0x12,streamFile); header_type = meta_ADX_03;
/* encryption */ if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
if (version_signature == 0x0408) { loop_flag = (read_32bitBE(0x18,streamFile) != 0);
if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) loop_start_sample = read_32bitBE(0x1c,streamFile);
{ //loop_start_offset = read_32bitBE(0x20,streamFile);
coding_type = coding_CRI_ADX_enc_8; loop_end_sample = read_32bitBE(0x24,streamFile);
version_signature = 0x0400; //loop_end_offset = read_32bitBE(0x28,streamFile);
} }
} } else if (version_signature == 0x0400) {
else if (version_signature == 0x0409) {
if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add))
{
coding_type = coding_CRI_ADX_enc_9;
version_signature = 0x0400;
}
}
if (version_signature == 0x0300) { /* type 03 */ off_t ainf_info_length=0;
header_type = meta_ADX_03;
if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
loop_flag = (read_32bitBE(0x18,streamFile) != 0);
loop_start_sample = read_32bitBE(0x1c,streamFile);
//loop_start_offset = read_32bitBE(0x20,streamFile);
loop_end_sample = read_32bitBE(0x24,streamFile);
//loop_end_offset = read_32bitBE(0x28,streamFile);
}
} else if (version_signature == 0x0400) {
off_t ainf_info_length=0; if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */
ainf_info_length = (off_t)read_32bitBE(0x28,streamFile);
if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */ header_type = meta_ADX_04;
ainf_info_length = (off_t)read_32bitBE(0x28,streamFile); if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */
if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE)
loop_flag = 0;
else
loop_flag = (read_32bitBE(0x24,streamFile) != 0);
header_type = meta_ADX_04; loop_start_sample = read_32bitBE(0x28,streamFile);
if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */ //loop_start_offset = read_32bitBE(0x2c,streamFile);
if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE) loop_end_sample = read_32bitBE(0x30,streamFile);
//loop_end_offset = read_32bitBE(0x34,streamFile);
}
} else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */
header_type = meta_ADX_05;
} else goto fail; /* not a known/supported version signature */
/* At this point we almost certainly have an ADX file,
* so let's build the VGMSTREAM. */
/* high-pass cutoff frequency, always 500 that I've seen */
cutoff = (uint16_t)read_16bitBE(0x10,streamFile);
if (loop_start_sample == 0 && loop_end_sample == 0) {
loop_flag = 0; loop_flag = 0;
else }
loop_flag = (read_32bitBE(0x24,streamFile) != 0);
loop_start_sample = read_32bitBE(0x28,streamFile); channel_count = read_8bit(7,streamFile);
//loop_start_offset = read_32bitBE(0x2c,streamFile); }
loop_end_sample = read_32bitBE(0x30,streamFile);
//loop_end_offset = read_32bitBE(0x34,streamFile);
}
} else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */
header_type = meta_ADX_05;
} else goto fail; /* not a known/supported version signature */
/* At this point we almost certainly have an ADX file,
* so let's build the VGMSTREAM. */
/* high-pass cutoff frequency, always 500 that I've seen */
cutoff = (uint16_t)read_16bitBE(0x10,streamFile);
if (loop_start_sample == 0 && loop_end_sample == 0) {
loop_flag = 0;
}
channel_count = read_8bit(7,streamFile);
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ /* fill in the vital statistics */
vgmstream->num_samples = read_32bitBE(0xc,streamFile);
vgmstream->sample_rate = read_32bitBE(8,streamFile); if (xb3d_flag) {
/* channels and loop flag are set by allocate_vgmstream */ for (j=0;j<vgmstream->channels;j++) {
vgmstream->loop_start_sample = loop_start_sample; for (i=0;i<16;i++) {
vgmstream->loop_end_sample = loop_end_sample; vgmstream->ch[j].adpcm_coef[i]=read_16bitLE(4+j*channel_header_spacing+i*2,streamFile);
vgmstream->coding_type = coding_type;
if (channel_count==1)
vgmstream->layout_type = layout_none;
else
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = header_type;
vgmstream->interleave_block_size=18;
/* calculate filter coefficients */
{
double x,y,z,a,b,c;
x = cutoff;
y = vgmstream->sample_rate;
z = cos(2.0*M_PI*x/y);
a = M_SQRT2-z;
b = M_SQRT2-1.0;
c = (a-sqrt((a+b)*(a-b)))/b;
coef1 = floor(c*8192);
coef2 = floor(c*c*-4096);
}
{
int i;
STREAMFILE * chstreamfile;
/* ADX is so tightly interleaved that having two buffers is silly */
chstreamfile = streamFile->open(streamFile,filename,18*0x400);
if (!chstreamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = chstreamfile;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
stream_offset+18*i;
vgmstream->ch[i].adpcm_coef[0] = coef1;
vgmstream->ch[i].adpcm_coef[1] = coef2;
if (coding_type == coding_CRI_ADX_enc_8 ||
coding_type == coding_CRI_ADX_enc_9)
{
int j;
vgmstream->ch[i].adx_channels = channel_count;
vgmstream->ch[i].adx_xor = xor_start;
vgmstream->ch[i].adx_mult = xor_mult;
vgmstream->ch[i].adx_add = xor_add;
for (j=0;j<i;j++)
adx_next_key(&vgmstream->ch[i]);
} }
} }
} vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_type;
vgmstream->meta_type = meta_XB3D_ADX;
vgmstream->sample_rate = read_32bitLE(0x70,streamFile);
vgmstream->num_samples = read_32bitLE(0x74, streamFile);
vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile);
for (i = 0; i<channel_count; i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, 0x1000);
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset
= read_32bitLE(0x34+i*channel_header_spacing, streamFile);
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
else {
vgmstream->num_samples = read_32bitBE(0xc,streamFile);
vgmstream->sample_rate = read_32bitBE(8,streamFile);
/* channels and loop flag are set by allocate_vgmstream */
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->coding_type = coding_type;
if (channel_count==1)
vgmstream->layout_type = layout_none;
else
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = header_type;
vgmstream->interleave_block_size=18;
/* calculate filter coefficients */
{
double x,y,z,a,b,c;
x = cutoff;
y = vgmstream->sample_rate;
z = cos(2.0*M_PI*x/y);
a = M_SQRT2-z;
b = M_SQRT2-1.0;
c = (a-sqrt((a+b)*(a-b)))/b;
coef1 = floor(c*8192);
coef2 = floor(c*c*-4096);
}
{
int i;
STREAMFILE * chstreamfile;
/* ADX is so tightly interleaved that having two buffers is silly */
chstreamfile = streamFile->open(streamFile,filename,18*0x400);
if (!chstreamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = chstreamfile;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
stream_offset+18*i;
vgmstream->ch[i].adpcm_coef[0] = coef1;
vgmstream->ch[i].adpcm_coef[1] = coef2;
if (coding_type == coding_CRI_ADX_enc_8 ||
coding_type == coding_CRI_ADX_enc_9)
{
int j;
vgmstream->ch[i].adx_channels = channel_count;
vgmstream->ch[i].adx_xor = xor_start;
vgmstream->ch[i].adx_mult = xor_mult;
vgmstream->ch[i].adx_add = xor_add;
for (j=0;j<i;j++)
adx_next_key(&vgmstream->ch[i]);
}
}
}
}
return vgmstream; return vgmstream;
/* clean up anything we may have opened */ /* clean up anything we may have opened */

View file

@ -127,6 +127,29 @@ void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, i
put_32bitLE(buf+0x28, (int32_t)bytecount); put_32bitLE(buf+0x28, (int32_t)bytecount);
} }
void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end) {
int i;
/* RIFF header */
memcpy(buf+0, "smpl", 4);
/* size of RIFF */
put_32bitLE(buf+4, 0x3c);
for (i = 0; i < 7; i++)
put_32bitLE(buf+8 + i * 4, 0);
put_32bitLE(buf+36, 1);
for (i = 0; i < 3; i++)
put_32bitLE(buf+40 + i * 4, 0);
put_32bitLE(buf+52, loop_start);
put_32bitLE(buf+56, loop_end);
put_32bitLE(buf+60, 0);
put_32bitLE(buf+64, 0);
}
void swap_samples_le(sample *buf, int count) { void swap_samples_le(sample *buf, int count) {
int i; int i;
for (i=0;i<count;i++) { for (i=0;i<count;i++) {

View file

@ -63,6 +63,7 @@ static inline int clamp16(int32_t val) {
/* make a header for PCM .wav */ /* make a header for PCM .wav */
/* buffer must be 0x2c bytes */ /* buffer must be 0x2c bytes */
void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, int channels); void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, int channels);
void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end);
void swap_samples_le(sample *buf, int count); void swap_samples_le(sample *buf, int count);
void concatn(int length, char * dst, const char * src); void concatn(int length, char * dst, const char * src);

View file

@ -3148,6 +3148,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case meta_MCA: case meta_MCA:
snprintf(temp,TEMPSIZE,"Capcom MCA Header"); snprintf(temp,TEMPSIZE,"Capcom MCA Header");
break; break;
case meta_XB3D_ADX:
snprintf(temp, TEMPSIZE,"Xenoblade 3D ADX Header");
break;
default: default:
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
} }

View file

@ -592,6 +592,7 @@ typedef enum {
meta_3DS_IDSP, // Nintendo 3DS IDSP meta_3DS_IDSP, // Nintendo 3DS IDSP
meta_G1L, // Tecmo Koei G1L meta_G1L, // Tecmo Koei G1L
meta_MCA, // Capcom MCA "MADP" meta_MCA, // Capcom MCA "MADP"
meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX
#ifdef VGM_USE_MP4V2 #ifdef VGM_USE_MP4V2
meta_MP4, meta_MP4,
#endif #endif