mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-15 11:07:40 +01:00
Added FSB multichannel IMA
Variation of the XBOX IMA used in >2ch FSB (interleaved header)
This commit is contained in:
parent
13bec0d5be
commit
6cd991ebf6
@ -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; 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;
|
||||
}
|
||||
|
@ -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"},
|
||||
|
@ -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;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