use crate::queues::signal::SetParameter;
use crate::time::ClockTick;
use crate::traits::param::{ParamMetadata, ParamValue, ParameterId};
use crate::traits::ProcessResult;
use std::any::TypeId;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(pub u32);
impl NodeId {
pub const fn new(id: u32) -> Self {
Self(id)
}
pub const fn inner(&self) -> u32 {
self.0
}
}
impl fmt::Display for NodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Node({})", self.0)
}
}
impl From<u32> for NodeId {
fn from(id: u32) -> Self {
Self(id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NodeCategory {
Source,
Processor,
Sink,
Utility,
Analyzer,
Sequencer,
}
impl NodeCategory {
pub const fn name(&self) -> &'static str {
match self {
Self::Source => "source",
Self::Processor => "processor",
Self::Sink => "sink",
Self::Utility => "utility",
Self::Analyzer => "analyzer",
Self::Sequencer => "sequencer",
}
}
}
impl fmt::Display for NodeCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeTypeId(TypeId);
impl NodeTypeId {
pub fn of<T: 'static + ?Sized>() -> Self {
Self(TypeId::of::<T>())
}
pub fn as_type_id(&self) -> TypeId {
self.0
}
}
#[derive(Debug, Clone)]
pub struct NodeMetadata {
pub name: String,
pub type_name: Option<String>,
pub category: NodeCategory,
pub description: String,
pub author: String,
pub version: String,
pub signal_inputs: usize,
pub signal_outputs: usize,
pub control_inputs: usize,
pub control_outputs: usize,
pub clock_inputs: usize,
pub clock_outputs: usize,
pub feedback_ports: usize,
pub parameters: Vec<ParamMetadata>,
}
impl NodeMetadata {
pub fn new(name: &str, category: NodeCategory) -> Self {
Self {
type_name: None,
name: name.to_string(),
category,
description: String::new(),
author: String::new(),
version: String::new(),
signal_inputs: 0,
signal_outputs: 0,
control_inputs: 0,
control_outputs: 0,
clock_inputs: 0,
clock_outputs: 0,
feedback_ports: 0,
parameters: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct NodeState<T: crate::math::Transcendental, const BUF_SIZE: usize> {
pub sample_pos: u64,
pub blocks_processed: u64,
pub sample_rate: f32,
pub active: bool,
pub phase: T,
}
impl<T: crate::math::Transcendental, const BUF_SIZE: usize> NodeState<T, BUF_SIZE> {
pub fn new(sample_rate: f32) -> Self {
Self {
sample_pos: 0,
blocks_processed: 0,
sample_rate,
active: true,
phase: T::ZERO,
}
}
pub fn advance(&mut self) {
self.sample_pos += BUF_SIZE as u64;
self.blocks_processed += 1;
}
pub fn current_time_seconds(&self) -> f64 {
self.sample_pos as f64 / self.sample_rate as f64
}
pub fn reset(&mut self) {
self.sample_pos = 0;
self.blocks_processed = 0;
self.phase = T::ZERO;
}
}
pub trait SignalNode<T: crate::math::Transcendental, const BUF_SIZE: usize>: Send + Sync {
fn metadata(&self) -> NodeMetadata;
fn node_type_id(&self) -> NodeTypeId
where
Self: 'static + Sized,
{
NodeTypeId::of::<Self>()
}
fn init(&mut self, sample_rate: f32);
fn reset(&mut self);
fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue>;
fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()>;
fn apply_set_parameter(&mut self, cmd: &SetParameter) -> ProcessResult<()> {
use crate::traits::port::{PortDirection, PortType};
let value = T::from_f32(cmd.value);
let port = match cmd.port.port_type() {
PortType::Control => self.control_port_mut(cmd.port.index() as usize),
PortType::Signal => match cmd.port.direction() {
PortDirection::Input => self.input_port_mut(cmd.port.index() as usize),
PortDirection::Output => self.output_port_mut(cmd.port.index() as usize),
},
PortType::Param => self.input_port_mut(cmd.port.index() as usize),
PortType::Clock | PortType::Feedback => None,
};
match port {
Some(p) => {
p.set_value(value);
Ok(())
}
None => self.set_parameter(&cmd.parameter, ParamValue::Float(cmd.value)),
}
}
fn id(&self) -> NodeId;
fn set_id(&mut self, id: NodeId);
fn input_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
fn input_port_mut(
&mut self,
index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
fn output_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
fn output_port_mut(
&mut self,
index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
fn control_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
fn control_port_mut(
&mut self,
index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
fn state(&self) -> &NodeState<T, BUF_SIZE>;
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE>;
fn num_signal_inputs(&self) -> usize {
0
}
fn num_signal_outputs(&self) -> usize {
0
}
fn num_control_inputs(&self) -> usize {
0
}
fn num_control_outputs(&self) -> usize {
0
}
fn num_clock_inputs(&self) -> usize {
0
}
fn num_clock_outputs(&self) -> usize {
0
}
fn num_feedback_ports(&self) -> usize {
0
}
fn num_inputs(&self) -> usize {
self.num_signal_inputs()
+ self.num_control_inputs()
+ self.num_clock_inputs()
+ self.num_feedback_ports()
}
fn num_outputs(&self) -> usize {
self.num_signal_outputs() + self.num_control_outputs() + self.num_clock_outputs()
}
}
pub trait Source<T: crate::math::Transcendental, const BUF_SIZE: usize>: SignalNode<T, BUF_SIZE> {
fn generate(
&mut self,
clock: &ClockTick,
control_inputs: &[T],
clock_inputs: &[ClockTick],
) -> ProcessResult<()>;
fn num_signal_outputs(&self) -> usize {
1
}
fn num_control_inputs(&self) -> usize {
0
}
fn num_clock_inputs(&self) -> usize {
0
}
}
pub trait Processor<T: crate::math::Transcendental, const BUF_SIZE: usize>:
SignalNode<T, BUF_SIZE>
{
fn process(
&mut self,
clock: &ClockTick,
signal_inputs: &[&[T; BUF_SIZE]],
control_inputs: &[T],
clock_inputs: &[ClockTick],
feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()>;
fn latency(&self) -> usize {
0
}
}
pub trait Sink<T: crate::math::Transcendental, const BUF_SIZE: usize>: SignalNode<T, BUF_SIZE> {
fn consume(
&mut self,
clock: &ClockTick,
signal_inputs: &[&[T; BUF_SIZE]],
control_inputs: &[T],
clock_inputs: &[ClockTick],
feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::Transcendental;
struct TestNode;
impl<T: Transcendental, const BUF_SIZE: usize> SignalNode<T, BUF_SIZE> for TestNode {
fn metadata(&self) -> NodeMetadata {
NodeMetadata {
name: "Test".to_string(),
type_name: None,
category: NodeCategory::Utility,
description: "Test node".to_string(),
author: "Rill".to_string(),
version: "1.0".to_string(),
signal_inputs: 0,
signal_outputs: 0,
control_inputs: 0,
control_outputs: 0,
clock_inputs: 0,
clock_outputs: 0,
feedback_ports: 0,
parameters: vec![],
}
}
fn init(&mut self, _sample_rate: f32) {}
fn reset(&mut self) {}
fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
None
}
fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
Ok(())
}
fn id(&self) -> NodeId {
NodeId(0)
}
fn set_id(&mut self, _id: NodeId) {}
fn input_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn input_port_mut(
&mut self,
_index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn output_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn output_port_mut(
&mut self,
_index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn control_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn control_port_mut(
&mut self,
_index: usize,
) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
None
}
fn state(&self) -> &NodeState<T, BUF_SIZE> {
unimplemented!()
}
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
unimplemented!()
}
}
#[test]
fn test_node_id() {
let id = NodeId::new(42);
assert_eq!(id.inner(), 42);
assert_eq!(format!("{}", id), "Node(42)");
}
#[test]
fn test_node_category() {
assert_eq!(NodeCategory::Source.name(), "source");
assert_eq!(NodeCategory::Processor.name(), "processor");
assert_eq!(NodeCategory::Sink.name(), "sink");
assert_eq!(NodeCategory::Utility.name(), "utility");
}
#[test]
fn test_node_metadata_new() {
let metadata = NodeMetadata::new("Test", NodeCategory::Source);
assert_eq!(metadata.name, "Test");
assert_eq!(metadata.category, NodeCategory::Source);
}
#[test]
fn test_node_state() {
let mut state = NodeState::<f32, 64>::new(44100.0);
assert_eq!(state.sample_pos, 0);
assert_eq!(state.sample_rate, 44100.0);
state.advance();
assert_eq!(state.sample_pos, 64);
assert_eq!(state.blocks_processed, 1);
state.reset();
assert_eq!(state.sample_pos, 0);
assert_eq!(state.blocks_processed, 0);
}
}