Skip to content

Commit 8d56ad1

Browse files
authored
Merge pull request #432 from software-mansion/feat/audio-param-connection-from-node
feat: added logic for connecting node to param
2 parents 4dc0f2a + dcc8de5 commit 8d56ad1

22 files changed

+308
-94
lines changed

packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#pragma once
22

3-
#include <audioapi/jsi/JsiHostObject.h>
3+
#include <audioapi/HostObjects/AudioParamHostObject.h>
44
#include <audioapi/core/AudioNode.h>
5+
#include <audioapi/jsi/JsiHostObject.h>
56

67
#include <jsi/jsi.h>
78
#include <memory>
@@ -47,21 +48,33 @@ class AudioNodeHostObject : public JsiHostObject {
4748
}
4849

4950
JSI_HOST_FUNCTION(connect) {
50-
auto node =
51-
args[0].getObject(runtime).getHostObject<AudioNodeHostObject>(runtime);
52-
node_->connect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
51+
auto obj = args[0].getObject(runtime);
52+
if (obj.isHostObject<AudioNodeHostObject>(runtime)) {
53+
auto node = obj.getHostObject<AudioNodeHostObject>(runtime);
54+
node_->connect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
55+
}
56+
if (obj.isHostObject<AudioParamHostObject>(runtime)) {
57+
auto param = obj.getHostObject<AudioParamHostObject>(runtime);
58+
node_->connect(std::shared_ptr<AudioParamHostObject>(param)->param_);
59+
}
5360
return jsi::Value::undefined();
5461
}
5562

5663
JSI_HOST_FUNCTION(disconnect) {
57-
if(args[0].isUndefined()) {
58-
node_->disconnect();
59-
return jsi::Value::undefined();
60-
}
64+
if (args[0].isUndefined()) {
65+
node_->disconnect();
66+
return jsi::Value::undefined();
67+
}
68+
auto obj = args[0].getObject(runtime);
69+
if (obj.isHostObject<AudioNodeHostObject>(runtime)) {
70+
auto node = obj.getHostObject<AudioNodeHostObject>(runtime);
71+
node_->disconnect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
72+
}
6173

62-
auto node =
63-
args[0].getObject(runtime).getHostObject<AudioNodeHostObject>(runtime);
64-
node_->disconnect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
74+
if (obj.isHostObject<AudioParamHostObject>(runtime)) {
75+
auto param = obj.getHostObject<AudioParamHostObject>(runtime);
76+
node_->disconnect(std::shared_ptr<AudioParamHostObject>(param)->param_);
77+
}
6578
return jsi::Value::undefined();
6679
}
6780

packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class AudioParamHostObject : public JsiHostObject {
3232

3333
addSetters(JSI_EXPORT_PROPERTY_SETTER(AudioParamHostObject, value));
3434
}
35+
friend class AudioNodeHostObject;
3536

