mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
.caf (Apple Core Audio Format file), as seen on iPhone, using Apple's IMA
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@677 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
8101ace4ed
commit
81d4cbf919
@ -217,6 +217,7 @@ etc:
|
||||
- .afc (GC AFC ADPCM)
|
||||
- .ahx (MPEG-2 Layer II)
|
||||
- .aix (CRI ADX ADPCM)
|
||||
- .caf (Apple IMA4 ADPCM)
|
||||
- .bgw (FFXI PS-like ADPCM)
|
||||
- .de2 (MS ADPCM)
|
||||
- .kcey (EACS IMA ADPCM)
|
||||
|
@ -203,7 +203,8 @@ META_OBJS=meta/adx_header.o \
|
||||
meta/ps2_vgv.o \
|
||||
meta/ngc_gcub.o \
|
||||
meta/maxis_xa.o \
|
||||
meta/ngc_sck_dsp.o
|
||||
meta/ngc_sck_dsp.o \
|
||||
meta/apple_caff.o
|
||||
|
||||
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS)
|
||||
|
||||
|
@ -21,6 +21,9 @@ void decode_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
|
||||
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_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_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
@ -394,3 +394,53 @@ void decode_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
stream->adpcm_history1_32=hist1;
|
||||
stream->adpcm_step_index=step_index;
|
||||
}
|
||||
|
||||
void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i;
|
||||
|
||||
int32_t sample_count=0;
|
||||
int16_t hist1=stream->adpcm_history1_16;
|
||||
int step_index = stream->adpcm_step_index;
|
||||
|
||||
off_t packet_offset = stream->offset + first_sample/64*34;
|
||||
|
||||
first_sample = first_sample % 64;
|
||||
|
||||
if (first_sample == 0)
|
||||
{
|
||||
hist1 = (int16_t)((uint16_t)read_16bitBE(packet_offset,stream->streamfile) & 0xff80);
|
||||
step_index = read_8bit(packet_offset+1,stream->streamfile) & 0x7f;
|
||||
}
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int step = ADPCMTable[step_index];
|
||||
uint8_t sample_byte;
|
||||
int sample_nibble;
|
||||
int sample_decoded;
|
||||
int delta;
|
||||
|
||||
sample_byte = read_8bit(packet_offset+2+i/2,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> (i&1?4:0))&0xf;
|
||||
|
||||
sample_decoded = hist1;
|
||||
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&0x7];
|
||||
if (step_index < 0) step_index=0;
|
||||
if (step_index > 88) step_index=88;
|
||||
|
||||
outbuf[sample_count]=(short)(hist1);
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16=hist1;
|
||||
stream->adpcm_step_index=step_index;
|
||||
}
|
||||
|
@ -236,6 +236,10 @@
|
||||
RelativePath=".\meta\aix.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\apple_caff.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ast.c"
|
||||
>
|
||||
|
@ -163,5 +163,6 @@ libmeta_la_SOURCES += ps2_vgv.c
|
||||
libmeta_la_SOURCES += ngc_gcub.c
|
||||
libmeta_la_SOURCES += maxis_xa.c
|
||||
libmeta_la_SOURCES += ngc_sck_dsp.c
|
||||
libmeta_la_SOURCES += apple_caff.c
|
||||
|
||||
EXTRA_DIST = meta.h
|
||||
|
154
src/meta/apple_caff.c
Normal file
154
src/meta/apple_caff.c
Normal file
@ -0,0 +1,154 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* Apple Core Audio Format */
|
||||
|
||||
VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[260];
|
||||
|
||||
off_t start_offset;
|
||||
off_t data_size;
|
||||
off_t sample_count;
|
||||
off_t interleave;
|
||||
int sample_rate,unused_frames;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("caf",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check "caff" id */
|
||||
if (read_32bitBE(0,streamFile)!=0x63616666) goto fail;
|
||||
/* check version, flags */
|
||||
if (read_32bitBE(4,streamFile)!=0x00010000) goto fail;
|
||||
|
||||
off_t chunk_offset = 8;
|
||||
off_t file_length = (off_t)get_streamfile_size(streamFile);
|
||||
|
||||
int found_desc = 0, found_pakt = 0, found_data = 0;
|
||||
|
||||
while (chunk_offset < file_length)
|
||||
{
|
||||
/* high half of size (expect 0s) */
|
||||
if (read_32bitBE(chunk_offset+4,streamFile) != 0) goto fail;
|
||||
|
||||
/* handle chunk type */
|
||||
switch (read_32bitBE(chunk_offset,streamFile))
|
||||
{
|
||||
case 0x64657363: /* desc */
|
||||
found_desc = 1;
|
||||
{
|
||||
/* rather than put a convoluted conversion here for
|
||||
portability, just look it up */
|
||||
uint32_t sratefloat = read_32bitBE(chunk_offset+0x0c, streamFile);
|
||||
if (read_32bitBE(chunk_offset+0x10, streamFile) != 0) goto fail;
|
||||
switch (sratefloat)
|
||||
{
|
||||
case 0x40E58880:
|
||||
sample_rate = 44100;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t codec_4cc = read_32bitBE(chunk_offset+0x14, streamFile);
|
||||
/* only supporting ima4 for now */
|
||||
if (codec_4cc != 0x696d6134) goto fail;
|
||||
|
||||
/* format flags */
|
||||
if (read_32bitBE(chunk_offset+0x18, streamFile) != 0) goto fail;
|
||||
uint32_t bytes_per_packet = read_32bitBE(chunk_offset+0x1c, streamFile);
|
||||
uint32_t frames_per_packet = read_32bitBE(chunk_offset+0x20, streamFile);
|
||||
uint32_t channels_per_frame = read_32bitBE(chunk_offset+0x24, streamFile);
|
||||
uint32_t bits_per_channel = read_32bitBE(chunk_offset+0x28, streamFile);
|
||||
|
||||
interleave = bytes_per_packet / channels_per_frame;
|
||||
channel_count = channels_per_frame;
|
||||
if (channels_per_frame != 1 && channels_per_frame != 2)
|
||||
goto fail;
|
||||
/* ima4-specific */
|
||||
if (frames_per_packet != 64) goto fail;
|
||||
if ((frames_per_packet / 2 + 2) * channels_per_frame !=
|
||||
bytes_per_packet) goto fail;
|
||||
if (bits_per_channel != 0) goto fail;
|
||||
}
|
||||
break;
|
||||
case 0x70616b74: /* pakt */
|
||||
found_pakt = 1;
|
||||
/* 64-bit packet table size, 0 for constant bitrate */
|
||||
if (
|
||||
read_32bitBE(chunk_offset+0x0c,streamFile) != 0 ||
|
||||
read_32bitBE(chunk_offset+0x10,streamFile) != 0) goto fail;
|
||||
/* high half of valid frames count */
|
||||
if (read_32bitBE(chunk_offset+0x14,streamFile) != 0) goto fail;
|
||||
/* frame count */
|
||||
sample_count = read_32bitBE(chunk_offset+0x18,streamFile);
|
||||
/* priming frames */
|
||||
if (read_32bitBE(chunk_offset+0x1c,streamFile) != 0) goto fail;
|
||||
/* remainder (unused) frames */
|
||||
unused_frames = read_32bitBE(chunk_offset+0x20,streamFile);
|
||||
break;
|
||||
case 0x66726565: /* free */
|
||||
/* padding, ignore */
|
||||
break;
|
||||
case 0x64617461: /* data */
|
||||
if (read_32bitBE(chunk_offset+12,streamFile) != 1) goto fail;
|
||||
found_data = 1;
|
||||
start_offset = chunk_offset + 16;
|
||||
data_size = read_32bitBE(chunk_offset+8,streamFile) - 4;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* done with chunk */
|
||||
chunk_offset += 12 + read_32bitBE(chunk_offset+8,streamFile);
|
||||
}
|
||||
|
||||
if (!found_pakt || !found_desc || !found_data) goto fail;
|
||||
|
||||
/* ima4-specific */
|
||||
/* check for full packets */
|
||||
if (data_size % (interleave*channel_count) != 0) goto fail;
|
||||
if ((sample_count+unused_frames)%((interleave-2)*2) != 0) goto fail;
|
||||
/* check that all packets are accounted for */
|
||||
if (data_size/interleave/channel_count !=
|
||||
(sample_count+unused_frames)/((interleave-2)*2)) goto fail;
|
||||
|
||||
vgmstream = allocate_vgmstream(channel_count,0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = sample_count;
|
||||
/* ima4-specific */
|
||||
vgmstream->coding_type = coding_APPLE_IMA4;
|
||||
if (channel_count == 2)
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
else
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->meta_type = meta_CAFF;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].offset =
|
||||
vgmstream->ch[i].channel_start_offset =
|
||||
start_offset + interleave * i;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -405,4 +405,6 @@ VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngc_sck_dsp(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE * streamFile);
|
||||
|
||||
#endif
|
||||
|
@ -222,6 +222,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_ngc_gcub,
|
||||
init_vgmstream_maxis_xa,
|
||||
init_vgmstream_ngc_sck_dsp,
|
||||
init_vgmstream_apple_caff,
|
||||
};
|
||||
|
||||
#define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0]))
|
||||
@ -715,6 +716,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return vgmstream->ws_output_size;
|
||||
case coding_MSADPCM:
|
||||
return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2/vgmstream->channels;
|
||||
case coding_APPLE_IMA4:
|
||||
return 64;
|
||||
case coding_MS_IMA:
|
||||
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
|
||||
case coding_NDS_PROCYON:
|
||||
@ -793,6 +796,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_INT_DVI_IMA:
|
||||
case coding_AICA:
|
||||
return 1;
|
||||
case coding_APPLE_IMA4:
|
||||
return 34;
|
||||
case coding_MSADPCM:
|
||||
return vgmstream->interleave_block_size;
|
||||
default:
|
||||
@ -1048,6 +1053,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_APPLE_IMA4:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_apple_ima4(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_WS:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan,
|
||||
@ -1389,6 +1401,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case coding_MS_IMA:
|
||||
snprintf(temp,TEMPSIZE,"Microsoft 4-bit IMA ADPCM");
|
||||
break;
|
||||
case coding_APPLE_IMA4:
|
||||
snprintf(temp,TEMPSIZE,"Apple Quicktime 4-bit IMA ADPCM");
|
||||
break;
|
||||
case coding_WS:
|
||||
snprintf(temp,TEMPSIZE,"Westwood Studios DPCM");
|
||||
break;
|
||||
@ -2234,6 +2249,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case meta_NGC_SWD:
|
||||
snprintf(temp,TEMPSIZE,"PSF + Standard DSP Headers");
|
||||
break;
|
||||
case meta_CAFF:
|
||||
snprintf(temp,TEMPSIZE,"Apple Core Audio Format Header");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ typedef enum {
|
||||
coding_IMA, /* bare IMA, low nibble first */
|
||||
coding_INT_IMA, /* */
|
||||
coding_MS_IMA, /* Microsoft IMA */
|
||||
coding_APPLE_IMA4, /* Apple Quicktime IMA4 */
|
||||
coding_WS, /* Westwood Studios' custom VBR ADPCM */
|
||||
#ifdef VGM_USE_MPEG
|
||||
coding_fake_MPEG2_L2, /* MPEG-2 Layer 2 (AHX), with lying headers */
|
||||
@ -404,6 +405,7 @@ typedef enum {
|
||||
meta_NGC_GCUB, /* Sega Soccer Slam */
|
||||
meta_MAXIS_XA, /* Sim City 3000 (PC) */
|
||||
meta_NGC_SCK_DSP, /* Scorpion King (NGC) */
|
||||
meta_CAFF, /* iPhone .caf */
|
||||
} meta_t;
|
||||
|
||||
typedef struct {
|
||||
@ -647,4 +649,4 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length);
|
||||
* stereo stream. */
|
||||
void try_dual_file_stereo(VGMSTREAM * opened_stream, STREAMFILE *streamFile);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -182,6 +182,7 @@ gchar *vgmstream_exts [] = {
|
||||
"2dx",
|
||||
"adpcm",
|
||||
"hwas",
|
||||
"caf",
|
||||
/* terminator */
|
||||
NULL
|
||||
};
|
||||
|
@ -110,6 +110,7 @@ char * extension_list[] = {
|
||||
"bmdx\0BMDX Audio File (*.BMDX)\0",
|
||||
"brstm;brstmspm\0BRSTM Audio File (*.BRSTM)\0",
|
||||
|
||||
"caf\0CAF Audio File (*.CAF)\0",
|
||||
"ccc\0CCC Audio File (*.CCC)\0",
|
||||
"cfn\0CFN Audio File (*.CFN)\0",
|
||||
"cnk\0CNK Audio File (*.CNK)\0",
|
||||
|
Loading…
Reference in New Issue
Block a user