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