Sound Byte Libs 0.5.1-121-g3358a44
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1// sbl/dsp/diagram/graph.hpp — A model's wiring, as data (AP-037)
2//
3// Physical-modeling components describe themselves into a Graph: the nodes a
4// paper's block diagram would draw — delay lines, filters, reflections,
5// junctions, sources, loads, taps — and the edges between them, each carrying
6// a wave variable. A widget that wired components together describes the
7// connections its lambdas made, because only it knows.
8//
9// This is a development tool. describe() is never called from audio code, so
10// on the target it is never emitted; the Graph itself is fixed-capacity POD
11// with no allocation, so it can sit in the library without a host-only
12// guard. Rendering (mermaid.hpp) is where formatting lives.
13//
14// Groups nest: a widget's group holds its compositions' groups. The renderer
15// can draw every node (Depth::Full, the paper) or fold each child group into
16// one box (Depth::Components).
17
18#ifndef SBL_DSP_DIAGRAM_GRAPH_HPP_
19#define SBL_DSP_DIAGRAM_GRAPH_HPP_
20
21#include <cstdint>
22
24
25/// What a node is, in the paper's vocabulary.
26enum class Kind : uint8_t {
27 DelayLine, ///< value: length in samples
28 Loss, ///< value: cutoff in Hz
29 Allpass, ///< value: delay in samples
30 DcBlock, ///< value: corner in Hz
31 Reflection, ///< value: coefficient (−1 wall, +1 free end)
32 Junction, ///< where waves scatter or a source meets the string
33 Source, ///< a driving signal (bow velocity, pluck)
34 Load, ///< a lumped body; value: Hz, value2: Q
35 Resonator, ///< a modal source; value: Hz, value2: Q
36 Bandpass, ///< a banded-waveguide band; value: Hz, value2: Q
37 Gain, ///< value: the factor
38 Sum, ///< where signals add
39 Tap, ///< an output
40 Component, ///< a folded group, made by the renderer
41};
42
43/// What travels along an edge.
44enum class Wave : uint8_t { Velocity, Force, Signal };
45
46constexpr uint8_t NO_GROUP = 0xFF;
47constexpr uint8_t NO_NODE = 0xFF;
48
49struct Node {
51 const char* name; ///< short label; string literals only, the graph does not own it
52 float value;
53 float value2;
54 uint8_t group;
55};
56
57struct Edge {
58 uint8_t from;
59 uint8_t to;
61};
62
63struct Group {
64 const char* name;
65 uint8_t parent;
66};
67
68class Graph {
69public:
70 static constexpr uint8_t MAX_NODES = 64; ///< a 32-mode bank and the string around it
71 static constexpr uint8_t MAX_EDGES = 128;
72 static constexpr uint8_t MAX_GROUPS = 16;
73
74 /// Open a group (a component); returns its id. Nodes added with it belong to it.
75 uint8_t add_group(const char* name, uint8_t parent = NO_GROUP) {
76 if (group_count_ >= MAX_GROUPS) { dropped_ = true; return NO_GROUP; }
77 groups_[group_count_] = Group{name, parent};
78 return group_count_++;
79 }
80
81 uint8_t add_node(Kind kind, const char* name, uint8_t group,
82 float value = 0.0f, float value2 = 0.0f) {
83 if (node_count_ >= MAX_NODES) { dropped_ = true; return NO_NODE; }
84 nodes_[node_count_] = Node{kind, name, value, value2, group};
85 return node_count_++;
86 }
87
88 void add_edge(uint8_t from, uint8_t to, Wave wave) {
89 if (edge_count_ >= MAX_EDGES || from == NO_NODE || to == NO_NODE) { dropped_ = true; return; }
90 edges_[edge_count_++] = Edge{from, to, wave};
91 }
92
93 uint8_t node_count() const { return node_count_; }
94 uint8_t edge_count() const { return edge_count_; }
95 uint8_t group_count() const { return group_count_; }
96 const Node& node(uint8_t i) const { return nodes_[i]; }
97 const Edge& edge(uint8_t i) const { return edges_[i]; }
98 const Group& group(uint8_t i) const { return groups_[i]; }
99
100 /// How many nodes of a kind the graph holds — the structural tests' question.
101 uint8_t count(Kind kind) const {
102 uint8_t n = 0;
103 for (uint8_t i = 0; i < node_count_; ++i) {
104 if (nodes_[i].kind == kind) ++n;
105 }
106 return n;
107 }
108
109 /// True if a describe() lost something: a node, edge or group was refused.
110 bool overflowed() const { return dropped_; }
111
112 void clear() {
113 node_count_ = 0;
114 edge_count_ = 0;
115 group_count_ = 0;
116 dropped_ = false;
117 }
118
119private:
120 Node nodes_[MAX_NODES] = {};
121 Edge edges_[MAX_EDGES] = {};
122 Group groups_[MAX_GROUPS] = {};
123 uint8_t node_count_ = 0;
124 uint8_t edge_count_ = 0;
125 uint8_t group_count_ = 0;
126 bool dropped_ = false;
127};
128
129/// The ports a component exposes after describing itself, so an owner can wire them.
130struct Ports {
131 static constexpr uint8_t MAX_NAMED = 8;
132
133 uint8_t in = NO_NODE; ///< where a wave enters (the junction, the load)
134 uint8_t out = NO_NODE; ///< where a wave leaves (the pickup, the reflection)
135 uint8_t group = NO_GROUP;
136
137 /// Further ports by name, for a component with more ends than in and
138 /// out: a loop's second end, an N-port junction's arms, a bank's per-mode taps.
139 struct Named {
140 const char* name; ///< string literal
141 uint8_t node;
142 };
144 uint8_t named_count = 0;
145
146 /// Add a named port; a ninth is refused and find() answers NO_NODE for it.
147 void add(const char* name, uint8_t node) {
148 if (named_count >= MAX_NAMED) return;
149 named[named_count++] = Named{name, node};
150 }
151
152 /// The node behind a name, or NO_NODE.
153 uint8_t find(const char* name) const {
154 for (uint8_t i = 0; i < named_count; ++i) {
155 const char* a = named[i].name;
156 const char* b = name;
157 while (*a != '\0' && *a == *b) { ++a; ++b; }
158 if (*a == '\0' && *b == '\0') return named[i].node;
159 }
160 return NO_NODE;
161 }
162};
163
164} // namespace sbl::dsp::diagram
165
166#endif // SBL_DSP_DIAGRAM_GRAPH_HPP_
uint8_t node_count() const
Definition graph.hpp:93
bool overflowed() const
True if a describe() lost something: a node, edge or group was refused.
Definition graph.hpp:110
static constexpr uint8_t MAX_GROUPS
Definition graph.hpp:72
static constexpr uint8_t MAX_EDGES
Definition graph.hpp:71
uint8_t add_group(const char *name, uint8_t parent=NO_GROUP)
Open a group (a component); returns its id. Nodes added with it belong to it.
Definition graph.hpp:75
uint8_t group_count() const
Definition graph.hpp:95
void add_edge(uint8_t from, uint8_t to, Wave wave)
Definition graph.hpp:88
const Group & group(uint8_t i) const
Definition graph.hpp:98
const Edge & edge(uint8_t i) const
Definition graph.hpp:97
uint8_t count(Kind kind) const
How many nodes of a kind the graph holds — the structural tests' question.
Definition graph.hpp:101
uint8_t edge_count() const
Definition graph.hpp:94
const Node & node(uint8_t i) const
Definition graph.hpp:96
static constexpr uint8_t MAX_NODES
a 32-mode bank and the string around it
Definition graph.hpp:70
uint8_t add_node(Kind kind, const char *name, uint8_t group, float value=0.0f, float value2=0.0f)
Definition graph.hpp:81
A model's wiring as data, for pictures.
Definition graph.hpp:23
Wave
What travels along an edge.
Definition graph.hpp:44
constexpr uint8_t NO_NODE
Definition graph.hpp:47
Kind
What a node is, in the paper's vocabulary.
Definition graph.hpp:26
@ Loss
value: cutoff in Hz
@ Bandpass
a banded-waveguide band; value: Hz, value2: Q
@ Component
a folded group, made by the renderer
@ Gain
value: the factor
@ DcBlock
value: corner in Hz
@ Junction
where waves scatter or a source meets the string
@ Resonator
a modal source; value: Hz, value2: Q
@ Sum
where signals add
@ Reflection
value: coefficient (−1 wall, +1 free end)
@ DelayLine
value: length in samples
@ Load
a lumped body; value: Hz, value2: Q
@ Source
a driving signal (bow velocity, pluck)
@ Allpass
value: delay in samples
constexpr uint8_t NO_GROUP
Definition graph.hpp:46
const char * name
Definition graph.hpp:64
const char * name
short label; string literals only, the graph does not own it
Definition graph.hpp:51
const char * name
string literal
Definition graph.hpp:140
The ports a component exposes after describing itself, so an owner can wire them.
Definition graph.hpp:130
static constexpr uint8_t MAX_NAMED
Definition graph.hpp:131
uint8_t find(const char *name) const
The node behind a name, or NO_NODE.
Definition graph.hpp:153
void add(const char *name, uint8_t node)
Add a named port; a ninth is refused and find() answers NO_NODE for it.
Definition graph.hpp:147
uint8_t in
where a wave enters (the junction, the load)
Definition graph.hpp:133
Named named[MAX_NAMED]
Definition graph.hpp:143
uint8_t out
where a wave leaves (the pickup, the reflection)
Definition graph.hpp:134