coreaudio 0.5.0

A safe and simple wrapper around the CoreAudio HAL
Documentation
//! Marker traits and builder traits for compile-time property validation
//!
//! These traits are used as bounds on `get_property`, `set_property`, and
//! `add_listener` to reject invalid calls at compile time with actionable
//! error messages
//! They also provide the builder methods (`.with_qualifier()`,
//! `.for_element()`) that complete partially-specified property constants
//! before they can be used

use std::ptr::NonNull;

use objc2_core_audio::AudioObjectPropertyAddress;

// ---- Imports ------------
use crate::{Device, Global, Process, Property, SampleRateRange, Stream, System, Tap, property::{Listenable, NeedBoth, NeedElement, NeedQualifier, NoExtra, Qualifier, ReadWrite}};
use crate::encode::{bytes_u32, bytes_vec_u32};

// ---- Traits ------------

/// Asserts that a property's object type is compatible with an `AudioObject<T>`
///
/// Implemented for the pairings that CoreAudio permits:
/// - `System` and `Global` properties work with `AudioObject<System>`
/// - `Device` and `Global` properties work with `AudioObject<Device>`
/// - `Stream` and `Global` properties work with `AudioObject<Stream>`
/// - `Process` and `Global` properties work with `AudioObject<Process>`
/// - `Tap` and `Global` properties work with `AudioObject<Tap>`
#[diagnostic::on_unimplemented(
    message = "`{Self}` properties are incompatible with `AudioObject<{T}>`",
    label = "Use this with `AudioObject<{Self}>` instead",
)]
pub trait ObjectCompatibleWith<T> {}

impl ObjectCompatibleWith<System> for System {}
impl ObjectCompatibleWith<Device> for Device {}
impl ObjectCompatibleWith<Stream> for Stream {}
impl ObjectCompatibleWith<Process> for Process {}
impl ObjectCompatibleWith<Tap> for Tap {}

impl ObjectCompatibleWith<System> for Global {}
impl ObjectCompatibleWith<Device> for Global {}
impl ObjectCompatibleWith<Stream> for Global {}
impl ObjectCompatibleWith<Process> for Global {}
impl ObjectCompatibleWith<Tap> for Global {}

/// Asserts that a property's access mode permits writing
///
/// Only `ReadWrite` properties implement this trait; attempting to call
/// `set_property` with a `ReadOnly` property produces a compile error
#[diagnostic::on_unimplemented(
    message = "this property is read-only",
    label = "`set_property()` requires properties with `ReadWrite`",
)]
pub trait Writeable {}

impl Writeable for ReadWrite {}

/// Asserts that a property supports change listeners
///
/// Only `Listenable` properties implement this trait; attempting to call
/// `add_listener` with a `Silent` property produces a compile error
#[diagnostic::on_unimplemented(
    message = "this property does not support listeners",
    label = "`add_listener()` requires propertes with `Listenable`",
)]
pub trait CanListen {}

impl CanListen for Listenable {}

/// Asserts that a property has all required data (element and qualifier) set
///
/// Properties with an `E` parameter of `NoExtra` are fully specified and can
/// be passed directly to `get_property`, `set_property`, or `add_listener`
/// Properties with `NeedElement`, `NeedQualifier`, or `NeedBoth` must first be
/// completed via `.for_element()` or `.with_qualifier()`
#[diagnostic::on_unimplemented(
    message = "this property requires additional data before it can be used",
    label = "check for `.with_qualifier()` or `.for_element()` methods on this property",
)]
pub trait HasAllData {}

impl HasAllData for NoExtra {}

/// Converts a qualifier value into raw bytes for a property address
///
/// Implement this for any type that can serve as qualifier data
/// The built-in implementations cover the types accepted by CoreAudio
/// qualifiers (`u32`, `Vec<u32>`, and `String`)
#[diagnostic::on_unimplemented(
    message = "incorrect qualifier data type",
    label = "check the `.with_qualifier()` method for expected type",
)]
pub trait IntoQualifierBytes {
    /// Serialises the qualifier value into a byte vector suitable for passing
    /// to CoreAudio
    fn into_bytes(self) -> Vec<u8>;

    /// The qualifier CoreAudio is handed
    /// By default just `into_bytes`; types CoreAudio takes by reference
    /// (strings, as a `CFStringRef`) override it to keep what the bytes point
    /// to alive
    fn into_qualifier(self) -> Qualifier
    where
        Self: Sized,
    {
        Qualifier::from_bytes(self.into_bytes())
    }
}

impl IntoQualifierBytes for u32 {
    fn into_bytes(self) -> Vec<u8> {
        bytes_u32(self)
    }
}

impl IntoQualifierBytes for Vec<u32> {
    fn into_bytes(self) -> Vec<u8> {
        bytes_vec_u32(self)
    }
}

impl IntoQualifierBytes for String {
    /// The text's UTF-8 bytes
    /// CoreAudio takes string qualifiers as a `CFStringRef`, which
    /// `into_qualifier` builds; this is only for callers that want the raw text
    fn into_bytes(self) -> Vec<u8> {
        self.into()
    }

    fn into_qualifier(self) -> Qualifier {
        Qualifier::from_string(&self)
    }
}

/// Attaches qualifier data to a property that requires it
///
/// Calling `.with_qualifier(value)` on a `NeedQualifier<T>` or `NeedBoth<T>`
/// property transitions the `E` type parameter, removing the qualifier
/// requirement
/// The resulting property may be fully specified (`NoExtra`) or may still need
/// an element (`NeedElement`)
#[diagnostic::on_unimplemented(
    message = "this property does not accept qualifier data",
)]
pub trait MissingQualifier<T> {
    /// The property type produced after the qualifier is attached
    type Output;

    /// Attaches `qualifier` to this property and returns the updated property
    fn with_qualifier(self, qualifier: T) -> Self::Output
    where
        T: IntoQualifierBytes;
}

impl<V, D, A, L, T> MissingQualifier<T> for Property<V, D, A, L, NeedQualifier<T>> {
    type Output = Property<V, D, A, L, NoExtra>;

    fn with_qualifier(self, qualifier: T) -> Self::Output
    where
        T: IntoQualifierBytes,
    {
        let mut new: Property<V, D, A, L, NoExtra> = Property::new(
            self.address,
            self.size,
            self.read,
            self.encode,
        );

        new.qualifier = Some(qualifier.into_qualifier());
        new
    }
}

impl<V, D, A, L, T> MissingQualifier<T> for Property<V, D, A, L, NeedBoth<T>> {
    type Output = Property<V, D, A, L, NeedElement>;

    fn with_qualifier(self, qualifier: T) -> Self::Output
    where
        T: IntoQualifierBytes,
    {
        let mut new: Property<V, D, A, L, NeedElement> = Property::new(
            self.address,
            self.size,
            self.read,
            self.encode,
        );

        new.qualifier = Some(qualifier.into_qualifier());
        new
    }
}

/// Sets the element on a property that requires one
///
/// Calling `.for_element(n)` on a `NeedElement` or `NeedBoth<T>` property
/// writes `n` into `mElement` of the property address, transitioning the `E`
/// type parameter to remove the element requirement
#[diagnostic::on_unimplemented(
    message = "this property does not accept element data",
)]
pub trait MissingElement {
    /// The property type produced after the element is set
    type Output;

    /// Sets `element` as the `mElement` of the property address and returns
    /// the updated property
    fn for_element(self, element: u32) -> Self::Output;
}

impl<V, D, A, L> MissingElement for Property<V, D, A, L, NeedElement> {
    type Output = Property<V, D, A, L, NoExtra>;

    fn for_element(self, element: u32) -> Self::Output {
        let mut new: Property<V, D, A, L, NoExtra> = Property::new(
            self.address,
            self.size,
            self.read,
            self.encode,
        );
        new.address.mElement = element;

        new.qualifier = self.qualifier;
        new
    }
}

impl<V, D, A, L, T> MissingElement for Property<V, D, A, L, NeedBoth<T>> {
    type Output = Property<V, D, A, L, NeedQualifier<T>>;

    fn for_element(self, element: u32) -> Self::Output {
        let mut new: Property<V, D, A, L, NeedQualifier<T>> = Property::new(
            self.address,
            self.size,
            self.read,
            self.encode,
        );
        new.address.mElement = element;

        new.qualifier = self.qualifier;
        new
    }
}

/// Adds a method to `Vec<SampleRateRange>`
/// to parse all valid sample rates for a device
pub trait ValidSampleRates {
    fn valid_rates(&self) -> Vec<f64>;
}

impl ValidSampleRates for Vec<SampleRateRange> {
    fn valid_rates(&self) -> Vec<f64> {
        self.iter()
        .map(|range| {
            range.get_min()
        }).collect()
    }
}

/// Wraps a reference to a value in a `NonNull` pointing at that same value
///
/// The pointer is only valid for as long as the value is, so call it on a
/// place that outlives the FFI call, not on a temporary
pub(crate) trait IntoNonNull {
    /// Wraps `&mut self` into a `NonNull<Self>`
    fn non_null(&mut self) -> NonNull<Self>
    where
        Self: Sized,
    {
        NonNull::from(self)
    }
}

impl IntoNonNull for AudioObjectPropertyAddress {}
impl IntoNonNull for u32 {}

/// Wraps a raw pointer in a `NonNull` pointing at the same data
///
/// Unlike [`IntoNonNull`], which points at its own copy of the value, this
/// keeps the pointer's target, so it's the one to use for buffers
pub(crate) trait PtrIntoNonNull<T> {
    /// Wraps the pointer into a `NonNull<T>` without checking for null
    ///
    /// # Safety
    /// The pointer must not be null
    unsafe fn non_null(self) -> NonNull<T>;
}

impl<T> PtrIntoNonNull<T> for *mut T {
    unsafe fn non_null(self) -> NonNull<T> {
        unsafe { NonNull::new_unchecked(self) }
    }
}

impl<T> PtrIntoNonNull<T> for *const T {
    unsafe fn non_null(self) -> NonNull<T> {
        unsafe { NonNull::new_unchecked(self as *mut _) }
    }
}