Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Thom Chiovoloni <chiovolonit@gmail.com>
#
# Please keep this section sorted in ascending order.

acieslewicz [https://github.com/acieslewicz]
aschey [https://github.com/aschey]
BlackHoleFox [https://github.com/blackholefox]
darksv [https://github.com/darksv]
Expand All @@ -26,6 +27,7 @@ erikas-taroza [https://github.com/erikas-taroza]
FelixMcFelix [https://github.com/FelixMcFelix]
geckoxx [https://github.com/geckoxx]
Herohtar [https://github.com/herohtar]
hikari_no_yume [https://github.com/hikari-no-yume]
nicholaswyoung [https://github.com/nicholaswyoung]
richardmitic [https://github.com/richardmitic]
sscobici [https://github.com/sscobici]
Expand Down
102 changes: 0 additions & 102 deletions symphonia-codec-adpcm/src/codec_ima.rs

This file was deleted.

47 changes: 47 additions & 0 deletions symphonia-codec-adpcm/src/codec_ima_qt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Symphonia
// Copyright (c) 2019-2025 The Project Symphonia Developers.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::errors::Result;
use symphonia_core::io::ReadBytes;

use crate::common::{u16_to_i32, Nibble};
use crate::common_ima::AdpcmImaBlockStatus;

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

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

pub(crate) fn decode_mono<B: ReadBytes>(
stream: &mut B,
buffer: &mut [i32],
_: usize,
) -> Result<()> {
// IMA4 apparently always uses 34 bytes packets
// https://wiki.multimedia.cx/index.php/Apple_QuickTime_IMA_ADPCM
let mut status = read_preamble(stream)?;
for byte in 0..32 {
let nibbles = stream.read_u8()?;
buffer[byte * 2] = status.expand_nibble(nibbles, Nibble::Lower);
buffer[byte * 2 + 1] = status.expand_nibble(nibbles, Nibble::Upper);
}
Ok(())
}

pub(crate) fn decode_stereo<B: ReadBytes>(
stream: &mut B,
buffers: [&mut [i32]; 2],
_: usize,
) -> Result<()> {
decode_mono(stream, buffers[0], 0)?;
decode_mono(stream, buffers[1], 0)?;
Ok(())
}
63 changes: 63 additions & 0 deletions symphonia-codec-adpcm/src/codec_ima_wav.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Symphonia
// Copyright (c) 2019-2022 The Project Symphonia Developers.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::errors::{decode_error, Result};
use symphonia_core::io::ReadBytes;

use crate::common::{from_i16_shift, u16_to_i32, Nibble};
use crate::common_ima::AdpcmImaBlockStatus;

fn read_preamble<B: ReadBytes>(stream: &mut B) -> Result<AdpcmImaBlockStatus> {
let predictor = u16_to_i32!(stream.read_u16()?);
let step_index = stream.read_byte()? as i32;
if step_index > 88 {
return decode_error("adpcm (ima): invalid step index");
}
//reserved byte
let _ = stream.read_byte()?;
let status = AdpcmImaBlockStatus { predictor, step_index };
Ok(status)
}

pub(crate) fn decode_mono<B: ReadBytes>(
stream: &mut B,
buffer: &mut [i32],
frames_per_block: usize,
) -> Result<()> {
let data_bytes_per_channel = (frames_per_block - 1) / 2;
let mut status = read_preamble(stream)?;
buffer[0] = from_i16_shift!(status.predictor);
for byte in 0..data_bytes_per_channel {
let nibbles = stream.read_u8()?;
buffer[1 + byte * 2] = status.expand_nibble(nibbles, Nibble::Lower);
buffer[1 + byte * 2 + 1] = status.expand_nibble(nibbles, Nibble::Upper);
}
Ok(())
}

pub(crate) fn decode_stereo<B: ReadBytes>(
stream: &mut B,
buffers: [&mut [i32]; 2],
frames_per_block: usize,
) -> Result<()> {
let data_bytes_per_channel = frames_per_block - 1;
let mut status =
[read_preamble(stream)?, read_preamble(stream)?];
buffers[0][0] = from_i16_shift!(status[0].predictor);
buffers[1][0] = from_i16_shift!(status[1].predictor);
for index in 0..data_bytes_per_channel {
let channel = (index / 4) & 1;
let offset = (index / 8) * 8;
let byte = index % 4;
let nibbles = stream.read_u8()?;
buffers[channel][1 + offset + byte * 2] =
status[channel].expand_nibble(nibbles, Nibble::Lower);
buffers[channel][1 + offset + byte * 2 + 1] =
status[channel].expand_nibble(nibbles, Nibble::Upper);
}
Ok(())
}
49 changes: 49 additions & 0 deletions symphonia-codec-adpcm/src/common_ima.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Symphonia
// Copyright (c) 2019-2022 The Project Symphonia Developers.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::util::clamp::clamp_i16;

use crate::common::{from_i16_shift, Nibble};

#[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]
pub(crate) 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
pub(crate) struct AdpcmImaBlockStatus {
pub(crate) predictor: i32,
pub(crate) step_index: i32,
}

impl AdpcmImaBlockStatus {
pub(crate) fn expand_nibble(&mut self, byte: u8, nibble: Nibble) -> i32 {
let nibble = nibble.get_nibble(byte);
let step = IMA_STEP_TABLE[self.step_index as usize];
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 + IMA_INDEX_TABLE[nibble as usize]).clamp(0, 88);
from_i16_shift!(self.predictor)
}
}
19 changes: 14 additions & 5 deletions symphonia-codec-adpcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,37 @@ use symphonia_core::support_audio_codec;
use symphonia_core::audio::{
AsGenericAudioBufferRef, Audio, AudioBuffer, AudioMut, AudioSpec, GenericAudioBufferRef,
};
use symphonia_core::codecs::audio::well_known::{CODEC_ID_ADPCM_IMA_WAV, CODEC_ID_ADPCM_MS};
use symphonia_core::codecs::audio::well_known::{
CODEC_ID_ADPCM_IMA_QT, CODEC_ID_ADPCM_IMA_WAV, CODEC_ID_ADPCM_MS,
};
use symphonia_core::codecs::audio::{AudioCodecId, AudioCodecParameters, AudioDecoderOptions};
use symphonia_core::codecs::audio::{AudioDecoder, FinalizeResult};
use symphonia_core::errors::{unsupported_error, Result};
use symphonia_core::formats::Packet;
use symphonia_core::io::ReadBytes;

mod codec_ima;
mod codec_ima_wav;
mod codec_ima_qt;
mod codec_ms;
mod common_ima;
mod common;

fn is_supported_adpcm_codec(codec_id: AudioCodecId) -> bool {
matches!(codec_id, CODEC_ID_ADPCM_MS | CODEC_ID_ADPCM_IMA_WAV)
matches!(codec_id, CODEC_ID_ADPCM_MS | CODEC_ID_ADPCM_IMA_WAV | CODEC_ID_ADPCM_IMA_QT)
}

enum InnerDecoder {
AdpcmMs,
AdpcmIma,
AdpcmImaQT,
}

impl InnerDecoder {
fn decode_mono_fn<B: ReadBytes>(&self) -> impl Fn(&mut B, &mut [i32], usize) -> Result<()> {
match *self {
InnerDecoder::AdpcmMs => codec_ms::decode_mono,
InnerDecoder::AdpcmIma => codec_ima::decode_mono,
InnerDecoder::AdpcmIma => codec_ima_wav::decode_mono,
InnerDecoder::AdpcmImaQT => codec_ima_qt::decode_mono,
}
}

Expand All @@ -54,7 +60,8 @@ impl InnerDecoder {
) -> impl Fn(&mut B, [&mut [i32]; 2], usize) -> Result<()> {
match *self {
InnerDecoder::AdpcmMs => codec_ms::decode_stereo,
InnerDecoder::AdpcmIma => codec_ima::decode_stereo,
InnerDecoder::AdpcmIma => codec_ima_wav::decode_stereo,
InnerDecoder::AdpcmImaQT => codec_ima_qt::decode_stereo,
}
}
}
Expand Down Expand Up @@ -101,6 +108,7 @@ impl AdpcmDecoder {
let inner_decoder = match params.codec {
CODEC_ID_ADPCM_MS => InnerDecoder::AdpcmMs,
CODEC_ID_ADPCM_IMA_WAV => InnerDecoder::AdpcmIma,
CODEC_ID_ADPCM_IMA_QT => InnerDecoder::AdpcmImaQT,
_ => return unsupported_error("adpcm: codec is unsupported"),
};

Expand Down Expand Up @@ -199,6 +207,7 @@ impl RegisterableAudioDecoder for AdpcmDecoder {
&[
support_audio_codec!(CODEC_ID_ADPCM_MS, "adpcm_ms", "Microsoft ADPCM"),
support_audio_codec!(CODEC_ID_ADPCM_IMA_WAV, "adpcm_ima_wav", "ADPCM IMA WAV"),
support_audio_codec!(CODEC_ID_ADPCM_IMA_QT, "adpcm_ima_qt", "ADPCM IMA QT"),
]
}
}