1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! 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};
//!
//! // The `()` is a context that will be given to the nodes.
//! // The nodes that we are going to use do not need such a context.
//! 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 can be turned into an iterator using the `with_ctx` function.
//! for (i, val) in sink.with_ctx(|| &()).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>,
//! }
//!
//! // The ctx must stay generic because even if this node does not need
//! // a global context, other nodes in the graph may need it.
//! impl<Ctx> Node<Ctx> 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, _ctx: &Ctx) {
//!         // 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, SinkWithCtx};

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