11#pragma once
22
3- #include < audioapi/core/AudioNode.h>
43#include < audioapi/core/utils/graph/InputPool.hpp>
54#include < audioapi/core/utils/graph/NodeHandle.hpp>
65
76#include < algorithm>
87#include < cassert>
9- #include < concepts>
108#include < cstdint>
119#include < iterator>
1210#include < memory>
1614
1715namespace audioapi ::utils::graph {
1816
19- template <typename T>
20- concept AudioGraphNode = std::derived_from<T, ::audioapi::AudioNode>;
21-
2217// / @brief Cache-friendly, index-stable node storage with in-place topological sort.
2318// /
2419// / Nodes are stored in a flat vector that is kept topologically sorted
2520// / (sources first, sinks last). The graph supports O(V+E) compaction of
2621// / orphaned nodes and O(1)-extra-space Kahn's toposort.
2722// /
2823// / @note Can store at most 2^30 nodes due to bit-packed indices (~10^9).
29- template <AudioGraphNode NodeType>
3024class AudioGraph {
3125 // ── Node ────────────────────────────────────────────────────────────────
3226
3327 struct Node {
3428 Node () = default ;
35- explicit Node (std::shared_ptr<NodeHandle<NodeType> > handle) : handle(handle) {}
29+ explicit Node (std::shared_ptr<NodeHandle> handle) : handle(handle) {}
3630
37- std::shared_ptr<NodeHandle<NodeType> > handle = nullptr ; // owned handle bridging to HostGraph
38- std::uint32_t input_head = InputPool::kNull ; // head of input linked list in pool_
31+ std::shared_ptr<NodeHandle> handle = nullptr ; // owned handle bridging to HostGraph
32+ std::uint32_t input_head = InputPool::kNull ; // head of input linked list in pool_
3933
4034 std::uint32_t topo_out_degree : 31 = 0 ; // scratch — Kahn's out-degree counter
4135 unsigned will_be_deleted : 1 = 0 ; // scratch — marked for compaction removal
@@ -60,10 +54,10 @@ class AudioGraph {
6054 AudioGraph (AudioGraph &&) noexcept = default ;
6155 AudioGraph &operator =(AudioGraph &&) noexcept = default ;
6256
63- // / @brief Entry returned by iter() — a reference to the audio node and a view of its inputs.
57+ // / @brief Entry returned by iter() — a reference to the graph object and a view of its inputs.
6458 template <typename InputsView>
6559 struct Entry {
66- NodeType &audioNode ;
60+ GraphObject &graphObject ;
6761 InputsView inputs;
6862 };
6963
@@ -83,13 +77,13 @@ class AudioGraph {
8377
8478 // / @brief Provides an iterable view of the nodes in topological order.
8579 // /
86- // / Each entry contains a reference to the AudioNode and an immutable view
87- // / of its inputs (as references to AudioNodes ).
80+ // / Each entry contains a reference to the GraphObject and an immutable view
81+ // / of its inputs (as references to GraphObject ).
8882 // /
8983 // / ## Example usage:
9084 // / ```cpp
91- // / for (auto [audioNode , inputs] : graph.iter()) {
92- // / // process audioNode and its inputs
85+ // / for (auto [graphObject , inputs] : graph.iter()) {
86+ // / // process graphObject and its inputs
9387 // / }
9488 // / ```
9589 // / @note Lifetime of entries is bound to this graph — they are not owned.
@@ -115,14 +109,14 @@ class AudioGraph {
115109
116110 // / @brief Adds a new node. AudioGraph takes shared ownership of the handle.
117111 // / @param handle shared NodeHandle bridging to HostGraph
118- void addNode (std::shared_ptr<NodeHandle<NodeType> > handle);
112+ void addNode (std::shared_ptr<NodeHandle> handle);
119113
120114 // / @brief Recomputes topological order (if dirty), then compacts the graph
121115 // / by removing orphaned, input-free, destructible nodes.
122116 // /
123117 // / When a node is compacted out its `shared_ptr<NodeHandle>` is released
124118 // / (refcount drops 2 → 1). HostGraph detects this via `use_count() == 1`
125- // / and destroys the ghost + AudioNode on the main thread.
119+ // / and destroys the ghost + GraphObject on the main thread.
126120 // /
127121 // / Uses a two-pass approach: pass 1 marks deletions (cascading in topo
128122 // / order) and computes index remapping; pass 2 remaps inputs and shifts
@@ -155,68 +149,59 @@ class AudioGraph {
155149
156150// ── Accessors ─────────────────────────────────────────────────────────────
157151
158- template <AudioGraphNode NodeType>
159- auto AudioGraph<NodeType>::operator [](std::uint32_t index) -> Node & {
152+ inline auto AudioGraph::operator [](std::uint32_t index) -> Node & {
160153 return nodes[index];
161154}
162155
163- template <AudioGraphNode NodeType>
164- auto AudioGraph<NodeType>::operator [](std::uint32_t index) const -> const Node & {
156+ inline auto AudioGraph::operator [](std::uint32_t index) const -> const Node & {
165157 return nodes[index];
166158}
167159
168- template <AudioGraphNode NodeType>
169- size_t AudioGraph<NodeType>::size() const {
160+ inline size_t AudioGraph::size () const {
170161 return nodes.size ();
171162}
172163
173- template <AudioGraphNode NodeType>
174- bool AudioGraph<NodeType>::empty() const {
164+ inline bool AudioGraph::empty () const {
175165 return nodes.empty ();
176166}
177167
178- template <AudioGraphNode NodeType>
179- auto AudioGraph<NodeType>::iter() {
180- return nodes | std::views::transform ([this ](Node &node) {
168+ inline auto AudioGraph::iter () {
169+ return nodes |
170+ std::views::filter ([](const Node &n) { return n.handle ->audioNode ->isProcessable (); }) |
171+ std::views::transform ([this ](Node &node) {
181172 return Entry{
182173 *node.handle ->audioNode ,
183174 pool_.view (node.input_head ) |
184- std::views::transform ([this ](std::uint32_t idx) -> const NodeType & {
175+ std::views::transform ([this ](std::uint32_t idx) -> const GraphObject & {
185176 return *nodes[idx].handle ->audioNode ;
186177 })};
187178 });
188179}
189180
190- template <AudioGraphNode NodeType>
191- InputPool &AudioGraph<NodeType>::pool() {
181+ inline InputPool &AudioGraph::pool () {
192182 return pool_;
193183}
194184
195- template <AudioGraphNode NodeType>
196- const InputPool &AudioGraph<NodeType>::pool() const {
185+ inline const InputPool &AudioGraph::pool () const {
197186 return pool_;
198187}
199188
200- template <AudioGraphNode NodeType>
201- void AudioGraph<NodeType>::reserveNodes(std::uint32_t capacity) {
189+ inline void AudioGraph::reserveNodes (std::uint32_t capacity) {
202190 nodes.reserve (capacity);
203191}
204192
205193// ── Mutators ──────────────────────────────────────────────────────────────
206194
207- template <AudioGraphNode NodeType>
208- void AudioGraph<NodeType>::markDirty() {
195+ inline void AudioGraph::markDirty () {
209196 topo_order_dirty = true ;
210197}
211198
212- template <AudioGraphNode NodeType>
213- void AudioGraph<NodeType>::addNode(std::shared_ptr<NodeHandle<NodeType>> handle) {
199+ inline void AudioGraph::addNode (std::shared_ptr<NodeHandle> handle) {
214200 handle->index = static_cast <std::uint32_t >(nodes.size ());
215201 nodes.emplace_back (std::move (handle));
216202}
217203
218- template <AudioGraphNode NodeType>
219- void AudioGraph<NodeType>::process() {
204+ inline void AudioGraph::process () {
220205 if (topo_order_dirty) {
221206 topo_order_dirty = false ;
222207 kahn_toposort ();
@@ -293,8 +278,7 @@ void AudioGraph<NodeType>::process() {
293278
294279// ── Kahn's toposort ───────────────────────────────────────────────────────
295280
296- template <AudioGraphNode NodeType>
297- void AudioGraph<NodeType>::kahn_toposort() {
281+ inline void AudioGraph::kahn_toposort () {
298282 const auto n = static_cast <std::uint32_t >(nodes.size ());
299283 if (n <= 1 )
300284 return ;
0 commit comments