use std::ffi::{CStr, CString};
use std::borrow::Cow;
use num_derive::{FromPrimitive, ToPrimitive};
use crate::time::MicroSeconds;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[derive(FromPrimitive, ToPrimitive)]
#[allow(non_camel_case_types)]
pub enum Format {
U8,
ALaw,
ULaw,
S16le,
S16be,
F32le,
F32be,
S32le,
S32be,
S24le,
S24be,
S24_32le,
S24_32be,
#[default]
Invalid = -1,
}
#[test]
fn format_compare_capi() {
assert_eq!(std::mem::size_of::<Format>(), std::mem::size_of::<capi::pa_sample_format_t>());
assert_eq!(std::mem::align_of::<Format>(), std::mem::align_of::<capi::pa_sample_format_t>());
assert_eq!(Format::U8, Format::from(capi::pa_sample_format_t::U8));
assert_eq!(Format::ALaw, Format::from(capi::pa_sample_format_t::ALaw));
assert_eq!(Format::ULaw, Format::from(capi::pa_sample_format_t::ULaw));
assert_eq!(Format::S16le, Format::from(capi::pa_sample_format_t::S16le));
assert_eq!(Format::S16be, Format::from(capi::pa_sample_format_t::S16be));
assert_eq!(Format::F32le, Format::from(capi::pa_sample_format_t::F32le));
assert_eq!(Format::F32be, Format::from(capi::pa_sample_format_t::F32be));
assert_eq!(Format::S32le, Format::from(capi::pa_sample_format_t::S32le));
assert_eq!(Format::S32be, Format::from(capi::pa_sample_format_t::S32be));
assert_eq!(Format::S24le, Format::from(capi::pa_sample_format_t::S24le));
assert_eq!(Format::S24be, Format::from(capi::pa_sample_format_t::S24be));
assert_eq!(Format::S24_32le, Format::from(capi::pa_sample_format_t::S24_32le));
assert_eq!(Format::S24_32be, Format::from(capi::pa_sample_format_t::S24_32be));
assert_eq!(Format::Invalid, Format::from(capi::pa_sample_format_t::Invalid));
}
impl From<Format> for capi::pa_sample_format_t {
#[inline]
fn from(f: Format) -> Self {
unsafe { std::mem::transmute(f) }
}
}
impl From<capi::pa_sample_format_t> for Format {
#[inline]
fn from(f: capi::pa_sample_format_t) -> Self {
unsafe { std::mem::transmute(f) }
}
}
#[cfg(target_endian = "big")]
mod ei_formats {
use super::Format;
pub const SAMPLE_S16NE: Format = Format::S16be;
pub const SAMPLE_FLOAT32NE: Format = Format::F32be;
pub const SAMPLE_S32NE: Format = Format::S32be;
pub const SAMPLE_S24NE: Format = Format::S24be;
pub const SAMPLE_S24_32NE: Format = Format::S24_32be;
pub const SAMPLE_S16RE: Format = Format::S16le;
pub const SAMPLE_FLOAT32RE: Format = Format::F32le;
pub const SAMPLE_S32RE: Format = Format::S32le;
pub const SAMPLE_S24RE: Format = Format::S24le;
pub const SAMPLE_S24_32RE: Format = Format::S24_32le;
}
#[cfg(target_endian = "little")]
mod ei_formats {
use super::Format;
pub const SAMPLE_S16NE: Format = Format::S16le;
pub const SAMPLE_FLOAT32NE: Format = Format::F32le;
pub const SAMPLE_S32NE: Format = Format::S32le;
pub const SAMPLE_S24NE: Format = Format::S24le;
pub const SAMPLE_S24_32NE: Format = Format::S24_32le;
pub const SAMPLE_S16RE: Format = Format::S16be;
pub const SAMPLE_FLOAT32RE: Format = Format::F32be;
pub const SAMPLE_S32RE: Format = Format::S32be;
pub const SAMPLE_S24RE: Format = Format::S24be;
pub const SAMPLE_S24_32RE: Format = Format::S24_32be;
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq)]
pub struct Spec {
pub format: Format,
pub rate: u32,
pub channels: u8,
}
#[test]
fn spec_compare_capi() {
assert_eq!(std::mem::size_of::<Spec>(), std::mem::size_of::<capi::pa_sample_spec>());
assert_eq!(std::mem::align_of::<Spec>(), std::mem::align_of::<capi::pa_sample_spec>());
}
impl AsRef<capi::pa_sample_spec> for Spec {
#[inline]
fn as_ref(&self) -> &capi::pa_sample_spec {
unsafe { &*(self as *const Self as *const capi::pa_sample_spec) }
}
}
impl AsMut<capi::pa_sample_spec> for Spec {
#[inline]
fn as_mut(&mut self) -> &mut capi::pa_sample_spec {
unsafe { &mut *(self as *mut Self as *mut capi::pa_sample_spec) }
}
}
impl AsRef<Spec> for capi::pa_sample_spec {
#[inline]
fn as_ref(&self) -> &Spec {
unsafe { &*(self as *const Self as *const Spec) }
}
}
impl From<capi::pa_sample_spec> for Spec {
#[inline]
fn from(s: capi::pa_sample_spec) -> Self {
unsafe { std::mem::transmute(s) }
}
}
impl PartialEq for Spec {
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe { capi::pa_sample_spec_equal(self.as_ref(), other.as_ref()) != 0 }
}
}
impl Spec {
pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
pub const RATE_MAX: u32 = capi::PA_RATE_MAX;
#[inline]
pub fn init(&mut self) {
unsafe { capi::pa_sample_spec_init(self.as_mut()); }
}
#[inline]
pub fn is_valid(&self) -> bool {
unsafe { capi::pa_sample_spec_valid(self.as_ref()) != 0 }
}
#[inline]
pub fn format_is_valid(&self) -> bool {
unsafe { capi::pa_sample_format_valid(self.format as u32) != 0 }
}
#[inline]
pub fn rate_is_valid(&self) -> bool {
unsafe { capi::pa_sample_rate_valid(self.rate) != 0 }
}
#[inline]
pub fn channels_are_valid(&self) -> bool {
unsafe { capi::pa_channels_valid(self.channels) != 0 }
}
#[inline]
pub fn bytes_per_second(&self) -> usize {
unsafe { capi::pa_bytes_per_second(self.as_ref()) }
}
#[inline]
pub fn frame_size(&self) -> usize {
unsafe { capi::pa_frame_size(self.as_ref()) }
}
#[inline]
pub fn sample_size(&self) -> usize {
unsafe { capi::pa_sample_size(self.as_ref()) }
}
#[inline]
pub fn bytes_to_usec(&self, length: u64) -> MicroSeconds {
MicroSeconds(unsafe { capi::pa_bytes_to_usec(length, self.as_ref()) })
}
#[inline]
pub fn usec_to_bytes(&self, t: MicroSeconds) -> usize {
unsafe { capi::pa_usec_to_bytes(t.0, self.as_ref()) }
}
pub fn print(&self) -> String {
const PRINT_MAX: usize = capi::PA_SAMPLE_SPEC_SNPRINT_MAX;
let mut tmp = Vec::with_capacity(PRINT_MAX);
unsafe {
capi::pa_sample_spec_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
}
}
}
pub fn bytes_print(bytes: u32) -> String {
const PRINT_MAX: usize = capi::PA_BYTES_SNPRINT_MAX;
let mut tmp = Vec::with_capacity(PRINT_MAX);
unsafe {
capi::pa_bytes_snprint(tmp.as_mut_ptr(), PRINT_MAX, bytes);
CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
}
}
impl Format {
pub const S16NE: Self = self::ei_formats::SAMPLE_S16NE;
pub const FLOAT32NE: Self = self::ei_formats::SAMPLE_FLOAT32NE;
pub const S32NE: Self = self::ei_formats::SAMPLE_S32NE;
pub const S24NE: Self = self::ei_formats::SAMPLE_S24NE;
pub const S24_32NE: Self = self::ei_formats::SAMPLE_S24_32NE;
pub const S16RE: Self = self::ei_formats::SAMPLE_S16RE;
pub const FLOAT32RE: Self = self::ei_formats::SAMPLE_FLOAT32RE;
pub const S32RE: Self = self::ei_formats::SAMPLE_S32RE;
pub const S24RE: Self = self::ei_formats::SAMPLE_S24RE;
pub const S24_32RE: Self = self::ei_formats::SAMPLE_S24_32RE;
#[inline]
pub fn size(&self) -> usize {
unsafe { capi::pa_sample_size_of_format((*self).into()) }
}
pub fn to_string(&self) -> Option<Cow<'static, str>> {
let ptr = unsafe { capi::pa_sample_format_to_string((*self).into()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
true => None,
}
}
pub fn parse(format: &str) -> Self {
let c_format = CString::new(format).unwrap();
unsafe { capi::pa_parse_sample_format(c_format.as_ptr()).into() }
}
pub fn is_le(&self) -> Option<bool> {
match unsafe { capi::pa_sample_format_is_le((*self).into()) } {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
pub fn is_be(&self) -> Option<bool> {
match unsafe { capi::pa_sample_format_is_be((*self).into()) } {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
#[inline]
pub fn is_ne(&self) -> Option<bool> {
#[cfg(target_endian = "big")]
{ Format::is_be(self) }
#[cfg(target_endian = "little")]
{ Format::is_le(self) }
}
#[inline]
pub fn is_re(&self) -> Option<bool> {
self.is_ne().and_then(|b| Some(!b))
}
}