Files
pmomusic/pmoaudio/src/bit_depth.rs

163 lines
3.8 KiB
Rust
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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()
}