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;