nice-plug-au2 0.1.1

Audio Unit (AU2) support for nice-plug
Documentation
use std::ffi::c_void;
use std::mem::size_of;
use std::sync::atomic::Ordering;

use coreaudio_sys::*;

use crate::bridge;

use super::component::Component;

#[repr(C)]
struct BufferList32 {
    count: u32,
    buffers: [AudioBuffer; 32],
}

impl BufferList32 {
    fn new() -> Self {
        unsafe { std::mem::zeroed() }
    }
    fn as_mut_ptr(&mut self) -> *mut AudioBufferList {
        (self as *mut Self).cast()
    }
}

unsafe fn notify(
    component: &Component,
    flags: u32,
    timestamp: *const AudioTimeStamp,
    bus: u32,
    frames: u32,
    data: *mut AudioBufferList,
) -> OSStatus {
    for item in &component.render_notifies {
        if let Some(callback) = item.callback {
            let mut callback_flags = flags;
            let status = unsafe {
                callback(
                    item.user_data,
                    &mut callback_flags,
                    timestamp,
                    bus,
                    frames,
                    data,
                )
            };
            if status != 0 {
                return status;
            }
        }
    }
    0
}

pub unsafe extern "C" fn render(
    this: *mut c_void,
    flags: *mut u32,
    timestamp: *const AudioTimeStamp,
    output_bus: u32,
    frames: u32,
    io_data: *mut AudioBufferList,
) -> OSStatus {
    let Some(component) = (unsafe { Component::from_self(this) }) else {
        return kAudioUnitErr_InvalidProperty;
    };
    if frames > component.max_frames {
        return kAudioUnitErr_TooManyFramesToProcess;
    }

    let output_channels = component.output_channels().min(32) as usize;
    let mut output_storage = BufferList32::new();
    let output = if io_data.is_null() {
        output_storage.count = output_channels as u32;
        for (index, buffer) in component
            .output_buffers
            .iter_mut()
            .take(output_channels)
            .enumerate()
        {
            output_storage.buffers[index] = AudioBuffer {
                mNumberChannels: 1,
                mDataByteSize: frames * size_of::<f32>() as u32,
                mData: buffer.as_mut_ptr().cast(),
            };
        }
        output_storage.as_mut_ptr()
    } else {
        let list = unsafe { &mut *io_data };
        let buffers = unsafe {
            std::slice::from_raw_parts_mut(
                list.mBuffers.as_mut_ptr(),
                list.mNumberBuffers.min(output_channels as u32) as usize,
            )
        };
        for (index, buffer) in buffers.iter_mut().enumerate() {
            if buffer.mData.is_null() {
                buffer.mNumberChannels = 1;
                buffer.mDataByteSize = frames * size_of::<f32>() as u32;
                buffer.mData = component.output_buffers[index].as_mut_ptr().cast();
            }
        }
        io_data
    };

    let input_channels = component.input_channels().min(32) as usize;
    let has_callback = component.input_callback.inputProc.is_some();
    let has_connection = !component.input_connection.sourceAudioUnit.is_null();
    let mut input_storage = BufferList32::new();
    let input = if input_channels > 0 && (has_callback || has_connection) {
        input_storage.count = input_channels as u32;
        for (index, buffer) in component
            .input_buffers
            .iter_mut()
            .take(input_channels)
            .enumerate()
        {
            input_storage.buffers[index] = AudioBuffer {
                mNumberChannels: 1,
                mDataByteSize: frames * size_of::<f32>() as u32,
                mData: buffer.as_mut_ptr().cast(),
            };
        }
        let input = input_storage.as_mut_ptr();
        let status = if let Some(callback) = component.input_callback.inputProc {
            unsafe {
                callback(
                    component.input_callback.inputProcRefCon,
                    flags,
                    timestamp,
                    0,
                    frames,
                    input,
                )
            }
        } else {
            unsafe {
                AudioUnitRender(
                    component.input_connection.sourceAudioUnit,
                    flags,
                    timestamp,
                    component.input_connection.sourceOutputNumber,
                    frames,
                    input,
                )
            }
        };
        if status != 0 {
            return status;
        }
        input
    } else {
        std::ptr::null_mut()
    };

    let base_flags = if flags.is_null() {
        0
    } else {
        unsafe { *flags }
    };
    let status = unsafe {
        notify(
            component,
            base_flags | kAudioUnitRenderAction_PreRender,
            timestamp,
            output_bus,
            frames,
            output,
        )
    };
    if status != 0 {
        return status;
    }
    let event_count = component.scheduled_event_count.swap(0, Ordering::AcqRel);
    let midi_events = component.take_midi_events();
    let timestamp_value = if timestamp.is_null() {
        None
    } else {
        Some(unsafe { &*timestamp })
    };
    let mut transport = crate::render::Au2TransportInfo {
        sample_rate: component.sample_rate as f32,
        sample_position: timestamp_value
            .filter(|value| value.mFlags & kAudioTimeStampSampleTimeValid != 0)
            .map(|value| value.mSampleTime.round() as i64),
        playing: None,
        recording: None,
        tempo: None,
        position_beats: None,
        time_signature: None,
        cycle_beats: None,
    };
    let callbacks = component.host_callbacks;
    if let Some(callback) = callbacks.beat_and_tempo {
        let mut beat = 0.0;
        let mut tempo = 0.0;
        if unsafe { callback(callbacks.user_data, &mut beat, &mut tempo) } == 0 {
            transport.position_beats = Some(beat);
            transport.tempo = Some(tempo);
        }
    }
    if let Some(callback) = callbacks.musical_time {
        let mut delta = 0;
        let mut numerator = 0.0;
        let mut denominator = 0;
        let mut downbeat = 0.0;
        if unsafe {
            callback(
                callbacks.user_data,
                &mut delta,
                &mut numerator,
                &mut denominator,
                &mut downbeat,
            )
        } == 0
        {
            transport.time_signature = Some((numerator as i32, denominator as i32));
        }
    }
    if let Some(callback) = callbacks.transport2 {
        let mut playing = 0;
        let mut recording = 0;
        let mut changed = 0;
        let mut sample_position = transport.sample_position.unwrap_or_default() as f64;
        let mut cycling = 0;
        let mut cycle_start = 0.0;
        let mut cycle_end = 0.0;
        let status = unsafe {
            callback(
                callbacks.user_data,
                &mut playing,
                &mut recording,
                &mut changed,
                &mut sample_position,
                &mut cycling,
                &mut cycle_start,
                &mut cycle_end,
            )
        };
        if status == 0 {
            transport.playing = Some(playing != 0);
            transport.recording = callbacks.transport2.map(|_| recording != 0);
            transport.sample_position = Some(sample_position.round() as i64);
            transport.cycle_beats = (cycling != 0).then_some((cycle_start, cycle_end));
        }
    } else if let Some(callback) = callbacks.transport {
        let mut playing = 0;
        let mut changed = 0;
        let mut sample_position = transport.sample_position.unwrap_or_default() as f64;
        let mut cycling = 0;
        let mut cycle_start = 0.0;
        let mut cycle_end = 0.0;
        if unsafe {
            callback(
                callbacks.user_data,
                &mut playing,
                &mut changed,
                &mut sample_position,
                &mut cycling,
                &mut cycle_start,
                &mut cycle_end,
            )
        } == 0
        {
            transport.playing = Some(playing != 0);
            transport.sample_position = Some(sample_position.round() as i64);
            transport.cycle_beats = (cycling != 0).then_some((cycle_start, cycle_end));
        }
    }
    let mut midi_output = Vec::with_capacity(32);
    let mut status = bridge::nice_au2_render(
        component.rust_instance,
        flags,
        timestamp.cast(),
        frames,
        output_bus as isize,
        output.cast(),
        midi_events.as_ptr(),
        midi_events.len() as u32,
        std::ptr::null(),
        input.cast(),
        component.scheduled_events.as_ptr(),
        event_count,
        &mut midi_output,
        &transport,
    );
    if let Some(callback) = component.midi_output_callback.callback {
        for event in midi_output {
            let mut packet = super::component::MidiPacketList {
                num_packets: 1,
                packet: super::component::MidiPacket {
                    timestamp: timestamp_value.map_or(0, |value| value.mHostTime),
                    length: 3,
                    data: [0; 256],
                },
            };
            packet.packet.data[..3].copy_from_slice(&[event.status, event.data1, event.data2]);
            let callback_status = unsafe {
                callback(
                    component.midi_output_callback.user_data,
                    timestamp,
                    1,
                    &packet,
                )
            };
            if status == 0 && callback_status != 0 {
                status = callback_status;
            }
        }
    }
    let post_flags = base_flags
        | kAudioUnitRenderAction_PostRender
        | if status == 0 {
            0
        } else {
            kAudioUnitRenderAction_PostRenderError
        };
    let notify_status =
        unsafe { notify(component, post_flags, timestamp, output_bus, frames, output) };
    if status == 0 {
        status = notify_status;
    }
    status
}