use std::borrow::{Borrow, BorrowMut};
use std::ffi::{CStr, CString};
use std::borrow::Cow;
use num_derive::{FromPrimitive, ToPrimitive};
use crate::sample;
pub use capi::pa_channel_map_def_t as MapDef;
pub type PositionMask = capi::channelmap::pa_channel_position_mask_t;
pub const POSITION_MASK_ALL: PositionMask = 0xffffffffffffffffu64;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[derive(FromPrimitive, ToPrimitive)]
pub enum Position {
#[default]
Invalid = -1,
Mono = 0,
FrontLeft,
FrontRight,
FrontCenter,
RearCenter,
RearLeft,
RearRight,
Lfe,
FrontLeftOfCenter,
FrontRightOfCenter,
SideLeft,
SideRight,
Aux0,
Aux1,
Aux2,
Aux3,
Aux4,
Aux5,
Aux6,
Aux7,
Aux8,
Aux9,
Aux10,
Aux11,
Aux12,
Aux13,
Aux14,
Aux15,
Aux16,
Aux17,
Aux18,
Aux19,
Aux20,
Aux21,
Aux22,
Aux23,
Aux24,
Aux25,
Aux26,
Aux27,
Aux28,
Aux29,
Aux30,
Aux31,
TopCenter,
TopFrontLeft,
TopFrontRight,
TopFrontCenter,
TopRearLeft,
TopRearRight,
TopRearCenter,
}
#[test]
fn pos_compare_capi() {
assert_eq!(std::mem::size_of::<Position>(), std::mem::size_of::<capi::pa_channel_position_t>());
assert_eq!(std::mem::align_of::<Position>(), std::mem::align_of::<capi::pa_channel_position_t>());
assert_eq!(Position::Invalid, Position::from(capi::pa_channel_position_t::Invalid));
assert_eq!(Position::Mono, Position::from(capi::pa_channel_position_t::Mono));
assert_eq!(Position::FrontLeft, Position::from(capi::pa_channel_position_t::FrontLeft));
assert_eq!(Position::FrontRight, Position::from(capi::pa_channel_position_t::FrontRight));
assert_eq!(Position::FrontCenter, Position::from(capi::pa_channel_position_t::FrontCenter));
assert_eq!(Position::RearCenter, Position::from(capi::pa_channel_position_t::RearCenter));
assert_eq!(Position::RearLeft, Position::from(capi::pa_channel_position_t::RearLeft));
assert_eq!(Position::RearRight, Position::from(capi::pa_channel_position_t::RearRight));
assert_eq!(Position::Lfe, Position::from(capi::pa_channel_position_t::Lfe));
assert_eq!(Position::FrontLeftOfCenter, Position::from(capi::pa_channel_position_t::FrontLeftOfCenter));
assert_eq!(Position::FrontRightOfCenter, Position::from(capi::pa_channel_position_t::FrontRightOfCenter));
assert_eq!(Position::SideLeft, Position::from(capi::pa_channel_position_t::SideLeft));
assert_eq!(Position::SideRight, Position::from(capi::pa_channel_position_t::SideRight));
assert_eq!(Position::Aux0, Position::from(capi::pa_channel_position_t::Aux0));
assert_eq!(Position::Aux1, Position::from(capi::pa_channel_position_t::Aux1));
assert_eq!(Position::Aux2, Position::from(capi::pa_channel_position_t::Aux2));
assert_eq!(Position::Aux3, Position::from(capi::pa_channel_position_t::Aux3));
assert_eq!(Position::Aux4, Position::from(capi::pa_channel_position_t::Aux4));
assert_eq!(Position::Aux5, Position::from(capi::pa_channel_position_t::Aux5));
assert_eq!(Position::Aux6, Position::from(capi::pa_channel_position_t::Aux6));
assert_eq!(Position::Aux7, Position::from(capi::pa_channel_position_t::Aux7));
assert_eq!(Position::Aux8, Position::from(capi::pa_channel_position_t::Aux8));
assert_eq!(Position::Aux9, Position::from(capi::pa_channel_position_t::Aux9));
assert_eq!(Position::Aux10, Position::from(capi::pa_channel_position_t::Aux10));
assert_eq!(Position::Aux11, Position::from(capi::pa_channel_position_t::Aux11));
assert_eq!(Position::Aux12, Position::from(capi::pa_channel_position_t::Aux12));
assert_eq!(Position::Aux13, Position::from(capi::pa_channel_position_t::Aux13));
assert_eq!(Position::Aux14, Position::from(capi::pa_channel_position_t::Aux14));
assert_eq!(Position::Aux15, Position::from(capi::pa_channel_position_t::Aux15));
assert_eq!(Position::Aux16, Position::from(capi::pa_channel_position_t::Aux16));
assert_eq!(Position::Aux17, Position::from(capi::pa_channel_position_t::Aux17));
assert_eq!(Position::Aux18, Position::from(capi::pa_channel_position_t::Aux18));
assert_eq!(Position::Aux19, Position::from(capi::pa_channel_position_t::Aux19));
assert_eq!(Position::Aux20, Position::from(capi::pa_channel_position_t::Aux20));
assert_eq!(Position::Aux21, Position::from(capi::pa_channel_position_t::Aux21));
assert_eq!(Position::Aux22, Position::from(capi::pa_channel_position_t::Aux22));
assert_eq!(Position::Aux23, Position::from(capi::pa_channel_position_t::Aux23));
assert_eq!(Position::Aux24, Position::from(capi::pa_channel_position_t::Aux24));
assert_eq!(Position::Aux25, Position::from(capi::pa_channel_position_t::Aux25));
assert_eq!(Position::Aux26, Position::from(capi::pa_channel_position_t::Aux26));
assert_eq!(Position::Aux27, Position::from(capi::pa_channel_position_t::Aux27));
assert_eq!(Position::Aux28, Position::from(capi::pa_channel_position_t::Aux28));
assert_eq!(Position::Aux29, Position::from(capi::pa_channel_position_t::Aux29));
assert_eq!(Position::Aux30, Position::from(capi::pa_channel_position_t::Aux30));
assert_eq!(Position::Aux31, Position::from(capi::pa_channel_position_t::Aux31));
assert_eq!(Position::TopCenter, Position::from(capi::pa_channel_position_t::TopCenter));
assert_eq!(Position::TopFrontLeft, Position::from(capi::pa_channel_position_t::TopFrontLeft));
assert_eq!(Position::TopFrontRight, Position::from(capi::pa_channel_position_t::TopFrontRight));
assert_eq!(Position::TopFrontCenter, Position::from(capi::pa_channel_position_t::TopFrontCenter));
assert_eq!(Position::TopRearLeft, Position::from(capi::pa_channel_position_t::TopRearLeft));
assert_eq!(Position::TopRearRight, Position::from(capi::pa_channel_position_t::TopRearRight));
assert_eq!(Position::TopRearCenter, Position::from(capi::pa_channel_position_t::TopRearCenter));
}
impl From<Position> for capi::pa_channel_position_t {
#[inline]
fn from(p: Position) -> Self {
unsafe { std::mem::transmute(p) }
}
}
impl From<capi::pa_channel_position_t> for Position {
#[inline]
fn from(p: capi::pa_channel_position_t) -> Self {
unsafe { std::mem::transmute(p) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Map {
channels: u8,
map: [Position; Self::CHANNELS_MAX as usize],
}
impl Borrow<[Position]> for Map {
fn borrow(&self) -> &[Position] {
&self.map[..self.channels as usize]
}
}
impl BorrowMut<[Position]> for Map {
fn borrow_mut(&mut self) -> &mut [Position] {
&mut self.map[..self.channels as usize]
}
}
#[test]
fn map_compare_capi() {
assert_eq!(std::mem::size_of::<Map>(), std::mem::size_of::<capi::pa_channel_map>());
assert_eq!(std::mem::align_of::<Map>(), std::mem::align_of::<capi::pa_channel_map>());
}
impl AsRef<capi::pa_channel_map> for Map {
#[inline]
fn as_ref(&self) -> &capi::pa_channel_map {
unsafe { &*(self as *const Self as *const capi::pa_channel_map) }
}
}
impl AsMut<capi::pa_channel_map> for Map {
#[inline]
fn as_mut(&mut self) -> &mut capi::pa_channel_map {
unsafe { &mut *(self as *mut Self as *mut capi::pa_channel_map) }
}
}
impl AsRef<Map> for capi::pa_channel_map {
#[inline]
fn as_ref(&self) -> &Map {
unsafe { &*(self as *const Self as *const Map) }
}
}
impl From<capi::pa_channel_map> for Map {
#[inline]
fn from(m: capi::pa_channel_map) -> Self {
unsafe { std::mem::transmute(m) }
}
}
impl Default for Map {
fn default() -> Self {
Self { channels: 0, map: [Position::Invalid; Self::CHANNELS_MAX as usize] }
}
}
impl PartialEq for Map {
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe { capi::pa_channel_map_equal(self.as_ref(), other.as_ref()) == 1 }
}
}
impl Position {
pub const fn to_mask(self) -> PositionMask {
match self {
Position::Invalid => 0,
_ => (1 as PositionMask) << (self as PositionMask),
}
}
pub fn to_string(pos: Self) -> Option<Cow<'static, str>> {
let ptr = unsafe { capi::pa_channel_position_to_string(pos.into()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
true => None,
}
}
pub fn to_pretty_string(pos: Self) -> Option<String> {
let ptr = unsafe { capi::pa_channel_position_to_pretty_string(pos.into()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
true => None,
}
}
pub fn from_string(s: &str) -> Self {
let c_str = CString::new(s).unwrap();
unsafe { capi::pa_channel_position_from_string(c_str.as_ptr()).into() }
}
}
impl Map {
pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
pub fn new_from_string(s: &str) -> Result<Self, ()> {
let c_str = CString::new(s).unwrap();
let mut map: Self = Self::default();
unsafe {
if capi::pa_channel_map_parse((&mut map).as_mut(), c_str.as_ptr()).is_null() {
return Err(());
}
}
Ok(map)
}
#[inline]
pub fn init(&mut self) -> &mut Self {
unsafe { capi::pa_channel_map_init(self.as_mut()) };
self
}
#[inline]
pub fn init_mono(&mut self) -> &mut Self {
unsafe { capi::pa_channel_map_init_mono(self.as_mut()) };
self
}
#[inline]
pub fn init_stereo(&mut self) -> &mut Self {
unsafe { capi::pa_channel_map_init_stereo(self.as_mut()) };
self
}
pub fn init_auto(&mut self, channels: u8, def: MapDef) -> Option<&mut Self> {
debug_assert!(channels <= Self::CHANNELS_MAX);
unsafe {
if capi::pa_channel_map_init_auto(self.as_mut(), channels as u32, def).is_null() {
return None;
}
}
Some(self)
}
pub fn init_extend(&mut self, channels: u8, def: MapDef) -> &mut Self {
debug_assert!(channels <= Self::CHANNELS_MAX);
unsafe { capi::pa_channel_map_init_extend(self.as_mut(), channels as u32, def) };
self
}
#[inline]
pub fn is_valid(&self) -> bool {
unsafe { capi::pa_channel_map_valid(self.as_ref()) != 0 }
}
#[inline]
pub const fn len(&self) -> u8 {
self.channels
}
#[inline]
pub fn set_len(&mut self, channels: u8) {
assert!(channels <= Self::CHANNELS_MAX);
self.channels = channels;
}
#[inline]
pub fn get(&self) -> &[Position] {
self.borrow()
}
#[inline]
pub fn get_mut(&mut self) -> &mut [Position] {
self.borrow_mut()
}
pub fn print(&self) -> String {
const PRINT_MAX: usize = capi::PA_CHANNEL_MAP_SNPRINT_MAX;
let mut tmp = Vec::with_capacity(PRINT_MAX);
unsafe {
capi::pa_channel_map_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
}
}
#[inline]
pub fn is_compatible_with_sample_spec(&self, ss: &sample::Spec) -> bool {
unsafe { capi::pa_channel_map_compatible(self.as_ref(), ss.as_ref()) != 0 }
}
#[inline]
pub fn is_superset_of(&self, of: &Self) -> bool {
unsafe { capi::pa_channel_map_superset(self.as_ref(), of.as_ref()) != 0 }
}
#[inline]
pub fn can_balance(&self) -> bool {
unsafe { capi::pa_channel_map_can_balance(self.as_ref()) != 0 }
}
#[inline]
pub fn can_fade(&self) -> bool {
unsafe { capi::pa_channel_map_can_fade(self.as_ref()) != 0 }
}
#[inline]
#[cfg(any(doc, feature = "pa_v8"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))]
pub fn can_lfe_balance(&self) -> bool {
unsafe { capi::pa_channel_map_can_lfe_balance(self.as_ref()) != 0 }
}
pub fn to_name(&self) -> Option<Cow<'static, str>> {
let ptr = unsafe { capi::pa_channel_map_to_name(self.as_ref()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
true => None,
}
}
pub fn to_pretty_name(&self) -> Option<String> {
let ptr = unsafe { capi::pa_channel_map_to_pretty_name(self.as_ref()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
true => None,
}
}
#[inline]
pub fn has_position(&self, p: Position) -> bool {
unsafe { capi::pa_channel_map_has_position(self.as_ref(), p.into()) != 0 }
}
#[inline]
pub fn get_mask(&self) -> PositionMask {
unsafe { capi::pa_channel_map_mask(self.as_ref()) }
}
}