siraph 0.1.2

A node-based digital signal processing crate
Documentation
//! The `siraph` crate is a node-based digital signal processing crate.
//!
//! # Features
//! * **nodes:** Adds the `nodes` module that contains a couple of basic useful nodes as `Map` or `Const`. This feature is enabled by default.
//!
//! * **math:** Adds the `nodes::math` module that contains useful nodes related to mathematics as `Add` or `Oscillator`. This feature depends on the `num-traits` crate and the `nodes` feature.
//!
//! * **random:** Adds the `nodes::random` module that contains useful nodes related to random number generators as `SampleAndHold`. This feature depends on the `rand` crate and the `nodes` feature.
//!
//! # Example
//! ```rust
//! // Using the `nodes` feature
//!
//! use siraph::Graph;
//! use siraph::nodes::{Hold, Pulse, FromIter, Map};
//!
//! let mut graph = Graph::new();
//!
//! // First, we can insert nodes into the graph.
//!
//! // This node just takes the values given by an iterator
//! // and send them into its output.
//! let from_iter = graph.insert(FromIter::new(std::iter::successors(Some(0u32), |&i| Some(i + 1))));
//!
//! // This node infinitly outputs 4 `false` then 1 `true`.
//! let pulse = graph.insert(Pulse::new(4));
//!
//! // This one wait for a pulse and holds the value its has in its input
//! // until a new pulse.
//! let hold = graph.insert(Hold::<u32>::new());
//!
//! // Simply uses the given function to maps its input to its output.
//! let map = graph.insert(Map::new(|val: u32| val * val));
//!
//! // Then, we can plug them together.
//! graph.plug(from_iter, "output", hold, "input").unwrap();
//! graph.plug(pulse, "output", hold, "resample").unwrap();
//! graph.plug(hold, "output", map, "input").unwrap();
//!
//! // Once our graph is done, we can retreive values from it using a sink.
//! let mut sink = graph.sink(map, "output").unwrap();
//!
//! // Here is a simple schem of what we have so far
//! /*
//! +------------------------+
//! | from_iter   output i32 >----+   +------------------------------+
//! +------------------------+    +---> input                        |  +----------------------+
//!                                   |            hold   output i32 >--> input   map   output > sink
//!                               +---> resample                     |  +----------------------+
//! +---------------------+       |   +------------------------------+
//! | pulse   output bool >-------+
//! +---------------------+
//! */
//!
//! // Values can be retreived with the `next` function on the sink
//! // Once again, the `()` is the context provided to the nodes.
//! assert_eq!(sink.next(), Some(0));
//! assert_eq!(sink.next(), Some(0));
//! assert_eq!(sink.next(), Some(0));
//! assert_eq!(sink.next(), Some(0));
//! assert_eq!(sink.next(), Some(0));
//! assert_eq!(sink.next(), Some(25));
//! assert_eq!(sink.next(), Some(25));
//! assert_eq!(sink.next(), Some(25));
//! assert_eq!(sink.next(), Some(25));
//! assert_eq!(sink.next(), Some(25));
//!
//! // The sink is an iterator
//! for (i, val) in sink.take(1000).enumerate() {
//!     let i = ((i/5)*5) as u32 + 10;
//!     assert_eq!(val, i*i)
//! }
//! ```
//!
//! ## Create your own nodes
//! You can create your own nodes using the `Node` trait.
//! ```rust
//! use siraph::{Node, Register, Input, Output};
//!
//! // Our node will take an input and smooth it using a basic interpolation function.
//! #[derive(Default)]
//! pub struct Smooth {
//!     input: Input<f64>,
//!     output: Output<f64>,
//!
//!     last_value: Option<f64>,
//! }
//!
//! impl Node for Smooth {
//!     fn register(&self, r: &mut Register) {
//!         // This function will register the inputs and outputs of this node.
//!         r.input("input", &self.input);
//!         r.output("output", &self.output);
//!     }
//!
//!     fn process(&mut self) {
//!         // It is in this function that all the processing will be done.
//!         const X: f64 = 1.0/3.0;
//!
//!         // In our case, our computation is not very expensive but
//!         // in other cases, things can get complicated.
//!         // We can skip certain part of the processing by
//!         // checking if our outputs are used.
//!         if self.output.is_used() {
//!             if let Some(cur) = self.input.get() {
//!                 if let Some(last) = self.last_value {
//!                     self.last_value = Some(last * X + (1.0 - X) * cur);
//!                     self.output.set(self.last_value);
//!                 } else {
//!                     self.output.set(cur);
//!                     self.last_value = Some(cur);
//!                 }
//!             } else {
//!                 self.output.set(None);
//!             }
//!         }
//!     }
//!
//!     fn reset(&mut self) {
//!         // In this function, the inputs of the node should not be used even
//!         // if they may return valid values.
//!         self.last_value = None;
//!     }
//! }
//! ```

mod error;
pub use error::{Error, Result};

mod node;
pub use node::{Input, Name, Node, Output, Register};

mod graph;
pub use graph::{Dependencies, Graph, NodeHandle, Sink};

#[cfg(feature = "nodes")]
pub mod nodes;