The siraph crate is a node-based digital signal processing crate.

Features

Example

``rust // Using thenodes` 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::::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. asserteq!(sink.next(), Some(0)); asserteq!(sink.next(), Some(0)); asserteq!(sink.next(), Some(0)); asserteq!(sink.next(), Some(0)); asserteq!(sink.next(), Some(0)); asserteq!(sink.next(), Some(25)); asserteq!(sink.next(), Some(25)); asserteq!(sink.next(), Some(25)); asserteq!(sink.next(), Some(25)); asserteq!(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, ii) } ```

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, output: Output,

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;
}

} ```

Todo List