Skip to content

Adpcm ima4 #384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: dev-0.6
Choose a base branch
from
Open

Adpcm ima4 #384

wants to merge 3 commits into from

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!

@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!

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.

2 participants