mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Add Cricket Audio .cks/ckb [Part Time UFO, Mega Man 1-6 (Android)]
Includes Cricket Audio's MSADPCM variation, also cleaned up MSADPCM code
This commit is contained in:
parent
094d95e4de
commit
a4f67bf077
@ -114,7 +114,8 @@ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);
|
||||
|
||||
/* msadpcm_decoder */
|
||||
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
|
||||
|
||||
/* yamaha_decoder */
|
||||
|
@ -1,17 +1,15 @@
|
||||
#include "../util.h"
|
||||
#include "coding.h"
|
||||
|
||||
/* used to compute next scale */
|
||||
static const int ADPCMTable[16] =
|
||||
{
|
||||
|
||||
static const int msadpcm_steps[16] = {
|
||||
230, 230, 230, 230,
|
||||
307, 409, 512, 614,
|
||||
768, 614, 512, 409,
|
||||
307, 230, 230, 230
|
||||
};
|
||||
|
||||
static const int ADPCMCoeffs[7][2] =
|
||||
{
|
||||
static const int msadpcm_coefs[7][2] = {
|
||||
{ 256, 0 },
|
||||
{ 512, -256 },
|
||||
{ 0, 0 },
|
||||
@ -23,138 +21,203 @@ static const int ADPCMCoeffs[7][2] =
|
||||
|
||||
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL *ch1,*ch2;
|
||||
int i;
|
||||
int framesin;
|
||||
STREAMFILE *streamfile;
|
||||
off_t offset;
|
||||
|
||||
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream);
|
||||
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream);
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
ch1 = &vgmstream->ch[0];
|
||||
ch2 = &vgmstream->ch[1];
|
||||
streamfile = ch1->streamfile;
|
||||
offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream);
|
||||
|
||||
/* external interleave (variable size), stereo */
|
||||
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
|
||||
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = ch1->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0];
|
||||
ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1];
|
||||
ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0];
|
||||
ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1];
|
||||
ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile);
|
||||
ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile);
|
||||
ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile);
|
||||
ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile);
|
||||
ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile);
|
||||
ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample==0) {
|
||||
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
|
||||
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
|
||||
ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0];
|
||||
ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1];
|
||||
ch1->adpcm_scale = read_16bitLE(offset+2,streamfile);
|
||||
ch2->adpcm_scale = read_16bitLE(offset+4,streamfile);
|
||||
ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile);
|
||||
ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile);
|
||||
ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile);
|
||||
ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile);
|
||||
|
||||
outbuf[0] = ch1->adpcm_history2_16;
|
||||
outbuf[1] = ch2->adpcm_history2_16;
|
||||
|
||||
outbuf+=2;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample==1 && samples_to_do > 0) {
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = ch1->adpcm_history1_16;
|
||||
outbuf[1] = ch2->adpcm_history1_16;
|
||||
|
||||
outbuf+=2;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
for (i=first_sample; i<first_sample+samples_to_do; i++) {
|
||||
int j;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int ch;
|
||||
|
||||
for (j=0;j<2;j++)
|
||||
{
|
||||
VGMSTREAMCHANNEL *ch = &vgmstream->ch[j];
|
||||
int sample_nibble =
|
||||
(j == 0 ?
|
||||
get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) :
|
||||
get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile))
|
||||
);
|
||||
int32_t hist1,hist2;
|
||||
int32_t predicted;
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch];
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (ch == 0) ? /* L = high nibble first */
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) :
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile));
|
||||
|
||||
hist1 = ch->adpcm_history1_16;
|
||||
hist2 = ch->adpcm_history2_16;
|
||||
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1];
|
||||
predicted /= 256;
|
||||
predicted += sample_nibble*ch->adpcm_scale;
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
ch->adpcm_history2_16 = ch->adpcm_history1_16;
|
||||
ch->adpcm_history1_16 = outbuf[0];
|
||||
ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] *
|
||||
ch->adpcm_scale) / 256;
|
||||
if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10;
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL *ch1;
|
||||
int i;
|
||||
int framesin;
|
||||
STREAMFILE *streamfile;
|
||||
off_t offset;
|
||||
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream);
|
||||
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream);
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
|
||||
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
ch1 = &vgmstream->ch[0];
|
||||
streamfile = ch1->streamfile;
|
||||
offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream);
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
if (first_sample==0) {
|
||||
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
|
||||
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
|
||||
ch1->adpcm_scale = read_16bitLE(offset+1,streamfile);
|
||||
ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile);
|
||||
ch1->adpcm_history2_16 = read_16bitLE(offset+5,streamfile);
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
outbuf[0] = ch1->adpcm_history2_16;
|
||||
|
||||
outbuf++;
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample==1 && samples_to_do > 0) {
|
||||
outbuf[0] = ch1->adpcm_history1_16;
|
||||
|
||||
outbuf++;
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
for (i=first_sample; i<first_sample+samples_to_do; i++) {
|
||||
{
|
||||
VGMSTREAMCHANNEL *ch = &vgmstream->ch[0];
|
||||
int sample_nibble =
|
||||
(i & 1 ?
|
||||
get_low_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) :
|
||||
get_high_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile))
|
||||
);
|
||||
int32_t hist1,hist2;
|
||||
int32_t predicted;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* high nibble first */
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = ch->adpcm_history1_16;
|
||||
hist2 = ch->adpcm_history2_16;
|
||||
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1];
|
||||
predicted /= 256;
|
||||
predicted += sample_nibble*ch->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
ch->adpcm_history2_16 = ch->adpcm_history1_16;
|
||||
ch->adpcm_history1_16 = outbuf[0];
|
||||
ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] *
|
||||
ch->adpcm_scale) / 256;
|
||||
if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10;
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order
|
||||
* (their tools may convert to float/others but internally it's all PCM16, from debugging). */
|
||||
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
|
||||
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */
|
||||
get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted >> 8; /* probably no difference vs MSADPCM */
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
|
||||
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
|
||||
|
||||
/* some extensions require external libraries and could be #ifdef, no really needed */
|
||||
/* some extensions require external libraries and could be #ifdef, not really needed */
|
||||
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
|
||||
|
||||
|
||||
@ -98,7 +98,9 @@ static const char* extension_list[] = {
|
||||
"ccc",
|
||||
"cd",
|
||||
"cfn", //fake extension/header id for .caf (to be removed)
|
||||
"ckb",
|
||||
"ckd",
|
||||
"cks",
|
||||
"cnk",
|
||||
"cps",
|
||||
"cvs",
|
||||
@ -543,6 +545,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
|
||||
|
||||
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
|
||||
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
|
||||
{coding_WS, "Westwood Studios VBR ADPCM"},
|
||||
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
|
||||
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
|
||||
@ -1045,6 +1048,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
|
||||
{meta_ASF, "Argonaut ASF header"},
|
||||
{meta_XMD, "Konami XMD header"},
|
||||
{meta_CKS, "Cricket Audio CKS header"},
|
||||
{meta_CKB, "Cricket Audio CKB header"},
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{meta_FFmpeg, "FFmpeg supported file format"},
|
||||
|
@ -356,6 +356,10 @@
|
||||
RelativePath=".\meta\capdsp.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ck.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\cstr.c"
|
||||
>
|
||||
|
@ -207,6 +207,7 @@
|
||||
<ClCompile Include="meta\brstm.c" />
|
||||
<ClCompile Include="meta\btsnd.c" />
|
||||
<ClCompile Include="meta\capdsp.c" />
|
||||
<ClCompile Include="meta\ck.c" />
|
||||
<ClCompile Include="meta\cstr.c" />
|
||||
<ClCompile Include="meta\dc_asd.c" />
|
||||
<ClCompile Include="meta\dc_dcsw_dcs.c" />
|
||||
|
@ -214,6 +214,9 @@
|
||||
<ClCompile Include="meta\capdsp.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ck.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\cstr.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
177
src/meta/ck.c
Normal file
177
src/meta/ck.c
Normal file
@ -0,0 +1,177 @@
|
||||
#include "meta.h"
|
||||
|
||||
|
||||
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
size_t block_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "cks"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
|
||||
goto fail;
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
codec = read_8bit(0x10,streamFile);
|
||||
channel_count = read_8bit(0x11,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitLE(0x12,streamFile);
|
||||
num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_16bitLE(0x18,streamFile);
|
||||
/* 0x1c(2): volume */
|
||||
/* 0x1e(2): pan */
|
||||
loop_start = read_32bitLE(0x20,streamFile);
|
||||
loop_end = read_32bitLE(0x24,streamFile);
|
||||
loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x2a(2): unused? */
|
||||
|
||||
start_offset = 0x2c;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->meta_type = meta_CKS;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
/* frame_size is always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, name_offset = 0;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
size_t block_size, stream_size;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "ckb"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
|
||||
goto fail;
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
/* 0x10: bank name (size 0x1c+1) */
|
||||
/* 0x30/34: reserved? */
|
||||
total_subsongs = read_32bitLE(0x38,streamFile);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
/* 0x3c: total_subsongs again? (ignored) */
|
||||
/* 0x40/44: unknown (ignored) */
|
||||
|
||||
/* get subsong (stream offset isn't given so must calc manually) */
|
||||
{
|
||||
int i;
|
||||
off_t header_offset = 0x48;
|
||||
off_t stream_offset = 0x48 + total_subsongs*0x48;
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
|
||||
codec = read_8bit(header_offset+0x20,streamFile);
|
||||
channel_count = read_8bit(header_offset+0x21,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile);
|
||||
num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_16bitLE(header_offset+0x28,streamFile);
|
||||
/* 0x2c(2): volume */
|
||||
/* 0x2e(2): pan */
|
||||
loop_start = read_32bitLE(header_offset+0x30,streamFile);
|
||||
loop_end = read_32bitLE(header_offset+0x34,streamFile);
|
||||
loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x3a(2): unused? */
|
||||
stream_size = read_32bitLE(header_offset+0x3c,streamFile);
|
||||
/* 0x40/44(4): unused? */
|
||||
|
||||
if (target_subsong == (i+1))
|
||||
break;
|
||||
header_offset += 0x48;
|
||||
stream_offset += stream_size;
|
||||
}
|
||||
|
||||
start_offset = stream_offset;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile);
|
||||
|
||||
vgmstream->meta_type = meta_CKB;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
/* frame_size is always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -767,4 +767,7 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -420,10 +420,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_ps2_ads_container,
|
||||
init_vgmstream_asf,
|
||||
init_vgmstream_xmd,
|
||||
init_vgmstream_cks,
|
||||
init_vgmstream_ckb,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_ffmpeg, /* should go at the end */
|
||||
init_vgmstream_ffmpeg, /* should go at the end (lowest priority) */
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -1099,7 +1101,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return 128;
|
||||
|
||||
case coding_MSADPCM:
|
||||
return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2/vgmstream->channels;
|
||||
return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
|
||||
case coding_MSADPCM_ck:
|
||||
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
|
||||
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
|
||||
return vgmstream->ws_output_size;
|
||||
case coding_AICA:
|
||||
@ -1269,6 +1273,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
return 0x4c*vgmstream->channels;
|
||||
|
||||
case coding_MSADPCM:
|
||||
case coding_MSADPCM_ck:
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_WS:
|
||||
return vgmstream->current_block_size;
|
||||
@ -1846,17 +1851,21 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
break;
|
||||
case coding_MSADPCM:
|
||||
if (vgmstream->channels == 2) {
|
||||
decode_msadpcm_stereo(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,
|
||||
decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels,
|
||||
vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
}
|
||||
else if (vgmstream->channels == 1)
|
||||
{
|
||||
decode_msadpcm_mono(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,
|
||||
vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
else if (vgmstream->channels == 1) {
|
||||
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do,0);
|
||||
}
|
||||
break;
|
||||
case coding_MSADPCM_ck:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do, chan);
|
||||
}
|
||||
break;
|
||||
case coding_AICA:
|
||||
@ -2224,6 +2233,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) {
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_MSADPCM:
|
||||
case coding_MSADPCM_ck:
|
||||
case coding_MS_IMA:
|
||||
case coding_MC3:
|
||||
case coding_WWISE_IMA:
|
||||
|
@ -141,7 +141,8 @@ typedef enum {
|
||||
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
|
||||
coding_UBI_IMA, /* Ubisoft IMA ADPCM */
|
||||
|
||||
coding_MSADPCM, /* Microsoft ADPCM */
|
||||
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
|
||||
coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */
|
||||
coding_WS, /* Westwood Studios VBR ADPCM */
|
||||
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
|
||||
coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */
|
||||
@ -685,6 +686,8 @@ typedef enum {
|
||||
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
|
||||
meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */
|
||||
meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */
|
||||
meta_CKS, /* Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
meta_CKB, /* Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
meta_FFmpeg,
|
||||
|
Loading…
x
Reference in New Issue
Block a user