mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-16 11:33:20 +01:00
commit
35824ae6bc
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
@ -304,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)
|
||||
@ -384,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
|
||||
@ -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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
off_t byte_offset = stream->offset + 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;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ static const char* extension_list[] = {
|
||||
"aifcl",
|
||||
//"aiff", //common
|
||||
"aix",
|
||||
"akb", //AAC
|
||||
"akb",
|
||||
"amts",
|
||||
"as4",
|
||||
"asd",
|
||||
@ -53,6 +53,12 @@ static const char* extension_list[] = {
|
||||
"bg00",
|
||||
"bgw",
|
||||
"bh2pcm",
|
||||
"bik",
|
||||
"bika",
|
||||
"bik2",
|
||||
"bik2a",
|
||||
"bk2",
|
||||
"bk2a",
|
||||
"bmdx",
|
||||
"bms",
|
||||
"bnk",
|
||||
@ -135,11 +141,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 +156,7 @@ static const char* extension_list[] = {
|
||||
"mic",
|
||||
"mihb",
|
||||
"mnstr",
|
||||
//"mp4", //common
|
||||
"mpdsp",
|
||||
"mpds",
|
||||
"msa",
|
||||
@ -170,6 +178,7 @@ static const char* extension_list[] = {
|
||||
"nus3bank",
|
||||
"nwa",
|
||||
|
||||
//"ogg", //common
|
||||
"oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
|
||||
"omu",
|
||||
"otm",
|
||||
@ -285,6 +294,7 @@ static const char* extension_list[] = {
|
||||
"wad",
|
||||
"wam",
|
||||
"was",
|
||||
//"wav", //common
|
||||
"wavm",
|
||||
"wb",
|
||||
"wii",
|
||||
@ -406,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"},
|
||||
|
@ -272,6 +272,10 @@
|
||||
RelativePath=".\meta\bgw.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\bik.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\bnsf.c"
|
||||
>
|
||||
|
@ -158,6 +158,7 @@
|
||||
<ClCompile Include="meta\ast.c" />
|
||||
<ClCompile Include="meta\baf.c" />
|
||||
<ClCompile Include="meta\bgw.c" />
|
||||
<ClCompile Include="meta\bik.c" />
|
||||
<ClCompile Include="meta\bnsf.c" />
|
||||
<ClCompile Include="meta\brstm.c" />
|
||||
<ClCompile Include="meta\btsnd.c" />
|
||||
|
@ -1030,5 +1030,8 @@
|
||||
<ClCompile Include="meta\dsp_adx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bik.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -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
|
||||
|
125
src/meta/bik.c
Normal file
125
src/meta/bik.c
Normal file
@ -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
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;c<ChannelCount;c++) {
|
||||
for (i=0;i<16;i++)
|
||||
{
|
||||
vgmstream->ch[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;i<ChannelCount;i++) {
|
||||
vgmstream->ch[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;
|
||||
|
||||
|
@ -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);
|
||||
|
100
src/meta/mp4.c
100
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
|
||||
|
@ -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;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;
|
||||
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
20
src/util.h
20
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
|
||||
|
||||
|
@ -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
|
||||
@ -1042,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;
|
||||
@ -1174,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;
|
||||
@ -1558,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;chan<vgmstream->channels;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;chan<vgmstream->channels;chan++) {
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user