[][src]Crate siraph

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

// 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.

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

Modules

nodes

Structs

Dependencies

An iterator that look for the dependencies of a node within a Graph.

Graph

The central structure of the crate. The graph manages a bunch of nodes and makes them work together.

Input

A read-only interface to a T.

Name

The name of an input or an output.

NodeHandle

A handle to a node.

Output

A write-only reference to a T.

Register

A simple structure responsible for registering the inputs and outputs of a node.

Sink

Enums

Error

A generic error type for the crate.

Traits

Node

Nodes are signal processing units. They take data from their inputs to and send it back through their outputs.

Type Definitions

Result

The result type for the crate.