2014-06-02 02:19:05 +02:00
|
|
|
#include "../util.h"
|
|
|
|
#include "coding.h"
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
/**
|
|
|
|
* IMA ADPCM algorithms (expand one nibble to one sample, based on prev sample/history and step table).
|
|
|
|
* Nibbles are usually grouped in blocks/chunks, with a header, containing 1 or N channels
|
|
|
|
*
|
|
|
|
* All IMAs are mostly the same with these variations:
|
|
|
|
* - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels)
|
2017-10-27 18:56:34 +02:00
|
|
|
* - block header: none (external), normal (4 bytes of history 16b + step 8b + reserved 8b) or others; per channel/global
|
2018-02-24 22:30:17 +01:00
|
|
|
* - expand type: IMA style or variations; low or high nibble first
|
2017-01-25 20:19:13 +01:00
|
|
|
*/
|
|
|
|
|
2017-12-06 15:32:52 +01:00
|
|
|
static const int ADPCMTable[89] = {
|
2014-06-02 02:19:05 +02:00
|
|
|
7, 8, 9, 10, 11, 12, 13, 14,
|
|
|
|
16, 17, 19, 21, 23, 25, 28, 31,
|
|
|
|
34, 37, 41, 45, 50, 55, 60, 66,
|
|
|
|
73, 80, 88, 97, 107, 118, 130, 143,
|
|
|
|
157, 173, 190, 209, 230, 253, 279, 307,
|
|
|
|
337, 371, 408, 449, 494, 544, 598, 658,
|
|
|
|
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
|
|
|
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
|
|
|
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
|
|
|
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
|
|
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
|
|
|
32767
|
|
|
|
};
|
|
|
|
|
2017-12-06 15:32:52 +01:00
|
|
|
static const int IMA_IndexTable[16] = {
|
2014-06-02 02:19:05 +02:00
|
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
|
|
-1, -1, -1, -1, 2, 4, 6, 8
|
|
|
|
};
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */
|
2017-11-25 00:43:18 +01:00
|
|
|
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
|
2017-03-10 18:08:12 +01:00
|
|
|
int sample_nibble, sample_decoded, step, delta;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* simplified through math from:
|
|
|
|
* - diff = (code + 1/2) * (step / 4)
|
|
|
|
* > diff = ((step * nibble) + (step / 2)) / 4
|
|
|
|
* > diff = (step * nibble / 4) + (step / 8)
|
|
|
|
* final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */
|
2017-12-06 15:32:52 +01:00
|
|
|
|
|
|
|
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
|
|
|
|
sample_decoded = *hist1; /* predictor value */
|
|
|
|
step = ADPCMTable[*step_index]; /* current step */
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
delta = step >> 3;
|
|
|
|
if (sample_nibble & 1) delta += step >> 2;
|
|
|
|
if (sample_nibble & 2) delta += step >> 1;
|
|
|
|
if (sample_nibble & 4) delta += step;
|
2017-12-06 15:32:52 +01:00
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
2017-03-10 18:08:12 +01:00
|
|
|
|
2017-11-05 17:05:39 +01:00
|
|
|
*hist1 = clamp16(sample_decoded);
|
2017-03-10 18:08:12 +01:00
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
}
|
|
|
|
|
2017-12-06 15:32:52 +01:00
|
|
|
/* Apple's IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend?) */
|
2017-11-25 00:43:18 +01:00
|
|
|
static void std_ima_expand_nibble_16(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int16_t * hist1, int32_t * step_index) {
|
2017-02-04 19:19:51 +01:00
|
|
|
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];
|
2017-12-06 15:32:52 +01:00
|
|
|
|
2017-02-04 19:19:51 +01:00
|
|
|
delta = step >> 3;
|
|
|
|
if (sample_nibble & 1) delta += step >> 2;
|
|
|
|
if (sample_nibble & 2) delta += step >> 1;
|
|
|
|
if (sample_nibble & 4) delta += step;
|
2017-12-06 15:32:52 +01:00
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
*hist1 = clamp16(sample_decoded); /* no need for this, actually */
|
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Original IMA expansion, but using MULs rather than shift+ADDs (faster for newer processors).
|
|
|
|
* There is minor rounding difference between ADD and MUL expansions, noticeable/propagated in non-headered IMAs. */
|
|
|
|
static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
|
|
|
|
int sample_nibble, sample_decoded, step, delta;
|
|
|
|
|
|
|
|
/* simplified through math from:
|
|
|
|
* - diff = (code + 1/2) * (step / 4)
|
|
|
|
* > diff = (code + 1/2) * step) / 4) * (2 / 2)
|
|
|
|
* > diff = (code + 1/2) * 2 * step / 8
|
|
|
|
* final diff = [signed] ((code * 2 + 1) * step) / 8 */
|
|
|
|
|
|
|
|
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
|
|
|
|
sample_decoded = *hist1;
|
|
|
|
step = ADPCMTable[*step_index];
|
|
|
|
|
|
|
|
delta = (sample_nibble & 0x7);
|
|
|
|
delta = ((delta * 2 + 1) * step) >> 3;
|
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
|
|
|
|
|
|
|
*hist1 = clamp16(sample_decoded);
|
2017-02-04 19:19:51 +01:00
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* 3DS IMA (Mario Golf, Mario Tennis; maybe other Camelot games) */
|
|
|
|
static void n3ds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
|
2017-03-10 18:08:12 +01:00
|
|
|
int sample_nibble, sample_decoded, step, delta;
|
|
|
|
|
|
|
|
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
|
|
|
|
sample_decoded = *hist1;
|
2017-12-06 15:32:52 +01:00
|
|
|
step = ADPCMTable[*step_index];
|
2017-11-24 21:23:01 +01:00
|
|
|
|
|
|
|
sample_decoded = sample_decoded << 3;
|
2018-02-23 23:01:44 +01:00
|
|
|
delta = (sample_nibble & 0x07);
|
|
|
|
delta = step * delta * 2 + step; /* custom */
|
2017-12-06 15:32:52 +01:00
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
2017-11-24 21:23:01 +01:00
|
|
|
sample_decoded = sample_decoded >> 3;
|
2017-03-10 18:08:12 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
*hist1 = clamp16(sample_decoded);
|
2017-03-10 18:08:12 +01:00
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
}
|
|
|
|
|
2017-12-06 15:32:52 +01:00
|
|
|
/* The Incredibles PC, updates step_index before doing current sample */
|
2017-03-09 21:47:23 +01:00
|
|
|
static void snds_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;
|
2017-12-06 15:32:52 +01:00
|
|
|
sample_decoded = *hist1;
|
2017-03-09 21:47:23 +01:00
|
|
|
|
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
|
|
|
|
step = ADPCMTable[*step_index];
|
2017-12-06 15:32:52 +01:00
|
|
|
|
|
|
|
delta = (sample_nibble & 7) * step / 4 + step / 8; /* standard IMA */
|
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
2017-03-09 21:47:23 +01:00
|
|
|
|
|
|
|
*hist1 = clamp16(sample_decoded);
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:10:47 +02:00
|
|
|
/* Omikron: The Nomad Soul, algorithm from the .exe */
|
2017-03-09 21:47:23 +01:00
|
|
|
static void otns_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];
|
2017-12-06 15:32:52 +01:00
|
|
|
|
2017-03-09 21:47:23 +01:00
|
|
|
delta = 0;
|
2018-09-06 18:10:47 +02:00
|
|
|
if(sample_nibble & 4) delta = step * 4;
|
|
|
|
if(sample_nibble & 2) delta += step * 2;
|
2017-03-09 21:47:23 +01:00
|
|
|
if(sample_nibble & 1) delta += step;
|
|
|
|
delta >>= 2;
|
2017-12-06 15:32:52 +01:00
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
sample_decoded += delta;
|
2017-03-09 21:47:23 +01:00
|
|
|
|
|
|
|
*hist1 = clamp16(sample_decoded);
|
|
|
|
*step_index += IMA_IndexTable[sample_nibble];
|
|
|
|
if (*step_index < 0) *step_index=0;
|
|
|
|
if (*step_index > 88) *step_index=88;
|
|
|
|
}
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2018-08-02 18:22:01 +02:00
|
|
|
/* Fairly OddParents (PC) .WV6: minor variation, reverse engineered from the .exe */
|
|
|
|
static void wv6_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 = (sample_nibble & 0x7);
|
|
|
|
delta = ((delta * step) >> 3) + ((delta * step) >> 2);
|
|
|
|
if (sample_nibble & 8) delta = -delta;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* ************************************ */
|
|
|
|
/* DVI/IMA */
|
|
|
|
/* ************************************ */
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* Standard DVI/IMA ADPCM (as in, ADPCM recommended by the IMA using Intel/DVI's implementation).
|
|
|
|
* Configurable: stereo or mono/interleave nibbles, and high or low nibble first.
|
|
|
|
* For vgmstream, low nibble is called "IMA ADPCM" and high nibble is "DVI IMA ADPCM" (same thing though). */
|
|
|
|
void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first) {
|
|
|
|
int i, sample_count = 0;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* external interleave */
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* no header (external setup), pre-clamp for wrong values */
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* decode nibbles (layout: varies) */
|
2017-11-24 21:23:01 +01:00
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
|
|
|
|
off_t byte_offset = is_stereo ?
|
|
|
|
stream->offset + i : /* stereo: one nibble per channel */
|
|
|
|
stream->offset + i/2; /* mono: consecutive nibbles */
|
|
|
|
int nibble_shift = is_high_first ?
|
|
|
|
is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */
|
|
|
|
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
2014-06-02 02:19:05 +02:00
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-01-25 20:19:13 +01:00
|
|
|
int i, sample_count;
|
2017-11-24 21:23:01 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
//external interleave
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//no header
|
2014-06-02 02:19:05 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); //low nibble order
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
n3ds_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
2014-06-02 02:19:05 +02:00
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
2017-01-25 20:19:13 +01:00
|
|
|
int i, sample_count;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//external interleave
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//no header
|
2014-06-02 02:19:05 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + i;//one nibble per channel
|
|
|
|
int nibble_shift = (channel==0?0:4); //high nibble first, based on channel
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
snds_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
2017-01-25 20:19:13 +01:00
|
|
|
int i, sample_count;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//internal/byte interleave
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//no header
|
2014-06-02 02:19:05 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + (vgmstream->channels==1 ? i/2 : i); //one nibble per channel if stereo
|
|
|
|
int nibble_shift = (vgmstream->channels==1) ? //todo simplify
|
|
|
|
(i&1?0:4) : //high nibble first(?)
|
|
|
|
(channel==0?4:0); //low=ch0, high=ch1 (this is correct compared to vids)
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
otns_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-08-02 18:22:01 +02:00
|
|
|
/* WV6 IMA, DVI IMA with custom nibble expand */
|
|
|
|
void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
|
|
|
int i, sample_count;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
|
|
|
//external interleave
|
|
|
|
|
|
|
|
//no header
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
off_t byte_offset = stream->offset + i/2;
|
|
|
|
int nibble_shift = (i&1?0:4); //high nibble first
|
|
|
|
|
|
|
|
wv6_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
/* ************************************ */
|
2018-02-17 17:23:52 +01:00
|
|
|
/* MS-IMA */
|
2017-11-24 21:23:01 +01:00
|
|
|
/* ************************************ */
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* IMA with custom frame sizes, header and nibble layout. Outputs an odd number of samples per frame,
|
|
|
|
* so to simplify calcs this decodes full frames, thus hist doesn't need to be mantained.
|
2018-02-17 17:23:52 +01:00
|
|
|
* Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */
|
2018-02-17 18:19:28 +01:00
|
|
|
void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
2018-02-17 17:23:52 +01:00
|
|
|
int i, samples_read = 0, samples_done = 0, max_samples;
|
|
|
|
int32_t hist1;// = stream->adpcm_history1_32;
|
|
|
|
int step_index;// = stream->adpcm_step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* internal interleave (configurable size), mixed channels */
|
2018-02-17 17:23:52 +01:00
|
|
|
int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
|
2014-06-02 02:19:05 +02:00
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* normal header (hist+step+reserved), per channel */
|
|
|
|
{ //if (first_sample == 0) {
|
2018-02-17 17:23:52 +01:00
|
|
|
off_t header_offset = stream->offset + 0x04*channel;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
|
|
|
|
step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */
|
|
|
|
if (step_index < 0) step_index = 0;
|
|
|
|
if (step_index > 88) step_index = 88;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* write header sample (odd samples per block) */
|
2018-02-17 17:23:52 +01:00
|
|
|
if (samples_read >= first_sample && samples_done < samples_to_do) {
|
|
|
|
outbuf[samples_done * channelspacing] = (short)hist1;
|
|
|
|
samples_done++;
|
|
|
|
}
|
|
|
|
samples_read++;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
max_samples = (block_samples - samples_read);
|
|
|
|
if (max_samples > samples_to_do + first_sample - samples_done)
|
|
|
|
max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */
|
2018-02-17 17:23:52 +01:00
|
|
|
for (i = 0; i < max_samples; i++) {
|
|
|
|
off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2;
|
|
|
|
int nibble_shift = (i&1?4:0); /* low nibble first */
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); /* original expand */
|
2018-02-17 17:23:52 +01:00
|
|
|
|
|
|
|
if (samples_read >= first_sample && samples_done < samples_to_do) {
|
|
|
|
outbuf[samples_done * channelspacing] = (short)(hist1);
|
|
|
|
samples_done++;
|
|
|
|
}
|
|
|
|
samples_read++;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
/* internal interleave: increment offset on complete frame */
|
|
|
|
if (first_sample + samples_done == block_samples) {
|
|
|
|
stream->offset += vgmstream->interleave_block_size;
|
|
|
|
}
|
2017-11-24 21:23:01 +01:00
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
//stream->adpcm_history1_32 = hist1;
|
|
|
|
//stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* Reflection's MS-IMA with custom nibble layout (some info from XA2WAV by Deniz Oezmen) */
|
2018-02-17 18:19:28 +01:00
|
|
|
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
|
|
|
int i, samples_read = 0, samples_done = 0, max_samples;
|
|
|
|
int32_t hist1;// = stream->adpcm_history1_32;
|
|
|
|
int step_index;// = stream->adpcm_step_index;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* internal interleave (configurable size), mixed channels */
|
2018-02-17 18:19:28 +01:00
|
|
|
int block_channel_size = (vgmstream->interleave_block_size - 0x04*vgmstream->channels) / vgmstream->channels;
|
|
|
|
int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
|
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* normal header (hist+step+reserved), per channel */
|
|
|
|
{ //if (first_sample == 0) {
|
2018-02-17 18:19:28 +01:00
|
|
|
off_t header_offset = stream->offset + 0x04*channel;
|
|
|
|
|
|
|
|
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
|
2018-02-23 23:01:44 +01:00
|
|
|
step_index = read_8bit(header_offset+0x02,stream->streamfile);
|
2018-02-17 18:19:28 +01:00
|
|
|
if (step_index < 0) step_index = 0;
|
|
|
|
if (step_index > 88) step_index = 88;
|
|
|
|
|
|
|
|
/* write header sample */
|
|
|
|
if (samples_read >= first_sample && samples_done < samples_to_do) {
|
|
|
|
outbuf[samples_done * channelspacing] = (short)hist1;
|
|
|
|
samples_done++;
|
|
|
|
}
|
|
|
|
samples_read++;
|
|
|
|
}
|
|
|
|
|
|
|
|
max_samples = (block_samples - samples_read);
|
|
|
|
if (max_samples > samples_to_do + first_sample - samples_done)
|
|
|
|
max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */
|
|
|
|
|
|
|
|
/* decode nibbles (layout: all nibbles from one channel, then other channels) */
|
|
|
|
for (i = 0; i < max_samples; i++) {
|
|
|
|
off_t byte_offset = stream->offset + 0x04*vgmstream->channels + block_channel_size*channel + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); /* low nibble first */
|
|
|
|
|
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
|
|
|
|
|
|
|
if (samples_read >= first_sample && samples_done < samples_to_do) {
|
|
|
|
outbuf[samples_done * channelspacing] = (short)(hist1);
|
|
|
|
samples_done++;
|
|
|
|
}
|
|
|
|
samples_read++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* internal interleave: increment offset on complete frame */
|
|
|
|
if (first_sample + samples_done == block_samples) {
|
|
|
|
stream->offset += vgmstream->interleave_block_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
//stream->adpcm_history1_32 = hist1;
|
|
|
|
//stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
/* ************************************ */
|
|
|
|
/* XBOX-IMA */
|
|
|
|
/* ************************************ */
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* MS-IMA with fixed frame size, and outputs an even number of samples per frame (skips last nibble).
|
2018-09-04 17:10:41 +02:00
|
|
|
* Defined in Xbox's SDK. Usable in mono or stereo modes (both suitable for interleaved multichannel). */
|
|
|
|
void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
|
|
|
int i, frames_in, sample_pos = 0, block_samples, frame_size;
|
2017-01-25 20:19:13 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
2018-09-04 17:10:41 +02:00
|
|
|
off_t frame_offset;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-09-04 17:10:41 +02:00
|
|
|
/* external interleave (fixed size), stereo/mono */
|
|
|
|
block_samples = (0x24 - 0x4) * 2;
|
|
|
|
frames_in = first_sample / block_samples;
|
2017-01-25 20:19:13 +01:00
|
|
|
first_sample = first_sample % block_samples;
|
2018-09-04 17:10:41 +02:00
|
|
|
frame_size = is_stereo ? 0x24*2 : 0x24;
|
|
|
|
|
|
|
|
frame_offset = stream->offset + frame_size*frames_in;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-09-04 17:10:41 +02:00
|
|
|
/* normal header (hist+step+reserved), stereo/mono */
|
2017-01-25 20:19:13 +01:00
|
|
|
if (first_sample == 0) {
|
2018-09-04 17:10:41 +02:00
|
|
|
off_t header_offset = is_stereo ?
|
|
|
|
frame_offset + 0x04*(channel % 2) :
|
|
|
|
frame_offset + 0x00;
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2018-02-24 22:30:17 +01:00
|
|
|
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
|
|
|
|
step_index = read_8bit(header_offset+0x02,stream->streamfile);
|
2014-06-02 02:19:05 +02:00
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
|
|
|
|
2018-02-24 22:30:17 +01:00
|
|
|
/* write header sample (even samples per block, skips last nibble) */
|
2018-09-04 17:10:41 +02:00
|
|
|
outbuf[sample_pos] = (short)(hist1);
|
|
|
|
sample_pos += channelspacing;
|
2018-02-24 22:30:17 +01:00
|
|
|
first_sample += 1;
|
|
|
|
samples_to_do -= 1;
|
|
|
|
}
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2018-09-04 17:10:41 +02:00
|
|
|
/* decode nibbles (layout: straight in mono or 4 bytes per channel in stereo) */
|
2018-02-24 22:30:17 +01:00
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
2018-09-04 17:10:41 +02:00
|
|
|
off_t byte_offset = is_stereo ?
|
|
|
|
frame_offset + 0x04*2 + 0x04*(channel % 2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2 :
|
|
|
|
frame_offset + 0x04 + (i-1)/2;
|
|
|
|
int nibble_shift = (!((i-1)&1) ? 0:4); /* low first */
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2018-09-04 17:10:41 +02:00
|
|
|
/* must skip last nibble per spec, rarely needed though (ex. Gauntlet Dark Legacy) */
|
2018-02-24 22:30:17 +01:00
|
|
|
if (i < block_samples) {
|
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2018-09-04 17:10:41 +02:00
|
|
|
outbuf[sample_pos] = (short)(hist1);
|
|
|
|
sample_pos += channelspacing;
|
2018-02-24 22:30:17 +01:00
|
|
|
}
|
2017-01-25 20:19:13 +01:00
|
|
|
}
|
|
|
|
|
2018-03-23 21:21:44 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multichannel XBOX-IMA ADPCM, with all channels mixed in the same block (equivalent to multichannel MS-IMA; seen in .rsd XADP). */
|
|
|
|
void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
|
|
|
int i, sample_count = 0, num_frame;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
|
|
|
/* external interleave (fixed size), multichannel */
|
|
|
|
int block_samples = (0x24 - 0x4) * 2;
|
|
|
|
num_frame = first_sample / block_samples;
|
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
|
|
|
/* normal header (hist+step+reserved), multichannel */
|
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset + 0x24*channelspacing*num_frame + 0x04*channel;
|
|
|
|
|
|
|
|
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
|
|
|
|
step_index = read_8bit(header_offset+0x02,stream->streamfile);
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
|
|
|
|
|
|
|
/* write header sample (even samples per block, skips last nibble) */
|
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
sample_count += channelspacing;
|
|
|
|
first_sample += 1;
|
|
|
|
samples_to_do -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
|
|
|
off_t byte_offset = (stream->offset + 0x24*channelspacing*num_frame + 0x04*channelspacing) + 0x04*channel + 0x04*channelspacing*((i-1)/8) + ((i-1)%8)/2;
|
|
|
|
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
|
|
|
|
|
|
|
|
/* must skip last nibble per spec, rarely needed though */
|
|
|
|
if (i < block_samples) {
|
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
sample_count += channelspacing;
|
|
|
|
}
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* Similar to MS-IMA with even number of samples, header sample is not written (setup only).
|
|
|
|
* Apparently clamps to -32767 unlike standard's -32768 (probably not noticeable).
|
|
|
|
* Info here: http://problemkaputt.de/gbatek.htm#dssoundnotes */
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
|
|
|
int i, sample_count;
|
2018-02-23 23:01:44 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* external interleave (configurable size), mono */
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* normal header (hist+step+reserved), single channel */
|
2017-11-24 21:23:01 +01:00
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset;
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
hist1 = read_16bitLE(header_offset,stream->streamfile);
|
|
|
|
step_index = read_16bitLE(header_offset+2,stream->streamfile);
|
2018-02-23 23:01:44 +01:00
|
|
|
if (step_index < 0) step_index=0; /* probably pre-adjusted */
|
|
|
|
if (step_index > 88) step_index=88;
|
2017-11-24 21:23:01 +01:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:17:13 +01:00
|
|
|
/* decode nibbles (layout: all nibbles from the channel) */
|
2017-11-24 21:23:01 +01:00
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2018-02-23 23:01:44 +01:00
|
|
|
off_t byte_offset = stream->offset + 0x04 + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); /* low nibble first */
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
//todo waveform has minor deviations using known expands
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-11-18 22:25:44 +01:00
|
|
|
int i, sample_count;
|
2017-11-24 21:23:01 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16?
|
2017-11-18 22:25:44 +01:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
|
|
|
//external interleave
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//normal header
|
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset;
|
|
|
|
|
|
|
|
hist1 = read_16bitLE(header_offset,stream->streamfile);
|
|
|
|
step_index = read_8bit(header_offset+2,stream->streamfile);
|
|
|
|
|
|
|
|
//todo clip step_index?
|
|
|
|
}
|
2017-11-18 22:25:44 +01:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + 4 + i/2;
|
|
|
|
int nibble_shift = (i&1?0:4); //high nibble first
|
2017-11-18 22:25:44 +01:00
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
}
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
stream->adpcm_history1_16 = hist1;
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_rad_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;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//internal interleave (configurable size), mixed channels (4 byte per ch)
|
|
|
|
int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels;
|
2017-01-25 20:19:13 +01:00
|
|
|
first_sample = first_sample % block_samples;
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//inverted header (per channel)
|
2017-01-25 20:19:13 +01:00
|
|
|
if (first_sample == 0) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t header_offset = stream->offset + 4*channel;
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
step_index = read_16bitLE(header_offset,stream->streamfile);
|
|
|
|
hist1 = read_16bitLE(header_offset+2,stream->streamfile);
|
2017-10-27 18:56:34 +02:00
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + 4*vgmstream->channels + channel + i/2*vgmstream->channels;
|
2017-03-09 21:47:23 +01:00
|
|
|
int nibble_shift = (i&1?4:0); //low nibble first
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//internal interleave: increment offset on complete frame
|
|
|
|
if (i == block_samples) stream->offset += vgmstream->interleave_block_size;
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-01-25 20:19:13 +01:00
|
|
|
int i, sample_count;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
2014-06-02 02:19:05 +02:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//semi-external interleave?
|
|
|
|
int block_samples = 0x14 * 2;
|
|
|
|
first_sample = first_sample % block_samples;
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//inverted header
|
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset;
|
|
|
|
|
|
|
|
step_index = read_16bitLE(header_offset,stream->streamfile);
|
|
|
|
hist1 = read_16bitLE(header_offset+2,stream->streamfile);
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
|
|
|
}
|
2017-01-25 20:19:13 +01:00
|
|
|
|
2014-06-02 02:19:05 +02:00
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = stream->offset + 4 + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); //low nibble first
|
2014-06-02 02:19:05 +02:00
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:19:13 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
|
|
|
|
2017-01-25 20:19:13 +01:00
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
2014-06-02 02:19:05 +02:00
|
|
|
}
|
2017-01-25 20:25:39 +01:00
|
|
|
|
2018-03-23 21:21:44 +01:00
|
|
|
/* Apple's IMA4, a.k.a QuickTime IMA. 2 byte header and header sample is not written (setup only). */
|
2017-11-24 21:23:01 +01:00
|
|
|
void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
|
|
|
int i, sample_count, num_frame;
|
|
|
|
int16_t hist1 = stream->adpcm_history1_16;//todo unneeded 16?
|
2017-01-25 20:25:39 +01:00
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//external interleave
|
|
|
|
int block_samples = (0x22 - 0x2) * 2;
|
|
|
|
num_frame = first_sample / block_samples;
|
|
|
|
first_sample = first_sample % block_samples;
|
2017-01-25 20:25:39 +01:00
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
//2-byte header
|
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset + 0x22*num_frame;
|
|
|
|
|
|
|
|
hist1 = (int16_t)((uint16_t)read_16bitBE(header_offset,stream->streamfile) & 0xff80);
|
|
|
|
step_index = read_8bit(header_offset+1,stream->streamfile) & 0x7f;
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
|
|
|
}
|
2017-01-25 20:25:39 +01:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2017-11-24 21:23:01 +01:00
|
|
|
off_t byte_offset = (stream->offset + 0x22*num_frame + 0x2) + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); //low nibble first
|
2017-01-25 20:25:39 +01:00
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble_16(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-01-25 20:25:39 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
}
|
|
|
|
|
2017-11-24 21:23:01 +01:00
|
|
|
stream->adpcm_history1_16 = hist1;
|
2017-01-25 20:25:39 +01:00
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2018-02-17 17:23:52 +01:00
|
|
|
/* XBOX-IMA with modified data layout */
|
2017-02-04 19:19:51 +01:00
|
|
|
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
|
2018-02-23 23:17:13 +01:00
|
|
|
int i, sample_count = 0;
|
2017-02-04 19:19:51 +01:00
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* internal interleave (configurable size), mixed channels */
|
|
|
|
int block_samples = (0x24 - 0x4) * 2;
|
2017-02-04 19:19:51 +01:00
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* interleaved header (all hist per channel + all step_index+reserved per channel) */
|
2017-02-04 19:19:51 +01:00
|
|
|
if (first_sample == 0) {
|
2018-02-23 23:01:44 +01:00
|
|
|
off_t hist_offset = stream->offset + 0x02*channel + 0x00;
|
|
|
|
off_t step_offset = stream->offset + 0x02*channel + 0x02*vgmstream->channels;
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
hist1 = read_16bitLE(hist_offset,stream->streamfile);
|
2017-02-04 19:19:51 +01:00
|
|
|
step_index = read_8bit(step_offset,stream->streamfile);
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
2018-02-23 23:01:44 +01:00
|
|
|
|
2018-02-23 23:17:13 +01:00
|
|
|
/* write header sample (even samples per block, skips last nibble) */
|
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
sample_count += channelspacing;
|
|
|
|
first_sample += 1;
|
|
|
|
samples_to_do -= 1;
|
2017-02-04 19:19:51 +01:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* decode nibbles (layout: 2 bytes/2*2 nibbles per channel) */
|
2018-02-23 23:17:13 +01:00
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
|
|
|
off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x02*channel + (i-1)/4*2*vgmstream->channels + ((i-1)%4)/2;
|
|
|
|
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
|
2017-02-04 19:19:51 +01:00
|
|
|
|
2018-02-23 23:17:13 +01:00
|
|
|
/* must skip last nibble per official decoder, probably not needed though */
|
|
|
|
if (i < block_samples) {
|
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
sample_count += channelspacing;
|
|
|
|
}
|
2017-02-04 19:19:51 +01:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* internal interleave: increment offset on complete frame */
|
|
|
|
if (i == block_samples) {
|
|
|
|
stream->offset += 0x24*vgmstream->channels;
|
|
|
|
}
|
2017-02-04 19:19:51 +01:00
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
2017-04-11 19:59:29 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* mono XBOX-IMA with header endianness and alt nibble expand (per hcs's decompilation) */
|
|
|
|
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
|
|
|
int i, sample_count = 0, num_frame;
|
2017-04-11 19:59:29 +02:00
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* external interleave (fixed size), mono */
|
|
|
|
int block_samples = (0x24 - 0x4) * 2;
|
|
|
|
num_frame = first_sample / block_samples;
|
2017-04-11 19:59:29 +02:00
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* normal header (hist+step+reserved), single channel */
|
2017-04-11 19:59:29 +02:00
|
|
|
if (first_sample == 0) {
|
|
|
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
|
2018-02-23 23:01:44 +01:00
|
|
|
off_t header_offset = stream->offset + 0x24*num_frame;
|
2017-04-11 19:59:29 +02:00
|
|
|
|
|
|
|
hist1 = read_16bit(header_offset,stream->streamfile);
|
|
|
|
step_index = read_8bit(header_offset+2,stream->streamfile);
|
|
|
|
if (step_index < 0) step_index=0;
|
|
|
|
if (step_index > 88) step_index=88;
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* write header sample (even samples per block, skips last nibble) */
|
2017-04-11 19:59:29 +02:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
sample_count += channelspacing;
|
|
|
|
first_sample += 1;
|
2017-04-13 16:55:40 +02:00
|
|
|
samples_to_do -= 1;
|
2017-04-11 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
2018-02-23 23:17:13 +01:00
|
|
|
/* decode nibbles (layout: all nibbles from one channel) */
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
2018-02-23 23:01:44 +01:00
|
|
|
off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2;
|
2018-02-23 23:17:13 +01:00
|
|
|
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
|
2017-04-11 19:59:29 +02:00
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
/* must skip last nibble like other XBOX-IMAs, often needed (ex. Bayonetta 2 sfx) */
|
2017-04-11 19:59:29 +02:00
|
|
|
if (i < block_samples) {
|
2018-02-23 23:01:44 +01:00
|
|
|
std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-04-11 19:59:29 +02:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
2018-02-23 23:01:44 +01:00
|
|
|
sample_count += channelspacing;
|
2017-04-11 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
2018-02-23 23:01:44 +01:00
|
|
|
/* from hcs's analysis Wwise IMA expands nibbles slightly different, reducing dbs. Just "MUL" expand?
|
|
|
|
<_ZN13CAkADPCMCodec12DecodeSampleEiii>: //From Wwise_v2015.1.6_Build5553_SDK.Linux
|
2018-02-17 19:44:18 +01:00
|
|
|
10: 83 e0 07 and $0x7,%eax ; sample
|
|
|
|
13: 01 c0 add %eax,%eax ; sample*2
|
|
|
|
15: 83 c0 01 add $0x1,%eax ; sample*2+1
|
|
|
|
18: 0f af 45 e4 imul -0x1c(%rbp),%eax ; (sample*2+1)*scale
|
|
|
|
1c: 8d 50 07 lea 0x7(%rax),%edx ; result+7
|
|
|
|
1f: 85 c0 test %eax,%eax ; result negative?
|
|
|
|
21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division
|
|
|
|
24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */
|
2017-08-28 16:07:09 +02:00
|
|
|
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
|
|
|
int i, sample_count;
|
|
|
|
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
|
|
|
//internal interleave, mono
|
|
|
|
int block_samples = (0x800 - 4) * 2;
|
|
|
|
first_sample = first_sample % block_samples;
|
|
|
|
|
|
|
|
//inverted header
|
|
|
|
if (first_sample == 0) {
|
|
|
|
off_t header_offset = stream->offset;
|
|
|
|
|
|
|
|
step_index = read_16bitLE(header_offset,stream->streamfile);
|
|
|
|
hist1 = read_16bitLE(header_offset+2,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 + i/2;
|
|
|
|
int nibble_shift = (i&1?4:0); //low nibble first
|
|
|
|
|
2017-11-25 00:43:18 +01:00
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-08-28 16:07:09 +02:00
|
|
|
outbuf[sample_count] = (short)(hist1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//internal interleave: increment offset on complete frame
|
|
|
|
if (i == block_samples) stream->offset += 0x800;
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-05 17:05:39 +01:00
|
|
|
/* DVI stereo/mono with some mini header and sample output */
|
|
|
|
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
|
|
|
int i, sample_count = 0;
|
|
|
|
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
|
|
|
|
//internal interleave
|
|
|
|
|
|
|
|
//header in the beginning of the stream
|
|
|
|
if (stream->channel_start_offset == stream->offset) {
|
|
|
|
int version, big_endian, header_samples, max_samples_to_do;
|
|
|
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
off_t offset = stream->offset;
|
|
|
|
|
|
|
|
/* header fields mostly unknown (vary a lot or look like flags),
|
|
|
|
* 0x07 0x06 = major/minor tool version?, 0x0c: stereo flag? */
|
|
|
|
version = read_8bit(offset + 0x00, stream->streamfile);
|
|
|
|
big_endian = version < 5; //todo and sb.big_endian?
|
|
|
|
read_16bit = big_endian ? read_16bitBE : read_16bitLE;
|
|
|
|
|
|
|
|
header_samples = read_16bit(offset + 0x0E, stream->streamfile); /* always 10 (per channel) */
|
|
|
|
hist1 = read_16bit(offset + 0x10 + channel*0x04,stream->streamfile);
|
|
|
|
step_index = read_8bit(offset + 0x12 + channel*0x04,stream->streamfile);
|
|
|
|
offset += 0x10 + 0x08 + 0x04; //todo v6 has extra 0x08?
|
|
|
|
|
|
|
|
/* write PCM samples, must be written to match header's num_samples (hist mustn't) */
|
|
|
|
max_samples_to_do = ((samples_to_do > header_samples) ? header_samples : samples_to_do);
|
|
|
|
for (i = first_sample; i < max_samples_to_do; i++, sample_count += channelspacing) {
|
|
|
|
outbuf[sample_count] = read_16bit(offset + channel*sizeof(sample) + i*channelspacing*sizeof(sample),stream->streamfile);
|
|
|
|
first_sample++;
|
|
|
|
samples_to_do--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* header done */
|
|
|
|
if (i == header_samples) {
|
|
|
|
stream->offset = offset + header_samples*channelspacing*sizeof(sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
|
|
|
|
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
|
|
|
|
off_t byte_offset = channelspacing == 1 ?
|
|
|
|
stream->offset + i/2 : /* mono mode */
|
|
|
|
stream->offset + i; /* stereo mode */
|
|
|
|
int nibble_shift = channelspacing == 1 ?
|
|
|
|
(!(i%2) ? 4:0) : /* mono mode (high first) */
|
|
|
|
(channel==0 ? 4:0); /* stereo mode (high=L,low=R) */
|
|
|
|
|
2018-02-23 23:01:44 +01:00
|
|
|
std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
2017-11-05 17:05:39 +01:00
|
|
|
outbuf[sample_count] = (short)(hist1); /* all samples are written */
|
|
|
|
}
|
|
|
|
|
|
|
|
//external interleave
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
2018-08-19 00:38:08 +02:00
|
|
|
/* IMA with variable frame formats controlled by the block layout. The original code uses
|
|
|
|
* tables mapping all standard IMA combinations (to optimize calculations), but decodes the same.
|
|
|
|
* Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */
|
|
|
|
void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format) {
|
|
|
|
int i, samples_done = 0;
|
|
|
|
int32_t hist1 = stream->adpcm_history1_32;
|
|
|
|
int step_index = stream->adpcm_step_index;
|
|
|
|
size_t header_size;
|
|
|
|
int is_stereo = (channelspacing > 1);
|
|
|
|
|
|
|
|
/* external interleave (blocked, should call 1 frame) */
|
|
|
|
|
|
|
|
/* custom header, per channel */
|
|
|
|
if (first_sample == 0) {
|
|
|
|
int channel_pos = is_stereo ? (1 - channel) : channel; /* R hist goes first */
|
|
|
|
switch(frame_format) {
|
|
|
|
case 1: /* combined hist+index */
|
|
|
|
hist1 = read_16bitBE(stream->offset + 0x02*channel_pos + 0x00,stream->streamfile) & 0xFFFFFF80;
|
|
|
|
step_index = (uint8_t)read_8bit(stream->offset + 0x02*channel_pos + 0x01,stream->streamfile) & 0x7f;
|
|
|
|
break;
|
|
|
|
case 3: /* separate hist+index */
|
|
|
|
hist1 = read_16bitBE(stream->offset + 0x03*channel_pos + 0x00,stream->streamfile);
|
|
|
|
step_index = (uint8_t)read_8bit(stream->offset + 0x03*channel_pos + 0x02,stream->streamfile);
|
|
|
|
break;
|
|
|
|
case 2: /* no hist/index (continues from previous frame) */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write header sample (last nibble is skipped) */
|
|
|
|
if (frame_format == 1 || frame_format == 3) {
|
|
|
|
outbuf[samples_done * channelspacing] = (short)hist1;
|
|
|
|
samples_done++;
|
|
|
|
samples_to_do--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clamp corrupted data just in case */
|
|
|
|
if (step_index < 0) step_index = 0;
|
|
|
|
if (step_index > 88) step_index = 88;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* offset adjust for header sample */
|
|
|
|
if (frame_format == 1 || frame_format == 3) {
|
|
|
|
first_sample--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* offset adjust */
|
|
|
|
switch(frame_format) {
|
|
|
|
case 1: header_size = (channelspacing*0x02); break;
|
|
|
|
case 3: header_size = (channelspacing*0x03); break;
|
|
|
|
default: header_size = 0; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* decode block nibbles */
|
|
|
|
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
|
|
|
off_t byte_offset = is_stereo ?
|
|
|
|
stream->offset + header_size + i : /* stereo: one nibble per channel */
|
|
|
|
stream->offset + header_size + i/2; /* mono: consecutive nibbles */
|
|
|
|
int nibble_shift = is_stereo ?
|
|
|
|
(!(channel&1) ? 0:4) : /* stereo: L=low, R=high */
|
|
|
|
(!(i&1) ? 0:4); /* mono: low first */
|
|
|
|
|
|
|
|
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
|
|
|
|
|
|
|
outbuf[samples_done * channelspacing] = (short)(hist1);
|
|
|
|
samples_done++;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->adpcm_history1_32 = hist1;
|
|
|
|
stream->adpcm_step_index = step_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************* */
|
2017-08-28 16:07:09 +02:00
|
|
|
|
2018-02-17 12:30:14 +01:00
|
|
|
size_t ima_bytes_to_samples(size_t bytes, int channels) {
|
|
|
|
/* 2 samples per byte (2 nibbles) in stereo or mono config */
|
|
|
|
return bytes * 2 / channels;
|
|
|
|
}
|
|
|
|
|
2017-04-11 19:59:29 +02:00
|
|
|
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
|
2018-02-23 23:01:44 +01:00
|
|
|
/* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
|
2018-02-17 17:23:52 +01:00
|
|
|
return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1)
|
|
|
|
+ ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0);
|
2017-04-11 19:59:29 +02:00
|
|
|
}
|
2017-07-23 12:49:10 +02:00
|
|
|
|
2018-02-17 12:30:14 +01:00
|
|
|
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
|
|
|
|
int block_align = 0x24 * channels;
|
|
|
|
/* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
|
|
|
|
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
|
2018-02-23 23:01:44 +01:00
|
|
|
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
|
2017-07-23 12:49:10 +02:00
|
|
|
}
|
2017-11-05 17:05:39 +01:00
|
|
|
|
2018-03-17 19:57:54 +01:00
|
|
|
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) {
|
|
|
|
int block_align = 0x22 * channels;
|
|
|
|
return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels
|
|
|
|
+ ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0);
|
|
|
|
}
|
|
|
|
|
2017-11-05 17:05:39 +01:00
|
|
|
size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) {
|
|
|
|
int version, big_endian, header_samples;
|
|
|
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
size_t header_size = 0;
|
|
|
|
|
|
|
|
version = read_8bit(offset + 0x00, streamFile);
|
|
|
|
big_endian = version < 5; //todo and sb.big_endian?
|
|
|
|
read_16bit = big_endian ? read_16bitBE : read_16bitLE;
|
|
|
|
|
|
|
|
header_samples = read_16bit(offset + 0x0E, streamFile); /* always 10 (per channel) */
|
|
|
|
header_size += 0x10 + 0x04 * channels + 0x04; //todo v6 has extra 0x08?
|
|
|
|
header_size += header_samples * channels * sizeof(sample);
|
|
|
|
|
|
|
|
return header_samples + ima_bytes_to_samples(bytes - header_size, channels);
|
|
|
|
}
|