Skip to content

Commit 08a5ee2

Browse files
refactor: added separate input and output buffers getters to satisfy StereoPannerNode constraints
1 parent 98e76f2 commit 08a5ee2

13 files changed

Lines changed: 64 additions & 76 deletions

File tree

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ AudioNode::AudioNode(
2222
RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate());
2323
}
2424

25-
AudioNode::~AudioNode() {
26-
if (isInitialized_.load(std::memory_order_acquire)) {
27-
cleanup();
28-
}
29-
}
30-
3125
bool AudioNode::canBeDestructed() const {
3226
return true;
3327
}

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
#include <cassert>
1212
#include <cstddef>
1313
#include <memory>
14-
#include <unordered_set>
1514
#include <utility>
16-
#include <vector>
1715

1816
namespace audioapi {
1917

@@ -24,18 +22,17 @@ class AudioNode : public utils::graph::GraphObject, public std::enable_shared_fr
2422
explicit AudioNode(
2523
const std::shared_ptr<BaseAudioContext> &context,
2624
const AudioNodeOptions &options = AudioNodeOptions());
27-
virtual ~AudioNode();
2825

2926
size_t getChannelCount() const;
3027

3128
template <std::ranges::input_range R>
3229
requires std::same_as<std::ranges::range_reference_t<R>, const GraphObject &>
3330
void process(R &&inputs, int numFrames) {
34-
audioBuffer_->zero();
31+
getInputBuffer()->zero();
3532

3633
for (const auto &input : inputs) {
3734
if (const AudioNode *audioNode = input.asAudioNode()) {
38-
audioBuffer_->sum(*audioNode->audioBuffer_, channelInterpretation_);
35+
getInputBuffer()->sum(*audioNode->getOutputBuffer(), channelInterpretation_);
3936
}
4037
}
4138

@@ -54,7 +51,21 @@ class AudioNode : public utils::graph::GraphObject, public std::enable_shared_fr
5451
return getContextSampleRate() / 2.0f;
5552
}
5653

57-
std::shared_ptr<DSPAudioBuffer> getAudioBuffer() const {
54+
/// @brief Returns the input buffer for this node. By default, this is the same as the output buffer.
55+
/// @note Audio Thread only.
56+
/// @note For StereoPannerNode and PannerNode due to channel limitations -
57+
/// https://webaudio.github.io/web-audio-api/#StereoPanner-channel-limitations
58+
/// the input buffer is negotiate with inputs, but output buffer is always stereo.
59+
std::shared_ptr<DSPAudioBuffer> getInputBuffer() const {
60+
return audioBuffer_;
61+
}
62+
63+
/// @brief Returns the output buffer for this node. By default, this is the same as the input buffer.
64+
/// @note Audio Thread only.
65+
/// @note For StereoPannerNode and PannerNode due to channel limitations -
66+
/// https://webaudio.github.io/web-audio-api/#StereoPanner-channel-limitations
67+
/// the input buffer is negotiate with inputs, but output buffer is always stereo.
68+
virtual std::shared_ptr<DSPAudioBuffer> getOutputBuffer() const {
5869
return audioBuffer_;
5970
}
6071

@@ -81,8 +92,7 @@ class AudioNode : public utils::graph::GraphObject, public std::enable_shared_fr
8192
}
8293

8394
protected:
84-
friend class AudioDestinationNode;
85-
friend class ConvolverNode;
95+
// friend class ConvolverNode;
8696
friend class DelayNodeHostObject;
8797

8898
std::weak_ptr<BaseAudioContext> context_;
@@ -97,14 +107,11 @@ class AudioNode : public utils::graph::GraphObject, public std::enable_shared_fr
97107

98108
std::atomic<bool> isInitialized_ = false;
99109

100-
std::size_t lastRenderedFrame_{SIZE_MAX};
101-
102110
virtual void disable() {
103111
cleanup();
104112
};
105113

106114
virtual void processNode(int) = 0;
107-
108115
void cleanup();
109116
};
110117

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void BaseAudioContext::processGraph(DSPAudioBuffer *buffer, int numFrames) {
123123
if (audioNode != nullptr) {
124124
audioNode->process(inputs, numFrames);
125125
if (audioNode == destination_.get()) {
126-
buffer->copy(*audioNode->getAudioBuffer(), 0, 0, numFrames);
126+
buffer->copy(*audioNode->getOutputBuffer(), 0, 0, numFrames);
127127
}
128128
}
129129
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ void ConvolverNode::processNode(int framesToProcess) {
105105
return;
106106
}
107107
}
108+
108109
if (internalBufferIndex_ < framesToProcess) {
109110
performConvolution(audioBuffer_); // reads from audioBuffer_, result goes to intermediateBuffer_
110111
audioBuffer_->zero();
@@ -113,6 +114,7 @@ void ConvolverNode::processNode(int framesToProcess) {
113114
internalBuffer_->copy(*audioBuffer_, 0, internalBufferIndex_, RENDER_QUANTUM_SIZE);
114115
internalBufferIndex_ += RENDER_QUANTUM_SIZE;
115116
}
117+
116118
audioBuffer_->zero();
117119
audioBuffer_->copy(*internalBuffer_, 0, 0, framesToProcess);
118120
int remainingFrames = internalBufferIndex_ - framesToProcess;

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,23 @@ StereoPannerNode::StereoPannerNode(
1414
const std::shared_ptr<BaseAudioContext> &context,
1515
const StereoPannerOptions &options)
1616
: AudioNode(context, options),
17-
panParam_(std::make_shared<AudioParam>(options.pan, -1.0f, 1.0f, context)) {
17+
panParam_(std::make_shared<AudioParam>(options.pan, -1.0f, 1.0f, context)),
18+
outputBuffer_(
19+
std::make_shared<DSPAudioBuffer>(
20+
RENDER_QUANTUM_SIZE,
21+
channelCount_,
22+
context->getSampleRate())) {
1823
isInitialized_.store(true, std::memory_order_release);
1924
}
2025

2126
std::shared_ptr<AudioParam> StereoPannerNode::getPanParam() const {
2227
return panParam_;
2328
}
2429

30+
std::shared_ptr<DSPAudioBuffer> StereoPannerNode::getOutputBuffer() const {
31+
return outputBuffer_;
32+
}
33+
2534
void StereoPannerNode::processNode(int framesToProcess) {
2635
std::shared_ptr<BaseAudioContext> context = context_.lock();
2736
if (context == nullptr)
@@ -31,8 +40,8 @@ void StereoPannerNode::processNode(int framesToProcess) {
3140

3241
auto panParamValues = panParam_->processARateParam(framesToProcess, time)->getChannel(0)->span();
3342

34-
auto outputLeft = audioBuffer_->getChannelByType(AudioBuffer::ChannelLeft)->span();
35-
auto outputRight = audioBuffer_->getChannelByType(AudioBuffer::ChannelRight)->span();
43+
auto outputLeft = outputBuffer_->getChannelByType(AudioBuffer::ChannelLeft)->span();
44+
auto outputRight = outputBuffer_->getChannelByType(AudioBuffer::ChannelRight)->span();
3645

3746
// Input is mono
3847
if (audioBuffer_->getNumberOfChannels() == 1) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ class StereoPannerNode : public AudioNode {
1919

2020
[[nodiscard]] std::shared_ptr<AudioParam> getPanParam() const;
2121

22+
std::shared_ptr<DSPAudioBuffer> getOutputBuffer() const override;
23+
2224
protected:
2325
void processNode(int framesToProcess) override;
2426

2527
private:
2628
const std::shared_ptr<AudioParam> panParam_;
29+
const std::shared_ptr<DSPAudioBuffer> outputBuffer_;
2730
};
2831

2932
} // namespace audioapi

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,6 @@ void AudioBufferSourceNode::start(double when, double offset, double duration) {
9595
vReadIndex_ = static_cast<double>(buffer_->getSampleRate() * offset);
9696
}
9797

98-
void AudioBufferSourceNode::disable() {
99-
AudioScheduledSourceNode::disable();
100-
}
101-
10298
void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) {
10399
onLoopEndedCallbackId_ = callbackId;
104100
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode {
3939
/// @note Audio Thread only
4040
void start(double when, double offset, double duration = -1);
4141

42-
/// @note Audio Thread only
43-
void disable() override;
44-
4542
/// @note Audio Thread only
4643
void setOnLoopEndedCallbackId(uint64_t callbackId);
4744

packages/react-native-audio-api/common/cpp/test/src/core/effects/DelayTest.cpp

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,8 @@ class TestableDelayNode : public DelayNode {
3333
getDelayTimeParam()->setValue(value);
3434
}
3535

36-
void setInput(const std::shared_ptr<DSPAudioBuffer> &input) {
37-
size_t copyChannels = std::min(
38-
static_cast<size_t>(input->getNumberOfChannels()),
39-
static_cast<size_t>(audioBuffer_->getNumberOfChannels()));
40-
for (size_t ch = 0; ch < copyChannels; ch++) {
41-
audioBuffer_->getChannel(ch)->copy(*input->getChannel(ch), 0, 0, input->getSize());
42-
}
36+
void setInputBuffer(const std::shared_ptr<DSPAudioBuffer> &input) {
37+
audioBuffer_ = input;
4338
}
4439

4540
void processNode(int framesToProcess) override {
@@ -65,9 +60,9 @@ TEST_F(DelayTest, DelayWithZeroDelayOutputsInputSignal) {
6560
(*buffer->getChannel(0))[i] = i + 1;
6661
}
6762

68-
delayNode.setInput(buffer);
63+
delayNode.setInputBuffer(buffer);
6964
delayNode.processNode(FRAMES_TO_PROCESS);
70-
auto resultBuffer = delayNode.getAudioBuffer();
65+
auto resultBuffer = delayNode.getOutputBuffer();
7166
for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) {
7267
EXPECT_FLOAT_EQ((*resultBuffer->getChannel(0))[i], static_cast<float>(i + 1));
7368
}
@@ -86,9 +81,9 @@ TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) {
8681
(*buffer->getChannel(0))[i] = i + 1;
8782
}
8883

89-
delayNode.setInput(buffer);
84+
delayNode.setInputBuffer(buffer);
9085
delayNode.processNode(FRAMES_TO_PROCESS);
91-
auto resultBuffer = delayNode.getAudioBuffer();
86+
auto resultBuffer = delayNode.getOutputBuffer();
9287
for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) {
9388
if (i < FRAMES_TO_PROCESS / 2) { // First 64 samples should be zero due to delay
9489
EXPECT_FLOAT_EQ((*resultBuffer->getChannel(0))[i], 0.0f);
@@ -114,12 +109,12 @@ TEST_F(DelayTest, DelayHandlesTailCorrectly) {
114109
(*buffer->getChannel(0))[i] = i + 1;
115110
}
116111

117-
delayNode.setInput(buffer);
112+
delayNode.setInputBuffer(buffer);
118113
delayNode.processNode(FRAMES_TO_PROCESS);
119114
// Second call uses the result of the first call as input (same as old behavior
120115
// where the same buffer object was passed to both calls)
121116
delayNode.processNode(FRAMES_TO_PROCESS);
122-
auto resultBuffer = delayNode.getAudioBuffer();
117+
auto resultBuffer = delayNode.getOutputBuffer();
123118
for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) {
124119
if (i < FRAMES_TO_PROCESS / 2) { // First 64 samples should be 2nd part of buffer
125120
EXPECT_FLOAT_EQ(

packages/react-native-audio-api/common/cpp/test/src/core/effects/GainTest.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,8 @@ class TestableGainNode : public GainNode {
3333
getGainParam()->setValue(value);
3434
}
3535

36-
void setInput(const std::shared_ptr<DSPAudioBuffer> &input) {
37-
size_t copyChannels = std::min(
38-
static_cast<size_t>(input->getNumberOfChannels()),
39-
static_cast<size_t>(audioBuffer_->getNumberOfChannels()));
40-
for (size_t ch = 0; ch < copyChannels; ch++) {
41-
audioBuffer_->getChannel(ch)->copy(*input->getChannel(ch), 0, 0, input->getSize());
42-
}
36+
void setInputBuffer(const std::shared_ptr<DSPAudioBuffer> &input) {
37+
audioBuffer_ = input;
4338
}
4439

4540
void processNode(int framesToProcess) override {
@@ -63,9 +58,9 @@ TEST_F(GainTest, GainModulatesVolumeCorrectly) {
6358
(*buffer->getChannel(0))[i] = i + 1;
6459
}
6560

66-
gainNode.setInput(buffer);
61+
gainNode.setInputBuffer(buffer);
6762
gainNode.processNode(FRAMES_TO_PROCESS);
68-
auto resultBuffer = gainNode.getAudioBuffer();
63+
auto resultBuffer = gainNode.getOutputBuffer();
6964
for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) {
7065
EXPECT_FLOAT_EQ((*resultBuffer->getChannel(0))[i], (i + 1) * GAIN_VALUE);
7166
}
@@ -83,9 +78,9 @@ TEST_F(GainTest, GainModulatesVolumeCorrectlyMultiChannel) {
8378
(*buffer->getChannel(1))[i] = -i - 1;
8479
}
8580

86-
gainNode.setInput(buffer);
81+
gainNode.setInputBuffer(buffer);
8782
gainNode.processNode(FRAMES_TO_PROCESS);
88-
auto resultBuffer = gainNode.getAudioBuffer();
83+
auto resultBuffer = gainNode.getOutputBuffer();
8984
for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) {
9085
EXPECT_FLOAT_EQ((*resultBuffer->getChannel(0))[i], (i + 1) * GAIN_VALUE);
9186
EXPECT_FLOAT_EQ((*resultBuffer->getChannel(1))[i], (-i - 1) * GAIN_VALUE);

0 commit comments

Comments
 (0)