Files
pmomusic/pmoaudio/src/bit_depth.rs

163 lines
3.8 KiB
Rust
Raw Normal View History

//! Bit depth abstraction for audio processing.
//!
//! Provides both compile-time generic types (`Bit8`, `Bit16`, …)
//! and a dynamic `BitDepth` enum for runtime selection.
use std::fmt;
/// Trait implemented by compile-time bit-depth marker types.
pub trait BitDepthType {
const BITS: u32;
const MAX_VALUE: f32;
}
/// Compile-time bit depth markers
#[derive(Clone, Copy, Debug)]
pub struct Bit8;
#[derive(Clone, Copy, Debug)]
pub struct Bit16;
#[derive(Clone, Copy, Debug)]
pub struct Bit24;
#[derive(Clone, Copy, Debug)]
pub struct Bit32;
impl BitDepthType for Bit8 {
const BITS: u32 = 8;
const MAX_VALUE: f32 = 128.0;
}
impl BitDepthType for Bit16 {
const BITS: u32 = 16;
const MAX_VALUE: f32 = 32_768.0;
}
impl BitDepthType for Bit24 {
const BITS: u32 = 24;
const MAX_VALUE: f32 = 8_388_608.0;
}
impl BitDepthType for Bit32 {
const BITS: u32 = 32;
const MAX_VALUE: f32 = 2_147_483_648.0;
}
/// Runtime bit-depth descriptor.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BitDepth {
B8,
B16,
B24,
B32,
}
impl BitDepth {
/// Returns the number of bits.
#[inline(always)]
pub const fn bits(self) -> u32 {
match self {
BitDepth::B8 => 8,
BitDepth::B16 => 16,
BitDepth::B24 => 24,
BitDepth::B32 => 32,
}
}
/// Returns the full-scale signed maximum value as `f32`.
#[inline(always)]
pub const fn max_value(self) -> f32 {
match self {
BitDepth::B8 => 128.0,
BitDepth::B16 => 32_768.0,
BitDepth::B24 => 8_388_608.0,
BitDepth::B32 => 2_147_483_648.0,
}
}
/// Create from bit count, returning `None` if unsupported.
#[inline(always)]
pub const fn from_u32(bits: u32) -> Option<Self> {
match bits {
8 => Some(Self::B8),
16 => Some(Self::B16),
24 => Some(Self::B24),
32 => Some(Self::B32),
_ => None,
}
}
/// Create from bit count, panicking if unsupported (non-const).
#[inline(always)]
pub fn from_u32_strict(bits: u32) -> Self {
Self::from_u32(bits).unwrap_or_else(|| panic!("Unsupported bit depth: {}", bits))
}
}
impl fmt::Display for BitDepth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-bit", self.bits())
}
}
/// Comparaisons dordre fondées sur la valeur en bits.
impl PartialOrd for BitDepth {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BitDepth {
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bits().cmp(&other.bits())
}
}
/// Bridge between dynamic [`BitDepth`] and static [`BitDepthType`] markers.
///
/// Example:
/// ```
/// use pmoaudio::{
/// bit_depth::dispatch_by_bitdepth, Bit8, Bit16, Bit24, Bit32, BitDepth,
/// };
/// use pmoaudio::bit_depth::BitDepthType;
///
/// fn type_bits<B: BitDepthType>() -> u32 {
/// B::BITS
/// }
///
/// let depth = BitDepth::B16;
/// let bits = dispatch_by_bitdepth(
/// depth,
/// || type_bits::<Bit8>(),
/// || type_bits::<Bit16>(),
/// || type_bits::<Bit24>(),
/// || type_bits::<Bit32>(),
/// );
/// assert_eq!(bits, 16);
/// ```
#[inline(always)]
pub fn dispatch_by_bitdepth<R, F8, F16, F24, F32>(
depth: BitDepth,
f8: F8,
f16: F16,
f24: F24,
f32: F32,
) -> R
where
F8: FnOnce() -> R,
F16: FnOnce() -> R,
F24: FnOnce() -> R,
F32: FnOnce() -> R,
{
match depth {
BitDepth::B8 => f8(),
BitDepth::B16 => f16(),
BitDepth::B24 => f24(),
BitDepth::B32 => f32(),
}
}
/// Conversion helper from a runtime [`BitDepth`] to a compile-time constant.
#[inline(always)]
pub fn max_value_for(depth: BitDepth) -> f32 {
depth.max_value()
}