From bcc60c3b64acdfebace0f205c2f70cbcd5b9b9fb Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 11:06:35 +0100 Subject: [PATCH 01/10] Added buffer logging debug util --- src/util.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/util.h b/src/util.h index 6ae18492..6d12377c 100644 --- a/src/util.h +++ b/src/util.h @@ -70,19 +70,31 @@ void concatn(int length, char * dst, const char * src); /* Simple stdout logging for debugging and regression testing purposes. * Needs C99 variadic macros. */ #ifdef VGM_DEBUG_OUTPUT - +/* equivalent to printf when condition is true */ #define VGM_ASSERT(condition, ...) \ do { if (condition) printf(__VA_ARGS__); } while (0) +/* equivalent to printf */ #define VGM_LOG(...) \ do { printf(__VA_ARGS__); } while (0) +/* prints file/line/func */ #define VGM_LOGF() \ do { printf("%s:%i '%s'\n", __FILE__, __LINE__, __func__); } while (0) - +/* prints a buffer/array */ +#define VGM_LOGB(buf, buf_size, bytes_per_line) \ + do { \ + int i; \ + for (i=0; i < buf_size; i++) { \ + printf("%02x",buf[i]); \ + if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \ + } \ + printf("\n"); \ + } while (0) #else -#define VGM_ASSERT(condition,fmt, ...) /* nothing */ +#define VGM_ASSERT(condition, ...) /* nothing */ #define VGM_LOG(...) /* nothing */ -#define VGM_LOGF(...) /* nothing */ +#define VGM_LOGF() /* nothing */ +#define VGM_LOGB(buf, buf_size, bytes_per_line) /* nothing */ #endif From bc49757f209d1f6c4d9ab3f8371e22bd2d1eec6e Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 11:10:02 +0100 Subject: [PATCH 02/10] Added/finished looping MP4 (.lmp4) [FFDimensions, Tales of Hearts R iOS] --- src/formats.c | 10 +++-- src/meta/mp4.c | 100 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/formats.c b/src/formats.c index 076509aa..c3cead9c 100644 --- a/src/formats.c +++ b/src/formats.c @@ -28,7 +28,7 @@ static const char* extension_list[] = { "aifcl", //"aiff", //common "aix", - "akb", //AAC + "akb", "amts", "as4", "asd", @@ -135,11 +135,12 @@ static const char* extension_list[] = { "kraw", "leg", - "logg", + "lmp4", //fake extension, for looping + "logg", //fake extension, for looping "lpcm", "lps", "lsf", - "lwav", + "lwav", //fake extension, for looping "matx", "mca", @@ -149,6 +150,7 @@ static const char* extension_list[] = { "mic", "mihb", "mnstr", + //"mp4", //common "mpdsp", "mpds", "msa", @@ -170,6 +172,7 @@ static const char* extension_list[] = { "nus3bank", "nwa", + //"ogg", //common "oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "omu", "otm", @@ -285,6 +288,7 @@ static const char* extension_list[] = { "wad", "wam", "was", + //"wav", //common "wavm", "wb", "wii", diff --git a/src/meta/mp4.c b/src/meta/mp4.c index 3ea6b8c5..2bbf3ca2 100644 --- a/src/meta/mp4.c +++ b/src/meta/mp4.c @@ -165,69 +165,92 @@ fail: #ifdef VGM_USE_FFMPEG +static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size); + + VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset = 0; int loop_flag = 0; int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; + size_t filesize; + off_t atom_offset; + size_t atom_size; + int is_ffdl = 0; ffmpeg_codec_data *ffmpeg_data = NULL; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if ( strcasecmp("mp4",filename_extension(filename)) - && strcasecmp("m4a",filename_extension(filename)) - && strcasecmp("m4v",filename_extension(filename)) - && strcasecmp("bin",filename_extension(filename)) ) /* Final Fantasy Dimensions iOS */ + /* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */ + if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin")) goto fail; + filesize = streamFile->get_size(streamFile); /* check header for Final Fantasy Dimensions */ - if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of FFD file) */ + if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of file) */ + is_ffdl = 1; if (read_32bitBE(0x04,streamFile) == 0x6D747873) { /* "mtxs" (bgm file) */ + /* this value is erratic so we'll use FFmpeg's num_samples + * (can be bigger = silence-padded, or smaller = cut; doesn't matter for looping though)*/ num_samples = read_32bitLE(0x08,streamFile); + /* loop samples are within num_samples, and don't have encoder delay (loop_start=0 starts from encoder_delay) */ loop_start_sample = read_32bitLE(0x0c,streamFile); loop_end_sample = read_32bitLE(0x10,streamFile); loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples); start_offset = 0x14; + + /* some FFDL have muxed streams ("FFDL" + "mtxs" data1 + mp4 data1 + "mtxs" data2 + mp4 data2 + etc) + * check if there is anything after the first mp4 data */ + if (!find_atom_be(streamFile, 0x6D646174, start_offset, &atom_offset, &atom_size)) goto fail; /* "mdat" */ + if (atom_offset-8 + atom_size < filesize && read_32bitBE(atom_offset-8 + atom_size,streamFile) == 0x6D747873) { /*"mtxs"*/ + VGM_LOG("FFDL: multiple streams found\n"); + filesize = atom_offset-8 + atom_size; /* clamp size, though FFmpeg will ignore the extra data anyway */ + } } else { - start_offset = 0x4; /* some SEs */ + start_offset = 0x4; /* some SEs contain "ftyp" after "FFDL" */ } - /* todo some FFDL have multi streams ("FFLD" + mtxsdata1 + mp4data1 + mtxsdata2 + mp4data2 + etc) */ } - /* check header */ - if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* size 0x00 + "ftyp" 0x04 */ + if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* atom size @0x00 + "ftyp" @0x04 */ goto fail; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile)); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, filesize); if ( !ffmpeg_data ) goto fail; + /* Tales of Hearts iOS has loop info in the first "free" atom */ + if (!is_ffdl && find_atom_be(streamFile, 0x66726565, start_offset, &atom_offset, &atom_size)) { /* "free" */ + if (read_32bitBE(atom_offset,streamFile) == 0x4F700002 + && (atom_size == 0x38 || atom_size == 0x40)) { /* make sure it's ToHr "free" */ + /* 0x00: id? 0x04/8: s_rate; 0x10: num_samples (without padding, same as FFmpeg's) */ + /* 0x14/18/1c: 0x238/250/278? 0x20: ? 0x24: start_pad */ + loop_flag = read_32bitBE(atom_offset+0x28,streamFile); + if (loop_flag) { /* atom ends if no loop flag */ + loop_start_sample = read_32bitBE(atom_offset+0x2c,streamFile); + loop_end_sample = read_32bitBE(atom_offset+0x30,streamFile); + } + } + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(ffmpeg_data->channels,loop_flag); if (!vgmstream) goto fail; - - vgmstream->num_samples = ffmpeg_data->totalSamples; /* todo FFD num_samples is different from this */ - vgmstream->sample_rate = ffmpeg_data->sampleRate; - vgmstream->channels = ffmpeg_data->channels; - if (loop_flag) { - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - } - + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; + vgmstream->num_samples = ffmpeg_data->totalSamples; + vgmstream->sample_rate = ffmpeg_data->sampleRate; + vgmstream->channels = ffmpeg_data->channels; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; return vgmstream; fail: - /* clean up anything we may have opened */ if (ffmpeg_data) { free_ffmpeg(ffmpeg_data); if (vgmstream) vgmstream->codec_data = NULL; @@ -236,4 +259,35 @@ fail: return NULL; } +/** + * Almost the same as streamfile.c's find_chunk but for "atom" chunks, which have chunk_size first because Apple. + * + * returns 0 on failure + */ +static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size) { + size_t filesize; + off_t current_atom = start_offset; + int full_atom_size = 1; + int size_big_endian = 1; + + filesize = get_streamfile_size(streamFile); + /* read chunks */ + while (current_atom < filesize) { + off_t chunk_size = size_big_endian ? + read_32bitBE(current_atom+0,streamFile) : + read_32bitLE(current_atom+0,streamFile); + uint32_t chunk_type = read_32bitBE(current_atom+4,streamFile); + + if (chunk_type == atom_id) { + if (out_atom_size) *out_atom_size = chunk_size; + if (out_atom_offset) *out_atom_offset = current_atom+8; + return 1; + } + + current_atom += full_atom_size ? chunk_size : 4+4+chunk_size; + } + + return 0; +} + #endif From 2aa025f2455123c446e920fb356c6a6735564544 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 13:00:13 +0100 Subject: [PATCH 03/10] Added BIK/BIKa 1/2 [various game movies] --- src/Makefile | 3 +- src/formats.c | 6 ++ src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/Makefile.unix.am | 1 + src/meta/bik.c | 125 +++++++++++++++++++++++++++++++ src/meta/meta.h | 2 + src/vgmstream.c | 1 + 9 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/meta/bik.c diff --git a/src/Makefile b/src/Makefile index e174a6b0..c5953670 100644 --- a/src/Makefile +++ b/src/Makefile @@ -310,7 +310,8 @@ META_OBJS=meta/adx_header.o \ meta/xma.o \ meta/ps2.o \ meta/x360.o \ - meta/dsp_adx.o + meta/dsp_adx.o \ + meta/bik.o EXT_LIBS = ../ext_libs/clHCA.o diff --git a/src/formats.c b/src/formats.c index c3cead9c..895af51a 100644 --- a/src/formats.c +++ b/src/formats.c @@ -53,6 +53,12 @@ static const char* extension_list[] = { "bg00", "bgw", "bh2pcm", + "bik", + "bika", + "bik2", + "bik2a", + "bk2", + "bk2a", "bmdx", "bms", "bnk", diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 2669456c..d34aa864 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -272,6 +272,10 @@ RelativePath=".\meta\bgw.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 384290c2..50c6c084 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -158,6 +158,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 48ea5587..3cbe7f84 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1030,5 +1030,8 @@ meta\Source Files + + meta\Source Files + \ No newline at end of file diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 3cb360ea..83d3a002 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -251,5 +251,6 @@ libmeta_la_SOURCES += xma.c libmeta_la_SOURCES += ps2.c libmeta_la_SOURCES += x360.c libmeta_la_SOURCES += dsp_adx.c +libmeta_la_SOURCES += bik.c EXTRA_DIST = meta.h diff --git a/src/meta/bik.c b/src/meta/bik.c new file mode 100644 index 00000000..82cc9b13 --- /dev/null +++ b/src/meta/bik.c @@ -0,0 +1,125 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + +#ifdef VGM_USE_FFMPEG + +static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample); + +/* BIK 1/2 - RAD Game Tools movies (audio/video format) */ +VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + ffmpeg_codec_data *data = NULL; + + /* check extension, case insensitive (bika = manually demuxed audio) */ + if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail; + + /* check header "BIK" (bik1) or "KB2" (bik2) (followed by version-char) */ + if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 && + (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; + + /* FFmpeg can parse BIK audio, but can't get the number of samples, which vgmstream needs. + * The only way to get them is to read all frame headers */ + data = init_ffmpeg_offset(streamFile, 0x0, get_streamfile_size(streamFile)); + if (!data) goto fail; + + vgmstream = allocate_vgmstream(data->channels, 0); /* alloc FFmpeg first to get channel count */ + if (!vgmstream) goto fail; + vgmstream->codec_data = data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_FFmpeg; + vgmstream->sample_rate = data->sampleRate; + + /* manually get num_samples since data->totalSamples is always 0 */ + vgmstream->num_samples = bik_get_num_samples(streamFile, data->bitsPerSample); + if (vgmstream->num_samples == 0) + goto fail; + + return vgmstream; + +fail: + free_ffmpeg(data); + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } + return NULL; +} + +/** + * Gets the number of samples in a BIK file by reading all frames' headers, + * as they are not in the main header. The header for BIK1 and 2 is the same. + * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) + * + * Needs bits per sample to calculate PCM samples, since most bink audio seems to use 32, actually. + */ +static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample) { + uint32_t *offsets = NULL; + uint32_t num_samples_b = 0; + off_t cur_offset; + size_t filesize; + int i, j, num_frames, num_tracks; + int target_stream = 0; + + filesize = get_streamfile_size(streamFile); + + num_frames = read_32bitLE(0x08,streamFile); + if (num_frames<=0) goto fail; + + /* multichannel audio is usually N tracks of stereo/mono, no way to know channel layout */ + num_tracks = read_32bitLE(0x28,streamFile); + if (num_tracks<=0 || num_tracks > 255) goto fail; + + /* find the frame index table, which is after 3 audio headers of size 4 for each track */ + cur_offset = 0x2c + num_tracks*4 * 3; + + /* read offsets in a buffer, to avoid fseeking to the table back and forth + * the number of frames can be highly variable so we'll alloc */ + offsets = malloc(sizeof(uint32_t) * num_frames); + for (i=0; i < num_frames; i++) { + offsets[i] = read_32bitLE(cur_offset,streamFile) & 0xFFFFFFFE; /* mask first bit (= keyframe) */ + cur_offset += 0x4; + + if (offsets[i] > filesize) goto fail; + } + /* after the last index is the file size, validate just in case */ + if (read_32bitLE(cur_offset,streamFile)!=filesize) goto fail; + + /* multistream support just for fun (FFmpeg should select the same target stream) + * (num_samples for other streams seem erratic though) */ + if (target_stream > num_tracks) goto fail; + if (target_stream == 0) target_stream = 1; + VGM_ASSERT(num_tracks > 1, "BIK: multiple streams found (%i entries)\n", num_tracks); + + /* read each frame header and sum all samples + * a frame has N audio packets with header (one per track) + video packet */ + for (i=0; i < num_frames; i++) { + cur_offset = offsets[i]; + + /* read audio packet headers */ + for (j=0; j < num_tracks; j++) { + uint32_t ap_size, samples_b; + ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ + samples_b = read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ + if (ap_size==0) break; /* no audio in this frame */ + + if (j == target_stream-1) { /* target samples found, read next frame */ + num_samples_b += samples_b; + break; + } else { /* check next audio packet */ + cur_offset += 4 + ap_size; /* todo sometimes ap_size doesn't include itself (+4), others it does? */ + } + } + } + + + free(offsets); + return num_samples_b / (bits_per_sample / 8); + +fail: + free(offsets); + return 0; +} + +#endif diff --git a/src/meta/meta.h b/src/meta/meta.h index 4a62af29..bec30eba 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -662,6 +662,8 @@ VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile); #ifdef VGM_USE_FFMPEG VGMSTREAM * init_vgmstream_xma(STREAMFILE* streamFile); + +VGMSTREAM * init_vgmstream_bik(STREAMFILE* streamFile); #endif VGMSTREAM * init_vgmstream_ps2_vds_vdm(STREAMFILE* streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index 0020a204..43d7e2ba 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -342,6 +342,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { #ifdef VGM_USE_FFMPEG init_vgmstream_xma, init_vgmstream_mp4_aac_ffmpeg, + init_vgmstream_bik, init_vgmstream_ffmpeg, /* should go at the end */ #endif From 13bec0d5be20b32285b3ca052c6c62398c629747 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 17:54:43 +0100 Subject: [PATCH 04/10] Add check in free_ffmpeg to avoid segfault if data is NULL free() accepts NULL so free_ffmpeg should too, I guess --- src/meta/ffmpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/meta/ffmpeg.c b/src/meta/ffmpeg.c index 6689d184..e06fb6a7 100644 --- a/src/meta/ffmpeg.c +++ b/src/meta/ffmpeg.c @@ -480,6 +480,9 @@ fail: void free_ffmpeg(ffmpeg_codec_data *data) { + if (data == NULL) + return; + if (data->lastReadPacket) { av_packet_unref(data->lastReadPacket); free(data->lastReadPacket); From 6cd991ebf60d405ff9cf02fdda817e595010af30 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 19:19:51 +0100 Subject: [PATCH 05/10] Added FSB multichannel IMA Variation of the XBOX IMA used in >2ch FSB (interleaved header) --- src/coding/ima_decoder.c | 64 +++++++++++++++++++++++++++++++++++++++- src/formats.c | 1 + src/vgmstream.c | 9 ++++++ src/vgmstream.h | 1 + 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 4fea2412..0456a566 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -34,6 +34,32 @@ static const int IMA_IndexTable[16] = }; +/** + * expands one nibble to one PCM sample, MS-IMA style + */ +static void ms_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + delta = step >> 3; + if (sample_nibble & 1) delta += step >> 2; + if (sample_nibble & 2) delta += step >> 1; + if (sample_nibble & 4) delta += step; + if (sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + + + void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; @@ -657,7 +683,7 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - //external interleave + //internal/byte interleave //no header @@ -695,3 +721,39 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } + +void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal interleave + int block_samples = (36 - 4) * 2; /* block size - header, 2 samples per byte */ + first_sample = first_sample % block_samples; + + //interleaved header (all hist per channel + all step_index per channel) + if (first_sample == 0) { + off_t hist_offset = stream->offset + 2*channel; + off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels; + + hist1 = read_16bitLE(hist_offset,stream->streamfile); + step_index = read_8bit(step_offset,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + } + + for (i=first_sample,sample_count=0; ioffset + 4*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel + int nibble_shift = (i&1?4:0); //low nibble first + + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + //internal interleave: increment offset on complete frame + if (i == block_samples) stream->offset += 36*vgmstream->channels; + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} diff --git a/src/formats.c b/src/formats.c index 895af51a..ae799f4f 100644 --- a/src/formats.c +++ b/src/formats.c @@ -416,6 +416,7 @@ static const coding_info coding_info_list[] = { {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, + {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, {coding_WS, "Westwood Studios ADPCM"}, {coding_ACM, "InterPlay ACM"}, {coding_NWA0, "NWA DPCM Level 0"}, diff --git a/src/vgmstream.c b/src/vgmstream.c index 43d7e2ba..35e28b15 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1043,6 +1043,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */ case coding_XBOX: case coding_INT_XBOX: + case coding_FSB_IMA: return 64; case coding_EA_XA: return 28; @@ -1175,6 +1176,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 14*vgmstream->channels; case coding_XBOX: case coding_INT_XBOX: + case coding_FSB_IMA: return 36; case coding_MAXIS_ADPCM: return 15*vgmstream->channels; @@ -1559,6 +1561,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; + case coding_FSB_IMA: + for (chan=0;chanchannels;chan++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; case coding_WS: for (chan=0;chanchannels;chan++) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 0449e0a8..d2a84c94 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -120,6 +120,7 @@ typedef enum { coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ + coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_MSADPCM, /* Microsoft ADPCM */ From b7ee1f8f02b84fac4ea4fe42ec22e0b3992990fe Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 19:37:00 +0100 Subject: [PATCH 06/10] Add todo/note in XBOX IMAs --- src/coding/ima_decoder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 0456a566..2646fe10 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -330,7 +330,7 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o //internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo) int block_samples = (vgmstream->channels==1) ? 32 : - 32*(vgmstream->channels&2); + 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % first_sample = first_sample % block_samples; //normal header (per channel) @@ -410,7 +410,7 @@ void decode_int_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample //semi-internal interleave (0x24 size), mixed channels (4 byte per ch)? int block_samples = (vgmstream->channels==1) ? 32 : - 32*(vgmstream->channels&2); + 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % first_sample = first_sample % block_samples; //normal header From b44cd87a7b35fdd35f684811c8cb445ccf9c4886 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 19:37:48 +0100 Subject: [PATCH 07/10] Fix multichannel IMA FSB --- src/meta/fsb.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/meta/fsb.c b/src/meta/fsb.c index b31441dc..fd713db5 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -315,27 +315,16 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { #endif } else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */ - if (fsbh.mode & FSOUND_IMAADPCMSTEREO) { /* noninterleaved, true stereo IMA */ - /* FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3) */ - vgmstream->coding_type = coding_MS_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x24*vgmstream->channels; - //VGM_LOG("FSB FSOUND_IMAADPCMSTEREO found\n"); - } else { - /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ - vgmstream->coding_type = coding_MS_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x24*vgmstream->channels; - //VGM_LOG("FSB FSOUND_IMAADPCM found\n"); -#if 0 - if (fsbh.numchannels > 2) { /* Blade Kitten 5.1 (interleaved header?) */ - vgmstream->coding_type = coding_XBOX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x12 * vgmstream->channels; - } -#endif + //VGM_ASSERT(fsbh.mode & FSOUND_IMAADPCMSTEREO, "FSB FSOUND_IMAADPCMSTEREO found\n"); + /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different + * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - } + /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ + vgmstream->coding_type = coding_XBOX; + vgmstream->layout_type = layout_none; + /* "interleaved header" IMA, which seems only used with >2ch (ex. Blade Kitten 5.1) */ + if (vgmstream->channels > 2) + vgmstream->coding_type = coding_FSB_IMA; } else if (fsbh.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis From cc181f5e0dc35013c0c5a977ff226e60a2b3ff30 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 23:17:56 +0100 Subject: [PATCH 08/10] Added FSB multichannel IMA --- src/coding/coding.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coding/coding.h b/src/coding/coding.h index 557daac5..96645c70 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -26,6 +26,7 @@ void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); From 3d9ce4c15e36c2eb2076e4609675d32817a77034 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 23:20:11 +0100 Subject: [PATCH 09/10] Fixed FSB5 sample rate and channels, multichannel IMA ADPCM; cleanup --- src/meta/fsb5.c | 99 +++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index 240bd55c..829fd2f3 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -5,8 +5,6 @@ /* FSB5 header */ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t StartOffset; int LoopFlag = 0; @@ -19,10 +17,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { int SampleHeaderStart, SampleHeaderLength, NameTableLength, SampleDataLength, CodingID, SampleMode; int ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; + int freq_mode, ch_mode; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("fsb",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"fsb")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */ if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */ @@ -39,23 +37,41 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { StartOffset = SampleHeaderLength + NameTableLength + 0x3C; SampleMode = read_32bitLE(SampleHeaderStart+0x00,streamFile); - if (SampleMode&0x02) - { - SampleRate = 48000; - } else { - SampleRate = 44100; + /* get sample rate */ + freq_mode = (SampleMode >> 1) & 0x0f; /* bits 5..1 */ + switch (freq_mode) { + case 0: SampleRate = 4000; break; //??? + case 1: SampleRate = 8000; break; + case 2: SampleRate = 11000; break; + case 3: SampleRate = 11025; break; + case 4: SampleRate = 16000; break; + case 5: SampleRate = 22050; break; + case 6: SampleRate = 24000; break; + case 7: SampleRate = 32000; break; + case 8: SampleRate = 44100; break; + case 9: SampleRate = 48000; break; + case 10: SampleRate = 96000; break; //??? + default: + SampleRate = 44100; + //goto fail; /* probably better? */ + break; } - if (SampleMode&0x20) - { - ChannelCount = 2; - } else { - ChannelCount = 1; + /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ + ch_mode = (SampleMode >> 5) & 0x03; /* bits 7..6 (maybe 8 too?) */ + switch (ch_mode) { + case 0: ChannelCount = 1; break; + case 1: ChannelCount = 2; break; + case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ + case 3: ChannelCount = 8; break;/* some IMA ADPCM */ + /* other values (ex. 10ch) seem specified in the extra flags */ + default: + goto fail; } - + + /* get extra flags */ ExtraFlagStart = SampleHeaderStart+0x08; - - if (SampleMode&0x01) + if (SampleMode&0x01) /* bit 0 */ { do { @@ -66,6 +82,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { switch(ExtraFlagType) { + case 0x01: /* Channel Info */ + { + ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile); + } + break; case 0x02: /* Sample Rate Info */ { SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile); @@ -99,8 +120,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->channels = ChannelCount; - vgmstream->sample_rate = SampleRate; + vgmstream->channels = ChannelCount; + vgmstream->sample_rate = SampleRate; switch (CodingID) @@ -166,26 +187,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; - /* DSP Coeffs */ - { - int c,i; - for (c=0;cch[c].adpcm_coef[i] = read_16bitBE(DSPInfoStart + c*0x2E + i*2,streamFile); - } - } - } + dsp_read_coefs_be(vgmstream,streamFile,DSPInfoStart,0x2E); } break; case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ { - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/4; vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_XBOX; - + if (vgmstream->channels > 2) /* multichannel FSB IMA (interleaved header) */ + vgmstream->coding_type = coding_FSB_IMA; } break; @@ -211,7 +223,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { { NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/2/ChannelCount; - #ifdef VGM_USE_MPEG +#ifdef VGM_USE_MPEG { mpeg_codec_data *mpeg_data = NULL; struct mpg123_frameinfo mi; @@ -269,27 +281,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { vgmstream->loop_end_sample = LoopEnd; } - /* 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;ich[i].streamfile = file; - - - if (vgmstream->coding_type == coding_XBOX) { - /* xbox interleaving is a little odd */ - vgmstream->ch[i].channel_start_offset=StartOffset; - } else { - vgmstream->ch[i].channel_start_offset= - StartOffset+vgmstream->interleave_block_size*i; - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset)) + goto fail; return vgmstream; From 5040b4746ed2a8fcbabe2419e6967052f58c76cb Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Feb 2017 23:26:13 +0100 Subject: [PATCH 10/10] Fix minor bugs; cleanup - loop flag parsed from the spec [TTT HD] - add joint stereo ATRAC3 [Silent Hill HD] - fix MSFv1 ATRAC3 sample rate [Digi World SP] - use "fake RIFF" ATRAC3 for flexibility (sample rate fix) --- src/meta/ps3_msf.c | 271 ++++++++++++++++++++------------------------- 1 file changed, 120 insertions(+), 151 deletions(-) diff --git a/src/meta/ps3_msf.c b/src/meta/ps3_msf.c index 169aab64..653fab9f 100644 --- a/src/meta/ps3_msf.c +++ b/src/meta/ps3_msf.c @@ -2,164 +2,149 @@ #include "../coding/coding.h" #include "../util.h" -/* MSF header */ +/* MSF - Sony's PS3 SDK format (MultiStream File) */ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset, header_offset = 0; - int32_t data_size, loop_start, loop_end; - int loop_flag = 0; - int channel_count; - int codec_id; - -#ifdef VGM_USE_FFMPEG - ffmpeg_codec_data *ffmpeg_data = NULL; -#endif + uint32_t data_size, loop_start = 0, loop_end = 0; + uint32_t id, codec_id, flags; + int loop_flag = 0, channel_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("msf",filename_extension(filename))) goto fail; - + if (!check_extensions(streamFile,"msf,at3")) goto fail; /* .at3: Silent Hill HD Collection */ /* "WMSF" variation with a mini header over the MSFC header, same extension */ if (read_32bitBE(0x00,streamFile) == 0x574D5346) { header_offset = 0x10; } - start_offset = header_offset+0x40; + start_offset = header_offset+0x40; /* MSF header is always 0x40 */ - /* usually 0x4D534643 "MSFC" */ - if (read_8bit(header_offset+0x0,streamFile) != 0x4D) goto fail; /* M */ - if (read_8bit(header_offset+0x1,streamFile) != 0x53) goto fail; /* S */ - if (read_8bit(header_offset+0x2,streamFile) != 0x46) goto fail; /* F */ + /* check header "MSF" + version-char + * usually "MSF\0\1", "MSF\0\2", "MSF5"(\3\5), "MSFC"(\4\3) (latest/common version) */ + id = read_32bitBE(header_offset+0x00,streamFile); + if ((id & 0xffffff00) != 0x4D534600) goto fail; + codec_id = read_32bitBE(header_offset+0x04,streamFile); + channel_count = read_32bitBE(header_offset+0x08,streamFile); + data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header */ + if (data_size == 0xFFFFFFFF) /* unneeded? */ + data_size = get_streamfile_size(streamFile) - start_offset; - data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header*/ - if (data_size==0xFFFFFFFF) { - size_t fileLength = get_streamfile_size(streamFile); - data_size = fileLength - start_offset; - } + /* byte flags, not in MSFv1 or v2 + * 0x01/02/04/08: loop marker 0/1/2/3 (requires flag 0x10) + * 0x10: "resample" loop option (may be active with no 0x01 flag set) + * 0x20: VBR MP3 + * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) + * 0x80+: (none/reserved) */ + flags = read_32bitBE(header_offset+0x14,streamFile); + /* sometimes loop_start/end is set but not flag 0x01, but from tests it only loops with 0x01 */ + loop_flag = flags != 0xffffffff && (flags & 0x10) && (flags & 0x01); - /* block_align/loop_type? = read_32bitBE(header_offset+0x14,streamFile);*/ /* 00/40 when no loop, 11/50/51/71 */ - - loop_start = read_32bitBE(header_offset+0x18,streamFile); - loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ - loop_flag = loop_start != 0xFFFFFFFF; + /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { - if (loop_end==0xFFFFFFFF) {/* not seen */ + loop_start = read_32bitBE(header_offset+0x18,streamFile); + loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ + loop_end = loop_start + loop_end; /* usually equals data_size but not always */ + if (loop_end > data_size)/* not seen */ loop_end = data_size; - } else { - loop_end = loop_start + loop_end; /* usually equals data_size but not always */ - if ( loop_end > data_size)/* not seen */ - loop_end = data_size; - } } - channel_count = read_32bitBE(header_offset+0x8,streamFile); - codec_id = read_32bitBE(header_offset+0x4,streamFile); - /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - - /* Sample rate hack for strange files that don't have a specified frequency */ - if (read_32bitBE(header_offset+0x10,streamFile)==0x00000000) + /* Sample rate hack for strange MSFv1 files that don't have a specified frequency */ + vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); + if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */ vgmstream->sample_rate = 48000; - else - vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); - vgmstream->meta_type = meta_PS3_MSF; switch (codec_id) { - case 0x0: /* PCM (Big Endian) */ - { - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = data_size/2/channel_count; - - if (loop_flag){ - vgmstream->loop_start_sample = loop_start/2/channel_count; - vgmstream->loop_end_sample = loop_end/2/channel_count; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else if (channel_count > 1) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - } - } - break; - case 0x3: /* PSx ADPCM */ - { - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = data_size*28/16/channel_count; + case 0x0: /* PCM (Big Endian) */ + case 0x1: { /* PCM (Little Endian) */ + vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 2; - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start*28/16/channel_count; - vgmstream->loop_end_sample = loop_end*28/16/channel_count; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else if (channel_count > 1) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - } + vgmstream->num_samples = data_size/2/channel_count; + if (loop_flag){ + vgmstream->loop_start_sample = loop_start/2/channel_count; + vgmstream->loop_end_sample = loop_end/2/channel_count; } + break; + } + + case 0x2: { /* PCM 32 (Float) */ + goto fail; //probably unused/spec only + } + + case 0x3: { /* PS ADPCM */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = data_size*28/16/channel_count; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start*28/16/channel_count; + vgmstream->loop_end_sample = loop_end*28/16/channel_count; + } + + break; + } + #ifdef VGM_USE_FFMPEG - case 0x4: /* ATRAC3 (frame size 96) */ - case 0x5: /* ATRAC3 (frame size 152) */ - case 0x6: /* ATRAC3 (frame size 192) */ - /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); - if ( !ffmpeg_data ) goto fail; + case 0x4: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) */ + case 0x5: /* ATRAC3 mid (105 kbps, frame size 152) */ + case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo, max_samples; + block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels; + encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2) + max_samples = (data_size / block_size) * samples_size; + joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */ + + if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ + vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks + + /* make a fake riff so FFmpeg can parse the ATRAC3 */ + bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; - vgmstream->num_samples = ffmpeg_data->totalSamples; - - if (loop_flag && ffmpeg_data->blockAlign > 0) { - vgmstream->loop_start_sample = (loop_start / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; - vgmstream->loop_end_sample = (loop_end / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; + vgmstream->num_samples = max_samples; + if (loop_flag) { + vgmstream->loop_start_sample = (loop_start / block_size) * samples_size; + vgmstream->loop_end_sample = (loop_end / block_size) * samples_size; } break; + } #endif #ifdef VGM_USE_FFMPEG - case 0x7: /* MPEG */ + case 0x7: { /* MPEG (LAME MP3 of any quality) */ /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); + ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); if ( !ffmpeg_data ) goto fail; - + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; - - /* TODO check CBR better (bitrate % X != 0?) */ - if (ffmpeg_data->bitrate == 0) - goto fail; /* vgmstream->num_samples = ffmpeg_data->totalSamples; */ /* duration may not be set/inaccurate */ vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { + //todo properly apply encoder delay, which seems to vary between 1152 (1f), 528, 576 or 528+576 int frame_size = ffmpeg_data->frameSize; vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size @@ -170,67 +155,51 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { } break; + } #endif #if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG) - case 0x7: /* MPEG */ - { - int frame_size = 576; /* todo incorrect looping calcs, MP3 can have other sizes */ + case 0x7: { /* MPEG (LAME MP3 of any quality) */ + int frame_size = 576; /* todo incorrect looping calcs */ - mpeg_codec_data *mpeg_data = NULL; - struct mpg123_frameinfo mi; - coding_t ct; + mpeg_codec_data *mpeg_data = NULL; + struct mpg123_frameinfo mi; + coding_t ct; - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; - if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; + if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; - vgmstream->coding_type = ct; - vgmstream->layout_type = layout_mpeg; - if (mi.vbr != MPG123_CBR) goto fail; - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi); - vgmstream->num_samples -= vgmstream->num_samples % frame_size; - if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); - vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); - vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; - } - vgmstream->interleave_block_size = 0; + vgmstream->coding_type = ct; + vgmstream->layout_type = layout_mpeg; + if (mi.vbr != MPG123_CBR) goto fail; + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi); + vgmstream->num_samples -= vgmstream->num_samples % frame_size; + if (loop_flag) { + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); + vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); + vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; } + vgmstream->interleave_block_size = 0; + break; + } #endif - default: + + default: /* 8+: not defined */ goto fail; } /* 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;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+vgmstream->interleave_block_size*i; - - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: -#ifdef VGM_USE_FFMPEG - if (ffmpeg_data) { - free_ffmpeg(ffmpeg_data); - if (vgmstream) vgmstream->codec_data = NULL; - } -#endif - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; }