melody-bay 0.1.0

Kira-compatible generated synthesis sound sources.
Documentation
#[derive(Debug, Clone)]
pub struct GainNode {
    id: NodeId,
    graph: Arc<Mutex<GraphInner>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GainOptions {
    pub gain: f32,
}

impl Default for GainOptions {
    fn default() -> Self {
        Self { gain: 1.0 }
    }
}

impl GainNode {
    #[must_use]
    pub fn gain(&self) -> AudioParamHandle {
        AudioParamHandle {
            graph: Arc::clone(&self.graph),
            id: self.gain_param_id(),
        }
    }

    #[must_use]
    fn gain_param_id(&self) -> ParamId {
        ParamId {
            node: self.id,
            param: ParamKind::Gain,
        }
    }

    #[must_use]
    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
        self.parameter(name)
    }

    #[must_use]
    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
        match name {
            "gain" => Some(self.gain()),
            _ => None,
        }
    }

    #[must_use]
    pub fn channel_count(&self) -> usize {
        node_channel_config(&self.graph, self.id).channel_count
    }

    #[must_use]
    pub fn channel_count_mode(&self) -> ChannelCountMode {
        node_channel_config(&self.graph, self.id).channel_count_mode
    }

    #[must_use]
    pub fn channel_interpretation(&self) -> ChannelInterpretation {
        node_channel_config(&self.graph, self.id).channel_interpretation
    }

    pub fn try_set_channel_config(
        &self,
        channel_count: usize,
        channel_count_mode: ChannelCountMode,
        channel_interpretation: ChannelInterpretation,
    ) -> Result<(), GraphError> {
        try_set_node_channel_config(
            &self.graph,
            self.id,
            channel_count,
            channel_count_mode,
            channel_interpretation,
        )
    }
}

impl From<GainNode> for NodeId {
    fn from(value: GainNode) -> Self {
        value.id
    }
}

impl From<&GainNode> for NodeId {
    fn from(value: &GainNode) -> Self {
        value.id
    }
}

#[derive(Debug, Clone)]
pub struct AudioParamHandle {
    graph: Arc<Mutex<GraphInner>>,
    id: ParamId,
}

impl AudioParamHandle {
    #[must_use]
    pub fn default_value(&self) -> f32 {
        self.with_param(ParamTimeline::default_value).unwrap_or(0.0)
    }

    #[must_use]
    pub fn min_value(&self) -> f32 {
        self.with_param(ParamTimeline::min_value).unwrap_or(0.0)
    }

    #[must_use]
    pub fn max_value(&self) -> f32 {
        self.with_param(ParamTimeline::max_value).unwrap_or(0.0)
    }

    #[must_use]
    pub fn automation_rate(&self) -> AutomationRate {
        self.with_param(ParamTimeline::automation_rate)
            .unwrap_or(AutomationRate::ARate)
    }

    pub fn set_automation_rate(&self, automation_rate: AutomationRate) -> Result<(), GraphError> {
        self.try_set_automation_rate(automation_rate)
    }

    pub fn try_set_automation_rate(
        &self,
        automation_rate: AutomationRate,
    ) -> Result<(), GraphError> {
        self.update_param_result(|param| param.try_set_automation_rate(automation_rate))
    }

    #[must_use]
    pub fn value(&self) -> f32 {
        self.with_param(ParamTimeline::value).unwrap_or(0.0)
    }

    #[must_use]
    pub fn value_at(&self, time: f64) -> f32 {
        self.with_param(|param| param.value_at(time)).unwrap_or(0.0)
    }

    pub fn set_value(&self, value: f32) -> Result<(), GraphError> {
        validate_automation_value(value)?;
        self.update_param(|param| {
            param.current_value = param.clamp_value(value);
            param.events.retain(|event| {
                !matches!(
                    event,
                    AudioParamEvent::SetValue { time, .. } if *time <= f64::EPSILON
                )
            });
            param
                .events
                .push(AudioParamEvent::SetValue { time: 0.0, value });
            param.sort_events();
        })
    }

    pub fn set_value_at_time(&self, value: f32, time: f64) -> Result<(), GraphError> {
        validate_automation_time(time)?;
        validate_automation_value(value)?;
        self.update_param_result(|param| {
            param.validate_event_time_for_value_curves(time)?;
            *param = param.clone().set_value_at_time(value, time);
            Ok(())
        })
    }

    pub fn linear_ramp_to_value_at_time(
        &self,
        value: f32,
        end_time: f64,
    ) -> Result<(), GraphError> {
        validate_automation_time(end_time)?;
        validate_automation_value(value)?;
        self.update_param_result(|param| {
            param.validate_event_time_for_value_curves(end_time)?;
            *param = param.clone().linear_ramp_to_value_at_time(value, end_time);
            Ok(())
        })
    }

    pub fn exponential_ramp_to_value_at_time(
        &self,
        value: f32,
        end_time: f64,
    ) -> Result<(), GraphError> {
        self.try_exponential_ramp_to_value_at_time(value, end_time)
    }

    pub fn try_exponential_ramp_to_value_at_time(
        &self,
        value: f32,
        end_time: f64,
    ) -> Result<(), GraphError> {
        validate_automation_time(end_time)?;
        validate_automation_value(value)?;
        self.update_param_result(|param| {
            param.validate_event_time_for_value_curves(end_time)?;
            *param = param
                .clone()
                .try_exponential_ramp_to_value_at_time(value, end_time)?;
            Ok(())
        })
    }

    pub fn set_target_at_time(
        &self,
        target: f32,
        start_time: f64,
        time_constant: f64,
    ) -> Result<(), GraphError> {
        validate_automation_time(start_time)?;
        validate_automation_value(target)?;
        if time_constant < 0.0 || !time_constant.is_finite() {
            return Err(GraphError::InvalidAutomationValue);
        }
        self.update_param_result(|param| {
            param.validate_event_time_for_value_curves(start_time)?;
            *param = param
                .clone()
                .set_target_at_time(target, start_time, time_constant);
            Ok(())
        })
    }

    pub fn set_value_curve_at_time(
        &self,
        values: impl IntoIterator<Item = f32>,
        start_time: f64,
        duration: f64,
    ) -> Result<(), GraphError> {
        validate_automation_time(start_time)?;
        let values = values.into_iter().collect::<Vec<_>>();
        if values.len() < 2
            || duration <= 0.0
            || !duration.is_finite()
            || values.iter().any(|value| !value.is_finite())
        {
            return Err(GraphError::InvalidAutomationValue);
        }
        self.update_param_result(|param| {
            param.validate_value_curve_interval(start_time, duration)?;
            *param = param
                .clone()
                .set_value_curve_at_time(values, start_time, duration);
            Ok(())
        })
    }

    pub fn cancel_scheduled_values(&self, cancel_time: f64) -> Result<(), GraphError> {
        validate_automation_time(cancel_time)?;
        self.update_param(|param| {
            *param = param.clone().cancel_scheduled_values(cancel_time);
        })
    }

    pub fn cancel_and_hold_at_time(&self, cancel_time: f64) -> Result<(), GraphError> {
        validate_automation_time(cancel_time)?;
        self.update_param(|param| {
            *param = param.clone().cancel_and_hold_at_time(cancel_time);
        })
    }

    fn with_param<T>(&self, read: impl FnOnce(&ParamTimeline) -> T) -> Option<T> {
        let inner = self.graph.lock().expect("graph mutex poisoned");
        inner.param(self.id).map(read)
    }

    fn update_param(&self, update: impl FnOnce(&mut ParamTimeline)) -> Result<(), GraphError> {
        self.update_param_result(|param| {
            update(param);
            Ok(())
        })
    }

    fn update_param_result(
        &self,
        update: impl FnOnce(&mut ParamTimeline) -> Result<(), GraphError>,
    ) -> Result<(), GraphError> {
        let mut inner = self.graph.lock().expect("graph mutex poisoned");
        let Some(param) = inner.param_mut(self.id) else {
            return Err(GraphError::UnknownParam);
        };
        update(param)
    }

    fn context_identity(&self) -> Option<usize> {
        Some(Arc::as_ptr(&self.graph) as usize)
    }
}

fn validate_automation_time(time: f64) -> Result<(), GraphError> {
    if time < 0.0 {
        return Err(GraphError::NegativeTime);
    }
    if !time.is_finite() {
        return Err(GraphError::InvalidAutomationValue);
    }
    Ok(())
}

fn validate_automation_value(value: f32) -> Result<(), GraphError> {
    if !value.is_finite() {
        return Err(GraphError::InvalidAutomationValue);
    }
    Ok(())
}

fn validate_source_time(time: f64) -> Result<(), GraphError> {
    validate_automation_time(time)
}