react-native-audio-api 1.0.0 → 1.1.0-nightly-1df11c0-20261105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/cpp/audioapi/core/utils/graph/AudioGraph.cpp +15 -3
- package/common/cpp/audioapi/core/utils/graph/AudioGraph.h +10 -0
- package/common/cpp/audioapi/core/utils/graph/Graph.cpp +1 -1
- package/common/cpp/audioapi/core/utils/graph/HostGraph.cpp +17 -13
- package/common/cpp/audioapi/core/utils/graph/NodeHandle.cpp +1 -2
- package/common/cpp/audioapi/core/utils/graph/NodeHandle.h +11 -4
- package/common/cpp/audioapi/core/utils/graph/README.md +1 -1
- package/package.json +1 -1
|
@@ -16,6 +16,18 @@ auto AudioGraph::operator[](std::uint32_t index) const -> const Vertex & {
|
|
|
16
16
|
return nodes[index];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
auto AudioGraph::operator[](const NodeHandle &handle) -> Vertex & {
|
|
20
|
+
return nodes[handle.index_];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
auto AudioGraph::operator[](const NodeHandle &handle) const -> const Vertex & {
|
|
24
|
+
return nodes[handle.index_];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
std::uint32_t AudioGraph::indexOf(const NodeHandle &handle) const {
|
|
28
|
+
return handle.index_;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
size_t AudioGraph::size() const {
|
|
20
32
|
return nodes.size();
|
|
21
33
|
}
|
|
@@ -43,7 +55,7 @@ void AudioGraph::markDirty() {
|
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
void AudioGraph::addNode(std::shared_ptr<NodeHandle> handle) {
|
|
46
|
-
handle->
|
|
58
|
+
handle->index_ = static_cast<std::uint32_t>(nodes.size());
|
|
47
59
|
nodes.emplace_back(std::move(handle));
|
|
48
60
|
}
|
|
49
61
|
|
|
@@ -120,7 +132,7 @@ void AudioGraph::sortAndCompact() {
|
|
|
120
132
|
if (b != e) {
|
|
121
133
|
nodes[b] = std::move(nodes[e]);
|
|
122
134
|
}
|
|
123
|
-
nodes[b].handle->
|
|
135
|
+
nodes[b].handle->index_ = b;
|
|
124
136
|
b++;
|
|
125
137
|
}
|
|
126
138
|
|
|
@@ -242,7 +254,7 @@ void AudioGraph::kahn_toposort() {
|
|
|
242
254
|
// Phase 5: update handle indices & reset scratch
|
|
243
255
|
for (std::uint32_t i = 0; i < n; i++) {
|
|
244
256
|
if (nodes[i].handle) {
|
|
245
|
-
nodes[i].handle->
|
|
257
|
+
nodes[i].handle->index_ = i;
|
|
246
258
|
}
|
|
247
259
|
nodes[i].target_place = -1;
|
|
248
260
|
}
|
|
@@ -103,6 +103,16 @@ class AudioGraph {
|
|
|
103
103
|
/// @brief Access node by flat-vector index (const).
|
|
104
104
|
[[nodiscard]] const Vertex &operator[](std::uint32_t index) const;
|
|
105
105
|
|
|
106
|
+
/// @brief Access the node a handle refers to, at its current position.
|
|
107
|
+
[[nodiscard]] Vertex &operator[](const NodeHandle &handle);
|
|
108
|
+
|
|
109
|
+
/// @brief Access the node a handle refers to, at its current position (const).
|
|
110
|
+
[[nodiscard]] const Vertex &operator[](const NodeHandle &handle) const;
|
|
111
|
+
|
|
112
|
+
/// @brief Current flat-vector position of the handle's node.
|
|
113
|
+
/// @note Valid only until the next sortAndCompact(); never cache it across threads.
|
|
114
|
+
[[nodiscard]] std::uint32_t indexOf(const NodeHandle &handle) const;
|
|
115
|
+
|
|
106
116
|
/// @brief Number of live nodes in the graph.
|
|
107
117
|
[[nodiscard]] size_t size() const;
|
|
108
118
|
|
|
@@ -82,7 +82,7 @@ void Graph::advanceUnpulledNodes(std::size_t currentSampleFrame) {
|
|
|
82
82
|
Graph::HostVertex *Graph::addNode(std::unique_ptr<GraphObject> audioNode) {
|
|
83
83
|
// collectDisposedNodes();
|
|
84
84
|
|
|
85
|
-
auto handle = std::make_shared<NodeHandle>(
|
|
85
|
+
auto handle = std::make_shared<NodeHandle>(std::move(audioNode));
|
|
86
86
|
auto [hostNode, event] = hostGraph.addNode(handle);
|
|
87
87
|
|
|
88
88
|
sendNodeGrowIfNeeded();
|
|
@@ -298,8 +298,7 @@ auto HostGraph::removeNode(HostVertex *node) -> Result<AGEvent, GraphError> {
|
|
|
298
298
|
|
|
299
299
|
node->ghost = true;
|
|
300
300
|
|
|
301
|
-
return Ok(
|
|
302
|
-
[h = node->handle](AudioGraph &graph, auto &) mutable { graph[h->index].orphaned = true; });
|
|
301
|
+
return Ok([h = node->handle](AudioGraph &graph, auto &) mutable { graph[*h].orphaned = true; });
|
|
303
302
|
}
|
|
304
303
|
|
|
305
304
|
auto HostGraph::addEdge(HostVertex *from, HostVertex *to) -> Result<AGEvent, GraphError> {
|
|
@@ -355,7 +354,7 @@ auto HostGraph::addEdge(HostVertex *from, HostVertex *to) -> Result<AGEvent, Gra
|
|
|
355
354
|
disposer.dispose(toNode->exchangeInputScratch(std::move(*reservedInputs)));
|
|
356
355
|
// dispose the unique_ptr
|
|
357
356
|
disposer.dispose(std::move(reservedInputs));
|
|
358
|
-
graph.pool().push(graph[hTo
|
|
357
|
+
graph.pool().push(graph[*hTo].input_head, graph.indexOf(*hFrom));
|
|
359
358
|
graph.markDirty();
|
|
360
359
|
});
|
|
361
360
|
}
|
|
@@ -395,7 +394,7 @@ auto HostGraph::removeEdge(HostVertex *from, HostVertex *to) -> Result<AGEvent,
|
|
|
395
394
|
AudioGraph &graph, auto &disposer) mutable {
|
|
396
395
|
applyChannelNegotiations(*negotiations, disposer);
|
|
397
396
|
disposer.dispose(std::move(negotiations));
|
|
398
|
-
graph.pool().remove(graph[hTo
|
|
397
|
+
graph.pool().remove(graph[*hTo].input_head, graph.indexOf(*hFrom));
|
|
399
398
|
graph.markDirty();
|
|
400
399
|
});
|
|
401
400
|
}
|
|
@@ -406,8 +405,6 @@ auto HostGraph::removeAllEdges(HostVertex *from) -> Result<AGEvent, GraphError>
|
|
|
406
405
|
return Err(GraphError::NODE_NOT_FOUND);
|
|
407
406
|
}
|
|
408
407
|
|
|
409
|
-
auto pairs = std::vector<std::pair<std::uint32_t, std::uint32_t>>();
|
|
410
|
-
pairs.reserve(from->outputs.size());
|
|
411
408
|
auto formerOutputs = std::vector<HostVertex *>();
|
|
412
409
|
formerOutputs.reserve(from->outputs.size());
|
|
413
410
|
|
|
@@ -417,7 +414,6 @@ auto HostGraph::removeAllEdges(HostVertex *from) -> Result<AGEvent, GraphError>
|
|
|
417
414
|
to->inputs.erase(itIn);
|
|
418
415
|
}
|
|
419
416
|
edgeCount_--;
|
|
420
|
-
pairs.emplace_back(from->handle->index, to->handle->index);
|
|
421
417
|
formerOutputs.push_back(to);
|
|
422
418
|
}
|
|
423
419
|
from->outputs.clear();
|
|
@@ -432,15 +428,23 @@ auto HostGraph::removeAllEdges(HostVertex *from) -> Result<AGEvent, GraphError>
|
|
|
432
428
|
// that lost this input re-derives its layout.
|
|
433
429
|
auto negotiations = collectNegotiations(++channelLayoutTerm_, formerOutputs);
|
|
434
430
|
|
|
435
|
-
|
|
436
|
-
|
|
431
|
+
auto formerOutputHandles = std::vector<std::shared_ptr<NodeHandle>>();
|
|
432
|
+
formerOutputHandles.reserve(from->outputs.size());
|
|
433
|
+
std::ranges::for_each(formerOutputs, [&formerOutputHandles](const auto *to) {
|
|
434
|
+
formerOutputHandles.push_back(to->handle);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
return Ok([hFrom = from->handle,
|
|
438
|
+
formerOutputHandles = std::move(formerOutputHandles),
|
|
439
|
+
negotiations = std::move(negotiations)](AudioGraph &graph, auto &disposer) mutable {
|
|
437
440
|
applyChannelNegotiations(*negotiations, disposer);
|
|
438
441
|
disposer.dispose(std::move(negotiations));
|
|
439
|
-
for (const auto &
|
|
440
|
-
graph.pool().remove(graph[
|
|
442
|
+
for (const auto &hTo : formerOutputHandles) {
|
|
443
|
+
graph.pool().remove(graph[*hTo].input_head, graph.indexOf(*hFrom));
|
|
441
444
|
}
|
|
442
445
|
graph.markDirty();
|
|
443
|
-
|
|
446
|
+
// Off the audio thread, so these handle releases never run here.
|
|
447
|
+
disposer.dispose(std::move(formerOutputHandles));
|
|
444
448
|
});
|
|
445
449
|
}
|
|
446
450
|
|
|
@@ -510,7 +514,7 @@ std::optional<HostGraph::AGEvent> HostGraph::linkNodes(HostVertex *from, HostVer
|
|
|
510
514
|
linkCount_++;
|
|
511
515
|
|
|
512
516
|
return AGEvent([hFrom = from->handle, hTo = to->handle](AudioGraph &graph, auto &) mutable {
|
|
513
|
-
graph.pool().push(graph[hFrom
|
|
517
|
+
graph.pool().push(graph[*hFrom].link_head, graph.indexOf(*hTo));
|
|
514
518
|
graph.markDirty();
|
|
515
519
|
});
|
|
516
520
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
namespace audioapi::utils::graph {
|
|
6
6
|
|
|
7
|
-
NodeHandle::NodeHandle(std::
|
|
8
|
-
: audioNode(std::move(audioNode)), index(index) {}
|
|
7
|
+
NodeHandle::NodeHandle(std::unique_ptr<GraphObject> audioNode) : audioNode(std::move(audioNode)) {}
|
|
9
8
|
|
|
10
9
|
} // namespace audioapi::utils::graph
|
|
@@ -10,8 +10,8 @@ namespace audioapi::utils::graph {
|
|
|
10
10
|
/// @brief Shared handle bridging HostGraph and AudioGraph.
|
|
11
11
|
///
|
|
12
12
|
/// A single heap allocation stores both the payload node and the mutable
|
|
13
|
-
/// index into AudioGraph's flat vector. AudioGraph
|
|
14
|
-
/// compaction and topological sort.
|
|
13
|
+
/// index into AudioGraph's flat vector. AudioGraph assigns the index in
|
|
14
|
+
/// addNode() and updates it during compaction and topological sort.
|
|
15
15
|
///
|
|
16
16
|
/// Ownership model (shared_ptr):
|
|
17
17
|
/// - Created on the main thread via std::make_shared.
|
|
@@ -21,11 +21,18 @@ namespace audioapi::utils::graph {
|
|
|
21
21
|
/// - When AudioGraph compacts out an orphaned node it releases its shared_ptr
|
|
22
22
|
/// (refcount 2 → 1). HostGraph detects use_count() == 1 and destroys the
|
|
23
23
|
/// ghost + payload on the main thread.
|
|
24
|
+
///
|
|
25
|
+
/// The position is private to AudioGraph: only the audio thread may read it,
|
|
26
|
+
/// through `AudioGraph::operator[](const NodeHandle &)` or `AudioGraph::indexOf`,
|
|
27
|
+
/// because every toposort and compaction rewrites it.
|
|
24
28
|
struct NodeHandle {
|
|
25
29
|
std::unique_ptr<GraphObject> audioNode; // payload graph object (may be null in tests)
|
|
26
|
-
std::uint32_t index; // current position in AudioGraph::nodes
|
|
27
30
|
|
|
28
|
-
NodeHandle(std::
|
|
31
|
+
explicit NodeHandle(std::unique_ptr<GraphObject> audioNode);
|
|
32
|
+
|
|
33
|
+
private:
|
|
34
|
+
friend class AudioGraph;
|
|
35
|
+
std::uint32_t index_ = 0; // current position in AudioGraph::nodes
|
|
29
36
|
};
|
|
30
37
|
|
|
31
38
|
} // namespace audioapi::utils::graph
|
|
@@ -23,7 +23,7 @@ It has very limited task that needs to be performed very fast, so it is designed
|
|
|
23
23
|
`InputPool.hpp` contains the `InputPool` class which is responsible for managing the pool of edges. It is designed to be as efficient as possible, so it uses a free list to manage the pool of edges. It also provides API for allocating and deallocating edges. It is designed to be used on the audio thread, so it is not thread safe and should be used within the `AudioGraph` class. Or its events.
|
|
24
24
|
|
|
25
25
|
## NodeHandle
|
|
26
|
-
`NodeHandle.
|
|
26
|
+
`NodeHandle.h` contains the `NodeHandle` class which is responsible for managing node index in AudioGraph. Indices are 32 bit to minimize memory usage and maximize cache efficiency. So it is only to be used in `AGEvent` closures to reference nodes in `AudioGraph` only having a handle to it.
|
|
27
27
|
|
|
28
28
|
## Disposer
|
|
29
29
|
`Disposer.hpp` contains the `Disposer` class which is responsible for managing the disposal of nodes and edges. It is designed to be used on single thread. It has a worker thread that we can offload destruction onto through SPSC queue. It is more robust then previous implementation allowing wide range of payloads to be disposed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-audio-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0-nightly-1df11c0-20261105",
|
|
4
4
|
"description": "react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification",
|
|
5
5
|
"bin": {
|
|
6
6
|
"setup-rn-audio-api-web": "./scripts/setup-rn-audio-api-web.js"
|