Fix MS-IMA ADPCM decoding/bytes_to_samples missing 1 sample per block

Now correctly follows Microsoft's spec and matches other players.
This commit is contained in:
bnnm 2018-02-17 17:23:52 +01:00
parent 9cf9416665
commit e73023d6e2
2 changed files with 59 additions and 33 deletions

View File

@ -272,46 +272,70 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
} }
/* ************************************ */ /* ************************************ */
/* MS IMA */ /* MS-IMA */
/* ************************************ */ /* ************************************ */
/* IMA with frames with header and custom sizes */ /* IMA with variable-sized frames, header and custom nibble layout (outputs non-aligned number of samples).
void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */
int i, sample_count; void decode_ms_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; int32_t hist1;// = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index;// = stream->adpcm_step_index;
//internal interleave (configurable size), mixed channels (4 byte per ch) /* internal interleave (configurable size), mixed channels (4 byte per ch) */
int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels; int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
//normal header (per channel) /* normal header (hist+step+reserved per channel) */
if (first_sample == 0) { {
off_t header_offset = stream->offset + 4*channel; off_t header_offset = stream->offset + 0x04*channel;
hist1 = read_16bitLE(header_offset,stream->streamfile); hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_8bit(header_offset+2,stream->streamfile); step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */
if (step_index < 0) step_index=0; if (step_index < 0) step_index = 0;
if (step_index > 88) step_index=88; 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++;
} }
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { max_samples = (block_samples - samples_read);
off_t byte_offset = stream->offset + 4*channel + 4*vgmstream->channels + i/8*4*vgmstream->channels + (i%8)/2; if (max_samples > samples_to_do + first_sample - samples_done)
int nibble_shift = (i&1?4:0); //low nibble first max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */
/* decode nibbles (layout: alternates 4*2 nibbles per channel) */
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 */
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
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 /* internal interleave: increment offset on complete frame */
if (i == block_samples) stream->offset += vgmstream->interleave_block_size; if (first_sample + samples_done == block_samples) {
stream->offset += vgmstream->interleave_block_size;
}
stream->adpcm_history1_32 = hist1; //stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index; //stream->adpcm_step_index = step_index;
} }
/* MS IMA with fixed frame size and custom multichannel nibble layout. /* ************************************ */
/* XBOX-IMA */
/* ************************************ */
/* MS-IMA with fixed frame size, skips last sample per channel (for aligment) and custom multichannel nibble layout.
* For multichannel the layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */ * For multichannel the layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */
void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
int i, sample_count; int i, sample_count;
@ -351,12 +375,12 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o
} }
//internal interleave: increment offset on complete frame //internal interleave: increment offset on complete frame
if (channelspacing==1) { if (channelspacing==1) { /* mono */
if(offset-stream->offset==32+3) // ?? if (offset-stream->offset == 32+3) // ??
stream->offset+=36; stream->offset += 0x24;
} else { } else {
if(offset-stream->offset==64+(4*(channel%2))+3) // ?? if (offset-stream->offset == 64+(4*(channel%2))+3) // ??
stream->offset+=36*channelspacing; stream->offset += 0x24*channelspacing;
} }
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;
@ -565,6 +589,7 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
/* XBOX-IMA with modified data layout */
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
int i, sample_count; int i, sample_count;
@ -572,7 +597,7 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
//internal interleave //internal interleave
int block_samples = (36 - 4) * 2; /* block size - header, 2 samples per byte */ int block_samples = (0x24 - 4) * 2; /* block size - header, 2 samples per byte */
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
//interleaved header (all hist per channel + all step_index per channel) //interleaved header (all hist per channel + all step_index per channel)
@ -601,7 +626,7 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
/* XBOX-IMA with modified data layout */
void decode_wwise_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { 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; int i, sample_count = 0;
@ -792,8 +817,8 @@ size_t ima_bytes_to_samples(size_t bytes, int channels) {
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
/* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ /* MS 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 return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1)
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); + ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0);
} }
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) { size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {

View File

@ -1076,6 +1076,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_APPLE_IMA4: case coding_APPLE_IMA4:
return 64; return 64;
case coding_MS_IMA: case coding_MS_IMA:
return ((vgmstream->interleave_block_size-4*vgmstream->channels) * 2 / vgmstream->channels) + 1;
case coding_RAD_IMA: case coding_RAD_IMA:
case coding_WWISE_IMA: case coding_WWISE_IMA:
case coding_REF_IMA: case coding_REF_IMA: