audio-toolbox 0.1.1

Safe bindings to AudioToolbox framework
Documentation
use std::{
    mem,
    ptr::{null_mut, NonNull},
};

use core_audio_types::{AudioBufferList, AudioClassDescription, AudioStreamBasicDescription, AudioStreamPacketDescription};
use core_foundation::base::OSStatus;
use libc::c_void;
use objc2_audio_toolbox as sys;
pub use sys::{AudioConverterOptions, AudioConverterPropertyID};

use crate::base::status_to_result;

#[derive(Debug)]
pub struct AudioConverter {
    raw: NonNull<sys::OpaqueAudioConverter>,
}

impl AudioConverter {
    #[inline]
    pub unsafe fn from_raw(raw: sys::AudioConverterRef) -> Option<Self> {
        NonNull::new(raw).map(|raw| Self {
            raw,
        })
    }

    #[inline]
    pub fn as_raw(&self) -> sys::AudioConverterRef {
        self.raw.as_ptr()
    }

    #[inline]
    pub fn into_raw(self) -> sys::AudioConverterRef {
        let raw = self.as_raw();
        mem::forget(self);
        raw
    }

    #[inline]
    pub fn new(source_format: &AudioStreamBasicDescription, destination_format: &AudioStreamBasicDescription) -> Result<Self, OSStatus> {
        let mut converter = std::ptr::null_mut();
        let status =
            unsafe { sys::AudioConverterNew(NonNull::from(source_format), NonNull::from(destination_format), NonNull::from(&mut converter)) };
        status_to_result(status)?;
        unsafe { Self::from_raw(converter).ok_or(sys::kAudioConverterErr_UnspecifiedError) }
    }

    pub fn new_specific(
        source_format: &AudioStreamBasicDescription,
        destination_format: &AudioStreamBasicDescription,
        class_descriptions: &[AudioClassDescription],
    ) -> Result<Self, OSStatus> {
        let mut converter = std::ptr::null_mut();
        let class_description_count = class_descriptions.len() as u32;
        let class_descriptions = NonNull::new(class_descriptions.as_ptr() as *mut AudioClassDescription).unwrap_or_else(NonNull::dangling);
        let status = unsafe {
            sys::AudioConverterNewSpecific(
                NonNull::from(source_format),
                NonNull::from(destination_format),
                class_description_count,
                class_descriptions,
                NonNull::from(&mut converter),
            )
        };
        status_to_result(status)?;
        unsafe { Self::from_raw(converter).ok_or(sys::kAudioConverterErr_UnspecifiedError) }
    }

    pub fn new_with_options(
        source_format: &AudioStreamBasicDescription,
        destination_format: &AudioStreamBasicDescription,
        options: AudioConverterOptions,
    ) -> Result<Self, OSStatus> {
        let mut converter = std::ptr::null_mut();
        let status = unsafe {
            sys::AudioConverterNewWithOptions(NonNull::from(source_format), NonNull::from(destination_format), options, NonNull::from(&mut converter))
        };
        status_to_result(status)?;
        unsafe { Self::from_raw(converter).ok_or(sys::kAudioConverterErr_UnspecifiedError) }
    }

    #[inline]
    pub fn reset(&self) -> Result<(), OSStatus> {
        let status = unsafe { sys::AudioConverterReset(self.as_raw()) };
        status_to_result(status)
    }

    pub fn property_info(&self, property_id: AudioConverterPropertyID) -> Result<(u32, bool), OSStatus> {
        let mut size = 0;
        let mut writable = 0;
        let status = unsafe { sys::AudioConverterGetPropertyInfo(self.as_raw(), property_id, &mut size, &mut writable) };
        status_to_result(status).map(|_| (size, writable != 0))
    }

