nice-plug-au2 0.1.0

Audio Unit (AU2) support for nice-plug
Documentation
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::sync::atomic::{AtomicU32, Ordering};

use coreaudio_sys::*;

use crate::bridge::{NiceAu2BusConfig, NiceAu2BusInfo, NiceAu2BusType};
use crate::render::Au2ScheduledParameterEvent;

use super::selectors;

const MAX_CHANNELS: usize = 32;
const MAX_MIDI_EVENTS: usize = 256;

#[repr(C)]
#[derive(Clone, Copy, Default)]
pub(super) struct MidiOutputCallback {
    pub callback: Option<
        unsafe extern "C" fn(
            *mut c_void,
            *const AudioTimeStamp,
            u32,
            *const MidiPacketList,
        ) -> OSStatus,
    >,
    pub user_data: *mut c_void,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub(super) struct MidiPacket {
    pub timestamp: u64,
    pub length: u16,
    pub data: [u8; 256],
}

#[repr(C)]
#[derive(Clone, Copy)]
pub(super) struct MidiPacketList {
    pub num_packets: u32,
    pub packet: MidiPacket,
}

#[repr(C)]
#[derive(Clone, Copy, Default)]
pub(super) struct HostCallbacks {
    pub user_data: *mut c_void,
    pub beat_and_tempo: Option<unsafe extern "C" fn(*mut c_void, *mut f64, *mut f64) -> OSStatus>,
    pub musical_time: Option<
        unsafe extern "C" fn(*mut c_void, *mut u32, *mut f32, *mut u32, *mut f64) -> OSStatus,
    >,
    pub transport: Option<
        unsafe extern "C" fn(
            *mut c_void,
            *mut u8,
            *mut u8,
            *mut f64,
            *mut u8,
            *mut f64,
            *mut f64,
        ) -> OSStatus,
    >,
    pub transport2: Option<
        unsafe extern "C" fn(
            *mut c_void,
            *mut u8,
            *mut u8,
            *mut u8,
            *mut f64,
            *mut u8,
            *mut f64,
            *mut f64,
        ) -> OSStatus,
    >,
}

#[derive(Clone, Copy, Default)]
pub(super) struct PropertyListener {
    pub property: AudioUnitPropertyID,
    pub callback: AudioUnitPropertyListenerProc,
    pub user_data: *mut c_void,
}

#[derive(Clone, Copy, Default)]
pub(super) struct RenderNotify {
    pub callback: AURenderCallback,
    pub user_data: *mut c_void,
}

#[repr(C)]
pub(super) struct Component {
    pub interface: AudioComponentPlugInInterface,
    pub rust_instance: *mut crate::bridge::NiceInstanceHandle,
    pub component_instance: AudioComponentInstance,
    pub bus_config: NiceAu2BusConfig,
    pub input_format: AudioStreamBasicDescription,
    pub output_format: AudioStreamBasicDescription,
    pub sample_rate: f64,
    pub max_frames: u32,
    pub render_resources_allocated: bool,
    pub input_callback: AURenderCallbackStruct,
    pub input_connection: AudioUnitConnection,
    pub input_buffers: [Vec<f32>; MAX_CHANNELS],
    pub output_buffers: [Vec<f32>; MAX_CHANNELS],
    pub property_listeners: Vec<PropertyListener>,
    pub render_notifies: Vec<RenderNotify>,
    pub scheduled_events: [Au2ScheduledParameterEvent; 256],
    pub scheduled_event_count: AtomicU32,
    pub midi_events: UnsafeCell<[crate::render::Au2MidiEvent; MAX_MIDI_EVENTS]>,
    pub midi_event_count: AtomicU32,
    pub midi_output_callback: MidiOutputCallback,
    pub host_callbacks: HostCallbacks,
}

impl Component {
    pub fn allocate() -> *mut AudioComponentPlugInInterface {
        let component = Box::new(Self {
            interface: AudioComponentPlugInInterface {
                Open: Some(selectors::open),
                Close: Some(selectors::close),
                Lookup: Some(selectors::lookup),
                reserved: std::ptr::null_mut(),
            },
            rust_instance: std::ptr::null_mut(),
            component_instance: std::ptr::null_mut(),
            bus_config: NiceAu2BusConfig {
                input_bus_count: 0,
                output_bus_count: 0,
                input_buses: [NiceAu2BusInfo {
                    channel_count: 0,
                    bus_type: NiceAu2BusType::Main,
                }; 16],
                output_buses: [NiceAu2BusInfo {
                    channel_count: 0,
                    bus_type: NiceAu2BusType::Main,
                }; 16],
            },
            input_format: unsafe { std::mem::zeroed() },
            output_format: unsafe { std::mem::zeroed() },
            sample_rate: 44_100.0,
            max_frames: 1024,
            render_resources_allocated: false,
            input_callback: unsafe { std::mem::zeroed() },
            input_connection: unsafe { std::mem::zeroed() },
            input_buffers: std::array::from_fn(|_| Vec::new()),
            output_buffers: std::array::from_fn(|_| Vec::new()),
            property_listeners: Vec::new(),
            render_notifies: Vec::new(),
            scheduled_events: [Au2ScheduledParameterEvent::default(); 256],
            scheduled_event_count: AtomicU32::new(0),
            midi_events: UnsafeCell::new([crate::render::Au2MidiEvent::default(); MAX_MIDI_EVENTS]),
            midi_event_count: AtomicU32::new(0),
            midi_output_callback: MidiOutputCallback::default(),
            host_callbacks: HostCallbacks::default(),
        });
        Box::into_raw(component).cast()
    }

