diff --git a/src/coding/coding.h b/src/coding/coding.h index cf6111fd..9911d3cb 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -181,17 +181,18 @@ int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_co 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_xma_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); +int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, 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) */ +/* MS audio format's sample 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 */ + int loop_flag; uint32_t loop_start_b; uint32_t loop_end_b; uint32_t loop_start_subframe; @@ -202,8 +203,9 @@ typedef struct { 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); +} xma_sample_data; /* ms_sample_data */ +void xma_get_samples(xma_sample_data * msd, STREAMFILE *streamFile); +void wmapro_get_samples(xma_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags); #endif diff --git a/src/coding/ffmpeg_decoder_utils.c b/src/coding/ffmpeg_decoder_utils.c index 8ce1409a..83598e6c 100644 --- a/src/coding/ffmpeg_decoder_utils.c +++ b/src/coding/ffmpeg_decoder_utils.c @@ -1,13 +1,9 @@ #include "coding.h" +#include "math.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 */ /* ******************************************** */ @@ -287,7 +283,7 @@ 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) { +int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, 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) @@ -302,11 +298,11 @@ int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t samp 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_32bitLE(buf+0x1c, avg_bps); /* average bytes 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 */ + put_16bitLE(buf+0x24, 0); /* extra size */ + /* here goes the "dpds" table, but it's optional and not needed by FFmpeg */ memcpy(buf+0x26, "data", 4); put_32bitLE(buf+0x2a, data_size); /* data size */ @@ -374,33 +370,33 @@ fail: /* ******************************************** */ /* XMA PARSING */ /* ******************************************** */ +#define XMA_CHECK_SKIPS 0 /** - * Find total and loop samples by reading XMA frame headers. + * Find total and loop samples of Microsoft audio formats (WMAPRO/XMA1/XMA2) by reading 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. + * The stream is made of packets, each containing N small frames of X samples. Frames are further divided into subframes. + * XMA1/XMA2/WMAPRO only differ in the packet headers. */ -void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { +static void ms_audio_get_samples(xma_sample_data * msd, STREAMFILE *streamFile, int bytes_per_packet, int samples_per_frame, int samples_per_subframe, int bits_frame_size) { 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; + uint32_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b, header_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; + uint32_t packet_size = bytes_per_packet; + off_t offset = msd->data_offset; + uint32_t stream_offset_b = msd->data_offset * 8; - size = offset + xma->data_size; + size = offset + msd->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) + //streams = (msd->stream_mode==0 ? (msd->channels + 1) / 2 : msd->channels) skip_packets = 0; /* read packets */ @@ -414,19 +410,28 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { continue; } - /* XMA1 or XMA2 packet header */ - if (xma->xma_version == 1) { + /* packet header */ + if (msd->xma_version == 1) { /* XMA1 */ //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 */ + first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, 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 { + header_size_b = 32; + } else if (msd->xma_version == 2) { /* XMA2 */ //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 */ + first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, 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 */ + header_size_b = 32; + } else { /* WMAPRO(v3) */ + //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, bits_frame_size, streamFile); /* offset in bits inside the packet */ + packet_skip_count = 0; /* xwma probably has no need to skip packets since it uses real multichannel ch audio */ + header_size_b = 4+2+bits_frame_size; /* variable-size header */ } + /* full packet skip */ if (packet_skip_count == 0x7FF) { packet_skip_count = 0; @@ -438,22 +443,22 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { 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 */ + packet_offset_b = header_size_b + 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) + if (msd->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == msd->loop_start_b) loop_start_frame = frames; - if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_end_b) + if (msd->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == msd->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; + /* frame header */ + frame_size_b = read_bitsBE_b(frame_offset_b, bits_frame_size, streamFile); + frame_offset_b += bits_frame_size; if (frame_size_b == 0) /* observed in some files with empty frames/packets */ break; packet_offset_b += frame_size_b; /* including header */ @@ -469,7 +474,7 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { // continue; // todo read packet end bit instead } #endif - frame_offset_b += 15; + frame_offset_b += 15; //todo bits_frame_size? if (frame_size_b == 0x7FFF) { /* end packet frame marker */ break; @@ -481,14 +486,14 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { int flag; /* ignore "postproc transform" */ - if (xma->channels > 1) { + if (msd->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? */ + frame_offset_b += 1 + 4 * msd->channels*msd->channels; /* 4-something per double channel? */ } } } @@ -508,9 +513,9 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *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 */ + if (new_skip > samples_per_frame) { /* from xmaencode */ VGM_LOG("XMA: bad start_skip (%i)\n", new_skip); - new_skip = XMA_SAMPLES_PER_FRAME; + new_skip = samples_per_frame; } if (frames==0) first_start_skip = new_skip; /* sometimes in the middle */ @@ -526,9 +531,9 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *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 */ + if (new_skip > samples_per_frame) { /* from xmaencode */ VGM_LOG("XMA: bad end_skip (%i)\n", new_skip); - new_skip = XMA_SAMPLES_PER_FRAME; + new_skip = samples_per_frame; } last_end_skip = new_skip; /* not seen */ @@ -540,7 +545,7 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { } #endif - samples += XMA_SAMPLES_PER_FRAME; + samples += samples_per_frame; frames++; } } @@ -551,20 +556,79 @@ void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { samples = samples + 64 - start_skip; samples = samples + 64 - end_skip; - xma->skip_samples = 64 + 512; //todo not always correct + msd->skip_samples = 64 + samples_per_frame; //todo not always correct #endif - xma->num_samples = samples; + msd->num_samples = samples; + VGM_LOG("frames=%i\n", frames); - 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 (msd->loop_flag && loop_end_frame > loop_start_frame) { + msd->loop_start_sample = loop_start_frame * samples_per_frame + msd->loop_start_subframe * samples_per_subframe; + msd->loop_end_sample = loop_end_frame * samples_per_frame + msd->loop_end_subframe * 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; + //msd->loop_start_sample -= msd->skip_samples; + //msd->loop_end_sample -= msd->skip_samples; #endif } } +void xma_get_samples(xma_sample_data * msd, STREAMFILE *streamFile) { + const int bytes_per_packet = 2048; + const int samples_per_frame = 512; + const int samples_per_subframe = 128; + + ms_audio_get_samples(msd, streamFile, bytes_per_packet, samples_per_frame, samples_per_subframe, 15); +} +void wmapro_get_samples(xma_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags) { + int bytes_per_packet = block_align; + int samples_per_frame = 0; + int samples_per_subframe = 0; + int bits_frame_size = 0; + + /* do some WMAPRO setup (code from ffmpeg) */ + + /* get samples per frame */ + { + int version = 3; + int frame_len_bits; + + if (sample_rate <= 16000) + frame_len_bits = 9; + else if (sample_rate <= 22050 || (sample_rate <= 32000 && version == 1)) + frame_len_bits = 10; + else if (sample_rate <= 48000 || version < 3) + frame_len_bits = 11; + else if (sample_rate <= 96000) + frame_len_bits = 12; + else + frame_len_bits = 13; + + if (version == 3) { + int tmp = decode_flags & 0x6; + if (tmp == 0x2) + ++frame_len_bits; + else if (tmp == 0x4) + --frame_len_bits; + else if (tmp == 0x6) + frame_len_bits -= 2; + } + + samples_per_frame = 1 << frame_len_bits; + } + + /* max bits needed to represent this block_align */ + bits_frame_size = floor(log(block_align) / log(2)) + 4; + + /* not really needed as I've never seen loop subframe data for WMA (probably possible though) + * (FFmpeg has code to get min_samples_per subframe) */ + samples_per_subframe = 0; + + /* signal it's not XMA */ + msd->xma_version = 0; + + ms_audio_get_samples(msd, streamFile, bytes_per_packet, samples_per_frame, samples_per_subframe, bits_frame_size); +} + + #endif diff --git a/src/meta/wwise.c b/src/meta/wwise.c index c76b49d4..e96db49f 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -25,11 +25,11 @@ typedef struct { int channels; int sample_rate; int block_align; - int bit_per_sample; + int average_bps; + int bits_per_sample; size_t extra_size; int loop_flag; - uint32_t num_samples; uint32_t loop_start_sample; uint32_t loop_end_sample; } wwise_header; @@ -51,7 +51,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */ (read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */ goto fail; - if ((read_32bitBE(0x08,streamFile) != 0x57415645)) /* "WAVE" */ + if ((read_32bitBE(0x08,streamFile) != 0x57415645) && /* "WAVE" */ + (read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */ goto fail; memset(&ww,0,sizeof(wwise_header)); @@ -68,7 +69,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { ww.file_size = streamFile->get_size(streamFile); #if 0 - /* sometimes uses a RIFF size that doesn't count chunk/sizes, or just wrong...? */ + /* sometimes uses a RIFF size that doesn't count chunk/sizes, LE value in RIFX, or just wrong...? */ if (4+4+read_32bit(0x04,streamFile) != ww.file_size) { VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", 4+4+read_32bit(0x04,streamFile), ww.file_size); goto fail; @@ -95,13 +96,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile); ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile); ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile); - /* 0x08: average samples per second */ + ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */ ww.block_align = (uint16_t)read_16bit(ww.fmt_offset+0x0c,streamFile); - ww.bit_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile); + ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile); if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */ ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,streamFile); #if 0 - /* channel bitmask, see AkSpeakerConfig.h (ex. 1ch uses FRONT_CENTER 0x4, 2ch FRONT_LEFT 0x1 | FRONT_RIGHT 0x2) */ + /* channel bitmask, see AkSpeakerConfig.h (ex. 1ch uses FRONT_CENTER 0x4, 2ch FRONT_LEFT 0x1 | FRONT_RIGHT 0x2, etc) */ if (ww.extra_size >= 6) ww.channel_config = read_32bit(ww.fmt_offset+0x14,streamFile); #endif @@ -121,17 +122,19 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } /* other Wwise specific: */ - //"JUNK": optional padding so that raw data starts in an offset multiple of 0x10 (0-size JUNK exists) + //"JUNK": optional padding so that raw data starts in an offset multiple of 0x10 (0-size JUNK exists too) //"akd ": unknown (IMA/PCM; "audiokinetic data"?) } /* format to codec */ switch(ww.format) { case 0x0001: ww.codec = PCM; break; /* older Wwise */ - case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM) */ + case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */ case 0x0011: ww.codec = IMA; break; /* older Wwise */ case 0x0069: ww.codec = IMA; break; /* older Wwise */ - case 0x0165: ww.codec = XMA2; break; + case 0x0161: ww.codec = XWMA; break; + case 0x0162: ww.codec = XWMA; break; + case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */ case 0x0166: ww.codec = XMA2; break; case 0xAAC0: ww.codec = AAC; break; case 0xFFF0: ww.codec = DSP; break; @@ -154,7 +157,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = ww.sample_rate; - vgmstream->num_samples = ww.num_samples; vgmstream->loop_start_sample = ww.loop_start_sample; vgmstream->loop_end_sample = ww.loop_end_sample; vgmstream->meta_type = meta_WWISE_RIFF; @@ -164,25 +166,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { switch(ww.codec) { case PCM: /* common */ /* normally riff.c has priority but it's needed when .wem is used */ - if (ww.bit_per_sample != 16) goto fail; + if (ww.bits_per_sample != 16) goto fail; vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); vgmstream->layout_type = ww.channels > 1 ? layout_interleave : layout_none; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = ww.data_size / ww.channels / (ww.bit_per_sample/8); + vgmstream->num_samples = pcm_bytes_to_samples(ww.data_size, ww.channels, ww.bits_per_sample); break; case IMA: /* common */ /* slightly modified MS-IMA. * Original research by hcs in ima_rejigger (https://github.com/hcs64/vgm_ripping/tree/master/demux/ima_rejigger5) */ #if 0 - if (ww.bit_per_sample != 4) goto fail; + if (ww.bits_per_sample != 4) goto fail; vgmstream->coding_type = coding_WWISE_IMA; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = ww.block_align; - vgmstream->num_samples = (ww.data_size / ww.block_align) * (ww.block_align - 4 * vgmstream->channels) * 2 /vgmstream->channels; + vgmstream->num_samples = (ww.data_size / ww.block_align) * (ww.block_align - 4 * vgmstream->channels) * 2 /vgmstream->channels;//todo ms_ima_bytes_to_samples break; #endif VGM_LOG("WWISE: IMA found (unsupported)\n"); @@ -201,7 +203,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { int setup_type = 0; /* 1: triad, 2 = inline codebooks, 3 = external codebooks, 4 = external aoTuV codebooks */ int blocksize_0_pow = 0, blocksize_1_pow = 0; - if (ww.block_align != 0 || ww.bit_per_sample != 0) goto fail; + if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* autodetect format */ if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/ @@ -259,7 +261,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { size_t wiih_size; int i; - if (ww.bit_per_sample != 4) goto fail; + if (ww.bits_per_sample != 4) goto fail; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; @@ -267,7 +269,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* find coef position */ if (find_chunk(streamFile, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH"*/ /* older Wwise */ - vgmstream->num_samples = ww.data_size / ww.channels / 8 * 14; + vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels); if (wiih_size != 0x2e * ww.channels) goto fail; } else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer Wwise */ @@ -295,17 +297,49 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { //"XMA2", "seek": same as the official ones //"XMAc": Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) + if (!ww.big_endian) goto fail; /* must be from Wwise X360 (real LE XMA(2)WAVEFORMAT/EX are parsed elsewhere) */ + VGM_LOG("WWISE: XMA2 found (unsupported)\n"); goto fail; } - case XWMA: /* X360 */ - VGM_LOG("WWISE: XWMA found (unsupported)\n"); - goto fail; + case XWMA: { /* X360 */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int bytes; + + if (!ww.big_endian) goto fail; /* must be from Wwise X360 (PC LE XWMA is parsed elsewhere) */ + + bytes = ffmpeg_make_riff_xwma(buf, 100, ww.format, ww.data_size, vgmstream->channels, vgmstream->sample_rate, ww.average_bps, ww.block_align); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, ww.data_offset,ww.data_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* manually find total samples, why don't they put this in the header is beyond me */ + if (ww.format == 0x0162) { /* WMAPRO */ + xma_sample_data msd; + memset(&msd,0,sizeof(xma_sample_data)); + + msd.channels = ww.channels; + msd.data_offset = ww.data_offset; + msd.data_size = ww.data_size; + wmapro_get_samples(&msd, streamFile, ww.block_align, ww.sample_rate,0x0000); + + vgmstream->num_samples = msd.num_samples; + } else { /* WMAv2 */ + vgmstream->num_samples = ffmpeg_data->totalSamples; //todo inaccurate approximation using the avg_bps + } + + break; + } case AAC: { /* iOS/Mac */ ffmpeg_codec_data * ffmpeg_data = NULL; - if (ww.block_align != 0 || ww.bit_per_sample != 0) goto fail; + if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* extra: size 0x12, unknown values */ @@ -322,18 +356,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case HEVAG: /* PSV */ /* changed values, another bizarre Wwise quirk */ //ww.block_align /* unknown (1ch=2, 2ch=4) */ - //ww.bit_per_sample; /* probably interleave (0x10) */ - //if (ww.bit_per_sample != 4) goto fail; + //ww.bits_per_sample; /* probably interleave (0x10) */ + //if (ww.bits_per_sample != 4) goto fail; if (ww.big_endian) goto fail; - /* extra_data: size 0x06, @0x00: samples per block (28), @0x04: channel config */ + /* extra_data: size 0x06, @0x00: samples per block (0x1c), @0x04: channel config */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = ww.data_size * 28 / 16 / ww.channels; + vgmstream->num_samples = ps_bytes_to_samples(ww.data_size, ww.channels); break; case ATRAC9: /* PSV/PS4 */ diff --git a/src/meta/xwb.c b/src/meta/xwb.c index 5565a033..105111a8 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -380,7 +380,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { 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); + bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, 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);