    #[inline]
    pub fn get_property<T: Copy>(&self, property_id: AudioConverterPropertyID) -> Result<T, OSStatus> {
        let mut value = mem::MaybeUninit::<T>::uninit();
        let mut size = mem::size_of::<T>() as u32;
        let status = unsafe {
            sys::AudioConverterGetProperty(self.as_raw(), property_id, NonNull::from(&mut size), NonNull::new_unchecked(value.as_mut_ptr().cast()))
        };
        status_to_result(status)?;
        Ok(unsafe { value.assume_init() })
    }

    pub fn get_property_bytes(&self, property_id: AudioConverterPropertyID) -> Result<Vec<u8>, OSStatus> {
        let (size, _) = self.property_info(property_id)?;
        let mut bytes = vec![0; size as usize];
        let mut io_size = size;
        let out_data = NonNull::new(bytes.as_mut_ptr().cast::<c_void>()).unwrap_or_else(NonNull::dangling);
        let status = unsafe { sys::AudioConverterGetProperty(self.as_raw(), property_id, NonNull::from(&mut io_size), out_data) };
        status_to_result(status)?;
        bytes.truncate(io_size as usize);
        Ok(bytes)
    }

    #[inline]
    pub fn set_property<T: Copy>(&self, property_id: AudioConverterPropertyID, value: &T) -> Result<(), OSStatus> {
        let status = unsafe { sys::AudioConverterSetProperty(self.as_raw(), property_id, mem::size_of::<T>() as u32, NonNull::from(value).cast()) };
        status_to_result(status)
    }

    pub fn set_property_bytes(&self, property_id: AudioConverterPropertyID, bytes: &[u8]) -> Result<(), OSStatus> {
        let data = NonNull::new(bytes.as_ptr() as *mut c_void).unwrap_or_else(NonNull::dangling);
        let status = unsafe { sys::AudioConverterSetProperty(self.as_raw(), property_id, bytes.len() as u32, data) };
        status_to_result(status)
    }

    pub fn convert_buffer(&self, input: &[u8], output: &mut [u8]) -> Result<usize, OSStatus> {
        let input_data = NonNull::new(input.as_ptr() as *mut c_void).unwrap_or_else(NonNull::dangling);
        let output_data = NonNull::new(output.as_mut_ptr().cast::<c_void>()).unwrap_or_else(NonNull::dangling);
        let mut output_size = output.len() as u32;
        let status =
            unsafe { sys::AudioConverterConvertBuffer(self.as_raw(), input.len() as u32, input_data, NonNull::from(&mut output_size), output_data) };
        status_to_result(status).map(|_| output_size as usize)
    }

    pub unsafe fn convert_complex_buffer(
        &self,
        number_pcm_frames: u32,
        input_data: &mut AudioBufferList,
        output_data: &mut AudioBufferList,
    ) -> Result<(), OSStatus> {
        let status = sys::AudioConverterConvertComplexBuffer(self.as_raw(), number_pcm_frames, NonNull::from(input_data), NonNull::from(output_data));
        status_to_result(status)
    }

    pub unsafe fn fill_complex_buffer(
        &self,
        input_data_proc: sys::AudioConverterComplexInputDataProc,
        input_data_proc_user_data: Option<*mut c_void>,
        output_data_packet_size: &mut u32,
        output_data: &mut AudioBufferList,
        packet_descriptions: Option<&mut [AudioStreamPacketDescription]>,
    ) -> Result<(), OSStatus> {
        let packet_descriptions = packet_descriptions.map_or(std::ptr::null_mut(), |descriptions| descriptions.as_mut_ptr());
        let status = sys::AudioConverterFillComplexBuffer(
            self.as_raw(),
            input_data_proc,
            input_data_proc_user_data.unwrap_or(null_mut()),
            NonNull::from(output_data_packet_size),
            NonNull::from(output_data),
            packet_descriptions,
        );
        status_to_result(status)
    }
}

impl Drop for AudioConverter {
    fn drop(&mut self) {
        unsafe {
            sys::AudioConverterDispose(self.as_raw());
        }
    }
}