    pub fn queue_midi_event(&self, event: crate::render::Au2MidiEvent) -> OSStatus {
        let index = self.midi_event_count.fetch_add(1, Ordering::AcqRel) as usize;
        if index >= MAX_MIDI_EVENTS {
            self.midi_event_count
                .store(MAX_MIDI_EVENTS as u32, Ordering::Release);
            return kAudioUnitErr_TooManyFramesToProcess;
        }
        unsafe { (*self.midi_events.get())[index] = event };
        0
    }

    pub fn take_midi_events(&self) -> &[crate::render::Au2MidiEvent] {
        let count = self
            .midi_event_count
            .swap(0, Ordering::AcqRel)
            .min(MAX_MIDI_EVENTS as u32) as usize;
        unsafe { std::slice::from_raw_parts((*self.midi_events.get()).as_ptr(), count) }
    }

    pub unsafe fn from_self<'a>(this: *mut c_void) -> Option<&'a mut Self> {
        unsafe { this.cast::<Self>().as_mut() }
    }

    pub fn input_channels(&self) -> u32 {
        self.bus_config.input_buses[..self.bus_config.input_bus_count as usize]
            .iter()
            .map(|bus| bus.channel_count)
            .sum()
    }

    pub fn output_channels(&self) -> u32 {
        self.bus_config.output_buses[..self.bus_config.output_bus_count as usize]
            .iter()
            .map(|bus| bus.channel_count)
            .sum()
    }

    pub fn update_formats(&mut self) {
        self.input_format = make_format(self.sample_rate, self.input_channels());
        self.output_format = make_format(self.sample_rate, self.output_channels());
    }
}

fn make_format(sample_rate: f64, channels: u32) -> AudioStreamBasicDescription {
    AudioStreamBasicDescription {
        mSampleRate: sample_rate,
        mFormatID: kAudioFormatLinearPCM,
        mFormatFlags: kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved,
        mBytesPerPacket: 4,
        mFramesPerPacket: 1,
        mBytesPerFrame: 4,
        mChannelsPerFrame: channels,
        mBitsPerChannel: 32,
        mReserved: 0,
    }
}