use crate::fourcc::FourCharCode;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[repr(transparent)]
pub struct AudioObjectId(pub u32);
impl AudioObjectId {
pub const UNKNOWN: Self = Self(0);
pub const PLUGIN: Self = Self(1);
pub const FIRST_DYNAMIC: Self = Self(2);
#[inline]
#[must_use]
pub const fn from_u32(value: u32) -> Self {
Self(value)
}
#[inline]
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
#[inline]
#[must_use]
pub const fn is_unknown(self) -> bool {
self.0 == 0
}
#[inline]
#[must_use]
pub const fn is_plugin(self) -> bool {
self.0 == Self::PLUGIN.0
}
}
impl From<u32> for AudioObjectId {
#[inline]
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<AudioObjectId> for u32 {
#[inline]
fn from(value: AudioObjectId) -> Self {
value.0
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum ObjectKind {
PlugIn,
Box,
Device,
Stream,
Control,
}
impl ObjectKind {
#[inline]
#[must_use]
pub const fn class_id(self) -> FourCharCode {
match self {
Self::PlugIn => FourCharCode::new(*b"aplg"),
Self::Box => FourCharCode::new(*b"abox"),
Self::Device => FourCharCode::new(*b"adev"),
Self::Stream => FourCharCode::new(*b"astr"),
Self::Control => FourCharCode::new(*b"actl"),
}
}
#[inline]
#[must_use]
pub const fn base_class_id(self) -> FourCharCode {
FourCharCode::new(*b"aobj")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reserved_ids_have_fixed_values() {
assert_eq!(AudioObjectId::UNKNOWN.as_u32(), 0);
assert_eq!(AudioObjectId::PLUGIN.as_u32(), 1);
assert_eq!(AudioObjectId::FIRST_DYNAMIC.as_u32(), 2);
}
#[test]
fn predicates_classify_reserved_ids() {
assert!(AudioObjectId::UNKNOWN.is_unknown());
assert!(!AudioObjectId::UNKNOWN.is_plugin());
assert!(AudioObjectId::PLUGIN.is_plugin());
assert!(!AudioObjectId::PLUGIN.is_unknown());
assert!(!AudioObjectId::from_u32(42).is_unknown());
assert!(!AudioObjectId::from_u32(42).is_plugin());
}
#[test]
fn round_trips_through_u32() {
for raw in [0, 1, 2, 99, u32::MAX] {
let id = AudioObjectId::from_u32(raw);
assert_eq!(id.as_u32(), raw);
let back: u32 = id.into();
assert_eq!(back, raw);
assert_eq!(AudioObjectId::from(raw), id);
}
}
#[test]
fn class_ids_match_core_audio_codes() {
assert_eq!(ObjectKind::PlugIn.class_id(), FourCharCode::new(*b"aplg"));
assert_eq!(ObjectKind::Box.class_id(), FourCharCode::new(*b"abox"));
assert_eq!(ObjectKind::Device.class_id(), FourCharCode::new(*b"adev"));
assert_eq!(ObjectKind::Stream.class_id(), FourCharCode::new(*b"astr"));
assert_eq!(ObjectKind::Control.class_id(), FourCharCode::new(*b"actl"));
}
#[test]
fn every_kind_bases_on_audio_object() {
for kind in [
ObjectKind::PlugIn,
ObjectKind::Box,
ObjectKind::Device,
ObjectKind::Stream,
ObjectKind::Control,
] {
assert_eq!(kind.base_class_id(), FourCharCode::new(*b"aobj"));
}
}
#[test]
fn ids_order_numerically() {
assert!(AudioObjectId::UNKNOWN < AudioObjectId::PLUGIN);
assert!(AudioObjectId::PLUGIN < AudioObjectId::FIRST_DYNAMIC);
}
#[test]
fn layout_is_transparent_u32() {
use core::mem::{align_of, size_of};
assert_eq!(size_of::<AudioObjectId>(), size_of::<u32>());
assert_eq!(align_of::<AudioObjectId>(), align_of::<u32>());
}
}