Improve MSADPCM accuracy

This commit is contained in:
bnnm 2021-06-20 18:10:34 +02:00
parent 6d3a9a52d6
commit d49aacbf52
4 changed files with 14 additions and 15 deletions

View File

@ -161,7 +161,7 @@ STREAMFILE* nwa_get_streamfile(nwa_codec_data* data);
#define MSADPCM_MAX_BLOCK_SIZE 0x800 /* known max and RIFF spec seems to concur, while MS's encoders may be lower (typical stereo: 0x8c, 0x2C, 0x48, 0x400) */ #define MSADPCM_MAX_BLOCK_SIZE 0x800 /* known max and RIFF spec seems to concur, while MS's encoders may be lower (typical stereo: 0x8c, 0x2C, 0x48, 0x400) */
void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do);
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config);
void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* 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); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); int msadpcm_check_coefs(STREAMFILE* sf, off_t offset);

View File

@ -24,9 +24,9 @@ static const int16_t msadpcm_coefs[7][2] = {
/* Decodes MSADPCM as explained in the spec (RIFFNEW doc + msadpcm.c). /* Decodes MSADPCM as explained in the spec (RIFFNEW doc + msadpcm.c).
* Though RIFFNEW writes "predictor / 256" (DIV), msadpcm.c uses "predictor >> 8" (SHR). They may seem the * Though RIFFNEW writes "predictor / 256" (DIV), msadpcm.c uses "predictor >> 8" (SHR). They may seem the
* same but on negative values SHR gets different results (-128 / 256 = 0; -128 >> 8 = 1) = some output diffs. * same but on negative values SHR gets different results (-128 / 256 = 0; -128 >> 8 = -1) = some output diffs.
* SHR is true in Windows msadp32.acm decoders (up to Win10), while some non-Windows implementations or * SHR is true in Windows msadp32.acm decoders (up to Win10), while some non-Windows implementations or
* engines (like UE4) use DIV though (more accurate). * engines (like UE4) may use DIV.
* *
* On invalid coef index, msadpcm.c returns 0 decoded samples but here we clamp and keep on trucking. * On invalid coef index, msadpcm.c returns 0 decoded samples but here we clamp and keep on trucking.
* In theory blocks may be 0-padded and should use samples_per_frame from header, in practice seems to * In theory blocks may be 0-padded and should use samples_per_frame from header, in practice seems to
@ -131,25 +131,20 @@ void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first
/* decode nibbles */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int ch;
for (ch = 0; ch < 2; ch++) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[ch];
uint8_t byte = get_u8(frame+0x07*2+(i-2)); uint8_t byte = get_u8(frame+0x07*2+(i-2));
int shift = (ch == 0); /* L = high nibble first (iErrorDelta) */
outbuf[0] = msadpcm_adpcm_expand_nibble_div(stream, byte, shift); *outbuf++ = msadpcm_adpcm_expand_nibble_shr(&vgmstream->ch[0], byte, 1); /* L */
outbuf++; *outbuf++ = msadpcm_adpcm_expand_nibble_shr(&vgmstream->ch[1], byte, 0); /* R */
}
} }
} }
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[channel]; VGMSTREAMCHANNEL* stream = &vgmstream->ch[channel];
uint8_t frame[MSADPCM_MAX_BLOCK_SIZE] = {0}; uint8_t frame[MSADPCM_MAX_BLOCK_SIZE] = {0};
int i, frames_in; int i, frames_in;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
off_t frame_offset; off_t frame_offset;
int is_shr = (config == 0);
/* external interleave (variable size), mono */ /* external interleave (variable size), mono */
bytes_per_frame = vgmstream->frame_size; bytes_per_frame = vgmstream->frame_size;
@ -188,7 +183,9 @@ void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspac
uint8_t byte = get_u8(frame+0x07+(i-2)/2); uint8_t byte = get_u8(frame+0x07+(i-2)/2);
int shift = !(i & 1); /* high nibble first */ int shift = !(i & 1); /* high nibble first */
outbuf[0] = msadpcm_adpcm_expand_nibble_div(stream, byte, shift); outbuf[0] = is_shr ?
msadpcm_adpcm_expand_nibble_shr(stream, byte, shift) :
msadpcm_adpcm_expand_nibble_div(stream, byte, shift);
outbuf += channelspacing; outbuf += channelspacing;
} }
} }

View File

@ -1290,7 +1290,8 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) {
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
decode_msadpcm_mono(vgmstream,buffer+ch, decode_msadpcm_mono(vgmstream,buffer+ch,
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch,
vgmstream->codec_config);
} }
} }
else if (vgmstream->channels == 2) { else if (vgmstream->channels == 2) {

View File

@ -794,6 +794,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
/* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */ /* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, sf, &fmt, fact_sample_count, start_offset)) { if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, sf, &fmt, fact_sample_count, start_offset)) {
vgmstream->coding_type = coding_MSADPCM_int; vgmstream->coding_type = coding_MSADPCM_int;
vgmstream->codec_config = 1; /* mark as UE4 MSADPCM */
vgmstream->frame_size = fmt.block_size; vgmstream->frame_size = fmt.block_size;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(sf, &fmt, start_offset, data_size); vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(sf, &fmt, start_offset, data_size);