Skip to content

Conversation

acieslewicz
Copy link

Adds ima4 decoding support to adpcm. This seems to be working from my local testing. Please let me know if I missed anything or if I should make any changes!

Co-authored-by: hikari_no_yume <hikari@noyu.me>
@acieslewicz acieslewicz changed the base branch from master to dev-0.6 May 28, 2025 01:21
@hikari-no-yume
Copy link

I am listed as a co-author here because the ima4 format decoding code in this PR originates in my implementation for touchHLE, but it has been significantly refactored in this PR. We discussed it prior to the PR being made and I am happy for the code to be included in this way.

Comment on lines 14 to 63
#[rustfmt::skip]
const IMA_INDEX_TABLE: [i32; 16] = [
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8,
];

#[rustfmt::skip]
const IMA_STEP_TABLE: [i32; 89] = [
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,
];

/// `AdpcmImaBlockStatus` contains values to decode a block
struct AdpcmImaBlockStatus {
predictor: i32,
step_index: usize,
}

impl AdpcmImaBlockStatus {
fn read_preamble<B: ReadBytes>(stream: &mut B) -> Result<Self> {
let header = stream.read_be_u16()?;
let predictor = u16_to_i32!(header & 0xFF80);
let step_index = ((header & 0x7F) as usize).min(IMA_STEP_TABLE.len() - 1);

let status = Self { predictor, step_index };
Ok(status)
}

fn expand_nibble(&mut self, byte: u8, nibble: Nibble) -> i32 {
let nibble = nibble.get_nibble(byte);
let step = IMA_STEP_TABLE[self.step_index];
let sign = (nibble & 0x08) != 0;
let delta = (nibble & 0x07) as i32;
let diff = ((2 * delta + 1) * step) >> 3;
let predictor = if sign { self.predictor - diff } else { self.predictor + diff };
self.predictor = clamp_i16(predictor) as i32;
self.step_index = self
.step_index
.saturating_add_signed(IMA_INDEX_TABLE[nibble as usize] as isize)
.min(IMA_STEP_TABLE.len() - 1);
from_i16_shift!(self.predictor)
}
}
Copy link

@hikari-no-yume hikari-no-yume May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't all or most of this code be shared with codec_ima.rs? The ima4 format is basically just another framing of IMA ADPCM.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the tables and the nibble expansion could be shared. The preamble read definitely can't. I could consolidate if that's preferred.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was the table and nibbles part I had in mind, yeah, because they're from the IMA ADPCM standard.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored it a bit keeping the preamble and decoding separate with a shared expansion

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice, thank you!

@pdeljanov
Copy link
Owner

Thanks @acieslewicz and @hikari-no-yume for this, and apologies for the delay.

LGTM, but since it has been a while the workflow approval has timed out. I ran the tests locally but the formatting check fails. Could you run rustfmt with cargo +nightly fmt, thanks!

@acieslewicz
Copy link
Author

Done! Thanks for the review!

@pdeljanov pdeljanov merged commit d35eeb2 into pdeljanov:dev-0.6 Aug 21, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants