dhwani 0.1.1

A real-time audio processing engine for DAWs
Documentation
use std::ops::Range;

use crate::{
    channel::ChannelPositionsMask,
    event::Event,
    node::{NodeBuilderTrait, NodeCtx, NodeInputs, NodeOutputs, NodeTrait},
    nodes::{self, SimpleMixerProps},
    port::{PortId, PortProps, PortProxy, PortType},
};

#[derive(Clone)]
pub struct SimplePianoProps {
    channel_mask: ChannelPositionsMask,
    events: Vec<Event>,
}

impl SimplePianoProps {
    pub const PORT_ID_OUTPUT: PortId = PortId(0);

    #[must_use]
    pub const fn new(channel_mask: ChannelPositionsMask, events: Vec<Event>) -> Self {
        Self {
            channel_mask,
            events,
        }
    }
}

impl NodeBuilderTrait for SimplePianoProps {
    fn build(&self, ctx: &mut NodeCtx) -> Box<dyn NodeTrait> {
        Box::new(SimplePiano::new(ctx, self.clone()))
    }
}

struct SimplePiano {
    port_props: Vec<PortProps>,
}

impl SimplePiano {
    fn new(ctx: &mut NodeCtx, props: SimplePianoProps) -> Self {
        // C4 or 440hz is base note with multiplier 1
        const PROPS: [(f32, f32); 4] = [
            (440f32 * std::f32::consts::TAU, 1f32),          // fundamental
            (440f32 * 2f32 * std::f32::consts::TAU, 0.9f32), // 1st harmonics
            (440f32 * 3f32 * std::f32::consts::TAU, 0.75f32), // 2nd harmonics
            (440f32 * 4f32 * std::f32::consts::TAU, 0.25f32), // 3rd harmonics
        ];
        let mut port_props = Vec::<PortProps>::with_capacity(1);
        let piano_roll_node_id = ctx
            .add_node(&nodes::PianoRollProps::new(props.events))
            .unwrap();
        let output_node_id = ctx
            .add_node(&nodes::SimpleMixerProps::new(
                props.channel_mask,
                vec![1f32; PROPS.len()],
            ))
            .unwrap();
        let output_node_output_port = ctx
            .get_node(output_node_id)
            .unwrap()
            .get_port(SimpleMixerProps::PORT_ID_OUTPUT)
            .unwrap();
        port_props.push(PortProps {
            id: SimpleMixerProps::PORT_ID_OUTPUT,
            kind: PortType::Proxy(PortProxy::new(0usize.into(), output_node_output_port)),
            auto_connect: true,
            name: "Output",
        });
        for (w, mul) in &PROPS {
            let id = ctx
                .add_node(&nodes::OscProps::new_sin(props.channel_mask, *w, *mul).unwrap())
                .unwrap();
            let _ = ctx.connect_nodes(piano_roll_node_id, id);
            let _ = ctx.connect_nodes(id, output_node_id);
        }
        Self { port_props }
    }
}

impl NodeTrait for SimplePiano {
    fn process(
        &mut self,
        _step_range: Range<usize>,
        _inputs: &NodeInputs,
        _outputs: &mut NodeOutputs,
    ) {
    }

    fn port_props(&self) -> &[PortProps] {
        &self.port_props
    }

    fn name(&self) -> &'static str {
        "Simple Piano"
    }
}