3637
JSI_PROPERTY_GETTER(value) {
3738
return {param_->getValue()};

packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <audioapi/core/AudioNode.h>
2+
#include <audioapi/core/AudioParam.h>
23
#include <audioapi/core/BaseAudioContext.h>
34
#include <audioapi/core/utils/AudioNodeManager.h>
45
#include <audioapi/utils/AudioArray.h>
@@ -38,10 +39,15 @@ std::string AudioNode::getChannelInterpretation() const {
3839
}
3940

4041
void AudioNode::connect(const std::shared_ptr<AudioNode> &node) {
41-
context_->getNodeManager()->addPendingConnection(
42+
context_->getNodeManager()->addPendingNodeConnection(
4243
shared_from_this(), node, AudioNodeManager::ConnectionType::CONNECT);
4344
}
4445

46+
void AudioNode::connect(const std::shared_ptr<AudioParam> &param) {
47+
context_->getNodeManager()->addPendingParamConnection(
48+
shared_from_this(), param, AudioNodeManager::ConnectionType::CONNECT);
49+
}
50+
4551
void AudioNode::disconnect() {
4652
for (auto it = outputNodes_.begin(), end = outputNodes_.end(); it != end;
4753
++it) {
@@ -50,10 +56,15 @@ void AudioNode::disconnect() {
5056
}
5157

5258
void AudioNode::disconnect(const std::shared_ptr<AudioNode> &node) {
53-
context_->getNodeManager()->addPendingConnection(
59+
context_->getNodeManager()->addPendingNodeConnection(
5460
shared_from_this(), node, AudioNodeManager::ConnectionType::DISCONNECT);
5561
}
5662

63+
void AudioNode::disconnect(const std::shared_ptr<AudioParam> &param) {
64+
context_->getNodeManager()->addPendingParamConnection(
65+
shared_from_this(), param, AudioNodeManager::ConnectionType::DISCONNECT);
66+
}
67+
5768
bool AudioNode::isEnabled() const {
5869
return isEnabled_;
5970
}
@@ -221,6 +232,15 @@ void AudioNode::connectNode(const std::shared_ptr<AudioNode> &node) {
221232
}
222233
}
223234

235+
void AudioNode::connectParam(const std::shared_ptr<AudioParam> &param) {
236+
auto position = outputParams_.find(param);
237+
238+
if (position == outputParams_.end()) {
239+
outputParams_.insert(param);
240+
param->addInputNode(this);
241+
}
242+
}
243+
224244
void AudioNode::disconnectNode(const std::shared_ptr<AudioNode> &node) {
225245
auto position = outputNodes_.find(node);
226246

@@ -230,6 +250,15 @@ void AudioNode::disconnectNode(const std::shared_ptr<AudioNode> &node) {
230250
}
231251
}
232252

253+
void AudioNode::disconnectParam(const std::shared_ptr<AudioParam> &param) {
254+
auto position = outputParams_.find(param);
255+
256+
if (position != outputParams_.end()) {
257+
param->removeInputNode(this);
258+
outputParams_.erase(param);
259+
}
260+
}
261+
233262
void AudioNode::onInputEnabled() {
234263
numberOfEnabledInputNodes_ += 1;
235264

packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace audioapi {
1515

1616
class AudioBus;
1717
class BaseAudioContext;
18+
class AudioParam;
1819

1920
class AudioNode : public std::enable_shared_from_this<AudioNode> {
2021
public:
@@ -27,8 +28,11 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
2728
std::string getChannelCountMode() const;
2829
std::string getChannelInterpretation() const;
2930
void connect(const std::shared_ptr<AudioNode> &node);
31+
void connect(const std::shared_ptr<AudioParam> &param);
3032
void disconnect();
3133
void disconnect(const std::shared_ptr<AudioNode> &node);
34+
void disconnect(const std::shared_ptr<AudioParam> &param);
35+
virtual std::shared_ptr<AudioBus> processAudio(const std::shared_ptr<AudioBus> &outputBus, int framesToProcess, bool checkIsAlreadyProcessed);
3236

3337
bool isEnabled() const;
3438
void enable();
@@ -50,6 +54,7 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
5054

5155
std::unordered_set<AudioNode *> inputNodes_ = {};
5256
std::unordered_set<std::shared_ptr<AudioNode>> outputNodes_ = {};
57+
std::unordered_set<std::shared_ptr<AudioParam>> outputParams_ = {};
5358

5459
int numberOfEnabledInputNodes_ = 0;
5560
bool isInitialized_ = false;
@@ -63,7 +68,6 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
6368
static std::string toString(ChannelCountMode mode);
6469
static std::string toString(ChannelInterpretation interpretation);
6570

66-
virtual std::shared_ptr<AudioBus> processAudio(const std::shared_ptr<AudioBus> &outputBus, int framesToProcess, bool checkIsAlreadyProcessed);
6771
virtual void processNode(const std::shared_ptr<AudioBus>&, int) = 0;
6872

6973
bool isAlreadyProcessed();
@@ -73,6 +77,8 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
7377

7478
void connectNode(const std::shared_ptr<AudioNode> &node);
7579
void disconnectNode(const std::shared_ptr<AudioNode> &node);
80+
void connectParam(const std::shared_ptr<AudioParam> &param);
81+
void disconnectParam(const std::shared_ptr<AudioParam> &param);
7682

7783
void onInputEnabled();
7884
void onInputDisabled();

packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
#include <audioapi/core/AudioParam.h>
22
#include <audioapi/core/BaseAudioContext.h>
33
#include <audioapi/dsp/AudioUtils.h>
4+
#include <audioapi/dsp/VectorMath.h>
5+
#include <audioapi/utils/AudioArray.h>
6+
#include <iostream>
47

58
namespace audioapi {
69

7-
AudioParam::AudioParam(float defaultValue, float minValue, float maxValue)
10+
AudioParam::AudioParam(
11+
float defaultValue,
12+
float minValue,
13+
float maxValue,
14+
BaseAudioContext *context)
815
: value_(defaultValue),
916
defaultValue_(defaultValue),
1017
minValue_(minValue),
11-
maxValue_(maxValue) {
18+
maxValue_(maxValue),
19+
context_(context),
20+
audioBus_(
21+
std::make_shared<AudioBus>(
22+
RENDER_QUANTUM_SIZE,
23+
1,
24+
context->getSampleRate())) {
1225
startTime_ = 0;
1326
endTime_ = 0;
1427
startValue_ = value_;
@@ -256,6 +269,44 @@ void AudioParam::cancelAndHoldAtTime(double cancelTime) {
256269
}
257270
}
258271

272+
void AudioParam::addInputNode(AudioNode *node) {
273+
auto position = inputNodes_.find(node);
274+
if (position == inputNodes_.end()) {
275+
inputNodes_.insert(node);
276+
}
277+
}
278+
279+
void AudioParam::removeInputNode(AudioNode *node) {
280+
auto position = inputNodes_.find(node);
281+
282+
if (position != inputNodes_.end()) {
283+
inputNodes_.erase(position);
284+
}
285+
}
286+
287+
std::shared_ptr<AudioBus> AudioParam::processARateParam(
288+
int framesToProcess,
289+
double time) {
290+
auto processingBus = audioBus_;
291+
processingBus->zero();
292+
if (!inputNodes_.empty()) {
293+
processInputs(processingBus, framesToProcess, true);
294+
mixInputsBuses(processingBus);
295+
}
296+
for (size_t i = 0; i < framesToProcess; i++) {
297+
auto sample = getValueAtTime(time + i / context_->getSampleRate());
298+
processingBus->getChannel(0)->getData()[i] += sample;
299+
}
300+
// processingBus is a mono bus
301+
return processingBus;
302+
}
303+
304+
float AudioParam::processKRateParam(int framesToProcess, double time) {
305+
auto processingBus = processARateParam(framesToProcess, time);
306+
// processingBus is a mono bus
307+
return processingBus->getChannel(0)->getData()[0];
308+
}
309+
259310
double AudioParam::getQueueEndTime() {
260311
if (eventsQueue_.empty()) {
261312
return endTime_;
@@ -292,4 +343,35 @@ void AudioParam::updateQueue(ParamChangeEvent &event) {
292343
eventsQueue_.push_back(event);
293344
}
294345

346+
void AudioParam::processInputs(
347+
const std::shared_ptr<AudioBus> &outputBus,
348+
int framesToProcess,
349+
bool checkIsAlreadyProcessed) {
350+
for (auto it = inputNodes_.begin(), end = inputNodes_.end(); it != end;
351+
++it) {
352+
auto inputNode = *it;
353+
assert(inputNode != nullptr);
354+
355+
if (!inputNode->isEnabled()) {
356+
continue;
357+
}
358+
359+
auto inputBus = inputNode->processAudio(
360+
outputBus, framesToProcess, checkIsAlreadyProcessed);
361+
inputBuses_.push_back(inputBus);
362+
}
363+
}
364+
365+
void AudioParam::mixInputsBuses(
366+
const std::shared_ptr<AudioBus> &processingBus) {
367+
assert(processingBus != nullptr);
368+
369+
for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end;
370+
++it) {
371+
processingBus->sum(it->get(), ChannelInterpretation::SPEAKERS);
372+
}
373+
374+
inputBuses_.clear();
375+
}
376+
295377
} // namespace audioapi

packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
#include <audioapi/core/utils/ParamChangeEvent.h>
44
#include <audioapi/core/types/ParamChangeEventType.h>
5+
#include <audioapi/utils/AudioBus.h>
6+
#include <audioapi/core/AudioNode.h>
57

68
#include <deque>
79
#include <memory>
810
#include <vector>
11+
#include <unordered_set>
912
#include <cstddef>
1013

1114
namespace audioapi {
1215

1316
class AudioParam {
1417
public:
15-
explicit AudioParam(float defaultValue, float minValue, float maxValue);
18+
explicit AudioParam(float defaultValue, float minValue, float maxValue, BaseAudioContext *context);
1619

1720
[[nodiscard]] float getValue() const;
1821
float getValueAtTime(double time);
@@ -33,23 +36,33 @@ class AudioParam {
3336
double duration);
3437
void cancelScheduledValues(double cancelTime);
3538
void cancelAndHoldAtTime(double cancelTime);
39+
void addInputNode(AudioNode* node);
40+
void removeInputNode(AudioNode* node);
41+
std::shared_ptr<AudioBus> processARateParam(int framesToProcess, double time);
42+
float processKRateParam(int framesToProcess, double time);
3643

3744
private:
3845
float value_;
3946
float defaultValue_;
4047
float minValue_;
4148
float maxValue_;
49+
BaseAudioContext *context_;
4250
std::deque<ParamChangeEvent> eventsQueue_;
51+
std::unordered_set<AudioNode *> inputNodes_;
52+
std::shared_ptr<AudioBus> audioBus_;
4353

4454
double startTime_;
4555
double endTime_;
4656
float startValue_;
4757
float endValue_;
4858
std::function<float(double, double, float, float, double)> calculateValue_;
59+
std::vector<std::shared_ptr<AudioBus>> inputBuses_ = {};
4960

5061
double getQueueEndTime();
5162
float getQueueEndValue();
5263
void updateQueue(ParamChangeEvent &event);
64+
void processInputs(const std::shared_ptr<AudioBus>& outputBus, int framesToProcess, bool checkIsAlreadyProcessed);
65+
void mixInputsBuses(const std::shared_ptr<AudioBus>& processingBus);
5366
};
5467

5568
} // namespace audioapi

0 commit comments

Comments
 (0)