1use super::{Wav, WavEncoding};
11use thiserror::Error;
12
13fn normalize_sample(sample: i32) -> f32 {
17 let narrow = i16::try_from(sample).expect("ADPCM samples are clamped to i16 range");
18 f32::from(narrow) / 32768.0
19}
20
21#[derive(Debug, Error)]
23pub enum AdpcmError {
24 #[error("unsupported encoding: {0:?}")]
26 UnsupportedEncoding(Option<WavEncoding>),
27
28 #[error("invalid block align: {0}")]
30 InvalidBlockAlign(u16),
31
32 #[error("invalid or missing coefficient table")]
34 InvalidCoefficients,
35
36 #[error("unexpected end of data")]
38 UnexpectedEof,
39}
40
41pub fn decode_adpcm_as_float(wav: &Wav) -> Result<Vec<f32>, AdpcmError> {
46 let encoding = wav.encoding.known();
47
48 match encoding {
49 Some(WavEncoding::MsAdpcm) => decode_ms_adpcm(wav),
50 Some(WavEncoding::ImaAdpcm) => decode_ima_adpcm(wav),
51 _ => Err(AdpcmError::UnsupportedEncoding(encoding)),
52 }
53}
54
55const MS_ADAPTATION_TABLE: [i32; 16] = [
62 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230,
63];
64
65fn decode_ms_adpcm(wav: &Wav) -> Result<Vec<f32>, AdpcmError> {
66 let channels = usize::from(wav.channels);
67 let block_align = usize::from(wav.block_align);
68
69 if block_align == 0 {
70 return Err(AdpcmError::InvalidBlockAlign(0));
71 }
72
73 let standard_coeffs = [
79 (256, 0),
80 (512, -256),
81 (0, 0),
82 (192, 64),
83 (240, 0),
84 (460, -208),
85 (392, -232),
86 ];
87
88 let num_blocks = wav.data.len() / block_align;
89 let samples_per_block = ms_adpcm_samples_per_block(channels, block_align)?;
90 let mut output = Vec::with_capacity(num_blocks * samples_per_block * channels);
91
92 for block_idx in 0..num_blocks {
93 let block_offset = block_idx * block_align;
94 let block = &wav.data[block_offset..block_offset + block_align];
95
96 if channels == 1 {
97 decode_ms_adpcm_block_mono(block, &standard_coeffs, &mut output)?;
98 } else if channels == 2 {
99 decode_ms_adpcm_block_stereo(block, &standard_coeffs, &mut output)?;
100 } else {
101 return Err(AdpcmError::UnsupportedEncoding(None));
102 }
103 }
104
105 Ok(output)
106}
107
108fn ms_adpcm_samples_per_block(channels: usize, block_align: usize) -> Result<usize, AdpcmError> {
109 let overhead = 7 * channels;
113 if block_align < overhead {
114 return Err(AdpcmError::InvalidBlockAlign(
115 u16::try_from(block_align).expect("block_align originates from a u16 field"),
116 ));
117 }
118 Ok(2 + (block_align - overhead) * 2 / channels)
119}
120
121fn decode_ms_adpcm_block_mono(
122 block: &[u8],
123 coeffs: &[(i32, i32)],
124 output: &mut Vec<f32>,
125) -> Result<(), AdpcmError> {
126 if block.len() < 7 {
127 return Err(AdpcmError::UnexpectedEof);
128 }
129
130 let predictor = usize::from(block[0]);
131 if predictor >= coeffs.len() {
132 return Err(AdpcmError::InvalidCoefficients);
133 }
134 let (c1, c2) = coeffs[predictor];
135
136 let mut delta = i32::from(i16::from_le_bytes([block[1], block[2]]));
137 let mut samp1 = i32::from(i16::from_le_bytes([block[3], block[4]]));
139 let mut samp2 = i32::from(i16::from_le_bytes([block[5], block[6]]));
140
141 output.push(normalize_sample(samp2));
143 output.push(normalize_sample(samp1));
144
145 for byte in &block[7..] {
147 for nibble in [i32::from(byte >> 4), i32::from(byte & 0x0F)] {
148 let pred = (samp1 * c1 + samp2 * c2) / 256;
149
150 let signed_nibble = if nibble >= 8 { nibble - 16 } else { nibble };
152 let clamped = (pred + signed_nibble * delta).clamp(-32768, 32767);
153
154 delta = (delta
155 * MS_ADAPTATION_TABLE[usize::try_from(nibble).expect("nibble is 0..15")]
156 / 256)
157 .max(16);
158
159 samp2 = samp1;
161 samp1 = clamped;
162
163 output.push(normalize_sample(samp1));
164 }
165 }
166 Ok(())
167}
168
169fn decode_ms_adpcm_block_stereo(
170 block: &[u8],
171 coeffs: &[(i32, i32)],
172 output: &mut Vec<f32>,
173) -> Result<(), AdpcmError> {
174 if block.len() < 14 {
175 return Err(AdpcmError::UnexpectedEof);
176 }
177
178 let pred_l = usize::from(block[0]);
179 let pred_r = usize::from(block[1]);
180 if pred_l >= coeffs.len() || pred_r >= coeffs.len() {
181 return Err(AdpcmError::InvalidCoefficients);
182 }
183 let (c1_l, c2_l) = coeffs[pred_l];
184 let (c1_r, c2_r) = coeffs[pred_r];
185
186 let mut delta_l = i32::from(i16::from_le_bytes([block[2], block[3]]));
187 let mut delta_r = i32::from(i16::from_le_bytes([block[4], block[5]]));
188
189 let mut samp1_l = i32::from(i16::from_le_bytes([block[6], block[7]]));
191 let mut samp1_r = i32::from(i16::from_le_bytes([block[8], block[9]]));
192 let mut samp2_l = i32::from(i16::from_le_bytes([block[10], block[11]]));
193 let mut samp2_r = i32::from(i16::from_le_bytes([block[12], block[13]]));
194
195 output.push(normalize_sample(samp2_l));
197 output.push(normalize_sample(samp2_r));
198 output.push(normalize_sample(samp1_l));
199 output.push(normalize_sample(samp1_r));
200
201 for byte in &block[14..] {
203 let nibble_l = i32::from(byte >> 4);
204 let nibble_r = i32::from(byte & 0x0F);
205
206 let pred = (samp1_l * c1_l + samp2_l * c2_l) / 256;
208 let signed_nibble = if nibble_l >= 8 {
209 nibble_l - 16
210 } else {
211 nibble_l
212 };
213 let clamped_l = (pred + signed_nibble * delta_l).clamp(-32768, 32767);
214 delta_l = (delta_l
215 * MS_ADAPTATION_TABLE[usize::try_from(nibble_l).expect("nibble is 0..15")]
216 / 256)
217 .max(16);
218 samp2_l = samp1_l;
219 samp1_l = clamped_l;
220
221 let pred = (samp1_r * c1_r + samp2_r * c2_r) / 256;
223 let signed_nibble = if nibble_r >= 8 {
224 nibble_r - 16
225 } else {
226 nibble_r
227 };
228 let clamped_r = (pred + signed_nibble * delta_r).clamp(-32768, 32767);
229 delta_r = (delta_r
230 * MS_ADAPTATION_TABLE[usize::try_from(nibble_r).expect("nibble is 0..15")]
231 / 256)
232 .max(16);
233 samp2_r = samp1_r;
234 samp1_r = clamped_r;
235
236 output.push(normalize_sample(clamped_l));
237 output.push(normalize_sample(clamped_r));
238 }
239 Ok(())
240}
241
242const IMA_INDEX_TABLE: [i8; 16] = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
247
248const IMA_STEP_TABLE: [i32; 89] = [
249 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66,
250 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
251 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272,
252 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
253 10442, 11487, 12635, 13899, 15290, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
254];
255
256fn decode_ima_adpcm(wav: &Wav) -> Result<Vec<f32>, AdpcmError> {
257 let channels = usize::from(wav.channels);
258 let block_align = usize::from(wav.block_align);
259
260 let header_bytes = 4 * channels;
261 if block_align < header_bytes {
262 return Err(AdpcmError::InvalidBlockAlign(wav.block_align));
263 }
264
265 let samples_per_block = (block_align - header_bytes) * 2 / channels + 1;
270
271 let num_blocks = wav.data.len() / block_align;
272 let mut output = Vec::with_capacity(num_blocks * samples_per_block * channels);
273
274 for block_idx in 0..num_blocks {
275 let block_offset = block_idx * block_align;
276 let block = &wav.data[block_offset..block_offset + block_align];
277
278 if channels == 1 {
279 decode_ima_adpcm_block_mono(block, &mut output)?;
280 } else if channels == 2 {
281 decode_ima_adpcm_block_stereo(block, &mut output)?;
282 } else {
283 return Err(AdpcmError::UnsupportedEncoding(None));
284 }
285 }
286
287 Ok(output)
288}
289
290fn decode_ima_adpcm_block_mono(block: &[u8], output: &mut Vec<f32>) -> Result<(), AdpcmError> {
291 if block.len() < 4 {
292 return Err(AdpcmError::UnexpectedEof);
293 }
294
295 let mut predictor = i16::from_le_bytes([block[0], block[1]]).into();
296 let mut step_index = i32::from(block[2]).clamp(0, 88);
297 output.push(normalize_sample(predictor));
300
301 for byte in &block[4..] {
303 for nibble in [i32::from(byte & 0x0F), i32::from(byte >> 4)] {
304 let step = IMA_STEP_TABLE
305 [usize::try_from(step_index).expect("step_index is clamped to 0..88")];
306 let mut diff = step >> 3;
307 if (nibble & 4) != 0 {
308 diff += step;
309 }
310 if (nibble & 2) != 0 {
311 diff += step >> 1;
312 }
313 if (nibble & 1) != 0 {
314 diff += step >> 2;
315 }
316
317 if (nibble & 8) != 0 {
318 predictor -= diff;
319 } else {
320 predictor += diff;
321 }
322 predictor = predictor.clamp(-32768, 32767);
323 step_index = (step_index
324 + i32::from(IMA_INDEX_TABLE[usize::try_from(nibble).expect("nibble is 0..15")]))
325 .clamp(0, 88);
326
327 output.push(normalize_sample(predictor));
328 }
329 }
330 Ok(())
331}
332
333fn decode_ima_adpcm_block_stereo(block: &[u8], output: &mut Vec<f32>) -> Result<(), AdpcmError> {
334 if block.len() < 8 {
335 return Err(AdpcmError::UnexpectedEof);
336 }
337
338 let mut left_pred = i16::from_le_bytes([block[0], block[1]]).into();
340 let mut left_step_idx = i32::from(block[2]).clamp(0, 88);
341
342 let mut right_pred = i32::from(i16::from_le_bytes([block[4], block[5]]));
343 let mut right_step_idx = i32::from(block[6]).clamp(0, 88);
344
345 output.push(normalize_sample(left_pred));
346 output.push(normalize_sample(right_pred));
347
348 let mut cursor = 8;
351 while cursor + 8 <= block.len() {
352 let left_chunk = &block[cursor..cursor + 4];
353 let right_chunk = &block[cursor + 4..cursor + 8];
354 cursor += 8;
355
356 let mut l_samples = [0f32; 8];
357 let mut r_samples = [0f32; 8];
358
359 for (i, &byte) in left_chunk.iter().enumerate() {
360 for (j, nibble) in [i32::from(byte & 0x0F), i32::from(byte >> 4)]
361 .into_iter()
362 .enumerate()
363 {
364 let step = IMA_STEP_TABLE
365 [usize::try_from(left_step_idx).expect("step_index is clamped to 0..88")];
366 let mut diff = step >> 3;
367 if (nibble & 4) != 0 {
368 diff += step;
369 }
370 if (nibble & 2) != 0 {
371 diff += step >> 1;
372 }
373 if (nibble & 1) != 0 {
374 diff += step >> 2;
375 }
376 if (nibble & 8) != 0 {
377 left_pred -= diff;
378 } else {
379 left_pred += diff;
380 }
381 left_pred = left_pred.clamp(-32768, 32767);
382 left_step_idx = (left_step_idx
383 + i32::from(
384 IMA_INDEX_TABLE[usize::try_from(nibble).expect("nibble is 0..15")],
385 ))
386 .clamp(0, 88);
387 l_samples[i * 2 + j] = normalize_sample(left_pred);
388 }
389 }
390
391 for (i, &byte) in right_chunk.iter().enumerate() {
392 for (j, nibble) in [i32::from(byte & 0x0F), i32::from(byte >> 4)]
393 .into_iter()
394 .enumerate()
395 {
396 let step = IMA_STEP_TABLE
397 [usize::try_from(right_step_idx).expect("step_index is clamped to 0..88")];
398 let mut diff = step >> 3;
399 if (nibble & 4) != 0 {
400 diff += step;
401 }
402 if (nibble & 2) != 0 {
403 diff += step >> 1;
404 }
405 if (nibble & 1) != 0 {
406 diff += step >> 2;
407 }
408 if (nibble & 8) != 0 {
409 right_pred -= diff;
410 } else {
411 right_pred += diff;
412 }
413 right_pred = right_pred.clamp(-32768, 32767);
414 right_step_idx = (right_step_idx
415 + i32::from(
416 IMA_INDEX_TABLE[usize::try_from(nibble).expect("nibble is 0..15")],
417 ))
418 .clamp(0, 88);
419 r_samples[i * 2 + j] = normalize_sample(right_pred);
420 }
421 }
422
423 for i in 0..8 {
424 output.push(l_samples[i]);
425 output.push(r_samples[i]);
426 }
427 }
428
429 Ok(())
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::wav::{WavEncodingCode, WavType, WavWaveMetadata};
436
437 fn make_wav(encoding: WavEncoding, channels: u16, block_align: u16, data: Vec<u8>) -> Wav {
438 Wav::new_wave(
439 WavType::Vo,
440 WavWaveMetadata {
441 encoding: WavEncodingCode::from(encoding),
442 channels,
443 sample_rate: 44100,
444 bytes_per_sec: 0,
445 block_align,
446 bits_per_sample: 4,
447 },
448 data,
449 )
450 }
451
452 #[test]
453 fn test_ms_adpcm_mono_header() {
454 let data = vec![
456 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, ];
461 let wav = make_wav(WavEncoding::MsAdpcm, 1, 7, data);
462 let samples = decode_adpcm_as_float(&wav).unwrap();
463 assert_eq!(samples.len(), 2);
464 assert_eq!(samples[0], 0.0);
465 assert_eq!(samples[1], 0.0);
466 }
467
468 #[test]
469 fn test_ima_adpcm_mono_header() {
470 let data = vec![
472 0x00, 0x00, 0x00, 0x00, ];
475 let wav = make_wav(WavEncoding::ImaAdpcm, 1, 4, data);
476 let samples = decode_adpcm_as_float(&wav).unwrap();
477 assert_eq!(samples.len(), 1);
478 assert_eq!(samples[0], 0.0);
479 }
480
481 #[test]
482 fn test_unsupported_encoding() {
483 let wav = make_wav(WavEncoding::Pcm, 1, 2, vec![]);
484 let err = decode_adpcm_as_float(&wav).unwrap_err();
485 assert!(matches!(err, AdpcmError::UnsupportedEncoding(_)));
486 }
487}