mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Fix some ADM3 .wem [The Crew Motorfest (PC)]
This commit is contained in:
parent
aa1db48b36
commit
ab530d40b6
@ -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++) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* ************************************************************* */
|
||||
|
||||
|
@ -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)"},
|
||||
|
@ -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);
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user