nice-plug-au2 0.1.1

Audio Unit (AU2) support for nice-plug
Documentation
pub use nice_plug_core::audio_setup::{AudioIOLayout, BufferConfig, PortNames, ProcessMode};

#[derive(Debug, Clone, PartialEq)]
pub enum BusType {
    Main,
    Aux,
}

#[derive(Debug, Clone)]
pub struct BusInfo {
    pub name: String,
    pub bus_type: BusType,
    pub channel_count: usize,
}

impl BusInfo {
    pub fn stereo(name: &str) -> Self {
        Self {
            name: name.to_string(),
            bus_type: BusType::Main,
            channel_count: 2,
        }
    }

    pub fn mono(name: &str) -> Self {
        Self {
            name: name.to_string(),
            bus_type: BusType::Main,
            channel_count: 1,
        }
    }
}

#[derive(Debug, Clone)]
pub struct CachedBusInfo {
    pub channel_count: usize,
    pub bus_type: BusType,
}

impl CachedBusInfo {
    pub fn new(channel_count: usize, bus_type: BusType) -> Self {
        Self {
            channel_count,
            bus_type,
        }
    }
}

#[derive(Debug, Clone)]
pub struct CachedBusConfig {
    pub input_buses: Vec<CachedBusInfo>,
    pub output_buses: Vec<CachedBusInfo>,
}

impl CachedBusConfig {
    pub fn new(input_buses: Vec<CachedBusInfo>, output_buses: Vec<CachedBusInfo>) -> Self {
        Self {
            input_buses,
            output_buses,
        }
    }

    pub fn validate(&self) -> Result<(), String> {
        fn validate_side(name: &str, buses: &[CachedBusInfo]) -> Result<(), String> {
            if buses.len() > 16 {
                return Err(format!("too many {name} buses"));
            }
            if buses
                .iter()
                .any(|bus| bus.channel_count == 0 || bus.channel_count > 32)
            {
                return Err(format!("invalid {name} bus channel count"));
            }
            if buses
                .iter()
                .filter(|bus| bus.bus_type == BusType::Main)
                .count()
                > 1
            {
                return Err(format!("more than one main {name} bus"));
            }
            if buses
                .first()
                .is_some_and(|bus| bus.bus_type != BusType::Main)
            {
                return Err(format!("the main {name} bus must be first"));
            }
            if buses.iter().skip(1).any(|bus| bus.bus_type != BusType::Aux) {
                return Err(format!("only the first {name} bus may be main"));
            }
            Ok(())
        }

        validate_side("input", &self.input_buses)?;
        validate_side("output", &self.output_buses)?;
        Ok(())
    }

    pub fn input_channel_count(&self) -> usize {
        self.input_buses.iter().map(|b| b.channel_count).sum()
    }

    pub fn output_channel_count(&self) -> usize {
        self.output_buses.iter().map(|b| b.channel_count).sum()
    }
}

pub fn from_nice_audio_io_layout(layout: &AudioIOLayout) -> CachedBusConfig {
    let mut input_buses = Vec::new();
    let mut output_buses = Vec::new();

    if let Some(channels) = layout.main_input_channels {
        input_buses.push(CachedBusInfo::new(channels.get() as usize, BusType::Main));
    }

    for ports in layout.aux_input_ports {
        input_buses.push(CachedBusInfo::new(ports.get() as usize, BusType::Aux));
    }

    if let Some(channels) = layout.main_output_channels {
        output_buses.push(CachedBusInfo::new(channels.get() as usize, BusType::Main));
    }

    for ports in layout.aux_output_ports {
        output_buses.push(CachedBusInfo::new(ports.get() as usize, BusType::Aux));
    }

    CachedBusConfig::new(input_buses, output_buses)
}