Skip to content

Commit 5853e5f

Browse files
committed
Add a dasp_graph crate.
This adds a new crate for working with dynamic audio graphs. From the new docs: > `dasp_graph` is targeted towards users who require an efficient yet > flexible and dynamically configurable audio graph. Use cases might > include virtual mixers, digital audio workstations, game audio systems, > virtual modular synthesizers and related usecases. This work has been a long-time coming and is the result of many discussions in the rust audio community and many lessons learned over the last few years of working with rust and audio. In particular: - the development of and reflection on [dsp-chain](https://crates.io/crates/dsp-chain) and its shortcomings. - The (reasonable) limitations of [dasp_signal](https://crates.io/crates/dasp_signal) when dynamically configuring graphs. - Discussion on the design of audio graphs with @raphlinus at RustAudio/dsp-chain#141. - The development of the [spatial audio server](https://github.com/museumsvictoria/spatial_audio_server). - A recent email discussion with Sami Perttu on DASP and audio graphs. `dasp_graph` is of course not a one-size-fits-all solution. Instead, it is designed specifically to work well alongside (and fill a gap within) the rest of the `dasp` crate ecosystem. Please refer to the "Comparing `dasp_signal`" section of the `dasp_graph` root documentation for a more detailed overview of the design choices between the two, what applications each are best suited toward and how the two best interoperate together. A small suite of node implementations are provided out of the box including a `Delay`, `Sum`, `Pass`, `GraphNode` and `BoxedNode`, all of which can be enabled/disabled via their associated features. Following this, I have some ideas for adding an optional `sync` module to the crate, aimed at controlling and monitoring a dasp graph and it's nodes from a separate thread (i.e. for convenient use alongside a GUI) in a non-dynamically-allocating, non-blocking manner. The work so far has been performed with these plans in mind. The ideas are for the most part based on the discussion at RustAudio/dsp-chain#141. Also, `no_std` support for `dasp_graph` is currently blocked on petgraph support for `no_std`. A PR is open for adding `no_std` support at petgraph/petgraph#238. In the meantime, the `std` feature must be enabled to use the new `dasp::graph` module. This is also noted in the updated docs. For more information about the crate and inner workings feel free to read through the new `dasp_graph` docs. I'm yet to add examples, but hopefully the added tests can give a good idea of how to use the crate in the meantime.
1 parent 16f7498 commit 5853e5f

16 files changed

+1167
-1
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ members = [
33
"dasp",
44
"dasp_envelope",
55
"dasp_frame",
6+
"dasp_graph",
67
"dasp_interpolate",
78
"dasp_peak",
89
"dasp_ring_buffer",

dasp/Cargo.toml

+15-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ edition = "2018"
1313
[dependencies]
1414
dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, optional = true }
1515
dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false }
16+
dasp_graph = { version = "0.11", path = "../dasp_graph", default-features = false, optional = true }
1617
dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false, optional = true }
1718
dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false, optional = true }
1819
dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false, optional = true }
@@ -24,7 +25,13 @@ dasp_window = { version = "0.11", path = "../dasp_window", default-features = fa
2425

2526
[features]
2627
default = ["std"]
27-
all = ["std", "all-no-std"]
28+
all = [
29+
"std",
30+
"all-no-std",
31+
# TODO: Move these into `all-no-std` once `dasp_graph` gains `no_std` support.
32+
"graph",
33+
"graph-all-nodes",
34+
]
2835
all-no-std = [
2936
"envelope",
3037
"envelope-peak",
@@ -65,6 +72,13 @@ std = [
6572
envelope = ["dasp_envelope"]
6673
envelope-peak = ["dasp_envelope/peak"]
6774
envelope-rms = ["dasp_envelope/rms"]
75+
graph = ["dasp_graph"]
76+
graph-all-nodes = ["dasp_graph/all-nodes"]
77+
graph-node-boxed = ["dasp_graph/node-boxed"]
78+
graph-node-delay = ["dasp_graph/node-delay"]
79+
graph-node-graph = ["dasp_graph/node-graph"]
80+
graph-node-pass = ["dasp_graph/node-pass"]
81+
graph-node-sum = ["dasp_graph/node-sum"]
6882
interpolate = ["dasp_interpolate"]
6983
interpolate-floor = ["dasp_interpolate/floor"]
7084
interpolate-linear = ["dasp_interpolate/linear"]

dasp/src/lib.rs

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
//! - See the [**Converter** type](./signal/interpolate/struct.Converter.html) for sample rate
2424
//! conversion and scaling.
2525
//! - See the [**ring_buffer** module](./ring_buffer/index.html) for fast FIFO queue options.
26+
//! - See the [**graph** module](./graph/index.html) for working with dynamic audio graphs.
2627
//!
2728
//! ## Optional Features
2829
//!
@@ -40,6 +41,16 @@
4041
//! [envelope](./envelope/index.html) module.
4142
//! - The **envelope-peak** feature enables peak envelope detection.
4243
//! - The **envelope-rms** feature enables RMS envelope detection.
44+
//! - The **graph** feature enables the `dasp_graph` crate via the [graph](./graph/index.html)
45+
//! module.
46+
//! - The **node-boxed** feature provides a `Node` implementation for `Box<dyn Node>`.
47+
//! - The **node-delay** feature provides a simple multi-channel `Delay` node.
48+
//! - The **node-graph** feature provides an implementation of `Node` for a type that encapsulates
49+
//! another `dasp` graph type.
50+
//! - The **node-pass** feature provides a `Pass` node that simply passes audio from its
51+
//! inputs to its outputs.
52+
//! - The **node-signal** feature provides an implementation of `Node` for `dyn Signal`.
53+
//! - The **node-sum** feature provides `Sum` and `SumBuffers` `Node` implementations.
4354
//! - The **interpolate** feature enables the `dasp_interpolate` crate via the
4455
//! [interpolate](./interpolate/index.html) module.
4556
//! - The **interpolate-floor** feature enables a floor interpolation implementation.
@@ -83,6 +94,10 @@
8394
//! `--no-default-features`.
8495
//!
8596
//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature.
97+
//!
98+
//! *Note: The **graph** module is currently only available with the **std** feature enabled.
99+
//! Adding support for `no_std` is pending the addition of support for `no_std` in petgraph. See
100+
//! [this PR](https://github.com/petgraph/petgraph/pull/238).
86101
87102
#![cfg_attr(not(feature = "std"), no_std)]
88103

@@ -91,6 +106,10 @@
91106
pub use dasp_envelope as envelope;
92107
#[doc(inline)]
93108
pub use dasp_frame::{self as frame, Frame};
109+
// TODO: Remove `std` requirement once `dasp_graph` gains `no_std` support.
110+
#[cfg(all(feature = "graph", feature = "std"))]
111+
#[doc(inline)]
112+
pub use dasp_graph as graph;
94113
#[cfg(feature = "interpolate")]
95114
#[doc(inline)]
96115
pub use dasp_interpolate as interpolate;

dasp_graph/Cargo.toml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "dasp_graph"
3+
description = "A digital audio signal processing graph."
4+
version = "0.11.0"
5+
authors = ["mitchmindtree <[email protected]>"]
6+
readme = "../README.md"
7+
keywords = ["dsp", "audio", "graph", "pcm", "audio"]
8+
license = "MIT OR Apache-2.0"
9+
repository = "https://github.com/rustaudio/dasp.git"
10+
homepage = "https://github.com/rustaudio/dasp"
11+
edition = "2018"
12+
13+
[features]
14+
default = ["all-nodes"]
15+
all-nodes = ["node-boxed", "node-delay", "node-graph", "node-pass", "node-signal", "node-sum"]
16+
node-boxed = []
17+
node-delay = ["dasp_ring_buffer"]
18+
node-graph = []
19+
node-pass = []
20+
node-signal = ["dasp_frame", "dasp_signal"]
21+
node-sum = ["dasp_slice"]
22+
23+
[dependencies]
24+
dasp_frame = { version = "0.11", default-features = false, features = ["std"], optional = true }
25+
dasp_ring_buffer = { version = "0.11", default-features = false, features = ["std"], optional = true }
26+
dasp_signal = { version = "0.11", default-features = false, features = ["std"], optional = true }
27+
dasp_slice = { version = "0.11", default-features = false, features = ["std"], optional = true }
28+
petgraph = { version = "0.5", default-features = false }
29+
30+
[dev-dependencies]
31+
petgraph = { version = "0.5", features = ["stable_graph"] }

dasp_graph/src/buffer.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use core::fmt;
2+
use core::ops::{Deref, DerefMut};
3+
4+
/// The fixed-size buffer used for processing the graph.
5+
#[derive(Clone)]
6+
pub struct Buffer {
7+
data: [f32; Self::LEN],
8+
}
9+
10+
impl Buffer {
11+
/// The fixed length of the **Buffer** type.
12+
pub const LEN: usize = 64;
13+
/// A silent **Buffer**.
14+
pub const SILENT: Self = Buffer {
15+
data: [0.0; Self::LEN],
16+
};
17+
18+
/// Short-hand for writing silence to the whole buffer.
19+
pub fn silence(&mut self) {
20+
self.data.copy_from_slice(&Self::SILENT)
21+
}
22+
}
23+
24+
impl Default for Buffer {
25+
fn default() -> Self {
26+
Self::SILENT
27+
}
28+
}
29+
30+
impl From<[f32; Self::LEN]> for Buffer {
31+
fn from(data: [f32; Self::LEN]) -> Self {
32+
Buffer { data }
33+
}
34+
}
35+
36+
impl fmt::Debug for Buffer {
37+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38+
fmt::Debug::fmt(&self.data[..], f)
39+
}
40+
}
41+
42+
impl PartialEq for Buffer {
43+
fn eq(&self, other: &Self) -> bool {
44+
&self[..] == &other[..]
45+
}
46+
}
47+
48+
impl Deref for Buffer {
49+
type Target = [f32];
50+
fn deref(&self) -> &Self::Target {
51+
&self.data[..]
52+
}
53+
}
54+
55+
impl DerefMut for Buffer {
56+
fn deref_mut(&mut self) -> &mut Self::Target {
57+
&mut self.data[..]
58+
}
59+
}

0 commit comments

Comments
 (0)