Fix some ADM3 .wem [The Crew Motorfest (PC)]

This commit is contained in:
bnnm 2023-09-24 15:19:38 +02:00
parent aa1db48b36
commit ab530d40b6
6 changed files with 93 additions and 28 deletions

View File

@ -418,7 +418,8 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_XBOX_IMA_int:
case coding_FSB_IMA:
case coding_WWISE_IMA:
case coding_CD_IMA:
case coding_CD_IMA: /* (0x24 - 0x04) * 2 */
case coding_CRANKCASE_IMA: /* (0x23 - 0x3) * 2 */
return 64;
case coding_APPLE_IMA4:
return 64;
@ -654,6 +655,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
case coding_WWISE_IMA:
case coding_CD_IMA:
return 0x24;
case coding_CRANKCASE_IMA:
return 0x23;
case coding_XBOX_IMA_mch:
case coding_FSB_IMA:
return 0x24 * vgmstream->channels;
@ -1264,6 +1267,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_CRANKCASE_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_crankcase_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_WS:
for (ch = 0; ch < vgmstream->channels; ch++) {

View File

@ -45,6 +45,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspaci
void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
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 xbox_ima_bytes_to_samples(size_t bytes, int channels);

View File

@ -35,8 +35,8 @@ static const int IMA_IndexTable[16] = {
/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */
static void std_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;
static void std_ima_expand_nibble_data(uint8_t byte, int shift, int32_t* hist1, int32_t* index) {
int code, sample, step, delta;
/* simplified through math from:
* - diff = (code + 1/2) * (step / 4)
@ -44,21 +44,26 @@ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
* > diff = (step * nibble / 4) + (step / 8)
* final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
sample_decoded = *hist1; /* predictor value */
step = ADPCMTable[*step_index]; /* current step */
code = (byte >> shift) & 0xf;
sample = *hist1; /* predictor value */
step = ADPCMTable[*index]; /* current step */
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) delta = -delta;
sample_decoded += delta;
if (code & 1) delta += step >> 2;
if (code & 2) delta += step >> 1;
if (code & 4) delta += step;
if (code & 8) delta = -delta;
sample += 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;
*hist1 = clamp16(sample);
*index += IMA_IndexTable[code];
if (*index < 0) *index = 0;
if (*index > 88) *index = 88;
}
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
uint8_t byte = read_u8(byte_offset,stream->streamfile);
std_ima_expand_nibble_data(byte, nibble_shift, hist1, step_index);
}
/* Apple's IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend?) */
@ -1287,6 +1292,43 @@ void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin
stream->adpcm_step_index = step_index;
}
/* Crankcase Audio IMA, from libs (internally CrankcaseAudio::ADPCM and revadpcm) */
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
uint8_t frame[0x23] = {0};
int i, frames_in, sample_pos = 0, block_samples, frame_size;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
uint32_t frame_offset;
/* external interleave (fixed size), mono */
frame_size = 0x23;
block_samples = (frame_size - 0x3) * 2;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + frame_size * frames_in;
read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */
/* normal header (hist+step), mono */
if (first_sample == 0) {
hist1 = get_s16be(frame + 0x00);
step_index = get_u8(frame + 0x02); /* no reserved value at 0x03 unlike other IMAs (misaligned reads?) */
step_index = _clamp_s32(step_index, 0, 88);
}
/* decode nibbles (layout: straight in mono) */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int pos = 0x03 + (i/2);
int shift = (i & 1 ? 4:0); /* low first */
std_ima_expand_nibble_data(frame[pos], shift, &hist1, &step_index);
outbuf[sample_pos] = (short)(hist1); /* internally output to float using "sample / 32767.0" */
sample_pos += channelspacing;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* ************************************************************* */

View File

@ -831,6 +831,7 @@ static const coding_info coding_info_list[] = {
{coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},
{coding_CRANKCASE_IMA, "CrankcaseAudio REV 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},

View File

@ -5,7 +5,8 @@
typedef struct {
int total_subsongs;
int target_subsong;
int version;
int file_version;
int header_version; /* major.minor in hex */
uint32_t stream_offset;
uint32_t stream_size;
@ -18,7 +19,7 @@ typedef struct {
static int parse_adm(adm_header_t* adm, STREAMFILE* sf);
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version);
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version);
/* ADM2 - Crankcase Audio REV plugin file [The Grand Tour Game (PC)] */
VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) {
@ -38,26 +39,24 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "ADM3"))
return NULL;
if (!check_extensions(sf, "wem"))
if (!check_extensions(sf, "wem,bnk"))
return NULL;
return init_vgmstream_adm(sf, 3);
}
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version) {
VGMSTREAM* vgmstream = NULL;
adm_header_t adm = {0};
/* ADMx are files used with the Wwise Crankaudio plugin, that simulate engine noises with
* base internal samples and some internal RPM config (probably). Actual file seems to
* define some combo of samples, this only plays those separate samples.
* Decoder is basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
* each sample during decode by multiplying by 0.000030518509 */
* define some combo of samples, this only plays those separate samples. */
adm.target_subsong = sf->stream_index;
if (adm.target_subsong == 0) adm.target_subsong = 1;
adm.version = version;
adm.file_version = file_version;
if (!parse_adm(&adm, sf))
goto fail;
@ -73,9 +72,21 @@ static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
vgmstream->num_streams = adm.total_subsongs;
vgmstream->stream_size = adm.stream_size;
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x22;
switch(adm.header_version) {
case 0x00070000: /* The Crew Motorfest (PC) */
vgmstream->coding_type = coding_CRANKCASE_IMA;
//vgmstream->layout_type = layout_interleave;
//vgmstream->interleave_block_size = 0x23;
break;
default: /* The Grand Tour Game (PC) [0x00050000], MotoGP 21 (PC) [0x00060000] */
/* Basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
* each sample during decode by multiplying by 0.000030518509 */
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x22;
break;
}
if (!vgmstream_open_stream(vgmstream, sf, adm.stream_offset))
goto fail;
@ -169,12 +180,12 @@ static int parse_adm(adm_header_t* adm, STREAMFILE* sf) {
uint32_t offset;
/* 0x04: null */
/* 0x08: version? (ADM2: 0x00050000, ADM3: 0x00060000) */
adm->header_version = read_u32le(0x08, sf); /* ADM2: 0x00050000, ADM3: 0x00060000 (older) / 0x00070000 (2023) */
/* 0x0c: header size */
/* 0x10: data start */
/* rest unknown, looks mostly the same between files (some floats and stuff) */
switch(adm->version) {
switch(adm->file_version) {
case 2:
/* low to high */
offset = read_u32le(0x104, sf);

View File

@ -90,6 +90,7 @@ typedef enum {
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
coding_CRANKCASE_IMA, /* CrankcaseAudio REV IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */