From 43fed076ad29e8aa8a14b5765e1d133aa9ff7cbb Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Feb 2026 16:21:51 +0100 Subject: [PATCH 01/73] refactor: audio array --- .../common/cpp/audioapi/utils/AudioArray.cpp | 229 +++++++++++++----- .../common/cpp/audioapi/utils/AudioArray.h | 76 ++++-- 2 files changed, 215 insertions(+), 90 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 0e321ed80..bc29aaf59 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -1,121 +1,218 @@ #include #include + #include +#include +#include -namespace audioapi { +#if defined(__ARM_NEON) +#include +#endif -AudioArray::AudioArray(size_t size) : data_(nullptr), size_(size) { - resize(size); -} - -AudioArray::AudioArray(const AudioArray &other) : data_(nullptr), size_(0) { - resize(other.size_); +namespace audioapi { - copy(&other); +AudioArray::AudioArray(size_t size): size_(size) { + if (size_ > 0) { + data_ = std::make_unique(size_); + zero(); + } } AudioArray::AudioArray(const float *data, size_t size) : size_(size) { - data_ = new float[size_]; - memcpy(data_, data, size_ * sizeof(float)); + if (size_ > 0) { + data_ = std::make_unique(size_); + std::memcpy(data_.get(), data, size_ * sizeof(float)); + } } -AudioArray::~AudioArray() { - if (data_) { - delete[] data_; - data_ = nullptr; - } +AudioArray::AudioArray(const AudioArray &other) : size_(other.size_) { + if (size_ > 0 && other.data_) { + data_ = std::make_unique(size_); + std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); + } } -size_t AudioArray::getSize() const { - return size_; +AudioArray::AudioArray(audioapi::AudioArray &&other) noexcept : data_(std::move(other.data_)), size_(other.size_) { + other.size_ = 0; } -float *AudioArray::getData() const { - return data_; -} +AudioArray &AudioArray::operator=(const audioapi::AudioArray &other) { + if (this != &other) { + if (size_ != other.size_) { + size_ = other.size_; + data_ = (size_ > 0) ? std::make_unique(size_) : nullptr; + } -float &AudioArray::operator[](size_t index) { - return data_[index]; -} + if (size_ > 0 && data_) { + std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); + } + } -const float &AudioArray::operator[](size_t index) const { - return data_[index]; + return *this; } -void AudioArray::normalize() { - float maxAbsValue = getMaxAbsValue(); - - if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { - return; - } +AudioArray &AudioArray::operator=(audioapi::AudioArray &&other) noexcept { + if (this != &other) { + data_ = std::move(other.data_); + size_ = other.size_; + other.size_ = 0; + } - dsp::multiplyByScalar(data_, 1.0f / maxAbsValue, data_, size_); + return *this; } void AudioArray::resize(size_t size) { - if (size == size_) { - if (!data_) { - data_ = new float[size]; - } - - zero(0, size); + if (size == size_ && data_ != nullptr) { + zero(); return; } - delete[] data_; size_ = size; - data_ = new float[size_]; + data_ = (size_ > 0) ? std::make_unique(size_) : nullptr; + if (data_ != nullptr) { + zero(); + } +} +void AudioArray::zero() noexcept { zero(0, size_); } -void AudioArray::scale(float value) { - dsp::multiplyByScalar(data_, value, data_, size_); +void AudioArray::zero(size_t start, size_t length) noexcept { + if (data_ == nullptr || length <= 0) { + return; + } + + memset(data_.get() + start, 0, length * sizeof(float)); } -float AudioArray::getMaxAbsValue() const { - return dsp::maximumMagnitude(data_, size_); +void AudioArray::sum(const AudioArray &source, float gain) { + sum(source, 0, 0, size_, gain); } -void AudioArray::zero() { - zero(0, size_); +void AudioArray::sum( + const AudioArray &source, + size_t sourceStart, + size_t destinationStart, + size_t length, + float gain) { + if (length == 0 || data_ == nullptr || source.data_ == nullptr) { + return; + } + + // Using restrict to inform the compiler that the source and destination do not overlap + float* __restrict dest = data_.get() + destinationStart; + const float* __restrict src = source.data_.get() + sourceStart; + + dsp::multiplyByScalarThenAddToOutput(src, gain, dest, length); } -void AudioArray::zero(size_t start, size_t length) { - memset(data_ + start, 0, length * sizeof(float)); +void AudioArray::multiply(const AudioArray &source) { + if (data_ == nullptr || source.data_ == nullptr) { + return; + } + + float* __restrict dest = data_.get(); + const float* __restrict src = source.data_.get(); + + dsp::multiply(src, dest, dest, size_); } -void AudioArray::sum(const AudioArray *source) { - sum(source, 0, 0, size_); +void AudioArray::multiplyByScalar(float value) { + if (data_ == nullptr) { + return; + } + + dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); } -void AudioArray::sum(const AudioArray *source, size_t start, size_t length) { - sum(source, start, start, length); +void AudioArray::copy(const AudioArray &source) { + copy(source, 0, 0, size_); } -void AudioArray::sum( - const AudioArray *source, +void AudioArray::copy( + const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length) { - dsp::add( - data_ + destinationStart, source->getData() + sourceStart, data_ + destinationStart, length); + if (length == 0 || data_ == nullptr || source.data_ == nullptr) { + return; + } + + memcpy(data_.get() + destinationStart, source.data_.get() + sourceStart, length * sizeof(float)); } -void AudioArray::copy(const AudioArray *source) { - copy(source, 0, size_); +void AudioArray::reverse() { + if (data_ == nullptr && size_ > 1) { + return; + } + + std::reverse(begin(), end()); } -void AudioArray::copy(const AudioArray *source, size_t start, size_t length) { - copy(source, start, start, length); +void AudioArray::normalize() { + float maxAbsValue = getMaxAbsValue(); + + if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { + return; + } + + dsp::multiplyByScalar(data_.get(), 1.0f / maxAbsValue, data_.get(), size_); } -void AudioArray::copy( - const AudioArray *source, - size_t sourceStart, - size_t destinationStart, - size_t length) { - memcpy(data_ + destinationStart, source->getData() + sourceStart, length * sizeof(float)); +void AudioArray::scale(float value) { + if (data_ == nullptr) { + return; + } + + dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); +} + +float AudioArray::getMaxAbsValue() const { + if (data_ == nullptr) { + return 0.0f; + } + + return dsp::maximumMagnitude(data_.get(), size_); +} + +float AudioArray::computeConvolution(const audioapi::AudioArray &kernel, size_t startIndex) const { + const auto kernelSize = kernel.size_; + + if (startIndex + kernelSize > size_ || !data_ || !kernel.data_) { + return 0.0f; + } + + const auto stateStart = data_.get() + startIndex; + const auto kernelStart = kernel.data_.get(); + + float sum = 0.0f; + size_t k = 0; + +#ifdef __ARM_NEON + float32x4_t vSum = vdupq_n_f32(0.0f); + + // process 4 samples at a time + for (; k <= kernelSize_ - 4; k += 4) { + float32x4_t vState = vld1q_f32(stateStart + k); + float32x4_t vKernel = vld1q_f32(kernelStart + k); + + // fused multiply-add: vSum += vState * vKernel + vSum = vmlaq_f32(vSum, vState, vKernel); + } + + // horizontal reduction: Sum the 4 lanes of vSum into a single float + sum += vgetq_lane_f32(vSum, 0); + sum += vgetq_lane_f32(vSum, 1); + sum += vgetq_lane_f32(vSum, 2); + sum += vgetq_lane_f32(vSum, 3); +#endif + + for (; k < kernelSize; ++k) { + sum += stateStart[k] * kernelStart[k]; + } + + return sum; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index 9291f22ff..bf6743bad 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -10,40 +10,68 @@ namespace audioapi { class AudioArray { public: explicit AudioArray(size_t size); - AudioArray(const AudioArray &other); - - /// @brief Construct AudioArray from raw float data - /// @param data Pointer to the float data - /// @param size Number of float samples /// @note The data is copied, so it does not take ownership of the pointer AudioArray(const float *data, size_t size); - ~AudioArray(); + ~AudioArray() = default; - [[nodiscard]] size_t getSize() const; - [[nodiscard]] float *getData() const; + AudioArray(const AudioArray &other); + AudioArray(AudioArray &&other) noexcept; + AudioArray &operator=(const AudioArray &other); + AudioArray &operator=(AudioArray &&other) noexcept; - float &operator[](size_t index); - const float &operator[](size_t index) const; + [[nodiscard]] inline size_t getSize() const { + return size_; + } + + inline float &operator[](size_t index) { + return data_[index]; + } + inline const float &operator[](size_t index) const { + return data_[index]; + } + + [[nodiscard]] inline float *begin() noexcept { + return data_.get(); + } + [[nodiscard]] inline float *end() noexcept { + return data_.get() + size_; + } + + [[nodiscard]] inline const float *begin() const noexcept { + return data_.get(); + } + [[nodiscard]] inline const float *end() const noexcept { + return data_.get() + size_; + } - void normalize(); void resize(size_t size); - void scale(float value); - [[nodiscard]] float getMaxAbsValue() const; - void zero(); - void zero(size_t start, size_t length); + void zero() noexcept; + void zero(size_t start, size_t length) noexcept; + + void sum(const AudioArray &source, float gain = 1.0f); + void sum( + const AudioArray &source, + size_t sourceStart, + size_t destinationStart, + size_t length, + float gain = 1.0f); + + void multiply(const AudioArray &source); + void multiplyByScalar(float value); - void sum(const AudioArray *source); - void sum(const AudioArray *source, size_t start, size_t length); - void sum(const AudioArray *source, size_t sourceStart, size_t destinationStart, size_t length); + void copy(const AudioArray &source); + void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); - void copy(const AudioArray *source); - void copy(const AudioArray *source, size_t start, size_t length); - void copy(const AudioArray *source, size_t sourceStart, size_t destinationStart, size_t length); + void reverse(); + void normalize(); + void scale(float value); + [[nodiscard]] float getMaxAbsValue() const; + [[nodiscard]] float computeConvolution(const AudioArray &kernel, size_t startIndex = 0) const; - protected: - float *data_; - size_t size_; + private: + std::unique_ptr data_ = nullptr; + size_t size_ = 0; }; } // namespace audioapi From 6ad3a2b16929802d83ff18b5ac9617241b2bf4d8 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Feb 2026 16:23:32 +0100 Subject: [PATCH 02/73] feat: audio bus --- .../common/cpp/audioapi/utils/AudioBus.cpp | 393 +++++++----------- .../common/cpp/audioapi/utils/AudioBus.h | 60 +-- 2 files changed, 180 insertions(+), 273 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp index 5276b434b..ee14402fc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp @@ -24,66 +24,63 @@ AudioBus::AudioBus(size_t size, int numberOfChannels, float sampleRate) createChannels(); } -AudioBus::AudioBus(const AudioBus &other) { - numberOfChannels_ = other.numberOfChannels_; - sampleRate_ = other.sampleRate_; - size_ = other.size_; - +AudioBus::AudioBus(const AudioBus &other): numberOfChannels_(other.numberOfChannels_), + sampleRate_(other.sampleRate_), + size_(other.size_) { createChannels(); for (int i = 0; i < numberOfChannels_; i += 1) { - channels_[i] = std::make_shared(*other.channels_[i]); + *channels_[i] = *other.channels_[i]; } } -AudioBus::AudioBus(AudioBus &&other) noexcept - : channels_(std::move(other.channels_)), - numberOfChannels_(other.numberOfChannels_), - sampleRate_(other.sampleRate_), - size_(other.size_) { - other.numberOfChannels_ = 0; - other.sampleRate_ = 0.0f; - other.size_ = 0; +AudioBus::AudioBus(audioapi::AudioBus &&other) noexcept : + channels_(std::move(other.channels_)), + numberOfChannels_(other.numberOfChannels_), + sampleRate_(other.sampleRate_), + size_(other.size_) { + other.numberOfChannels_ = 0; + other.sampleRate_ = 0.0f; + other.size_ = 0; } AudioBus &AudioBus::operator=(const AudioBus &other) { - if (this == &other) { - return *this; - } - - numberOfChannels_ = other.numberOfChannels_; - sampleRate_ = other.sampleRate_; - size_ = other.size_; + if (this != &other) { + if (numberOfChannels_ != other.numberOfChannels_ || size_ != other.size_) { + numberOfChannels_ = other.numberOfChannels_; + size_ = other.size_; + createChannels(); + } - createChannels(); + sampleRate_ = other.sampleRate_; - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_[i] = std::make_shared(*other.channels_[i]); + for (int i = 0; i < numberOfChannels_; i += 1) { + *channels_[i] = *other.channels_[i]; + } } return *this; } -AudioBus::~AudioBus() { - channels_.clear(); +AudioBus &AudioBus::operator=(audioapi::AudioBus &&other) noexcept { + if (this != &other) { + channels_ = std::move(other.channels_); + + numberOfChannels_ = other.numberOfChannels_; + sampleRate_ = other.sampleRate_; + size_ = other.size_; + + other.numberOfChannels_ = 0; + other.sampleRate_ = 0.0f; + other.size_ = 0; + } + return *this; } /** * Public interfaces - getters */ -int AudioBus::getNumberOfChannels() const { - return numberOfChannels_; -} - -float AudioBus::getSampleRate() const { - return sampleRate_; -} - -size_t AudioBus::getSize() const { - return size_; -} - AudioArray *AudioBus::getChannel(int index) const { return channels_[index].get(); } @@ -162,14 +159,6 @@ std::shared_ptr AudioBus::getSharedChannel(int index) const { return channels_[index]; } -AudioArray &AudioBus::operator[](size_t index) { - return *channels_[index]; -} - -const AudioArray &AudioBus::operator[](size_t index) const { - return *channels_[index]; -} - /** * Public interfaces - audio processing and setters */ @@ -184,57 +173,21 @@ void AudioBus::zero(size_t start, size_t length) { } } -void AudioBus::normalize() { - float maxAbsValue = this->maxAbsValue(); - - if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { - return; - } - - float scale = 1.0f / maxAbsValue; - this->scale(scale); -} - -void AudioBus::scale(float value) { - for (auto &channel : channels_) { - channel->scale(value); - } -} - -float AudioBus::maxAbsValue() const { - float maxAbsValue = 1.0f; - - for (const auto &channel : channels_) { - float channelMaxAbsValue = channel->getMaxAbsValue(); - maxAbsValue = std::max(maxAbsValue, channelMaxAbsValue); - } - - return maxAbsValue; -} - -void AudioBus::sum(const AudioBus *source, ChannelInterpretation interpretation) { +void AudioBus::sum(const AudioBus& source, ChannelInterpretation interpretation) { sum(source, 0, 0, getSize(), interpretation); } void AudioBus::sum( - const AudioBus *source, - size_t start, - size_t length, - ChannelInterpretation interpretation) { - sum(source, start, start, length, interpretation); -} - -void AudioBus::sum( - const AudioBus *source, + const AudioBus& source, size_t sourceStart, size_t destinationStart, size_t length, ChannelInterpretation interpretation) { - if (source == this) { + if (&source == this) { return; } - int numberOfSourceChannels = source->getNumberOfChannels(); + int numberOfSourceChannels = source.getNumberOfChannels(); int numberOfChannels = getNumberOfChannels(); if (interpretation == ChannelInterpretation::DISCRETE) { @@ -255,31 +208,27 @@ void AudioBus::sum( } // Source and destination channel counts are the same. Just sum the channels. - for (int i = 0; i < numberOfChannels_; i += 1) { - getChannel(i)->sum(source->getChannel(i), sourceStart, destinationStart, length); + for (int i = 0; i < getNumberOfChannels(); i += 1) { + channels_[i]->sum(*source.channels_[i], sourceStart, destinationStart, length); } } -void AudioBus::copy(const AudioBus *source) { +void AudioBus::copy(const AudioBus& source) { copy(source, 0, 0, getSize()); } -void AudioBus::copy(const AudioBus *source, size_t start, size_t length) { - copy(source, start, start, length); -} - void AudioBus::copy( - const AudioBus *source, + const AudioBus& source, size_t sourceStart, size_t destinationStart, size_t length) { - if (source == this) { + if (&source == this) { return; } - if (source->getNumberOfChannels() == getNumberOfChannels()) { + if (source.getNumberOfChannels() == getNumberOfChannels()) { for (int i = 0; i < getNumberOfChannels(); i += 1) { - getChannel(i)->copy(source->getChannel(i), sourceStart, destinationStart, length); + channels_[i]->copy(*source.channels_[i], sourceStart, destinationStart, length); } return; @@ -290,15 +239,50 @@ void AudioBus::copy( sum(source, sourceStart, destinationStart, length); } +void AudioBus::normalize() { + float maxAbsValue = this->maxAbsValue(); + + if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { + return; + } + + float scale = 1.0f / maxAbsValue; + this->scale(scale); +} + +void AudioBus::scale(float value) { + for (auto &channel : channels_) { + channel->scale(value); + } +} + +float AudioBus::maxAbsValue() const { + float maxAbsValue = 1.0f; + + for (const auto &channel : channels_) { + float channelMaxAbsValue = channel->getMaxAbsValue(); + maxAbsValue = std::max(maxAbsValue, channelMaxAbsValue); + } + + return maxAbsValue; +} + /** * Internal tooling - channel initialization */ void AudioBus::createChannels() { - channels_ = std::vector>(numberOfChannels_); + if (channels_.size() != static_cast(numberOfChannels_)) { + channels_.clear(); + channels_.reserve(numberOfChannels_); - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_[i] = std::make_shared(size_); + for (int i = 0; i < numberOfChannels_; i += 1) { + channels_.emplace_back(std::make_shared(size_)); + } + } else { + for (int i = 0; i < numberOfChannels_; i += 1) { + channels_[i]->resize(size_); + } } } @@ -307,65 +291,64 @@ void AudioBus::createChannels() { */ void AudioBus::discreteSum( - const AudioBus *source, + const AudioBus& source, size_t sourceStart, size_t destinationStart, size_t length) const { - int numberOfChannels = std::min(getNumberOfChannels(), source->getNumberOfChannels()); + int numberOfChannels = std::min(getNumberOfChannels(), source.getNumberOfChannels()); // In case of source > destination, we "down-mix" and drop the extra channels. // In case of source < destination, we "up-mix" as many channels as we have, // leaving the remaining channels untouched. for (int i = 0; i < numberOfChannels; i++) { - getChannel(i)->sum(source->getChannel(i), sourceStart, destinationStart, length); + channels_[i]->sum(*source.channels_[i], sourceStart, destinationStart, length); } } void AudioBus::sumByUpMixing( - const AudioBus *source, + const AudioBus& source, size_t sourceStart, size_t destinationStart, size_t length) { - int numberOfSourceChannels = source->getNumberOfChannels(); + int numberOfSourceChannels = source.getNumberOfChannels(); int numberOfChannels = getNumberOfChannels(); // Mono to stereo (1 -> 2, 4) if (numberOfSourceChannels == 1 && (numberOfChannels == 2 || numberOfChannels == 4)) { - AudioArray *sourceChannel = source->getChannelByType(ChannelMono); + AudioArray *sourceChannel = source.getChannelByType(ChannelMono); - getChannelByType(ChannelLeft)->sum(sourceChannel, sourceStart, destinationStart, length); - getChannelByType(ChannelRight)->sum(sourceChannel, sourceStart, destinationStart, length); + getChannelByType(ChannelLeft)->sum(*sourceChannel, sourceStart, destinationStart, length); + getChannelByType(ChannelRight)->sum(*sourceChannel, sourceStart, destinationStart, length); return; } // Mono to 5.1 (1 -> 6) if (numberOfSourceChannels == 1 && numberOfChannels == 6) { - AudioArray *sourceChannel = source->getChannel(0); + AudioArray *sourceChannel = source.getChannel(0); - getChannelByType(ChannelCenter)->sum(sourceChannel, sourceStart, destinationStart, length); + getChannelByType(ChannelCenter)->sum(*sourceChannel, sourceStart, destinationStart, length); return; } // Stereo 2 to stereo 4 or 5.1 (2 -> 4, 6) if (numberOfSourceChannels == 2 && (numberOfChannels == 4 || numberOfChannels == 6)) { getChannelByType(ChannelLeft) - ->sum(source->getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); getChannelByType(ChannelRight) - ->sum(source->getChannelByType(ChannelRight), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); return; } // Stereo 4 to 5.1 (4 -> 6) if (numberOfSourceChannels == 4 && numberOfChannels == 6) { getChannelByType(ChannelLeft) - ->sum(source->getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); getChannelByType(ChannelRight) - ->sum(source->getChannelByType(ChannelRight), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); getChannelByType(ChannelSurroundLeft) - ->sum(source->getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); getChannelByType(ChannelSurroundRight) - ->sum( - source->getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); + ->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); return; } @@ -373,24 +356,19 @@ void AudioBus::sumByUpMixing( } void AudioBus::sumByDownMixing( - const AudioBus *source, + const AudioBus& source, size_t sourceStart, size_t destinationStart, size_t length) { - int numberOfSourceChannels = source->getNumberOfChannels(); + int numberOfSourceChannels = source.getNumberOfChannels(); int numberOfChannels = getNumberOfChannels(); // Stereo to mono (2 -> 1): output += 0.5 * (input.left + input.right). if (numberOfSourceChannels == 2 && numberOfChannels == 1) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); + auto destinationData = getChannelByType(ChannelMono); - float *destinationData = getChannelByType(ChannelMono)->getData(); - - dsp::multiplyByScalarThenAddToOutput( - sourceLeft + sourceStart, 0.5f, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceRight + sourceStart, 0.5f, destinationData + destinationStart, length); + destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); + destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); return; } @@ -398,21 +376,12 @@ void AudioBus::sumByDownMixing( // output += 0.25 * (input.left + input.right + input.surroundLeft + // input.surroundRight) if (numberOfSourceChannels == 4 && numberOfChannels == 1) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); - float *sourceSurroundLeft = source->getChannelByType(ChannelSurroundLeft)->getData(); - float *sourceSurroundRight = source->getChannelByType(ChannelSurroundRight)->getData(); - - float *destinationData = getChannelByType(ChannelMono)->getData(); - - dsp::multiplyByScalarThenAddToOutput( - sourceLeft + sourceStart, 0.25f, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceRight + sourceStart, 0.25f, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundLeft + sourceStart, 0.25f, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundRight + sourceStart, 0.25f, destinationData + destinationStart, length); + auto destinationData = getChannelByType(ChannelMono); + + destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.25f); + destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.25f); + destinationData->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.25f); + destinationData->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.25f); return; } @@ -420,28 +389,13 @@ void AudioBus::sumByDownMixing( // output += sqrt(1/2) * (input.left + input.right) + input.center + 0.5 * // (input.surroundLeft + input.surroundRight) if (numberOfSourceChannels == 6 && numberOfChannels == 1) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); - float *sourceCenter = source->getChannelByType(ChannelCenter)->getData(); - float *sourceSurroundLeft = source->getChannelByType(ChannelSurroundLeft)->getData(); - float *sourceSurroundRight = source->getChannelByType(ChannelSurroundRight)->getData(); - - float *destinationData = getChannelByType(ChannelMono)->getData(); - - dsp::multiplyByScalarThenAddToOutput( - sourceLeft + sourceStart, SQRT_HALF, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceRight + sourceStart, SQRT_HALF, destinationData + destinationStart, length); - dsp::add( - sourceCenter + sourceStart, - destinationData + destinationStart, - destinationData + destinationStart, - length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundLeft + sourceStart, 0.5f, destinationData + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundRight + sourceStart, 0.5f, destinationData + destinationStart, length); + auto destinationData = getChannelByType(ChannelMono); + destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, SQRT_HALF); + destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, SQRT_HALF); + destinationData->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length); + destinationData->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); + destinationData->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.5f); return; } @@ -449,23 +403,14 @@ void AudioBus::sumByDownMixing( // output.left += 0.5 * (input.left + input.surroundLeft) // output.right += 0.5 * (input.right + input.surroundRight) if (numberOfSourceChannels == 4 && numberOfChannels == 2) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); - float *sourceSurroundLeft = source->getChannelByType(ChannelSurroundLeft)->getData(); - float *sourceSurroundRight = source->getChannelByType(ChannelSurroundRight)->getData(); - - float *destinationLeft = getChannelByType(ChannelLeft)->getData(); - float *destinationRight = getChannelByType(ChannelRight)->getData(); - - dsp::multiplyByScalarThenAddToOutput( - sourceLeft + sourceStart, 0.5f, destinationLeft + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundLeft + sourceStart, 0.5f, destinationLeft + destinationStart, length); - - dsp::multiplyByScalarThenAddToOutput( - sourceRight + sourceStart, 0.5f, destinationRight + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundRight + sourceStart, 0.5f, destinationRight + destinationStart, length); + auto destinationLeft = getChannelByType(ChannelLeft); + auto destinationRight = getChannelByType(ChannelRight); + + destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); + destinationLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); + + destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); + destinationRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.5f); return; } @@ -474,34 +419,16 @@ void AudioBus::sumByDownMixing( // output.right += input.right + sqrt(1/2) * (input.center + // input.surroundRight) if (numberOfSourceChannels == 6 && numberOfChannels == 2) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); - float *sourceCenter = source->getChannelByType(ChannelCenter)->getData(); - float *sourceSurroundLeft = source->getChannelByType(ChannelSurroundLeft)->getData(); - float *sourceSurroundRight = source->getChannelByType(ChannelSurroundRight)->getData(); - - float *destinationLeft = getChannelByType(ChannelLeft)->getData(); - float *destinationRight = getChannelByType(ChannelRight)->getData(); - - dsp::add( - sourceLeft + sourceStart, - destinationLeft + destinationStart, - destinationLeft + destinationStart, - length); - dsp::multiplyByScalarThenAddToOutput( - sourceCenter + sourceStart, SQRT_HALF, destinationLeft + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundLeft + sourceStart, SQRT_HALF, destinationLeft + destinationStart, length); - - dsp::add( - sourceRight + sourceStart, - destinationRight + destinationStart, - destinationRight + destinationStart, - length); - dsp::multiplyByScalarThenAddToOutput( - sourceCenter + sourceStart, SQRT_HALF, destinationRight + destinationStart, length); - dsp::multiplyByScalarThenAddToOutput( - sourceSurroundRight + sourceStart, SQRT_HALF, destinationRight + destinationStart, length); + auto destinationLeft = getChannelByType(ChannelLeft); + auto destinationRight = getChannelByType(ChannelRight); + + destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + destinationLeft->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, SQRT_HALF); + + destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); + destinationRight->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, SQRT_HALF); return; } @@ -511,43 +438,19 @@ void AudioBus::sumByDownMixing( // output.surroundLeft += input.surroundLeft // output.surroundRight += input.surroundRight if (numberOfSourceChannels == 6 && numberOfChannels == 4) { - float *sourceLeft = source->getChannelByType(ChannelLeft)->getData(); - float *sourceRight = source->getChannelByType(ChannelRight)->getData(); - float *sourceCenter = source->getChannelByType(ChannelCenter)->getData(); - float *sourceSurroundLeft = source->getChannelByType(ChannelSurroundLeft)->getData(); - float *sourceSurroundRight = source->getChannelByType(ChannelSurroundRight)->getData(); - - float *destinationLeft = getChannelByType(ChannelLeft)->getData(); - float *destinationRight = getChannelByType(ChannelRight)->getData(); - float *destinationSurroundLeft = getChannelByType(ChannelSurroundLeft)->getData(); - float *destinationSurroundRight = getChannelByType(ChannelSurroundRight)->getData(); - - dsp::add( - sourceLeft + sourceStart, - destinationLeft + destinationStart, - destinationLeft + destinationStart, - length); - dsp::multiplyByScalarThenAddToOutput( - sourceCenter, SQRT_HALF, destinationLeft + destinationStart, length); - - dsp::add( - sourceRight + sourceStart, - destinationRight + destinationStart, - destinationRight + destinationStart, - length); - dsp::multiplyByScalarThenAddToOutput( - sourceCenter, SQRT_HALF, destinationRight + destinationStart, length); - - dsp::add( - sourceSurroundLeft + sourceStart, - destinationSurroundLeft + destinationStart, - destinationSurroundLeft + destinationStart, - length); - dsp::add( - sourceSurroundRight + sourceStart, - destinationSurroundRight + destinationStart, - destinationSurroundRight + destinationStart, - length); + auto destinationLeft = getChannelByType(ChannelLeft); + auto destinationRight = getChannelByType(ChannelRight); + auto destinationSurroundLeft = getChannelByType(ChannelSurroundLeft); + auto destinationSurroundRight = getChannelByType(ChannelSurroundRight); + + destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + destinationLeft->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + + destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); + destinationRight->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + + destinationSurroundLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); + destinationSurroundRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h index ebcf34cf7..fa3d77a90 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h @@ -9,7 +9,6 @@ namespace audioapi { -class BaseAudioContext; class AudioArray; class AudioBus { @@ -29,62 +28,67 @@ class AudioBus { AudioBus(const AudioBus &other); AudioBus(AudioBus &&other) noexcept; AudioBus &operator=(const AudioBus &other); + AudioBus &operator=(AudioBus &&other) noexcept; + ~AudioBus() = default; + + [[nodiscard]] inline int getNumberOfChannels() const noexcept { + return numberOfChannels_; + } + [[nodiscard]] inline float getSampleRate() const noexcept { + return sampleRate_; + } + [[nodiscard]] inline size_t getSize() const noexcept { + return size_; + } - ~AudioBus(); - - [[nodiscard]] int getNumberOfChannels() const; - [[nodiscard]] float getSampleRate() const; - [[nodiscard]] size_t getSize() const; [[nodiscard]] AudioArray *getChannel(int index) const; [[nodiscard]] AudioArray *getChannelByType(int channelType) const; [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; - AudioArray &operator[](size_t index); - const AudioArray &operator[](size_t index) const; - - void normalize(); - void scale(float value); - [[nodiscard]] float maxAbsValue() const; + AudioArray &operator[](size_t index) { + return *channels_[index]; + } + const AudioArray &operator[](size_t index) const { + return *channels_[index]; + } void zero(); void zero(size_t start, size_t length); void sum( - const AudioBus *source, - ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); - void sum( - const AudioBus *source, - size_t start, - size_t length, + const AudioBus &source, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); void sum( - const AudioBus *source, + const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); - void copy(const AudioBus *source); - void copy(const AudioBus *source, size_t start, size_t length); - void copy(const AudioBus *source, size_t sourceStart, size_t destinationStart, size_t length); + void copy(const AudioBus &source); + void copy(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); + + void normalize(); + void scale(float value); + [[nodiscard]] float maxAbsValue() const; private: std::vector> channels_; - int numberOfChannels_; - float sampleRate_; - size_t size_; + int numberOfChannels_ = 0; + float sampleRate_ = 0.0f; + size_t size_ = 0; void createChannels(); void discreteSum( - const AudioBus *source, + const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length) const; void - sumByUpMixing(const AudioBus *source, size_t sourceStart, size_t destinationStart, size_t length); + sumByUpMixing(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); void sumByDownMixing( - const AudioBus *source, + const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); From f32a9f7f1c4b9c4f47f5203099685807d9b04b32 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Feb 2026 16:30:16 +0100 Subject: [PATCH 03/73] chore: added docs for both utils --- .../common/cpp/audioapi/utils/AudioArray.h | 21 +++++++++++++++++++ .../common/cpp/audioapi/utils/AudioBus.h | 18 ++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index bf6743bad..11f8b7b58 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -7,9 +7,15 @@ namespace audioapi { +/// @brief AudioArray is a simple wrapper around a float array for audio data manipulation. +/// It provides various utility functions for audio processing. +/// @note AudioArray manages its own memory and provides copy and move semantics. +/// @note Not thread-safe. class AudioArray { public: explicit AudioArray(size_t size); + + /// @brief Constructs an AudioArray from existing data. /// @note The data is copied, so it does not take ownership of the pointer AudioArray(const float *data, size_t size); ~AudioArray() = default; @@ -49,7 +55,19 @@ class AudioArray { void zero() noexcept; void zero(size_t start, size_t length) noexcept; + /// @brief Sums the source AudioArray into this AudioArray with an optional gain. + /// @param source The source AudioArray to sum from. + /// @param gain The gain to apply to the source before summing. Default is 1.0f. + /// @note Assumes that source and this AudioArray are not the same. void sum(const AudioArray &source, float gain = 1.0f); + + /// @brief Sums the source AudioArray into this AudioArray with an optional gain. + /// @param source The source AudioArray to sum from. + /// @param sourceStart The starting index in the source AudioArray. + /// @param destinationStart The starting index in this AudioArray. + /// @param length The number of samples to sum. + /// @param gain The gain to apply to the source before summing. Default is 1.0f. + /// @note Assumes that source and this AudioArray are not the same. void sum( const AudioArray &source, size_t sourceStart, @@ -57,6 +75,9 @@ class AudioArray { size_t length, float gain = 1.0f); + /// @brief Multiplies this AudioArray by the source AudioArray element-wise. + /// @param source The source AudioArray to multiply with. + /// @note Assumes that source and this AudioArray are not the same. void multiply(const AudioArray &source); void multiplyByScalar(float value); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h index fa3d77a90..3e56682ce 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h @@ -41,8 +41,19 @@ class AudioBus { return size_; } + /// @brief Get the AudioArray for a specific channel index. + /// @param index The channel index. + /// @return Pointer to the AudioArray for the specified channel - not owning. [[nodiscard]] AudioArray *getChannel(int index) const; + + /// @brief Get the AudioArray for a specific channel type. + /// @param channelType The channel type (e.g., ChannelLeft, ChannelRight). + /// @return Pointer to the AudioArray for the specified channel type - not owning. [[nodiscard]] AudioArray *getChannelByType(int channelType) const; + + /// @brief Get a shared pointer to the AudioArray for a specific channel index. + /// @param index The channel index. + /// @return Shared pointer to the AudioArray for the specified channel - owning. [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; AudioArray &operator[](size_t index) { @@ -85,8 +96,11 @@ class AudioBus { size_t sourceStart, size_t destinationStart, size_t length) const; - void - sumByUpMixing(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); + void sumByUpMixing( + const AudioBus &source, + size_t sourceStart, + size_t destinationStart, + size_t length); void sumByDownMixing( const AudioBus &source, size_t sourceStart, From 91503c4ca5d57bc9e16b01e1e05938702831cea0 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Feb 2026 16:30:33 +0100 Subject: [PATCH 04/73] ci: lint --- .../common/cpp/audioapi/utils/AudioBus.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h index 3e56682ce..d7c2550df 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h @@ -96,11 +96,8 @@ class AudioBus { size_t sourceStart, size_t destinationStart, size_t length) const; - void sumByUpMixing( - const AudioBus &source, - size_t sourceStart, - size_t destinationStart, - size_t length); + void + sumByUpMixing(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); void sumByDownMixing( const AudioBus &source, size_t sourceStart, From 456b1395db7a90fb2f71260507eb3720a9f15d4a Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Feb 2026 17:48:33 +0100 Subject: [PATCH 05/73] refactor: audio array and fft --- .../common/cpp/audioapi/dsp/FFT.h | 11 ++++---- .../common/cpp/audioapi/utils/AudioArray.cpp | 14 ++++------ .../common/cpp/audioapi/utils/AudioArray.h | 27 +++++++++++++++++-- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h index db9caf47c..751646ec4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -17,9 +18,9 @@ class FFT { ~FFT(); template - void doFFT(float *in, std::vector, Allocator> &out) { + void doFFT(const AudioArray& in, std::vector, Allocator> &out) { pffft_transform_ordered( - pffftSetup_, in, reinterpret_cast(&out[0]), work_, PFFFT_FORWARD); + pffftSetup_, in.begin(), reinterpret_cast(&out[0]), work_, PFFFT_FORWARD); // this is a possible place for bugs and mistakes // due to pffft implementation and how it stores results // keep this information in mind @@ -28,11 +29,11 @@ class FFT { } template - void doInverseFFT(std::vector, Allocator> &in, float *out) { + void doInverseFFT(std::vector, Allocator> &in, AudioArray& out) { pffft_transform_ordered( - pffftSetup_, reinterpret_cast(&in[0]), out, work_, PFFFT_BACKWARD); + pffftSetup_, reinterpret_cast(&in[0]), out.begin(), work_, PFFFT_BACKWARD); - dsp::multiplyByScalar(out, 1.0f / static_cast(size_), out, size_); + out.scale(1.0f / static_cast(size_)); } private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index bc29aaf59..b3cb6aa9b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -108,6 +108,10 @@ void AudioArray::sum( } void AudioArray::multiply(const AudioArray &source) { + multiply(source, size_); +} + +void AudioArray::multiply(const audioapi::AudioArray &source, size_t length) { if (data_ == nullptr || source.data_ == nullptr) { return; } @@ -115,15 +119,7 @@ void AudioArray::multiply(const AudioArray &source) { float* __restrict dest = data_.get(); const float* __restrict src = source.data_.get(); - dsp::multiply(src, dest, dest, size_); -} - -void AudioArray::multiplyByScalar(float value) { - if (data_ == nullptr) { - return; - } - - dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); + dsp::multiply(src, dest, dest, length); } void AudioArray::copy(const AudioArray &source) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index 11f8b7b58..f071172d3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace audioapi { @@ -50,6 +51,21 @@ class AudioArray { return data_.get() + size_; } + [[nodiscard]] inline std::span span() noexcept { + return {data_.get(), size_}; + } + + [[nodiscard]] inline std::span span() const noexcept { + return {data_.get(), size_}; + } + + [[nodiscard]] inline std::span subSpan(size_t length, size_t offset = 0) { + if (offset + length > size_) { + throw std::out_of_range("AudioArray::subSpan - offset + length exceeds array size"); + } + return {data_.get() + offset, length}; + } + void resize(size_t size); void zero() noexcept; @@ -79,7 +95,14 @@ class AudioArray { /// @param source The source AudioArray to multiply with. /// @note Assumes that source and this AudioArray are not the same. void multiply(const AudioArray &source); - void multiplyByScalar(float value); + + /// @brief Multiplies this AudioArray by the source AudioArray element-wise. + /// @param source The source AudioArray to multiply with. + /// @param length The number of samples to multiply. + /// @note Assumes that source and this AudioArray are not the same. + void multiply( + const AudioArray &source, + size_t length); void copy(const AudioArray &source); void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); @@ -90,7 +113,7 @@ class AudioArray { [[nodiscard]] float getMaxAbsValue() const; [[nodiscard]] float computeConvolution(const AudioArray &kernel, size_t startIndex = 0) const; - private: + protected: std::unique_ptr data_ = nullptr; size_t size_ = 0; }; From 8f439a6c88fef7970b3eb9abf51ed39b364cc517 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 07:31:49 +0100 Subject: [PATCH 06/73] chore: requested changes --- .../common/cpp/audioapi/dsp/FFT.h | 4 ++-- .../common/cpp/audioapi/utils/AudioArray.h | 12 +++++------- .../common/cpp/audioapi/utils/AudioBus.h | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h index 751646ec4..c7308699f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/FFT.h @@ -18,7 +18,7 @@ class FFT { ~FFT(); template - void doFFT(const AudioArray& in, std::vector, Allocator> &out) { + void doFFT(const AudioArray &in, std::vector, Allocator> &out) { pffft_transform_ordered( pffftSetup_, in.begin(), reinterpret_cast(&out[0]), work_, PFFFT_FORWARD); // this is a possible place for bugs and mistakes @@ -29,7 +29,7 @@ class FFT { } template - void doInverseFFT(std::vector, Allocator> &in, AudioArray& out) { + void doInverseFFT(std::vector, Allocator> &in, AudioArray &out) { pffft_transform_ordered( pffftSetup_, reinterpret_cast(&in[0]), out.begin(), work_, PFFFT_BACKWARD); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index f071172d3..e7f309ecd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -26,14 +26,14 @@ class AudioArray { AudioArray &operator=(const AudioArray &other); AudioArray &operator=(AudioArray &&other) noexcept; - [[nodiscard]] inline size_t getSize() const { + [[nodiscard]] inline size_t getSize() const noexcept { return size_; } - inline float &operator[](size_t index) { + inline float &operator[](size_t index) noexcept { return data_[index]; } - inline const float &operator[](size_t index) const { + inline const float &operator[](size_t index) const noexcept { return data_[index]; } @@ -61,7 +61,7 @@ class AudioArray { [[nodiscard]] inline std::span subSpan(size_t length, size_t offset = 0) { if (offset + length > size_) { - throw std::out_of_range("AudioArray::subSpan - offset + length exceeds array size"); + throw std::out_of_range("AudioArray::subSpan - offset + length exceeds array size"); } return {data_.get() + offset, length}; } @@ -100,9 +100,7 @@ class AudioArray { /// @param source The source AudioArray to multiply with. /// @param length The number of samples to multiply. /// @note Assumes that source and this AudioArray are not the same. - void multiply( - const AudioArray &source, - size_t length); + void multiply(const AudioArray &source, size_t length); void copy(const AudioArray &source); void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h index d7c2550df..243c900a8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h @@ -51,9 +51,9 @@ class AudioBus { /// @return Pointer to the AudioArray for the specified channel type - not owning. [[nodiscard]] AudioArray *getChannelByType(int channelType) const; - /// @brief Get a shared pointer to the AudioArray for a specific channel index. + /// @brief Get a copy of shared pointer to the AudioArray for a specific channel index. /// @param index The channel index. - /// @return Shared pointer to the AudioArray for the specified channel - owning. + /// @return Copy of shared pointer to the AudioArray for the specified channel [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; AudioArray &operator[](size_t index) { From e74317fe7e3e4bba5f240d271198d271dd74082d Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 09:14:29 +0100 Subject: [PATCH 07/73] refactor: next part for audio bus and audio array --- .../common/cpp/audioapi/utils/AudioArray.cpp | 15 +++++++++++++++ .../common/cpp/audioapi/utils/AudioArray.h | 8 ++++++++ .../common/cpp/audioapi/utils/AudioBus.cpp | 13 +++++++++++++ .../common/cpp/audioapi/utils/AudioBus.h | 1 + 4 files changed, 37 insertions(+) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index b3cb6aa9b..ef738c773 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -138,6 +138,21 @@ void AudioArray::copy( memcpy(data_.get() + destinationStart, source.data_.get() + sourceStart, length * sizeof(float)); } +void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceStart, + size_t destinationStart, size_t length) { + if (length == 0 || data_ == nullptr || source.data_ == nullptr) { + return; + } + + auto dstView = this->subSpan(length, destinationStart); + auto srcView = source.span(); + const float* __restrict srcPtr = &srcView[sourceStart]; + + for (size_t i = 0; i < length; ++i) { + dstView[i] = srcPtr[-static_cast(i)]; + } +} + void AudioArray::reverse() { if (data_ == nullptr && size_ > 1) { return; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index e7f309ecd..a456b9dbe 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -105,6 +105,14 @@ class AudioArray { void copy(const AudioArray &source); void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Copies data from the source AudioArray in reverse order into this AudioArray. + /// @param source The source AudioArray to copy from. + /// @param sourceStart The starting index in the source AudioArray. + /// @param destinationStart The starting index in this AudioArray. + /// @param length The number of samples to copy. + /// @note Assumes that source and this AudioArray are not the same. + void copyReverse(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + void reverse(); void normalize(); void scale(float value); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp index ee14402fc..99c9e7327 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp @@ -34,6 +34,19 @@ AudioBus::AudioBus(const AudioBus &other): numberOfChannels_(other.numberOfChann } } +AudioBus::AudioBus(std::vector> channels, float sampleRate) + : channels_(std::move(channels)), + sampleRate_(sampleRate) { + + numberOfChannels_ = static_cast(channels_.size()); + + if (numberOfChannels_ > 0 && channels_[0] != nullptr) { + size_ = channels_[0]->getSize(); + } else { + size_ = 0; + } +} + AudioBus::AudioBus(audioapi::AudioBus &&other) noexcept : channels_(std::move(other.channels_)), numberOfChannels_(other.numberOfChannels_), diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h index 243c900a8..497f04450 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h @@ -25,6 +25,7 @@ class AudioBus { explicit AudioBus() = default; explicit AudioBus(size_t size, int numberOfChannels, float sampleRate); + AudioBus(std::vector> channels, float sampleRate); AudioBus(const AudioBus &other); AudioBus(AudioBus &&other) noexcept; AudioBus &operator=(const AudioBus &other); From d1dede6569966e041217be6c83a9f2da045237e7 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 11:36:26 +0100 Subject: [PATCH 08/73] refactor: wip --- .../docs/guides/create-your-own-effect.mdx | 8 +- .../basic/shared/MyProcessorNode.cpp | 11 +- .../templates/basic/shared/MyProcessorNode.h | 8 +- .../android/core/AndroidAudioRecorder.cpp | 7 +- .../android/core/AndroidAudioRecorder.h | 27 +- .../cpp/audioapi/android/core/AudioPlayer.cpp | 8 +- .../cpp/audioapi/android/core/AudioPlayer.h | 17 +- .../core/utils/AndroidRecorderCallback.cpp | 6 +- .../core/utils/AndroidRecorderCallback.h | 10 +- .../android/core/utils/AudioDecoder.cpp | 16 +- .../HostObjects/AudioParamHostObject.cpp | 4 +- .../effects/WaveShaperNodeHostObject.cpp | 9 +- .../inputs/AudioRecorderHostObject.cpp | 4 +- .../sources/AudioBufferHostObject.cpp | 13 +- .../sources/AudioBufferHostObject.h | 4 +- .../audioapi/HostObjects/utils/NodeOptions.h | 5 +- .../HostObjects/utils/NodeOptionsParser.h | 2 +- .../common/cpp/audioapi/core/AudioContext.cpp | 6 +- .../common/cpp/audioapi/core/AudioContext.h | 2 +- .../common/cpp/audioapi/core/AudioNode.cpp | 44 +-- .../common/cpp/audioapi/core/AudioNode.h | 21 +- .../common/cpp/audioapi/core/AudioParam.cpp | 28 +- .../common/cpp/audioapi/core/AudioParam.h | 20 +- .../cpp/audioapi/core/BaseAudioContext.cpp | 15 +- .../cpp/audioapi/core/BaseAudioContext.h | 2 +- .../cpp/audioapi/core/OfflineAudioContext.cpp | 20 +- .../cpp/audioapi/core/OfflineAudioContext.h | 2 +- .../audioapi/core/analysis/AnalyserNode.cpp | 44 +-- .../cpp/audioapi/core/analysis/AnalyserNode.h | 8 +- .../destinations/AudioDestinationNode.cpp | 8 +- .../core/destinations/AudioDestinationNode.h | 7 +- .../core/effects/BiquadFilterNode.cpp | 31 +- .../audioapi/core/effects/BiquadFilterNode.h | 6 +- .../audioapi/core/effects/ConvolverNode.cpp | 64 ++-- .../cpp/audioapi/core/effects/ConvolverNode.h | 16 +- .../cpp/audioapi/core/effects/DelayNode.cpp | 23 +- .../cpp/audioapi/core/effects/DelayNode.h | 10 +- .../cpp/audioapi/core/effects/GainNode.cpp | 14 +- .../cpp/audioapi/core/effects/GainNode.h | 6 +- .../audioapi/core/effects/IIRFilterNode.cpp | 37 ++- .../cpp/audioapi/core/effects/IIRFilterNode.h | 4 +- .../audioapi/core/effects/PeriodicWave.cpp | 52 ++-- .../cpp/audioapi/core/effects/PeriodicWave.h | 23 +- .../core/effects/StereoPannerNode.cpp | 57 ++-- .../audioapi/core/effects/StereoPannerNode.h | 6 +- .../audioapi/core/effects/WaveShaperNode.cpp | 16 +- .../audioapi/core/effects/WaveShaperNode.h | 14 +- .../cpp/audioapi/core/effects/WorkletNode.cpp | 18 +- .../cpp/audioapi/core/effects/WorkletNode.h | 16 +- .../core/effects/WorkletProcessingNode.cpp | 32 +- .../core/effects/WorkletProcessingNode.h | 14 +- .../cpp/audioapi/core/inputs/AudioRecorder.h | 2 +- .../cpp/audioapi/core/sources/AudioBuffer.cpp | 61 ---- .../cpp/audioapi/core/sources/AudioBuffer.h | 45 --- .../sources/AudioBufferBaseSourceNode.cpp | 10 +- .../core/sources/AudioBufferBaseSourceNode.h | 12 +- .../sources/AudioBufferQueueSourceNode.cpp | 34 +-- .../core/sources/AudioBufferQueueSourceNode.h | 12 +- .../core/sources/AudioBufferSourceNode.cpp | 46 ++- .../core/sources/AudioBufferSourceNode.h | 14 +- .../core/sources/AudioScheduledSourceNode.cpp | 11 +- .../core/sources/AudioScheduledSourceNode.h | 4 +- .../core/sources/ConstantSourceNode.cpp | 21 +- .../core/sources/ConstantSourceNode.h | 6 +- .../audioapi/core/sources/OscillatorNode.cpp | 50 +-- .../audioapi/core/sources/OscillatorNode.h | 6 +- .../core/sources/RecorderAdapterNode.cpp | 16 +- .../core/sources/RecorderAdapterNode.h | 14 +- .../audioapi/core/sources/StreamerNode.cpp | 40 ++- .../cpp/audioapi/core/sources/StreamerNode.h | 20 +- .../core/sources/WorkletSourceNode.cpp | 31 +- .../audioapi/core/sources/WorkletSourceNode.h | 14 +- .../audioapi/core/utils/AudioGraphManager.cpp | 6 +- .../audioapi/core/utils/AudioGraphManager.h | 4 +- .../core/utils/AudioParamEventQueue.h | 2 +- .../core/utils/AudioRecorderCallback.cpp | 18 +- .../core/utils/AudioRecorderCallback.h | 6 +- .../audioapi/core/utils/AudioStretcher.cpp | 17 +- .../cpp/audioapi/core/utils/AudioStretcher.h | 2 +- .../audioapi/core/utils/ParamChangeEvent.cpp | 20 -- ...aramChangeEvent.h => ParamChangeEvent.hpp} | 19 +- .../common/cpp/audioapi/dsp/AudioUtils.cpp | 27 -- .../common/cpp/audioapi/dsp/AudioUtils.h | 16 - .../common/cpp/audioapi/dsp/AudioUtils.hpp | 38 +++ .../common/cpp/audioapi/dsp/Convolver.cpp | 35 ++- .../common/cpp/audioapi/dsp/Convolver.h | 8 +- .../common/cpp/audioapi/dsp/Resampler.cpp | 74 +---- .../common/cpp/audioapi/dsp/Resampler.h | 18 +- .../common/cpp/audioapi/dsp/VectorMath.cpp | 2 +- .../common/cpp/audioapi/dsp/WaveShaper.cpp | 39 ++- .../common/cpp/audioapi/dsp/WaveShaper.h | 10 +- .../common/cpp/audioapi/dsp/Windows.cpp | 75 ----- .../common/cpp/audioapi/dsp/Windows.h | 96 ------ .../common/cpp/audioapi/dsp/Windows.hpp | 196 ++++++++++++ .../cpp/audioapi/jsi/AudioArrayBuffer.cpp | 13 - .../cpp/audioapi/jsi/AudioArrayBuffer.h | 34 --- .../audioapi/libs/ffmpeg/FFmpegDecoding.cpp | 84 ++--- .../cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h | 12 +- .../libs/signalsmith-stretch/fft-pffft.h | 2 + .../common/cpp/audioapi/utils/AudioArray.cpp | 23 +- .../common/cpp/audioapi/utils/AudioArray.h | 6 +- .../cpp/audioapi/utils/AudioArrayBuffer.hpp | 48 +++ .../utils/{AudioBus.cpp => AudioBuffer.cpp} | 287 ++++++++++-------- .../utils/{AudioBus.h => AudioBuffer.h} | 49 +-- .../cpp/audioapi/utils/CircularAudioArray.cpp | 29 +- .../cpp/audioapi/utils/CircularAudioArray.h | 7 +- .../utils/CircularOverflowableAudioArray.cpp | 16 +- .../utils/CircularOverflowableAudioArray.h | 4 +- .../common/cpp/test/src/AudioParamTest.cpp | 11 +- .../cpp/test/src/AudioScheduledSourceTest.cpp | 8 +- .../cpp/test/src/ConstantSourceTest.cpp | 8 +- .../common/cpp/test/src/DelayTest.cpp | 21 +- .../common/cpp/test/src/GainTest.cpp | 19 +- .../common/cpp/test/src/StereoPannerTest.cpp | 34 +-- .../src/core/effects/WaveShaperNodeTest.cpp | 18 +- .../common/cpp/test/src/dsp/ResamplerTest.cpp | 15 +- .../ios/audioapi/ios/core/IOSAudioPlayer.h | 8 +- .../ios/audioapi/ios/core/IOSAudioPlayer.mm | 6 +- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 2 +- .../audioapi/ios/core/utils/AudioDecoder.mm | 9 +- .../ios/core/utils/IOSRecorderCallback.h | 2 +- .../ios/core/utils/IOSRecorderCallback.mm | 2 +- 122 files changed, 1357 insertions(+), 1462 deletions(-) delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.cpp rename packages/react-native-audio-api/common/cpp/audioapi/core/utils/{ParamChangeEvent.h => ParamChangeEvent.hpp} (84%) delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArrayBuffer.hpp rename packages/react-native-audio-api/common/cpp/audioapi/utils/{AudioBus.cpp => AudioBuffer.cpp} (58%) rename packages/react-native-audio-api/common/cpp/audioapi/utils/{AudioBus.h => AudioBuffer.h} (65%) diff --git a/packages/audiodocs/docs/guides/create-your-own-effect.mdx b/packages/audiodocs/docs/guides/create-your-own-effect.mdx index 9eb8ec8a2..85baaddf5 100644 --- a/packages/audiodocs/docs/guides/create-your-own-effect.mdx +++ b/packages/audiodocs/docs/guides/create-your-own-effect.mdx @@ -45,14 +45,14 @@ For the sake of a simplicity, we will use value as a raw `double` type, not wrap #include namespace audioapi { - class AudioBus; + class AudioBuffer; class MyProcessorNode : public AudioNode { public: explicit MyProcessorNode(BaseAudioContext *context); protected: - void processNode(const std::shared_ptr &bus, + void processNode(const std::shared_ptr &bus, int framesToProcess) override; // highlight-start @@ -69,7 +69,7 @@ namespace audioapi { ```cpp #include "MyProcessorNode.h" -#include +#include #include namespace audioapi { @@ -79,7 +79,7 @@ namespace audioapi { isInitialized_ = true; } - void MyProcessorNode::processNode(const std::shared_ptr &bus, + void MyProcessorNode::processNode(const std::shared_ptr &bus, int framesToProcess) { // highlight-start for (int channel = 0; channel < bus->getNumberOfChannels(); ++channel) { diff --git a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp index 3aa07f407..b6c9c60cd 100644 --- a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp +++ b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp @@ -1,14 +1,15 @@ #include "MyProcessorNode.h" -#include +#include namespace audioapi { MyProcessorNode::MyProcessorNode(BaseAudioContext *context) : AudioNode(context) { - isInitialized_ = true; + isInitialized_ = true; } -std::shared_ptr MyProcessorNode::processNode(const std::shared_ptr &bus, - int framesToProcess) { - // put your processing logic here +std::shared_ptr +MyProcessorNode::processNode(const std::shared_ptr &bus, + int framesToProcess) { + // put your processing logic here } } // namespace audioapi diff --git a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h index e065ff359..58cc43cc6 100644 --- a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h +++ b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h @@ -2,15 +2,15 @@ #include namespace audioapi { -class AudioBus; +class AudioBuffer; class MyProcessorNode : public AudioNode { public: explicit MyProcessorNode(BaseAudioContext *context); protected: - std::shared_ptr processNode(const std::shared_ptr &bus, - int framesToProcess) override; - + std::shared_ptr + processNode(const std::shared_ptr &bus, + int framesToProcess) override; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index 07d185a85..30bcc4bba 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -375,12 +375,13 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady( if (isConnected()) { if (auto adapterLock = Locker::tryLock(adapterNodeMutex_)) { for (int channel = 0; channel < streamChannelCount_; ++channel) { + auto channelData = deinterleavingBuffer_->span(); for (int frame = 0; frame < numFrames; ++frame) { - deinterleavingBuffer_->getData()[frame] = + channelData[frame] = static_cast(audioData)[frame * streamChannelCount_ + channel]; } - adapterNode_->buff_[channel]->write(deinterleavingBuffer_->getData(), numFrames); + adapterNode_->buff_[channel]->write(*deinterleavingBuffer_, numFrames); } } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h index 5c2ea8bbf..625fa91d9 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h @@ -1,18 +1,18 @@ #pragma once +#include #include +#include #include #include #include +#include #include #include -#include -#include -#include namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioArray; class CircularAudioArray; class AudioFileProperties; @@ -22,14 +22,16 @@ class AudioEventHandlerRegistry; class AndroidAudioRecorder : public oboe::AudioStreamCallback, public AudioRecorder { public: - explicit AndroidAudioRecorder(const std::shared_ptr &audioEventHandlerRegistry); + explicit AndroidAudioRecorder( + const std::shared_ptr &audioEventHandlerRegistry); ~AndroidAudioRecorder() override; void cleanup(); Result start(const std::string &fileNameOverride) override; Result, std::string> stop() override; - Result enableFileOutput(std::shared_ptr properties) override; + Result enableFileOutput( + std::shared_ptr properties) override; void disableFileOutput() override; void pause() override; @@ -38,17 +40,18 @@ class AndroidAudioRecorder : public oboe::AudioStreamCallback, public AudioRecor bool isPaused() const override; bool isIdle() const override; - Result setOnAudioReadyCallback(float sampleRate, size_t bufferLength, int channelCount, uint64_t callbackId) - override; + Result setOnAudioReadyCallback( + float sampleRate, + size_t bufferLength, + int channelCount, + uint64_t callbackId) override; void clearOnAudioReadyCallback() override; void connect(const std::shared_ptr &node) override; void disconnect() override; - oboe::DataCallbackResult onAudioReady( - oboe::AudioStream *oboeStream, - void *audioData, - int32_t numFrames) override; + oboe::DataCallbackResult + onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override; void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override; private: diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index e112c98a5..215f7f238 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -12,7 +12,7 @@ namespace audioapi { AudioPlayer::AudioPlayer( - const std::function, int)> &renderAudio, + const std::function, int)> &renderAudio, float sampleRate, int channelCount) : renderAudio_(renderAudio), @@ -42,7 +42,7 @@ bool AudioPlayer::openAudioStream() { return false; } - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate_); + audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate_); return true; } @@ -119,7 +119,7 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF for (int i = 0; i < framesToProcess; i++) { for (int channel = 0; channel < channelCount_; channel++) { buffer[(processedFrames + i) * channelCount_ + channel] = - audioBus_->getChannel(channel)->getData()[i]; + (*audioBus_->getChannel(channel))[i]; } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 2af28dde0..7fcc1796c 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -12,12 +12,12 @@ namespace audioapi { using namespace oboe; class AudioContext; -class AudioBus; +class AudioBuffer; class AudioPlayer : public AudioStreamDataCallback, AudioStreamErrorCallback { public: AudioPlayer( - const std::function, int)> &renderAudio, + const std::function, int)> &renderAudio, float sampleRate, int channelCount); @@ -34,18 +34,15 @@ class AudioPlayer : public AudioStreamDataCallback, AudioStreamErrorCallback { [[nodiscard]] bool isRunning() const; - DataCallbackResult onAudioReady( - AudioStream *oboeStream, - void *audioData, - int32_t numFrames) override; - - void onErrorAfterClose(AudioStream * /* audioStream */, Result /* error */) + DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override; + void onErrorAfterClose(AudioStream * /* audioStream */, Result /* error */) override; + private: - std::function, int)> renderAudio_; + std::function, int)> renderAudio_; std::shared_ptr mStream_; - std::shared_ptr audioBus_; + std::shared_ptr audioBus_; bool isInitialized_ = false; float sampleRate_; int channelCount_; diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp index b354c7355..2b2573ff6 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -165,13 +165,13 @@ void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFr auto *inputData = static_cast(data); for (int channel = 0; channel < channelCount_; ++channel) { - float *channelData = deinterleavingArray_->getData(); + auto channelData = deinterleavingArray_->span(); for (int frame = 0; frame < numFrames; ++frame) { channelData[frame] = inputData[frame * channelCount_ + channel]; } - circularBus_[channel]->push_back(channelData, numFrames); + circularBus_[channel]->push_back(*deinterleavingArray_, numFrames); } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h index 088bca2c5..1f573a65e 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h @@ -1,15 +1,14 @@ #pragma once - -#include #include +#include #include -#include #include +#include namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioArray; class CircularAudioArray; class AudioEventHandlerRegistry; @@ -24,7 +23,8 @@ class AndroidRecorderCallback : public AudioRecorderCallback { uint64_t callbackId); ~AndroidRecorderCallback(); - Result prepare(float streamSampleRate, int streamChannelCount, size_t maxInputBufferLength); + Result + prepare(float streamSampleRate, int streamChannelCount, size_t maxInputBufferLength); void cleanup() override; void receiveAudioData(void *data, int numFrames); diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp index da0c8ae6f..c27e69074 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp @@ -1,9 +1,8 @@ -#include #include #include #include #include -#include +#include #include #include @@ -54,15 +53,15 @@ std::shared_ptr AudioDecoder::makeAudioBufferFromFloatBuffer( } auto outputFrames = buffer.size() / outputChannels; - auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); + auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = buffer[i * outputChannels + ch]; } } - return std::make_shared(audioBus); + return audioBus; } std::shared_ptr AudioDecoder::decodeWithFilePath( @@ -162,10 +161,11 @@ std::shared_ptr AudioDecoder::decodeWithPCMInBase64( const auto uint8Data = reinterpret_cast(decodedData.data()); size_t numFramesDecoded = decodedData.size() / (inputChannelCount * sizeof(int16_t)); - auto audioBus = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); + auto audioBus = + std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); for (int ch = 0; ch < inputChannelCount; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { size_t offset; @@ -180,7 +180,7 @@ std::shared_ptr AudioDecoder::decodeWithPCMInBase64( channelData[i] = uint8ToFloat(uint8Data[offset], uint8Data[offset + 1]); } } - return std::make_shared(audioBus); + return audioBus; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp index 45508e702..6d501e25d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include #include @@ -80,7 +82,7 @@ JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, setValueCurveAtTime) { args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); auto rawValues = reinterpret_cast(arrayBuffer.data(runtime)); auto length = static_cast(arrayBuffer.size(runtime)); - auto values = std::make_unique>(rawValues, rawValues + length); + auto values = std::make_unique(rawValues, length); double startTime = args[1].getNumber(); double duration = args[2].getNumber(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp index 1550e9b72..3a989cc25 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -37,8 +37,7 @@ JSI_PROPERTY_GETTER_IMPL(WaveShaperNodeHostObject, curve) { } // copy AudioArray holding curve data to avoid subsequent modifications - auto audioArray = std::make_shared(*curve); - auto audioArrayBuffer = std::make_shared(audioArray); + auto audioArrayBuffer = std::make_shared(*curve); auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer); auto float32ArrayCtor = runtime.global().getPropertyAsFunction(runtime, "Float32Array"); @@ -58,14 +57,14 @@ JSI_HOST_FUNCTION_IMPL(WaveShaperNodeHostObject, setCurve) { auto waveShaperNode = std::static_pointer_cast(node_); if (args[0].isNull()) { - waveShaperNode->setCurve(std::shared_ptr(nullptr)); + waveShaperNode->setCurve(nullptr); return jsi::Value::undefined(); } auto arrayBuffer = args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); - auto curve = std::make_shared( + auto curve = std::make_shared( reinterpret_cast(arrayBuffer.data(runtime)), static_cast(arrayBuffer.size(runtime) / sizeof(float))); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp index 10976563b..9ea3aeb0e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp @@ -3,10 +3,10 @@ #include #include #include -#include #include -#include #include +#include +#include #ifdef ANDROID #include #else diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp index 91a7c577a..cdaff96b0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp @@ -1,7 +1,7 @@ #include -#include -#include +#include +#include #include #include @@ -30,7 +30,7 @@ JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, sampleRate) { } JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, length) { - return {static_cast(audioBuffer_->getLength())}; + return {static_cast(audioBuffer_->getSize())}; } JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, duration) { @@ -43,8 +43,7 @@ JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, numberOfChannels) { JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, getChannelData) { auto channel = static_cast(args[0].getNumber()); - auto audioArrayBuffer = - std::make_shared(audioBuffer_->bus_->getSharedChannel(channel)); + auto audioArrayBuffer = audioBuffer_->getSharedChannel(channel); auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer); auto float32ArrayCtor = runtime.global().getPropertyAsFunction(runtime, "Float32Array"); @@ -63,7 +62,7 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, copyFromChannel) { auto channelNumber = static_cast(args[1].getNumber()); auto startInChannel = static_cast(args[2].getNumber()); - audioBuffer_->copyFromChannel(destination, length, channelNumber, startInChannel); + audioBuffer_->getChannel(channelNumber)->copyTo(destination, startInChannel, 0, length); return jsi::Value::undefined(); } @@ -76,7 +75,7 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, copyToChannel) { auto channelNumber = static_cast(args[1].getNumber()); auto startInChannel = static_cast(args[2].getNumber()); - audioBuffer_->copyToChannel(source, length, channelNumber, startInChannel); + audioBuffer_->getChannel(channelNumber)->copy(source, 0, startInChannel, length); return jsi::Value::undefined(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h index 7bd601004..09d440d55 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -29,7 +29,7 @@ class AudioBufferHostObject : public JsiHostObject { } [[nodiscard]] inline size_t getSizeInBytes() const { - return audioBuffer_->getLength() * audioBuffer_->getNumberOfChannels() * sizeof(float); + return audioBuffer_->getSize() * audioBuffer_->getNumberOfChannels() * sizeof(float); } JSI_PROPERTY_GETTER_DECL(sampleRate); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h index b3fcd250c..e6d7cf68a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h @@ -6,13 +6,14 @@ #include #include -#include #include #include #include #include #include #include +#include +#include namespace audioapi { struct AudioNodeOptions { @@ -104,7 +105,7 @@ struct IIRFilterOptions : AudioNodeOptions { }; struct WaveShaperOptions : AudioNodeOptions { - std::shared_ptr curve; + std::shared_ptr curve; OverSampleType oversample; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h index ede0e4a94..0810d684e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h @@ -253,7 +253,7 @@ WaveShaperOptions parseWaveShaperOptions(jsi::Runtime &runtime, const jsi::Objec if (optionsObject.hasProperty(runtime, "buffer")) { auto arrayBuffer = optionsObject.getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); - options.curve = std::make_shared( + options.curve = std::make_shared( reinterpret_cast(arrayBuffer.data(runtime)), static_cast(arrayBuffer.size(runtime) / sizeof(float))); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp index 7e90d9a36..853f5633c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp @@ -16,7 +16,7 @@ AudioContext::AudioContext( const std::shared_ptr &audioEventHandlerRegistry, const RuntimeRegistry &runtimeRegistry) : BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry), - isInitialized_(false) {} + isInitialized_(false) {} AudioContext::~AudioContext() { if (getState() != ContextState::CLOSED) { @@ -90,8 +90,8 @@ bool AudioContext::start() { return false; } -std::function, int)> AudioContext::renderAudio() { - return [this](const std::shared_ptr &data, int frames) { +std::function, int)> AudioContext::renderAudio() { + return [this](const std::shared_ptr &data, int frames) { destination_->renderAudio(data, frames); }; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h index c1eced267..967b38426 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h @@ -37,7 +37,7 @@ class AudioContext : public BaseAudioContext { bool isDriverRunning() const override; - std::function, int)> renderAudio(); + std::function, int)> renderAudio(); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp index e8cd1503e..8997adb39 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp @@ -4,16 +4,16 @@ #include #include #include -#include +#include #include #include #include namespace audioapi { -AudioNode::AudioNode(const std::shared_ptr& context) : context_(context) { +AudioNode::AudioNode(const std::shared_ptr &context) : context_(context) { audioBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); } AudioNode::AudioNode( @@ -24,7 +24,7 @@ AudioNode::AudioNode( channelCountMode_(options.channelCountMode), channelInterpretation_(options.channelInterpretation) { audioBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); } AudioNode::~AudioNode() { @@ -55,36 +55,36 @@ ChannelInterpretation AudioNode::getChannelInterpretation() const { void AudioNode::connect(const std::shared_ptr &node) { if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->addPendingNodeConnection( - shared_from_this(), node, AudioGraphManager::ConnectionType::CONNECT); + context->getGraphManager()->addPendingNodeConnection( + shared_from_this(), node, AudioGraphManager::ConnectionType::CONNECT); } } void AudioNode::connect(const std::shared_ptr ¶m) { if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->addPendingParamConnection( - shared_from_this(), param, AudioGraphManager::ConnectionType::CONNECT); + context->getGraphManager()->addPendingParamConnection( + shared_from_this(), param, AudioGraphManager::ConnectionType::CONNECT); } } void AudioNode::disconnect() { if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->addPendingNodeConnection( - shared_from_this(), nullptr, AudioGraphManager::ConnectionType::DISCONNECT_ALL); + context->getGraphManager()->addPendingNodeConnection( + shared_from_this(), nullptr, AudioGraphManager::ConnectionType::DISCONNECT_ALL); } } void AudioNode::disconnect(const std::shared_ptr &node) { if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->addPendingNodeConnection( - shared_from_this(), node, AudioGraphManager::ConnectionType::DISCONNECT); + context->getGraphManager()->addPendingNodeConnection( + shared_from_this(), node, AudioGraphManager::ConnectionType::DISCONNECT); } } void AudioNode::disconnect(const std::shared_ptr ¶m) { if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->addPendingParamConnection( - shared_from_this(), param, AudioGraphManager::ConnectionType::DISCONNECT); + context->getGraphManager()->addPendingParamConnection( + shared_from_this(), param, AudioGraphManager::ConnectionType::DISCONNECT); } } @@ -120,8 +120,8 @@ void AudioNode::disable() { } } -std::shared_ptr AudioNode::processAudio( - const std::shared_ptr &outputBus, +std::shared_ptr AudioNode::processAudio( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { if (!isInitialized_) { @@ -166,8 +166,8 @@ bool AudioNode::isAlreadyProcessed() { return true; } -std::shared_ptr AudioNode::processInputs( - const std::shared_ptr &outputBus, +std::shared_ptr AudioNode::processInputs( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { auto processingBus = audioBus_; @@ -194,8 +194,8 @@ std::shared_ptr AudioNode::processInputs( return processingBus; } -std::shared_ptr AudioNode::applyChannelCountMode( - const std::shared_ptr &processingBus) { +std::shared_ptr AudioNode::applyChannelCountMode( + const std::shared_ptr &processingBus) { // If the channelCountMode is EXPLICIT, the node should output the number of // channels specified by the channelCount. if (channelCountMode_ == ChannelCountMode::EXPLICIT) { @@ -212,11 +212,11 @@ std::shared_ptr AudioNode::applyChannelCountMode( return processingBus; } -void AudioNode::mixInputsBuses(const std::shared_ptr &processingBus) { +void AudioNode::mixInputsBuses(const std::shared_ptr &processingBus) { assert(processingBus != nullptr); for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end; ++it) { - processingBus->sum(it->get(), channelInterpretation_); + processingBus->sum(**it, channelInterpretation_); } inputBuses_.clear(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 8a52f15c3..319108b1c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -13,7 +13,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class BaseAudioContext; class AudioParam; struct AudioNodeOptions; @@ -36,8 +36,8 @@ class AudioNode : public std::enable_shared_from_this { void disconnect(); void disconnect(const std::shared_ptr &node); void disconnect(const std::shared_ptr ¶m); - virtual std::shared_ptr processAudio( - const std::shared_ptr &outputBus, + virtual std::shared_ptr processAudio( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed); @@ -53,7 +53,7 @@ class AudioNode : public std::enable_shared_from_this { friend class DelayNodeHostObject; std::weak_ptr context_; - std::shared_ptr audioBus_; + std::shared_ptr audioBus_; int numberOfInputs_ = 1; int numberOfOutputs_ = 1; @@ -73,17 +73,18 @@ class AudioNode : public std::enable_shared_from_this { std::size_t lastRenderedFrame_{SIZE_MAX}; private: - std::vector> inputBuses_ = {}; + std::vector> inputBuses_ = {}; - virtual std::shared_ptr processInputs( - const std::shared_ptr &outputBus, + virtual std::shared_ptr processInputs( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed); - virtual std::shared_ptr processNode(const std::shared_ptr &, int) = 0; + virtual std::shared_ptr processNode(const std::shared_ptr &, int) = 0; bool isAlreadyProcessed(); - std::shared_ptr applyChannelCountMode(const std::shared_ptr &processingBus); - void mixInputsBuses(const std::shared_ptr &processingBus); + std::shared_ptr applyChannelCountMode( + const std::shared_ptr &processingBus); + void mixInputsBuses(const std::shared_ptr &processingBus); void connectNode(const std::shared_ptr &node); void disconnectNode(const std::shared_ptr &node); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp index c0873dbe8..87b134fef 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -12,7 +12,7 @@ AudioParam::AudioParam( float defaultValue, float minValue, float maxValue, - const std::shared_ptr& context) + const std::shared_ptr &context) : context_(context), value_(defaultValue), defaultValue_(defaultValue), @@ -24,7 +24,7 @@ AudioParam::AudioParam( endTime_(0), startValue_(defaultValue), endValue_(defaultValue), - audioBus_(std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { + audioBus_(std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { inputBuses_.reserve(4); inputNodes_.reserve(4); // Default calculation function just returns the static value @@ -174,7 +174,7 @@ void AudioParam::setTargetAtTime(float target, double startTime, double timeCons } void AudioParam::setValueCurveAtTime( - const std::shared_ptr>& values, + const std::shared_ptr &values, size_t length, double startTime, double duration) { @@ -197,7 +197,7 @@ void AudioParam::setValueCurveAtTime( // Calculate interpolation factor between adjacent array elements auto factor = static_cast( (time - startTime) * static_cast(length - 1) / (endTime - startTime) - k); - return dsp::linearInterpolate(values->data(), k, k + 1, factor); + return dsp::linearInterpolate(values->span(), k, k + 1, factor); } return endValue; @@ -207,7 +207,7 @@ void AudioParam::setValueCurveAtTime( startTime, startTime + duration, param.getQueueEndValue(), - values->at(length - 1), + values->span()[length - 1], std::move(calculateValue), ParamChangeEventType::SET_VALUE_CURVE)); }; @@ -242,8 +242,8 @@ void AudioParam::removeInputNode(AudioNode *node) { } } -std::shared_ptr AudioParam::calculateInputs( - const std::shared_ptr &processingBus, +std::shared_ptr AudioParam::calculateInputs( + const std::shared_ptr &processingBus, int framesToProcess) { processingBus->zero(); if (inputNodes_.empty()) { @@ -254,7 +254,7 @@ std::shared_ptr AudioParam::calculateInputs( return processingBus; } -std::shared_ptr AudioParam::processARateParam(int framesToProcess, double time) { +std::shared_ptr AudioParam::processARateParam(int framesToProcess, double time) { processScheduledEvents(); auto processingBus = calculateInputs(audioBus_, framesToProcess); @@ -262,7 +262,7 @@ std::shared_ptr AudioParam::processARateParam(int framesToProcess, dou if (context == nullptr) return processingBus; float sampleRate = context->getSampleRate(); - float *busData = processingBus->getChannel(0)->getData(); + auto busData = processingBus->getChannel(0)->span(); float timeCache = time; float timeStep = 1.0f / sampleRate; float sample = 0.0f; @@ -281,11 +281,11 @@ float AudioParam::processKRateParam(int framesToProcess, double time) { auto processingBus = calculateInputs(audioBus_, framesToProcess); // Return block-rate parameter value plus first sample of input modulation - return processingBus->getChannel(0)->getData()[0] + getValueAtTime(time); + return processingBus->getChannel(0)->span()[0] + getValueAtTime(time); } void AudioParam::processInputs( - const std::shared_ptr &outputBus, + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { for (auto it = inputNodes_.begin(), end = inputNodes_.end(); it != end; ++it) { @@ -302,12 +302,12 @@ void AudioParam::processInputs( } } -void AudioParam::mixInputsBuses(const std::shared_ptr &processingBus) { +void AudioParam::mixInputsBuses(const std::shared_ptr &processingBus) { assert(processingBus != nullptr); // Sum all input buses into the processing bus for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end; ++it) { - processingBus->sum(it->get(), ChannelInterpretation::SPEAKERS); + processingBus->sum(**it, ChannelInterpretation::SPEAKERS); } // Clear for next processing cycle diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index 0f53b8a7e..165a48809 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -3,8 +3,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -65,7 +65,7 @@ class AudioParam { // JS-Thread only void setValueCurveAtTime( - const std::shared_ptr> &values, + const std::shared_ptr &values, size_t length, double startTime, double duration); @@ -86,7 +86,7 @@ class AudioParam { void removeInputNode(AudioNode *node); // Audio-Thread only - std::shared_ptr processARateParam(int framesToProcess, double time); + std::shared_ptr processARateParam(int framesToProcess, double time); // Audio-Thread only float processKRateParam(int framesToProcess, double time); @@ -111,8 +111,8 @@ class AudioParam { // Input modulation system std::vector inputNodes_; - std::shared_ptr audioBus_; - std::vector> inputBuses_; + std::shared_ptr audioBus_; + std::vector> inputBuses_; /// @brief Get the end time of the parameter queue. /// @return The end time of the parameter queue or last endTime_ if queue is empty. @@ -145,12 +145,12 @@ class AudioParam { } float getValueAtTime(double time); void processInputs( - const std::shared_ptr &outputBus, + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed); - void mixInputsBuses(const std::shared_ptr &processingBus); - std::shared_ptr calculateInputs( - const std::shared_ptr &processingBus, + void mixInputsBuses(const std::shared_ptr &processingBus); + std::shared_ptr calculateInputs( + const std::shared_ptr &processingBus, int framesToProcess); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index 78b8888cf..8ec251296 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -78,7 +77,7 @@ std::shared_ptr BaseAudioContext::getDestination() const { } void BaseAudioContext::setState(audioapi::ContextState state) { - state_.store(state, std::memory_order_release); + state_.store(state, std::memory_order_release); } std::shared_ptr BaseAudioContext::createWorkletSourceNode( @@ -122,7 +121,8 @@ std::shared_ptr BaseAudioContext::createRecorderAdapter() { return recorderAdapter; } -std::shared_ptr BaseAudioContext::createOscillator(const OscillatorOptions &options) { +std::shared_ptr BaseAudioContext::createOscillator( + const OscillatorOptions &options) { auto oscillator = std::make_shared(shared_from_this(), options); graphManager_->addSourceNode(oscillator); return oscillator; @@ -192,7 +192,7 @@ std::shared_ptr BaseAudioContext::createBufferQueueS } std::shared_ptr BaseAudioContext::createBuffer(const AudioBufferOptions &options) { - return std::make_shared(options); + return std::make_shared(options.length, options.numberOfChannels, options.sampleRate); } std::shared_ptr BaseAudioContext::createPeriodicWave( @@ -214,7 +214,8 @@ std::shared_ptr BaseAudioContext::createConvolver(const Convolver return convolver; } -std::shared_ptr BaseAudioContext::createWaveShaper(const WaveShaperOptions &options) { +std::shared_ptr BaseAudioContext::createWaveShaper( + const WaveShaperOptions &options) { auto waveShaper = std::make_shared(shared_from_this(), options); graphManager_->addProcessingNode(waveShaper); return waveShaper; @@ -261,7 +262,7 @@ std::shared_ptr BaseAudioContext::getAudioEventHandl } const RuntimeRegistry &BaseAudioContext::getRuntimeRegistry() const { - return runtimeRegistry_; + return runtimeRegistry_; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index a684eda13..bcbc837a4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -16,7 +16,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class GainNode; class DelayNode; class AudioBuffer; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp index 0b771c973..9b2b15f7a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp @@ -2,12 +2,11 @@ #include #include -#include #include #include #include #include -#include +#include #include #include @@ -28,10 +27,10 @@ OfflineAudioContext::OfflineAudioContext( length_(length), numberOfChannels_(numberOfChannels), currentSampleFrame_(0), - resultBus_(std::make_shared(length, numberOfChannels, sampleRate)) {} + resultBus_(std::make_shared(length, numberOfChannels, sampleRate)) {} OfflineAudioContext::~OfflineAudioContext() { - getGraphManager()->cleanup(); + getGraphManager()->cleanup(); } void OfflineAudioContext::resume() { @@ -65,7 +64,8 @@ void OfflineAudioContext::renderAudio() { setState(ContextState::RUNNING); std::thread([this]() { - auto audioBus = std::make_shared(RENDER_QUANTUM_SIZE, numberOfChannels_, getSampleRate()); + auto audioBus = + std::make_shared(RENDER_QUANTUM_SIZE, numberOfChannels_, getSampleRate()); while (currentSampleFrame_ < length_) { Locker locker(mutex_); @@ -74,12 +74,7 @@ void OfflineAudioContext::renderAudio() { destination_->renderAudio(audioBus, framesToProcess); - for (int i = 0; i < framesToProcess; i++) { - for (int channel = 0; channel < numberOfChannels_; channel += 1) { - resultBus_->getChannel(channel)->getData()[currentSampleFrame_ + i] = - audioBus->getChannel(channel)->getData()[i]; - } - } + resultBus_->copy(*audioBus, currentSampleFrame_, 0, framesToProcess); currentSampleFrame_ += framesToProcess; @@ -96,8 +91,7 @@ void OfflineAudioContext::renderAudio() { } // Rendering completed - auto buffer = std::make_shared(resultBus_); - resultCallback_(buffer); + resultCallback_(resultBus_); }).detach(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h index a33fd384a..e3c16e015 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h @@ -38,7 +38,7 @@ class OfflineAudioContext : public BaseAudioContext { int numberOfChannels_; size_t currentSampleFrame_; - std::shared_ptr resultBus_; + std::shared_ptr resultBus_; void renderAudio(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index f0396a522..70c9ae50d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -1,11 +1,11 @@ #include #include #include -#include +#include #include -#include +#include #include -#include +#include #include #include @@ -25,7 +25,7 @@ AnalyserNode::AnalyserNode( smoothingTimeConstant_(options.smoothingTimeConstant), windowType_(WindowType::BLACKMAN), inputBuffer_(std::make_unique(MAX_FFT_SIZE * 2)), - downMixBus_(std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), + downMixBus_(std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), tempBuffer_(std::make_unique(fftSize_)), fft_(std::make_unique(fftSize_)), complexData_(std::vector>(fftSize_)), @@ -91,13 +91,14 @@ void AnalyserNode::getFloatFrequencyData(float *data, int length) { doFFTAnalysis(); length = std::min(static_cast(magnitudeBuffer_->getSize()), length); - dsp::linearToDecibels(magnitudeBuffer_->getData(), data, length); + // TODO + // dsp::linearToDecibels(magnitudeBuffer_->getData(), data, length); } void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { doFFTAnalysis(); - auto magnitudeBufferData = magnitudeBuffer_->getData(); + auto magnitudeBufferData = magnitudeBuffer_->span(); length = std::min(static_cast(magnitudeBuffer_->getSize()), length); const auto rangeScaleFactor = @@ -121,18 +122,19 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { void AnalyserNode::getFloatTimeDomainData(float *data, int length) { auto size = std::min(fftSize_, length); - inputBuffer_->pop_back(data, size, std::max(0, fftSize_ - size), true); +// TODO +// inputBuffer_->pop_back(data, size, std::max(0, fftSize_ - size), true); } void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { auto size = std::min(fftSize_, length); - inputBuffer_->pop_back(tempBuffer_->getData(), fftSize_, std::max(0, fftSize_ - size), true); + inputBuffer_->pop_back(*tempBuffer_, fftSize_, std::max(0, fftSize_ - size), true); - for (int i = 0; i < size; i++) { - auto value = tempBuffer_->getData()[i]; + auto values = tempBuffer_->span(); - float scaledValue = 128 * (value + 1); + for (int i = 0; i < size; i++) { + float scaledValue = 128 * (values[i] + 1); if (scaledValue < 0) { scaledValue = 0; @@ -145,16 +147,16 @@ void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { } } -std::shared_ptr AnalyserNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr AnalyserNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { // Analyser should behave like a sniffer node, it should not modify the // processingBus but instead copy the data to its own input buffer. // Down mix the input bus to mono - downMixBus_->copy(processingBus.get()); + downMixBus_->copy(*processingBus); // Copy the down mixed bus to the input buffer (circular buffer) - inputBuffer_->push_back(downMixBus_->getChannel(0)->getData(), framesToProcess, true); + inputBuffer_->push_back(*downMixBus_->getChannel(0), framesToProcess, true); shouldDoFFTAnalysis_ = true; @@ -170,18 +172,18 @@ void AnalyserNode::doFFTAnalysis() { // We want to copy last fftSize_ elements added to the input buffer to apply // the window. - inputBuffer_->pop_back(tempBuffer_->getData(), fftSize_, 0, true); + inputBuffer_->pop_back(*tempBuffer_, fftSize_, 0, true); - dsp::multiply(tempBuffer_->getData(), windowData_->getData(), tempBuffer_->getData(), fftSize_); + tempBuffer_->multiply(*windowData_, fftSize_); // do fft analysis - get frequency domain data - fft_->doFFT(tempBuffer_->getData(), complexData_); + fft_->doFFT(*tempBuffer_, complexData_); // Zero out nquist component complexData_[0] = std::complex(complexData_[0].real(), 0); const float magnitudeScale = 1.0f / static_cast(fftSize_); - auto magnitudeBufferData = magnitudeBuffer_->getData(); + auto magnitudeBufferData = magnitudeBuffer_->span(); for (int i = 0; i < magnitudeBuffer_->getSize(); i++) { auto scalarMagnitude = std::abs(complexData_[i]) * magnitudeScale; @@ -203,10 +205,10 @@ void AnalyserNode::setWindowData(AnalyserNode::WindowType type, int size) { switch (windowType_) { case WindowType::BLACKMAN: - dsp::Blackman().apply(windowData_->getData(), static_cast(windowData_->getSize())); + dsp::Blackman().apply(windowData_->span()); break; case WindowType::HANN: - dsp::Hann().apply(windowData_->getData(), static_cast(windowData_->getSize())); + dsp::Hann().apply(windowData_->span()); break; } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index b5e22822f..3ef915803 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -12,7 +12,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioArray; class CircularAudioArray; struct AnalyserOptions; @@ -43,8 +43,8 @@ class AnalyserNode : public AudioNode { void getByteTimeDomainData(uint8_t *data, int length); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: @@ -57,7 +57,7 @@ class AnalyserNode : public AudioNode { std::shared_ptr windowData_; std::unique_ptr inputBuffer_; - std::unique_ptr downMixBus_; + std::unique_ptr downMixBus_; std::unique_ptr tempBuffer_; std::unique_ptr fft_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp index 9133f688a..87fc96461 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include namespace audioapi { @@ -28,14 +28,14 @@ double AudioDestinationNode::getCurrentTime() const { } void AudioDestinationNode::renderAudio( - const std::shared_ptr &destinationBus, + const std::shared_ptr &destinationBus, int numFrames) { if (numFrames < 0 || !destinationBus || !isInitialized_) { return; } if (std::shared_ptr context = context_.lock()) { - context->getGraphManager()->preProcessGraph(); + context->getGraphManager()->preProcessGraph(); } destinationBus->zero(); @@ -43,7 +43,7 @@ void AudioDestinationNode::renderAudio( auto processedBus = processAudio(destinationBus, numFrames, true); if (processedBus && processedBus != destinationBus) { - destinationBus->copy(processedBus.get()); + destinationBus->copy(*processedBus); } destinationBus->normalize(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h index 94b8e3bba..478e979d8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h @@ -10,7 +10,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class BaseAudioContext; class AudioDestinationNode : public AudioNode { @@ -20,12 +20,13 @@ class AudioDestinationNode : public AudioNode { std::size_t getCurrentSampleFrame() const; double getCurrentTime() const; - void renderAudio(const std::shared_ptr &audioData, int numFrames); + void renderAudio(const std::shared_ptr &audioData, int numFrames); protected: // DestinationNode is triggered by AudioContext using renderAudio // processNode function is not necessary and is never called. - std::shared_ptr processNode(const std::shared_ptr &processingBus, int) final { + std::shared_ptr processNode(const std::shared_ptr &processingBus, int) + final { return processingBus; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index d7da37744..0e4248ba2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -39,9 +39,12 @@ namespace audioapi { -BiquadFilterNode::BiquadFilterNode(const std::shared_ptr& context, const BiquadFilterOptions &options) : AudioNode(context, options) { - frequencyParam_ = - std::make_shared(options.frequency, 0.0f, context->getNyquistFrequency(), context); +BiquadFilterNode::BiquadFilterNode( + const std::shared_ptr &context, + const BiquadFilterOptions &options) + : AudioNode(context, options) { + frequencyParam_ = std::make_shared( + options.frequency, 0.0f, context->getNyquistFrequency(), context); detuneParam_ = std::make_shared( options.detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, @@ -383,8 +386,8 @@ void BiquadFilterNode::applyFilter() { } } -std::shared_ptr BiquadFilterNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr BiquadFilterNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { int numChannels = processingBus->getNumberOfChannels(); @@ -400,24 +403,30 @@ std::shared_ptr BiquadFilterNode::processNode( float x1, x2, y1, y2; for (int c = 0; c < numChannels; ++c) { - auto channelData = processingBus->getChannel(c)->getData(); + auto channel = processingBus->getChannel(c)->subSpan(framesToProcess); x1 = x1_[c]; x2 = x2_[c]; y1 = y1_[c]; y2 = y2_[c]; - for (int i = 0; i < framesToProcess; ++i) { - float input = channelData[i]; - float output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + for (float &sample : channel) { + auto input = sample; + auto output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; - channelData[i] = output; + // Avoid denormalized numbers + if (std::abs(output) < 1e-15f) { + output = 0.0f; + } + + sample = output; x2 = x1; x1 = input; y2 = y1; y1 = output; } + x1_[c] = x1; x2_[c] = x2; y1_[c] = y1; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index 38f140c3e..287e4ca53 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -45,7 +45,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct BiquadFilterOptions; class BiquadFilterNode : public AudioNode { @@ -72,8 +72,8 @@ class BiquadFilterNode : public AudioNode { size_t length); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 35077afdd..f107e8041 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -1,18 +1,20 @@ #include #include #include -#include #include -#include +#include #include #include +#include #include #include #include #include namespace audioapi { -ConvolverNode::ConvolverNode(const std::shared_ptr& context, const ConvolverOptions &options) +ConvolverNode::ConvolverNode( + const std::shared_ptr &context, + const ConvolverOptions &options) : AudioNode(context, options), gainCalibrationSampleRate_(context->getSampleRate()), remainingSegments_(0), @@ -25,7 +27,7 @@ ConvolverNode::ConvolverNode(const std::shared_ptr& context, c internalBuffer_(nullptr) { setBuffer(options.bus); audioBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); requiresTailProcessing_ = true; isInitialized_ = true; } @@ -58,20 +60,18 @@ void ConvolverNode::setBuffer(const std::shared_ptr &buffer) { convolvers_.clear(); for (int i = 0; i < buffer->getNumberOfChannels(); ++i) { convolvers_.emplace_back(); - AudioArray channelData(buffer->getLength()); - memcpy(channelData.getData(), buffer->getChannelData(i), buffer->getLength() * sizeof(float)); - convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getLength()); + AudioArray channelData(*buffer->getChannel(i)); + convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getSize()); } if (buffer->getNumberOfChannels() == 1) { // add one more convolver, because right now input is always stereo convolvers_.emplace_back(); - AudioArray channelData(buffer->getLength()); - memcpy(channelData.getData(), buffer->getChannelData(0), buffer->getLength() * sizeof(float)); - convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getLength()); + AudioArray channelData(*buffer->getChannel(0)); + convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getSize()); } - internalBuffer_ = - std::make_shared(RENDER_QUANTUM_SIZE * 2, channelCount_, buffer->getSampleRate()); - intermediateBus_ = std::make_shared( + internalBuffer_ = std::make_shared( + RENDER_QUANTUM_SIZE * 2, channelCount_, buffer->getSampleRate()); + intermediateBus_ = std::make_shared( RENDER_QUANTUM_SIZE, convolvers_.size(), buffer->getSampleRate()); internalBufferIndex_ = 0; } @@ -85,8 +85,8 @@ void ConvolverNode::onInputDisabled() { } } -std::shared_ptr ConvolverNode::processInputs( - const std::shared_ptr &outputBus, +std::shared_ptr ConvolverNode::processInputs( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { if (internalBufferIndex_ < framesToProcess) { @@ -97,8 +97,8 @@ std::shared_ptr ConvolverNode::processInputs( // processing pipeline: processingBus -> intermediateBus_ -> audioBus_ (mixing // with intermediateBus_) -std::shared_ptr ConvolverNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr ConvolverNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (signalledToStop_) { if (remainingSegments_ > 0) { @@ -112,30 +112,23 @@ std::shared_ptr ConvolverNode::processNode( } if (internalBufferIndex_ < framesToProcess) { performConvolution(processingBus); // result returned to intermediateBus_ - audioBus_->sum(intermediateBus_.get()); + audioBus_->sum(*intermediateBus_); - internalBuffer_->copy(audioBus_.get(), 0, internalBufferIndex_, RENDER_QUANTUM_SIZE); + internalBuffer_->copy(*audioBus_, 0, internalBufferIndex_, RENDER_QUANTUM_SIZE); internalBufferIndex_ += RENDER_QUANTUM_SIZE; } audioBus_->zero(); - audioBus_->copy(internalBuffer_.get(), 0, 0, framesToProcess); + audioBus_->copy(*internalBuffer_, 0, 0, framesToProcess); int remainingFrames = internalBufferIndex_ - framesToProcess; if (remainingFrames > 0) { for (int i = 0; i < internalBuffer_->getNumberOfChannels(); ++i) { - memmove( - internalBuffer_->getChannel(i)->getData(), - internalBuffer_->getChannel(i)->getData() + framesToProcess, - remainingFrames * sizeof(float)); + internalBuffer_->copy(*internalBuffer_, framesToProcess, 0, remainingFrames); } } internalBufferIndex_ -= framesToProcess; for (int i = 0; i < audioBus_->getNumberOfChannels(); ++i) { - dsp::multiplyByScalar( - audioBus_->getChannel(i)->getData(), - scaleFactor_, - audioBus_->getChannel(i)->getData(), - framesToProcess); + audioBus_->getChannel(i)->scale(scaleFactor_); } return audioBus_; @@ -143,13 +136,13 @@ std::shared_ptr ConvolverNode::processNode( void ConvolverNode::calculateNormalizationScale() { int numberOfChannels = buffer_->getNumberOfChannels(); - int length = buffer_->getLength(); + auto length = buffer_->getSize(); float power = 0; for (int channel = 0; channel < numberOfChannels; ++channel) { float channelPower = 0; - auto channelData = buffer_->getChannelData(channel); + auto channelData = buffer_->getChannel(channel)->span(); for (int i = 0; i < length; ++i) { float sample = channelData[i]; channelPower += sample * sample; @@ -166,12 +159,11 @@ void ConvolverNode::calculateNormalizationScale() { scaleFactor_ *= gainCalibrationSampleRate_ / buffer_->getSampleRate(); } -void ConvolverNode::performConvolution(const std::shared_ptr &processingBus) { +void ConvolverNode::performConvolution(const std::shared_ptr &processingBus) { if (processingBus->getNumberOfChannels() == 1) { for (int i = 0; i < convolvers_.size(); ++i) { threadPool_->schedule([&, i] { - convolvers_[i].process( - processingBus->getChannel(0)->getData(), intermediateBus_->getChannel(i)->getData()); + convolvers_[i].process(*processingBus->getChannel(0), *intermediateBus_->getChannel(i)); }); } } else if (processingBus->getNumberOfChannels() == 2) { @@ -187,8 +179,8 @@ void ConvolverNode::performConvolution(const std::shared_ptr &processi for (int i = 0; i < convolvers_.size(); ++i) { threadPool_->schedule([this, i, inputChannelMap, outputChannelMap, &processingBus] { convolvers_[i].process( - processingBus->getChannel(inputChannelMap[i])->getData(), - intermediateBus_->getChannel(outputChannelMap[i])->getData()); + *processingBus->getChannel(inputChannelMap[i]), + *intermediateBus_->getChannel(outputChannelMap[i])); }); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h index 5a705787e..33054f524 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h @@ -15,7 +15,7 @@ static constexpr double MIN_IR_POWER = 0.000125; namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioBuffer; struct ConvolverOptions; @@ -31,13 +31,13 @@ class ConvolverNode : public AudioNode { void setBuffer(const std::shared_ptr &buffer); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: - std::shared_ptr processInputs( - const std::shared_ptr &outputBus, + std::shared_ptr processInputs( + const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) override; void onInputDisabled() override; @@ -47,18 +47,18 @@ class ConvolverNode : public AudioNode { bool normalize_; bool signalledToStop_; float scaleFactor_; - std::shared_ptr intermediateBus_; + std::shared_ptr intermediateBus_; // impulse response buffer std::shared_ptr buffer_; // buffer to hold internal processed data - std::shared_ptr internalBuffer_; + std::shared_ptr internalBuffer_; // vectors of convolvers, one per channel std::vector convolvers_; std::shared_ptr threadPool_; void calculateNormalizationScale(); - void performConvolution(const std::shared_ptr &processingBus); + void performConvolution(const std::shared_ptr &processingBus); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 04a4dd886..019a15dd2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -3,16 +3,17 @@ #include #include #include -#include +#include #include namespace audioapi { -DelayNode::DelayNode(const std::shared_ptr& context, const DelayOptions &options) +DelayNode::DelayNode(const std::shared_ptr &context, const DelayOptions &options) : AudioNode(context, options), - delayTimeParam_(std::make_shared(options.delayTime, 0, options.maxDelayTime, context)), + delayTimeParam_( + std::make_shared(options.delayTime, 0, options.maxDelayTime, context)), delayBuffer_( - std::make_shared( + std::make_shared( static_cast( options.maxDelayTime * context->getSampleRate() + 1), // +1 to enable delayTime equal to maxDelayTime @@ -39,7 +40,7 @@ void DelayNode::onInputDisabled() { } void DelayNode::delayBufferOperation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess, size_t &operationStartingIndex, DelayNode::BufferAction action) { @@ -51,10 +52,10 @@ void DelayNode::delayBufferOperation( if (action == BufferAction::WRITE) { delayBuffer_->sum( - processingBus.get(), processingBusStartIndex, operationStartingIndex, framesToEnd); + *processingBus, processingBusStartIndex, operationStartingIndex, framesToEnd); } else { // READ processingBus->sum( - delayBuffer_.get(), operationStartingIndex, processingBusStartIndex, framesToEnd); + *delayBuffer_, operationStartingIndex, processingBusStartIndex, framesToEnd); } operationStartingIndex = 0; @@ -64,11 +65,11 @@ void DelayNode::delayBufferOperation( if (action == BufferAction::WRITE) { delayBuffer_->sum( - processingBus.get(), processingBusStartIndex, operationStartingIndex, framesToProcess); + *processingBus, processingBusStartIndex, operationStartingIndex, framesToProcess); processingBus->zero(); } else { // READ processingBus->sum( - delayBuffer_.get(), operationStartingIndex, processingBusStartIndex, framesToProcess); + *delayBuffer_, operationStartingIndex, processingBusStartIndex, framesToProcess); delayBuffer_->zero(operationStartingIndex, framesToProcess); } @@ -79,8 +80,8 @@ void DelayNode::delayBufferOperation( // processing is split into two parts // 1. writing to delay buffer (mixing if needed) from processing bus // 2. reading from delay buffer to processing bus (mixing if needed) with delay -std::shared_ptr DelayNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr DelayNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { // handling tail processing if (signalledToStop_) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index 0d280e0fd..a44a77007 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -8,7 +8,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct DelayOptions; class DelayNode : public AudioNode { @@ -18,20 +18,20 @@ class DelayNode : public AudioNode { [[nodiscard]] std::shared_ptr getDelayTimeParam() const; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: void onInputDisabled() override; enum class BufferAction { READ, WRITE }; void delayBufferOperation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess, size_t &operationStartingIndex, BufferAction action); std::shared_ptr delayTimeParam_; - std::shared_ptr delayBuffer_; + std::shared_ptr delayBuffer_; size_t readIndex_ = 0; bool signalledToStop_ = false; int remainingFrames_ = 0; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp index 57da13581..e538a1187 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include namespace audioapi { @@ -23,8 +23,8 @@ std::shared_ptr GainNode::getGainParam() const { return gainParam_; } -std::shared_ptr GainNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr GainNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { std::shared_ptr context = context_.lock(); if (context == nullptr) @@ -32,11 +32,9 @@ std::shared_ptr GainNode::processNode( double time = context->getCurrentTime(); auto gainParamValues = gainParam_->processARateParam(framesToProcess, time); for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - dsp::multiply( - processingBus->getChannel(i)->getData(), - gainParamValues->getChannel(0)->getData(), - processingBus->getChannel(i)->getData(), - framesToProcess); + auto channel = processingBus->getChannel(i); + auto gainValues = gainParamValues->getChannel(0); + channel->multiply(*gainValues, framesToProcess); } return processingBus; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h index b224f8f55..928d5a5fc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h @@ -7,7 +7,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct GainOptions; class GainNode : public AudioNode { @@ -17,8 +17,8 @@ class GainNode : public AudioNode { [[nodiscard]] std::shared_ptr getGainParam() const; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 467098bf1..a1a160850 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,9 +39,7 @@ namespace audioapi { IIRFilterNode::IIRFilterNode( const std::shared_ptr &context, const IIRFilterOptions &options) - : AudioNode(context, options), - feedforward_(std::move(options.feedforward)), - feedback_(std::move(options.feedback)) { + : AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) { int maxChannels = MAX_CHANNEL_COUNT; xBuffers_.resize(maxChannels); @@ -123,8 +121,8 @@ void IIRFilterNode::getFrequencyResponse( // TODO: tail -std::shared_ptr IIRFilterNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr IIRFilterNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { int numChannels = processingBus->getNumberOfChannels(); @@ -135,31 +133,38 @@ std::shared_ptr IIRFilterNode::processNode( int mask = bufferLength - 1; for (int c = 0; c < numChannels; ++c) { - auto channelData = processingBus->getChannel(c)->getData(); + auto channel = processingBus->getChannel(c)->subSpan(0, framesToProcess); + auto &x = xBuffers_[c]; auto &y = yBuffers_[c]; size_t bufferIndex = bufferIndices[c]; - for (int n = 0; n < framesToProcess; ++n) { - float yn = feedforward_[0] * channelData[n]; + for (float &sample : channel) { + const float x_n = sample; + float y_n = feedforward_[0] * sample; for (int k = 1; k < minLength; ++k) { int m = (bufferIndex - k) & mask; - yn = std::fma(feedforward_[k], x[m], yn); - yn = std::fma(-feedback_[k], y[m], yn); + y_n = std::fma(feedforward_[k], x[m], y_n); + y_n = std::fma(-feedback_[k], y[m], y_n); } for (int k = minLength; k < feedforwardLength; ++k) { - yn = std::fma(feedforward_[k], x[(bufferIndex - k) & mask], yn); + y_n = std::fma(feedforward_[k], x[(bufferIndex - k) & mask], y_n); } for (int k = minLength; k < feedbackLength; ++k) { - yn = std::fma(-feedback_[k], y[(bufferIndex - k) & (bufferLength - 1)], yn); + y_n = std::fma(-feedback_[k], y[(bufferIndex - k) & (bufferLength - 1)], y_n); + } + + // Avoid denormalized numbers + if (std::abs(y_n) < 1e-15f) { + y_n = 0.0f; } - channelData[n] = yn; + sample = y_n; - x[bufferIndex] = channelData[n]; - y[bufferIndex] = yn; + x[bufferIndex] = x_n; + y[bufferIndex] = y_n; bufferIndex = (bufferIndex + 1) & (bufferLength - 1); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 4ac80bf8b..8d6b44937 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -49,8 +49,8 @@ class IIRFilterNode : public AudioNode { size_t length); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.cpp index 07bbcd86e..423c21789 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.cpp @@ -47,7 +47,8 @@ PeriodicWave::PeriodicWave(float sampleRate, bool disableNormalization) lowestFundamentalFrequency_ = static_cast(nyquistFrequency) / static_cast(getMaxNumberOfPartials()); scale_ = static_cast(getPeriodicWaveSize()) / static_cast(sampleRate_); - bandLimitedTables_ = new float *[numberOfRanges_]; + bandLimitedTables_ = + std::make_unique(getPeriodicWaveSize(), numberOfRanges_, sampleRate_); fft_ = std::make_unique(getPeriodicWaveSize()); } @@ -69,14 +70,6 @@ PeriodicWave::PeriodicWave( createBandLimitedTables(complexData, length); } -PeriodicWave::~PeriodicWave() { - for (int i = 0; i < numberOfRanges_; i++) { - delete[] bandLimitedTables_[i]; - } - delete[] bandLimitedTables_; - bandLimitedTables_ = nullptr; -} - int PeriodicWave::getPeriodicWaveSize() const { if (sampleRate_ <= 24000) { return 2048; @@ -94,13 +87,10 @@ float PeriodicWave::getScale() const { } float PeriodicWave::getSample(float fundamentalFrequency, float phase, float phaseIncrement) { - float *lowerWaveData = nullptr; - float *higherWaveData = nullptr; + WaveTableSource source = getWaveDataForFundamentalFrequency(fundamentalFrequency); - auto interpolationFactor = - getWaveDataForFundamentalFrequency(fundamentalFrequency, lowerWaveData, higherWaveData); - - return doInterpolation(phase, phaseIncrement, interpolationFactor, lowerWaveData, higherWaveData); + return doInterpolation( + phase, phaseIncrement, source.interpolationFactor, *source.lower, *source.higher); } int PeriodicWave::getMaxNumberOfPartials() const { @@ -217,31 +207,24 @@ void PeriodicWave::createBandLimitedTables( // Zero out the DC and nquist components. complexFFTData[0] = {0.0f, 0.0f}; - bandLimitedTables_[rangeIndex] = new float[fftSize]; + auto channel = bandLimitedTables_->getChannel(rangeIndex); // Perform the inverse FFT to get the time domain representation of the // band-limited waveform. - fft_->doInverseFFT(complexFFTData, bandLimitedTables_[rangeIndex]); + fft_->doInverseFFT(complexFFTData, *channel); if (!disableNormalization_ && rangeIndex == 0) { - float maxValue = dsp::maximumMagnitude(bandLimitedTables_[rangeIndex], fftSize); + float maxValue = channel->getMaxAbsValue(); if (maxValue != 0) { normalizationFactor = 1.0f / maxValue; } } - dsp::multiplyByScalar( - bandLimitedTables_[rangeIndex], - normalizationFactor, - bandLimitedTables_[rangeIndex], - fftSize); + channel->scale(normalizationFactor); } } -float PeriodicWave::getWaveDataForFundamentalFrequency( - float fundamentalFrequency, - float *&lowerWaveData, - float *&higherWaveData) { +WaveTableSource PeriodicWave::getWaveDataForFundamentalFrequency(float fundamentalFrequency) const { // negative frequencies are allowed and will be treated as positive. fundamentalFrequency = std::fabs(fundamentalFrequency); @@ -260,19 +243,19 @@ float PeriodicWave::getWaveDataForFundamentalFrequency( lowerRangeIndex < numberOfRanges_ - 1 ? lowerRangeIndex + 1 : lowerRangeIndex; // get the wave data for the lower and higher range index. - lowerWaveData = bandLimitedTables_[lowerRangeIndex]; - higherWaveData = bandLimitedTables_[higherRangeIndex]; - // calculate the interpolation factor between the lower and higher range data. - return pitchRange - static_cast(lowerRangeIndex); + return { + bandLimitedTables_->getChannel(lowerRangeIndex), + bandLimitedTables_->getChannel(higherRangeIndex), + pitchRange - static_cast(lowerRangeIndex)}; } float PeriodicWave::doInterpolation( float phase, float phaseIncrement, float waveTableInterpolationFactor, - const float *lowerWaveData, - const float *higherWaveData) const { + const AudioArray &lowerWaveData, + const AudioArray &higherWaveData) const { float lowerWaveDataSample = 0; float higherWaveDataSample = 0; @@ -336,7 +319,6 @@ float PeriodicWave::doInterpolation( } } - return (1 - waveTableInterpolationFactor) * higherWaveDataSample + - waveTableInterpolationFactor * lowerWaveDataSample; + return std::lerp(higherWaveDataSample, lowerWaveDataSample, waveTableInterpolationFactor); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.h index e04b83dae..d39cb1e15 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/PeriodicWave.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -38,6 +39,13 @@ #include namespace audioapi { + +struct WaveTableSource { + const AudioArray *lower; + const AudioArray *higher; + float interpolationFactor; +}; + class PeriodicWave { public: explicit PeriodicWave(float sampleRate, OscillatorType type, bool disableNormalization); @@ -46,7 +54,6 @@ class PeriodicWave { const std::vector> &complexData, int length, bool disableNormalization); - ~PeriodicWave(); [[nodiscard]] int getPeriodicWaveSize() const; [[nodiscard]] float getScale() const; @@ -81,21 +88,18 @@ class PeriodicWave { // This function returns the interpolation factor between the lower and higher // range data and sets the lower and higher wave data for the given // fundamental frequency. - float getWaveDataForFundamentalFrequency( - float fundamentalFrequency, - float *&lowerWaveData, - float *&higherWaveData); + WaveTableSource getWaveDataForFundamentalFrequency(float fundamentalFrequency) const; // This function performs interpolation between the lower and higher range // data based on the interpolation factor and current buffer index. Type of // interpolation is determined by the phase increment. Returns the // interpolated sample. - float doInterpolation( + [[nodiscard]] float doInterpolation( float bufferIndex, float phaseIncrement, float waveTableInterpolationFactor, - const float *lowerWaveData, - const float *higherWaveData) const; + const AudioArray &lowerWaveData, + const AudioArray &higherWaveData) const; // determines the time resolution of the waveform. float sampleRate_; @@ -108,8 +112,7 @@ class PeriodicWave { // rate. float scale_; // array of band-limited waveforms. - float **bandLimitedTables_; - // + std::unique_ptr bandLimitedTables_; std::unique_ptr fft_; // if true, the waveTable is not normalized. bool disableNormalization_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp index ca6b0ad47..be31dd231 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include // https://webaudio.github.io/web-audio-api/#stereopanner-algorithm @@ -22,8 +22,8 @@ std::shared_ptr StereoPannerNode::getPanParam() const { return panParam_; } -std::shared_ptr StereoPannerNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr StereoPannerNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { std::shared_ptr context = context_.lock(); if (context == nullptr) @@ -31,46 +31,43 @@ std::shared_ptr StereoPannerNode::processNode( double time = context->getCurrentTime(); double deltaTime = 1.0 / context->getSampleRate(); - auto *inputLeft = processingBus->getChannelByType(AudioBus::ChannelLeft); - auto panParamValues = - panParam_->processARateParam(framesToProcess, time)->getChannel(0)->getData(); + auto panParamValues = panParam_->processARateParam(framesToProcess, time)->getChannel(0)->span(); - auto *outputLeft = audioBus_->getChannelByType(AudioBus::ChannelLeft); - auto *outputRight = audioBus_->getChannelByType(AudioBus::ChannelRight); + auto outputLeft = audioBus_->getChannelByType(AudioBuffer::ChannelLeft)->span(); + auto outputRight = audioBus_->getChannelByType(AudioBuffer::ChannelRight)->span(); // Input is mono if (processingBus->getNumberOfChannels() == 1) { - for (int i = 0; i < framesToProcess; i++) { - auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); - auto x = (pan + 1) / 2; - - auto gainL = static_cast(cos(x * PI / 2)); - auto gainR = static_cast(sin(x * PI / 2)); + auto inputLeft = processingBus->getChannelByType(AudioBuffer::ChannelMono)->span(); - float input = (*inputLeft)[i]; + for (int i = 0; i < framesToProcess; i++) { + const auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); + const auto x = (pan + 1) / 2; + const auto angle = x * (PI / 2); + const float input = inputLeft[i]; - (*outputLeft)[i] = input * gainL; - (*outputRight)[i] = input * gainR; + outputLeft[i] = input * std::cos(angle); + outputRight[i] = input * std::sin(angle); time += deltaTime; } } else { // Input is stereo - auto *inputRight = processingBus->getChannelByType(AudioBus::ChannelRight); - for (int i = 0; i < framesToProcess; i++) { - auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); - auto x = (pan <= 0 ? pan + 1 : pan); - - auto gainL = static_cast(cos(x * PI / 2)); - auto gainR = static_cast(sin(x * PI / 2)); + auto inputLeft = processingBus->getChannelByType(AudioBuffer::ChannelMono)->span(); + auto inputRight = processingBus->getChannelByType(AudioBuffer::ChannelRight)->span(); - float inputL = (*inputLeft)[i]; - float inputR = (*inputRight)[i]; + for (int i = 0; i < framesToProcess; i++) { + const auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); + const auto x = (pan <= 0 ? pan + 1 : pan); + const auto gainL = static_cast(cos(x * PI / 2)); + const auto gainR = static_cast(sin(x * PI / 2)); + const float inputL = inputLeft[i]; + const float inputR = inputRight[i]; if (pan <= 0) { - (*outputLeft)[i] = inputL + inputR * gainL; - (*outputRight)[i] = inputR * gainR; + outputLeft[i] = inputL + inputR * gainL; + outputRight[i] = inputR * gainR; } else { - (*outputLeft)[i] = inputL * gainL; - (*outputRight)[i] = inputR + inputL * gainR; + outputLeft[i] = inputL * gainL; + outputRight[i] = inputR + inputL * gainR; } time += deltaTime; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h index 6f79ecb52..da99cbe13 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h @@ -9,7 +9,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct StereoPannerOptions; class StereoPannerNode : public AudioNode { @@ -21,8 +21,8 @@ class StereoPannerNode : public AudioNode { [[nodiscard]] std::shared_ptr getPanParam() const; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index e16198b4b..70bc34cec 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -39,12 +39,12 @@ void WaveShaperNode::setOversample(OverSampleType type) { } } -std::shared_ptr WaveShaperNode::getCurve() const { +std::shared_ptr WaveShaperNode::getCurve() const { std::scoped_lock lock(mutex_); return curve_; } -void WaveShaperNode::setCurve(const std::shared_ptr &curve) { +void WaveShaperNode::setCurve(const std::shared_ptr &curve) { std::scoped_lock lock(mutex_); curve_ = curve; @@ -53,8 +53,8 @@ void WaveShaperNode::setCurve(const std::shared_ptr &curve) { } } -std::shared_ptr WaveShaperNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr WaveShaperNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (!isInitialized_) { return processingBus; @@ -71,9 +71,9 @@ std::shared_ptr WaveShaperNode::processNode( } for (int channel = 0; channel < processingBus->getNumberOfChannels(); channel++) { - auto channelData = processingBus->getSharedChannel(channel); + auto channelData = processingBus->getChannel(channel); - waveShapers_[channel]->process(channelData, framesToProcess); + waveShapers_[channel]->process(*channelData, framesToProcess); } return processingBus; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index d1620e02a..759b706fb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -14,8 +14,8 @@ namespace audioapi { -class AudioBus; -class AudioArray; +class AudioBuffer; +class AudioArrayBuffer; struct WaveShaperOptions; class WaveShaperNode : public AudioNode { @@ -25,19 +25,19 @@ class WaveShaperNode : public AudioNode { const WaveShaperOptions &options); [[nodiscard]] OverSampleType getOversample() const; - [[nodiscard]] std::shared_ptr getCurve() const; + [[nodiscard]] std::shared_ptr getCurve() const; void setOversample(OverSampleType); - void setCurve(const std::shared_ptr &curve); + void setCurve(const std::shared_ptr &curve); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: std::atomic oversample_; - std::shared_ptr curve_{}; + std::shared_ptr curve_; mutable std::mutex mutex_; std::vector> waveShapers_{}; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index 1a76ea72e..befb84c23 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -7,21 +7,22 @@ namespace audioapi { WorkletNode::WorkletNode( - std::shared_ptr context, + const std::shared_ptr &context, size_t bufferLength, size_t inputChannelCount, WorkletsRunner &&runtime) : AudioNode(context), workletRunner_(std::move(runtime)), - bus_(std::make_shared(bufferLength, inputChannelCount, context->getSampleRate())), + bus_( + std::make_shared(bufferLength, inputChannelCount, context->getSampleRate())), bufferLength_(bufferLength), inputChannelCount_(inputChannelCount), curBuffIndex_(0) { isInitialized_ = true; } -std::shared_ptr WorkletNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr WorkletNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { size_t processed = 0; size_t channelCount_ = @@ -34,7 +35,7 @@ std::shared_ptr WorkletNode::processNode( /// here we copy /// to [curBuffIndex_, curBuffIndex_ + shouldProcess] /// from [processed, processed + shouldProcess] - bus_->copy(processingBus.get(), processed, curBuffIndex_, shouldProcess); + bus_->copy(*processingBus, processed, curBuffIndex_, shouldProcess); processed += shouldProcess; curBuffIndex_ += shouldProcess; @@ -48,10 +49,9 @@ std::shared_ptr WorkletNode::processNode( workletRunner_.executeOnRuntimeSync([this, channelCount_](jsi::Runtime &uiRuntimeRaw) { /// Arguments preparation auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_); - for (size_t ch = 0; ch < channelCount_; ch++) { - auto audioArray = std::make_shared(bufferLength_); - audioArray->copy(bus_->getChannel(ch)); - auto sharedAudioArray = std::make_shared(audioArray); + for (int ch = 0; ch < channelCount_; ch++) { + auto sharedAudioArray = std::make_shared(bufferLength_); + sharedAudioArray->copy(*bus_->getChannel(ch)); auto sharedAudioArraySize = sharedAudioArray->size(); auto arrayBuffer = jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray)); arrayBuffer.setExternalMemoryPressure(uiRuntimeRaw, sharedAudioArraySize); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h index a830acb7e..f42bd6f3b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h @@ -3,9 +3,9 @@ #include #include #include -#include #include -#include +#include +#include #include #include @@ -24,8 +24,8 @@ class WorkletNode : public AudioNode { : AudioNode(context) {} protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return processingBus; } @@ -37,7 +37,7 @@ using namespace facebook; class WorkletNode : public AudioNode { public: explicit WorkletNode( - std::shared_ptr context, + const std::shared_ptr &context, size_t bufferLength, size_t inputChannelCount, WorkletsRunner &&workletRunner); @@ -45,13 +45,13 @@ class WorkletNode : public AudioNode { ~WorkletNode() override = default; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: WorkletsRunner workletRunner_; - std::shared_ptr bus_; + std::shared_ptr bus_; /// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer size_t bufferLength_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index e385f7b89..cc3f12b1d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -6,7 +6,7 @@ namespace audioapi { WorkletProcessingNode::WorkletProcessingNode( - std::shared_ptr context, + const std::shared_ptr &context, WorkletsRunner &&workletRunner) : AudioNode(context), workletRunner_(std::move(workletRunner)) { // Pre-allocate buffers for max 128 frames and 2 channels (stereo) @@ -15,29 +15,22 @@ WorkletProcessingNode::WorkletProcessingNode( outputBuffsHandles_.resize(maxChannelCount); for (size_t i = 0; i < maxChannelCount; ++i) { - auto inputAudioArray = std::make_shared(RENDER_QUANTUM_SIZE); - inputBuffsHandles_[i] = std::make_shared(inputAudioArray); - - auto outputAudioArray = std::make_shared(RENDER_QUANTUM_SIZE); - outputBuffsHandles_[i] = std::make_shared(outputAudioArray); + inputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); + outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); } isInitialized_ = true; } -std::shared_ptr WorkletProcessingNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr WorkletProcessingNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { size_t channelCount = std::min( static_cast(2), // Fixed to stereo for now static_cast(processingBus->getNumberOfChannels())); // Copy input data to pre-allocated input buffers - for (size_t ch = 0; ch < channelCount; ch++) { - auto channelData = processingBus->getChannel(ch)->getData(); - std::memcpy( - /* dest */ inputBuffsHandles_[ch]->data(), - /* src */ reinterpret_cast(channelData), - /* size */ framesToProcess * sizeof(float)); + for (int ch = 0; ch < channelCount; ch++) { + inputBuffsHandles_[ch]->copy(*processingBus->getChannel(ch), 0, 0, framesToProcess); } // Execute the worklet @@ -71,18 +64,15 @@ std::shared_ptr WorkletProcessingNode::processNode( }); // Copy processed output data back to the processing bus or zero on failure - for (size_t ch = 0; ch < channelCount; ch++) { - auto channelData = processingBus->getChannel(ch)->getData(); + for (int ch = 0; ch < channelCount; ch++) { + auto channelData = processingBus->getChannel(ch); if (result.has_value()) { // Copy processed output data - std::memcpy( - /* dest */ reinterpret_cast(channelData), - /* src */ outputBuffsHandles_[ch]->data(), - /* size */ framesToProcess * sizeof(float)); + channelData->copy(*inputBuffsHandles_[ch], 0, 0, framesToProcess); } else { // Zero the output on worklet execution failure - std::memset(channelData, 0, framesToProcess * sizeof(float)); + channelData->zero(0, framesToProcess); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h index b302e81bc..5fe43bb8d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h @@ -3,9 +3,9 @@ #include #include #include -#include #include -#include +#include +#include #include #include @@ -22,8 +22,8 @@ class WorkletProcessingNode : public AudioNode { : AudioNode(context) {} protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return processingBus; } @@ -35,12 +35,12 @@ using namespace facebook; class WorkletProcessingNode : public AudioNode { public: explicit WorkletProcessingNode( - std::shared_ptr context, + const std::shared_ptr &context, WorkletsRunner &&workletRunner); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index b0f1fb89c..c2b196765 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -9,7 +9,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioFileWriter; class CircularAudioArray; class RecorderAdapterNode; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp deleted file mode 100644 index 528ca7703..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -namespace audioapi { - -AudioBuffer::AudioBuffer(const AudioBufferOptions &options) - : bus_(std::make_shared(options.length, options.numberOfChannels, options.sampleRate)) {} - -AudioBuffer::AudioBuffer(std::shared_ptr bus) { - bus_ = std::move(bus); -} - -size_t AudioBuffer::getLength() const { - return bus_->getSize(); -} - -int AudioBuffer::getNumberOfChannels() const { - return bus_->getNumberOfChannels(); -} - -float AudioBuffer::getSampleRate() const { - return bus_->getSampleRate(); -} - -double AudioBuffer::getDuration() const { - return static_cast(getLength()) / getSampleRate(); -} - -float *AudioBuffer::getChannelData(int channel) const { - return bus_->getChannel(channel)->getData(); -} - -void AudioBuffer::copyFromChannel( - float *destination, - size_t destinationLength, - int channelNumber, - size_t startInChannel) const { - memcpy( - destination, - bus_->getChannel(channelNumber)->getData() + startInChannel, - std::min(destinationLength, getLength() - startInChannel) * sizeof(float)); -} - -void AudioBuffer::copyToChannel( - const float *source, - size_t sourceLength, - int channelNumber, - size_t startInChannel) { - memcpy( - bus_->getChannel(channelNumber)->getData() + startInChannel, - source, - std::min(sourceLength, getLength() - startInChannel) * sizeof(float)); -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h deleted file mode 100644 index 6d0b157a9..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace audioapi { - -class AudioBus; -struct AudioBufferOptions; - -/// AudioBuffer is not thread-safe. -/// Due to that fact it should be copied when passing between threads. -class AudioBuffer { - public: - explicit AudioBuffer(const AudioBufferOptions &options); - explicit AudioBuffer(std::shared_ptr bus); - - [[nodiscard]] size_t getLength() const; - [[nodiscard]] float getSampleRate() const; - [[nodiscard]] double getDuration() const; - - [[nodiscard]] int getNumberOfChannels() const; - [[nodiscard]] float *getChannelData(int channel) const; - - void copyFromChannel( - float *destination, - size_t destinationLength, - int channelNumber, - size_t startInChannel) const; - void - copyToChannel(const float *source, size_t sourceLength, int channelNumber, size_t startInChannel); - - private: - friend class AudioBufferSourceNode; - friend class AudioBufferQueueSourceNode; - friend class AudioBufferHostObject; - - std::shared_ptr bus_; -}; - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index f73a4b3c7..38d038fb7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,8 +24,8 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( playbackRateParam_ = std::make_shared( options.playbackRate, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); - playbackRateBus_ = - std::make_shared(RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); + playbackRateBus_ = std::make_shared( + RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); stretch_ = std::make_shared>(); } @@ -99,7 +99,7 @@ void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() { } void AudioBufferBaseSourceNode::processWithPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; @@ -145,7 +145,7 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( } void AudioBufferBaseSourceNode::processWithoutPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index d44a4c702..87b3e4cf0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -9,7 +9,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioParam; struct BaseAudioBufferSourceOptions; @@ -36,7 +36,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { // pitch correction std::shared_ptr> stretch_; - std::shared_ptr playbackRateBus_; + std::shared_ptr playbackRateBus_; // k-rate params std::shared_ptr detuneParam_; @@ -55,22 +55,22 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { void sendOnPositionChangedEvent(); void processWithPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess); void processWithoutPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess); float getComputedPlaybackRateValue(int framesToProcess, double time); virtual void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) = 0; virtual void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) = 0; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index c845dd40e..956a99ac0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -4,10 +4,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include @@ -35,7 +35,7 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( extraTailFrames, static_cast(channelCount_), context->getSampleRate()); tailBuffer_ = std::make_shared(audioBufferOptions); - tailBuffer_->bus_->zero(); + tailBuffer_->zero(); } isInitialized_ = true; @@ -137,8 +137,8 @@ void AudioBufferQueueSourceNode::setOnBufferEndedCallbackId(uint64_t callbackId) } } -std::shared_ptr AudioBufferQueueSourceNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr AudioBufferQueueSourceNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (auto locker = Locker::tryLock(getBufferLock())) { // no audio data to fill, zero the output and return. @@ -187,7 +187,7 @@ void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool is */ void AudioBufferQueueSourceNode::processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -201,22 +201,22 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( size_t framesLeft = offsetLength; while (framesLeft > 0) { - size_t framesToEnd = buffer->getLength() - readIndex; + size_t framesToEnd = buffer->getSize() - readIndex; size_t framesToCopy = std::min(framesToEnd, framesLeft); framesToCopy = framesToCopy > 0 ? framesToCopy : 0; assert(readIndex >= 0); assert(writeIndex >= 0); - assert(readIndex + framesToCopy <= buffer->getLength()); + assert(readIndex + framesToCopy <= buffer->getSize()); assert(writeIndex + framesToCopy <= processingBus->getSize()); - processingBus->copy(buffer->bus_.get(), readIndex, writeIndex, framesToCopy); + processingBus->copy(*buffer, readIndex, writeIndex, framesToCopy); writeIndex += framesToCopy; readIndex += framesToCopy; framesLeft -= framesToCopy; - if (readIndex >= buffer->getLength()) { + if (readIndex >= buffer->getSize()) { playedBuffersDuration_ += buffer->getDuration(); buffers_.pop(); @@ -248,7 +248,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( } void AudioBufferQueueSourceNode::processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -267,7 +267,7 @@ void AudioBufferQueueSourceNode::processWithInterpolation( bool crossBufferInterpolation = false; std::shared_ptr nextBuffer = nullptr; - if (nextReadIndex >= buffer->getLength()) { + if (nextReadIndex >= buffer->getSize()) { if (buffers_.size() > 1) { auto tempQueue = buffers_; tempQueue.pop(); @@ -280,11 +280,11 @@ void AudioBufferQueueSourceNode::processWithInterpolation( } for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - float *destination = processingBus->getChannel(i)->getData(); - const float *currentSource = buffer->bus_->getChannel(i)->getData(); + const auto destination = processingBus->getChannel(i)->span(); + const auto currentSource = buffer->getChannel(i)->span(); if (crossBufferInterpolation) { - const float *nextSource = nextBuffer->bus_->getChannel(i)->getData(); + const auto nextSource = nextBuffer->getChannel(i)->span(); float currentSample = currentSource[readIndex]; float nextSample = nextSource[nextReadIndex]; destination[writeIndex] = currentSample + factor * (nextSample - currentSample); @@ -299,7 +299,7 @@ void AudioBufferQueueSourceNode::processWithInterpolation( vReadIndex_ += std::abs(playbackRate); framesLeft -= 1; - if (vReadIndex_ >= static_cast(buffer->getLength())) { + if (vReadIndex_ >= static_cast(buffer->getSize())) { playedBuffersDuration_ += buffer->getDuration(); buffers_.pop(); @@ -311,7 +311,7 @@ void AudioBufferQueueSourceNode::processWithInterpolation( break; } - vReadIndex_ = vReadIndex_ - buffer->getLength(); + vReadIndex_ = vReadIndex_ - buffer->getSize(); data = buffers_.front(); bufferId = data.first; buffer = data.second; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index 6e017018b..12714c44d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include +#include #include #include @@ -12,7 +12,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioParam; struct BaseAudioBufferSourceOptions; @@ -37,8 +37,8 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { void setOnBufferEndedCallbackId(uint64_t callbackId); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; double getCurrentPosition() const override; @@ -59,13 +59,13 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { std::atomic onBufferEndedCallbackId_ = 0; // 0 means no callback void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) override; void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 06764ece4..ce42ba036 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -4,10 +4,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include @@ -22,7 +22,7 @@ AudioBufferSourceNode::AudioBufferSourceNode( loopStart_(options.loopStart), loopEnd_(options.loopEnd) { buffer_ = std::shared_ptr(options.buffer); - alignedBus_ = std::shared_ptr(nullptr); + alignedBus_ = std::shared_ptr(nullptr); isInitialized_ = true; } @@ -81,7 +81,7 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer if (buffer == nullptr || context == nullptr) { buffer_ = std::shared_ptr(nullptr); - alignedBus_ = std::shared_ptr(nullptr); + alignedBus_ = std::shared_ptr(nullptr); loopEnd_ = 0; return; } @@ -94,19 +94,19 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer if (pitchCorrection_) { int extraTailFrames = static_cast((getInputLatency() + getOutputLatency()) * context->getSampleRate()); - size_t totalSize = buffer_->getLength() + extraTailFrames; + size_t totalSize = buffer_->getSize() + extraTailFrames; - alignedBus_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); - alignedBus_->copy(buffer_->bus_.get(), 0, 0, buffer_->getLength()); + alignedBus_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); + alignedBus_->copy(*buffer_); - alignedBus_->zero(buffer_->getLength(), extraTailFrames); + alignedBus_->zero(buffer_->getSize(), extraTailFrames); } else { - alignedBus_ = std::make_shared(*buffer_->bus_); + alignedBus_ = std::make_shared(*buffer_); } audioBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); - playbackRateBus_ = - std::make_shared(RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + playbackRateBus_ = std::make_shared( + RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); loopEnd_ = buffer_->getDuration(); } @@ -145,8 +145,8 @@ void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { } } -std::shared_ptr AudioBufferSourceNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr AudioBufferSourceNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (auto locker = Locker::tryLock(getBufferLock())) { // No audio data to fill, zero the output and return. @@ -186,7 +186,7 @@ void AudioBufferSourceNode::sendOnLoopEndedEvent() { */ void AudioBufferSourceNode::processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -228,13 +228,11 @@ void AudioBufferSourceNode::processWithoutInterpolation( // Direction is forward, we can normally copy the data if (direction == 1) { - processingBus->copy(alignedBus_.get(), readIndex, writeIndex, framesToCopy); + processingBus->copy(*alignedBus_, readIndex, writeIndex, framesToCopy); } else { - for (int i = 0; i < framesToCopy; i += 1) { - for (int j = 0; j < processingBus->getNumberOfChannels(); j += 1) { - (*processingBus->getChannel(j))[writeIndex + i] = - (*alignedBus_->getChannel(j))[readIndex - i]; - } + for (int ch = 0; ch < processingBus->getNumberOfChannels(); ch += 1) { + processingBus->getChannel(ch)->copyReverse( + *alignedBus_->getChannel(ch), readIndex, writeIndex, framesToCopy); } } @@ -262,7 +260,7 @@ void AudioBufferSourceNode::processWithoutInterpolation( } void AudioBufferSourceNode::processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -301,8 +299,8 @@ void AudioBufferSourceNode::processWithInterpolation( } for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - float *destination = processingBus->getChannel(i)->getData(); - const float *source = alignedBus_->getChannel(i)->getData(); + auto destination = processingBus->getChannel(i)->span(); + const auto source = alignedBus_->getChannel(i)->span(); destination[writeIndex] = dsp::linearInterpolate(source, readIndex, nextReadIndex, factor); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index 806d513c9..a47f49620 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include +#include #include #include @@ -11,7 +11,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioParam; struct AudioBufferSourceOptions; @@ -41,8 +41,8 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { void setOnLoopEndedCallbackId(uint64_t callbackId); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; double getCurrentPosition() const override; @@ -55,19 +55,19 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { // User provided buffer std::shared_ptr buffer_; - std::shared_ptr alignedBus_; + std::shared_ptr alignedBus_; std::atomic onLoopEndedCallbackId_ = 0; // 0 means no callback void sendOnLoopEndedEvent(); void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) override; void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, size_t startOffset, size_t offsetLength, float playbackRate) override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp index 5a419463e..a2ee963cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp @@ -1,10 +1,10 @@ #include #include #include -#include +#include #include #include -#include +#include #if !RN_AUDIO_API_TEST #include @@ -16,7 +16,7 @@ namespace audioapi { -AudioScheduledSourceNode::AudioScheduledSourceNode(std::shared_ptr context) +AudioScheduledSourceNode::AudioScheduledSourceNode(const std::shared_ptr &context) : AudioNode(context), startTime_(-1.0), stopTime_(-1.0), @@ -71,7 +71,7 @@ void AudioScheduledSourceNode::setOnEndedCallbackId(const uint64_t callbackId) { } void AudioScheduledSourceNode::updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, @@ -167,7 +167,8 @@ void AudioScheduledSourceNode::disable() { auto onEndedCallbackId = onEndedCallbackId_.load(std::memory_order_acquire); if (onEndedCallbackId != 0) { - audioEventHandlerRegistry_->invokeHandlerWithEventBody(AudioEvent::ENDED, onEndedCallbackId, {}); + audioEventHandlerRegistry_->invokeHandlerWithEventBody( + AudioEvent::ENDED, onEndedCallbackId, {}); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 8717c2825..7f9f0f277 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -26,7 +26,7 @@ class AudioScheduledSourceNode : public AudioNode { // STOP_SCHEDULED: The node is scheduled to stop at a specific time, but is still playing. // FINISHED: The node has finished playing. enum class PlaybackState { UNSCHEDULED, SCHEDULED, PLAYING, STOP_SCHEDULED, FINISHED }; - explicit AudioScheduledSourceNode(std::shared_ptr context); + explicit AudioScheduledSourceNode(const std::shared_ptr &context); virtual void start(double when); virtual void stop(double when); @@ -51,7 +51,7 @@ class AudioScheduledSourceNode : public AudioNode { std::shared_ptr audioEventHandlerRegistry_; void updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp index 23292ad39..48a9ad884 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp @@ -1,9 +1,9 @@ #include #include #include -#include +#include #include -#include +#include #include namespace audioapi { @@ -20,8 +20,8 @@ std::shared_ptr ConstantSourceNode::getOffsetParam() const { return offsetParam_; } -std::shared_ptr ConstantSourceNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr ConstantSourceNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; @@ -44,16 +44,13 @@ std::shared_ptr ConstantSourceNode::processNode( processingBus->zero(); return processingBus; } - auto offsetBus = offsetParam_->processARateParam(framesToProcess, context->getCurrentTime()); - auto offsetChannelData = offsetBus->getChannel(0)->getData(); - for (int channel = 0; channel < processingBus->getNumberOfChannels(); ++channel) { - auto outputChannelData = processingBus->getChannel(channel)->getData(); + auto offsetChannel = + offsetParam_->processARateParam(framesToProcess, context->getCurrentTime())->getChannel(0); - std::copy( - offsetChannelData + startOffset, - offsetChannelData + startOffset + offsetLength, - outputChannelData + startOffset); + for (int channel = 0; channel < processingBus->getNumberOfChannels(); ++channel) { + processingBus->getChannel(channel)->copy( + *offsetChannel, startOffset, startOffset, offsetLength); } if (isStopScheduled()) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h index 8b83ad762..61a099055 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h @@ -9,7 +9,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct ConstantSourceOptions; class ConstantSourceNode : public AudioScheduledSourceNode { @@ -21,8 +21,8 @@ class ConstantSourceNode : public AudioScheduledSourceNode { [[nodiscard]] std::shared_ptr getOffsetParam() const; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index 36b3e5349..82a13db30 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -1,9 +1,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include @@ -27,7 +27,7 @@ OscillatorNode::OscillatorNode( periodicWave_ = context->getBasicWaveForm(type_); } - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate()); + audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate()); isInitialized_ = true; } @@ -56,8 +56,8 @@ void OscillatorNode::setPeriodicWave(const std::shared_ptr &period type_ = OscillatorType::CUSTOM; } -std::shared_ptr OscillatorNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr OscillatorNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; @@ -83,25 +83,41 @@ std::shared_ptr OscillatorNode::processNode( auto time = context->getCurrentTime() + static_cast(startOffset) * 1.0 / context->getSampleRate(); - auto detuneParamValues = detuneParam_->processARateParam(framesToProcess, time); - auto frequencyParamValues = frequencyParam_->processARateParam(framesToProcess, time); + auto detuneSpan = detuneParam_->processARateParam(framesToProcess, time)->getChannel(0)->span(); + auto freqSpan = frequencyParam_->processARateParam(framesToProcess, time)->getChannel(0)->span(); - for (size_t i = startOffset; i < offsetLength; i += 1) { - auto detuneRatio = std::pow(2.0f, detuneParamValues->getChannel(0)->getData()[i] / 1200.0f); - auto detunedFrequency = frequencyParamValues->getChannel(0)->getData()[i] * detuneRatio; - auto phaseIncrement = detunedFrequency * periodicWave_->getScale(); + const auto tableSize = static_cast(periodicWave_->getPeriodicWaveSize()); + const auto tableScale = periodicWave_->getScale(); + const auto numChannels = processingBus->getNumberOfChannels(); - float sample = periodicWave_->getSample(detunedFrequency, phase_, phaseIncrement); + auto finalPhase = phase_; - for (int j = 0; j < processingBus->getNumberOfChannels(); j += 1) { - (*processingBus->getChannel(j))[i] = sample; + for (int ch = 0; ch < numChannels; ch += 1) { + auto channelSpan = processingBus->getChannel(ch)->span(); + float currentPhase = phase_; + + for (size_t i = startOffset; i < offsetLength; i += 1) { + auto detuneRatio = detuneSpan[i] == 0 ? 1.0f : std::pow(2.0f, detuneSpan[i] / 1200.0f); + auto detunedFrequency = freqSpan[i] * detuneRatio; + auto phaseIncrement = detunedFrequency * tableScale; + + channelSpan[i] = periodicWave_->getSample(detunedFrequency, currentPhase, phaseIncrement); + + currentPhase += phaseIncrement; + + if (currentPhase >= tableSize) { + currentPhase -= tableSize; + } else if (currentPhase < 0.0f) { + currentPhase += tableSize; + } } - phase_ += phaseIncrement; - phase_ -= floor(phase_ / static_cast(periodicWave_->getPeriodicWaveSize())) * - static_cast(periodicWave_->getPeriodicWaveSize()); + if (ch == 0) { + finalPhase = currentPhase; + } } + phase_ = finalPhase; handleStopScheduled(); return processingBus; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h index cfd0908a2..1805a6204 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h @@ -12,7 +12,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; struct OscillatorOptions; class OscillatorNode : public AudioScheduledSourceNode { @@ -28,8 +28,8 @@ class OscillatorNode : public AudioScheduledSourceNode { void setPeriodicWave(const std::shared_ptr &periodicWave); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index a94ad3423..8cf50cd7e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -3,13 +3,13 @@ #include #include #include -#include +#include #include #include namespace audioapi { -RecorderAdapterNode::RecorderAdapterNode(std::shared_ptr context) +RecorderAdapterNode::RecorderAdapterNode(const std::shared_ptr &context) : AudioNode(context) { // It should be marked as initialized only after it is connected to the // recorder. Internall buffer size is based on the recorder's buffer length. @@ -33,7 +33,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // This assumes that the sample rate is the same in audio context and recorder. // (recorder is not enforcing any sample rate on the system*). This means that only // channel mixing might be required. To do so, we create an output bus with - // the desired channel count and will take advantage of the AudioBus sum method. + // the desired channel count and will take advantage of the AudioBuffer sum method. // // * any allocations required by the recorder (including this method) are during recording start // or after, which means that audio context has already setup the system in 99% of sane cases. @@ -42,7 +42,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // context output and not enforcing anything on the system output/input configuration. // A lot of words for a couple of lines of implementation :shrug: adapterOutputBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); isInitialized_ = true; } @@ -52,8 +52,8 @@ void RecorderAdapterNode::cleanup() { adapterOutputBus_.reset(); } -std::shared_ptr RecorderAdapterNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr RecorderAdapterNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (!isInitialized_) { processingBus->zero(); @@ -62,7 +62,7 @@ std::shared_ptr RecorderAdapterNode::processNode( readFrames(framesToProcess); - processingBus->sum(adapterOutputBus_.get(), ChannelInterpretation::SPEAKERS); + processingBus->sum(*adapterOutputBus_, ChannelInterpretation::SPEAKERS); return processingBus; } @@ -70,7 +70,7 @@ void RecorderAdapterNode::readFrames(const size_t framesToRead) { adapterOutputBus_->zero(); for (size_t channel = 0; channel < channelCount_; ++channel) { - buff_[channel]->read(adapterOutputBus_->getChannel(channel)->getData(), framesToRead); + buff_[channel]->read(*adapterOutputBus_->getChannel(channel), framesToRead); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h index 46f2aa541..8c2dcbaf1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h @@ -10,7 +10,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; /// @brief RecorderAdapterNode is an AudioNode which adapts push Recorder into pull graph. /// It uses RingBuffer to store audio data and AudioParam to provide audio data in pull mode. @@ -19,7 +19,7 @@ class AudioBus; /// @note it will push silence if it is not connected to any Recorder class RecorderAdapterNode : public AudioNode { public: - explicit RecorderAdapterNode(std::shared_ptr context); + explicit RecorderAdapterNode(const std::shared_ptr &context); /// @brief Initialize the RecorderAdapterNode with a buffer size and channel count. /// @note This method should be called ONLY ONCE when the buffer size is known. @@ -28,15 +28,15 @@ class RecorderAdapterNode : public AudioNode { void init(size_t bufferSize, int channelCount); void cleanup(); - int channelCount_; - // TODO: CircularOverflowableAudioBus + int channelCount_{}; + // TODO: CircularOverflowableAudioBuffer std::vector> buff_; protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; - std::shared_ptr adapterOutputBus_; + std::shared_ptr adapterOutputBus_; private: /// @brief Read audio frames from the recorder's internal circular buffer into output buss. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp index 1b6c8a212..99a252d7c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp @@ -14,12 +14,13 @@ #include #include #include -#include +#include #include #include #include #include #include +#include namespace audioapi { #if !RN_AUDIO_API_FFMPEG_DISABLED @@ -36,6 +37,7 @@ StreamerNode::StreamerNode( swrCtx_(nullptr), resampledData_(nullptr), bufferedBus_(nullptr), + bufferedBusSize_(0), audio_stream_index_(-1), maxResampledSamples_(0), processedSamples_(0) {} @@ -89,7 +91,7 @@ bool StreamerNode::initialize(const std::string &input_url) { channelCount_ = codecpar_->ch_layout.nb_channels; audioBus_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); auto [sender, receiver] = channels::spsc::channel< StreamingData, @@ -106,8 +108,8 @@ bool StreamerNode::initialize(const std::string &input_url) { #endif // RN_AUDIO_API_FFMPEG_DISABLED } -std::shared_ptr StreamerNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr StreamerNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { #if !RN_AUDIO_API_FFMPEG_DISABLED size_t startOffset = 0; @@ -135,19 +137,14 @@ std::shared_ptr StreamerNode::processNode( int alreadyProcessed = 0; if (bufferRemaining < framesToProcess) { if (bufferedBus_ != nullptr) { - for (int ch = 0; ch < processingBus->getNumberOfChannels(); ch++) { - memcpy( - processingBus->getChannel(ch)->getData(), - bufferedBus_->getChannel(ch)->getData() + processedSamples_, - bufferRemaining * sizeof(float)); - } + processingBus->copy(*bufferedBus_, processedSamples_, 0, bufferRemaining); framesToProcess -= bufferRemaining; alreadyProcessed += bufferRemaining; } StreamingData data; auto res = receiver_.try_receive(data); if (res == channels::spsc::ResponseStatus::SUCCESS) { - bufferedBus_ = std::make_shared(std::move(data.bus)); + bufferedBus_ = std::make_shared(std::move(data.bus)); bufferedBusSize_ = data.size; processedSamples_ = 0; } else { @@ -155,12 +152,7 @@ std::shared_ptr StreamerNode::processNode( } } if (bufferedBus_ != nullptr) { - for (int ch = 0; ch < processingBus->getNumberOfChannels(); ch++) { - memcpy( - processingBus->getChannel(ch)->getData() + alreadyProcessed, - bufferedBus_->getChannel(ch)->getData() + processedSamples_, - framesToProcess * sizeof(float)); - } + processingBus->copy(*bufferedBus_, processedSamples_, alreadyProcessed, framesToProcess); processedSamples_ += framesToProcess; } #endif // RN_AUDIO_API_FFMPEG_DISABLED @@ -230,7 +222,7 @@ void StreamerNode::streamAudio() { bool StreamerNode::processFrameWithResampler( AVFrame *frame, - std::shared_ptr context) { + const std::shared_ptr &context) { // Check if we need to reallocate the resampled buffer int out_samples = swr_get_out_samples(swrCtx_, frame->nb_samples); if (out_samples > maxResampledSamples_) { @@ -267,16 +259,18 @@ bool StreamerNode::processFrameWithResampler( if (this->isFinished()) { return true; } - auto bus = AudioBus( - static_cast(converted_samples), + + AudioBuffer buffer = AudioBuffer( + converted_samples, codecCtx_->ch_layout.nb_channels, context->getSampleRate()); + for (int ch = 0; ch < codecCtx_->ch_layout.nb_channels; ch++) { auto *src = reinterpret_cast(resampledData_[ch]); - float *dst = bus.getChannel(ch)->getData(); - memcpy(dst, src, converted_samples * sizeof(float)); + buffer.getChannel(ch)->copy(src, 0, 0, converted_samples); } - StreamingData data{std::move(bus), static_cast(converted_samples)}; + + StreamingData data{std::move(buffer), static_cast(converted_samples)}; sender_.send(std::move(data)); return true; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h index 7ca9928a7..9700bc565 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h @@ -11,7 +11,7 @@ #pragma once #include -#include +#include #if !RN_AUDIO_API_FFMPEG_DISABLED extern "C" { @@ -40,10 +40,10 @@ static constexpr bool VERBOSE = false; static constexpr int CHANNEL_CAPACITY = 32; struct StreamingData { - audioapi::AudioBus bus; + audioapi::AudioBuffer bus; size_t size; StreamingData() = default; - StreamingData(audioapi::AudioBus b, size_t s) : bus(b), size(s) {} + StreamingData(audioapi::AudioBuffer b, size_t s) : bus(b), size(s) {} StreamingData(const StreamingData &data) : bus(data.bus), size(data.size) {} StreamingData(StreamingData &&data) noexcept : bus(std::move(data.bus)), size(data.size) {} StreamingData &operator=(const StreamingData &data) { @@ -58,7 +58,7 @@ struct StreamingData { namespace audioapi { -class AudioBus; +class AudioBuffer; struct StreamerOptions; class StreamerNode : public AudioScheduledSourceNode { @@ -82,8 +82,8 @@ class StreamerNode : public AudioScheduledSourceNode { } protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: @@ -97,9 +97,9 @@ class StreamerNode : public AudioScheduledSourceNode { SwrContext *swrCtx_; uint8_t **resampledData_; // weird ffmpeg way of using raw byte pointers for resampled data - std::shared_ptr bufferedBus_; // audio bus for buffering hls frames - size_t bufferedBusSize_; // size of currently buffered bus - int audio_stream_index_; // index of the audio stream channel in the input + std::shared_ptr bufferedBus_; // audio bus for buffering hls frames + size_t bufferedBusSize_; // size of currently buffered bus + int audio_stream_index_; // index of the audio stream channel in the input int maxResampledSamples_; size_t processedSamples_; @@ -128,7 +128,7 @@ class StreamerNode : public AudioScheduledSourceNode { * @param frame The AVFrame to resample * @return true if successful, false otherwise */ - bool processFrameWithResampler(AVFrame *frame, std::shared_ptr context); + bool processFrameWithResampler(AVFrame *frame, const std::shared_ptr &context); /** * @brief Thread function to continuously read and process audio frames diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp index a243742a2..5fdad711c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp @@ -6,7 +6,7 @@ namespace audioapi { WorkletSourceNode::WorkletSourceNode( - std::shared_ptr context, + const std::shared_ptr &context, WorkletsRunner &&workletRunner) : AudioScheduledSourceNode(context), workletRunner_(std::move(workletRunner)) { isInitialized_ = true; @@ -15,13 +15,12 @@ WorkletSourceNode::WorkletSourceNode( size_t outputChannelCount = this->getChannelCount(); outputBuffsHandles_.resize(outputChannelCount); for (size_t i = 0; i < outputChannelCount; ++i) { - auto audioArray = std::make_shared(RENDER_QUANTUM_SIZE); - outputBuffsHandles_[i] = std::make_shared(audioArray); + outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); } } -std::shared_ptr WorkletSourceNode::processNode( - const std::shared_ptr &processingBus, +std::shared_ptr WorkletSourceNode::processNode( + const std::shared_ptr &processingBus, int framesToProcess) { if (isUnscheduled() || isFinished() || !isEnabled()) { processingBus->zero(); @@ -36,7 +35,13 @@ std::shared_ptr WorkletSourceNode::processNode( processingBus->zero(); return processingBus; } - updatePlaybackInfo(processingBus, framesToProcess, startOffset, nonSilentFramesToProcess, context->getSampleRate(), context->getCurrentSampleFrame()); + updatePlaybackInfo( + processingBus, + framesToProcess, + startOffset, + nonSilentFramesToProcess, + context->getSampleRate(), + context->getCurrentSampleFrame()); if (nonSilentFramesToProcess == 0) { processingBus->zero(); @@ -46,7 +51,8 @@ std::shared_ptr WorkletSourceNode::processNode( size_t outputChannelCount = processingBus->getNumberOfChannels(); auto result = workletRunner_.executeOnRuntimeSync( - [this, nonSilentFramesToProcess, startOffset, time = context->getCurrentTime()](jsi::Runtime &rt) { + [this, nonSilentFramesToProcess, startOffset, time = context->getCurrentTime()]( + jsi::Runtime &rt) { auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size()); for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) { auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]); @@ -70,13 +76,10 @@ std::shared_ptr WorkletSourceNode::processNode( return processingBus; } - // Copy the processed data back to the AudioBus - for (size_t i = 0; i < outputChannelCount; ++i) { - float *channelData = processingBus->getChannel(i)->getData(); - memcpy( - channelData + startOffset, - outputBuffsHandles_[i]->data(), - nonSilentFramesToProcess * sizeof(float)); + // Copy the processed data back to the AudioBuffer + for (int i = 0; i < outputChannelCount; ++i) { + processingBus->getChannel(i)->copy( + *outputBuffsHandles_[i], 0, startOffset, nonSilentFramesToProcess); } handleStopScheduled(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h index ce7e01515..93b098767 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h @@ -3,9 +3,9 @@ #include #include #include -#include #include -#include +#include +#include #include #include @@ -22,8 +22,8 @@ class WorkletSourceNode : public AudioScheduledSourceNode { : AudioScheduledSourceNode(context) {} protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return processingBus; } @@ -33,12 +33,12 @@ class WorkletSourceNode : public AudioScheduledSourceNode { class WorkletSourceNode : public AudioScheduledSourceNode { public: explicit WorkletSourceNode( - std::shared_ptr context, + const std::shared_ptr &context, WorkletsRunner &&workletRunner); protected: - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp index eb02e3b6b..f5bc89fcb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp @@ -1,22 +1,22 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include namespace audioapi { -AudioGraphManager::Event::Event(Event &&other) { +AudioGraphManager::Event::Event(Event &&other) noexcept { *this = std::move(other); } -AudioGraphManager::Event &AudioGraphManager::Event::operator=(Event &&other) { +AudioGraphManager::Event &AudioGraphManager::Event::operator=(Event &&other) noexcept { if (this != &other) { // Clean up current resources this->~Event(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h index 60f5f1f5f..2017ce49d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h @@ -55,8 +55,8 @@ class AudioGraphManager { EventPayloadType payloadType; EventPayload payload; - Event(Event &&other); - Event &operator=(Event &&other); + Event(Event &&other) noexcept; + Event &operator=(Event &&other) noexcept; Event() : type(ConnectionType::CONNECT), payloadType(EventPayloadType::NODES), payload() {} ~Event(); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioParamEventQueue.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioParamEventQueue.h index e69514210..37abe6ef8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioParamEventQueue.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioParamEventQueue.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include namespace audioapi { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp index b7d467140..4de47a7a3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -56,27 +56,26 @@ void AudioRecorderCallback::emitAudioData(bool flush) { } while (circularBus_[0]->getNumberOfAvailableFrames() >= sizeLimit) { - auto bus = std::make_shared(sizeLimit, channelCount_, sampleRate_); + auto bus = std::make_shared(sizeLimit, channelCount_, sampleRate_); for (int i = 0; i < channelCount_; ++i) { - auto *outputChannel = bus->getChannel(i)->getData(); - circularBus_[i]->pop_front(outputChannel, sizeLimit); + circularBus_[i]->pop_front(*bus->getChannel(i), sizeLimit); } invokeCallback(bus, static_cast(sizeLimit)); } } -void AudioRecorderCallback::invokeCallback(const std::shared_ptr &bus, int numFrames) { - auto audioBuffer = std::make_shared(bus); - auto audioBufferHostObject = std::make_shared(audioBuffer); +void AudioRecorderCallback::invokeCallback(const std::shared_ptr &bus, int numFrames) { + auto audioBufferHostObject = std::make_shared(bus); std::unordered_map eventPayload = {}; eventPayload.insert({"buffer", audioBufferHostObject}); eventPayload.insert({"numFrames", numFrames}); if (audioEventHandlerRegistry_) { - audioEventHandlerRegistry_->invokeHandlerWithEventBody(AudioEvent::AUDIO_READY, callbackId_, eventPayload); + audioEventHandlerRegistry_->invokeHandlerWithEventBody( + AudioEvent::AUDIO_READY, callbackId_, eventPayload); } } @@ -98,7 +97,8 @@ void AudioRecorderCallback::invokeOnErrorCallback(const std::string &message) { } std::unordered_map eventPayload = {{"message", message}}; - audioEventHandlerRegistry_->invokeHandlerWithEventBody(AudioEvent::RECORDER_ERROR, callbackId, eventPayload); + audioEventHandlerRegistry_->invokeHandlerWithEventBody( + AudioEvent::RECORDER_ERROR, callbackId, eventPayload); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h index d35da9683..0d3d7656a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h @@ -8,7 +8,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioArray; class CircularAudioArray; class AudioEventHandlerRegistry; @@ -26,7 +26,7 @@ class AudioRecorderCallback { virtual void cleanup() = 0; void emitAudioData(bool flush = false); - void invokeCallback(const std::shared_ptr &bus, int numFrames); + void invokeCallback(const std::shared_ptr &bus, int numFrames); void setOnErrorCallback(uint64_t callbackId); void clearOnErrorCallback(); @@ -45,7 +45,7 @@ class AudioRecorderCallback { std::shared_ptr audioEventHandlerRegistry_; - // TODO: CircularAudioBus + // TODO: CircularAudioBuffer std::vector> circularBus_; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp index 3c84ebc50..1f650575e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp @@ -1,9 +1,8 @@ -#include #include #include #include #include -#include +#include #include #include #include @@ -12,12 +11,12 @@ namespace audioapi { std::vector AudioStretcher::castToInt16Buffer(AudioBuffer &buffer) { const size_t numChannels = buffer.getNumberOfChannels(); - const size_t numFrames = buffer.getLength(); + const size_t numFrames = buffer.getSize(); std::vector int16Buffer(numFrames * numChannels); - for (size_t ch = 0; ch < numChannels; ++ch) { - const float *channelData = buffer.getChannelData(ch); + for (int ch = 0; ch < numChannels; ++ch) { + auto channelData = buffer.getChannel(ch)->span(); for (size_t i = 0; i < numFrames; ++i) { int16Buffer[i * numChannels + ch] = floatToInt16(channelData[i]); } @@ -31,7 +30,7 @@ std::shared_ptr AudioStretcher::changePlaybackSpeed( float playbackSpeed) { const float sampleRate = buffer.getSampleRate(); const size_t outputChannels = buffer.getNumberOfChannels(); - const size_t numFrames = buffer.getLength(); + const size_t numFrames = buffer.getSize(); if (playbackSpeed == 1.0f) { return std::make_shared(buffer); @@ -60,16 +59,16 @@ std::shared_ptr AudioStretcher::changePlaybackSpeed( stretchedBuffer.resize(outputFrames * outputChannels); stretch_deinit(stretcher); - auto audioBus = std::make_shared(outputFrames, outputChannels, sampleRate); + auto audioBus = std::make_shared(outputFrames, outputChannels, sampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = int16ToFloat(stretchedBuffer[i * outputChannels + ch]); } } - return std::make_shared(audioBus); + return audioBus; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.h index 2fbff057e..94d7894c6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.h @@ -5,7 +5,7 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioBuffer; class AudioStretcher { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.cpp deleted file mode 100644 index 32612d66c..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -namespace audioapi { - -ParamChangeEvent::ParamChangeEvent( - double startTime, - double endTime, - float startValue, - float endValue, - std::function &&calculateValue, - ParamChangeEventType type) - : startTime_(startTime), - endTime_(endTime), - calculateValue_(std::move(calculateValue)), - startValue_(startValue), - endValue_(endValue), - type_(type) {} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.hpp similarity index 84% rename from packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.h rename to packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.hpp index afe93f0f1..506109959 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/ParamChangeEvent.hpp @@ -17,18 +17,25 @@ class ParamChangeEvent { float startValue, float endValue, std::function &&calculateValue, - ParamChangeEventType type); + ParamChangeEventType type) + : startTime_(startTime), + endTime_(endTime), + calculateValue_(std::move(calculateValue)), + startValue_(startValue), + endValue_(endValue), + type_(type) {} ParamChangeEvent(const ParamChangeEvent &other) = delete; ParamChangeEvent &operator=(const ParamChangeEvent &other) = delete; - explicit ParamChangeEvent(ParamChangeEvent &&other) noexcept + ParamChangeEvent(ParamChangeEvent &&other) noexcept : startTime_(other.startTime_), endTime_(other.endTime_), calculateValue_(std::move(other.calculateValue_)), startValue_(other.startValue_), endValue_(other.endValue_), type_(other.type_) {} + ParamChangeEvent &operator=(ParamChangeEvent &&other) noexcept { if (this != &other) { startTime_ = other.startTime_; @@ -72,11 +79,11 @@ class ParamChangeEvent { } private: - double startTime_; - double endTime_; + double startTime_ = 0.0; + double endTime_ = 0.0; std::function calculateValue_; - float startValue_; - float endValue_; + float startValue_ = 0.0f; + float endValue_ = 0.0f; ParamChangeEventType type_; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.cpp deleted file mode 100644 index cdfc33d63..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -namespace audioapi::dsp { -size_t timeToSampleFrame(double time, float sampleRate) { - return static_cast(time * sampleRate); -} - -double sampleFrameToTime(int sampleFrame, float sampleRate) { - return static_cast(sampleFrame) / sampleRate; -} - -float linearInterpolate(const float *source, size_t firstIndex, size_t secondIndex, float factor) { - if (firstIndex == secondIndex && firstIndex >= 1) { - return source[firstIndex] + factor * (source[firstIndex] - source[firstIndex - 1]); - } - - return source[firstIndex] + factor * (source[secondIndex] - source[firstIndex]); -} - -float linearToDecibels(float value) { - return 20 * log10f(value); -} - -float decibelsToLinear(float value) { - return pow(10, value / 20); -} -} // namespace audioapi::dsp \ No newline at end of file diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.h deleted file mode 100644 index f636021a9..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace audioapi::dsp { - -size_t timeToSampleFrame(double time, float sampleRate); -double sampleFrameToTime(int sampleFrame, float sampleRate); - -float linearInterpolate(const float *source, size_t firstIndex, size_t secondIndex, float factor); - -float linearToDecibels(float value); -float decibelsToLinear(float value); -} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp new file mode 100644 index 000000000..41bc36e42 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + +namespace audioapi::dsp { + +[[nodiscard]] inline size_t timeToSampleFrame(double time, float sampleRate) { + return static_cast(time * sampleRate); +} + +[[nodiscard]] inline double sampleFrameToTime(int sampleFrame, float sampleRate) { + return static_cast(sampleFrame) / sampleRate; +} + +[[nodiscard]] inline float linearInterpolate( + std::span source, + size_t firstIndex, + size_t secondIndex, + float factor) { + + if (firstIndex == secondIndex && firstIndex >= 1) { + return source[firstIndex] + factor * (source[firstIndex] - source[firstIndex - 1]); + } + + return std::lerp(source[firstIndex], source[secondIndex], factor); +} + +[[nodiscard]] inline float linearToDecibels(float value) { + return 20 * log10f(value); +} + +[[nodiscard]] inline float decibelsToLinear(float value) { + return static_cast(pow(10, value / 20)); +} +} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp index 812cbe34c..5d23ef200 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp @@ -5,10 +5,10 @@ #include #endif -#include #include #include #include +#include #include #include #include @@ -17,17 +17,16 @@ namespace audioapi { Convolver::Convolver() - : _blockSize(0), + : _trueSegmentCount(0), + _blockSize(0), _segSize(0), _segCount(0), _fftComplexSize(0), _segments(), _segmentsIR(), - _fftBuffer(0), _fft(nullptr), _preMultiplied(), - _current(0), - _inputBuffer(0) {} + _current(0) {} void Convolver::reset() { _blockSize = 0; @@ -39,8 +38,8 @@ void Convolver::reset() { _segments.clear(); _segmentsIR.clear(); _preMultiplied.clear(); - _fftBuffer.zero(); - _inputBuffer.zero(); + _fftBuffer->zero(); + _inputBuffer->zero(); } bool Convolver::init(size_t blockSize, const audioapi::AudioArray &ir, size_t irLen) { @@ -71,7 +70,7 @@ bool Convolver::init(size_t blockSize, const audioapi::AudioArray &ir, size_t ir // complex-conjugate symmetricity _fftComplexSize = _segSize / 2 + 1; _fft = std::make_shared(static_cast(_segSize)); - _fftBuffer.resize(_segSize); + _fftBuffer = std::make_unique(_segSize); // segments preparation for (int i = 0; i < _segCount; ++i) { @@ -86,18 +85,18 @@ bool Convolver::init(size_t blockSize, const audioapi::AudioArray &ir, size_t ir const size_t samplesToCopy = std::min(_blockSize, remainingSamples); if (samplesToCopy > 0) { - memcpy(_fftBuffer.getData(), ir.getData() + i * _blockSize, samplesToCopy * sizeof(float)); + _fftBuffer->copy(ir, i * _blockSize, 0, samplesToCopy); } // Each sub filter is zero-padded to length 2B and transformed using a // 2B-point real-to-complex FFT. - memset(_fftBuffer.getData() + _blockSize, 0, _blockSize * sizeof(float)); - _fft->doFFT(_fftBuffer.getData(), segment); + _fftBuffer->zero(_blockSize, _blockSize); + _fft->doFFT(*_fftBuffer, segment); segment.at(0).imag(0.0f); // ensure DC component is real _segmentsIR.push_back(segment); } _preMultiplied = aligned_vec_complex(_fftComplexSize); - _inputBuffer.resize(_segSize); + _inputBuffer = std::make_unique(_segSize); _current = 0; return true; @@ -165,12 +164,12 @@ void pairwise_complex_multiply_fast( #endif } -void Convolver::process(float *data, float *outputData) { +void Convolver::process(const AudioArray &input, AudioArray &output) { // The input buffer acts as a 2B-point sliding window of the input signal. // With each new input block, the right half of the input buffer is shifted // to the left and the new block is stored in the right half. - memmove(_inputBuffer.getData(), _inputBuffer.getData() + _blockSize, _blockSize * sizeof(float)); - memcpy(_inputBuffer.getData() + _blockSize, data, _blockSize * sizeof(float)); + _inputBuffer->copy(*_inputBuffer, _blockSize, 0, _blockSize); + _inputBuffer->copy(input, 0, _blockSize, _blockSize); // All contents (DFT spectra) in the FDL are shifted up by one slot. _current = (_current > 0) ? _current - 1 : _segCount - 1; @@ -178,7 +177,7 @@ void Convolver::process(float *data, float *outputData) { // resulting in B+1 complex-conjugate symmetric DFT coefficients. The // result is stored in the first FDL slot. // _current marks first FDL slot, which is the current input block. - _fft->doFFT(_inputBuffer.getData(), _segments[_current]); + _fft->doFFT(*_inputBuffer, _segments[_current]); _segments[_current][0].imag(0.0f); // ensure DC component is real // The P sub filter spectra are pairwisely multiplied with the input spectra @@ -194,8 +193,8 @@ void Convolver::process(float *data, float *outputData) { // Of the accumulated spectral convolutions, an 2B-point complex-to-real // IFFT is computed. From the resulting 2B samples, the left half is // discarded and the right half is returned as the next output block. - _fft->doInverseFFT(_preMultiplied, _fftBuffer.getData()); + _fft->doInverseFFT(_preMultiplied, *_fftBuffer); - memcpy(outputData, _fftBuffer.getData() + _blockSize, _blockSize * sizeof(float)); + output.copy(*_fftBuffer, _blockSize, 0, _blockSize); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.h index a356bf840..8d7b966cb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.h @@ -19,9 +19,9 @@ class Convolver { public: Convolver(); bool init(size_t blockSize, const AudioArray &ir, size_t irLen); - void process(float *inputData, float *outputData); + void process(const AudioArray &input, AudioArray &output); void reset(); - inline size_t getSegCount() const { + [[nodiscard]] inline size_t getSegCount() const { return _trueSegmentCount; } @@ -33,11 +33,11 @@ class Convolver { size_t _fftComplexSize; std::vector _segments; std::vector _segmentsIR; - AudioArray _fftBuffer; + std::unique_ptr _fftBuffer; std::shared_ptr _fft; aligned_vec_complex _preMultiplied; size_t _current; - AudioArray _inputBuffer; + std::unique_ptr _inputBuffer; friend void pairwise_complex_multiply_fast( const aligned_vec_complex &ir, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp index c90f9bc9d..80a262f18 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp @@ -44,7 +44,7 @@ namespace audioapi { Resampler::Resampler(int maxBlockSize, int kernelSize): kernelSize_(kernelSize), kernel_(std::make_shared(kernelSize)), - stateBuffer_(std::make_shared(2 * maxBlockSize)) { + stateBuffer_(std::make_unique(2 * maxBlockSize)) { stateBuffer_->zero(); } @@ -58,35 +58,6 @@ float Resampler::computeBlackmanWindow(double x) const { return static_cast(a0 - a1 * std::cos(2.0 * PI * n) + a2 * std::cos(4.0 * PI * n)); } -float Resampler::computeConvolution(const float *stateStart, const float *kernelStart) const { - float sum = 0.0f; - int k = 0; - -#ifdef __ARM_NEON - float32x4_t vSum = vdupq_n_f32(0.0f); - - // process 4 samples at a time - for (; k <= kernelSize_ - 4; k += 4) { - float32x4_t vState = vld1q_f32(stateStart + k); - float32x4_t vKernel = vld1q_f32(kernelStart + k); - - // fused multiply-add: vSum += vState * vKernel - vSum = vmlaq_f32(vSum, vState, vKernel); - } - - // horizontal reduction: Sum the 4 lanes of vSum into a single float - sum += vgetq_lane_f32(vSum, 0); - sum += vgetq_lane_f32(vSum, 1); - sum += vgetq_lane_f32(vSum, 2); - sum += vgetq_lane_f32(vSum, 3); -#endif - for (; k < kernelSize_; ++k) { - sum += stateStart[k] * kernelStart[k]; - } - - return sum; -} - void Resampler::reset() { if (stateBuffer_) { stateBuffer_->zero(); @@ -98,7 +69,6 @@ UpSampler::UpSampler(int maxBlockSize, int kernelSize) : Resampler(maxBlockSize, } void UpSampler::initializeKernel() { - auto kData = kernel_->getData(); int halfSize = kernelSize_ / 2; double subSampleOffset = -0.5; @@ -111,39 +81,33 @@ void UpSampler::initializeKernel() { double sinc = (std::abs(x) < 1e-9) ? 1.0 : std::sin(x * PI) / (x * PI); // apply window in order smooth out the edges, because sinc extends to infinity in both directions - kData[i] = static_cast(sinc * computeBlackmanWindow(i - subSampleOffset)); + (*kernel_)[i] = static_cast(sinc * computeBlackmanWindow(i - subSampleOffset)); } // reverse kernel to match convolution implementation - std::reverse(kData, kData + kernelSize_); + kernel_->reverse(); } int UpSampler::process( - const std::shared_ptr &input, - const std::shared_ptr &output, + AudioArray& input, + AudioArray& output, int framesToProcess) { - - const float *inputData = input->getData(); - float *outputData = output->getData(); - float *state = stateBuffer_->getData(); - const float *kernel = kernel_->getData(); - // copy new input [ HISTORY | NEW DATA ] - std::memcpy(state + kernelSize_, inputData, framesToProcess * sizeof(float)); + stateBuffer_->copy(input, 0, kernelSize_, framesToProcess); int halfKernel = kernelSize_ / 2; for (int i = 0; i < framesToProcess; ++i) { // direct copy for even samples with half kernel latency compensation - outputData[2 * i] = state[kernelSize_ + i - halfKernel]; + output[2 * i] = (*stateBuffer_)[kernelSize_ + i - halfKernel]; // convolution for odd samples // symmetric Linear Phase filter has latency of half kernel size - outputData[2 * i + 1] = computeConvolution(&state[i + 1], kernel); + output[2 * i + 1] = stateBuffer_->computeConvolution(*kernel_, i + 1); } // move new data to history [ NEW DATA | EMPTY ] - std::memmove(state, state + framesToProcess, kernelSize_ * sizeof(float)); + stateBuffer_->copy(*stateBuffer_, framesToProcess, 0, kernelSize_); return framesToProcess * 2; } @@ -153,7 +117,6 @@ DownSampler::DownSampler(int maxBlockSize, int kernelSize) : Resampler(maxBlockS } void DownSampler::initializeKernel() { - auto kData = kernel_->getData(); int halfSize = kernelSize_ / 2; for (int i = 0; i < kernelSize_; ++i) { @@ -166,34 +129,29 @@ void DownSampler::initializeKernel() { sinc *= 0.5; // apply window in order smooth out the edges, because sinc extends to infinity in both directions - kData[i] = static_cast(sinc * computeBlackmanWindow(i)); + (*kernel_)[i] = static_cast(sinc * computeBlackmanWindow(i)); } // reverse kernel to match convolution implementation - std::reverse(kData, kData + kernelSize_); + kernel_->reverse(); } int DownSampler::process( - const std::shared_ptr &input, - const std::shared_ptr &output, + AudioArray& input, + AudioArray& output, int framesToProcess) { - const float *inputData = input->getData(); - float *outputData = output->getData(); - float *state = stateBuffer_->getData(); - const float *kernel = kernel_->getData(); - // copy new input [ HISTORY | NEW DATA ] - std::memcpy(state + kernelSize_, inputData, framesToProcess * sizeof(float)); + stateBuffer_->copy(input, 0, kernelSize_, framesToProcess); auto outputCount = framesToProcess / 2; for (int i = 0; i < outputCount; ++i) { // convolution for downsampled samples - outputData[i] = computeConvolution(&state[2 * i + 1], kernel); + output[i] = stateBuffer_->computeConvolution(*kernel_, 2 * i + 1); } // move new data to history [ NEW DATA | EMPTY ] - std::memmove(state, state + framesToProcess, kernelSize_ * sizeof(float)); + stateBuffer_->copy(*stateBuffer_, framesToProcess, 0, kernelSize_); return outputCount; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.h index 4a99f9f45..08b748f0c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.h @@ -16,22 +16,18 @@ class Resampler { Resampler(int maxBlockSize, int kernelSize); virtual ~Resampler() = default; - virtual int process( - const std::shared_ptr &input, - const std::shared_ptr &output, - int framesToProcess) = 0; + virtual int process(AudioArray &input, AudioArray &output, int framesToProcess) = 0; void reset(); protected: [[nodiscard]] float computeBlackmanWindow(double x) const; - float computeConvolution(const float *stateStart, const float *kernelStart) const; virtual void initializeKernel() = 0; int kernelSize_; std::shared_ptr kernel_; // [ HISTORY | NEW DATA ] - std::shared_ptr stateBuffer_; + std::unique_ptr stateBuffer_; }; class UpSampler : public Resampler { @@ -39,10 +35,7 @@ class UpSampler : public Resampler { UpSampler(int maxBlockSize, int kernelSize); // N -> 2N - int process( - const std::shared_ptr &input, - const std::shared_ptr &output, - int framesToProcess) override; + int process(AudioArray &input, AudioArray &output, int framesToProcess) override; protected: void initializeKernel() final; @@ -53,10 +46,7 @@ class DownSampler : public Resampler { DownSampler(int maxBlockSize, int kernelSize); // N -> N / 2 - int process( - const std::shared_ptr &input, - const std::shared_ptr &output, - int framesToProcess) override; + int process(AudioArray &input, AudioArray &output, int framesToProcess) override; protected: void initializeKernel() final; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp index 4e0ffd3fa..379dd3dfd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp @@ -23,7 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.cpp index 9ee3c7207..cd846c2ae 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include @@ -46,7 +46,7 @@ void WaveShaper::setOversample(OverSampleType type) { } } -void WaveShaper::process(const std::shared_ptr &channelData, int framesToProcess) { +void WaveShaper::process(AudioArray &channelData, int framesToProcess) { if (curve_ == nullptr) { return; } @@ -66,40 +66,39 @@ void WaveShaper::process(const std::shared_ptr &channelData, int fra } // based on https://webaudio.github.io/web-audio-api/#WaveShaperNode -void WaveShaper::processNone(const std::shared_ptr &channelData, int framesToProcess) { - auto curveArray = curve_->getData(); +void WaveShaper::processNone(AudioArray &channelData, int framesToProcess) { auto curveSize = curve_->getSize(); - auto data = channelData->getData(); - for (int i = 0; i < framesToProcess; i++) { - float v = (static_cast(curveSize) - 1) * 0.5f * (data[i] + 1.0f); + float v = (static_cast(curveSize) - 1) * 0.5f * (channelData[i] + 1.0f); if (v <= 0) { - data[i] = curveArray[0]; + channelData[i] = (*curve_)[0]; } else if (v >= static_cast(curveSize) - 1) { - data[i] = curveArray[curveSize - 1]; + channelData[i] = (*curve_)[curveSize - 1]; } else { auto k = std::floor(v); auto f = v - k; auto kIndex = static_cast(k); - data[i] = (1 - f) * curveArray[kIndex] + f * curveArray[kIndex + 1]; + channelData[i] = (1 - f) * (*curve_)[kIndex] + f * (*curve_)[kIndex + 1]; } } } -void WaveShaper::process2x(const std::shared_ptr &channelData, int framesToProcess) { - auto outputFrames = upSampler_->process(channelData, tempBuffer2x_, framesToProcess); - processNone(tempBuffer2x_, outputFrames); - downSampler_->process(tempBuffer2x_, channelData, outputFrames); +void WaveShaper::process2x(AudioArray &channelData, int framesToProcess) { + auto outputFrames = upSampler_->process(channelData, *tempBuffer2x_, framesToProcess); + processNone(*tempBuffer2x_, outputFrames); + downSampler_->process(*tempBuffer2x_, channelData, outputFrames); } -void WaveShaper::process4x(const std::shared_ptr &channelData, int framesToProcess) { - auto upSamplerOutputFrames = upSampler_->process(channelData, tempBuffer2x_, framesToProcess); - auto upSampler2OutputFrames = upSampler2_->process(tempBuffer2x_, tempBuffer4x_, upSamplerOutputFrames); - processNone(tempBuffer4x_, upSampler2OutputFrames); - auto downSampler2OutputFrames = downSampler2_->process(tempBuffer4x_, tempBuffer2x_, upSampler2OutputFrames); - downSampler_->process(tempBuffer2x_, channelData, downSampler2OutputFrames); +void WaveShaper::process4x(AudioArray &channelData, int framesToProcess) { + auto upSamplerOutputFrames = upSampler_->process(channelData, *tempBuffer2x_, framesToProcess); + auto upSampler2OutputFrames = + upSampler2_->process(*tempBuffer2x_, *tempBuffer4x_, upSamplerOutputFrames); + processNone(*tempBuffer4x_, upSampler2OutputFrames); + auto downSampler2OutputFrames = + downSampler2_->process(*tempBuffer4x_, *tempBuffer2x_, upSampler2OutputFrames); + downSampler_->process(*tempBuffer2x_, channelData, downSampler2OutputFrames); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.h index fd5a04b44..9db0bffd5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/WaveShaper.h @@ -11,14 +11,14 @@ namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioArray; class WaveShaper { public: explicit WaveShaper(const std::shared_ptr &curve); - void process(const std::shared_ptr &channelData, int framesToProcess); + void process(AudioArray &channelData, int framesToProcess); void setCurve(const std::shared_ptr &curve); void setOversample(OverSampleType type); @@ -38,9 +38,9 @@ class WaveShaper { std::shared_ptr tempBuffer2x_; std::shared_ptr tempBuffer4x_; - void processNone(const std::shared_ptr &channelData, int framesToProcess); - void process2x(const std::shared_ptr &channelData, int framesToProcess); - void process4x(const std::shared_ptr &channelData, int framesToProcess); + void processNone(AudioArray &channelData, int framesToProcess); + void process2x(AudioArray &channelData, int framesToProcess); + void process4x(AudioArray &channelData, int framesToProcess); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.cpp deleted file mode 100644 index 79f7948ec..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include - -namespace audioapi::dsp { - -void WindowFunction::forcePerfectReconstruction(float *data, int windowLength, int interval) { - for (int i = 0; i < interval; ++i) { - float sum2 = 0; - - for (int index = i; index < windowLength; index += interval) { - sum2 += data[index] * data[index]; - } - - float factor = 1 / std::sqrt(sum2); - - for (int index = i; index < windowLength; index += interval) { - data[index] *= factor; - } - } -} - -void Hann::apply(float *data, int size) const { - for (int i = 0; i < size; ++i) { - auto x = static_cast(i) / static_cast(size - 1); - auto window = 0.5f - 0.5f * cos(2 * PI * x); - data[i] = window * amplitude_; - } -} - -void Blackman::apply(float *data, int size) const { - for (int i = 0; i < size; ++i) { - auto x = static_cast(i) / static_cast(size - 1); - auto window = 0.42f - 0.5f * cos(2 * PI * x) + 0.08f * cos(4 * PI * x); - data[i] = window * amplitude_; - } -} - -void Kaiser::apply(float *data, int size) const { - for (int i = 0; i < size; ++i) { - auto r = static_cast(2 * i + 1) / static_cast(size) - 1.0f; - auto arg = std::sqrt(1 - r * r); - data[i] = bessel0(beta_ * arg) * invB0_ * amplitude_; - } -} - -float Kaiser::bandwidthToBeta(float bandwidth, bool heuristicOptimal) { - if (heuristicOptimal) { // Heuristic based on numerical search - return bandwidth + 8.0f / (bandwidth + 3.0f) * (bandwidth + 3.0f) + - 0.25f * std::max(3.0f - bandwidth, 0.0f); - } - - bandwidth = std::max(bandwidth, 2.0f); - auto alpha = std::sqrt(bandwidth * bandwidth * 0.25f - 1.0f); - return alpha * PI; -} - -void ApproximateConfinedGaussian::apply(float *data, int size) const { - auto offsetScale = getGaussian(1.0f) / (getGaussian(3.0f) + getGaussian(-1.0f)); - auto norm = 1 / (getGaussian(1.0f) - 2 * offsetScale * getGaussian(2.0f)); - for (int i = 0; i < size; ++i) { - auto r = static_cast(2 * i + 1) / static_cast(size) - 1.0f; - data[i] = norm * (getGaussian(r) - offsetScale * (getGaussian(r - 2) + getGaussian(r + 2))); - } -} - -float ApproximateConfinedGaussian::bandwidthToSigma(float bandwidth) { - return 0.3f / std::sqrt(bandwidth); -} - -float ApproximateConfinedGaussian::getGaussian(float x) const { - return std::exp(-x * x * gaussianFactor_); -} - -} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.h deleted file mode 100644 index 8108e18a3..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include - -namespace audioapi::dsp { - -// https://en.wikipedia.org/wiki/Window_function -// https://personalpages.hs-kempten.de/~vollratj/InEl/pdf/Window%20function%20-%20Wikipedia.pdf -class WindowFunction { - public: - explicit WindowFunction(float amplitude = 1.0f) : amplitude_(amplitude) {} - - virtual void apply(float *data, int size) const = 0; - // forces STFT perfect-reconstruction (WOLA) on an existing window, for a given STFT interval. - static void forcePerfectReconstruction(float *data, int windowLength, int interval); - - protected: - // 1/L = amplitude - float amplitude_; -}; - -//https://en.wikipedia.org/wiki/Hann_function -// https://www.sciencedirect.com/topics/engineering/hanning-window -// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.hann.html#scipy.signal.windows.hann -class Hann : public WindowFunction { - public: - explicit Hann(float amplitude = 1.0f) : WindowFunction(amplitude) {} - - void apply(float *data, int size) const override; -}; - -// https://www.sciencedirect.com/topics/engineering/blackman-window -// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.blackman.html#scipy.signal.windows.blackman -class Blackman : public WindowFunction { - public: - explicit Blackman(float amplitude = 1.0f) : WindowFunction(amplitude) {} - - void apply(float *data, int size) const override; -}; - -// https://en.wikipedia.org/wiki/Kaiser_window -class Kaiser : public WindowFunction { - public: - explicit Kaiser(float beta, float amplitude = 1.0f) - : WindowFunction(amplitude), beta_(beta), invB0_(1.0f / bessel0(beta)) {} - - static Kaiser - withBandwidth(float bandwidth, bool heuristicOptimal = false, float amplitude = 1.0f) { - return Kaiser(bandwidthToBeta(bandwidth, heuristicOptimal), amplitude); - } - - void apply(float *data, int size) const override; - - private: - // beta = pi * alpha - // invB0 = 1 / I0(beta) - float beta_; - float invB0_; - - // https://en.wikipedia.org/wiki/Bessel_function#Modified_Bessel_functions:_I%CE%B1,_K%CE%B1 - static inline float bessel0(float x) { - const double significanceLimit = 1e-4; - auto result = 0.0f; - auto term = 1.0f; - auto m = 1.0f; - while (term > significanceLimit) { - result += term; - ++m; - term *= (x * x) / (4 * m * m); - } - - return result; - } - static float bandwidthToBeta(float bandwidth, bool heuristicOptimal = false); -}; - -// https://www.recordingblogs.com/wiki/gaussian-window -class ApproximateConfinedGaussian : public WindowFunction { - public: - explicit ApproximateConfinedGaussian(float sigma, float amplitude = 1.0f) - : WindowFunction(amplitude), gaussianFactor_(0.0625f / (sigma * sigma)) {} - - static ApproximateConfinedGaussian withBandwidth(float bandwidth, float amplitude = 1.0f) { - return ApproximateConfinedGaussian(bandwidthToSigma(bandwidth), amplitude); - } - - void apply(float *data, int size) const override; - - private: - float gaussianFactor_; - - static float bandwidthToSigma(float bandwidth); - - [[nodiscard]] float getGaussian(float x) const; -}; -} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp new file mode 100644 index 000000000..7c66985dc --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp @@ -0,0 +1,196 @@ +#pragma once + +#include +#include +#include + +namespace audioapi::dsp { + +// https://en.wikipedia.org/wiki/Window_function +// https://personalpages.hs-kempten.de/~vollratj/InEl/pdf/Window%20function%20-%20Wikipedia.pdf +class WindowFunction { + public: + explicit WindowFunction(float amplitude = 1.0f) : amplitude_(amplitude) {} + + virtual void apply(std::span data) const noexcept = 0; + // forces STFT perfect-reconstruction (WOLA) on an existing window, for a given STFT interval. + static void forcePerfectReconstruction(std::span data, int interval) { + int windowLength = static_cast(data.size()); + + for (int i = 0; i < interval; ++i) { + float sum2 = 0; + + for (int index = i; index < windowLength; index += interval) { + sum2 += data[index] * data[index]; + } + + float factor = 1 / std::sqrt(sum2); + + for (int index = i; index < windowLength; index += interval) { + data[index] *= factor; + } + } + } + + protected: + // 1/L = amplitude + float amplitude_; +}; + +//https://en.wikipedia.org/wiki/Hann_function +// https://www.sciencedirect.com/topics/engineering/hanning-window +// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.hann.html#scipy.signal.windows.hann +class Hann : public WindowFunction { + public: + explicit Hann(float amplitude = 1.0f) : WindowFunction(amplitude) {} + + void apply(std::span data) const noexcept override { + const size_t size = data.size(); + if (size < 2) { + return; + } + + const float invSizeMinusOne = 1.0f / static_cast(size - 1); + const float constantPart = 2.0f * std::numbers::pi_v * invSizeMinusOne; + + for (size_t i = 0; i < size; ++i) { + float window = 0.5f * (1.0f - std::cos(constantPart * i)); + data[i] = window * amplitude_; + } + } +}; + +// https://www.sciencedirect.com/topics/engineering/blackman-window +// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.blackman.html#scipy.signal.windows.blackman +class Blackman : public WindowFunction { + public: + explicit Blackman(float amplitude = 1.0f) : WindowFunction(amplitude) {} + + void apply(std::span data) const noexcept override { + const size_t size = data.size(); + if (size < 2) { + return; + } + + const float invSizeMinusOne = 1.0f / static_cast(size - 1); + const float alpha = 2.0f * std::numbers::pi_v * invSizeMinusOne; + + for (size_t i = 0; i < size; ++i) { + const float phase = alpha * i; + // 4*PI*x is just 2 * (2*PI*x) + const float window = 0.42f - 0.50f * std::cos(phase) + 0.08f * std::cos(2.0f * phase); + data[i] = window * amplitude_; + } + } +}; + +// https://en.wikipedia.org/wiki/Kaiser_window +class Kaiser : public WindowFunction { + public: + explicit Kaiser(float beta, float amplitude = 1.0f) + : WindowFunction(amplitude), beta_(beta), invB0_(1.0f / bessel0(beta)) {} + + static Kaiser + withBandwidth(float bandwidth, bool heuristicOptimal = false, float amplitude = 1.0f) { + return Kaiser(bandwidthToBeta(bandwidth, heuristicOptimal), amplitude); + } + + void apply(std::span data) const noexcept override { + const size_t size = data.size(); + if (size == 0) { + return; + } + + const float invSize = 1.0f / static_cast(size); + const float commonScale = invB0_ * amplitude_; + + for (size_t i = 0; i < size; ++i) { + // Optimized 'r' calculation: (2i+1)/size - 1 + const float r = (static_cast(2 * i + 1) * invSize) - 1.0f; + const float arg = std::sqrt(std::max(0.0f, 1.0f - r * r)); + + data[i] = bessel0(beta_ * arg) * commonScale; + } + } + + private: + // beta = pi * alpha + // invB0 = 1 / I0(beta) + float beta_; + float invB0_; + + // https://en.wikipedia.org/wiki/Bessel_function#Modified_Bessel_functions:_I%CE%B1,_K%CE%B1 + static inline float bessel0(float x) { + const double significanceLimit = 1e-4; + auto result = 0.0f; + auto term = 1.0f; + auto m = 1.0f; + while (term > significanceLimit) { + result += term; + ++m; + term *= (x * x) / (4 * m * m); + } + + return result; + } + inline static float bandwidthToBeta(float bandwidth, bool heuristicOptimal = false) { + if (heuristicOptimal) { // Heuristic based on numerical search + return bandwidth + 8.0f / (bandwidth + 3.0f) * (bandwidth + 3.0f) + + 0.25f * std::max(3.0f - bandwidth, 0.0f); + } + + bandwidth = std::max(bandwidth, 2.0f); + auto alpha = std::sqrt(bandwidth * bandwidth * 0.25f - 1.0f); + return alpha * PI; + } +}; + +// https://www.recordingblogs.com/wiki/gaussian-window +class ApproximateConfinedGaussian : public WindowFunction { + public: + explicit ApproximateConfinedGaussian(float sigma, float amplitude = 1.0f) + : WindowFunction(amplitude), gaussianFactor_(0.0625f / (sigma * sigma)) {} + + static ApproximateConfinedGaussian withBandwidth(float bandwidth, float amplitude = 1.0f) { + return ApproximateConfinedGaussian(bandwidthToSigma(bandwidth), amplitude); + } + + void apply(std::span data) const noexcept override { + const size_t size = data.size(); + if (size == 0) + return; + + const float g1 = getGaussian(1.0f); + const float g3 = getGaussian(3.0f); + const float g_1 = getGaussian(-1.0f); + const float g2 = getGaussian(2.0f); + + const float offsetScale = g1 / (g3 + g_1); + const float norm = 1.0f / (g1 - 2.0f * offsetScale * g2); + + const float invSize = 1.0f / static_cast(size); + const float totalAmplitude = norm * amplitude_; + + for (size_t i = 0; i < size; ++i) { + const float r = (static_cast(2 * i + 1) * invSize) - 1.0f; + + const float gR = getGaussian(r); + const float gRMinus2 = getGaussian(r - 2.0f); + const float gRPlus2 = getGaussian(r + 2.0f); + + data[i] = totalAmplitude * (gR - offsetScale * (gRMinus2 + gRPlus2)); + } + } + + private: + float gaussianFactor_; + + inline static float bandwidthToSigma(float bandwidth) noexcept { + return 0.3f / std::sqrt(bandwidth); + } + + [[nodiscard]] inline float getGaussian(float x) const noexcept { + return std::exp(-x * x * gaussianFactor_); + } +}; +} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp deleted file mode 100644 index 59bf02153..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -namespace audioapi { - -size_t AudioArrayBuffer::size() const { - return audioArray_->getSize() * sizeof(float); -} - -uint8_t *AudioArrayBuffer::data() { - return reinterpret_cast(audioArray_->getData()); -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.h deleted file mode 100644 index 6469ae13a..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/jsi/AudioArrayBuffer.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace audioapi { - -using namespace facebook; - -class AudioArrayBuffer : public jsi::MutableBuffer { - public: - explicit AudioArrayBuffer(const std::shared_ptr &audioArray) - : audioArray_(audioArray) {} - ~AudioArrayBuffer() override = default; - - AudioArrayBuffer(AudioArrayBuffer &&other) noexcept : audioArray_(std::move(other.audioArray_)) { - other.audioArray_ = nullptr; - } - - AudioArrayBuffer(const AudioArrayBuffer &) = delete; - AudioArrayBuffer &operator=(const AudioArrayBuffer &) = delete; - AudioArrayBuffer &operator=(AudioArrayBuffer &&other) = delete; - - [[nodiscard]] size_t size() const override; - uint8_t *data() override; - - private: - std::shared_ptr audioArray_; -}; - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp index 32c10d691..1773bab53 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp @@ -8,12 +8,12 @@ * FFmpeg, you must comply with the terms of the LGPL for FFmpeg itself. */ -#include +#include #if !RN_AUDIO_API_FFMPEG_DISABLED #include #endif // RN_AUDIO_API_FFMPEG_DISABLED #include -#include +#include #include namespace audioapi::ffmpegdecoder { @@ -25,8 +25,7 @@ int read_packet(void *opaque, uint8_t *buf, int buf_size) { return AVERROR_EOF; } - int bytes_to_read = - std::min(buf_size, static_cast(ctx->size - ctx->pos)); + int bytes_to_read = std::min(buf_size, static_cast(ctx->size - ctx->pos)); memcpy(buf, ctx->data + ctx->pos, bytes_to_read); ctx->pos += bytes_to_read; @@ -91,13 +90,9 @@ void convertFrameToBuffer( if (converted_samples > 0) { const size_t current_size = buffer.size(); - const size_t new_samples = - static_cast(converted_samples) * output_channel_count; + const size_t new_samples = static_cast(converted_samples) * output_channel_count; buffer.resize(current_size + new_samples); - memcpy( - buffer.data() + current_size, - resampled_data[0], - new_samples * sizeof(float)); + memcpy(buffer.data() + current_size, resampled_data[0], new_samples * sizeof(float)); framesRead += converted_samples; } } @@ -206,8 +201,7 @@ inline int findAudioStreamIndex(AVFormatContext *fmt_ctx) { bool setupDecoderContext( AVFormatContext *fmt_ctx, int &audio_stream_index, - std::unique_ptr> - &codec_ctx) { + std::unique_ptr> &codec_ctx) { audio_stream_index = findAudioStreamIndex(fmt_ctx); if (audio_stream_index == -1) { return false; @@ -241,37 +235,30 @@ std::shared_ptr decodeAudioFrames( int audio_stream_index, int sample_rate) { size_t framesRead = 0; - int output_sample_rate = - (sample_rate > 0) ? sample_rate : codec_ctx->sample_rate; + int output_sample_rate = (sample_rate > 0) ? sample_rate : codec_ctx->sample_rate; int output_channel_count = codec_ctx->ch_layout.nb_channels; std::vector decoded_buffer = readAllPcmFrames( - fmt_ctx, - codec_ctx, - output_sample_rate, - output_channel_count, - audio_stream_index, - framesRead); + fmt_ctx, codec_ctx, output_sample_rate, output_channel_count, audio_stream_index, framesRead); if (framesRead == 0 || decoded_buffer.empty()) { return nullptr; } auto outputFrames = decoded_buffer.size() / output_channel_count; - auto audioBus = std::make_shared( - outputFrames, output_channel_count, output_sample_rate); + auto audioBus = + std::make_shared(outputFrames, output_channel_count, output_sample_rate); for (int ch = 0; ch < output_channel_count; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = decoded_buffer[i * output_channel_count + ch]; } } - return std::make_shared(audioBus); + return audioBus; } -std::shared_ptr -decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) { +std::shared_ptr decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) { if (data == nullptr || size == 0) { return nullptr; } @@ -284,17 +271,9 @@ decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) { return nullptr; } - auto avio_ctx = - std::unique_ptr>( - avio_alloc_context( - io_buffer, - buffer_size, - 0, - &io_ctx, - read_packet, - nullptr, - seek_packet), - [](AVIOContext *ctx) { avio_context_free(&ctx); }); + auto avio_ctx = std::unique_ptr>( + avio_alloc_context(io_buffer, buffer_size, 0, &io_ctx, read_packet, nullptr, seek_packet), + [](AVIOContext *ctx) { avio_context_free(&ctx); }); if (avio_ctx == nullptr) { return nullptr; } @@ -310,29 +289,24 @@ decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) { return nullptr; } - auto fmt_ctx = - std::unique_ptr( - raw_fmt_ctx, &avformat_free_context); + auto fmt_ctx = std::unique_ptr( + raw_fmt_ctx, &avformat_free_context); if (avformat_find_stream_info(fmt_ctx.get(), nullptr) < 0) { return nullptr; } - auto codec_ctx = - std::unique_ptr>( - nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); }); + auto codec_ctx = std::unique_ptr>( + nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); }); int audio_stream_index = -1; if (!setupDecoderContext(fmt_ctx.get(), audio_stream_index, codec_ctx)) { return nullptr; } - return decodeAudioFrames( - fmt_ctx.get(), codec_ctx.get(), audio_stream_index, sample_rate); + return decodeAudioFrames(fmt_ctx.get(), codec_ctx.get(), audio_stream_index, sample_rate); } -std::shared_ptr decodeWithFilePath( - const std::string &path, - int sample_rate) { +std::shared_ptr decodeWithFilePath(const std::string &path, int sample_rate) { if (path.empty()) { return nullptr; } @@ -341,25 +315,21 @@ std::shared_ptr decodeWithFilePath( if (avformat_open_input(&raw_fmt_ctx, path.c_str(), nullptr, nullptr) < 0) return nullptr; - auto fmt_ctx = - std::unique_ptr>( - raw_fmt_ctx, - [](AVFormatContext *ctx) { avformat_close_input(&ctx); }); + auto fmt_ctx = std::unique_ptr>( + raw_fmt_ctx, [](AVFormatContext *ctx) { avformat_close_input(&ctx); }); if (avformat_find_stream_info(fmt_ctx.get(), nullptr) < 0) { return nullptr; } - auto codec_ctx = - std::unique_ptr>( - nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); }); + auto codec_ctx = std::unique_ptr>( + nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); }); int audio_stream_index = -1; if (!setupDecoderContext(fmt_ctx.get(), audio_stream_index, codec_ctx)) { return nullptr; } - return decodeAudioFrames( - fmt_ctx.get(), codec_ctx.get(), audio_stream_index, sample_rate); + return decodeAudioFrames(fmt_ctx.get(), codec_ctx.get(), audio_stream_index, sample_rate); } } // namespace audioapi::ffmpegdecoder diff --git a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h index f00047d7f..63b813f01 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h @@ -8,7 +8,7 @@ * FFmpeg, you must comply with the terms of the LGPL for FFmpeg itself. */ -#include +#include #include #include #include @@ -57,19 +57,15 @@ void convertFrameToBuffer( bool setupDecoderContext( AVFormatContext *fmt_ctx, int &audio_stream_index, - std::unique_ptr - &codec_ctx); + std::unique_ptr &codec_ctx); std::shared_ptr decodeAudioFrames( AVFormatContext *fmt_ctx, AVCodecContext *codec_ctx, int audio_stream_index, int sample_rate); -std::shared_ptr -decodeWithMemoryBlock(const void *data, size_t size, int sample_rate); +std::shared_ptr decodeWithMemoryBlock(const void *data, size_t size, int sample_rate); -std::shared_ptr decodeWithFilePath( - const std::string &path, - int sample_rate); +std::shared_ptr decodeWithFilePath(const std::string &path, int sample_rate); } // namespace audioapi::ffmpegdecoder diff --git a/packages/react-native-audio-api/common/cpp/audioapi/libs/signalsmith-stretch/fft-pffft.h b/packages/react-native-audio-api/common/cpp/audioapi/libs/signalsmith-stretch/fft-pffft.h index 1c687a8c9..8206de121 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/libs/signalsmith-stretch/fft-pffft.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/libs/signalsmith-stretch/fft-pffft.h @@ -1,6 +1,8 @@ #ifndef SIGNALSMITH_LINEAR_PLATFORM_FFT_PFFFT_H #define SIGNALSMITH_LINEAR_PLATFORM_FFT_PFFFT_H +#include + #if defined(__has_include) && !__has_include("pffft/pffft.h") # include #else diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index ef738c773..af555c1bb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -21,6 +21,7 @@ AudioArray::AudioArray(size_t size): size_(size) { AudioArray::AudioArray(const float *data, size_t size) : size_(size) { if (size_ > 0) { data_ = std::make_unique(size_); + copy(data, 0, 0, size_); std::memcpy(data_.get(), data, size_ * sizeof(float)); } } @@ -135,7 +136,7 @@ void AudioArray::copy( return; } - memcpy(data_.get() + destinationStart, source.data_.get() + sourceStart, length * sizeof(float)); + copy(source.data_.get(), sourceStart, destinationStart, length); } void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceStart, @@ -153,6 +154,24 @@ void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceSt } } +void AudioArray::copy(const float *source, size_t sourceStart, size_t destinationStart, + size_t length) { + if (length == 0 || data_ == nullptr || source == nullptr) { + return; + } + + memcpy(data_.get() + destinationStart, source + sourceStart, length * sizeof(float)); +} + +void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinationStart, + size_t length) const { + if (length == 0 || data_ == nullptr || destination == nullptr) { + return; + } + + memcpy(destination + destinationStart, data_.get() + sourceStart, length * sizeof(float)); +} + void AudioArray::reverse() { if (data_ == nullptr && size_ > 1) { return; @@ -204,7 +223,7 @@ float AudioArray::computeConvolution(const audioapi::AudioArray &kernel, size_t float32x4_t vSum = vdupq_n_f32(0.0f); // process 4 samples at a time - for (; k <= kernelSize_ - 4; k += 4) { + for (; k <= kernelSize - 4; k += 4) { float32x4_t vState = vld1q_f32(stateStart + k); float32x4_t vKernel = vld1q_f32(kernelStart + k); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index a456b9dbe..85af5c272 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -111,7 +111,11 @@ class AudioArray { /// @param destinationStart The starting index in this AudioArray. /// @param length The number of samples to copy. /// @note Assumes that source and this AudioArray are not the same. - void copyReverse(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + void + copyReverse(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + + void copy(const float *source, size_t sourceStart, size_t destinationStart, size_t length); + void copyTo(float *destination, size_t sourceStart, size_t destinationStart, size_t length) const; void reverse(); void normalize(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArrayBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArrayBuffer.hpp new file mode 100644 index 000000000..e72b7c9ac --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArrayBuffer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#if !RN_AUDIO_API_TEST +#include + +namespace audioapi { + +using namespace facebook; + +class AudioArrayBuffer : public jsi::MutableBuffer, public AudioArray { + public: + explicit AudioArrayBuffer(size_t size) : AudioArray(size) {}; + AudioArrayBuffer(const float *data, size_t size) : AudioArray(data, size) {}; + + [[nodiscard]] size_t size() const override { + return size_ * sizeof(float); + } + uint8_t *data() override { + return reinterpret_cast(data_.get()); + } +}; + +} // namespace audioapi + +#else + +namespace audioapi { + +class AudioArrayBuffer : public AudioArray { + public: + explicit AudioArrayBuffer(size_t size) : AudioArray(size) {}; + AudioArrayBuffer(const float *data, size_t size) : AudioArray(data, size) {}; + + [[nodiscard]] size_t size() const { + return size_ * sizeof(float); + } + uint8_t *data() { + return reinterpret_cast(data_.get()); + } +}; + +} // namespace audioapi + +#endif diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp similarity index 58% rename from packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp rename to packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 99c9e7327..99531b4e6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -1,7 +1,9 @@ #include #include #include -#include +#include +#include +#include #include #include #include @@ -19,14 +21,18 @@ namespace audioapi { * Public interfaces - memory management */ -AudioBus::AudioBus(size_t size, int numberOfChannels, float sampleRate) +AudioBuffer::AudioBuffer(size_t size, int numberOfChannels, float sampleRate) : numberOfChannels_(numberOfChannels), sampleRate_(sampleRate), size_(size) { createChannels(); } -AudioBus::AudioBus(const AudioBus &other): numberOfChannels_(other.numberOfChannels_), - sampleRate_(other.sampleRate_), - size_(other.size_) { +AudioBuffer::AudioBuffer(const audioapi::AudioBufferOptions &options) + : AudioBuffer(options.length, options.numberOfChannels, options.sampleRate) {} + +AudioBuffer::AudioBuffer(const AudioBuffer &other) + : numberOfChannels_(other.numberOfChannels_), + sampleRate_(other.sampleRate_), + size_(other.size_) { createChannels(); for (int i = 0; i < numberOfChannels_; i += 1) { @@ -34,71 +40,58 @@ AudioBus::AudioBus(const AudioBus &other): numberOfChannels_(other.numberOfChann } } -AudioBus::AudioBus(std::vector> channels, float sampleRate) - : channels_(std::move(channels)), - sampleRate_(sampleRate) { - - numberOfChannels_ = static_cast(channels_.size()); - - if (numberOfChannels_ > 0 && channels_[0] != nullptr) { - size_ = channels_[0]->getSize(); - } else { - size_ = 0; - } -} - -AudioBus::AudioBus(audioapi::AudioBus &&other) noexcept : - channels_(std::move(other.channels_)), - numberOfChannels_(other.numberOfChannels_), - sampleRate_(other.sampleRate_), - size_(other.size_) { - other.numberOfChannels_ = 0; - other.sampleRate_ = 0.0f; - other.size_ = 0; +AudioBuffer::AudioBuffer(audioapi::AudioBuffer &&other) noexcept + : channels_(std::move(other.channels_)), + numberOfChannels_(other.numberOfChannels_), + sampleRate_(other.sampleRate_), + size_(other.size_) { + other.numberOfChannels_ = 0; + other.sampleRate_ = 0.0f; + other.size_ = 0; } -AudioBus &AudioBus::operator=(const AudioBus &other) { +AudioBuffer &AudioBuffer::operator=(const AudioBuffer &other) { if (this != &other) { - if (numberOfChannels_ != other.numberOfChannels_ || size_ != other.size_) { - numberOfChannels_ = other.numberOfChannels_; - size_ = other.size_; - createChannels(); - } + if (numberOfChannels_ != other.numberOfChannels_ || size_ != other.size_) { + numberOfChannels_ = other.numberOfChannels_; + size_ = other.size_; + createChannels(); + } - sampleRate_ = other.sampleRate_; + sampleRate_ = other.sampleRate_; - for (int i = 0; i < numberOfChannels_; i += 1) { - *channels_[i] = *other.channels_[i]; - } + for (int i = 0; i < numberOfChannels_; i += 1) { + *channels_[i] = *other.channels_[i]; + } } return *this; } -AudioBus &AudioBus::operator=(audioapi::AudioBus &&other) noexcept { - if (this != &other) { - channels_ = std::move(other.channels_); +AudioBuffer &AudioBuffer::operator=(audioapi::AudioBuffer &&other) noexcept { + if (this != &other) { + channels_ = std::move(other.channels_); - numberOfChannels_ = other.numberOfChannels_; - sampleRate_ = other.sampleRate_; - size_ = other.size_; + numberOfChannels_ = other.numberOfChannels_; + sampleRate_ = other.sampleRate_; + size_ = other.size_; - other.numberOfChannels_ = 0; - other.sampleRate_ = 0.0f; - other.size_ = 0; - } - return *this; + other.numberOfChannels_ = 0; + other.sampleRate_ = 0.0f; + other.size_ = 0; + } + return *this; } /** * Public interfaces - getters */ -AudioArray *AudioBus::getChannel(int index) const { +AudioArray *AudioBuffer::getChannel(int index) const { return channels_[index].get(); } -AudioArray *AudioBus::getChannelByType(int channelType) const { +AudioArray *AudioBuffer::getChannelByType(int channelType) const { switch (getNumberOfChannels()) { case 1: // mono if (channelType == ChannelMono) { @@ -168,7 +161,7 @@ AudioArray *AudioBus::getChannelByType(int channelType) const { } } -std::shared_ptr AudioBus::getSharedChannel(int index) const { +std::shared_ptr AudioBuffer::getSharedChannel(int index) const { return channels_[index]; } @@ -176,22 +169,22 @@ std::shared_ptr AudioBus::getSharedChannel(int index) const { * Public interfaces - audio processing and setters */ -void AudioBus::zero() { +void AudioBuffer::zero() { zero(0, getSize()); } -void AudioBus::zero(size_t start, size_t length) { +void AudioBuffer::zero(size_t start, size_t length) { for (auto it = channels_.begin(); it != channels_.end(); it += 1) { it->get()->zero(start, length); } } -void AudioBus::sum(const AudioBus& source, ChannelInterpretation interpretation) { +void AudioBuffer::sum(const AudioBuffer &source, ChannelInterpretation interpretation) { sum(source, 0, 0, getSize(), interpretation); } -void AudioBus::sum( - const AudioBus& source, +void AudioBuffer::sum( + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length, @@ -226,12 +219,12 @@ void AudioBus::sum( } } -void AudioBus::copy(const AudioBus& source) { +void AudioBuffer::copy(const AudioBuffer &source) { copy(source, 0, 0, getSize()); } -void AudioBus::copy( - const AudioBus& source, +void AudioBuffer::copy( + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) { @@ -252,50 +245,50 @@ void AudioBus::copy( sum(source, sourceStart, destinationStart, length); } -void AudioBus::normalize() { - float maxAbsValue = this->maxAbsValue(); +void AudioBuffer::normalize() { + float maxAbsValue = this->maxAbsValue(); - if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { - return; - } + if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { + return; + } - float scale = 1.0f / maxAbsValue; - this->scale(scale); + float scale = 1.0f / maxAbsValue; + this->scale(scale); } -void AudioBus::scale(float value) { - for (auto &channel : channels_) { - channel->scale(value); - } +void AudioBuffer::scale(float value) { + for (auto &channel : channels_) { + channel->scale(value); + } } -float AudioBus::maxAbsValue() const { - float maxAbsValue = 1.0f; +float AudioBuffer::maxAbsValue() const { + float maxAbsValue = 1.0f; - for (const auto &channel : channels_) { - float channelMaxAbsValue = channel->getMaxAbsValue(); - maxAbsValue = std::max(maxAbsValue, channelMaxAbsValue); - } + for (const auto &channel : channels_) { + float channelMaxAbsValue = channel->getMaxAbsValue(); + maxAbsValue = std::max(maxAbsValue, channelMaxAbsValue); + } - return maxAbsValue; + return maxAbsValue; } /** * Internal tooling - channel initialization */ -void AudioBus::createChannels() { +void AudioBuffer::createChannels() { if (channels_.size() != static_cast(numberOfChannels_)) { - channels_.clear(); - channels_.reserve(numberOfChannels_); + channels_.clear(); + channels_.reserve(numberOfChannels_); - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_.emplace_back(std::make_shared(size_)); - } + for (int i = 0; i < numberOfChannels_; i += 1) { + channels_.emplace_back(std::make_shared(size_)); + } } else { - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_[i]->resize(size_); - } + for (int i = 0; i < numberOfChannels_; i += 1) { + channels_[i]->resize(size_); + } } } @@ -303,8 +296,8 @@ void AudioBus::createChannels() { * Internal tooling - channel summing */ -void AudioBus::discreteSum( - const AudioBus& source, +void AudioBuffer::discreteSum( + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) const { @@ -318,8 +311,8 @@ void AudioBus::discreteSum( } } -void AudioBus::sumByUpMixing( - const AudioBus& source, +void AudioBuffer::sumByUpMixing( + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) { @@ -361,15 +354,16 @@ void AudioBus::sumByUpMixing( getChannelByType(ChannelSurroundLeft) ->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); getChannelByType(ChannelSurroundRight) - ->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); + ->sum( + *source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); return; } discreteSum(source, sourceStart, destinationStart, length); } -void AudioBus::sumByDownMixing( - const AudioBus& source, +void AudioBuffer::sumByDownMixing( + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) { @@ -380,8 +374,10 @@ void AudioBus::sumByDownMixing( if (numberOfSourceChannels == 2 && numberOfChannels == 1) { auto destinationData = getChannelByType(ChannelMono); - destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); - destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); + destinationData->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); + destinationData->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); return; } @@ -391,10 +387,22 @@ void AudioBus::sumByDownMixing( if (numberOfSourceChannels == 4 && numberOfChannels == 1) { auto destinationData = getChannelByType(ChannelMono); - destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.25f); - destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.25f); - destinationData->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.25f); - destinationData->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.25f); + destinationData->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.25f); + destinationData->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.25f); + destinationData->sum( + *source.getChannelByType(ChannelSurroundLeft), + sourceStart, + destinationStart, + length, + 0.25f); + destinationData->sum( + *source.getChannelByType(ChannelSurroundRight), + sourceStart, + destinationStart, + length, + 0.25f); return; } @@ -404,11 +412,20 @@ void AudioBus::sumByDownMixing( if (numberOfSourceChannels == 6 && numberOfChannels == 1) { auto destinationData = getChannelByType(ChannelMono); - destinationData->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, SQRT_HALF); - destinationData->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, SQRT_HALF); - destinationData->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length); - destinationData->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); - destinationData->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.5f); + destinationData->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, SQRT_HALF); + destinationData->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, SQRT_HALF); + destinationData->sum( + *source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length); + destinationData->sum( + *source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); + destinationData->sum( + *source.getChannelByType(ChannelSurroundRight), + sourceStart, + destinationStart, + length, + 0.5f); return; } @@ -419,11 +436,19 @@ void AudioBus::sumByDownMixing( auto destinationLeft = getChannelByType(ChannelLeft); auto destinationRight = getChannelByType(ChannelRight); - destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); - destinationLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); - - destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); - destinationRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, 0.5f); + destinationLeft->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length, 0.5f); + destinationLeft->sum( + *source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, 0.5f); + + destinationRight->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length, 0.5f); + destinationRight->sum( + *source.getChannelByType(ChannelSurroundRight), + sourceStart, + destinationStart, + length, + 0.5f); return; } @@ -435,13 +460,27 @@ void AudioBus::sumByDownMixing( auto destinationLeft = getChannelByType(ChannelLeft); auto destinationRight = getChannelByType(ChannelRight); - destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); - destinationLeft->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); - destinationLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length, SQRT_HALF); - - destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); - destinationRight->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); - destinationRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length, SQRT_HALF); + destinationLeft->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + destinationLeft->sum( + *source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationLeft->sum( + *source.getChannelByType(ChannelSurroundLeft), + sourceStart, + destinationStart, + length, + SQRT_HALF); + + destinationRight->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); + destinationRight->sum( + *source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationRight->sum( + *source.getChannelByType(ChannelSurroundRight), + sourceStart, + destinationStart, + length, + SQRT_HALF); return; } @@ -456,14 +495,20 @@ void AudioBus::sumByDownMixing( auto destinationSurroundLeft = getChannelByType(ChannelSurroundLeft); auto destinationSurroundRight = getChannelByType(ChannelSurroundRight); - destinationLeft->sum(*source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); - destinationLeft->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationLeft->sum( + *source.getChannelByType(ChannelLeft), sourceStart, destinationStart, length); + destinationLeft->sum( + *source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); - destinationRight->sum(*source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); - destinationRight->sum(*source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); + destinationRight->sum( + *source.getChannelByType(ChannelRight), sourceStart, destinationStart, length); + destinationRight->sum( + *source.getChannelByType(ChannelCenter), sourceStart, destinationStart, length, SQRT_HALF); - destinationSurroundLeft->sum(*source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); - destinationSurroundRight->sum(*source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); + destinationSurroundLeft->sum( + *source.getChannelByType(ChannelSurroundLeft), sourceStart, destinationStart, length); + destinationSurroundRight->sum( + *source.getChannelByType(ChannelSurroundRight), sourceStart, destinationStart, length); return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h similarity index 65% rename from packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h rename to packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h index 497f04450..9803d5a47 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBus.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -9,9 +11,9 @@ namespace audioapi { -class AudioArray; +struct AudioBufferOptions; -class AudioBus { +class AudioBuffer { public: enum { ChannelMono = 0, @@ -23,14 +25,14 @@ class AudioBus { ChannelSurroundRight = 5, }; - explicit AudioBus() = default; - explicit AudioBus(size_t size, int numberOfChannels, float sampleRate); - AudioBus(std::vector> channels, float sampleRate); - AudioBus(const AudioBus &other); - AudioBus(AudioBus &&other) noexcept; - AudioBus &operator=(const AudioBus &other); - AudioBus &operator=(AudioBus &&other) noexcept; - ~AudioBus() = default; + explicit AudioBuffer() = default; + explicit AudioBuffer(size_t size, int numberOfChannels, float sampleRate); + explicit AudioBuffer(const AudioBufferOptions &options); + AudioBuffer(const AudioBuffer &other); + AudioBuffer(AudioBuffer &&other) noexcept; + AudioBuffer &operator=(const AudioBuffer &other); + AudioBuffer &operator=(AudioBuffer &&other) noexcept; + ~AudioBuffer() = default; [[nodiscard]] inline int getNumberOfChannels() const noexcept { return numberOfChannels_; @@ -42,6 +44,10 @@ class AudioBus { return size_; } + [[nodiscard]] double getDuration() const noexcept { + return static_cast(size_) / getSampleRate(); + } + /// @brief Get the AudioArray for a specific channel index. /// @param index The channel index. /// @return Pointer to the AudioArray for the specified channel - not owning. @@ -55,7 +61,7 @@ class AudioBus { /// @brief Get a copy of shared pointer to the AudioArray for a specific channel index. /// @param index The channel index. /// @return Copy of shared pointer to the AudioArray for the specified channel - [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; + [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; AudioArray &operator[](size_t index) { return *channels_[index]; @@ -68,24 +74,24 @@ class AudioBus { void zero(size_t start, size_t length); void sum( - const AudioBus &source, + const AudioBuffer &source, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); void sum( - const AudioBus &source, + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); - void copy(const AudioBus &source); - void copy(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); + void copy(const AudioBuffer &source); + void copy(const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length); void normalize(); void scale(float value); [[nodiscard]] float maxAbsValue() const; private: - std::vector> channels_; + std::vector> channels_; int numberOfChannels_ = 0; float sampleRate_ = 0.0f; @@ -93,14 +99,17 @@ class AudioBus { void createChannels(); void discreteSum( - const AudioBus &source, + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) const; - void - sumByUpMixing(const AudioBus &source, size_t sourceStart, size_t destinationStart, size_t length); + void sumByUpMixing( + const AudioBuffer &source, + size_t sourceStart, + size_t destinationStart, + size_t length); void sumByDownMixing( - const AudioBus &source, + const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp index 142cd9819..14848094a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp @@ -1,10 +1,12 @@ #include +#include + namespace audioapi { CircularAudioArray::CircularAudioArray(size_t size) : AudioArray(size) {} -void CircularAudioArray::push_back(const float *data, size_t size, bool skipAvailableSpaceCheck) { +void CircularAudioArray::push_back(const AudioArray & data, size_t size, bool skipAvailableSpaceCheck) { if (size > size_) { throw std::overflow_error("size exceeds CircularAudioArray size_"); } @@ -15,16 +17,17 @@ void CircularAudioArray::push_back(const float *data, size_t size, bool skipAvai if (vWriteIndex_ + size > size_) { auto partSize = size_ - vWriteIndex_; - memcpy(data_ + vWriteIndex_, data, partSize * sizeof(float)); - memcpy(data_, data + partSize, (size - partSize) * sizeof(float)); + + copy(data, 0, vWriteIndex_, partSize); + copy(data, partSize, 0, size - partSize); } else { - memcpy(data_ + vWriteIndex_, data, size * sizeof(float)); + copy(data, 0, vWriteIndex_, size); } vWriteIndex_ = vWriteIndex_ + size > size_ ? vWriteIndex_ + size - size_ : vWriteIndex_ + size; } -void CircularAudioArray::pop_front(float *data, size_t size, bool skipAvailableDataCheck) { +void CircularAudioArray::pop_front(AudioArray &data, size_t size, bool skipAvailableDataCheck) { if (size > size_) { throw std::overflow_error("size exceeds CircularAudioArray size_"); } @@ -35,17 +38,17 @@ void CircularAudioArray::pop_front(float *data, size_t size, bool skipAvailableD if (vReadIndex_ + size > size_) { auto partSize = size_ - vReadIndex_; - memcpy(data, data_ + vReadIndex_, partSize * sizeof(float)); - memcpy(data + partSize, data_, (size - partSize) * sizeof(float)); + data.copy(*this, vReadIndex_, 0, partSize); + data.copy(*this, 0, partSize, size - partSize); } else { - memcpy(data, data_ + vReadIndex_, size * sizeof(float)); + data.copy(*this, vReadIndex_, 0, size); } vReadIndex_ = vReadIndex_ + size > size_ ? vReadIndex_ + size - size_ : vReadIndex_ + size; } void CircularAudioArray::pop_back( - float *data, + AudioArray &data, size_t size, size_t offset, bool skipAvailableDataCheck) { @@ -58,13 +61,13 @@ void CircularAudioArray::pop_back( } if (vWriteIndex_ <= offset) { - memcpy(data, data_ + size_ - (offset - vWriteIndex_) - size, size * sizeof(float)); + data.copy(*this, size_ - (offset - vWriteIndex_) - size, 0, size); } else if (vWriteIndex_ <= size + offset) { auto partSize = size + offset - vWriteIndex_; - memcpy(data, data_ + size_ - partSize, partSize * sizeof(float)); - memcpy(data + partSize, data_, (size - partSize) * sizeof(float)); + data.copy(*this, size_ - partSize, 0, partSize); + data.copy(*this, 0, partSize, size - partSize); } else { - memcpy(data, data_ + vWriteIndex_ - size - offset, size * sizeof(float)); + data.copy(*this, vWriteIndex_ - size - offset, 0, size); } vReadIndex_ = vWriteIndex_ - offset < 0 ? size + vWriteIndex_ - offset : vWriteIndex_ - offset; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h index f0df81b75..999af4fce 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h @@ -11,9 +11,10 @@ class CircularAudioArray : public AudioArray { CircularAudioArray(const CircularAudioArray &other) = default; ~CircularAudioArray() = default; - void push_back(const float *data, size_t size, bool skipAvailableSpaceCheck = false); - void pop_front(float *data, size_t size, bool skipAvailableDataCheck = false); - void pop_back(float *data, size_t size, size_t offset = 0, bool skipAvailableDataCheck = false); + void push_back(const AudioArray &data, size_t size, bool skipAvailableSpaceCheck = false); + void pop_front(AudioArray &data, size_t size, bool skipAvailableDataCheck = false); + void + pop_back(AudioArray &data, size_t size, size_t offset = 0, bool skipAvailableDataCheck = false); [[nodiscard]] size_t getNumberOfAvailableFrames() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp index 86acde67c..66b6c852a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp @@ -8,7 +8,7 @@ CircularOverflowableAudioArray::CircularOverflowableAudioArray(size_t size) noex std::is_nothrow_constructible::value) : AudioArray(size) {} -void CircularOverflowableAudioArray::write(const float *data, const size_t size) { +void CircularOverflowableAudioArray::write(const AudioArray &data, const size_t size) { size_t writeIndex = vWriteIndex_.load(std::memory_order_relaxed); if (size > size_) { @@ -25,25 +25,25 @@ void CircularOverflowableAudioArray::write(const float *data, const size_t size) size_t partSize = size_ - writeIndex; if (size > partSize) { - std::memcpy(data_ + writeIndex, data, partSize * sizeof(float)); - std::memcpy(data_, data + partSize, (size - partSize) * sizeof(float)); + copy(data, 0, writeIndex, partSize); + copy(data, partSize, 0, size - partSize); } else { - std::memcpy(data_ + writeIndex, data, size * sizeof(float)); + copy(data, 0, writeIndex, size); } vWriteIndex_.store((writeIndex + size) % size_, std::memory_order_relaxed); } -size_t CircularOverflowableAudioArray::read(float *output, size_t size) const { +size_t CircularOverflowableAudioArray::read(AudioArray &data, size_t size) const { readLock_.lock(); size_t availableSpace = getAvailableSpace(); size_t readSize = std::min(size, availableSpace); size_t partSize = size_ - vReadIndex_; if (readSize > partSize) { - std::memcpy(output, data_ + vReadIndex_, partSize * sizeof(float)); - std::memcpy(output + partSize, data_, (readSize - partSize) * sizeof(float)); + data.copy(*this, vReadIndex_, 0, partSize); + data.copy(*this, 0, partSize, readSize - partSize); } else { - std::memcpy(output, data_ + vReadIndex_, readSize * sizeof(float)); + data.copy(*this, vReadIndex_, 0, readSize); } vReadIndex_ = (vReadIndex_ + readSize) % size_; readLock_.unlock(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h index 601da5610..b4889857b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h @@ -23,13 +23,13 @@ class CircularOverflowableAudioArray : public AudioArray { /// @note Might wait for read operation to finish if it is in progress. It ignores writes that exceed the buffer size. /// @param data Pointer to the input buffer. /// @param size Number of frames to write. - void write(const float *data, size_t size); + void write(const AudioArray &data, size_t size); /// @brief Reads data from the circular buffer. /// @param output Pointer to the output buffer. /// @param size Number of frames to read. /// @return The number of frames actually read. - size_t read(float *output, size_t size) const; + size_t read(AudioArray &data, size_t size) const; private: std::atomic vWriteIndex_ = {0}; diff --git a/packages/react-native-audio-api/common/cpp/test/src/AudioParamTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/AudioParamTest.cpp index 7d8cbae23..75efa552a 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/AudioParamTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/AudioParamTest.cpp @@ -121,8 +121,15 @@ TEST_F(AudioParamTest, SetTargetAtTime) { TEST_F(AudioParamTest, SetValueCurveAtTime) { auto param = AudioParam(0.0, 0.0, 1.0, context); param.setValue(0.5); - auto curve = std::make_shared>(std::vector{0.1, 0.4, 0.2, 0.8, 0.5}); - param.setValueCurveAtTime(curve, curve->size(), 0.1, 0.2); + auto curve = std::make_shared(5); + auto curveSpan = curve ->span(); + curveSpan[0] = 0.1f; + curveSpan[1] = 0.4f; + curveSpan[2] = 0.2f; + curveSpan[3] = 0.8f; + curveSpan[4] = 0.5f; + + param.setValueCurveAtTime(curve, curve->getSize(), 0.1, 0.2); // 5 elements over 0.2s => each element is 0.04s apart float value = param.processKRateParam(1, 0.05); diff --git a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp index ec2df8519..8c09eaa14 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { } void updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBus, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, @@ -48,7 +48,7 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { currentSampleFrame); } - std::shared_ptr processNode(const std::shared_ptr &, int) override { + std::shared_ptr processNode(const std::shared_ptr &, int) override { return nullptr; } @@ -60,7 +60,7 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { if (std::shared_ptr context = context_.lock()) { size_t startOffset = 0; size_t nonSilentFramesToProcess = 0; - auto processingBus = std::make_shared(128, 2, static_cast(SAMPLE_RATE)); + auto processingBus = std::make_shared(128, 2, static_cast(SAMPLE_RATE)); updatePlaybackInfo( processingBus, frames, diff --git a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp index 4bac9678f..85e7b4ad4 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,8 +33,8 @@ class TestableConstantSourceNode : public ConstantSourceNode { getOffsetParam()->setValue(value); } - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return ConstantSourceNode::processNode(processingBus, framesToProcess); } @@ -48,7 +48,7 @@ TEST_F(ConstantSourceTest, ConstantSourceCanBeCreated) { TEST_F(ConstantSourceTest, ConstantSourceOutputsConstantValue) { static constexpr int FRAMES_TO_PROCESS = 4; - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); auto constantSource = TestableConstantSourceNode(context); // constantSource.start(context->getCurrentTime()); // auto resultBus = constantSource.processNode(bus, FRAMES_TO_PROCESS); diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index 80f57b80c..3ea5f2b54 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -26,14 +26,15 @@ class DelayTest : public ::testing::Test { class TestableDelayNode : public DelayNode { public: - explicit TestableDelayNode(std::shared_ptr context, const DelayOptions& options) : DelayNode(context, options) {} + explicit TestableDelayNode(std::shared_ptr context, const DelayOptions &options) + : DelayNode(context, options) {} void setDelayTimeParam(float value) { getDelayTimeParam()->setValue(value); } - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return DelayNode::processNode(processingBus, framesToProcess); } @@ -52,9 +53,9 @@ TEST_F(DelayTest, DelayWithZeroDelayOutputsInputSignal) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = i + 1; + (*bus->getChannel(0))[i] = i + 1; } auto resultBus = delayNode.processNode(bus, FRAMES_TO_PROCESS); @@ -71,9 +72,9 @@ TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = i + 1; + (*bus->getChannel(0))[i] = i + 1; } auto resultBus = delayNode.processNode(bus, FRAMES_TO_PROCESS); @@ -97,9 +98,9 @@ TEST_F(DelayTest, DelayHandlesTailCorrectly) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = i + 1; + (*bus->getChannel(0))[i] = i + 1; } delayNode.processNode(bus, FRAMES_TO_PROCESS); diff --git a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp index e34870fb6..1accf6785 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -26,14 +26,15 @@ class GainTest : public ::testing::Test { class TestableGainNode : public GainNode { public: - explicit TestableGainNode(std::shared_ptr context) : GainNode(context, GainOptions()) {} + explicit TestableGainNode(std::shared_ptr context) + : GainNode(context, GainOptions()) {} void setGainParam(float value) { getGainParam()->setValue(value); } - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return GainNode::processNode(processingBus, framesToProcess); } @@ -50,9 +51,9 @@ TEST_F(GainTest, GainModulatesVolumeCorrectly) { auto gainNode = TestableGainNode(context); gainNode.setGainParam(GAIN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = i + 1; + (*bus->getChannel(0))[i] = i + 1; } auto resultBus = gainNode.processNode(bus, FRAMES_TO_PROCESS); @@ -67,10 +68,10 @@ TEST_F(GainTest, GainModulatesVolumeCorrectlyMultiChannel) { auto gainNode = TestableGainNode(context); gainNode.setGainParam(GAIN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = i + 1; - bus->getChannel(1)->getData()[i] = -i - 1; + (*bus->getChannel(0))[i] = i + 1; + (*bus->getChannel(1))[i] = -i - 1; } auto resultBus = gainNode.processNode(bus, FRAMES_TO_PROCESS); diff --git a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp index 68ce23c55..18d935e9f 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,8 +33,8 @@ class TestableStereoPannerNode : public StereoPannerNode { getPanParam()->setValue(value); } - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return StereoPannerNode::processNode(processingBus, framesToProcess); } @@ -51,9 +51,9 @@ TEST_F(StereoPannerTest, PanModulatesInputMonoCorrectly) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBus::ChannelLeft))[i] = i + 1; + (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; } auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); @@ -62,11 +62,11 @@ TEST_F(StereoPannerTest, PanModulatesInputMonoCorrectly) { // gainR = sin(x * (π / 2)) = sin(0.75 * (π / 2)) = 0.9238795325112867 for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelLeft))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelLeft))[i], (i + 1) * 0.38268343236508984, 1e-4); EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelRight))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelRight))[i], (i + 1) * 0.9238795325112867, 1e-4); } @@ -78,10 +78,10 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithNegativePan) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBus::ChannelLeft))[i] = i + 1; - (*bus->getChannelByType(AudioBus::ChannelRight))[i] = i + 1; + (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; + (*bus->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; } auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); @@ -90,11 +90,11 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithNegativePan) { // gainR = sin(x * (π / 2)) = sin(0.5 * (π / 2)) = 0.7071067811865476 for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelLeft))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelLeft))[i], (i + 1) + (i + 1) * 0.7071067811865476, 1e-4); EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelRight))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelRight))[i], (i + 1) * 0.7071067811865476, 1e-4); } @@ -106,10 +106,10 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithPositivePan) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBus::ChannelLeft))[i] = i + 1; - (*bus->getChannelByType(AudioBus::ChannelRight))[i] = i + 1; + (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; + (*bus->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; } auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); @@ -118,11 +118,11 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithPositivePan) { // gainR = sin(x * (π / 2)) = sin(0.75 * (π / 2)) = 0.9238795325112867 for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelLeft))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelLeft))[i], (i + 1) * 0.38268343236508984, 1e-4); EXPECT_NEAR( - (*resultBus->getChannelByType(AudioBus::ChannelRight))[i], + (*resultBus->getChannelByType(AudioBuffer::ChannelRight))[i], (i + 1) + (i + 1) * 0.9238795325112867, 1e-4); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 679b4f76e..6ae4eb87d 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -1,10 +1,10 @@ #include #include #include -#include #include +#include #include -#include +#include #include #include #include @@ -29,14 +29,14 @@ class TestableWaveShaperNode : public WaveShaperNode { explicit TestableWaveShaperNode(std::shared_ptr context) : WaveShaperNode(context, WaveShaperOptions()) { testCurve_ = std::make_shared(3); - auto data = testCurve_->getData(); + auto data = testCurve_->span(); data[0] = -2.0f; data[1] = 0.0f; data[2] = 2.0f; } - std::shared_ptr processNode( - const std::shared_ptr &processingBus, + std::shared_ptr processNode( + const std::shared_ptr &processingBus, int framesToProcess) override { return WaveShaperNode::processNode(processingBus, framesToProcess); } @@ -61,14 +61,14 @@ TEST_F(WaveShaperNodeTest, NoneOverSamplingProcessesCorrectly) { waveShaper->setOversample(OverSampleType::OVERSAMPLE_NONE); waveShaper->setCurve(waveShaper->testCurve_); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); for (size_t i = 0; i < bus->getSize(); ++i) { - bus->getChannel(0)->getData()[i] = -1.0f + i * 0.5f; + (*bus->getChannel(0))[i] = -1.0f + i * 0.5f; } auto resultBus = waveShaper->processNode(bus, FRAMES_TO_PROCESS); - auto curveData = waveShaper->testCurve_->getData(); - auto resultData = resultBus->getChannel(0)->getData(); + auto curveData = waveShaper->testCurve_->span(); + auto resultData = resultBus->getChannel(0)->span(); EXPECT_FLOAT_EQ(resultData[0], curveData[0]); EXPECT_FLOAT_EQ(resultData[1], -1.0f); diff --git a/packages/react-native-audio-api/common/cpp/test/src/dsp/ResamplerTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/dsp/ResamplerTest.cpp index f62fc9824..21e868148 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/dsp/ResamplerTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/dsp/ResamplerTest.cpp @@ -1,16 +1,16 @@ +#include #include #include #include -#include -#include +#include #include #include using namespace audioapi; class ResamplerTest : public ::testing::Test { - protected: - static constexpr int KERNEL_SIZE = RENDER_QUANTUM_SIZE; + protected: + static constexpr int KERNEL_SIZE = RENDER_QUANTUM_SIZE; }; class TestableUpSampler : public UpSampler { @@ -39,7 +39,8 @@ TEST_F(ResamplerTest, UpSamplerCanBeCreated) { } TEST_F(ResamplerTest, DownSamplerCanBeCreated) { - auto downSampler = std::make_unique(RENDER_QUANTUM_SIZE * 2, RENDER_QUANTUM_SIZE * 2); + auto downSampler = + std::make_unique(RENDER_QUANTUM_SIZE * 2, RENDER_QUANTUM_SIZE * 2); ASSERT_NE(downSampler, nullptr); } @@ -109,8 +110,8 @@ TEST_F(ResamplerTest, UpDownSamplingProcess) { int upSamplerOutputFrames; int downSamplerOutputFrames; - EXPECT_NO_THROW(upSamplerOutputFrames = upSampler->process(inputArray, outputArray, 4)); - EXPECT_NO_THROW(downSamplerOutputFrames = downSampler->process(outputArray, inputArray, 8)); + EXPECT_NO_THROW(upSamplerOutputFrames = upSampler->process(*inputArray, *outputArray, 4)); + EXPECT_NO_THROW(downSamplerOutputFrames = downSampler->process(*outputArray, *inputArray, 8)); EXPECT_EQ(upSamplerOutputFrames, 8); EXPECT_EQ(downSamplerOutputFrames, 4); diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h index 21e858888..c11e9b7fd 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h @@ -10,13 +10,13 @@ typedef struct objc_object NativeAudioPlayer; namespace audioapi { -class AudioBus; +class AudioBuffer; class AudioContext; class IOSAudioPlayer { public: IOSAudioPlayer( - const std::function, int)> &renderAudio, + const std::function, int)> &renderAudio, float sampleRate, int channelCount); ~IOSAudioPlayer(); @@ -30,9 +30,9 @@ class IOSAudioPlayer { bool isRunning() const; protected: - std::shared_ptr audioBus_; + std::shared_ptr audioBus_; NativeAudioPlayer *audioPlayer_; - std::function, int)> renderAudio_; + std::function, int)> renderAudio_; int channelCount_; std::atomic isRunning_; }; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm index 0b2609a1f..1c33c8ed0 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm @@ -5,12 +5,12 @@ #include #include #include -#include +#include namespace audioapi { IOSAudioPlayer::IOSAudioPlayer( - const std::function, int)> &renderAudio, + const std::function, int)> &renderAudio, float sampleRate, int channelCount) : renderAudio_(renderAudio), channelCount_(channelCount), audioBus_(0), isRunning_(false) @@ -42,7 +42,7 @@ sampleRate:sampleRate channelCount:channelCount_]; - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate); + audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate); } IOSAudioPlayer::~IOSAudioPlayer() diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index ac6229b1b..9e6168fcb 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm index 3b2b4b8d4..62b7a7914 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm @@ -2,16 +2,16 @@ #include #include -#include #include #include #include #include +#include #if !RN_AUDIO_API_FFMPEG_DISABLED #include #endif // RN_AUDIO_API_FFMPEG_DISABLED #include -#include +#include namespace audioapi { @@ -51,7 +51,7 @@ } auto outputFrames = buffer.size() / outputChannels; - auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); + auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); for (int ch = 0; ch < outputChannels; ++ch) { auto channelData = audioBus->getChannel(ch)->getData(); @@ -152,7 +152,8 @@ const auto uint8Data = reinterpret_cast(decodedData.data()); size_t numFramesDecoded = decodedData.size() / (inputChannelCount * sizeof(int16_t)); - auto audioBus = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); + auto audioBus = + std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); for (int ch = 0; ch < inputChannelCount; ++ch) { auto channelData = audioBus->getChannel(ch)->getData(); diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.h b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.h index 2c35ddeec..3409d6961 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.h @@ -13,7 +13,7 @@ typedef struct objc_object AVAudioConverter; namespace audioapi { -class AudioBus; +class AudioBuffer; class CircularAudioArray; class AudioEventHandlerRegistry; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index 3293f8f20..d7c951aa3 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include From 55bb04b4dfd3ad4076494150841d7a32bdcee284 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 15:33:16 +0100 Subject: [PATCH 09/73] fix: a few fixes --- .../HostObjects/sources/AudioBufferHostObject.cpp | 4 ++-- .../common/cpp/audioapi/core/OfflineAudioContext.cpp | 2 +- .../cpp/audioapi/core/sources/AudioBufferSourceNode.cpp | 2 +- .../common/cpp/audioapi/dsp/Convolver.cpp | 8 ++++++-- .../ios/audioapi/ios/core/IOSAudioPlayer.mm | 5 ++--- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 3 ++- .../ios/audioapi/ios/core/utils/AudioDecoder.mm | 8 ++++---- .../ios/audioapi/ios/core/utils/IOSRecorderCallback.mm | 6 ++++-- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp index cdaff96b0..1ac1d28af 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp @@ -58,7 +58,7 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, copyFromChannel) { auto arrayBuffer = args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); auto destination = reinterpret_cast(arrayBuffer.data(runtime)); - auto length = static_cast(arrayBuffer.size(runtime)); + auto length = arrayBuffer.size(runtime) / sizeof(float); auto channelNumber = static_cast(args[1].getNumber()); auto startInChannel = static_cast(args[2].getNumber()); @@ -71,7 +71,7 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, copyToChannel) { auto arrayBuffer = args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); auto source = reinterpret_cast(arrayBuffer.data(runtime)); - auto length = static_cast(arrayBuffer.size(runtime)); + auto length = arrayBuffer.size(runtime) / sizeof(float); auto channelNumber = static_cast(args[1].getNumber()); auto startInChannel = static_cast(args[2].getNumber()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp index 9b2b15f7a..79cb330cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp @@ -74,7 +74,7 @@ void OfflineAudioContext::renderAudio() { destination_->renderAudio(audioBus, framesToProcess); - resultBus_->copy(*audioBus, currentSampleFrame_, 0, framesToProcess); + resultBus_->copy(*audioBus, 0, currentSampleFrame_, framesToProcess); currentSampleFrame_ += framesToProcess; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index ce42ba036..591698447 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -97,7 +97,7 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer size_t totalSize = buffer_->getSize() + extraTailFrames; alignedBus_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); - alignedBus_->copy(*buffer_); + alignedBus_->copy(*buffer_, 0, 0, buffer_->getSize()); alignedBus_->zero(buffer_->getSize(), extraTailFrames); } else { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp index 5d23ef200..29a619251 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp @@ -38,8 +38,12 @@ void Convolver::reset() { _segments.clear(); _segmentsIR.clear(); _preMultiplied.clear(); - _fftBuffer->zero(); - _inputBuffer->zero(); + if (_fftBuffer) { + _fftBuffer->zero(); + } + if (_inputBuffer) { + _inputBuffer->zero(); + } } bool Convolver::init(size_t blockSize, const audioapi::AudioArray &ir, size_t irLen) { diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm index 1c33c8ed0..e86575440 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm @@ -29,9 +29,8 @@ for (int channel = 0; channel < channelCount_; channel += 1) { float *outputChannel = (float *)outputData->mBuffers[channel].mData; - auto *inputChannel = audioBus_->getChannel(channel)->getData(); - - memcpy(outputChannel + processedFrames, inputChannel, framesToProcess * sizeof(float)); + + audioBus_->getChannel(channel)->copyTo(outputChannel, 0, processedFrames, framesToProcess); } processedFrames += framesToProcess; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index 9e6168fcb..70e6d2408 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -53,7 +53,8 @@ for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { float *channelData = (float *)inputBuffer->mBuffers[channel].mData; - adapterNode_->buff_[channel]->write(channelData, numFrames); + // TODO +// adapterNode_->buff_[channel]->write(channelData, numFrames); } } } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm index 62b7a7914..3d2df2fb5 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm @@ -54,13 +54,13 @@ auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = buffer[i * outputChannels + ch]; } } - return std::make_shared(audioBus); + return audioBus; } std::shared_ptr AudioDecoder::decodeWithFilePath( @@ -156,7 +156,7 @@ std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); for (int ch = 0; ch < inputChannelCount; ++ch) { - auto channelData = audioBus->getChannel(ch)->getData(); + auto channelData = audioBus->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { size_t offset; @@ -171,7 +171,7 @@ channelData[i] = uint8ToFloat(uint8Data[offset], uint8Data[offset + 1]); } } - return std::make_shared(audioBus); + return audioBus; } } // namespace audioapi diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index 12d7b1d5a..bfb43be57 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -150,7 +150,8 @@ // Directly write to circular buffer for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); - circularBus_[i]->push_back(inputChannel, numFrames); + // TODO +// circularBus_[i]->push_back(inputChannel, numFrames); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { @@ -197,7 +198,8 @@ for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - circularBus_[i]->push_back(inputChannel, outputFrameCount); + // TODO + //circularBus_[i]->push_back(inputChannel, outputFrameCount); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { From e3b937c4167f66612c88338aad27c9144448dcdd Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 15:33:57 +0100 Subject: [PATCH 10/73] fix: lint --- .../ios/audioapi/ios/core/IOSAudioPlayer.mm | 2 +- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 2 +- .../ios/audioapi/ios/core/utils/IOSRecorderCallback.mm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm index e86575440..4d1d61b05 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm @@ -29,7 +29,7 @@ for (int channel = 0; channel < channelCount_; channel += 1) { float *outputChannel = (float *)outputData->mBuffers[channel].mData; - + audioBus_->getChannel(channel)->copyTo(outputChannel, 0, processedFrames, framesToProcess); } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index 70e6d2408..d71f68ca2 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -54,7 +54,7 @@ float *channelData = (float *)inputBuffer->mBuffers[channel].mData; // TODO -// adapterNode_->buff_[channel]->write(channelData, numFrames); + // adapterNode_->buff_[channel]->write(channelData, numFrames); } } } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index bfb43be57..d8ad00655 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -151,7 +151,7 @@ for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); // TODO -// circularBus_[i]->push_back(inputChannel, numFrames); + // circularBus_[i]->push_back(inputChannel, numFrames); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { From 83e8fdde9dff27e31f9aa4144492e515803a3250 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Feb 2026 16:17:35 +0100 Subject: [PATCH 11/73] fix: fixed tests --- .../common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 6ae4eb87d..474e953d0 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -28,7 +28,7 @@ class TestableWaveShaperNode : public WaveShaperNode { public: explicit TestableWaveShaperNode(std::shared_ptr context) : WaveShaperNode(context, WaveShaperOptions()) { - testCurve_ = std::make_shared(3); + testCurve_ = std::make_shared(3); auto data = testCurve_->span(); data[0] = -2.0f; data[1] = 0.0f; @@ -41,7 +41,7 @@ class TestableWaveShaperNode : public WaveShaperNode { return WaveShaperNode::processNode(processingBus, framesToProcess); } - std::shared_ptr testCurve_; + std::shared_ptr testCurve_; }; TEST_F(WaveShaperNodeTest, WaveShaperNodeCanBeCreated) { From 122b9e0b042e02fc7123f5eabd2b45f7893cbc42 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 07:34:05 +0100 Subject: [PATCH 12/73] fix: todos --- .../android/core/AndroidAudioRecorder.cpp | 27 +++++++++---------- .../android/core/AndroidAudioRecorder.h | 3 --- .../core/utils/AndroidRecorderCallback.h | 2 -- .../cpp/audioapi/core/inputs/AudioRecorder.h | 4 +++ .../core/utils/AudioRecorderCallback.h | 2 ++ .../common/cpp/audioapi/utils/AudioArray.cpp | 7 +++-- .../common/cpp/audioapi/utils/AudioArray.h | 1 + .../ios/audioapi/ios/core/IOSAudioRecorder.h | 1 + .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 26 +++++++++++------- .../ios/core/utils/IOSRecorderCallback.mm | 20 +++++++++++--- 10 files changed, 56 insertions(+), 37 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index 30bcc4bba..fad36c5c2 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -28,8 +28,7 @@ AndroidAudioRecorder::AndroidAudioRecorder( const std::shared_ptr &audioEventHandlerRegistry) : AudioRecorder(audioEventHandlerRegistry), streamSampleRate_(0.0), - streamChannelCount_(0), - streamMaxBufferSizeInFrames_(0) {} + streamChannelCount_(0) {} /// @brief Destructor ensures that the audio stream and each output type are closed and flushed up remaining data. /// TODO: Possibly locks here are not necessary, but we might have an issue with oboe having raw pointer to the @@ -91,7 +90,7 @@ Result AndroidAudioRecorder::openAudioStream() { streamSampleRate_ = static_cast(mStream_->getSampleRate()); streamChannelCount_ = mStream_->getChannelCount(); - streamMaxBufferSizeInFrames_ = mStream_->getBufferSizeInFrames(); + maxBufferSizeInFrames_ = mStream_->getBufferSizeInFrames(); return Result::Ok(None); } @@ -126,7 +125,7 @@ Result AndroidAudioRecorder::start(const std::string & ->openFile( streamSampleRate_, streamChannelCount_, - streamMaxBufferSizeInFrames_, + maxBufferSizeInFrames_, fileNameOverride); if (!fileResult.is_ok()) { @@ -139,12 +138,12 @@ Result AndroidAudioRecorder::start(const std::string & if (usesCallback()) { std::static_pointer_cast(dataCallback_) - ->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_); + ->prepare(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_); } if (isConnected()) { - deinterleavingBuffer_ = std::make_shared(streamMaxBufferSizeInFrames_); - adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_); + deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); } auto result = mStream_->requestStart(); @@ -233,7 +232,7 @@ Result AndroidAudioRecorder::enableFileOutput( if (!isIdle()) { auto fileResult = std::static_pointer_cast(fileWriter_) - ->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_, ""); + ->openFile(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_, ""); if (!fileResult.is_ok()) { return Result::Err( @@ -298,7 +297,7 @@ Result AndroidAudioRecorder::setOnAudioReadyCallback( if (!isIdle()) { std::static_pointer_cast(dataCallback_) - ->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_); + ->prepare(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_); } callbackOutputEnabled_.store(true, std::memory_order_release); @@ -324,8 +323,8 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n adapterNode_ = node; if (!isIdle()) { - deinterleavingBuffer_ = std::make_shared(streamMaxBufferSizeInFrames_); - adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_); + deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); } isConnected_.store(true, std::memory_order_release); @@ -337,7 +336,7 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n void AndroidAudioRecorder::disconnect() { std::scoped_lock adapterLock(adapterNodeMutex_); isConnected_.store(false, std::memory_order_release); - deinterleavingBuffer_ = nullptr; + deinterleavingArray_ = nullptr; adapterNode_ = nullptr; } @@ -375,13 +374,13 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady( if (isConnected()) { if (auto adapterLock = Locker::tryLock(adapterNodeMutex_)) { for (int channel = 0; channel < streamChannelCount_; ++channel) { - auto channelData = deinterleavingBuffer_->span(); + auto channelData = deinterleavingArray_->span(); for (int frame = 0; frame < numFrames; ++frame) { channelData[frame] = static_cast(audioData)[frame * streamChannelCount_ + channel]; } - adapterNode_->buff_[channel]->write(*deinterleavingBuffer_, numFrames); + adapterNode_->buff_[channel]->write(*deinterleavingArray_, numFrames); } } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h index 625fa91d9..7a242c479 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h @@ -55,11 +55,8 @@ class AndroidAudioRecorder : public oboe::AudioStreamCallback, public AudioRecor void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override; private: - std::shared_ptr deinterleavingBuffer_; - float streamSampleRate_; int32_t streamChannelCount_; - int32_t streamMaxBufferSizeInFrames_; facebook::jni::global_ref nativeAudioRecorder_; diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h index a20a4eb47..614967462 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h @@ -44,8 +44,6 @@ class AndroidRecorderCallback : public AudioRecorderCallback { ma_uint64 processingBufferLength_{0}; std::unique_ptr converter_{nullptr}; - std::shared_ptr deinterleavingArray_; - void deinterleaveAndPushAudioData(void *data, int numFrames); private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index c2b196765..7c379fe3c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -65,6 +66,9 @@ class AudioRecorder { std::atomic fileOutputEnabled_{false}; std::atomic callbackOutputEnabled_{false}; + std::shared_ptr deinterleavingArray_; + size_t maxBufferSizeInFrames_ = 0; + std::mutex callbackMutex_; std::mutex fileWriterMutex_; std::mutex errorCallbackMutex_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h index 74346aaef..d93e478e6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h @@ -43,6 +43,8 @@ class AudioRecorderCallback { uint64_t callbackId_; size_t ringBufferSize_; + std::shared_ptr deinterleavingArray_; + std::atomic errorCallbackId_{0}; std::shared_ptr audioEventHandlerRegistry_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index af555c1bb..41713ff9e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -22,7 +22,6 @@ AudioArray::AudioArray(const float *data, size_t size) : size_(size) { if (size_ > 0) { data_ = std::make_unique(size_); copy(data, 0, 0, size_); - std::memcpy(data_.get(), data, size_ * sizeof(float)); } } @@ -173,9 +172,9 @@ void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinati } void AudioArray::reverse() { - if (data_ == nullptr && size_ > 1) { - return; - } + if (data_ == nullptr && size_ > 1) { + return; + } std::reverse(begin(), end()); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index 85af5c272..fe7b26794 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -63,6 +63,7 @@ class AudioArray { if (offset + length > size_) { throw std::out_of_range("AudioArray::subSpan - offset + length exceeds array size"); } + return {data_.get() + offset, length}; } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h index 9137b9405..fc7174bb9 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h @@ -11,6 +11,7 @@ typedef struct objc_object NativeAudioRecorder; #include #include +#include #include diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index d71f68ca2..ad7227a5c 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -51,10 +51,14 @@ if (isConnected()) { if (auto lock = Locker::tryLock(adapterNodeMutex_)) { for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { - float *channelData = (float *)inputBuffer->mBuffers[channel].mData; - - // TODO - // adapterNode_->buff_[channel]->write(channelData, numFrames); + float *data = (float *)inputBuffer->mBuffers[channel].mData; + auto channelData = deinterleavingArray_->span(); + + for (int frame = 0; frame < numFrames; ++frame) { + channelData[frame] = data[frame]; + } + + adapterNode_->buff_[channel]->write(*deinterleavingArray_, numFrames); } } } @@ -100,12 +104,12 @@ [AudioEngine.sharedInstance stopIfNecessary]; // Estimate the maximum input buffer lengths that can be expected from the sink node - size_t maxInputBufferLength = [nativeRecorder_ getBufferSize]; + maxBufferSizeInFrames_ = [nativeRecorder_ getBufferSize]; auto inputFormat = [nativeRecorder_ getInputFormat]; if (usesFileOutput()) { auto fileResult = std::static_pointer_cast(fileWriter_) - ->openFile(inputFormat, maxInputBufferLength, fileNameOverride); + ->openFile(inputFormat, maxBufferSizeInFrames_, fileNameOverride); if (fileResult.is_err()) { return Result::Err( @@ -117,7 +121,7 @@ if (usesCallback()) { auto callbackResult = std::static_pointer_cast(dataCallback_) - ->prepare(inputFormat, maxInputBufferLength); + ->prepare(inputFormat, maxBufferSizeInFrames_); if (callbackResult.is_err()) { return Result::Err( @@ -126,8 +130,9 @@ } if (isConnected()) { + deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); // TODO: pass sample rate, in case conversion is necessary - adapterNode_->init(maxInputBufferLength, inputFormat.channelCount); + adapterNode_->init(maxBufferSizeInFrames_, inputFormat.channelCount); } [nativeRecorder_ start]; @@ -227,8 +232,8 @@ adapterNode_ = node; if (!isIdle()) { - adapterNode_->init( - [nativeRecorder_ getBufferSize], [nativeRecorder_ getInputFormat].channelCount); + deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + adapterNode_->init(maxBufferSizeInFrames_, [nativeRecorder_ getInputFormat].channelCount); } isConnected_.store(true, std::memory_order_release); @@ -240,6 +245,7 @@ void IOSAudioRecorder::disconnect() { std::scoped_lock lock(adapterNodeMutex_); + deinterleavingArray_ = nullptr; adapterNode_ = nullptr; isConnected_.store(false, std::memory_order_release); } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index d8ad00655..f0d3393a4 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -71,6 +71,8 @@ converterOutputBufferSize_ = std::max( (double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength); + + deinterleavingArray_ = std::make_shared(converterOutputBufferSize_); callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:sampleRate_ @@ -150,8 +152,13 @@ // Directly write to circular buffer for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); - // TODO - // circularBus_[i]->push_back(inputChannel, numFrames); + auto channelData = deinterleavingArray_->span(); + + for (int frame = 0; frame < numFrames; ++frame) { + channelData[frame] = inputChannel[frame]; + } + + circularBus_[i]->push_back(*deinterleavingArray_, numFrames); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { @@ -198,8 +205,13 @@ for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - // TODO - //circularBus_[i]->push_back(inputChannel, outputFrameCount); + auto channelData = deinterleavingArray_->span(); + + for (int frame = 0; frame < numFrames; ++frame) { + channelData[frame] = inputChannel[frame]; + } + + circularBus_[i]->push_back(*deinterleavingArray_, outputFrameCount); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { From a87f2f7e97f17249b1524bfc1aad3327afeed330 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 08:30:13 +0100 Subject: [PATCH 13/73] refactor: nits --- .../android/core/AndroidAudioRecorder.cpp | 14 ++-- .../audioapi/core/analysis/AnalyserNode.cpp | 44 +++++----- .../cpp/audioapi/core/analysis/AnalyserNode.h | 6 +- .../cpp/audioapi/core/inputs/AudioRecorder.h | 2 +- .../common/cpp/audioapi/dsp/VectorMath.cpp | 33 ++++++-- .../common/cpp/audioapi/dsp/VectorMath.h | 5 +- .../common/cpp/audioapi/utils/AudioArray.cpp | 83 ++++--------------- .../utils/CircularOverflowableAudioArray.h | 4 +- .../ios/audioapi/ios/core/IOSAudioRecorder.h | 2 +- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 8 +- .../ios/core/utils/IOSRecorderCallback.mm | 17 +--- 11 files changed, 87 insertions(+), 131 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index fad36c5c2..37e9eb528 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -26,9 +26,7 @@ namespace audioapi { AndroidAudioRecorder::AndroidAudioRecorder( const std::shared_ptr &audioEventHandlerRegistry) - : AudioRecorder(audioEventHandlerRegistry), - streamSampleRate_(0.0), - streamChannelCount_(0) {} + : AudioRecorder(audioEventHandlerRegistry), streamSampleRate_(0.0), streamChannelCount_(0) {} /// @brief Destructor ensures that the audio stream and each output type are closed and flushed up remaining data. /// TODO: Possibly locks here are not necessary, but we might have an issue with oboe having raw pointer to the @@ -121,12 +119,10 @@ Result AndroidAudioRecorder::start(const std::string & } if (usesFileOutput()) { - auto fileResult = std::static_pointer_cast(fileWriter_) - ->openFile( - streamSampleRate_, - streamChannelCount_, - maxBufferSizeInFrames_, - fileNameOverride); + auto fileResult = + std::static_pointer_cast(fileWriter_) + ->openFile( + streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_, fileNameOverride); if (!fileResult.is_ok()) { return Result::Err( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 70c9ae50d..b1ae3b6c3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -24,12 +24,12 @@ AnalyserNode::AnalyserNode( maxDecibels_(options.maxDecibels), smoothingTimeConstant_(options.smoothingTimeConstant), windowType_(WindowType::BLACKMAN), - inputBuffer_(std::make_unique(MAX_FFT_SIZE * 2)), + inputArray_(std::make_unique(MAX_FFT_SIZE * 2)), downMixBus_(std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), - tempBuffer_(std::make_unique(fftSize_)), + tempArray_(std::make_unique(fftSize_)), fft_(std::make_unique(fftSize_)), complexData_(std::vector>(fftSize_)), - magnitudeBuffer_(std::make_unique(fftSize_ / 2)) { + magnitudeArray_(std::make_unique(fftSize_ / 2)) { setWindowData(windowType_, fftSize_); isInitialized_ = true; } @@ -66,8 +66,8 @@ void AnalyserNode::setFftSize(int fftSize) { fftSize_ = fftSize; fft_ = std::make_unique(fftSize_); complexData_ = std::vector>(fftSize_); - magnitudeBuffer_ = std::make_unique(fftSize_ / 2); - tempBuffer_ = std::make_unique(fftSize_); + magnitudeArray_ = std::make_unique(fftSize_ / 2); + tempArray_ = std::make_unique(fftSize_); setWindowData(windowType_, fftSize_); } @@ -90,16 +90,19 @@ void AnalyserNode::setWindowType(AnalyserNode::WindowType type) { void AnalyserNode::getFloatFrequencyData(float *data, int length) { doFFTAnalysis(); - length = std::min(static_cast(magnitudeBuffer_->getSize()), length); - // TODO - // dsp::linearToDecibels(magnitudeBuffer_->getData(), data, length); + length = std::min(static_cast(magnitudeArray_->getSize()), length); + auto magnitudeSpan = magnitudeArray_->span(); + + for (int i = 0; i < length; i++) { + data[i] = dsp::linearToDecibels(magnitudeSpan[i]); + } } void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { doFFTAnalysis(); - auto magnitudeBufferData = magnitudeBuffer_->span(); - length = std::min(static_cast(magnitudeBuffer_->getSize()), length); + auto magnitudeBufferData = magnitudeArray_->span(); + length = std::min(static_cast(magnitudeArray_->getSize()), length); const auto rangeScaleFactor = maxDecibels_ == minDecibels_ ? 1 : 1 / (maxDecibels_ - minDecibels_); @@ -122,16 +125,17 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { void AnalyserNode::getFloatTimeDomainData(float *data, int length) { auto size = std::min(fftSize_, length); -// TODO -// inputBuffer_->pop_back(data, size, std::max(0, fftSize_ - size), true); + + inputArray_->pop_back(*tempArray_, size, std::max(0, fftSize_ - size), true); + tempArray_->copyTo(data, 0, 0, size); } void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { auto size = std::min(fftSize_, length); - inputBuffer_->pop_back(*tempBuffer_, fftSize_, std::max(0, fftSize_ - size), true); + inputArray_->pop_back(*tempArray_, size, std::max(0, fftSize_ - size), true); - auto values = tempBuffer_->span(); + auto values = tempArray_->span(); for (int i = 0; i < size; i++) { float scaledValue = 128 * (values[i] + 1); @@ -156,7 +160,7 @@ std::shared_ptr AnalyserNode::processNode( // Down mix the input bus to mono downMixBus_->copy(*processingBus); // Copy the down mixed bus to the input buffer (circular buffer) - inputBuffer_->push_back(*downMixBus_->getChannel(0), framesToProcess, true); + inputArray_->push_back(*downMixBus_->getChannel(0), framesToProcess, true); shouldDoFFTAnalysis_ = true; @@ -172,20 +176,20 @@ void AnalyserNode::doFFTAnalysis() { // We want to copy last fftSize_ elements added to the input buffer to apply // the window. - inputBuffer_->pop_back(*tempBuffer_, fftSize_, 0, true); + inputArray_->pop_back(*tempArray_, fftSize_, 0, true); - tempBuffer_->multiply(*windowData_, fftSize_); + tempArray_->multiply(*windowData_, fftSize_); // do fft analysis - get frequency domain data - fft_->doFFT(*tempBuffer_, complexData_); + fft_->doFFT(*tempArray_, complexData_); // Zero out nquist component complexData_[0] = std::complex(complexData_[0].real(), 0); const float magnitudeScale = 1.0f / static_cast(fftSize_); - auto magnitudeBufferData = magnitudeBuffer_->span(); + auto magnitudeBufferData = magnitudeArray_->span(); - for (int i = 0; i < magnitudeBuffer_->getSize(); i++) { + for (int i = 0; i < magnitudeArray_->getSize(); i++) { auto scalarMagnitude = std::abs(complexData_[i]) * magnitudeScale; magnitudeBufferData[i] = static_cast( smoothingTimeConstant_ * magnitudeBufferData[i] + diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 3ef915803..0dea5b123 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -56,13 +56,13 @@ class AnalyserNode : public AudioNode { WindowType windowType_; std::shared_ptr windowData_; - std::unique_ptr inputBuffer_; + std::unique_ptr inputArray_; std::unique_ptr downMixBus_; - std::unique_ptr tempBuffer_; + std::unique_ptr tempArray_; std::unique_ptr fft_; std::vector> complexData_; - std::unique_ptr magnitudeBuffer_; + std::unique_ptr magnitudeArray_; bool shouldDoFFTAnalysis_{true}; void doFFTAnalysis(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index 7c379fe3c..7df5cf075 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp index 379dd3dfd..4cadc42be 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp @@ -656,13 +656,32 @@ void multiplyByScalarThenAddToOutput( #endif -void linearToDecibels( - const float *inputVector, - float *outputVector, - size_t numberOfElementsToProcess) { - for (int i = 0; i < numberOfElementsToProcess; i++) { - outputVector[i] = dsp::linearToDecibels(inputVector[i]); +float computeConvolution(const float *state, const float *kernel, size_t kernelSize) { + float sum = 0.0f; + int k = 0; + +#ifdef HAVE_ARM_NEON_INTRINSICS + float32x4_t vSum = vdupq_n_f32(0.0f); + + // process 4 samples at a time + for (; k <= kernelSize - 4; k += 4) { + float32x4_t vState = vld1q_f32(state + k); + float32x4_t vKernel = vld1q_f32(kernel + k); + + // fused multiply-add: vSum += vState * vKernel + vSum = vmlaq_f32(vSum, vState, vKernel); } -} + // horizontal reduction: Sum the 4 lanes of vSum into a single float + sum += vgetq_lane_f32(vSum, 0); + sum += vgetq_lane_f32(vSum, 1); + sum += vgetq_lane_f32(vSum, 2); + sum += vgetq_lane_f32(vSum, 3); +#endif + for (; k < kernelSize; ++k) { + sum += state[k] * kernel[k]; + } + + return sum; +} } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h index 0451ee125..fd11dfaa1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h @@ -69,9 +69,6 @@ void multiply( // Finds the maximum magnitude of a float vector. float maximumMagnitude(const float *inputVector, size_t numberOfElementsToProcess); -void linearToDecibels( - const float *inputVector, - float *outputVector, - size_t numberOfElementsToProcess); +float computeConvolution(const float *state, const float *kernel, size_t kernelSize); } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 41713ff9e..69efaf367 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -5,10 +5,6 @@ #include #include -#if defined(__ARM_NEON) -#include -#endif - namespace audioapi { AudioArray::AudioArray(size_t size): size_(size) { @@ -79,10 +75,6 @@ void AudioArray::zero() noexcept { } void AudioArray::zero(size_t start, size_t length) noexcept { - if (data_ == nullptr || length <= 0) { - return; - } - memset(data_.get() + start, 0, length * sizeof(float)); } @@ -96,9 +88,9 @@ void AudioArray::sum( size_t destinationStart, size_t length, float gain) { - if (length == 0 || data_ == nullptr || source.data_ == nullptr) { - return; - } + if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough data to sum two vectors."); + } // Using restrict to inform the compiler that the source and destination do not overlap float* __restrict dest = data_.get() + destinationStart; @@ -112,8 +104,8 @@ void AudioArray::multiply(const AudioArray &source) { } void AudioArray::multiply(const audioapi::AudioArray &source, size_t length) { - if (data_ == nullptr || source.data_ == nullptr) { - return; + if (size_ < length || source.size_ < length) { + throw std::out_of_range("Not enough data to perform vector multiplication."); } float* __restrict dest = data_.get(); @@ -131,17 +123,17 @@ void AudioArray::copy( size_t sourceStart, size_t destinationStart, size_t length) { - if (length == 0 || data_ == nullptr || source.data_ == nullptr) { - return; - } + if (source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough data to copy from source."); + } copy(source.data_.get(), sourceStart, destinationStart, length); } void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length) { - if (length == 0 || data_ == nullptr || source.data_ == nullptr) { - return; + if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough space to copy to destination or from source."); } auto dstView = this->subSpan(length, destinationStart); @@ -155,8 +147,8 @@ void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceSt void AudioArray::copy(const float *source, size_t sourceStart, size_t destinationStart, size_t length) { - if (length == 0 || data_ == nullptr || source == nullptr) { - return; + if (size_ - destinationStart < length) { + throw std::out_of_range("Not enough space to copy to destination."); } memcpy(data_.get() + destinationStart, source + sourceStart, length * sizeof(float)); @@ -164,15 +156,15 @@ void AudioArray::copy(const float *source, size_t sourceStart, size_t destinatio void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinationStart, size_t length) const { - if (length == 0 || data_ == nullptr || destination == nullptr) { - return; + if (size_ - sourceStart < length) { + throw std::out_of_range("Not enough data to copy from source."); } memcpy(destination + destinationStart, data_.get() + sourceStart, length * sizeof(float)); } void AudioArray::reverse() { - if (data_ == nullptr && size_ > 1) { + if (size_ > 1) { return; } @@ -190,58 +182,19 @@ void AudioArray::normalize() { } void AudioArray::scale(float value) { - if (data_ == nullptr) { - return; - } - dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); } float AudioArray::getMaxAbsValue() const { - if (data_ == nullptr) { - return 0.0f; - } - return dsp::maximumMagnitude(data_.get(), size_); } float AudioArray::computeConvolution(const audioapi::AudioArray &kernel, size_t startIndex) const { - const auto kernelSize = kernel.size_; - - if (startIndex + kernelSize > size_ || !data_ || !kernel.data_) { - return 0.0f; - } - - const auto stateStart = data_.get() + startIndex; - const auto kernelStart = kernel.data_.get(); - - float sum = 0.0f; - size_t k = 0; - -#ifdef __ARM_NEON - float32x4_t vSum = vdupq_n_f32(0.0f); - - // process 4 samples at a time - for (; k <= kernelSize - 4; k += 4) { - float32x4_t vState = vld1q_f32(stateStart + k); - float32x4_t vKernel = vld1q_f32(kernelStart + k); - - // fused multiply-add: vSum += vState * vKernel - vSum = vmlaq_f32(vSum, vState, vKernel); - } - - // horizontal reduction: Sum the 4 lanes of vSum into a single float - sum += vgetq_lane_f32(vSum, 0); - sum += vgetq_lane_f32(vSum, 1); - sum += vgetq_lane_f32(vSum, 2); - sum += vgetq_lane_f32(vSum, 3); -#endif - - for (; k < kernelSize; ++k) { - sum += stateStart[k] * kernelStart[k]; + if (kernel.size_ > size_ - startIndex) { + throw std::out_of_range("Kernal size exceeds available data for convolution."); } - return sum; + return dsp::computeConvolution(data_.get() + startIndex, kernel.data_.get(), kernel.size_); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h index b4889857b..588c72d4e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h @@ -21,12 +21,12 @@ class CircularOverflowableAudioArray : public AudioArray { /// @brief Writes data to the circular buffer. /// @note Might wait for read operation to finish if it is in progress. It ignores writes that exceed the buffer size. - /// @param data Pointer to the input buffer. + /// @param data Reference to input AudioArray. /// @param size Number of frames to write. void write(const AudioArray &data, size_t size); /// @brief Reads data from the circular buffer. - /// @param output Pointer to the output buffer. + /// @param output Reference to output AudioArray. /// @param size Number of frames to read. /// @return The number of frames actually read. size_t read(AudioArray &data, size_t size) const; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h index fc7174bb9..ed356c18e 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h @@ -10,8 +10,8 @@ typedef struct objc_object NativeAudioRecorder; #endif // __OBJC__ #include -#include #include +#include #include diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index ad7227a5c..e77ceb39c 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -52,12 +52,8 @@ if (auto lock = Locker::tryLock(adapterNodeMutex_)) { for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { float *data = (float *)inputBuffer->mBuffers[channel].mData; - auto channelData = deinterleavingArray_->span(); - - for (int frame = 0; frame < numFrames; ++frame) { - channelData[frame] = data[frame]; - } - + + deinterleavingArray_->copy(data, 0, 0, numFrames); adapterNode_->buff_[channel]->write(*deinterleavingArray_, numFrames); } } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index f0d3393a4..b4ef82b2c 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -71,7 +71,7 @@ converterOutputBufferSize_ = std::max( (double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength); - + deinterleavingArray_ = std::make_shared(converterOutputBufferSize_); callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 @@ -152,12 +152,8 @@ // Directly write to circular buffer for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); - auto channelData = deinterleavingArray_->span(); - for (int frame = 0; frame < numFrames; ++frame) { - channelData[frame] = inputChannel[frame]; - } - + deinterleavingArray_->copy(inputChannel, 0, 0, numFrames); circularBus_[i]->push_back(*deinterleavingArray_, numFrames); } @@ -203,14 +199,9 @@ } for (int i = 0; i < channelCount_; ++i) { - auto *inputChannel = - static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - auto channelData = deinterleavingArray_->span(); + auto *data = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - for (int frame = 0; frame < numFrames; ++frame) { - channelData[frame] = inputChannel[frame]; - } - + deinterleavingArray_->copy(data, 0, 0, outputFrameCount); circularBus_[i]->push_back(*deinterleavingArray_, outputFrameCount); } From 58471e4b7a42142f9c72679de847e6ce9a469150 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 08:38:32 +0100 Subject: [PATCH 14/73] fix: nitpick --- .../common/cpp/audioapi/utils/AudioArray.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 69efaf367..04b3a17bb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -164,7 +164,7 @@ void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinati } void AudioArray::reverse() { - if (size_ > 1) { + if (size_ <= 1) { return; } From 1b51ce674a5601f76169896ada2db7f3f7e3a7c0 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 12:37:42 +0100 Subject: [PATCH 15/73] refactor: added copyWithin - memmove to audio array --- .../common/cpp/audioapi/utils/AudioArray.cpp | 8 ++++ .../common/cpp/audioapi/utils/AudioArray.h | 39 ++++++++++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 04b3a17bb..8c938e311 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -163,6 +163,14 @@ void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinati memcpy(destination + destinationStart, data_.get() + sourceStart, length * sizeof(float)); } +void AudioArray::copyWithin(size_t sourceStart, size_t destinationStart, size_t length) { + if (size_ - sourceStart < length || size_ - destinationStart < length) { + throw std::out_of_range("Not enough space for moving data or data to move."); + } + + memmove(data_.get() + destinationStart, data_.get() + sourceStart, length); +} + void AudioArray::reverse() { if (size_ <= 1) { return; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index fe7b26794..4c324de87 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -75,7 +75,7 @@ class AudioArray { /// @brief Sums the source AudioArray into this AudioArray with an optional gain. /// @param source The source AudioArray to sum from. /// @param gain The gain to apply to the source before summing. Default is 1.0f. - /// @note Assumes that source and this AudioArray are not the same. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void sum(const AudioArray &source, float gain = 1.0f); /// @brief Sums the source AudioArray into this AudioArray with an optional gain. @@ -84,7 +84,7 @@ class AudioArray { /// @param destinationStart The starting index in this AudioArray. /// @param length The number of samples to sum. /// @param gain The gain to apply to the source before summing. Default is 1.0f. - /// @note Assumes that source and this AudioArray are not the same. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void sum( const AudioArray &source, size_t sourceStart, @@ -94,16 +94,26 @@ class AudioArray { /// @brief Multiplies this AudioArray by the source AudioArray element-wise. /// @param source The source AudioArray to multiply with. - /// @note Assumes that source and this AudioArray are not the same. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void multiply(const AudioArray &source); /// @brief Multiplies this AudioArray by the source AudioArray element-wise. /// @param source The source AudioArray to multiply with. /// @param length The number of samples to multiply. - /// @note Assumes that source and this AudioArray are not the same. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void multiply(const AudioArray &source, size_t length); + /// @brief Copies source AudioArray into this AudioArray + /// @param source The source AudioArray to copy. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioArray &source); + + /// @brief Copies source AudioArray into this AudioArray + /// @param source The source AudioArray to copy. + /// @param sourceStart The starting index in the source AudioArray. + /// @param destinationStart The starting index in this AudioArray. + /// @param length The number of samples to copy. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); /// @brief Copies data from the source AudioArray in reverse order into this AudioArray. @@ -111,13 +121,32 @@ class AudioArray { /// @param sourceStart The starting index in the source AudioArray. /// @param destinationStart The starting index in this AudioArray. /// @param length The number of samples to copy. - /// @note Assumes that source and this AudioArray are not the same. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void copyReverse(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Copies data from a raw float pointer into this AudioArray. + /// @param source The source float pointer to copy from. + /// @param sourceStart The starting index in the source float pointer. + /// @param destinationStart The starting index in this AudioArray. + /// @param length The number of samples to copy. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const float *source, size_t sourceStart, size_t destinationStart, size_t length); + + /// @brief Copies data to a raw float pointer from this AudioArray. + /// @param destination The destination float pointer to copy to. + /// @param sourceStart The starting index in the this AudioArray. + /// @param destinationStart The starting index in the destination float pointer. + /// @param length The number of samples to copy. + /// @note Assumes that destination and this are located in two distinct, non-overlapping memory locations. void copyTo(float *destination, size_t sourceStart, size_t destinationStart, size_t length) const; + /// @brief Moves data inside this AudioArray. + /// @param sourceStart The starting index in the source float pointer. + /// @param destinationStart The starting index of memory. + /// @param length The number of samples to copy. + void copyWithin(size_t sourceStart, size_t destinationStart, size_t length); + void reverse(); void normalize(); void scale(float value); From 8127fa219a00b16b22a8b22fec8898f6ae5705dc Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 13:03:36 +0100 Subject: [PATCH 16/73] fix: memmove --- .../audioapi/core/effects/ConvolverNode.cpp | 5 +- .../common/cpp/audioapi/dsp/Convolver.cpp | 2 +- .../common/cpp/audioapi/dsp/Resampler.cpp | 4 +- .../common/cpp/audioapi/utils/AudioArray.cpp | 160 ++++++++++-------- .../common/cpp/audioapi/utils/AudioArray.h | 6 +- 5 files changed, 94 insertions(+), 83 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index f107e8041..2a026ccb5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -121,10 +121,11 @@ std::shared_ptr ConvolverNode::processNode( audioBus_->copy(*internalBuffer_, 0, 0, framesToProcess); int remainingFrames = internalBufferIndex_ - framesToProcess; if (remainingFrames > 0) { - for (int i = 0; i < internalBuffer_->getNumberOfChannels(); ++i) { - internalBuffer_->copy(*internalBuffer_, framesToProcess, 0, remainingFrames); + for (int ch = 0; ch < internalBuffer_->getNumberOfChannels(); ++ch) { + internalBuffer_->getChannel(ch)->copyWithin(framesToProcess, 0, remainingFrames); } } + internalBufferIndex_ -= framesToProcess; for (int i = 0; i < audioBus_->getNumberOfChannels(); ++i) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp index 29a619251..ca73e4d5a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp @@ -172,7 +172,7 @@ void Convolver::process(const AudioArray &input, AudioArray &output) { // The input buffer acts as a 2B-point sliding window of the input signal. // With each new input block, the right half of the input buffer is shifted // to the left and the new block is stored in the right half. - _inputBuffer->copy(*_inputBuffer, _blockSize, 0, _blockSize); + _inputBuffer->copyWithin(_blockSize, 0, _blockSize); _inputBuffer->copy(input, 0, _blockSize, _blockSize); // All contents (DFT spectra) in the FDL are shifted up by one slot. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp index 80a262f18..278f51474 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp @@ -107,7 +107,7 @@ int UpSampler::process( } // move new data to history [ NEW DATA | EMPTY ] - stateBuffer_->copy(*stateBuffer_, framesToProcess, 0, kernelSize_); + stateBuffer_->copyWithin(framesToProcess, 0, kernelSize_); return framesToProcess * 2; } @@ -151,7 +151,7 @@ int DownSampler::process( } // move new data to history [ NEW DATA | EMPTY ] - stateBuffer_->copy(*stateBuffer_, framesToProcess, 0, kernelSize_); + stateBuffer_->copyWithin(framesToProcess, 0, kernelSize_); return outputCount; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 8c938e311..ac7bc1c76 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -2,59 +2,60 @@ #include #include -#include #include +#include namespace audioapi { -AudioArray::AudioArray(size_t size): size_(size) { - if (size_ > 0) { - data_ = std::make_unique(size_); - zero(); - } +AudioArray::AudioArray(size_t size) : size_(size) { + if (size_ > 0) { + data_ = std::make_unique(size_); + zero(); + } } AudioArray::AudioArray(const float *data, size_t size) : size_(size) { - if (size_ > 0) { - data_ = std::make_unique(size_); - copy(data, 0, 0, size_); - } + if (size_ > 0) { + data_ = std::make_unique(size_); + copy(data, 0, 0, size_); + } } AudioArray::AudioArray(const AudioArray &other) : size_(other.size_) { - if (size_ > 0 && other.data_) { - data_ = std::make_unique(size_); - std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); - } + if (size_ > 0 && other.data_) { + data_ = std::make_unique(size_); + std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); + } } -AudioArray::AudioArray(audioapi::AudioArray &&other) noexcept : data_(std::move(other.data_)), size_(other.size_) { +AudioArray::AudioArray(audioapi::AudioArray &&other) noexcept + : data_(std::move(other.data_)), size_(other.size_) { other.size_ = 0; } AudioArray &AudioArray::operator=(const audioapi::AudioArray &other) { - if (this != &other) { - if (size_ != other.size_) { - size_ = other.size_; - data_ = (size_ > 0) ? std::make_unique(size_) : nullptr; - } + if (this != &other) { + if (size_ != other.size_) { + size_ = other.size_; + data_ = (size_ > 0) ? std::make_unique(size_) : nullptr; + } - if (size_ > 0 && data_) { + if (size_ > 0 && data_) { std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); - } } + } - return *this; + return *this; } AudioArray &AudioArray::operator=(audioapi::AudioArray &&other) noexcept { - if (this != &other) { - data_ = std::move(other.data_); - size_ = other.size_; - other.size_ = 0; - } + if (this != &other) { + data_ = std::move(other.data_); + size_ = other.size_; + other.size_ = 0; + } - return *this; + return *this; } void AudioArray::resize(size_t size) { @@ -88,13 +89,13 @@ void AudioArray::sum( size_t destinationStart, size_t length, float gain) { - if (size_ - destinationStart < length || source.size_ - sourceStart < length) { - throw std::out_of_range("Not enough data to sum two vectors."); - } + if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough data to sum two vectors."); + } // Using restrict to inform the compiler that the source and destination do not overlap - float* __restrict dest = data_.get() + destinationStart; - const float* __restrict src = source.data_.get() + sourceStart; + float *__restrict dest = data_.get() + destinationStart; + const float *__restrict src = source.data_.get() + sourceStart; dsp::multiplyByScalarThenAddToOutput(src, gain, dest, length); } @@ -108,8 +109,8 @@ void AudioArray::multiply(const audioapi::AudioArray &source, size_t length) { throw std::out_of_range("Not enough data to perform vector multiplication."); } - float* __restrict dest = data_.get(); - const float* __restrict src = source.data_.get(); + float *__restrict dest = data_.get(); + const float *__restrict src = source.data_.get(); dsp::multiply(src, dest, dest, length); } @@ -130,79 +131,88 @@ void AudioArray::copy( copy(source.data_.get(), sourceStart, destinationStart, length); } -void AudioArray::copyReverse(const audioapi::AudioArray &source, size_t sourceStart, - size_t destinationStart, size_t length) { - if (size_ - destinationStart < length || source.size_ - sourceStart < length) { - throw std::out_of_range("Not enough space to copy to destination or from source."); - } +void AudioArray::copyReverse( + const audioapi::AudioArray &source, + size_t sourceStart, + size_t destinationStart, + size_t length) { + if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough space to copy to destination or from source."); + } - auto dstView = this->subSpan(length, destinationStart); - auto srcView = source.span(); - const float* __restrict srcPtr = &srcView[sourceStart]; + auto dstView = this->subSpan(length, destinationStart); + auto srcView = source.span(); + const float *__restrict srcPtr = &srcView[sourceStart]; - for (size_t i = 0; i < length; ++i) { - dstView[i] = srcPtr[-static_cast(i)]; - } + for (size_t i = 0; i < length; ++i) { + dstView[i] = srcPtr[-static_cast(i)]; + } } -void AudioArray::copy(const float *source, size_t sourceStart, size_t destinationStart, - size_t length) { - if (size_ - destinationStart < length) { - throw std::out_of_range("Not enough space to copy to destination."); - } +void AudioArray::copy( + const float *source, + size_t sourceStart, + size_t destinationStart, + size_t length) { + if (size_ - destinationStart < length) { + throw std::out_of_range("Not enough space to copy to destination."); + } - memcpy(data_.get() + destinationStart, source + sourceStart, length * sizeof(float)); + memcpy(data_.get() + destinationStart, source + sourceStart, length * sizeof(float)); } -void AudioArray::copyTo(float *destination, size_t sourceStart, size_t destinationStart, - size_t length) const { - if (size_ - sourceStart < length) { - throw std::out_of_range("Not enough data to copy from source."); - } +void AudioArray::copyTo( + float *destination, + size_t sourceStart, + size_t destinationStart, + size_t length) const { + if (size_ - sourceStart < length) { + throw std::out_of_range("Not enough data to copy from source."); + } - memcpy(destination + destinationStart, data_.get() + sourceStart, length * sizeof(float)); + memcpy(destination + destinationStart, data_.get() + sourceStart, length * sizeof(float)); } void AudioArray::copyWithin(size_t sourceStart, size_t destinationStart, size_t length) { - if (size_ - sourceStart < length || size_ - destinationStart < length) { - throw std::out_of_range("Not enough space for moving data or data to move."); - } + if (size_ - sourceStart < length || size_ - destinationStart < length) { + throw std::out_of_range("Not enough space for moving data or data to move."); + } - memmove(data_.get() + destinationStart, data_.get() + sourceStart, length); + memmove(data_.get() + destinationStart, data_.get() + sourceStart, length * sizeof(float)); } void AudioArray::reverse() { if (size_ <= 1) { - return; + return; } std::reverse(begin(), end()); } void AudioArray::normalize() { - float maxAbsValue = getMaxAbsValue(); + float maxAbsValue = getMaxAbsValue(); - if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { - return; - } + if (maxAbsValue == 0.0f || maxAbsValue == 1.0f) { + return; + } - dsp::multiplyByScalar(data_.get(), 1.0f / maxAbsValue, data_.get(), size_); + dsp::multiplyByScalar(data_.get(), 1.0f / maxAbsValue, data_.get(), size_); } void AudioArray::scale(float value) { - dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); + dsp::multiplyByScalar(data_.get(), value, data_.get(), size_); } float AudioArray::getMaxAbsValue() const { - return dsp::maximumMagnitude(data_.get(), size_); + return dsp::maximumMagnitude(data_.get(), size_); } float AudioArray::computeConvolution(const audioapi::AudioArray &kernel, size_t startIndex) const { - if (kernel.size_ > size_ - startIndex) { - throw std::out_of_range("Kernal size exceeds available data for convolution."); - } + if (kernel.size_ > size_ - startIndex) { + throw std::out_of_range("Kernal size exceeds available data for convolution."); + } - return dsp::computeConvolution(data_.get() + startIndex, kernel.data_.get(), kernel.size_); + return dsp::computeConvolution(data_.get() + startIndex, kernel.data_.get(), kernel.size_); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index 4c324de87..77fc4cba3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -141,9 +141,9 @@ class AudioArray { /// @note Assumes that destination and this are located in two distinct, non-overlapping memory locations. void copyTo(float *destination, size_t sourceStart, size_t destinationStart, size_t length) const; - /// @brief Moves data inside this AudioArray. - /// @param sourceStart The starting index in the source float pointer. - /// @param destinationStart The starting index of memory. + /// @brief Copies a sub-section of the array to another location within itself. + /// @param sourceStart The index where the data to be copied begins. + /// @param destinationStart The index where the data should be placed. /// @param length The number of samples to copy. void copyWithin(size_t sourceStart, size_t destinationStart, size_t length); From 2b01ac345608c40d7132ae9cb37ace3000b0d285 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 13:53:20 +0100 Subject: [PATCH 17/73] chore: updated custom processor template --- .../docs/guides/create-your-own-effect.mdx | 28 ++++++++----------- .../basic/shared/MyProcessorNode.cpp | 5 ++-- .../templates/basic/shared/MyProcessorNode.h | 4 +-- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/audiodocs/docs/guides/create-your-own-effect.mdx b/packages/audiodocs/docs/guides/create-your-own-effect.mdx index 85baaddf5..de1e07882 100644 --- a/packages/audiodocs/docs/guides/create-your-own-effect.mdx +++ b/packages/audiodocs/docs/guides/create-your-own-effect.mdx @@ -45,21 +45,17 @@ For the sake of a simplicity, we will use value as a raw `double` type, not wrap #include namespace audioapi { - class AudioBuffer; +class AudioBuffer; - class MyProcessorNode : public AudioNode { - public: - explicit MyProcessorNode(BaseAudioContext *context); - - protected: - void processNode(const std::shared_ptr &bus, - int framesToProcess) override; +class MyProcessorNode : public AudioNode { +public: + explicit MyProcessorNode(const std::shared_ptr &context, ); - // highlight-start - private: - double gain; // value responsible for gain value - // highlight-end - }; +protected: + std::shared_ptr + processNode(const std::shared_ptr &buffer, + int framesToProcess) override; +}; } // namespace audioapi ``` @@ -73,16 +69,16 @@ namespace audioapi { #include namespace audioapi { - MyProcessorNode::MyProcessorNode(BaseAudioContext *context) + MyProcessorNode::MyProcessorNode(const std::shared_ptr &context) //highlight-next-line : AudioNode(context), gain(0.5) { isInitialized_ = true; } - void MyProcessorNode::processNode(const std::shared_ptr &bus, + void MyProcessorNode::processNode(const std::shared_ptr &buffer, int framesToProcess) { // highlight-start - for (int channel = 0; channel < bus->getNumberOfChannels(); ++channel) { + for (int channel = 0; channel < buffer->getNumberOfChannels(); ++channel) { auto *audioArray = bus->getChannel(channel); for (size_t i = 0; i < framesToProcess; ++i) { // Apply gain to each sample in the audio array diff --git a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp index b6c9c60cd..b1ffb9581 100644 --- a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp +++ b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp @@ -2,13 +2,14 @@ #include namespace audioapi { -MyProcessorNode::MyProcessorNode(BaseAudioContext *context) +MyProcessorNode::MyProcessorNode( + const std::shared_ptr &context, ) : AudioNode(context) { isInitialized_ = true; } std::shared_ptr -MyProcessorNode::processNode(const std::shared_ptr &bus, +MyProcessorNode::processNode(const std::shared_ptr &buffer, int framesToProcess) { // put your processing logic here } diff --git a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h index 58cc43cc6..250321092 100644 --- a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h +++ b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.h @@ -6,11 +6,11 @@ class AudioBuffer; class MyProcessorNode : public AudioNode { public: - explicit MyProcessorNode(BaseAudioContext *context); + explicit MyProcessorNode(const std::shared_ptr &context, ); protected: std::shared_ptr - processNode(const std::shared_ptr &bus, + processNode(const std::shared_ptr &buffer, int framesToProcess) override; }; } // namespace audioapi From dd2bf6ba4ae59cadba8ce0104e04678c6ef0a6bb Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 20:00:51 +0100 Subject: [PATCH 18/73] refactor: optimized interleaveTo function --- .../cpp/audioapi/android/core/AudioPlayer.cpp | 16 ++-- .../cpp/audioapi/android/core/AudioPlayer.h | 2 +- .../common/cpp/audioapi/dsp/VectorMath.cpp | 75 ++++++++++++++++++- .../common/cpp/audioapi/dsp/VectorMath.h | 6 ++ .../common/cpp/audioapi/utils/AudioBuffer.cpp | 33 ++++++++ .../common/cpp/audioapi/utils/AudioBuffer.h | 9 +++ 6 files changed, 128 insertions(+), 13 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index 215f7f238..6e3bd1692 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -42,7 +42,7 @@ bool AudioPlayer::openAudioStream() { return false; } - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate_); + buffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate_); return true; } @@ -108,21 +108,17 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF int processedFrames = 0; while (processedFrames < numFrames) { - int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE); + auto framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE); if (isRunning_.load(std::memory_order_acquire)) { - renderAudio_(audioBus_, framesToProcess); + renderAudio_(buffer_, framesToProcess); } else { - audioBus_->zero(); + buffer_->zero(); } - for (int i = 0; i < framesToProcess; i++) { - for (int channel = 0; channel < channelCount_; channel++) { - buffer[(processedFrames + i) * channelCount_ + channel] = - (*audioBus_->getChannel(channel))[i]; - } - } + float *destination = buffer + (processedFrames * channelCount_); + buffer_->interleaveTo(destination, framesToProcess); processedFrames += framesToProcess; } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 7fcc1796c..c453382a5 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -42,7 +42,7 @@ class AudioPlayer : public AudioStreamDataCallback, AudioStreamErrorCallback { private: std::function, int)> renderAudio_; std::shared_ptr mStream_; - std::shared_ptr audioBus_; + std::shared_ptr buffer_; bool isInitialized_ = false; float sampleRate_; int channelCount_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp index 4cadc42be..18bdeab4c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp @@ -97,6 +97,22 @@ void multiplyByScalarThenAddToOutput( vDSP_vsma(inputVector, 1, &scalar, outputVector, 1, outputVector, 1, numberOfElementsToProcess); } +float computeConvolution(const float *state, const float *kernel, size_t kernelSize) { + float result = 0.0f; + vDSP_conv(state, 1, kernel, 1, &result, 1, 1, kernelSize); + return result; +} + +void interleaveStereo( + const float* __restrict inputLeft, + const float* __restrict inputRight, + float* __restrict outputInterleaved, + size_t numberOfFrames) { + float zero = 0.0f; + vDSP_vsadd(inputLeft, 1, &zero, outputInterleaved, 2, numberOfFrames); + vDSP_vsadd(inputRight, 1, &zero, outputInterleaved + 1, 2, numberOfFrames); +} + #else #if defined(HAVE_X86_SSE2) @@ -654,8 +670,6 @@ void multiplyByScalarThenAddToOutput( } } -#endif - float computeConvolution(const float *state, const float *kernel, size_t kernelSize) { float sum = 0.0f; int k = 0; @@ -684,4 +698,61 @@ float computeConvolution(const float *state, const float *kernel, size_t kernelS return sum; } + +void interleaveStereo( + const float * __restrict inputLeft, + const float * __restrict inputRight, + float * __restrict outputInterleaved, + size_t numberOfFrames) { + + size_t n = numberOfFrames; + +#if defined(HAVE_ARM_NEON_INTRINSICS) + // process 4 frames (8 samples) at a time + size_t group = n / 4; + while (group--) { + // load contiguous planar data + float32x4_t vL = vld1q_f32(inputLeft); + float32x4_t vR = vld1q_f32(inputRight); + + // vst2q_f32 takes two registers and interleaves them during the store: + float32x4x2_t vOut = { vL, vR }; + vst2q_f32(outputInterleaved, vOut); + + inputLeft += 4; + inputRight += 4; + outputInterleaved += 8; + } + n %= 4; +#elif defined(HAVE_X86_SSE2) + // process 4 frames (8 samples) at a time + size_t group = n / 4; + while (group--) { + __m128 vL = _mm_loadu_ps(inputLeft); + __m128 vR = _mm_loadu_ps(inputRight); + + // unpack low: Interleaves first two elements of each register + __m128 vLow = _mm_unpacklo_ps(vL, vR); + + // unpack high: Interleaves last two elements of each register + __m128 vHigh = _mm_unpackhi_ps(vL, vR); + + _mm_storeu_ps(outputInterleaved, vLow); + _mm_storeu_ps(outputInterleaved + 4, vHigh); + + inputLeft += 4; + inputRight += 4; + outputInterleaved += 8; + } + n %= 4; +#endif + + while (n--) { + *outputInterleaved++ = *inputLeft++; + *outputInterleaved++ = *inputRight++; + } +} + +#endif + } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h index fd11dfaa1..c66f46f9b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h @@ -71,4 +71,10 @@ float maximumMagnitude(const float *inputVector, size_t numberOfElementsToProces float computeConvolution(const float *state, const float *kernel, size_t kernelSize); +void interleaveStereo( + const float *inputLeft, + const float *inputRight, + float *outputInterleaved, + size_t numberOfFrames); + } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 99531b4e6..9d0a9f18d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -4,6 +4,7 @@ #include #include #include + #include #include #include @@ -245,6 +246,38 @@ void AudioBuffer::copy( sum(source, sourceStart, destinationStart, length); } +void AudioBuffer::interleaveTo(float *destination, size_t frames) const { + if (frames == 0) { + return; + } + + if (numberOfChannels_ == 1) { + channels_[0]->copyTo(destination, 0, 0, frames); + return; + } + + if (numberOfChannels_ == 2) { + dsp::interleaveStereo(channels_[0]->begin(), channels_[1]->begin(), destination, frames); + return; + } + + float* channelsPtrs[MAX_CHANNEL_COUNT]; + for (int i = 0; i < numberOfChannels_; ++i) { + channelsPtrs[i] = channels_[i]->begin(); + } + + constexpr size_t kBlockSize = 64; + for (size_t blockStart = 0; blockStart < frames; blockStart += kBlockSize) { + size_t blockEnd = std::min(blockStart + kBlockSize, frames); + for (size_t i = blockStart; i < blockEnd; ++i) { + float* frameDest = destination + (i * numberOfChannels_); + for (int ch = 0; ch < numberOfChannels_; ++ch) { + frameDest[ch] = channelsPtrs[ch][i]; + } + } + } +} + void AudioBuffer::normalize() { float maxAbsValue = this->maxAbsValue(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h index 9803d5a47..660ff7135 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h @@ -86,6 +86,15 @@ class AudioBuffer { void copy(const AudioBuffer &source); void copy(const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Interleave audio data from this AudioBuffer into a destination buffer. + /// @param destination Pointer to the destination buffer where interleaved audio data will be written. + /// @param frames Number of frames to interleave into the destination buffer. + /// @note The destination buffer should have enough space to hold the interleaved data + /// according to the number of channels in this AudioBuffer. + /// Example of interleaved data for stereo (2 channels): + /// [L0, R0, L1, R1, L2, R2, ...] + void interleaveTo(float *destination, size_t frames) const; + void normalize(); void scale(float value); [[nodiscard]] float maxAbsValue() const; From 40d3ce7da0d6e9fad4aa375282503b1e029d1a3a Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 20:09:11 +0100 Subject: [PATCH 19/73] refactor: renaming --- .../docs/guides/create-your-own-effect.mdx | 5 +++ .../android/core/AndroidAudioRecorder.cpp | 10 +++--- .../core/utils/AndroidRecorderCallback.cpp | 6 ++-- .../audioapi/core/analysis/AnalyserNode.cpp | 8 ++--- .../cpp/audioapi/core/analysis/AnalyserNode.h | 2 +- .../core/destinations/AudioDestinationNode.h | 7 ++-- .../core/effects/BiquadFilterNode.cpp | 8 ++--- .../audioapi/core/effects/BiquadFilterNode.h | 2 +- .../audioapi/core/effects/ConvolverNode.cpp | 30 ++++++++-------- .../cpp/audioapi/core/effects/ConvolverNode.h | 6 ++-- .../cpp/audioapi/core/effects/DelayNode.cpp | 36 +++++++++---------- .../cpp/audioapi/core/effects/DelayNode.h | 4 +-- .../cpp/audioapi/core/effects/GainNode.cpp | 3 +- .../audioapi/core/effects/IIRFilterNode.cpp | 2 +- .../cpp/audioapi/core/inputs/AudioRecorder.h | 2 +- .../core/utils/AudioRecorderCallback.h | 2 +- .../common/cpp/audioapi/utils/AudioArray.cpp | 36 +++++++++---------- .../common/cpp/audioapi/utils/AudioArray.h | 16 ++++----- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 10 +++--- .../ios/core/utils/IOSRecorderCallback.mm | 10 +++--- 20 files changed, 106 insertions(+), 99 deletions(-) diff --git a/packages/audiodocs/docs/guides/create-your-own-effect.mdx b/packages/audiodocs/docs/guides/create-your-own-effect.mdx index de1e07882..e195c92f2 100644 --- a/packages/audiodocs/docs/guides/create-your-own-effect.mdx +++ b/packages/audiodocs/docs/guides/create-your-own-effect.mdx @@ -55,6 +55,11 @@ protected: std::shared_ptr processNode(const std::shared_ptr &buffer, int framesToProcess) override; + +// highlight-start +private: + double gain; // value responsible for gain value +// highlight-end }; } // namespace audioapi ``` diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index 37e9eb528..77eee6ccf 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -138,7 +138,7 @@ Result AndroidAudioRecorder::start(const std::string & } if (isConnected()) { - deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + tempArray_ = std::make_shared(maxBufferSizeInFrames_); adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); } @@ -319,7 +319,7 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n adapterNode_ = node; if (!isIdle()) { - deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + tempArray_ = std::make_shared(maxBufferSizeInFrames_); adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); } @@ -332,7 +332,7 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n void AndroidAudioRecorder::disconnect() { std::scoped_lock adapterLock(adapterNodeMutex_); isConnected_.store(false, std::memory_order_release); - deinterleavingArray_ = nullptr; + tempArray_ = nullptr; adapterNode_ = nullptr; } @@ -370,13 +370,13 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady( if (isConnected()) { if (auto adapterLock = Locker::tryLock(adapterNodeMutex_)) { for (int channel = 0; channel < streamChannelCount_; ++channel) { - auto channelData = deinterleavingArray_->span(); + auto channelData = tempArray_->span(); for (int frame = 0; frame < numFrames; ++frame) { channelData[frame] = static_cast(audioData)[frame * streamChannelCount_ + channel]; } - adapterNode_->buff_[channel]->write(*deinterleavingArray_, numFrames); + adapterNode_->buff_[channel]->write(*tempArray_, numFrames); } } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp index fab40cf20..c27f3a431 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp @@ -96,7 +96,7 @@ Result AndroidRecorderCallback::prepare( processingBufferLength_ = std::max(processingBufferLength_, (ma_uint64)maxInputBufferLength_); - deinterleavingArray_ = std::make_shared(processingBufferLength_); + tempArray_ = std::make_shared(processingBufferLength_); processingBuffer_ = ma_malloc( processingBufferLength_ * channelCount_ * ma_get_bytes_per_sample(ma_format_f32), nullptr); @@ -152,13 +152,13 @@ void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFr auto *inputData = static_cast(data); for (int channel = 0; channel < channelCount_; ++channel) { - auto channelData = deinterleavingArray_->span(); + auto channelData = tempArray_->span(); for (int frame = 0; frame < numFrames; ++frame) { channelData[frame] = inputData[frame * channelCount_ + channel]; } - circularBus_[channel]->push_back(*deinterleavingArray_, numFrames); + circularBus_[channel]->push_back(*tempArray_, numFrames); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index b1ae3b6c3..5cd6dd660 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -152,19 +152,19 @@ void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { } std::shared_ptr AnalyserNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { // Analyser should behave like a sniffer node, it should not modify the - // processingBus but instead copy the data to its own input buffer. + // processingBuffer but instead copy the data to its own input buffer. // Down mix the input bus to mono - downMixBus_->copy(*processingBus); + downMixBus_->copy(*processingBuffer); // Copy the down mixed bus to the input buffer (circular buffer) inputArray_->push_back(*downMixBus_->getChannel(0), framesToProcess, true); shouldDoFFTAnalysis_ = true; - return processingBus; + return processingBuffer; } void AnalyserNode::doFFTAnalysis() { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 0dea5b123..62afdbb84 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -44,7 +44,7 @@ class AnalyserNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h index 478e979d8..2e6e7350a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h @@ -25,9 +25,10 @@ class AudioDestinationNode : public AudioNode { protected: // DestinationNode is triggered by AudioContext using renderAudio // processNode function is not necessary and is never called. - std::shared_ptr processNode(const std::shared_ptr &processingBus, int) - final { - return processingBus; + std::shared_ptr processNode( + const std::shared_ptr &processingBuffer, + int) final { + return processingBuffer; }; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index 0e4248ba2..7c0998b5a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -387,9 +387,9 @@ void BiquadFilterNode::applyFilter() { } std::shared_ptr BiquadFilterNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { - int numChannels = processingBus->getNumberOfChannels(); + int numChannels = processingBuffer->getNumberOfChannels(); applyFilter(); @@ -403,7 +403,7 @@ std::shared_ptr BiquadFilterNode::processNode( float x1, x2, y1, y2; for (int c = 0; c < numChannels; ++c) { - auto channel = processingBus->getChannel(c)->subSpan(framesToProcess); + auto channel = processingBuffer->getChannel(c)->subSpan(framesToProcess); x1 = x1_[c]; x2 = x2_[c]; @@ -433,7 +433,7 @@ std::shared_ptr BiquadFilterNode::processNode( y2_[c] = y2; } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index 287e4ca53..3763f9c2e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -73,7 +73,7 @@ class BiquadFilterNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 2a026ccb5..1b72d49f4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -22,7 +22,7 @@ ConvolverNode::ConvolverNode( normalize_(!options.disableNormalization), signalledToStop_(false), scaleFactor_(1.0f), - intermediateBus_(nullptr), + intermediateBuffer_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { setBuffer(options.bus); @@ -71,7 +71,7 @@ void ConvolverNode::setBuffer(const std::shared_ptr &buffer) { } internalBuffer_ = std::make_shared( RENDER_QUANTUM_SIZE * 2, channelCount_, buffer->getSampleRate()); - intermediateBus_ = std::make_shared( + intermediateBuffer_ = std::make_shared( RENDER_QUANTUM_SIZE, convolvers_.size(), buffer->getSampleRate()); internalBufferIndex_ = 0; } @@ -95,10 +95,10 @@ std::shared_ptr ConvolverNode::processInputs( return AudioNode::processInputs(outputBus, 0, false); } -// processing pipeline: processingBus -> intermediateBus_ -> audioBus_ (mixing -// with intermediateBus_) +// processing pipeline: processingBuffer -> intermediateBuffer_ -> audioBus_ (mixing +// with intermediateBuffer_) std::shared_ptr ConvolverNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (signalledToStop_) { if (remainingSegments_ > 0) { @@ -107,12 +107,12 @@ std::shared_ptr ConvolverNode::processNode( disable(); signalledToStop_ = false; internalBufferIndex_ = 0; - return processingBus; + return processingBuffer; } } if (internalBufferIndex_ < framesToProcess) { - performConvolution(processingBus); // result returned to intermediateBus_ - audioBus_->sum(*intermediateBus_); + performConvolution(processingBuffer); // result returned to intermediateBuffer_ + audioBus_->sum(*intermediateBuffer_); internalBuffer_->copy(*audioBus_, 0, internalBufferIndex_, RENDER_QUANTUM_SIZE); internalBufferIndex_ += RENDER_QUANTUM_SIZE; @@ -160,14 +160,14 @@ void ConvolverNode::calculateNormalizationScale() { scaleFactor_ *= gainCalibrationSampleRate_ / buffer_->getSampleRate(); } -void ConvolverNode::performConvolution(const std::shared_ptr &processingBus) { - if (processingBus->getNumberOfChannels() == 1) { +void ConvolverNode::performConvolution(const std::shared_ptr &processingBuffer) { + if (processingBuffer->getNumberOfChannels() == 1) { for (int i = 0; i < convolvers_.size(); ++i) { threadPool_->schedule([&, i] { - convolvers_[i].process(*processingBus->getChannel(0), *intermediateBus_->getChannel(i)); + convolvers_[i].process(*processingBuffer->getChannel(0), *intermediateBuffer_->getChannel(i)); }); } - } else if (processingBus->getNumberOfChannels() == 2) { + } else if (processingBuffer->getNumberOfChannels() == 2) { std::vector inputChannelMap; std::vector outputChannelMap; if (convolvers_.size() == 2) { @@ -178,10 +178,10 @@ void ConvolverNode::performConvolution(const std::shared_ptr &proce outputChannelMap = {0, 3, 2, 1}; } for (int i = 0; i < convolvers_.size(); ++i) { - threadPool_->schedule([this, i, inputChannelMap, outputChannelMap, &processingBus] { + threadPool_->schedule([this, i, inputChannelMap, outputChannelMap, &processingBuffer] { convolvers_[i].process( - *processingBus->getChannel(inputChannelMap[i]), - *intermediateBus_->getChannel(outputChannelMap[i])); + *processingBuffer->getChannel(inputChannelMap[i]), + *intermediateBuffer_->getChannel(outputChannelMap[i])); }); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h index 33054f524..00b05b78f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h @@ -32,7 +32,7 @@ class ConvolverNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: @@ -47,7 +47,7 @@ class ConvolverNode : public AudioNode { bool normalize_; bool signalledToStop_; float scaleFactor_; - std::shared_ptr intermediateBus_; + std::shared_ptr intermediateBuffer_; // impulse response buffer std::shared_ptr buffer_; @@ -58,7 +58,7 @@ class ConvolverNode : public AudioNode { std::shared_ptr threadPool_; void calculateNormalizationScale(); - void performConvolution(const std::shared_ptr &processingBus); + void performConvolution(const std::shared_ptr &processingBuffer); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 019a15dd2..817a41239 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -40,11 +40,11 @@ void DelayNode::onInputDisabled() { } void DelayNode::delayBufferOperation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess, size_t &operationStartingIndex, DelayNode::BufferAction action) { - size_t processingBusStartIndex = 0; + size_t processingBufferStartIndex = 0; // handle buffer wrap around if (operationStartingIndex + framesToProcess > delayBuffer_->getSize()) { @@ -52,24 +52,24 @@ void DelayNode::delayBufferOperation( if (action == BufferAction::WRITE) { delayBuffer_->sum( - *processingBus, processingBusStartIndex, operationStartingIndex, framesToEnd); + *processingBuffer, processingBufferStartIndex, operationStartingIndex, framesToEnd); } else { // READ - processingBus->sum( - *delayBuffer_, operationStartingIndex, processingBusStartIndex, framesToEnd); + processingBuffer->sum( + *delayBuffer_, operationStartingIndex, processingBufferStartIndex, framesToEnd); } operationStartingIndex = 0; - processingBusStartIndex += framesToEnd; + processingBufferStartIndex += framesToEnd; framesToProcess -= framesToEnd; } if (action == BufferAction::WRITE) { delayBuffer_->sum( - *processingBus, processingBusStartIndex, operationStartingIndex, framesToProcess); - processingBus->zero(); + *processingBuffer, processingBufferStartIndex, operationStartingIndex, framesToProcess); + processingBuffer->zero(); } else { // READ - processingBus->sum( - *delayBuffer_, operationStartingIndex, processingBusStartIndex, framesToProcess); + processingBuffer->sum( + *delayBuffer_, operationStartingIndex, processingBufferStartIndex, framesToProcess); delayBuffer_->zero(operationStartingIndex, framesToProcess); } @@ -81,32 +81,32 @@ void DelayNode::delayBufferOperation( // 1. writing to delay buffer (mixing if needed) from processing bus // 2. reading from delay buffer to processing bus (mixing if needed) with delay std::shared_ptr DelayNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { // handling tail processing if (signalledToStop_) { if (remainingFrames_ <= 0) { disable(); signalledToStop_ = false; - return processingBus; + return processingBuffer; } - delayBufferOperation(processingBus, framesToProcess, readIndex_, DelayNode::BufferAction::READ); + delayBufferOperation(processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); remainingFrames_ -= framesToProcess; - return processingBus; + return processingBuffer; } // normal processing std::shared_ptr context = context_.lock(); if (context == nullptr) - return processingBus; + return processingBuffer; auto delayTime = delayTimeParam_->processKRateParam(framesToProcess, context->getCurrentTime()); size_t writeIndex = static_cast(readIndex_ + delayTime * context->getSampleRate()) % delayBuffer_->getSize(); - delayBufferOperation(processingBus, framesToProcess, writeIndex, DelayNode::BufferAction::WRITE); - delayBufferOperation(processingBus, framesToProcess, readIndex_, DelayNode::BufferAction::READ); + delayBufferOperation(processingBuffer, framesToProcess, writeIndex, DelayNode::BufferAction::WRITE); + delayBufferOperation(processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index a44a77007..eb6f74cd2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -19,14 +19,14 @@ class DelayNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: void onInputDisabled() override; enum class BufferAction { READ, WRITE }; void delayBufferOperation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess, size_t &operationStartingIndex, BufferAction action); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp index e538a1187..3213cfdc0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp @@ -31,9 +31,10 @@ std::shared_ptr GainNode::processNode( return processingBus; double time = context->getCurrentTime(); auto gainParamValues = gainParam_->processARateParam(framesToProcess, time); + auto gainValues = gainParamValues->getChannel(0); + for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { auto channel = processingBus->getChannel(i); - auto gainValues = gainParamValues->getChannel(0); channel->multiply(*gainValues, framesToProcess); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index a1a160850..28aeb7f08 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -133,7 +133,7 @@ std::shared_ptr IIRFilterNode::processNode( int mask = bufferLength - 1; for (int c = 0; c < numChannels; ++c) { - auto channel = processingBus->getChannel(c)->subSpan(0, framesToProcess); + auto channel = processingBus->getChannel(c)->subSpan(framesToProcess); auto &x = xBuffers_[c]; auto &y = yBuffers_[c]; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index 7df5cf075..72bca381d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -66,7 +66,7 @@ class AudioRecorder { std::atomic fileOutputEnabled_{false}; std::atomic callbackOutputEnabled_{false}; - std::shared_ptr deinterleavingArray_; + std::shared_ptr tempArray_; size_t maxBufferSizeInFrames_ = 0; std::mutex callbackMutex_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h index d93e478e6..4ffed6bae 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h @@ -43,7 +43,7 @@ class AudioRecorderCallback { uint64_t callbackId_; size_t ringBufferSize_; - std::shared_ptr deinterleavingArray_; + std::shared_ptr tempArray_; std::atomic errorCallbackId_{0}; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index ac7bc1c76..8923acab8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -131,24 +131,6 @@ void AudioArray::copy( copy(source.data_.get(), sourceStart, destinationStart, length); } -void AudioArray::copyReverse( - const audioapi::AudioArray &source, - size_t sourceStart, - size_t destinationStart, - size_t length) { - if (size_ - destinationStart < length || source.size_ - sourceStart < length) { - throw std::out_of_range("Not enough space to copy to destination or from source."); - } - - auto dstView = this->subSpan(length, destinationStart); - auto srcView = source.span(); - const float *__restrict srcPtr = &srcView[sourceStart]; - - for (size_t i = 0; i < length; ++i) { - dstView[i] = srcPtr[-static_cast(i)]; - } -} - void AudioArray::copy( const float *source, size_t sourceStart, @@ -161,6 +143,24 @@ void AudioArray::copy( memcpy(data_.get() + destinationStart, source + sourceStart, length * sizeof(float)); } +void AudioArray::copyReverse( + const audioapi::AudioArray &source, + size_t sourceStart, + size_t destinationStart, + size_t length) { + if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + throw std::out_of_range("Not enough space to copy to destination or from source."); + } + + auto dstView = this->subSpan(length, destinationStart); + auto srcView = source.span(); + const float *__restrict srcPtr = &srcView[sourceStart]; + + for (size_t i = 0; i < length; ++i) { + dstView[i] = srcPtr[-static_cast(i)]; + } +} + void AudioArray::copyTo( float *destination, size_t sourceStart, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index 77fc4cba3..e2fbbed67 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -116,6 +116,14 @@ class AudioArray { /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Copies data from a raw float pointer into this AudioArray. + /// @param source The source float pointer to copy from. + /// @param sourceStart The starting index in the source float pointer. + /// @param destinationStart The starting index in this AudioArray. + /// @param length The number of samples to copy. + /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. + void copy(const float *source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Copies data from the source AudioArray in reverse order into this AudioArray. /// @param source The source AudioArray to copy from. /// @param sourceStart The starting index in the source AudioArray. @@ -125,14 +133,6 @@ class AudioArray { void copyReverse(const AudioArray &source, size_t sourceStart, size_t destinationStart, size_t length); - /// @brief Copies data from a raw float pointer into this AudioArray. - /// @param source The source float pointer to copy from. - /// @param sourceStart The starting index in the source float pointer. - /// @param destinationStart The starting index in this AudioArray. - /// @param length The number of samples to copy. - /// @note Assumes that source and this are located in two distinct, non-overlapping memory locations. - void copy(const float *source, size_t sourceStart, size_t destinationStart, size_t length); - /// @brief Copies data to a raw float pointer from this AudioArray. /// @param destination The destination float pointer to copy to. /// @param sourceStart The starting index in the this AudioArray. diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index e77ceb39c..2436e4cfa 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -53,8 +53,8 @@ for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { float *data = (float *)inputBuffer->mBuffers[channel].mData; - deinterleavingArray_->copy(data, 0, 0, numFrames); - adapterNode_->buff_[channel]->write(*deinterleavingArray_, numFrames); + tempArray_->copy(data, 0, 0, numFrames); + adapterNode_->buff_[channel]->write(*tempArray_, numFrames); } } } @@ -126,7 +126,7 @@ } if (isConnected()) { - deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + tempArray_ = std::make_shared(maxBufferSizeInFrames_); // TODO: pass sample rate, in case conversion is necessary adapterNode_->init(maxBufferSizeInFrames_, inputFormat.channelCount); } @@ -228,7 +228,7 @@ adapterNode_ = node; if (!isIdle()) { - deinterleavingArray_ = std::make_shared(maxBufferSizeInFrames_); + tempArray_ = std::make_shared(maxBufferSizeInFrames_); adapterNode_->init(maxBufferSizeInFrames_, [nativeRecorder_ getInputFormat].channelCount); } @@ -241,7 +241,7 @@ void IOSAudioRecorder::disconnect() { std::scoped_lock lock(adapterNodeMutex_); - deinterleavingArray_ = nullptr; + tempArray_ = nullptr; adapterNode_ = nullptr; isConnected_.store(false, std::memory_order_release); } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index b4ef82b2c..2fa64d692 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -72,7 +72,7 @@ converterOutputBufferSize_ = std::max( (double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength); - deinterleavingArray_ = std::make_shared(converterOutputBufferSize_); + tempArray_ = std::make_shared(converterOutputBufferSize_); callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:sampleRate_ @@ -153,8 +153,8 @@ for (int i = 0; i < channelCount_; ++i) { auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); - deinterleavingArray_->copy(inputChannel, 0, 0, numFrames); - circularBus_[i]->push_back(*deinterleavingArray_, numFrames); + tempArray_->copy(inputChannel, 0, 0, numFrames); + circularBus_[i]->push_back(*tempArray_, numFrames); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { @@ -201,8 +201,8 @@ for (int i = 0; i < channelCount_; ++i) { auto *data = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - deinterleavingArray_->copy(data, 0, 0, outputFrameCount); - circularBus_[i]->push_back(*deinterleavingArray_, outputFrameCount); + tempArray_->copy(data, 0, 0, outputFrameCount); + circularBus_[i]->push_back(*tempArray_, outputFrameCount); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { From 770376766b314de45350726cf64d3f7a68daebe4 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 20:14:01 +0100 Subject: [PATCH 20/73] refactor: renaming --- .../common/cpp/audioapi/core/AudioNode.cpp | 30 +++++++-------- .../common/cpp/audioapi/core/AudioNode.h | 4 +- .../common/cpp/audioapi/core/AudioParam.cpp | 32 ++++++++-------- .../common/cpp/audioapi/core/AudioParam.h | 4 +- .../cpp/audioapi/core/effects/GainNode.cpp | 10 ++--- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../audioapi/core/effects/IIRFilterNode.cpp | 8 ++-- .../cpp/audioapi/core/effects/IIRFilterNode.h | 2 +- .../core/effects/StereoPannerNode.cpp | 12 +++--- .../audioapi/core/effects/StereoPannerNode.h | 2 +- .../audioapi/core/effects/WaveShaperNode.cpp | 14 +++---- .../audioapi/core/effects/WaveShaperNode.h | 2 +- .../cpp/audioapi/core/effects/WorkletNode.cpp | 8 ++-- .../cpp/audioapi/core/effects/WorkletNode.h | 6 +-- .../core/effects/WorkletProcessingNode.cpp | 10 ++--- .../core/effects/WorkletProcessingNode.h | 6 +-- .../sources/AudioBufferBaseSourceNode.cpp | 20 +++++----- .../core/sources/AudioBufferBaseSourceNode.h | 8 ++-- .../sources/AudioBufferQueueSourceNode.cpp | 30 +++++++-------- .../core/sources/AudioBufferQueueSourceNode.h | 6 +-- .../core/sources/AudioBufferSourceNode.cpp | 38 +++++++++---------- .../core/sources/AudioBufferSourceNode.h | 6 +-- .../core/sources/AudioScheduledSourceNode.cpp | 8 ++-- .../core/sources/AudioScheduledSourceNode.h | 2 +- .../core/sources/ConstantSourceNode.cpp | 18 ++++----- .../core/sources/ConstantSourceNode.h | 2 +- .../audioapi/core/sources/OscillatorNode.cpp | 18 ++++----- .../audioapi/core/sources/OscillatorNode.h | 2 +- .../core/sources/RecorderAdapterNode.cpp | 10 ++--- .../core/sources/RecorderAdapterNode.h | 2 +- .../audioapi/core/sources/StreamerNode.cpp | 24 ++++++------ .../cpp/audioapi/core/sources/StreamerNode.h | 2 +- .../core/sources/WorkletSourceNode.cpp | 26 ++++++------- .../audioapi/core/sources/WorkletSourceNode.h | 6 +-- .../cpp/test/src/AudioScheduledSourceTest.cpp | 11 +++--- .../cpp/test/src/ConstantSourceTest.cpp | 4 +- .../common/cpp/test/src/DelayTest.cpp | 4 +- .../common/cpp/test/src/GainTest.cpp | 4 +- .../common/cpp/test/src/StereoPannerTest.cpp | 4 +- .../src/core/effects/WaveShaperNodeTest.cpp | 4 +- 40 files changed, 205 insertions(+), 206 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp index 8997adb39..95aae1923 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp @@ -133,18 +133,18 @@ std::shared_ptr AudioNode::processAudio( } // Process inputs and return the bus with the most channels. - auto processingBus = processInputs(outputBus, framesToProcess, checkIsAlreadyProcessed); + auto processingBuffer = processInputs(outputBus, framesToProcess, checkIsAlreadyProcessed); // Apply channel count mode. - processingBus = applyChannelCountMode(processingBus); + processingBuffer = applyChannelCountMode(processingBuffer); // Mix all input buses into the processing bus. - mixInputsBuses(processingBus); + mixInputsBuses(processingBuffer); - assert(processingBus != nullptr); + assert(processingBuffer != nullptr); // Finally, process the node itself. - return processNode(processingBus, framesToProcess); + return processNode(processingBuffer, framesToProcess); } bool AudioNode::isAlreadyProcessed() { @@ -170,8 +170,8 @@ std::shared_ptr AudioNode::processInputs( const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { - auto processingBus = audioBus_; - processingBus->zero(); + auto processingBuffer = audioBus_; + processingBuffer->zero(); int maxNumberOfChannels = 0; for (auto it = inputNodes_.begin(), end = inputNodes_.end(); it != end; ++it) { @@ -187,15 +187,15 @@ std::shared_ptr AudioNode::processInputs( if (maxNumberOfChannels < inputBus->getNumberOfChannels()) { maxNumberOfChannels = inputBus->getNumberOfChannels(); - processingBus = inputBus; + processingBuffer = inputBus; } } - return processingBus; + return processingBuffer; } std::shared_ptr AudioNode::applyChannelCountMode( - const std::shared_ptr &processingBus) { + const std::shared_ptr &processingBuffer) { // If the channelCountMode is EXPLICIT, the node should output the number of // channels specified by the channelCount. if (channelCountMode_ == ChannelCountMode::EXPLICIT) { @@ -205,18 +205,18 @@ std::shared_ptr AudioNode::applyChannelCountMode( // If the channelCountMode is CLAMPED_MAX, the node should output the maximum // number of channels clamped to channelCount. if (channelCountMode_ == ChannelCountMode::CLAMPED_MAX && - processingBus->getNumberOfChannels() >= channelCount_) { + processingBuffer->getNumberOfChannels() >= channelCount_) { return audioBus_; } - return processingBus; + return processingBuffer; } -void AudioNode::mixInputsBuses(const std::shared_ptr &processingBus) { - assert(processingBus != nullptr); +void AudioNode::mixInputsBuses(const std::shared_ptr &processingBuffer) { + assert(processingBuffer != nullptr); for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end; ++it) { - processingBus->sum(**it, channelInterpretation_); + processingBuffer->sum(**it, channelInterpretation_); } inputBuses_.clear(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 319108b1c..91e4ab675 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -83,8 +83,8 @@ class AudioNode : public std::enable_shared_from_this { bool isAlreadyProcessed(); std::shared_ptr applyChannelCountMode( - const std::shared_ptr &processingBus); - void mixInputsBuses(const std::shared_ptr &processingBus); + const std::shared_ptr &processingBuffer); + void mixInputsBuses(const std::shared_ptr &processingBuffer); void connectNode(const std::shared_ptr &node); void disconnectNode(const std::shared_ptr &node); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp index 87b134fef..737e4507f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp @@ -243,26 +243,26 @@ void AudioParam::removeInputNode(AudioNode *node) { } std::shared_ptr AudioParam::calculateInputs( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { - processingBus->zero(); + processingBuffer->zero(); if (inputNodes_.empty()) { - return processingBus; + return processingBuffer; } - processInputs(processingBus, framesToProcess, true); - mixInputsBuses(processingBus); - return processingBus; + processInputs(processingBuffer, framesToProcess, true); + mixInputsBuses(processingBuffer); + return processingBuffer; } std::shared_ptr AudioParam::processARateParam(int framesToProcess, double time) { processScheduledEvents(); - auto processingBus = calculateInputs(audioBus_, framesToProcess); + auto processingBuffer = calculateInputs(audioBus_, framesToProcess); std::shared_ptr context = context_.lock(); if (context == nullptr) - return processingBus; + return processingBuffer; float sampleRate = context->getSampleRate(); - auto busData = processingBus->getChannel(0)->span(); + auto busData = processingBuffer->getChannel(0)->span(); float timeCache = time; float timeStep = 1.0f / sampleRate; float sample = 0.0f; @@ -272,16 +272,16 @@ std::shared_ptr AudioParam::processARateParam(int framesToProcess, sample = getValueAtTime(timeCache); busData[i] += sample; } - // processingBus is a mono bus containing per-sample parameter values - return processingBus; + // processingBuffer is a mono bus containing per-sample parameter values + return processingBuffer; } float AudioParam::processKRateParam(int framesToProcess, double time) { processScheduledEvents(); - auto processingBus = calculateInputs(audioBus_, framesToProcess); + auto processingBuffer = calculateInputs(audioBus_, framesToProcess); // Return block-rate parameter value plus first sample of input modulation - return processingBus->getChannel(0)->span()[0] + getValueAtTime(time); + return processingBuffer->getChannel(0)->span()[0] + getValueAtTime(time); } void AudioParam::processInputs( @@ -302,12 +302,12 @@ void AudioParam::processInputs( } } -void AudioParam::mixInputsBuses(const std::shared_ptr &processingBus) { - assert(processingBus != nullptr); +void AudioParam::mixInputsBuses(const std::shared_ptr &processingBuffer) { + assert(processingBuffer != nullptr); // Sum all input buses into the processing bus for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end; ++it) { - processingBus->sum(**it, ChannelInterpretation::SPEAKERS); + processingBuffer->sum(**it, ChannelInterpretation::SPEAKERS); } // Clear for next processing cycle diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index 165a48809..ed7f6fb66 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -148,9 +148,9 @@ class AudioParam { const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed); - void mixInputsBuses(const std::shared_ptr &processingBus); + void mixInputsBuses(const std::shared_ptr &processingBuffer); std::shared_ptr calculateInputs( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp index 3213cfdc0..9334b768a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp @@ -24,21 +24,21 @@ std::shared_ptr GainNode::getGainParam() const { } std::shared_ptr GainNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { std::shared_ptr context = context_.lock(); if (context == nullptr) - return processingBus; + return processingBuffer; double time = context->getCurrentTime(); auto gainParamValues = gainParam_->processARateParam(framesToProcess, time); auto gainValues = gainParamValues->getChannel(0); - for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - auto channel = processingBus->getChannel(i); + for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + auto channel = processingBuffer->getChannel(i); channel->multiply(*gainValues, framesToProcess); } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h index 928d5a5fc..86b18cc77 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h @@ -18,7 +18,7 @@ class GainNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 28aeb7f08..83ec8607b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -122,9 +122,9 @@ void IIRFilterNode::getFrequencyResponse( // TODO: tail std::shared_ptr IIRFilterNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { - int numChannels = processingBus->getNumberOfChannels(); + int numChannels = processingBuffer->getNumberOfChannels(); size_t feedforwardLength = feedforward_.size(); size_t feedbackLength = feedback_.size(); @@ -133,7 +133,7 @@ std::shared_ptr IIRFilterNode::processNode( int mask = bufferLength - 1; for (int c = 0; c < numChannels; ++c) { - auto channel = processingBus->getChannel(c)->subSpan(framesToProcess); + auto channel = processingBuffer->getChannel(c)->subSpan(framesToProcess); auto &x = xBuffers_[c]; auto &y = yBuffers_[c]; @@ -170,7 +170,7 @@ std::shared_ptr IIRFilterNode::processNode( } bufferIndices[c] = bufferIndex; } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 8d6b44937..28ebcef9d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -50,7 +50,7 @@ class IIRFilterNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp index be31dd231..8accf2e4d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp @@ -23,11 +23,11 @@ std::shared_ptr StereoPannerNode::getPanParam() const { } std::shared_ptr StereoPannerNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { std::shared_ptr context = context_.lock(); if (context == nullptr) - return processingBus; + return processingBuffer; double time = context->getCurrentTime(); double deltaTime = 1.0 / context->getSampleRate(); @@ -37,8 +37,8 @@ std::shared_ptr StereoPannerNode::processNode( auto outputRight = audioBus_->getChannelByType(AudioBuffer::ChannelRight)->span(); // Input is mono - if (processingBus->getNumberOfChannels() == 1) { - auto inputLeft = processingBus->getChannelByType(AudioBuffer::ChannelMono)->span(); + if (processingBuffer->getNumberOfChannels() == 1) { + auto inputLeft = processingBuffer->getChannelByType(AudioBuffer::ChannelMono)->span(); for (int i = 0; i < framesToProcess; i++) { const auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); @@ -51,8 +51,8 @@ std::shared_ptr StereoPannerNode::processNode( time += deltaTime; } } else { // Input is stereo - auto inputLeft = processingBus->getChannelByType(AudioBuffer::ChannelMono)->span(); - auto inputRight = processingBus->getChannelByType(AudioBuffer::ChannelRight)->span(); + auto inputLeft = processingBuffer->getChannelByType(AudioBuffer::ChannelMono)->span(); + auto inputRight = processingBuffer->getChannelByType(AudioBuffer::ChannelRight)->span(); for (int i = 0; i < framesToProcess; i++) { const auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h index da99cbe13..064a9e91b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h @@ -22,7 +22,7 @@ class StereoPannerNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index 70bc34cec..c010d70c2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -54,29 +54,29 @@ void WaveShaperNode::setCurve(const std::shared_ptr &curve) { } std::shared_ptr WaveShaperNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (!isInitialized_) { - return processingBus; + return processingBuffer; } std::unique_lock lock(mutex_, std::try_to_lock); if (!lock.owns_lock()) { - return processingBus; + return processingBuffer; } if (curve_ == nullptr) { - return processingBus; + return processingBuffer; } - for (int channel = 0; channel < processingBus->getNumberOfChannels(); channel++) { - auto channelData = processingBus->getChannel(channel); + for (int channel = 0; channel < processingBuffer->getNumberOfChannels(); channel++) { + auto channelData = processingBuffer->getChannel(channel); waveShapers_[channel]->process(*channelData, framesToProcess); } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 759b706fb..18a3f96a2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -32,7 +32,7 @@ class WaveShaperNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index befb84c23..fcce0f364 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -22,11 +22,11 @@ WorkletNode::WorkletNode( } std::shared_ptr WorkletNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t processed = 0; size_t channelCount_ = - std::min(inputChannelCount_, static_cast(processingBus->getNumberOfChannels())); + std::min(inputChannelCount_, static_cast(processingBuffer->getNumberOfChannels())); while (processed < framesToProcess) { size_t framesToWorkletInvoke = bufferLength_ - curBuffIndex_; size_t needsToProcess = framesToProcess - processed; @@ -35,7 +35,7 @@ std::shared_ptr WorkletNode::processNode( /// here we copy /// to [curBuffIndex_, curBuffIndex_ + shouldProcess] /// from [processed, processed + shouldProcess] - bus_->copy(*processingBus, processed, curBuffIndex_, shouldProcess); + bus_->copy(*processingBuffer, processed, curBuffIndex_, shouldProcess); processed += shouldProcess; curBuffIndex_ += shouldProcess; @@ -68,7 +68,7 @@ std::shared_ptr WorkletNode::processNode( }); } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h index f42bd6f3b..9a1afb11f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h @@ -25,9 +25,9 @@ class WorkletNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return processingBus; + return processingBuffer; } }; #else @@ -46,7 +46,7 @@ class WorkletNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index cc3f12b1d..9768bc6c5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -22,15 +22,15 @@ WorkletProcessingNode::WorkletProcessingNode( } std::shared_ptr WorkletProcessingNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t channelCount = std::min( static_cast(2), // Fixed to stereo for now - static_cast(processingBus->getNumberOfChannels())); + static_cast(processingBuffer->getNumberOfChannels())); // Copy input data to pre-allocated input buffers for (int ch = 0; ch < channelCount; ch++) { - inputBuffsHandles_[ch]->copy(*processingBus->getChannel(ch), 0, 0, framesToProcess); + inputBuffsHandles_[ch]->copy(*processingBuffer->getChannel(ch), 0, 0, framesToProcess); } // Execute the worklet @@ -65,7 +65,7 @@ std::shared_ptr WorkletProcessingNode::processNode( // Copy processed output data back to the processing bus or zero on failure for (int ch = 0; ch < channelCount; ch++) { - auto channelData = processingBus->getChannel(ch); + auto channelData = processingBuffer->getChannel(ch); if (result.has_value()) { // Copy processed output data @@ -76,7 +76,7 @@ std::shared_ptr WorkletProcessingNode::processNode( } } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h index 5fe43bb8d..42e8ec72b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h @@ -23,9 +23,9 @@ class WorkletProcessingNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return processingBus; + return processingBuffer; } }; #else @@ -40,7 +40,7 @@ class WorkletProcessingNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index 38d038fb7..059c5a74b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -99,14 +99,14 @@ void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() { } void AudioBufferBaseSourceNode::processWithPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); + processingBuffer->zero(); return; } auto time = context->getCurrentTime(); @@ -128,14 +128,14 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( context->getCurrentSampleFrame()); if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) { - processingBus->zero(); + processingBuffer->zero(); return; } processWithoutInterpolation(playbackRateBus_, startOffset, offsetLength, playbackRate); stretch_->process( - playbackRateBus_.get()[0], framesNeededToStretch, processingBus.get()[0], framesToProcess); + playbackRateBus_.get()[0], framesNeededToStretch, processingBuffer.get()[0], framesToProcess); if (detune != 0.0f) { stretch_->setTransposeSemitones(detune); @@ -145,20 +145,20 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( } void AudioBufferBaseSourceNode::processWithoutPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); + processingBuffer->zero(); return; } auto computedPlaybackRate = getComputedPlaybackRateValue(framesToProcess, context->getCurrentTime()); updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, offsetLength, @@ -166,14 +166,14 @@ void AudioBufferBaseSourceNode::processWithoutPitchCorrection( context->getCurrentSampleFrame()); if (computedPlaybackRate == 0.0f || (!isPlaying() && !isStopScheduled())) { - processingBus->zero(); + processingBuffer->zero(); return; } if (std::fabs(computedPlaybackRate) == 1.0) { - processWithoutInterpolation(processingBus, startOffset, offsetLength, computedPlaybackRate); + processWithoutInterpolation(processingBuffer, startOffset, offsetLength, computedPlaybackRate); } else { - processWithInterpolation(processingBus, startOffset, offsetLength, computedPlaybackRate); + processWithInterpolation(processingBuffer, startOffset, offsetLength, computedPlaybackRate); } sendOnPositionChangedEvent(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 87b3e4cf0..4969cba4c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -55,22 +55,22 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { void sendOnPositionChangedEvent(); void processWithPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess); void processWithoutPitchCorrection( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess); float getComputedPlaybackRateValue(int framesToProcess, double time); virtual void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) = 0; virtual void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) = 0; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index 956a99ac0..eac330254 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -138,27 +138,27 @@ void AudioBufferQueueSourceNode::setOnBufferEndedCallbackId(uint64_t callbackId) } std::shared_ptr AudioBufferQueueSourceNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (auto locker = Locker::tryLock(getBufferLock())) { // no audio data to fill, zero the output and return. if (buffers_.empty()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } if (!pitchCorrection_) { - processWithoutPitchCorrection(processingBus, framesToProcess); + processWithoutPitchCorrection(processingBuffer, framesToProcess); } else { - processWithPitchCorrection(processingBus, framesToProcess); + processWithPitchCorrection(processingBuffer, framesToProcess); } handleStopScheduled(); } else { - processingBus->zero(); + processingBuffer->zero(); } - return processingBus; + return processingBuffer; } double AudioBufferQueueSourceNode::getCurrentPosition() const { @@ -187,7 +187,7 @@ void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool is */ void AudioBufferQueueSourceNode::processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -208,9 +208,9 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( assert(readIndex >= 0); assert(writeIndex >= 0); assert(readIndex + framesToCopy <= buffer->getSize()); - assert(writeIndex + framesToCopy <= processingBus->getSize()); + assert(writeIndex + framesToCopy <= processingBuffer->getSize()); - processingBus->copy(*buffer, readIndex, writeIndex, framesToCopy); + processingBuffer->copy(*buffer, readIndex, writeIndex, framesToCopy); writeIndex += framesToCopy; readIndex += framesToCopy; @@ -229,7 +229,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( buffers_.emplace(bufferId, tailBuffer_); addExtraTailFrames_ = false; } else { - processingBus->zero(writeIndex, framesLeft); + processingBuffer->zero(writeIndex, framesLeft); readIndex = 0; break; @@ -248,7 +248,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( } void AudioBufferQueueSourceNode::processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -279,8 +279,8 @@ void AudioBufferQueueSourceNode::processWithInterpolation( } } - for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - const auto destination = processingBus->getChannel(i)->span(); + for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + const auto destination = processingBuffer->getChannel(i)->span(); const auto currentSource = buffer->getChannel(i)->span(); if (crossBufferInterpolation) { @@ -306,7 +306,7 @@ void AudioBufferQueueSourceNode::processWithInterpolation( sendOnBufferEndedEvent(bufferId, buffers_.empty()); if (buffers_.empty()) { - processingBus->zero(writeIndex, framesLeft); + processingBuffer->zero(writeIndex, framesLeft); vReadIndex_ = 0.0; break; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index 12714c44d..7ab344fef 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -38,7 +38,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; double getCurrentPosition() const override; @@ -59,13 +59,13 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { std::atomic onBufferEndedCallbackId_ = 0; // 0 means no callback void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) override; void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 591698447..d27729fad 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -146,27 +146,27 @@ void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { } std::shared_ptr AudioBufferSourceNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (auto locker = Locker::tryLock(getBufferLock())) { // No audio data to fill, zero the output and return. if (!alignedBus_) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } if (!pitchCorrection_) { - processWithoutPitchCorrection(processingBus, framesToProcess); + processWithoutPitchCorrection(processingBuffer, framesToProcess); } else { - processWithPitchCorrection(processingBus, framesToProcess); + processWithPitchCorrection(processingBuffer, framesToProcess); } handleStopScheduled(); } else { - processingBus->zero(); + processingBuffer->zero(); } - return processingBus; + return processingBuffer; } double AudioBufferSourceNode::getCurrentPosition() const { @@ -186,7 +186,7 @@ void AudioBufferSourceNode::sendOnLoopEndedEvent() { */ void AudioBufferSourceNode::processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -201,7 +201,7 @@ void AudioBufferSourceNode::processWithoutInterpolation( frameStart = static_cast(getVirtualStartFrame(context->getSampleRate())); frameEnd = static_cast(getVirtualEndFrame(context->getSampleRate())); } else { - processingBus->zero(); + processingBuffer->zero(); return; } size_t frameDelta = frameEnd - frameStart; @@ -224,14 +224,14 @@ void AudioBufferSourceNode::processWithoutInterpolation( assert(readIndex >= 0); assert(writeIndex >= 0); assert(readIndex + framesToCopy <= alignedBus_->getSize()); - assert(writeIndex + framesToCopy <= processingBus->getSize()); + assert(writeIndex + framesToCopy <= processingBuffer->getSize()); // Direction is forward, we can normally copy the data if (direction == 1) { - processingBus->copy(*alignedBus_, readIndex, writeIndex, framesToCopy); + processingBuffer->copy(*alignedBus_, readIndex, writeIndex, framesToCopy); } else { - for (int ch = 0; ch < processingBus->getNumberOfChannels(); ch += 1) { - processingBus->getChannel(ch)->copyReverse( + for (int ch = 0; ch < processingBuffer->getNumberOfChannels(); ch += 1) { + processingBuffer->getChannel(ch)->copyReverse( *alignedBus_->getChannel(ch), readIndex, writeIndex, framesToCopy); } } @@ -246,7 +246,7 @@ void AudioBufferSourceNode::processWithoutInterpolation( readIndex -= direction * frameDelta; if (!loop_) { - processingBus->zero(writeIndex, framesLeft); + processingBuffer->zero(writeIndex, framesLeft); playbackState_ = PlaybackState::STOP_SCHEDULED; break; } @@ -260,7 +260,7 @@ void AudioBufferSourceNode::processWithoutInterpolation( } void AudioBufferSourceNode::processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) { @@ -274,7 +274,7 @@ void AudioBufferSourceNode::processWithInterpolation( vFrameStart = getVirtualStartFrame(context->getSampleRate()); vFrameEnd = getVirtualEndFrame(context->getSampleRate()); } else { - processingBus->zero(); + processingBuffer->zero(); return; } auto vFrameDelta = vFrameEnd - vFrameStart; @@ -298,8 +298,8 @@ void AudioBufferSourceNode::processWithInterpolation( nextReadIndex = loop_ ? frameStart : readIndex; } - for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) { - auto destination = processingBus->getChannel(i)->span(); + for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + auto destination = processingBuffer->getChannel(i)->span(); const auto source = alignedBus_->getChannel(i)->span(); destination[writeIndex] = dsp::linearInterpolate(source, readIndex, nextReadIndex, factor); @@ -313,7 +313,7 @@ void AudioBufferSourceNode::processWithInterpolation( vReadIndex_ -= static_cast(direction) * vFrameDelta; if (!loop_) { - processingBus->zero(writeIndex, framesLeft); + processingBuffer->zero(writeIndex, framesLeft); playbackState_ = PlaybackState::STOP_SCHEDULED; break; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index a47f49620..0667ad9d2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -42,7 +42,7 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; double getCurrentPosition() const override; @@ -61,13 +61,13 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { void sendOnLoopEndedEvent(); void processWithoutInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) override; void processWithInterpolation( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, size_t startOffset, size_t offsetLength, float playbackRate) override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp index a2ee963cf..cfce6e393 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp @@ -71,7 +71,7 @@ void AudioScheduledSourceNode::setOnEndedCallbackId(const uint64_t callbackId) { } void AudioScheduledSourceNode::updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, @@ -118,10 +118,10 @@ void AudioScheduledSourceNode::updatePlaybackInfo( // stop will happen in the same render quantum if (stopFrame <= lastFrame && stopFrame >= firstFrame) { playbackState_ = PlaybackState::STOP_SCHEDULED; - processingBus->zero(stopFrame - firstFrame, lastFrame - stopFrame); + processingBuffer->zero(stopFrame - firstFrame, lastFrame - stopFrame); } - processingBus->zero(0, startOffset); + processingBuffer->zero(0, startOffset); return; } @@ -137,7 +137,7 @@ void AudioScheduledSourceNode::updatePlaybackInfo( assert(startOffset <= framesToProcess); assert(nonSilentFramesToProcess <= framesToProcess); - processingBus->zero(stopFrame - firstFrame, lastFrame - stopFrame); + processingBuffer->zero(stopFrame - firstFrame, lastFrame - stopFrame); return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 7f9f0f277..eba765c40 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -51,7 +51,7 @@ class AudioScheduledSourceNode : public AudioNode { std::shared_ptr audioEventHandlerRegistry_; void updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp index 48a9ad884..ca728f201 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp @@ -21,19 +21,19 @@ std::shared_ptr ConstantSourceNode::getOffsetParam() const { } std::shared_ptr ConstantSourceNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, offsetLength, @@ -41,15 +41,15 @@ std::shared_ptr ConstantSourceNode::processNode( context->getCurrentSampleFrame()); if (!isPlaying() && !isStopScheduled()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } auto offsetChannel = offsetParam_->processARateParam(framesToProcess, context->getCurrentTime())->getChannel(0); - for (int channel = 0; channel < processingBus->getNumberOfChannels(); ++channel) { - processingBus->getChannel(channel)->copy( + for (int channel = 0; channel < processingBuffer->getNumberOfChannels(); ++channel) { + processingBuffer->getChannel(channel)->copy( *offsetChannel, startOffset, startOffset, offsetLength); } @@ -57,6 +57,6 @@ std::shared_ptr ConstantSourceNode::processNode( handleStopScheduled(); } - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h index 61a099055..214fba989 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h @@ -22,7 +22,7 @@ class ConstantSourceNode : public AudioScheduledSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index 82a13db30..e9340f7f5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -57,19 +57,19 @@ void OscillatorNode::setPeriodicWave(const std::shared_ptr &period } std::shared_ptr OscillatorNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { size_t startOffset = 0; size_t offsetLength = 0; std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, offsetLength, @@ -77,8 +77,8 @@ std::shared_ptr OscillatorNode::processNode( context->getCurrentSampleFrame()); if (!isPlaying() && !isStopScheduled()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } auto time = @@ -88,12 +88,12 @@ std::shared_ptr OscillatorNode::processNode( const auto tableSize = static_cast(periodicWave_->getPeriodicWaveSize()); const auto tableScale = periodicWave_->getScale(); - const auto numChannels = processingBus->getNumberOfChannels(); + const auto numChannels = processingBuffer->getNumberOfChannels(); auto finalPhase = phase_; for (int ch = 0; ch < numChannels; ch += 1) { - auto channelSpan = processingBus->getChannel(ch)->span(); + auto channelSpan = processingBuffer->getChannel(ch)->span(); float currentPhase = phase_; for (size_t i = startOffset; i < offsetLength; i += 1) { @@ -120,7 +120,7 @@ std::shared_ptr OscillatorNode::processNode( phase_ = finalPhase; handleStopScheduled(); - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h index 1805a6204..c76fe956f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h @@ -29,7 +29,7 @@ class OscillatorNode : public AudioScheduledSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index 8cf50cd7e..096f20b40 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -53,17 +53,17 @@ void RecorderAdapterNode::cleanup() { } std::shared_ptr RecorderAdapterNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (!isInitialized_) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } readFrames(framesToProcess); - processingBus->sum(*adapterOutputBus_, ChannelInterpretation::SPEAKERS); - return processingBus; + processingBuffer->sum(*adapterOutputBus_, ChannelInterpretation::SPEAKERS); + return processingBuffer; } void RecorderAdapterNode::readFrames(const size_t framesToRead) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h index 8c2dcbaf1..a3d5aba8f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h @@ -34,7 +34,7 @@ class RecorderAdapterNode : public AudioNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; std::shared_ptr adapterOutputBus_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp index 784a8f0e2..f57af1e59 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp @@ -109,18 +109,18 @@ bool StreamerNode::initialize(const std::string &input_url) { } std::shared_ptr StreamerNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { #if !RN_AUDIO_API_FFMPEG_DISABLED size_t startOffset = 0; size_t offsetLength = 0; std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, offsetLength, @@ -129,15 +129,15 @@ std::shared_ptr StreamerNode::processNode( isNodeFinished_.store(isFinished(), std::memory_order_release); if (!isPlaying() && !isStopScheduled()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } int bufferRemaining = bufferedBusSize_ - processedSamples_; int alreadyProcessed = 0; if (bufferRemaining < framesToProcess) { if (bufferedBus_ != nullptr) { - processingBus->copy(*bufferedBus_, processedSamples_, 0, bufferRemaining); + processingBuffer->copy(*bufferedBus_, processedSamples_, 0, bufferRemaining); framesToProcess -= bufferRemaining; alreadyProcessed += bufferRemaining; } @@ -152,12 +152,12 @@ std::shared_ptr StreamerNode::processNode( } } if (bufferedBus_ != nullptr) { - processingBus->copy(*bufferedBus_, processedSamples_, alreadyProcessed, framesToProcess); + processingBuffer->copy(*bufferedBus_, processedSamples_, alreadyProcessed, framesToProcess); processedSamples_ += framesToProcess; } #endif // RN_AUDIO_API_FFMPEG_DISABLED - return processingBus; + return processingBuffer; } #if !RN_AUDIO_API_FFMPEG_DISABLED @@ -260,10 +260,8 @@ bool StreamerNode::processFrameWithResampler( return true; } - AudioBuffer buffer = AudioBuffer( - converted_samples, - codecCtx_->ch_layout.nb_channels, - context->getSampleRate()); + AudioBuffer buffer = + AudioBuffer(converted_samples, codecCtx_->ch_layout.nb_channels, context->getSampleRate()); for (int ch = 0; ch < codecCtx_->ch_layout.nb_channels; ch++) { auto *src = reinterpret_cast(resampledData_[ch]); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h index c2f8ce7d2..3c360d21d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h @@ -83,7 +83,7 @@ class StreamerNode : public AudioScheduledSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp index 5fdad711c..8637ee776 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp @@ -20,11 +20,11 @@ WorkletSourceNode::WorkletSourceNode( } std::shared_ptr WorkletSourceNode::processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) { if (isUnscheduled() || isFinished() || !isEnabled()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } size_t startOffset = 0; @@ -32,11 +32,11 @@ std::shared_ptr WorkletSourceNode::processNode( std::shared_ptr context = context_.lock(); if (context == nullptr) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, nonSilentFramesToProcess, @@ -44,11 +44,11 @@ std::shared_ptr WorkletSourceNode::processNode( context->getCurrentSampleFrame()); if (nonSilentFramesToProcess == 0) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } - size_t outputChannelCount = processingBus->getNumberOfChannels(); + size_t outputChannelCount = processingBuffer->getNumberOfChannels(); auto result = workletRunner_.executeOnRuntimeSync( [this, nonSilentFramesToProcess, startOffset, time = context->getCurrentTime()]( @@ -72,19 +72,19 @@ std::shared_ptr WorkletSourceNode::processNode( // If the worklet execution failed, zero the output // It might happen if the runtime is not available if (!result.has_value()) { - processingBus->zero(); - return processingBus; + processingBuffer->zero(); + return processingBuffer; } // Copy the processed data back to the AudioBuffer for (int i = 0; i < outputChannelCount; ++i) { - processingBus->getChannel(i)->copy( + processingBuffer->getChannel(i)->copy( *outputBuffsHandles_[i], 0, startOffset, nonSilentFramesToProcess); } handleStopScheduled(); - return processingBus; + return processingBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h index 93b098767..9596f6822 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h @@ -23,9 +23,9 @@ class WorkletSourceNode : public AudioScheduledSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return processingBus; + return processingBuffer; } }; #else @@ -38,7 +38,7 @@ class WorkletSourceNode : public AudioScheduledSourceNode { protected: std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override; private: diff --git a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp index 8c09eaa14..f8ce75501 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp @@ -33,14 +33,14 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { } void updatePlaybackInfo( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess, size_t &startOffset, size_t &nonSilentFramesToProcess, float sampleRate, size_t currentSampleFrame) { AudioScheduledSourceNode::updatePlaybackInfo( - processingBus, + processingBuffer, framesToProcess, startOffset, nonSilentFramesToProcess, @@ -60,15 +60,16 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { if (std::shared_ptr context = context_.lock()) { size_t startOffset = 0; size_t nonSilentFramesToProcess = 0; - auto processingBus = std::make_shared(128, 2, static_cast(SAMPLE_RATE)); + auto processingBuffer = + std::make_shared(128, 2, static_cast(SAMPLE_RATE)); updatePlaybackInfo( - processingBus, + processingBuffer, frames, startOffset, nonSilentFramesToProcess, context->getSampleRate(), context->getCurrentSampleFrame()); - context->getDestination()->renderAudio(processingBus, frames); + context->getDestination()->renderAudio(processingBuffer, frames); } } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp index 85e7b4ad4..90ad1ce32 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp @@ -34,9 +34,9 @@ class TestableConstantSourceNode : public ConstantSourceNode { } std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return ConstantSourceNode::processNode(processingBus, framesToProcess); + return ConstantSourceNode::processNode(processingBuffer, framesToProcess); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index 3ea5f2b54..2da57a8c2 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -34,9 +34,9 @@ class TestableDelayNode : public DelayNode { } std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return DelayNode::processNode(processingBus, framesToProcess); + return DelayNode::processNode(processingBuffer, framesToProcess); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp index 1accf6785..2303795f5 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp @@ -34,9 +34,9 @@ class TestableGainNode : public GainNode { } std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return GainNode::processNode(processingBus, framesToProcess); + return GainNode::processNode(processingBuffer, framesToProcess); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp index 18d935e9f..6bdc2160e 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp @@ -34,9 +34,9 @@ class TestableStereoPannerNode : public StereoPannerNode { } std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return StereoPannerNode::processNode(processingBus, framesToProcess); + return StereoPannerNode::processNode(processingBuffer, framesToProcess); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 474e953d0..f0d52d2ce 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -36,9 +36,9 @@ class TestableWaveShaperNode : public WaveShaperNode { } std::shared_ptr processNode( - const std::shared_ptr &processingBus, + const std::shared_ptr &processingBuffer, int framesToProcess) override { - return WaveShaperNode::processNode(processingBus, framesToProcess); + return WaveShaperNode::processNode(processingBuffer, framesToProcess); } std::shared_ptr testCurve_; From a39e9553b668875ff543d90cdc8a229ead97b07e Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 20:36:48 +0100 Subject: [PATCH 21/73] refactor: renaming --- .../android/core/utils/AudioDecoder.cpp | 13 +++---- .../BaseAudioContextHostObject.cpp | 9 ++--- .../audioapi/HostObjects/utils/NodeOptions.h | 2 +- .../HostObjects/utils/NodeOptionsParser.h | 2 +- .../common/cpp/audioapi/core/AudioNode.cpp | 16 ++++----- .../common/cpp/audioapi/core/AudioNode.h | 2 +- .../common/cpp/audioapi/core/AudioParam.cpp | 17 ++++----- .../common/cpp/audioapi/core/AudioParam.h | 2 +- .../cpp/audioapi/core/OfflineAudioContext.cpp | 6 ++-- .../audioapi/core/analysis/AnalyserNode.cpp | 6 ++-- .../audioapi/core/effects/ConvolverNode.cpp | 23 ++++++------ .../cpp/audioapi/core/effects/DelayNode.cpp | 13 ++++--- .../core/effects/StereoPannerNode.cpp | 6 ++-- .../cpp/audioapi/core/effects/WorkletNode.cpp | 8 ++--- .../cpp/audioapi/core/effects/WorkletNode.h | 2 +- .../core/effects/WorkletProcessingNode.cpp | 2 +- .../core/sources/AudioBufferSourceNode.cpp | 2 +- .../audioapi/core/sources/OscillatorNode.cpp | 2 +- .../core/sources/RecorderAdapterNode.cpp | 2 +- .../core/sources/RecorderAdapterNode.h | 2 +- .../audioapi/core/sources/StreamerNode.cpp | 6 ++-- .../cpp/audioapi/core/sources/StreamerNode.h | 16 ++++----- .../core/utils/AudioRecorderCallback.cpp | 12 ++++--- .../core/utils/AudioRecorderCallback.h | 2 +- .../audioapi/core/utils/AudioStretcher.cpp | 6 ++-- .../audioapi/libs/ffmpeg/FFmpegDecoding.cpp | 6 ++-- .../common/cpp/audioapi/utils/AudioBuffer.cpp | 36 +++++++++---------- .../cpp/audioapi/utils/CircularAudioArray.cpp | 1 - .../cpp/test/src/ConstantSourceTest.cpp | 6 ++-- .../common/cpp/test/src/DelayTest.cpp | 30 ++++++++-------- .../common/cpp/test/src/GainTest.cpp | 18 +++++----- .../common/cpp/test/src/StereoPannerTest.cpp | 28 +++++++-------- .../src/core/effects/WaveShaperNodeTest.cpp | 8 ++--- .../ios/audioapi/ios/core/IOSAudioPlayer.h | 2 +- .../ios/audioapi/ios/core/IOSAudioPlayer.mm | 15 ++++---- .../ios/audioapi/ios/core/IOSAudioRecorder.h | 1 - .../audioapi/ios/core/utils/AudioDecoder.mm | 12 +++---- 37 files changed, 174 insertions(+), 168 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp index c27e69074..c08e4ba30 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp @@ -53,15 +53,16 @@ std::shared_ptr AudioDecoder::makeAudioBufferFromFloatBuffer( } auto outputFrames = buffer.size() / outputChannels; - auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); + auto audioBuffer = std::make_shared(outputFrames, outputChannels, outputSampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = buffer[i * outputChannels + ch]; } } - return audioBus; + + return audioBuffer; } std::shared_ptr AudioDecoder::decodeWithFilePath( @@ -161,11 +162,11 @@ std::shared_ptr AudioDecoder::decodeWithPCMInBase64( const auto uint8Data = reinterpret_cast(decodedData.data()); size_t numFramesDecoded = decodedData.size() / (inputChannelCount * sizeof(int16_t)); - auto audioBus = + auto audioBuffer = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); for (int ch = 0; ch < inputChannelCount; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { size_t offset; @@ -180,7 +181,7 @@ std::shared_ptr AudioDecoder::decodeWithPCMInBase64( channelData[i] = uint8ToFloat(uint8Data[offset], uint8Data[offset + 1]); } } - return audioBus; + return audioBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index aa9978d2a..4bf7dd070 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include #include @@ -11,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,7 +78,8 @@ JSI_PROPERTY_GETTER_IMPL(BaseAudioContextHostObject, destination) { } JSI_PROPERTY_GETTER_IMPL(BaseAudioContextHostObject, state) { - return jsi::String::createFromUtf8(runtime, js_enum_parser::contextStateToString(context_->getState())); + return jsi::String::createFromUtf8( + runtime, js_enum_parser::contextStateToString(context_->getState())); } JSI_PROPERTY_GETTER_IMPL(BaseAudioContextHostObject, sampleRate) { @@ -309,7 +310,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) { const auto convolverOptions = audioapi::option_parser::parseConvolverOptions(runtime, options); auto convolverHostObject = std::make_shared(context_, convolverOptions); auto jsiObject = jsi::Object::createFromHostObject(runtime, convolverHostObject); - if (convolverOptions.bus != nullptr) { + if (convolverOptions.buffer != nullptr) { auto bufferHostObject = options.getProperty(runtime, "buffer") .getObject(runtime) .asHostObject(runtime); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h index e6d7cf68a..17a3a2bda 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h @@ -31,7 +31,7 @@ struct StereoPannerOptions : AudioNodeOptions { }; struct ConvolverOptions : AudioNodeOptions { - std::shared_ptr bus; + std::shared_ptr buffer; bool disableNormalization; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h index 0810d684e..595a13b8b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h @@ -63,7 +63,7 @@ ConvolverOptions parseConvolverOptions(jsi::Runtime &runtime, const jsi::Object auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") .getObject(runtime) .asHostObject(runtime); - options.bus = bufferHostObject->audioBuffer_; + options.buffer = bufferHostObject->audioBuffer_; } return options; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp index 95aae1923..f0cc01e1c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp @@ -12,7 +12,7 @@ namespace audioapi { AudioNode::AudioNode(const std::shared_ptr &context) : context_(context) { - audioBus_ = + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); } @@ -23,7 +23,7 @@ AudioNode::AudioNode( channelCount_(options.channelCount), channelCountMode_(options.channelCountMode), channelInterpretation_(options.channelInterpretation) { - audioBus_ = + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); } @@ -129,16 +129,16 @@ std::shared_ptr AudioNode::processAudio( } if (checkIsAlreadyProcessed && isAlreadyProcessed()) { - return audioBus_; + return audioBuffer_; } - // Process inputs and return the bus with the most channels. + // Process inputs and return the buffer with the most channels. auto processingBuffer = processInputs(outputBus, framesToProcess, checkIsAlreadyProcessed); // Apply channel count mode. processingBuffer = applyChannelCountMode(processingBuffer); - // Mix all input buses into the processing bus. + // Mix all input buffers into the processing buffer. mixInputsBuses(processingBuffer); assert(processingBuffer != nullptr); @@ -170,7 +170,7 @@ std::shared_ptr AudioNode::processInputs( const std::shared_ptr &outputBus, int framesToProcess, bool checkIsAlreadyProcessed) { - auto processingBuffer = audioBus_; + auto processingBuffer = audioBuffer_; processingBuffer->zero(); int maxNumberOfChannels = 0; @@ -199,14 +199,14 @@ std::shared_ptr AudioNode::applyChannelCountMode( // If the channelCountMode is EXPLICIT, the node should output the number of // channels specified by the channelCount. if (channelCountMode_ == ChannelCountMode::EXPLICIT) { - return audioBus_; + return audioBuffer_; } // If the channelCountMode is CLAMPED_MAX, the node should output the maximum // number of channels clamped to channelCount. if (channelCountMode_ == ChannelCountMode::CLAMPED_MAX && processingBuffer->getNumberOfChannels() >= channelCount_) { - return audioBus_; + return audioBuffer_; } return processingBuffer; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 91e4ab675..37578ff8c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -53,7 +53,7 @@ class AudioNode : public std::enable_shared_from_this { friend class DelayNodeHostObject; std::weak_ptr context_; - std::shared_ptr audioBus_; + std::shared_ptr audioBuffer_; int numberOfInputs_ = 1; int numberOfOutputs_ = 1; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp index 737e4507f..a2af0c5b5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp @@ -24,7 +24,8 @@ AudioParam::AudioParam( endTime_(0), startValue_(defaultValue), endValue_(defaultValue), - audioBus_(std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { + audioBuffer_( + std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { inputBuses_.reserve(4); inputNodes_.reserve(4); // Default calculation function just returns the static value @@ -256,13 +257,13 @@ std::shared_ptr AudioParam::calculateInputs( std::shared_ptr AudioParam::processARateParam(int framesToProcess, double time) { processScheduledEvents(); - auto processingBuffer = calculateInputs(audioBus_, framesToProcess); + auto processingBuffer = calculateInputs(audioBuffer_, framesToProcess); std::shared_ptr context = context_.lock(); if (context == nullptr) return processingBuffer; float sampleRate = context->getSampleRate(); - auto busData = processingBuffer->getChannel(0)->span(); + auto bufferData = processingBuffer->getChannel(0)->span(); float timeCache = time; float timeStep = 1.0f / sampleRate; float sample = 0.0f; @@ -270,15 +271,15 @@ std::shared_ptr AudioParam::processARateParam(int framesToProcess, // Add automated parameter value to each sample for (size_t i = 0; i < framesToProcess; i++, timeCache += timeStep) { sample = getValueAtTime(timeCache); - busData[i] += sample; + bufferData[i] += sample; } - // processingBuffer is a mono bus containing per-sample parameter values + // processingBuffer is a mono buffer containing per-sample parameter values return processingBuffer; } float AudioParam::processKRateParam(int framesToProcess, double time) { processScheduledEvents(); - auto processingBuffer = calculateInputs(audioBus_, framesToProcess); + auto processingBuffer = calculateInputs(audioBuffer_, framesToProcess); // Return block-rate parameter value plus first sample of input modulation return processingBuffer->getChannel(0)->span()[0] + getValueAtTime(time); @@ -296,7 +297,7 @@ void AudioParam::processInputs( continue; } - // Process this input node and store its output bus + // Process this input node and store its output buffer auto inputBus = inputNode->processAudio(outputBus, framesToProcess, checkIsAlreadyProcessed); inputBuses_.emplace_back(inputBus); } @@ -305,7 +306,7 @@ void AudioParam::processInputs( void AudioParam::mixInputsBuses(const std::shared_ptr &processingBuffer) { assert(processingBuffer != nullptr); - // Sum all input buses into the processing bus + // Sum all input buffers into the processing buffer for (auto it = inputBuses_.begin(), end = inputBuses_.end(); it != end; ++it) { processingBuffer->sum(**it, ChannelInterpretation::SPEAKERS); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index ed7f6fb66..37eb209d2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -111,7 +111,7 @@ class AudioParam { // Input modulation system std::vector inputNodes_; - std::shared_ptr audioBus_; + std::shared_ptr audioBuffer_; std::vector> inputBuses_; /// @brief Get the end time of the parameter queue. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp index 79cb330cf..f8d20a6db 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp @@ -64,7 +64,7 @@ void OfflineAudioContext::renderAudio() { setState(ContextState::RUNNING); std::thread([this]() { - auto audioBus = + auto audioBuffer = std::make_shared(RENDER_QUANTUM_SIZE, numberOfChannels_, getSampleRate()); while (currentSampleFrame_ < length_) { @@ -72,9 +72,9 @@ void OfflineAudioContext::renderAudio() { int framesToProcess = std::min(static_cast(length_ - currentSampleFrame_), RENDER_QUANTUM_SIZE); - destination_->renderAudio(audioBus, framesToProcess); + destination_->renderAudio(audioBuffer, framesToProcess); - resultBus_->copy(*audioBus, 0, currentSampleFrame_, framesToProcess); + resultBus_->copy(*audioBuffer, 0, currentSampleFrame_, framesToProcess); currentSampleFrame_ += framesToProcess; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 5cd6dd660..99d07d7e2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -94,7 +94,7 @@ void AnalyserNode::getFloatFrequencyData(float *data, int length) { auto magnitudeSpan = magnitudeArray_->span(); for (int i = 0; i < length; i++) { - data[i] = dsp::linearToDecibels(magnitudeSpan[i]); + data[i] = dsp::linearToDecibels(magnitudeSpan[i]); } } @@ -157,9 +157,9 @@ std::shared_ptr AnalyserNode::processNode( // Analyser should behave like a sniffer node, it should not modify the // processingBuffer but instead copy the data to its own input buffer. - // Down mix the input bus to mono + // Down mix the input buffer to mono downMixBus_->copy(*processingBuffer); - // Copy the down mixed bus to the input buffer (circular buffer) + // Copy the down mixed buffer to the input buffer (circular buffer) inputArray_->push_back(*downMixBus_->getChannel(0), framesToProcess, true); shouldDoFFTAnalysis_ = true; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 1b72d49f4..1deff6a10 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -25,8 +25,8 @@ ConvolverNode::ConvolverNode( intermediateBuffer_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - setBuffer(options.bus); - audioBus_ = + setBuffer(options.buffer); + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); requiresTailProcessing_ = true; isInitialized_ = true; @@ -95,7 +95,7 @@ std::shared_ptr ConvolverNode::processInputs( return AudioNode::processInputs(outputBus, 0, false); } -// processing pipeline: processingBuffer -> intermediateBuffer_ -> audioBus_ (mixing +// processing pipeline: processingBuffer -> intermediateBuffer_ -> audioBuffer_ (mixing // with intermediateBuffer_) std::shared_ptr ConvolverNode::processNode( const std::shared_ptr &processingBuffer, @@ -112,13 +112,13 @@ std::shared_ptr ConvolverNode::processNode( } if (internalBufferIndex_ < framesToProcess) { performConvolution(processingBuffer); // result returned to intermediateBuffer_ - audioBus_->sum(*intermediateBuffer_); + audioBuffer_->sum(*intermediateBuffer_); - internalBuffer_->copy(*audioBus_, 0, internalBufferIndex_, RENDER_QUANTUM_SIZE); + internalBuffer_->copy(*audioBuffer_, 0, internalBufferIndex_, RENDER_QUANTUM_SIZE); internalBufferIndex_ += RENDER_QUANTUM_SIZE; } - audioBus_->zero(); - audioBus_->copy(*internalBuffer_, 0, 0, framesToProcess); + audioBuffer_->zero(); + audioBuffer_->copy(*internalBuffer_, 0, 0, framesToProcess); int remainingFrames = internalBufferIndex_ - framesToProcess; if (remainingFrames > 0) { for (int ch = 0; ch < internalBuffer_->getNumberOfChannels(); ++ch) { @@ -128,11 +128,11 @@ std::shared_ptr ConvolverNode::processNode( internalBufferIndex_ -= framesToProcess; - for (int i = 0; i < audioBus_->getNumberOfChannels(); ++i) { - audioBus_->getChannel(i)->scale(scaleFactor_); + for (int i = 0; i < audioBuffer_->getNumberOfChannels(); ++i) { + audioBuffer_->getChannel(i)->scale(scaleFactor_); } - return audioBus_; + return audioBuffer_; } void ConvolverNode::calculateNormalizationScale() { @@ -164,7 +164,8 @@ void ConvolverNode::performConvolution(const std::shared_ptr &proce if (processingBuffer->getNumberOfChannels() == 1) { for (int i = 0; i < convolvers_.size(); ++i) { threadPool_->schedule([&, i] { - convolvers_[i].process(*processingBuffer->getChannel(0), *intermediateBuffer_->getChannel(i)); + convolvers_[i].process( + *processingBuffer->getChannel(0), *intermediateBuffer_->getChannel(i)); }); } } else if (processingBuffer->getNumberOfChannels() == 2) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 817a41239..f2b287446 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -78,8 +78,8 @@ void DelayNode::delayBufferOperation( // delay buffer always has channelCount_ channels // processing is split into two parts -// 1. writing to delay buffer (mixing if needed) from processing bus -// 2. reading from delay buffer to processing bus (mixing if needed) with delay +// 1. writing to delay buffer (mixing if needed) from processing buffer +// 2. reading from delay buffer to processing buffer (mixing if needed) with delay std::shared_ptr DelayNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { @@ -91,7 +91,8 @@ std::shared_ptr DelayNode::processNode( return processingBuffer; } - delayBufferOperation(processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); + delayBufferOperation( + processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); remainingFrames_ -= framesToProcess; return processingBuffer; } @@ -103,8 +104,10 @@ std::shared_ptr DelayNode::processNode( auto delayTime = delayTimeParam_->processKRateParam(framesToProcess, context->getCurrentTime()); size_t writeIndex = static_cast(readIndex_ + delayTime * context->getSampleRate()) % delayBuffer_->getSize(); - delayBufferOperation(processingBuffer, framesToProcess, writeIndex, DelayNode::BufferAction::WRITE); - delayBufferOperation(processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); + delayBufferOperation( + processingBuffer, framesToProcess, writeIndex, DelayNode::BufferAction::WRITE); + delayBufferOperation( + processingBuffer, framesToProcess, readIndex_, DelayNode::BufferAction::READ); return processingBuffer; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp index 8accf2e4d..879a6ea0b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp @@ -33,8 +33,8 @@ std::shared_ptr StereoPannerNode::processNode( auto panParamValues = panParam_->processARateParam(framesToProcess, time)->getChannel(0)->span(); - auto outputLeft = audioBus_->getChannelByType(AudioBuffer::ChannelLeft)->span(); - auto outputRight = audioBus_->getChannelByType(AudioBuffer::ChannelRight)->span(); + auto outputLeft = audioBuffer_->getChannelByType(AudioBuffer::ChannelLeft)->span(); + auto outputRight = audioBuffer_->getChannelByType(AudioBuffer::ChannelRight)->span(); // Input is mono if (processingBuffer->getNumberOfChannels() == 1) { @@ -74,7 +74,7 @@ std::shared_ptr StereoPannerNode::processNode( } } - return audioBus_; + return audioBuffer_; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index fcce0f364..4cbdb79e8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -13,7 +13,7 @@ WorkletNode::WorkletNode( WorkletsRunner &&runtime) : AudioNode(context), workletRunner_(std::move(runtime)), - bus_( + buffer_( std::make_shared(bufferLength, inputChannelCount, context->getSampleRate())), bufferLength_(bufferLength), inputChannelCount_(inputChannelCount), @@ -35,7 +35,7 @@ std::shared_ptr WorkletNode::processNode( /// here we copy /// to [curBuffIndex_, curBuffIndex_ + shouldProcess] /// from [processed, processed + shouldProcess] - bus_->copy(*processingBuffer, processed, curBuffIndex_, shouldProcess); + buffer_->copy(*processingBuffer, processed, curBuffIndex_, shouldProcess); processed += shouldProcess; curBuffIndex_ += shouldProcess; @@ -51,14 +51,14 @@ std::shared_ptr WorkletNode::processNode( auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_); for (int ch = 0; ch < channelCount_; ch++) { auto sharedAudioArray = std::make_shared(bufferLength_); - sharedAudioArray->copy(*bus_->getChannel(ch)); + sharedAudioArray->copy(*buffer_->getChannel(ch)); auto sharedAudioArraySize = sharedAudioArray->size(); auto arrayBuffer = jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray)); arrayBuffer.setExternalMemoryPressure(uiRuntimeRaw, sharedAudioArraySize); jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer)); } - bus_->zero(); + buffer_->zero(); /// Call the worklet workletRunner_.callUnsafe( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h index 9a1afb11f..0523f99fd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h @@ -51,7 +51,7 @@ class WorkletNode : public AudioNode { private: WorkletsRunner workletRunner_; - std::shared_ptr bus_; + std::shared_ptr buffer_; /// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer size_t bufferLength_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index 9768bc6c5..7aa0ca58e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -63,7 +63,7 @@ std::shared_ptr WorkletProcessingNode::processNode( jsi::Value(rt, time)); }); - // Copy processed output data back to the processing bus or zero on failure + // Copy processed output data back to the processing buffer or zero on failure for (int ch = 0; ch < channelCount; ch++) { auto channelData = processingBuffer->getChannel(ch); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index d27729fad..ddb17bc1c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -103,7 +103,7 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer } else { alignedBus_ = std::make_shared(*buffer_); } - audioBus_ = + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); playbackRateBus_ = std::make_shared( RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index e9340f7f5..cfab566fc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -27,7 +27,7 @@ OscillatorNode::OscillatorNode( periodicWave_ = context->getBasicWaveForm(type_); } - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate()); + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate()); isInitialized_ = true; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index 096f20b40..73bac391d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -32,7 +32,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // This assumes that the sample rate is the same in audio context and recorder. // (recorder is not enforcing any sample rate on the system*). This means that only - // channel mixing might be required. To do so, we create an output bus with + // channel mixing might be required. To do so, we create an output buffer with // the desired channel count and will take advantage of the AudioBuffer sum method. // // * any allocations required by the recorder (including this method) are during recording start diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h index a3d5aba8f..6d98e90a2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h @@ -39,7 +39,7 @@ class RecorderAdapterNode : public AudioNode { std::shared_ptr adapterOutputBus_; private: - /// @brief Read audio frames from the recorder's internal circular buffer into output buss. + /// @brief Read audio frames from the recorder's internal circular buffer into output buffers. /// @note If `framesToRead` is greater than the number of available frames, it will fill empty space with silence. /// @param framesToRead Number of frames to read. void readFrames(size_t framesToRead); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp index f57af1e59..b48ee9e38 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,6 @@ #include #include #include -#include namespace audioapi { #if !RN_AUDIO_API_FFMPEG_DISABLED @@ -90,7 +88,7 @@ bool StreamerNode::initialize(const std::string &input_url) { } channelCount_ = codecpar_->ch_layout.nb_channels; - audioBus_ = + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); auto [sender, receiver] = channels::spsc::channel< @@ -144,7 +142,7 @@ std::shared_ptr StreamerNode::processNode( StreamingData data; auto res = receiver_.try_receive(data); if (res == channels::spsc::ResponseStatus::SUCCESS) { - bufferedBus_ = std::make_shared(std::move(data.bus)); + bufferedBus_ = std::make_shared(std::move(data.buffer)); bufferedBusSize_ = data.size; processedSamples_ = 0; } else { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h index 3c360d21d..70b48a268 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h @@ -40,17 +40,17 @@ inline constexpr auto VERBOSE = false; inline constexpr auto CHANNEL_CAPACITY = 32; struct StreamingData { - audioapi::AudioBuffer bus; + audioapi::AudioBuffer buffer; size_t size; StreamingData() = default; - StreamingData(audioapi::AudioBuffer b, size_t s) : bus(b), size(s) {} - StreamingData(const StreamingData &data) : bus(data.bus), size(data.size) {} - StreamingData(StreamingData &&data) noexcept : bus(std::move(data.bus)), size(data.size) {} + StreamingData(audioapi::AudioBuffer b, size_t s) : buffer(b), size(s) {} + StreamingData(const StreamingData &data) : buffer(data.buffer), size(data.size) {} + StreamingData(StreamingData &&data) noexcept : buffer(std::move(data.buffer)), size(data.size) {} StreamingData &operator=(const StreamingData &data) { if (this == &data) { return *this; } - bus = data.bus; + buffer = data.buffer; size = data.size; return *this; } @@ -97,8 +97,8 @@ class StreamerNode : public AudioScheduledSourceNode { SwrContext *swrCtx_; uint8_t **resampledData_; // weird ffmpeg way of using raw byte pointers for resampled data - std::shared_ptr bufferedBus_; // audio bus for buffering hls frames - size_t bufferedBusSize_; // size of currently buffered bus + std::shared_ptr bufferedBus_; // audio buffer for buffering hls frames + size_t bufferedBusSize_; // size of currently buffered buffer int audio_stream_index_; // index of the audio stream channel in the input int maxResampledSamples_; size_t processedSamples_; @@ -133,7 +133,7 @@ class StreamerNode : public AudioScheduledSourceNode { /** * @brief Thread function to continuously read and process audio frames * @details This function runs in a separate thread to avoid blocking the main audio processing thread - * @note It will read frames from the input stream, resample them, and store them in the buffered bus + * @note It will read frames from the input stream, resample them, and store them in the buffered buffer * @note The thread will stop when streamFlag is set to false */ void streamAudio(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp index 4de47a7a3..fd9e41ea8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp @@ -56,18 +56,20 @@ void AudioRecorderCallback::emitAudioData(bool flush) { } while (circularBus_[0]->getNumberOfAvailableFrames() >= sizeLimit) { - auto bus = std::make_shared(sizeLimit, channelCount_, sampleRate_); + auto buffer = std::make_shared(sizeLimit, channelCount_, sampleRate_); for (int i = 0; i < channelCount_; ++i) { - circularBus_[i]->pop_front(*bus->getChannel(i), sizeLimit); + circularBus_[i]->pop_front(*buffer->getChannel(i), sizeLimit); } - invokeCallback(bus, static_cast(sizeLimit)); + invokeCallback(buffer, static_cast(sizeLimit)); } } -void AudioRecorderCallback::invokeCallback(const std::shared_ptr &bus, int numFrames) { - auto audioBufferHostObject = std::make_shared(bus); +void AudioRecorderCallback::invokeCallback( + const std::shared_ptr &buffer, + int numFrames) { + auto audioBufferHostObject = std::make_shared(buffer); std::unordered_map eventPayload = {}; eventPayload.insert({"buffer", audioBufferHostObject}); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h index 4ffed6bae..3b5746791 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h @@ -28,7 +28,7 @@ class AudioRecorderCallback { virtual void cleanup() = 0; void emitAudioData(bool flush = false); - void invokeCallback(const std::shared_ptr &bus, int numFrames); + void invokeCallback(const std::shared_ptr &buffer, int numFrames); void setOnErrorCallback(uint64_t callbackId); void clearOnErrorCallback(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp index 1f650575e..647787c28 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp @@ -59,16 +59,16 @@ std::shared_ptr AudioStretcher::changePlaybackSpeed( stretchedBuffer.resize(outputFrames * outputChannels); stretch_deinit(stretcher); - auto audioBus = std::make_shared(outputFrames, outputChannels, sampleRate); + auto audioBuffer = std::make_shared(outputFrames, outputChannels, sampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = int16ToFloat(stretchedBuffer[i * outputChannels + ch]); } } - return audioBus; + return audioBuffer; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp index 1773bab53..7cca55feb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp @@ -246,16 +246,16 @@ std::shared_ptr decodeAudioFrames( } auto outputFrames = decoded_buffer.size() / output_channel_count; - auto audioBus = + auto audioBuffer = std::make_shared(outputFrames, output_channel_count, output_sample_rate); for (int ch = 0; ch < output_channel_count; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = decoded_buffer[i * output_channel_count + ch]; } } - return audioBus; + return audioBuffer; } std::shared_ptr decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 9d0a9f18d..835d3b237 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -1,9 +1,9 @@ +#include #include #include #include #include #include -#include #include #include @@ -202,13 +202,13 @@ void AudioBuffer::sum( return; } - // Source channel count is smaller than current bus, we need to up-mix. + // Source channel count is smaller than current buffer, we need to up-mix. if (numberOfSourceChannels < numberOfChannels) { sumByUpMixing(source, sourceStart, destinationStart, length); return; } - // Source channel count is larger than current bus, we need to down-mix. + // Source channel count is larger than current buffer, we need to down-mix. if (numberOfSourceChannels > numberOfChannels) { sumByDownMixing(source, sourceStart, destinationStart, length); return; @@ -257,25 +257,25 @@ void AudioBuffer::interleaveTo(float *destination, size_t frames) const { } if (numberOfChannels_ == 2) { - dsp::interleaveStereo(channels_[0]->begin(), channels_[1]->begin(), destination, frames); - return; + dsp::interleaveStereo(channels_[0]->begin(), channels_[1]->begin(), destination, frames); + return; } - float* channelsPtrs[MAX_CHANNEL_COUNT]; - for (int i = 0; i < numberOfChannels_; ++i) { - channelsPtrs[i] = channels_[i]->begin(); - } + float *channelsPtrs[MAX_CHANNEL_COUNT]; + for (int i = 0; i < numberOfChannels_; ++i) { + channelsPtrs[i] = channels_[i]->begin(); + } - constexpr size_t kBlockSize = 64; - for (size_t blockStart = 0; blockStart < frames; blockStart += kBlockSize) { - size_t blockEnd = std::min(blockStart + kBlockSize, frames); - for (size_t i = blockStart; i < blockEnd; ++i) { - float* frameDest = destination + (i * numberOfChannels_); - for (int ch = 0; ch < numberOfChannels_; ++ch) { - frameDest[ch] = channelsPtrs[ch][i]; - } - } + constexpr size_t kBlockSize = 64; + for (size_t blockStart = 0; blockStart < frames; blockStart += kBlockSize) { + size_t blockEnd = std::min(blockStart + kBlockSize, frames); + for (size_t i = blockStart; i < blockEnd; ++i) { + float *frameDest = destination + (i * numberOfChannels_); + for (int ch = 0; ch < numberOfChannels_; ++ch) { + frameDest[ch] = channelsPtrs[ch][i]; + } } + } } void AudioBuffer::normalize() { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp index 14848094a..a0433bfc6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp @@ -17,7 +17,6 @@ void CircularAudioArray::push_back(const AudioArray & data, size_t size, bool sk if (vWriteIndex_ + size > size_) { auto partSize = size_ - vWriteIndex_; - copy(data, 0, vWriteIndex_, partSize); copy(data, partSize, 0, size - partSize); } else { diff --git a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp index 90ad1ce32..b85140e14 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp @@ -48,17 +48,17 @@ TEST_F(ConstantSourceTest, ConstantSourceCanBeCreated) { TEST_F(ConstantSourceTest, ConstantSourceOutputsConstantValue) { static constexpr int FRAMES_TO_PROCESS = 4; - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); auto constantSource = TestableConstantSourceNode(context); // constantSource.start(context->getCurrentTime()); - // auto resultBus = constantSource.processNode(bus, FRAMES_TO_PROCESS); + // auto resultBus = constantSource.processNode(buffer, FRAMES_TO_PROCESS); // for (int i = 0; i < FRAMES_TO_PROCESS; ++i) { // EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], 1.0f); // } // constantSource.setOffsetParam(0.5f); - // resultBus = constantSource.processNode(bus, FRAMES_TO_PROCESS); + // resultBus = constantSource.processNode(buffer, FRAMES_TO_PROCESS); // for (int i = 0; i < FRAMES_TO_PROCESS; ++i) { // EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], 0.5f); // } diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index 2da57a8c2..8b89667e5 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -53,12 +53,12 @@ TEST_F(DelayTest, DelayWithZeroDelayOutputsInputSignal) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = i + 1; } - auto resultBus = delayNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = delayNode.processNode(buffer, FRAMES_TO_PROCESS); for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], static_cast(i + 1)); } @@ -72,12 +72,12 @@ TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = i + 1; } - auto resultBus = delayNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = delayNode.processNode(buffer, FRAMES_TO_PROCESS); for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { if (i < FRAMES_TO_PROCESS / 2) { // First 64 samples should be zero due to delay EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], 0.0f); @@ -85,7 +85,7 @@ TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) { EXPECT_FLOAT_EQ( (*resultBus->getChannel(0))[i], static_cast( - i + 1 - FRAMES_TO_PROCESS / 2)); // Last 64 samples should be 1st part of bus + i + 1 - FRAMES_TO_PROCESS / 2)); // Last 64 samples should be 1st part of buffer } } } @@ -98,15 +98,15 @@ TEST_F(DelayTest, DelayHandlesTailCorrectly) { auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = i + 1; } - delayNode.processNode(bus, FRAMES_TO_PROCESS); - auto resultBus = delayNode.processNode(bus, FRAMES_TO_PROCESS); + delayNode.processNode(buffer, FRAMES_TO_PROCESS); + auto resultBus = delayNode.processNode(buffer, FRAMES_TO_PROCESS); for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { - if (i < FRAMES_TO_PROCESS / 2) { // First 64 samples should be 2nd part of bus + if (i < FRAMES_TO_PROCESS / 2) { // First 64 samples should be 2nd part of buffer EXPECT_FLOAT_EQ( (*resultBus->getChannel(0))[i], static_cast(i + 1 + FRAMES_TO_PROCESS / 2)); } else { diff --git a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp index 2303795f5..036675869 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp @@ -51,12 +51,12 @@ TEST_F(GainTest, GainModulatesVolumeCorrectly) { auto gainNode = TestableGainNode(context); gainNode.setGainParam(GAIN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = i + 1; } - auto resultBus = gainNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = gainNode.processNode(buffer, FRAMES_TO_PROCESS); for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], (i + 1) * GAIN_VALUE); } @@ -68,13 +68,13 @@ TEST_F(GainTest, GainModulatesVolumeCorrectlyMultiChannel) { auto gainNode = TestableGainNode(context); gainNode.setGainParam(GAIN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = i + 1; - (*bus->getChannel(1))[i] = -i - 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = i + 1; + (*buffer->getChannel(1))[i] = -i - 1; } - auto resultBus = gainNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = gainNode.processNode(buffer, FRAMES_TO_PROCESS); for (size_t i = 0; i < FRAMES_TO_PROCESS; ++i) { EXPECT_FLOAT_EQ((*resultBus->getChannel(0))[i], (i + 1) * GAIN_VALUE); EXPECT_FLOAT_EQ((*resultBus->getChannel(1))[i], (-i - 1) * GAIN_VALUE); diff --git a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp index 6bdc2160e..d0f597631 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/StereoPannerTest.cpp @@ -51,12 +51,12 @@ TEST_F(StereoPannerTest, PanModulatesInputMonoCorrectly) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; } - auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = panNode.processNode(buffer, FRAMES_TO_PROCESS); // x = (0.5 + 1) / 2 = 0.75 // gainL = cos(x * (π / 2)) = cos(0.75 * (π / 2)) = 0.38268343236508984 // gainR = sin(x * (π / 2)) = sin(0.75 * (π / 2)) = 0.9238795325112867 @@ -78,13 +78,13 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithNegativePan) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; - (*bus->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; + (*buffer->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; } - auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = panNode.processNode(buffer, FRAMES_TO_PROCESS); // x = -0.5 + 1 = 0.5 // gainL = cos(x * (π / 2)) = cos(0.5 * (π / 2)) = 0.7071067811865476 // gainR = sin(x * (π / 2)) = sin(0.5 * (π / 2)) = 0.7071067811865476 @@ -106,13 +106,13 @@ TEST_F(StereoPannerTest, PanModulatesInputStereoCorrectlyWithPositivePan) { auto panNode = TestableStereoPannerNode(context); panNode.setPanParam(PAN_VALUE); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; - (*bus->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 2, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannelByType(AudioBuffer::ChannelLeft))[i] = i + 1; + (*buffer->getChannelByType(AudioBuffer::ChannelRight))[i] = i + 1; } - auto resultBus = panNode.processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = panNode.processNode(buffer, FRAMES_TO_PROCESS); // x = 0.75 // gainL = cos(x * (π / 2)) = cos(0.75 * (π / 2)) = 0.38268343236508984 // gainR = sin(x * (π / 2)) = sin(0.75 * (π / 2)) = 0.9238795325112867 diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index f0d52d2ce..9dac1b694 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -61,12 +61,12 @@ TEST_F(WaveShaperNodeTest, NoneOverSamplingProcessesCorrectly) { waveShaper->setOversample(OverSampleType::OVERSAMPLE_NONE); waveShaper->setCurve(waveShaper->testCurve_); - auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); - for (size_t i = 0; i < bus->getSize(); ++i) { - (*bus->getChannel(0))[i] = -1.0f + i * 0.5f; + auto buffer = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); + for (size_t i = 0; i < buffer->getSize(); ++i) { + (*buffer->getChannel(0))[i] = -1.0f + i * 0.5f; } - auto resultBus = waveShaper->processNode(bus, FRAMES_TO_PROCESS); + auto resultBus = waveShaper->processNode(buffer, FRAMES_TO_PROCESS); auto curveData = waveShaper->testCurve_->span(); auto resultData = resultBus->getChannel(0)->span(); diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h index c11e9b7fd..bf4643abf 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.h @@ -30,7 +30,7 @@ class IOSAudioPlayer { bool isRunning() const; protected: - std::shared_ptr audioBus_; + std::shared_ptr audioBuffer_; NativeAudioPlayer *audioPlayer_; std::function, int)> renderAudio_; int channelCount_; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm index 4d1d61b05..f9d05810a 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm @@ -13,7 +13,7 @@ const std::function, int)> &renderAudio, float sampleRate, int channelCount) - : renderAudio_(renderAudio), channelCount_(channelCount), audioBus_(0), isRunning_(false) + : renderAudio_(renderAudio), channelCount_(channelCount), audioBuffer_(0), isRunning_(false) { RenderAudioBlock renderAudioBlock = ^(AudioBufferList *outputData, int numFrames) { int processedFrames = 0; @@ -22,15 +22,16 @@ int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE); if (isRunning_.load(std::memory_order_acquire)) { - renderAudio_(audioBus_, framesToProcess); + renderAudio_(audioBuffer_, framesToProcess); } else { - audioBus_->zero(); + audioBuffer_->zero(); } for (int channel = 0; channel < channelCount_; channel += 1) { float *outputChannel = (float *)outputData->mBuffers[channel].mData; - audioBus_->getChannel(channel)->copyTo(outputChannel, 0, processedFrames, framesToProcess); + audioBuffer_->getChannel(channel)->copyTo( + outputChannel, 0, processedFrames, framesToProcess); } processedFrames += framesToProcess; @@ -41,7 +42,7 @@ sampleRate:sampleRate channelCount:channelCount_]; - audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate); + audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, sampleRate); } IOSAudioPlayer::~IOSAudioPlayer() @@ -96,8 +97,8 @@ stop(); [audioPlayer_ cleanup]; - if (audioBus_) { - audioBus_ = nullptr; + if (audioBuffer_) { + audioBuffer_ = nullptr; } } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h index ed356c18e..9137b9405 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.h @@ -10,7 +10,6 @@ typedef struct objc_object NativeAudioRecorder; #endif // __OBJC__ #include -#include #include #include diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm index 3d2df2fb5..2995256bc 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm @@ -51,16 +51,16 @@ } auto outputFrames = buffer.size() / outputChannels; - auto audioBus = std::make_shared(outputFrames, outputChannels, outputSampleRate); + auto audioBuffer = std::make_shared(outputFrames, outputChannels, outputSampleRate); for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = buffer[i * outputChannels + ch]; } } - return audioBus; + return audioBuffer; } std::shared_ptr AudioDecoder::decodeWithFilePath( @@ -152,11 +152,11 @@ const auto uint8Data = reinterpret_cast(decodedData.data()); size_t numFramesDecoded = decodedData.size() / (inputChannelCount * sizeof(int16_t)); - auto audioBus = + auto audioBuffer = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); for (int ch = 0; ch < inputChannelCount; ++ch) { - auto channelData = audioBus->getChannel(ch)->span(); + auto channelData = audioBuffer->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { size_t offset; @@ -171,7 +171,7 @@ channelData[i] = uint8ToFloat(uint8Data[offset], uint8Data[offset + 1]); } } - return audioBus; + return audioBuffer; } } // namespace audioapi From f9a3ac2a4c2f777126c6bd4e9fad58ae9584da86 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Feb 2026 20:42:21 +0100 Subject: [PATCH 22/73] refactor: doxygen for AudioBuffer --- .../common/cpp/audioapi/utils/AudioBuffer.h | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h index 660ff7135..6bdfae9bd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h @@ -73,9 +73,23 @@ class AudioBuffer { void zero(); void zero(size_t start, size_t length); + /// @brief Sums audio data from a source AudioBuffer into this AudioBuffer. + /// @param source The source AudioBuffer to sum from. + /// @param interpretation The channel interpretation to use for summing (default is SPEAKERS). + /// @note Handles up-mixing and down-mixing based on the number of channels in both buffers. + /// Assumes that source and this are located in two distinct, non-overlapping memory locations. void sum( const AudioBuffer &source, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); + + /// @brief Sums audio data from a source AudioBuffer into this AudioBuffer. + /// @param source The source AudioBuffer to sum from. + /// @param sourceStart The starting index in the source AudioBuffer. + /// @param destinationStart The starting index in this AudioBuffer. + /// @param length The number of samples to sum. + /// @param interpretation The channel interpretation to use for summing (default is SPEAKERS). + /// @note Handles up-mixing and down-mixing based on the number of channels in both buffers. + /// Assumes that source and this are located in two distinct, non-overlapping memory locations. void sum( const AudioBuffer &source, size_t sourceStart, @@ -83,7 +97,19 @@ class AudioBuffer { size_t length, ChannelInterpretation interpretation = ChannelInterpretation::SPEAKERS); + /// @brief Copies audio data from a source AudioBuffer into this AudioBuffer. + /// @param source The source AudioBuffer to copy from. + /// @note Handles up-mixing and down-mixing based on the number of channels in both buffers. + /// Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioBuffer &source); + + /// @brief Copies audio data from a source AudioBuffer into this AudioBuffer. + /// @param source The source AudioBuffer to copy from. + /// @param sourceStart The starting index in the source AudioBuffer. + /// @param destinationStart The starting index in this AudioBuffer. + /// @param length The number of samples to copy. + /// @note Handles up-mixing and down-mixing based on the number of channels in both buffers. + /// Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length); /// @brief Interleave audio data from this AudioBuffer into a destination buffer. @@ -93,6 +119,7 @@ class AudioBuffer { /// according to the number of channels in this AudioBuffer. /// Example of interleaved data for stereo (2 channels): /// [L0, R0, L1, R1, L2, R2, ...] + /// Assumes that this and destination are located in two distinct, non-overlapping memory locations. void interleaveTo(float *destination, size_t frames) const; void normalize(); From a92bbf98d94b56289be9a564ea7f569ecb4828fb Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Feb 2026 08:45:20 +0100 Subject: [PATCH 23/73] refactor: deinterleave audio data using AudioBuffer method --- .../android/core/utils/AudioDecoder.cpp | 7 +-- .../common/cpp/audioapi/dsp/VectorMath.cpp | 62 +++++++++++++++++++ .../common/cpp/audioapi/dsp/VectorMath.h | 6 ++ .../common/cpp/audioapi/utils/AudioBuffer.cpp | 55 ++++++++++------ .../common/cpp/audioapi/utils/AudioBuffer.h | 9 +++ .../audioapi/ios/core/utils/AudioDecoder.mm | 7 +-- 6 files changed, 114 insertions(+), 32 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp index c08e4ba30..4fa157d2a 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp @@ -55,12 +55,7 @@ std::shared_ptr AudioDecoder::makeAudioBufferFromFloatBuffer( auto outputFrames = buffer.size() / outputChannels; auto audioBuffer = std::make_shared(outputFrames, outputChannels, outputSampleRate); - for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBuffer->getChannel(ch)->span(); - for (int i = 0; i < outputFrames; ++i) { - channelData[i] = buffer[i * outputChannels + ch]; - } - } + audioBuffer->deinterleaveFrom(buffer.data(), outputFrames); return audioBuffer; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp index 18bdeab4c..d965bafc6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.cpp @@ -103,6 +103,16 @@ float computeConvolution(const float *state, const float *kernel, size_t kernelS return result; } +void deinterleaveStereo( + const float * __restrict inputInterleaved, + float * __restrict outputLeft, + float * __restrict outputRight, + size_t numberOfFrames) { + float zero = 0.0f; + vDSP_vsadd(inputInterleaved, 2, &zero, outputLeft, 1, numberOfFrames); + vDSP_vsadd(inputInterleaved + 1, 2, &zero, outputRight, 1, numberOfFrames); +} + void interleaveStereo( const float* __restrict inputLeft, const float* __restrict inputRight, @@ -699,6 +709,58 @@ float computeConvolution(const float *state, const float *kernel, size_t kernelS return sum; } +void deinterleaveStereo( + const float * __restrict inputInterleaved, + float * __restrict outputLeft, + float * __restrict outputRight, + size_t numberOfFrames) { + + size_t n = numberOfFrames; + +#if defined(HAVE_ARM_NEON_INTRINSICS) + // process 4 frames (8 samples) at a time using NEON + size_t group = n / 4; + while (group--) { + // vld2q_f32 deinterleaves L and R into separate registers in one hardware op + float32x4x2_t v = vld2q_f32(inputInterleaved); + vst1q_f32(outputLeft, v.val[0]); + vst1q_f32(outputRight, v.val[1]); + + inputInterleaved += 8; + outputLeft += 4; + outputRight += 4; + } + n %= 4; +#elif defined(HAVE_X86_SSE2) + // process 4 frames (8 samples) at a time using SSE + size_t group = n / 4; + while (group--) { + // load two 128-bit registers (8 floats total) + __m128 s0 = _mm_loadu_ps(inputInterleaved); + __m128 s1 = _mm_loadu_ps(inputInterleaved + 4); + + // use shuffle to group the Left samples and Right samples + // mask 0x88 (2,0,2,0) picks indices 0 and 2 from both s0 and s1 + // mask 0xDD (3,1,3,1) picks indices 1 and 3 from both s0 and s1 + __m128 left_v = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0)); + __m128 right_v = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); + + _mm_storeu_ps(outputLeft, left_v); + _mm_storeu_ps(outputRight, right_v); + + inputInterleaved += 8; + outputLeft += 4; + outputRight += 4; + } + n %= 4; +#endif + + while (n--) { + *outputLeft++ = *inputInterleaved++; + *outputRight++ = *inputInterleaved++; + } +} + void interleaveStereo( const float * __restrict inputLeft, const float * __restrict inputRight, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h index c66f46f9b..cbbfedabb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/VectorMath.h @@ -77,4 +77,10 @@ void interleaveStereo( float *outputInterleaved, size_t numberOfFrames); +void deinterleaveStereo( + const float *inputInterleaved, + float *outputLeft, + float *outputRight, + size_t numberOfFrames); + } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 835d3b237..2555ac601 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -14,13 +14,10 @@ // source: // https://github.com/WebKit/WebKit/blob/main/Source/WebCore/platform/audio/AudioBus.cpp -const float SQRT_HALF = sqrtf(0.5f); - namespace audioapi { -/** - * Public interfaces - memory management - */ +const float SQRT_HALF = sqrtf(0.5f); +constexpr int BLOCK_SIZE = 64; AudioBuffer::AudioBuffer(size_t size, int numberOfChannels, float sampleRate) : numberOfChannels_(numberOfChannels), sampleRate_(sampleRate), size_(size) { @@ -84,10 +81,6 @@ AudioBuffer &AudioBuffer::operator=(audioapi::AudioBuffer &&other) noexcept { return *this; } -/** - * Public interfaces - getters - */ - AudioArray *AudioBuffer::getChannel(int index) const { return channels_[index].get(); } @@ -166,10 +159,6 @@ std::shared_ptr AudioBuffer::getSharedChannel(int index) const return channels_[index]; } -/** - * Public interfaces - audio processing and setters - */ - void AudioBuffer::zero() { zero(0, getSize()); } @@ -246,6 +235,37 @@ void AudioBuffer::copy( sum(source, sourceStart, destinationStart, length); } +void AudioBuffer::deinterleaveFrom(const float *source, size_t frames) { + if (frames == 0) { + return; + } + + if (numberOfChannels_ == 1) { + channels_[0]->copy(source, 0, 0, frames); + return; + } + + if (numberOfChannels_ == 2) { + dsp::deinterleaveStereo(source, channels_[0]->begin(), channels_[1]->begin(), frames); + return; + } + + float *channelsPtrs[MAX_CHANNEL_COUNT]; + for (int i = 0; i < numberOfChannels_; ++i) { + channelsPtrs[i] = channels_[i]->begin(); + } + + for (size_t blockStart = 0; blockStart < frames; blockStart += BLOCK_SIZE) { + size_t blockEnd = std::min(blockStart + BLOCK_SIZE, frames); + for (size_t i = blockStart; i < blockEnd; ++i) { + const float *frameSource = source + (i * numberOfChannels_); + for (int ch = 0; ch < numberOfChannels_; ++ch) { + channelsPtrs[ch][i] = frameSource[ch]; + } + } + } +} + void AudioBuffer::interleaveTo(float *destination, size_t frames) const { if (frames == 0) { return; @@ -266,9 +286,8 @@ void AudioBuffer::interleaveTo(float *destination, size_t frames) const { channelsPtrs[i] = channels_[i]->begin(); } - constexpr size_t kBlockSize = 64; - for (size_t blockStart = 0; blockStart < frames; blockStart += kBlockSize) { - size_t blockEnd = std::min(blockStart + kBlockSize, frames); + for (size_t blockStart = 0; blockStart < frames; blockStart += BLOCK_SIZE) { + size_t blockEnd = std::min(blockStart + BLOCK_SIZE, frames); for (size_t i = blockStart; i < blockEnd; ++i) { float *frameDest = destination + (i * numberOfChannels_); for (int ch = 0; ch < numberOfChannels_; ++ch) { @@ -325,10 +344,6 @@ void AudioBuffer::createChannels() { } } -/** - * Internal tooling - channel summing - */ - void AudioBuffer::discreteSum( const AudioBuffer &source, size_t sourceStart, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h index 6bdfae9bd..5e77f957a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h @@ -112,6 +112,15 @@ class AudioBuffer { /// Assumes that source and this are located in two distinct, non-overlapping memory locations. void copy(const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length); + /// @brief Deinterleave audio data from a source buffer into this AudioBuffer. + /// @param source Pointer to the source buffer containing interleaved audio data. + /// @param frames Number of frames to deinterleave from the source buffer. + /// @note The source buffer should contain interleaved audio data according to the number of channels in this AudioBuffer. + /// Example of interleaved data for stereo (2 channels): + /// [L0, R0, L1, R1, L2, R2, ...] + /// Assumes that source and this are located in two distinct, non-overlapping memory locations. + void deinterleaveFrom(const float *source, size_t frames); + /// @brief Interleave audio data from this AudioBuffer into a destination buffer. /// @param destination Pointer to the destination buffer where interleaved audio data will be written. /// @param frames Number of frames to interleave into the destination buffer. diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm index 2995256bc..8e54daa16 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm @@ -53,12 +53,7 @@ auto outputFrames = buffer.size() / outputChannels; auto audioBuffer = std::make_shared(outputFrames, outputChannels, outputSampleRate); - for (int ch = 0; ch < outputChannels; ++ch) { - auto channelData = audioBuffer->getChannel(ch)->span(); - for (int i = 0; i < outputFrames; ++i) { - channelData[i] = buffer[i * outputChannels + ch]; - } - } + audioBuffer->deinterleaveFrom(buffer.data(), outputFrames); return audioBuffer; } From 49537d004b1172be636ffb41a162a63f90fdacf5 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Feb 2026 08:46:35 +0100 Subject: [PATCH 24/73] refactor: broader support for circular data structures operations and deinterleaving --- .../android/core/AndroidAudioRecorder.cpp | 48 +++---- .../android/core/AndroidAudioRecorder.h | 3 + .../core/utils/AndroidRecorderCallback.cpp | 14 +-- .../core/utils/AndroidRecorderCallback.h | 2 + .../audioapi/core/analysis/AnalyserNode.cpp | 3 +- .../cpp/audioapi/core/inputs/AudioRecorder.h | 3 - .../core/utils/AudioRecorderCallback.h | 2 - .../cpp/audioapi/utils/CircularAudioArray.cpp | 119 ++++++++++-------- .../cpp/audioapi/utils/CircularAudioArray.h | 5 + .../utils/CircularOverflowableAudioArray.cpp | 80 ++++++------ .../utils/CircularOverflowableAudioArray.h | 14 ++- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 17 ++- .../ios/core/utils/IOSRecorderCallback.mm | 12 +- 13 files changed, 175 insertions(+), 147 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index 77eee6ccf..3c589c345 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -26,7 +26,10 @@ namespace audioapi { AndroidAudioRecorder::AndroidAudioRecorder( const std::shared_ptr &audioEventHandlerRegistry) - : AudioRecorder(audioEventHandlerRegistry), streamSampleRate_(0.0), streamChannelCount_(0) {} + : AudioRecorder(audioEventHandlerRegistry), + streamSampleRate_(0.0), + streamChannelCount_(0), + streamMaxBufferSizeInFrames_(0) {} /// @brief Destructor ensures that the audio stream and each output type are closed and flushed up remaining data. /// TODO: Possibly locks here are not necessary, but we might have an issue with oboe having raw pointer to the @@ -88,7 +91,7 @@ Result AndroidAudioRecorder::openAudioStream() { streamSampleRate_ = static_cast(mStream_->getSampleRate()); streamChannelCount_ = mStream_->getChannelCount(); - maxBufferSizeInFrames_ = mStream_->getBufferSizeInFrames(); + streamMaxBufferSizeInFrames_ = mStream_->getBufferSizeInFrames(); return Result::Ok(None); } @@ -119,10 +122,12 @@ Result AndroidAudioRecorder::start(const std::string & } if (usesFileOutput()) { - auto fileResult = - std::static_pointer_cast(fileWriter_) - ->openFile( - streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_, fileNameOverride); + auto fileResult = std::static_pointer_cast(fileWriter_) + ->openFile( + streamSampleRate_, + streamChannelCount_, + streamMaxBufferSizeInFrames_, + fileNameOverride); if (!fileResult.is_ok()) { return Result::Err( @@ -134,12 +139,13 @@ Result AndroidAudioRecorder::start(const std::string & if (usesCallback()) { std::static_pointer_cast(dataCallback_) - ->prepare(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_); + ->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_); } if (isConnected()) { - tempArray_ = std::make_shared(maxBufferSizeInFrames_); - adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); + deinterleavingBuffer_ = std::make_shared( + streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_); + adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_); } auto result = mStream_->requestStart(); @@ -228,7 +234,7 @@ Result AndroidAudioRecorder::enableFileOutput( if (!isIdle()) { auto fileResult = std::static_pointer_cast(fileWriter_) - ->openFile(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_, ""); + ->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_, ""); if (!fileResult.is_ok()) { return Result::Err( @@ -293,7 +299,7 @@ Result AndroidAudioRecorder::setOnAudioReadyCallback( if (!isIdle()) { std::static_pointer_cast(dataCallback_) - ->prepare(streamSampleRate_, streamChannelCount_, maxBufferSizeInFrames_); + ->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_); } callbackOutputEnabled_.store(true, std::memory_order_release); @@ -319,8 +325,9 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n adapterNode_ = node; if (!isIdle()) { - tempArray_ = std::make_shared(maxBufferSizeInFrames_); - adapterNode_->init(maxBufferSizeInFrames_, streamChannelCount_); + deinterleavingBuffer_ = std::make_shared( + streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_); + adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_); } isConnected_.store(true, std::memory_order_release); @@ -332,7 +339,7 @@ void AndroidAudioRecorder::connect(const std::shared_ptr &n void AndroidAudioRecorder::disconnect() { std::scoped_lock adapterLock(adapterNodeMutex_); isConnected_.store(false, std::memory_order_release); - tempArray_ = nullptr; + deinterleavingBuffer_ = nullptr; adapterNode_ = nullptr; } @@ -369,14 +376,11 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady( if (isConnected()) { if (auto adapterLock = Locker::tryLock(adapterNodeMutex_)) { - for (int channel = 0; channel < streamChannelCount_; ++channel) { - auto channelData = tempArray_->span(); - for (int frame = 0; frame < numFrames; ++frame) { - channelData[frame] = - static_cast(audioData)[frame * streamChannelCount_ + channel]; - } - - adapterNode_->buff_[channel]->write(*tempArray_, numFrames); + auto const data = static_cast(audioData); + deinterleavingBuffer_->deinterleaveFrom(data, numFrames); + + for (int ch = 0; ch < streamChannelCount_; ++ch) { + adapterNode_->buff_[ch]->write(*deinterleavingBuffer_->getChannel(ch), numFrames); } } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h index 7a242c479..05602cb10 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h @@ -55,8 +55,11 @@ class AndroidAudioRecorder : public oboe::AudioStreamCallback, public AudioRecor void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override; private: + std::shared_ptr deinterleavingBuffer_; + float streamSampleRate_; int32_t streamChannelCount_; + int32_t streamMaxBufferSizeInFrames_; facebook::jni::global_ref nativeAudioRecorder_; diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp index c27f3a431..bccc4dde7 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp @@ -96,7 +96,8 @@ Result AndroidRecorderCallback::prepare( processingBufferLength_ = std::max(processingBufferLength_, (ma_uint64)maxInputBufferLength_); - tempArray_ = std::make_shared(processingBufferLength_); + deinterleavingBuffer_ = + std::make_shared(processingBufferLength_, channelCount_, sampleRate_); processingBuffer_ = ma_malloc( processingBufferLength_ * channelCount_ * ma_get_bytes_per_sample(ma_format_f32), nullptr); @@ -150,15 +151,10 @@ void AndroidRecorderCallback::receiveAudioData(void *data, int numFrames) { /// @param numFrames Number of frames in the audio data. void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFrames) { auto *inputData = static_cast(data); + deinterleavingBuffer_->deinterleaveFrom(inputData, numFrames); - for (int channel = 0; channel < channelCount_; ++channel) { - auto channelData = tempArray_->span(); - - for (int frame = 0; frame < numFrames; ++frame) { - channelData[frame] = inputData[frame * channelCount_ + channel]; - } - - circularBus_[channel]->push_back(*tempArray_, numFrames); + for (int ch = 0; ch < channelCount_; ++ch) { + circularBus_[ch]->push_back(*deinterleavingBuffer_->getChannel(ch), numFrames); } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h index 614967462..51c9633d0 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h @@ -44,6 +44,8 @@ class AndroidRecorderCallback : public AudioRecorderCallback { ma_uint64 processingBufferLength_{0}; std::unique_ptr converter_{nullptr}; + std::shared_ptr deinterleavingBuffer_; + void deinterleaveAndPushAudioData(void *data, int numFrames); private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 99d07d7e2..014f8ab80 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -126,8 +126,7 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { void AnalyserNode::getFloatTimeDomainData(float *data, int length) { auto size = std::min(fftSize_, length); - inputArray_->pop_back(*tempArray_, size, std::max(0, fftSize_ - size), true); - tempArray_->copyTo(data, 0, 0, size); + inputArray_->pop_back(data, size, std::max(0, fftSize_ - size), true); } void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index 72bca381d..3c117c9a1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -66,9 +66,6 @@ class AudioRecorder { std::atomic fileOutputEnabled_{false}; std::atomic callbackOutputEnabled_{false}; - std::shared_ptr tempArray_; - size_t maxBufferSizeInFrames_ = 0; - std::mutex callbackMutex_; std::mutex fileWriterMutex_; std::mutex errorCallbackMutex_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h index 3b5746791..fb2141cd7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioRecorderCallback.h @@ -43,8 +43,6 @@ class AudioRecorderCallback { uint64_t callbackId_; size_t ringBufferSize_; - std::shared_ptr tempArray_; - std::atomic errorCallbackId_{0}; std::shared_ptr audioEventHandlerRegistry_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp index a0433bfc6..a09faa09d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.cpp @@ -7,43 +7,51 @@ namespace audioapi { CircularAudioArray::CircularAudioArray(size_t size) : AudioArray(size) {} void CircularAudioArray::push_back(const AudioArray & data, size_t size, bool skipAvailableSpaceCheck) { - if (size > size_) { - throw std::overflow_error("size exceeds CircularAudioArray size_"); - } - - if (size > getAvailableSpace() && !skipAvailableSpaceCheck) { - throw std::overflow_error("not enough space in CircularAudioArray"); - } - - if (vWriteIndex_ + size > size_) { - auto partSize = size_ - vWriteIndex_; - copy(data, 0, vWriteIndex_, partSize); - copy(data, partSize, 0, size - partSize); - } else { - copy(data, 0, vWriteIndex_, size); - } - - vWriteIndex_ = vWriteIndex_ + size > size_ ? vWriteIndex_ + size - size_ : vWriteIndex_ + size; + push_back(data.begin(), size, skipAvailableSpaceCheck); +} + +void CircularAudioArray::push_back(const float *data, size_t size, bool skipAvailableSpaceCheck) { + if (size > size_) { + throw std::overflow_error("size exceeds CircularAudioArray size_"); + } + + if (size > getAvailableSpace() && !skipAvailableSpaceCheck) { + throw std::overflow_error("not enough space in CircularAudioArray"); + } + + if (vWriteIndex_ + size > size_) { + auto partSize = size_ - vWriteIndex_; + copy(data, 0, vWriteIndex_, partSize); + copy(data, partSize, 0, size - partSize); + } else { + copy(data, 0, vWriteIndex_, size); + } + + vWriteIndex_ = vWriteIndex_ + size > size_ ? vWriteIndex_ + size - size_ : vWriteIndex_ + size; } void CircularAudioArray::pop_front(AudioArray &data, size_t size, bool skipAvailableDataCheck) { - if (size > size_) { - throw std::overflow_error("size exceeds CircularAudioArray size_"); - } - - if (size > getNumberOfAvailableFrames() && !skipAvailableDataCheck) { - throw std::overflow_error("not enough data in CircularAudioArray"); - } - - if (vReadIndex_ + size > size_) { - auto partSize = size_ - vReadIndex_; - data.copy(*this, vReadIndex_, 0, partSize); - data.copy(*this, 0, partSize, size - partSize); - } else { - data.copy(*this, vReadIndex_, 0, size); - } - - vReadIndex_ = vReadIndex_ + size > size_ ? vReadIndex_ + size - size_ : vReadIndex_ + size; + pop_front(data.begin(), size, skipAvailableDataCheck); +} + +void CircularAudioArray::pop_front(float *data, size_t size, bool skipAvailableDataCheck) { + if (size > size_) { + throw std::overflow_error("size exceeds CircularAudioArray size_"); + } + + if (size > getNumberOfAvailableFrames() && !skipAvailableDataCheck) { + throw std::overflow_error("not enough data in CircularAudioArray"); + } + + if (vReadIndex_ + size > size_) { + auto partSize = size_ - vReadIndex_; + copyTo(data, vReadIndex_, 0, partSize); + copyTo(data, 0, partSize, size - partSize); + } else { + copyTo(data, vReadIndex_, 0, size); + } + + vReadIndex_ = vReadIndex_ + size > size_ ? vReadIndex_ + size - size_ : vReadIndex_ + size; } void CircularAudioArray::pop_back( @@ -51,25 +59,30 @@ void CircularAudioArray::pop_back( size_t size, size_t offset, bool skipAvailableDataCheck) { - if (size > size_) { - throw std::overflow_error("size exceeds CircularAudioArray size_"); - } - - if (size + offset > getNumberOfAvailableFrames() && !skipAvailableDataCheck) { - throw std::overflow_error("not enough data in CircularAudioArray"); - } - - if (vWriteIndex_ <= offset) { - data.copy(*this, size_ - (offset - vWriteIndex_) - size, 0, size); - } else if (vWriteIndex_ <= size + offset) { - auto partSize = size + offset - vWriteIndex_; - data.copy(*this, size_ - partSize, 0, partSize); - data.copy(*this, 0, partSize, size - partSize); - } else { - data.copy(*this, vWriteIndex_ - size - offset, 0, size); - } - - vReadIndex_ = vWriteIndex_ - offset < 0 ? size + vWriteIndex_ - offset : vWriteIndex_ - offset; + pop_back(data.begin(), size, offset, skipAvailableDataCheck); +} + +void CircularAudioArray::pop_back(float *data, size_t size, size_t offset, + bool skipAvailableDataCheck) { + if (size > size_) { + throw std::overflow_error("size exceeds CircularAudioArray size_"); + } + + if (size + offset > getNumberOfAvailableFrames() && !skipAvailableDataCheck) { + throw std::overflow_error("not enough data in CircularAudioArray"); + } + + if (vWriteIndex_ <= offset) { + copyTo(data, size_ - (offset - vWriteIndex_) - size, 0, size); + } else if (vWriteIndex_ <= size + offset) { + auto partSize = size + offset - vWriteIndex_; + copyTo(data, size_ - partSize, 0, partSize); + copyTo(data, 0, partSize, size - partSize); + } else { + copyTo(data, vWriteIndex_ - size - offset, 0, size); + } + + vReadIndex_ = vWriteIndex_ - offset < 0 ? size + vWriteIndex_ - offset : vWriteIndex_ - offset; } size_t CircularAudioArray::getNumberOfAvailableFrames() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h index 999af4fce..757ed2098 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularAudioArray.h @@ -12,9 +12,14 @@ class CircularAudioArray : public AudioArray { ~CircularAudioArray() = default; void push_back(const AudioArray &data, size_t size, bool skipAvailableSpaceCheck = false); + void push_back(const float *data, size_t size, bool skipAvailableSpaceCheck = false); + void pop_front(AudioArray &data, size_t size, bool skipAvailableDataCheck = false); + void pop_front(float *data, size_t size, bool skipAvailableDataCheck = false); + void pop_back(AudioArray &data, size_t size, size_t offset = 0, bool skipAvailableDataCheck = false); + void pop_back(float *data, size_t size, size_t offset = 0, bool skipAvailableDataCheck = false); [[nodiscard]] size_t getNumberOfAvailableFrames() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp index 66b6c852a..eab7f11e5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.cpp @@ -9,45 +9,53 @@ CircularOverflowableAudioArray::CircularOverflowableAudioArray(size_t size) noex : AudioArray(size) {} void CircularOverflowableAudioArray::write(const AudioArray &data, const size_t size) { - size_t writeIndex = vWriteIndex_.load(std::memory_order_relaxed); - - if (size > size_) { - return; // Ignore write if size exceeds buffer size - } - - /// Advances the read index if there is not enough space - readLock_.lock(); - size_t availableSpace = (size_ + vReadIndex_ - writeIndex - 1) % size_; - if (size > availableSpace) { - vReadIndex_ = (writeIndex + size + 1) % size_; - } - readLock_.unlock(); - - size_t partSize = size_ - writeIndex; - if (size > partSize) { - copy(data, 0, writeIndex, partSize); - copy(data, partSize, 0, size - partSize); - } else { - copy(data, 0, writeIndex, size); - } - vWriteIndex_.store((writeIndex + size) % size_, std::memory_order_relaxed); + write(data.begin(), size); +} + +void CircularOverflowableAudioArray::write(const float *data, size_t size) { + size_t writeIndex = vWriteIndex_.load(std::memory_order_relaxed); + + if (size > size_) { + return; // Ignore write if size exceeds buffer size + } + + /// Advances the read index if there is not enough space + readLock_.lock(); + size_t availableSpace = (size_ + vReadIndex_ - writeIndex - 1) % size_; + if (size > availableSpace) { + vReadIndex_ = (writeIndex + size + 1) % size_; + } + readLock_.unlock(); + + size_t partSize = size_ - writeIndex; + if (size > partSize) { + copy(data, 0, writeIndex, partSize); + copy(data, partSize, 0, size - partSize); + } else { + copy(data, 0, writeIndex, size); + } + vWriteIndex_.store((writeIndex + size) % size_, std::memory_order_relaxed); } size_t CircularOverflowableAudioArray::read(AudioArray &data, size_t size) const { - readLock_.lock(); - size_t availableSpace = getAvailableSpace(); - size_t readSize = std::min(size, availableSpace); - - size_t partSize = size_ - vReadIndex_; - if (readSize > partSize) { - data.copy(*this, vReadIndex_, 0, partSize); - data.copy(*this, 0, partSize, readSize - partSize); - } else { - data.copy(*this, vReadIndex_, 0, readSize); - } - vReadIndex_ = (vReadIndex_ + readSize) % size_; - readLock_.unlock(); - return readSize; + return read(data.begin(), size); +} + +size_t CircularOverflowableAudioArray::read(float *data, size_t size) const { + readLock_.lock(); + size_t availableSpace = getAvailableSpace(); + size_t readSize = std::min(size, availableSpace); + + size_t partSize = size_ - vReadIndex_; + if (readSize > partSize) { + copyTo(data, vReadIndex_, 0, partSize); + copyTo(data, 0, partSize, readSize - partSize); + } else { + copyTo(data, vReadIndex_, 0, readSize); + } + vReadIndex_ = (vReadIndex_ + readSize) % size_; + readLock_.unlock(); + return readSize; } size_t CircularOverflowableAudioArray::getAvailableSpace() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h index 588c72d4e..449100423 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CircularOverflowableAudioArray.h @@ -25,12 +25,24 @@ class CircularOverflowableAudioArray : public AudioArray { /// @param size Number of frames to write. void write(const AudioArray &data, size_t size); + /// @brief Writes data to the circular buffer. + /// @note Might wait for read operation to finish if it is in progress. It ignores writes that exceed the buffer size. + /// @param data Pointer to the input buffer. + /// @param size Number of frames to write. + void write(const float *data, size_t size); + /// @brief Reads data from the circular buffer. - /// @param output Reference to output AudioArray. + /// @param data Reference to output AudioArray. /// @param size Number of frames to read. /// @return The number of frames actually read. size_t read(AudioArray &data, size_t size) const; + /// @brief Reads data from the circular buffer. + /// @param data Pointer to the output buffer. + /// @param size Number of frames to read. + /// @return The number of frames actually read. + size_t read(float *data, size_t size) const; + private: std::atomic vWriteIndex_ = {0}; mutable size_t vReadIndex_ = 0; // it is only used after acquiring readLock_ diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index 2436e4cfa..74c339c9a 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -53,8 +53,7 @@ for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { float *data = (float *)inputBuffer->mBuffers[channel].mData; - tempArray_->copy(data, 0, 0, numFrames); - adapterNode_->buff_[channel]->write(*tempArray_, numFrames); + adapterNode_->buff_[channel]->write(data, numFrames); } } } @@ -100,12 +99,12 @@ [AudioEngine.sharedInstance stopIfNecessary]; // Estimate the maximum input buffer lengths that can be expected from the sink node - maxBufferSizeInFrames_ = [nativeRecorder_ getBufferSize]; + size_t maxInputBufferLength = [nativeRecorder_ getBufferSize]; auto inputFormat = [nativeRecorder_ getInputFormat]; if (usesFileOutput()) { auto fileResult = std::static_pointer_cast(fileWriter_) - ->openFile(inputFormat, maxBufferSizeInFrames_, fileNameOverride); + ->openFile(inputFormat, maxInputBufferLength, fileNameOverride); if (fileResult.is_err()) { return Result::Err( @@ -117,7 +116,7 @@ if (usesCallback()) { auto callbackResult = std::static_pointer_cast(dataCallback_) - ->prepare(inputFormat, maxBufferSizeInFrames_); + ->prepare(inputFormat, maxInputBufferLength); if (callbackResult.is_err()) { return Result::Err( @@ -126,9 +125,8 @@ } if (isConnected()) { - tempArray_ = std::make_shared(maxBufferSizeInFrames_); // TODO: pass sample rate, in case conversion is necessary - adapterNode_->init(maxBufferSizeInFrames_, inputFormat.channelCount); + adapterNode_->init(maxInputBufferLength, inputFormat.channelCount); } [nativeRecorder_ start]; @@ -228,8 +226,8 @@ adapterNode_ = node; if (!isIdle()) { - tempArray_ = std::make_shared(maxBufferSizeInFrames_); - adapterNode_->init(maxBufferSizeInFrames_, [nativeRecorder_ getInputFormat].channelCount); + adapterNode_->init( + [nativeRecorder_ getBufferSize], [nativeRecorder_ getInputFormat].channelCount); } isConnected_.store(true, std::memory_order_release); @@ -241,7 +239,6 @@ void IOSAudioRecorder::disconnect() { std::scoped_lock lock(adapterNodeMutex_); - tempArray_ = nullptr; adapterNode_ = nullptr; isConnected_.store(false, std::memory_order_release); } diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index 2fa64d692..d922fb74c 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -72,8 +72,6 @@ converterOutputBufferSize_ = std::max( (double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength); - tempArray_ = std::make_shared(converterOutputBufferSize_); - callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:sampleRate_ channels:channelCount_ @@ -151,10 +149,8 @@ !bufferFormat_.isInterleaved) { // Directly write to circular buffer for (int i = 0; i < channelCount_; ++i) { - auto *inputChannel = static_cast(inputBuffer->mBuffers[i].mData); - - tempArray_->copy(inputChannel, 0, 0, numFrames); - circularBus_[i]->push_back(*tempArray_, numFrames); + auto *data = static_cast(inputBuffer->mBuffers[i].mData); + circularBus_[i]->push_back(data, numFrames); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { @@ -200,9 +196,7 @@ for (int i = 0; i < channelCount_; ++i) { auto *data = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); - - tempArray_->copy(data, 0, 0, outputFrameCount); - circularBus_[i]->push_back(*tempArray_, outputFrameCount); + circularBus_[i]->push_back(data, outputFrameCount); } if (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) { From 7a13612e143fa28e98ea67ecd377ead1ae3765c0 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Feb 2026 17:42:52 +0100 Subject: [PATCH 25/73] chore: requuested changes from code review --- .../android/core/AndroidAudioRecorder.cpp | 2 +- .../core/utils/AndroidRecorderCallback.cpp | 2 +- .../android/core/utils/AudioDecoder.cpp | 2 +- .../common/cpp/audioapi/core/AudioNode.h | 2 +- .../audioapi/core/effects/ConvolverNode.cpp | 6 +- .../cpp/audioapi/core/effects/GainNode.cpp | 2 +- .../audioapi/core/effects/WaveShaperNode.cpp | 4 +- .../cpp/audioapi/core/effects/WorkletNode.cpp | 2 +- .../core/effects/WorkletProcessingNode.cpp | 4 +- .../cpp/audioapi/core/inputs/AudioRecorder.h | 1 - .../sources/AudioBufferBaseSourceNode.cpp | 10 +-- .../core/sources/AudioBufferBaseSourceNode.h | 2 +- .../sources/AudioBufferQueueSourceNode.cpp | 2 +- .../core/sources/AudioBufferSourceNode.cpp | 42 +++++----- .../core/sources/AudioBufferSourceNode.h | 2 +- .../core/sources/ConstantSourceNode.cpp | 2 +- .../audioapi/core/sources/OscillatorNode.cpp | 2 +- .../core/sources/RecorderAdapterNode.cpp | 10 +-- .../core/sources/RecorderAdapterNode.h | 2 +- .../audioapi/core/sources/StreamerNode.cpp | 22 +++--- .../cpp/audioapi/core/sources/StreamerNode.h | 6 +- .../core/sources/WorkletSourceNode.cpp | 2 +- .../audioapi/core/utils/AudioStretcher.cpp | 4 +- .../common/cpp/audioapi/dsp/AudioUtils.hpp | 3 +- .../common/cpp/audioapi/dsp/Resampler.cpp | 2 +- .../audioapi/libs/ffmpeg/FFmpegDecoding.cpp | 2 +- .../common/cpp/audioapi/utils/AudioArray.cpp | 31 +++----- .../common/cpp/audioapi/utils/AudioArray.h | 22 +++--- .../common/cpp/audioapi/utils/AudioBuffer.cpp | 78 +++++++++---------- .../common/cpp/audioapi/utils/AudioBuffer.h | 19 ++--- .../ios/audioapi/ios/core/IOSAudioPlayer.mm | 2 +- .../audioapi/ios/core/utils/AudioDecoder.mm | 2 +- .../ios/core/utils/IOSRecorderCallback.mm | 8 +- 33 files changed, 144 insertions(+), 160 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp index 3c589c345..6be58a7f5 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp @@ -379,7 +379,7 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady( auto const data = static_cast(audioData); deinterleavingBuffer_->deinterleaveFrom(data, numFrames); - for (int ch = 0; ch < streamChannelCount_; ++ch) { + for (size_t ch = 0; ch < streamChannelCount_; ++ch) { adapterNode_->buff_[ch]->write(*deinterleavingBuffer_->getChannel(ch), numFrames); } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp index bccc4dde7..5f102f381 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp @@ -153,7 +153,7 @@ void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFr auto *inputData = static_cast(data); deinterleavingBuffer_->deinterleaveFrom(inputData, numFrames); - for (int ch = 0; ch < channelCount_; ++ch) { + for (size_t ch = 0; ch < channelCount_; ++ch) { circularBus_[ch]->push_back(*deinterleavingBuffer_->getChannel(ch), numFrames); } } diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp index 4fa157d2a..d7b3dd04a 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp @@ -160,7 +160,7 @@ std::shared_ptr AudioDecoder::decodeWithPCMInBase64( auto audioBuffer = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); - for (int ch = 0; ch < inputChannelCount; ++ch) { + for (size_t ch = 0; ch < inputChannelCount; ++ch) { auto channelData = audioBuffer->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 37578ff8c..df6aa6134 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -57,7 +57,7 @@ class AudioNode : public std::enable_shared_from_this { int numberOfInputs_ = 1; int numberOfOutputs_ = 1; - int channelCount_ = 2; + size_t channelCount_ = 2; ChannelCountMode channelCountMode_ = ChannelCountMode::MAX; ChannelInterpretation channelInterpretation_ = ChannelInterpretation::SPEAKERS; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 1deff6a10..0522619b8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -58,7 +58,7 @@ void ConvolverNode::setBuffer(const std::shared_ptr &buffer) { calculateNormalizationScale(); threadPool_ = std::make_shared(4); convolvers_.clear(); - for (int i = 0; i < buffer->getNumberOfChannels(); ++i) { + for (size_t i = 0; i < buffer->getNumberOfChannels(); ++i) { convolvers_.emplace_back(); AudioArray channelData(*buffer->getChannel(i)); convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getSize()); @@ -121,7 +121,7 @@ std::shared_ptr ConvolverNode::processNode( audioBuffer_->copy(*internalBuffer_, 0, 0, framesToProcess); int remainingFrames = internalBufferIndex_ - framesToProcess; if (remainingFrames > 0) { - for (int ch = 0; ch < internalBuffer_->getNumberOfChannels(); ++ch) { + for (size_t ch = 0; ch < internalBuffer_->getNumberOfChannels(); ++ch) { internalBuffer_->getChannel(ch)->copyWithin(framesToProcess, 0, remainingFrames); } } @@ -141,7 +141,7 @@ void ConvolverNode::calculateNormalizationScale() { float power = 0; - for (int channel = 0; channel < numberOfChannels; ++channel) { + for (size_t channel = 0; channel < numberOfChannels; ++channel) { float channelPower = 0; auto channelData = buffer_->getChannel(channel)->span(); for (int i = 0; i < length; ++i) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp index 9334b768a..86fd99dec 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp @@ -33,7 +33,7 @@ std::shared_ptr GainNode::processNode( auto gainParamValues = gainParam_->processARateParam(framesToProcess, time); auto gainValues = gainParamValues->getChannel(0); - for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i++) { auto channel = processingBuffer->getChannel(i); channel->multiply(*gainValues, framesToProcess); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index c010d70c2..49e9ef44d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -17,7 +17,7 @@ WaveShaperNode::WaveShaperNode( : AudioNode(context, options), oversample_(options.oversample) { waveShapers_.reserve(6); - for (int i = 0; i < channelCount_; i++) { + for (size_t i = 0; i < channelCount_; i++) { waveShapers_.emplace_back(std::make_unique(nullptr)); } setCurve(options.curve); @@ -70,7 +70,7 @@ std::shared_ptr WaveShaperNode::processNode( return processingBuffer; } - for (int channel = 0; channel < processingBuffer->getNumberOfChannels(); channel++) { + for (size_t channel = 0; channel < processingBuffer->getNumberOfChannels(); channel++) { auto channelData = processingBuffer->getChannel(channel); waveShapers_[channel]->process(*channelData, framesToProcess); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index 4cbdb79e8..cac5c720a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -49,7 +49,7 @@ std::shared_ptr WorkletNode::processNode( workletRunner_.executeOnRuntimeSync([this, channelCount_](jsi::Runtime &uiRuntimeRaw) { /// Arguments preparation auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_); - for (int ch = 0; ch < channelCount_; ch++) { + for (size_t ch = 0; ch < channelCount_; ch++) { auto sharedAudioArray = std::make_shared(bufferLength_); sharedAudioArray->copy(*buffer_->getChannel(ch)); auto sharedAudioArraySize = sharedAudioArray->size(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index 7aa0ca58e..f1bb4c642 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -29,7 +29,7 @@ std::shared_ptr WorkletProcessingNode::processNode( static_cast(processingBuffer->getNumberOfChannels())); // Copy input data to pre-allocated input buffers - for (int ch = 0; ch < channelCount; ch++) { + for (size_t ch = 0; ch < channelCount; ch++) { inputBuffsHandles_[ch]->copy(*processingBuffer->getChannel(ch), 0, 0, framesToProcess); } @@ -64,7 +64,7 @@ std::shared_ptr WorkletProcessingNode::processNode( }); // Copy processed output data back to the processing buffer or zero on failure - for (int ch = 0; ch < channelCount; ch++) { + for (size_t ch = 0; ch < channelCount; ch++) { auto channelData = processingBuffer->getChannel(ch); if (result.has_value()) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h index 3c117c9a1..c2b196765 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/inputs/AudioRecorder.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index 059c5a74b..ae421e033 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -24,7 +24,7 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( playbackRateParam_ = std::make_shared( options.playbackRate, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); - playbackRateBus_ = std::make_shared( + playbackRateBuffer_ = std::make_shared( RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); stretch_ = std::make_shared>(); @@ -115,12 +115,12 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( auto detune = std::clamp(detuneParam_->processKRateParam(framesToProcess, time) / 100.0f, -12.0f, 12.0f); - playbackRateBus_->zero(); + playbackRateBuffer_->zero(); auto framesNeededToStretch = static_cast(playbackRate * static_cast(framesToProcess)); updatePlaybackInfo( - playbackRateBus_, + playbackRateBuffer_, framesNeededToStretch, startOffset, offsetLength, @@ -132,10 +132,10 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( return; } - processWithoutInterpolation(playbackRateBus_, startOffset, offsetLength, playbackRate); + processWithoutInterpolation(playbackRateBuffer_, startOffset, offsetLength, playbackRate); stretch_->process( - playbackRateBus_.get()[0], framesNeededToStretch, processingBuffer.get()[0], framesToProcess); + playbackRateBuffer_.get()[0], framesNeededToStretch, processingBuffer.get()[0], framesToProcess); if (detune != 0.0f) { stretch_->setTransposeSemitones(detune); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 4969cba4c..139594fdb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -36,7 +36,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { // pitch correction std::shared_ptr> stretch_; - std::shared_ptr playbackRateBus_; + std::shared_ptr playbackRateBuffer_; // k-rate params std::shared_ptr detuneParam_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index eac330254..c1d2844a6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -279,7 +279,7 @@ void AudioBufferQueueSourceNode::processWithInterpolation( } } - for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { const auto destination = processingBuffer->getChannel(i)->span(); const auto currentSource = buffer->getChannel(i)->span(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index ddb17bc1c..f782fcd82 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -22,7 +22,7 @@ AudioBufferSourceNode::AudioBufferSourceNode( loopStart_(options.loopStart), loopEnd_(options.loopEnd) { buffer_ = std::shared_ptr(options.buffer); - alignedBus_ = std::shared_ptr(nullptr); + alignedBuffer_ = std::shared_ptr(nullptr); isInitialized_ = true; } @@ -31,7 +31,7 @@ AudioBufferSourceNode::~AudioBufferSourceNode() { Locker locker(getBufferLock()); buffer_.reset(); - alignedBus_.reset(); + alignedBuffer_.reset(); } bool AudioBufferSourceNode::getLoop() const { @@ -81,7 +81,7 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer if (buffer == nullptr || context == nullptr) { buffer_ = std::shared_ptr(nullptr); - alignedBus_ = std::shared_ptr(nullptr); + alignedBuffer_ = std::shared_ptr(nullptr); loopEnd_ = 0; return; } @@ -89,23 +89,23 @@ void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer buffer_ = buffer; channelCount_ = buffer_->getNumberOfChannels(); - stretch_->presetDefault(channelCount_, buffer_->getSampleRate()); + stretch_->presetDefault(static_cast(channelCount_), buffer_->getSampleRate()); if (pitchCorrection_) { int extraTailFrames = static_cast((getInputLatency() + getOutputLatency()) * context->getSampleRate()); size_t totalSize = buffer_->getSize() + extraTailFrames; - alignedBus_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); - alignedBus_->copy(*buffer_, 0, 0, buffer_->getSize()); + alignedBuffer_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); + alignedBuffer_->copy(*buffer_, 0, 0, buffer_->getSize()); - alignedBus_->zero(buffer_->getSize(), extraTailFrames); + alignedBuffer_->zero(buffer_->getSize(), extraTailFrames); } else { - alignedBus_ = std::make_shared(*buffer_); + alignedBuffer_ = std::make_shared(*buffer_); } audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); - playbackRateBus_ = std::make_shared( + playbackRateBuffer_ = std::make_shared( RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); loopEnd_ = buffer_->getDuration(); @@ -118,23 +118,23 @@ void AudioBufferSourceNode::start(double when, double offset, double duration) { AudioScheduledSourceNode::stop(when + duration); } - if (!alignedBus_) { + if (!alignedBuffer_) { return; } offset = - std::min(offset, static_cast(alignedBus_->getSize()) / alignedBus_->getSampleRate()); + std::min(offset, static_cast(alignedBuffer_->getSize()) / alignedBuffer_->getSampleRate()); if (loop_) { offset = std::min(offset, loopEnd_); } - vReadIndex_ = static_cast(alignedBus_->getSampleRate() * offset); + vReadIndex_ = static_cast(alignedBuffer_->getSampleRate() * offset); } void AudioBufferSourceNode::disable() { AudioScheduledSourceNode::disable(); - alignedBus_.reset(); + alignedBuffer_.reset(); } void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { @@ -150,7 +150,7 @@ std::shared_ptr AudioBufferSourceNode::processNode( int framesToProcess) { if (auto locker = Locker::tryLock(getBufferLock())) { // No audio data to fill, zero the output and return. - if (!alignedBus_) { + if (!alignedBuffer_) { processingBuffer->zero(); return processingBuffer; } @@ -223,16 +223,16 @@ void AudioBufferSourceNode::processWithoutInterpolation( assert(readIndex >= 0); assert(writeIndex >= 0); - assert(readIndex + framesToCopy <= alignedBus_->getSize()); + assert(readIndex + framesToCopy <= alignedBuffer_->getSize()); assert(writeIndex + framesToCopy <= processingBuffer->getSize()); // Direction is forward, we can normally copy the data if (direction == 1) { - processingBuffer->copy(*alignedBus_, readIndex, writeIndex, framesToCopy); + processingBuffer->copy(*alignedBuffer_, readIndex, writeIndex, framesToCopy); } else { - for (int ch = 0; ch < processingBuffer->getNumberOfChannels(); ch += 1) { + for (size_t ch = 0; ch < processingBuffer->getNumberOfChannels(); ch += 1) { processingBuffer->getChannel(ch)->copyReverse( - *alignedBus_->getChannel(ch), readIndex, writeIndex, framesToCopy); + *alignedBuffer_->getChannel(ch), readIndex, writeIndex, framesToCopy); } } @@ -298,9 +298,9 @@ void AudioBufferSourceNode::processWithInterpolation( nextReadIndex = loop_ ? frameStart : readIndex; } - for (int i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i++) { auto destination = processingBuffer->getChannel(i)->span(); - const auto source = alignedBus_->getChannel(i)->span(); + const auto source = alignedBuffer_->getChannel(i)->span(); destination[writeIndex] = dsp::linearInterpolate(source, readIndex, nextReadIndex, factor); } @@ -329,7 +329,7 @@ double AudioBufferSourceNode::getVirtualStartFrame(float sampleRate) const { } double AudioBufferSourceNode::getVirtualEndFrame(float sampleRate) { - auto inputBufferLength = static_cast(alignedBus_->getSize()); + auto inputBufferLength = static_cast(alignedBuffer_->getSize()); auto loopEndFrame = loopEnd_ * sampleRate; return loop_ && loopEndFrame > 0 && loopStart_ < loopEnd_ diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index 0667ad9d2..5a28a2c95 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -55,7 +55,7 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { // User provided buffer std::shared_ptr buffer_; - std::shared_ptr alignedBus_; + std::shared_ptr alignedBuffer_; std::atomic onLoopEndedCallbackId_ = 0; // 0 means no callback void sendOnLoopEndedEvent(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp index ca728f201..bbe14374a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp @@ -48,7 +48,7 @@ std::shared_ptr ConstantSourceNode::processNode( auto offsetChannel = offsetParam_->processARateParam(framesToProcess, context->getCurrentTime())->getChannel(0); - for (int channel = 0; channel < processingBuffer->getNumberOfChannels(); ++channel) { + for (size_t channel = 0; channel < processingBuffer->getNumberOfChannels(); ++channel) { processingBuffer->getChannel(channel)->copy( *offsetChannel, startOffset, startOffset, offsetLength); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index cfab566fc..2c5516c86 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -92,7 +92,7 @@ std::shared_ptr OscillatorNode::processNode( auto finalPhase = phase_; - for (int ch = 0; ch < numChannels; ch += 1) { + for (size_t ch = 0; ch < numChannels; ch += 1) { auto channelSpan = processingBuffer->getChannel(ch)->span(); float currentPhase = phase_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index 73bac391d..72cecd775 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -41,7 +41,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // we would need to add sample rate conversion as well or other weird bullshit like resampling // context output and not enforcing anything on the system output/input configuration. // A lot of words for a couple of lines of implementation :shrug: - adapterOutputBus_ = + adapterOutputBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); isInitialized_ = true; } @@ -49,7 +49,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { void RecorderAdapterNode::cleanup() { isInitialized_ = false; buff_.clear(); - adapterOutputBus_.reset(); + adapterOutputBuffer_.reset(); } std::shared_ptr RecorderAdapterNode::processNode( @@ -62,15 +62,15 @@ std::shared_ptr RecorderAdapterNode::processNode( readFrames(framesToProcess); - processingBuffer->sum(*adapterOutputBus_, ChannelInterpretation::SPEAKERS); + processingBuffer->sum(*adapterOutputBuffer_, ChannelInterpretation::SPEAKERS); return processingBuffer; } void RecorderAdapterNode::readFrames(const size_t framesToRead) { - adapterOutputBus_->zero(); + adapterOutputBuffer_->zero(); for (size_t channel = 0; channel < channelCount_; ++channel) { - buff_[channel]->read(*adapterOutputBus_->getChannel(channel), framesToRead); + buff_[channel]->read(*adapterOutputBuffer_->getChannel(channel), framesToRead); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h index 6d98e90a2..0e17b264d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h @@ -36,7 +36,7 @@ class RecorderAdapterNode : public AudioNode { std::shared_ptr processNode( const std::shared_ptr &processingBuffer, int framesToProcess) override; - std::shared_ptr adapterOutputBus_; + std::shared_ptr adapterOutputBuffer_; private: /// @brief Read audio frames from the recorder's internal circular buffer into output buffers. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp index b48ee9e38..078c01120 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp @@ -34,8 +34,8 @@ StreamerNode::StreamerNode( frame_(nullptr), swrCtx_(nullptr), resampledData_(nullptr), - bufferedBus_(nullptr), - bufferedBusSize_(0), + bufferedAudioBuffer_(nullptr), + bufferedAudioBufferSize_(0), audio_stream_index_(-1), maxResampledSamples_(0), processedSamples_(0) {} @@ -131,26 +131,26 @@ std::shared_ptr StreamerNode::processNode( return processingBuffer; } - int bufferRemaining = bufferedBusSize_ - processedSamples_; + int bufferRemaining = bufferedAudioBufferSize_ - processedSamples_; int alreadyProcessed = 0; if (bufferRemaining < framesToProcess) { - if (bufferedBus_ != nullptr) { - processingBuffer->copy(*bufferedBus_, processedSamples_, 0, bufferRemaining); + if (bufferedAudioBuffer_ != nullptr) { + processingBuffer->copy(*bufferedAudioBuffer_, processedSamples_, 0, bufferRemaining); framesToProcess -= bufferRemaining; alreadyProcessed += bufferRemaining; } StreamingData data; auto res = receiver_.try_receive(data); if (res == channels::spsc::ResponseStatus::SUCCESS) { - bufferedBus_ = std::make_shared(std::move(data.buffer)); - bufferedBusSize_ = data.size; + bufferedAudioBuffer_ = std::make_shared(std::move(data.buffer)); + bufferedAudioBufferSize_ = data.size; processedSamples_ = 0; } else { - bufferedBus_ = nullptr; + bufferedAudioBuffer_ = nullptr; } } - if (bufferedBus_ != nullptr) { - processingBuffer->copy(*bufferedBus_, processedSamples_, alreadyProcessed, framesToProcess); + if (bufferedAudioBuffer_ != nullptr) { + processingBuffer->copy(*bufferedAudioBuffer_, processedSamples_, alreadyProcessed, framesToProcess); processedSamples_ += framesToProcess; } #endif // RN_AUDIO_API_FFMPEG_DISABLED @@ -261,7 +261,7 @@ bool StreamerNode::processFrameWithResampler( AudioBuffer buffer = AudioBuffer(converted_samples, codecCtx_->ch_layout.nb_channels, context->getSampleRate()); - for (int ch = 0; ch < codecCtx_->ch_layout.nb_channels; ch++) { + for (size_t ch = 0; ch < codecCtx_->ch_layout.nb_channels; ch++) { auto *src = reinterpret_cast(resampledData_[ch]); buffer.getChannel(ch)->copy(src, 0, 0, converted_samples); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h index 70b48a268..ed3983f40 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h @@ -97,9 +97,9 @@ class StreamerNode : public AudioScheduledSourceNode { SwrContext *swrCtx_; uint8_t **resampledData_; // weird ffmpeg way of using raw byte pointers for resampled data - std::shared_ptr bufferedBus_; // audio buffer for buffering hls frames - size_t bufferedBusSize_; // size of currently buffered buffer - int audio_stream_index_; // index of the audio stream channel in the input + std::shared_ptr bufferedAudioBuffer_; // audio buffer for buffering hls frames + size_t bufferedAudioBufferSize_; // size of currently buffered buffer + int audio_stream_index_; // index of the audio stream channel in the input int maxResampledSamples_; size_t processedSamples_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp index 8637ee776..09d9450a8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp @@ -77,7 +77,7 @@ std::shared_ptr WorkletSourceNode::processNode( } // Copy the processed data back to the AudioBuffer - for (int i = 0; i < outputChannelCount; ++i) { + for (size_t i = 0; i < outputChannelCount; ++i) { processingBuffer->getChannel(i)->copy( *outputBuffsHandles_[i], 0, startOffset, nonSilentFramesToProcess); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp index 647787c28..9e4ff8cdd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioStretcher.cpp @@ -15,7 +15,7 @@ std::vector AudioStretcher::castToInt16Buffer(AudioBuffer &buffer) { std::vector int16Buffer(numFrames * numChannels); - for (int ch = 0; ch < numChannels; ++ch) { + for (size_t ch = 0; ch < numChannels; ++ch) { auto channelData = buffer.getChannel(ch)->span(); for (size_t i = 0; i < numFrames; ++i) { int16Buffer[i * numChannels + ch] = floatToInt16(channelData[i]); @@ -61,7 +61,7 @@ std::shared_ptr AudioStretcher::changePlaybackSpeed( auto audioBuffer = std::make_shared(outputFrames, outputChannels, sampleRate); - for (int ch = 0; ch < outputChannels; ++ch) { + for (size_t ch = 0; ch < outputChannels; ++ch) { auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = int16ToFloat(stretchedBuffer[i * outputChannels + ch]); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp index 41bc36e42..3cdcaa348 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/AudioUtils.hpp @@ -29,10 +29,11 @@ namespace audioapi::dsp { } [[nodiscard]] inline float linearToDecibels(float value) { - return 20 * log10f(value); + return 20.0f * log10f(value); } [[nodiscard]] inline float decibelsToLinear(float value) { return static_cast(pow(10, value / 20)); } + } // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp index 278f51474..8dc85dcb8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Resampler.cpp @@ -145,7 +145,7 @@ int DownSampler::process( auto outputCount = framesToProcess / 2; - for (int i = 0; i < outputCount; ++i) { + for (size_t i = 0; i < outputCount; ++i) { // convolution for downsampled samples output[i] = stateBuffer_->computeConvolution(*kernel_, 2 * i + 1); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp index 7cca55feb..b46529308 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp @@ -249,7 +249,7 @@ std::shared_ptr decodeAudioFrames( auto audioBuffer = std::make_shared(outputFrames, output_channel_count, output_sample_rate); - for (int ch = 0; ch < output_channel_count; ++ch) { + for (size_t ch = 0; ch < output_channel_count; ++ch) { auto channelData = audioBuffer->getChannel(ch)->span(); for (int i = 0; i < outputFrames; ++i) { channelData[i] = decoded_buffer[i * output_channel_count + ch]; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp index 8923acab8..6ba9b9f04 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.cpp @@ -24,7 +24,7 @@ AudioArray::AudioArray(const float *data, size_t size) : size_(size) { AudioArray::AudioArray(const AudioArray &other) : size_(other.size_) { if (size_ > 0 && other.data_) { data_ = std::make_unique(size_); - std::memcpy(data_.get(), other.data_.get(), size_ * sizeof(float)); + copy(other); } } @@ -58,19 +58,6 @@ AudioArray &AudioArray::operator=(audioapi::AudioArray &&other) noexcept { return *this; } -void AudioArray::resize(size_t size) { - if (size == size_ && data_ != nullptr) { - zero(); - return; - } - - size_ = size; - data_ = (size_ > 0) ? std::make_unique(size_) : nullptr; - if (data_ != nullptr) { - zero(); - } -} - void AudioArray::zero() noexcept { zero(0, size_); } @@ -89,7 +76,7 @@ void AudioArray::sum( size_t destinationStart, size_t length, float gain) { - if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + if (size_ - destinationStart < length || source.size_ - sourceStart < length) [[unlikely]] { throw std::out_of_range("Not enough data to sum two vectors."); } @@ -105,7 +92,7 @@ void AudioArray::multiply(const AudioArray &source) { } void AudioArray::multiply(const audioapi::AudioArray &source, size_t length) { - if (size_ < length || source.size_ < length) { + if (size_ < length || source.size_ < length) [[unlikely]] { throw std::out_of_range("Not enough data to perform vector multiplication."); } @@ -124,7 +111,7 @@ void AudioArray::copy( size_t sourceStart, size_t destinationStart, size_t length) { - if (source.size_ - sourceStart < length) { + if (source.size_ - sourceStart < length) [[unlikely]] { throw std::out_of_range("Not enough data to copy from source."); } @@ -136,7 +123,7 @@ void AudioArray::copy( size_t sourceStart, size_t destinationStart, size_t length) { - if (size_ - destinationStart < length) { + if (size_ - destinationStart < length) [[unlikely]] { throw std::out_of_range("Not enough space to copy to destination."); } @@ -148,7 +135,7 @@ void AudioArray::copyReverse( size_t sourceStart, size_t destinationStart, size_t length) { - if (size_ - destinationStart < length || source.size_ - sourceStart < length) { + if (size_ - destinationStart < length || source.size_ - sourceStart < length) [[unlikely]] { throw std::out_of_range("Not enough space to copy to destination or from source."); } @@ -166,7 +153,7 @@ void AudioArray::copyTo( size_t sourceStart, size_t destinationStart, size_t length) const { - if (size_ - sourceStart < length) { + if (size_ - sourceStart < length) [[unlikely]] { throw std::out_of_range("Not enough data to copy from source."); } @@ -174,7 +161,7 @@ void AudioArray::copyTo( } void AudioArray::copyWithin(size_t sourceStart, size_t destinationStart, size_t length) { - if (size_ - sourceStart < length || size_ - destinationStart < length) { + if (size_ - sourceStart < length || size_ - destinationStart < length) [[unlikely]] { throw std::out_of_range("Not enough space for moving data or data to move."); } @@ -208,7 +195,7 @@ float AudioArray::getMaxAbsValue() const { } float AudioArray::computeConvolution(const audioapi::AudioArray &kernel, size_t startIndex) const { - if (kernel.size_ > size_ - startIndex) { + if (kernel.size_ > size_ - startIndex) [[unlikely]] { throw std::out_of_range("Kernal size exceeds available data for convolution."); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h index e2fbbed67..68b5f2881 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioArray.h @@ -26,40 +26,40 @@ class AudioArray { AudioArray &operator=(const AudioArray &other); AudioArray &operator=(AudioArray &&other) noexcept; - [[nodiscard]] inline size_t getSize() const noexcept { + [[nodiscard]] size_t getSize() const noexcept { return size_; } - inline float &operator[](size_t index) noexcept { + float &operator[](size_t index) noexcept { return data_[index]; } - inline const float &operator[](size_t index) const noexcept { + const float &operator[](size_t index) const noexcept { return data_[index]; } - [[nodiscard]] inline float *begin() noexcept { + [[nodiscard]] float *begin() noexcept { return data_.get(); } - [[nodiscard]] inline float *end() noexcept { + [[nodiscard]] float *end() noexcept { return data_.get() + size_; } - [[nodiscard]] inline const float *begin() const noexcept { + [[nodiscard]] const float *begin() const noexcept { return data_.get(); } - [[nodiscard]] inline const float *end() const noexcept { + [[nodiscard]] const float *end() const noexcept { return data_.get() + size_; } - [[nodiscard]] inline std::span span() noexcept { + [[nodiscard]] std::span span() noexcept { return {data_.get(), size_}; } - [[nodiscard]] inline std::span span() const noexcept { + [[nodiscard]] std::span span() const noexcept { return {data_.get(), size_}; } - [[nodiscard]] inline std::span subSpan(size_t length, size_t offset = 0) { + [[nodiscard]] std::span subSpan(size_t length, size_t offset = 0) { if (offset + length > size_) { throw std::out_of_range("AudioArray::subSpan - offset + length exceeds array size"); } @@ -67,8 +67,6 @@ class AudioArray { return {data_.get() + offset, length}; } - void resize(size_t size); - void zero() noexcept; void zero(size_t start, size_t length) noexcept; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 2555ac601..0b22592ae 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -21,7 +21,10 @@ constexpr int BLOCK_SIZE = 64; AudioBuffer::AudioBuffer(size_t size, int numberOfChannels, float sampleRate) : numberOfChannels_(numberOfChannels), sampleRate_(sampleRate), size_(size) { - createChannels(); + channels_.reserve(numberOfChannels_); + for (size_t i = 0; i < numberOfChannels_; i += 1) { + channels_.emplace_back(std::make_shared(size_)); + } } AudioBuffer::AudioBuffer(const audioapi::AudioBufferOptions &options) @@ -31,10 +34,9 @@ AudioBuffer::AudioBuffer(const AudioBuffer &other) : numberOfChannels_(other.numberOfChannels_), sampleRate_(other.sampleRate_), size_(other.size_) { - createChannels(); - - for (int i = 0; i < numberOfChannels_; i += 1) { - *channels_[i] = *other.channels_[i]; + channels_.reserve(numberOfChannels_); + for (const auto& channel : other.channels_) { + channels_.emplace_back(std::make_shared(*channel)); } } @@ -50,15 +52,26 @@ AudioBuffer::AudioBuffer(audioapi::AudioBuffer &&other) noexcept AudioBuffer &AudioBuffer::operator=(const AudioBuffer &other) { if (this != &other) { - if (numberOfChannels_ != other.numberOfChannels_ || size_ != other.size_) { + sampleRate_ = other.sampleRate_; + + if (numberOfChannels_ != other.numberOfChannels_) { numberOfChannels_ = other.numberOfChannels_; size_ = other.size_; - createChannels(); + channels_.clear(); + channels_.reserve(numberOfChannels_); + + for (const auto& channel : other.channels_) { + channels_.emplace_back(std::make_shared(*channel)); + } + + return *this; } - sampleRate_ = other.sampleRate_; + if (size_ != other.size_) { + size_ = other.size_; + } - for (int i = 0; i < numberOfChannels_; i += 1) { + for (size_t i = 0; i < numberOfChannels_; i += 1) { *channels_[i] = *other.channels_[i]; } } @@ -81,7 +94,7 @@ AudioBuffer &AudioBuffer::operator=(audioapi::AudioBuffer &&other) noexcept { return *this; } -AudioArray *AudioBuffer::getChannel(int index) const { +AudioArray *AudioBuffer::getChannel(size_t index) const { return channels_[index].get(); } @@ -155,7 +168,7 @@ AudioArray *AudioBuffer::getChannelByType(int channelType) const { } } -std::shared_ptr AudioBuffer::getSharedChannel(int index) const { +std::shared_ptr AudioBuffer::getSharedChannel(size_t index) const { return channels_[index]; } @@ -183,8 +196,8 @@ void AudioBuffer::sum( return; } - int numberOfSourceChannels = source.getNumberOfChannels(); - int numberOfChannels = getNumberOfChannels(); + auto numberOfSourceChannels = source.getNumberOfChannels(); + auto numberOfChannels = getNumberOfChannels(); if (interpretation == ChannelInterpretation::DISCRETE) { discreteSum(source, sourceStart, destinationStart, length); @@ -204,7 +217,7 @@ void AudioBuffer::sum( } // Source and destination channel counts are the same. Just sum the channels. - for (int i = 0; i < getNumberOfChannels(); i += 1) { + for (size_t i = 0; i < getNumberOfChannels(); i += 1) { channels_[i]->sum(*source.channels_[i], sourceStart, destinationStart, length); } } @@ -223,7 +236,7 @@ void AudioBuffer::copy( } if (source.getNumberOfChannels() == getNumberOfChannels()) { - for (int i = 0; i < getNumberOfChannels(); i += 1) { + for (size_t i = 0; i < getNumberOfChannels(); i += 1) { channels_[i]->copy(*source.channels_[i], sourceStart, destinationStart, length); } @@ -251,7 +264,7 @@ void AudioBuffer::deinterleaveFrom(const float *source, size_t frames) { } float *channelsPtrs[MAX_CHANNEL_COUNT]; - for (int i = 0; i < numberOfChannels_; ++i) { + for (size_t i = 0; i < numberOfChannels_; ++i) { channelsPtrs[i] = channels_[i]->begin(); } @@ -259,7 +272,7 @@ void AudioBuffer::deinterleaveFrom(const float *source, size_t frames) { size_t blockEnd = std::min(blockStart + BLOCK_SIZE, frames); for (size_t i = blockStart; i < blockEnd; ++i) { const float *frameSource = source + (i * numberOfChannels_); - for (int ch = 0; ch < numberOfChannels_; ++ch) { + for (size_t ch = 0; ch < numberOfChannels_; ++ch) { channelsPtrs[ch][i] = frameSource[ch]; } } @@ -282,7 +295,7 @@ void AudioBuffer::interleaveTo(float *destination, size_t frames) const { } float *channelsPtrs[MAX_CHANNEL_COUNT]; - for (int i = 0; i < numberOfChannels_; ++i) { + for (size_t i = 0; i < numberOfChannels_; ++i) { channelsPtrs[i] = channels_[i]->begin(); } @@ -290,7 +303,7 @@ void AudioBuffer::interleaveTo(float *destination, size_t frames) const { size_t blockEnd = std::min(blockStart + BLOCK_SIZE, frames); for (size_t i = blockStart; i < blockEnd; ++i) { float *frameDest = destination + (i * numberOfChannels_); - for (int ch = 0; ch < numberOfChannels_; ++ch) { + for (size_t ch = 0; ch < numberOfChannels_; ++ch) { frameDest[ch] = channelsPtrs[ch][i]; } } @@ -329,32 +342,17 @@ float AudioBuffer::maxAbsValue() const { * Internal tooling - channel initialization */ -void AudioBuffer::createChannels() { - if (channels_.size() != static_cast(numberOfChannels_)) { - channels_.clear(); - channels_.reserve(numberOfChannels_); - - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_.emplace_back(std::make_shared(size_)); - } - } else { - for (int i = 0; i < numberOfChannels_; i += 1) { - channels_[i]->resize(size_); - } - } -} - void AudioBuffer::discreteSum( const AudioBuffer &source, size_t sourceStart, size_t destinationStart, size_t length) const { - int numberOfChannels = std::min(getNumberOfChannels(), source.getNumberOfChannels()); + auto numberOfChannels = std::min(getNumberOfChannels(), source.getNumberOfChannels()); // In case of source > destination, we "down-mix" and drop the extra channels. // In case of source < destination, we "up-mix" as many channels as we have, // leaving the remaining channels untouched. - for (int i = 0; i < numberOfChannels; i++) { + for (size_t i = 0; i < numberOfChannels; i++) { channels_[i]->sum(*source.channels_[i], sourceStart, destinationStart, length); } } @@ -364,8 +362,8 @@ void AudioBuffer::sumByUpMixing( size_t sourceStart, size_t destinationStart, size_t length) { - int numberOfSourceChannels = source.getNumberOfChannels(); - int numberOfChannels = getNumberOfChannels(); + auto numberOfSourceChannels = source.getNumberOfChannels(); + auto numberOfChannels = getNumberOfChannels(); // Mono to stereo (1 -> 2, 4) if (numberOfSourceChannels == 1 && (numberOfChannels == 2 || numberOfChannels == 4)) { @@ -415,8 +413,8 @@ void AudioBuffer::sumByDownMixing( size_t sourceStart, size_t destinationStart, size_t length) { - int numberOfSourceChannels = source.getNumberOfChannels(); - int numberOfChannels = getNumberOfChannels(); + auto numberOfSourceChannels = source.getNumberOfChannels(); + auto numberOfChannels = getNumberOfChannels(); // Stereo to mono (2 -> 1): output += 0.5 * (input.left + input.right). if (numberOfSourceChannels == 2 && numberOfChannels == 1) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h index 5e77f957a..a91bcada8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.h @@ -34,34 +34,36 @@ class AudioBuffer { AudioBuffer &operator=(AudioBuffer &&other) noexcept; ~AudioBuffer() = default; - [[nodiscard]] inline int getNumberOfChannels() const noexcept { + [[nodiscard]] size_t getNumberOfChannels() const noexcept { return numberOfChannels_; } - [[nodiscard]] inline float getSampleRate() const noexcept { + [[nodiscard]] float getSampleRate() const noexcept { return sampleRate_; } - [[nodiscard]] inline size_t getSize() const noexcept { + [[nodiscard]] size_t getSize() const noexcept { return size_; } [[nodiscard]] double getDuration() const noexcept { - return static_cast(size_) / getSampleRate(); + return static_cast(size_) / static_cast(getSampleRate()); } /// @brief Get the AudioArray for a specific channel index. /// @param index The channel index. /// @return Pointer to the AudioArray for the specified channel - not owning. - [[nodiscard]] AudioArray *getChannel(int index) const; + [[nodiscard]] AudioArray *getChannel(size_t index) const; /// @brief Get the AudioArray for a specific channel type. - /// @param channelType The channel type (e.g., ChannelLeft, ChannelRight). + /// @param channelType The channel type: ChannelMono = 0, ChannelLeft = 0, + /// ChannelRight = 1, ChannelCenter = 2, ChannelLFE = 3, + /// ChannelSurroundLeft = 4, ChannelSurroundRight = 5 /// @return Pointer to the AudioArray for the specified channel type - not owning. [[nodiscard]] AudioArray *getChannelByType(int channelType) const; /// @brief Get a copy of shared pointer to the AudioArray for a specific channel index. /// @param index The channel index. /// @return Copy of shared pointer to the AudioArray for the specified channel - [[nodiscard]] std::shared_ptr getSharedChannel(int index) const; + [[nodiscard]] std::shared_ptr getSharedChannel(size_t index) const; AudioArray &operator[](size_t index) { return *channels_[index]; @@ -138,11 +140,10 @@ class AudioBuffer { private: std::vector> channels_; - int numberOfChannels_ = 0; + size_t numberOfChannels_ = 0; float sampleRate_ = 0.0f; size_t size_ = 0; - void createChannels(); void discreteSum( const AudioBuffer &source, size_t sourceStart, diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm index f9d05810a..3181fb58e 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioPlayer.mm @@ -27,7 +27,7 @@ audioBuffer_->zero(); } - for (int channel = 0; channel < channelCount_; channel += 1) { + for (size_t channel = 0; channel < channelCount_; channel += 1) { float *outputChannel = (float *)outputData->mBuffers[channel].mData; audioBuffer_->getChannel(channel)->copyTo( diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm index 8e54daa16..eb1715158 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/AudioDecoder.mm @@ -150,7 +150,7 @@ auto audioBuffer = std::make_shared(numFramesDecoded, inputChannelCount, inputSampleRate); - for (int ch = 0; ch < inputChannelCount; ++ch) { + for (size_t ch = 0; ch < inputChannelCount; ++ch) { auto channelData = audioBuffer->getChannel(ch)->span(); for (size_t i = 0; i < numFramesDecoded; ++i) { diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm index d922fb74c..abeaeccf0 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm @@ -39,7 +39,7 @@ converterInputBuffer_ = nil; converterOutputBuffer_ = nil; - for (int i = 0; i < channelCount_; ++i) { + for (size_t i = 0; i < channelCount_; ++i) { circularBus_[i]->zero(); } } @@ -116,7 +116,7 @@ converterInputBuffer_ = nil; converterOutputBuffer_ = nil; - for (int i = 0; i < channelCount_; ++i) { + for (size_t i = 0; i < channelCount_; ++i) { circularBus_[i]->zero(); } offloader_.reset(); @@ -148,7 +148,7 @@ if (bufferFormat_.sampleRate == sampleRate_ && bufferFormat_.channelCount == channelCount_ && !bufferFormat_.isInterleaved) { // Directly write to circular buffer - for (int i = 0; i < channelCount_; ++i) { + for (size_t i = 0; i < channelCount_; ++i) { auto *data = static_cast(inputBuffer->mBuffers[i].mData); circularBus_[i]->push_back(data, numFrames); } @@ -194,7 +194,7 @@ return; } - for (int i = 0; i < channelCount_; ++i) { + for (size_t i = 0; i < channelCount_; ++i) { auto *data = static_cast(converterOutputBuffer_.audioBufferList->mBuffers[i].mData); circularBus_[i]->push_back(data, outputFrameCount); } From 8587663412428d771a3b2b0d3d2720ecbeefeca7 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Feb 2026 17:45:54 +0100 Subject: [PATCH 26/73] fix: nits --- .../common/cpp/audioapi/utils/AudioBuffer.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp index 0b22592ae..2349005da 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/AudioBuffer.cpp @@ -10,10 +10,6 @@ #include #include -// Implementation of channel summing/mixing is based on the WebKit approach, -// source: -// https://github.com/WebKit/WebKit/blob/main/Source/WebCore/platform/audio/AudioBus.cpp - namespace audioapi { const float SQRT_HALF = sqrtf(0.5f); From a033e1265782145636353c0945779b8761dc5a4a Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:11:31 +0100 Subject: [PATCH 27/73] ci: lint --- .../common/cpp/audioapi/AudioAPIModuleInstaller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h index 5d8531762..c58ee2519 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include From e43d34bd7878a318920ebf104084a96ee4ff027c Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:44:19 +0100 Subject: [PATCH 28/73] refactor: fat function --- .../utils/CrossThreadEventScheduler.hpp | 23 +-- .../common/cpp/audioapi/utils/FatFunction.hpp | 150 ++++++++++++++++++ 2 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/utils/FatFunction.hpp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp index 0d47e5d8a..ed30bccb8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -24,11 +25,13 @@ using namespace channels::spsc; /// In this setup no locking happens and modifications can be seen by Audio thread. /// @note it is intended to be used for two threads one which schedules events and one which processes them /// @note it is not safe to be copied across two threads use std::shared_ptr if you need to share data -template +template class CrossThreadEventScheduler { + using EventType = FatFunction; + public: explicit CrossThreadEventScheduler(size_t capacity) { - auto [sender, receiver] = channel>(capacity); + auto [sender, receiver] = channel(capacity); eventSender_ = std::move(sender); eventReceiver_ = std::move(receiver); } @@ -36,26 +39,26 @@ class CrossThreadEventScheduler { CrossThreadEventScheduler &operator=(const CrossThreadEventScheduler &) = delete; /// @brief Schedules an event to be processed on the audio thread. - /// @param event The event to schedule. - /// @return True if the event was successfully scheduled, false if the queue is full. - bool scheduleEvent(std::function &&event) noexcept( + /// @param event The event to schedule. Implicitly converts from lambdas. + /// @return True if scheduled, false if the queue is full. + /// @note Requires that sizeof(lambda) <= FunctionSize. + bool scheduleEvent(EventType &&event) noexcept( noexcept(eventSender_.try_send(std::move(event)))) { return eventSender_.try_send(std::move(event)) == ResponseStatus::SUCCESS; } /// @brief Processes all scheduled events. /// @param data The data to pass to each event. - void processAllEvents(T &data) noexcept( - noexcept(eventReceiver_.try_receive(std::declval &>()))) { - std::function event; + void processAllEvents(T &data) noexcept { + EventType event; while (eventReceiver_.try_receive(event) == ResponseStatus::SUCCESS) { event(data); } } private: - Sender> eventSender_; - Receiver> eventReceiver_; + Sender eventSender_; + Receiver eventReceiver_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/FatFunction.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/FatFunction.hpp new file mode 100644 index 000000000..b32d154ee --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/FatFunction.hpp @@ -0,0 +1,150 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace audioapi { + +template +concept CallableConcept = requires(_Callable &&_c, _FpArgs &&..._args) { + sizeof(std::decay_t<_Callable>) <= N; + { _c(std::forward<_FpArgs>(_args)...) } -> std::convertible_to<_FpReturnType>; +}; + +template +class FatFunction; + +/// @brief FatFunction is a fixed-size function wrapper that can store callable objects +/// of a specific size N without dynamic memory allocation. +/// @tparam N Size in bytes to allocate for the callable object +/// @tparam _Fp The function signature (e.g., void(), int(int), etc.) +template +class FatFunction { + + private: + using _InvokerType = _FpReturnType (*)(const std::byte *storage, _FpArgs... args); + using _DeleterType = void (*)(std::byte *storage); + using _MoverType = void (*)(std::byte *dest, std::byte *src); + + public: + FatFunction() = default; + FatFunction(std::nullptr_t) : FatFunction() {} + + /// @brief Constructs a FatFunction from a callable object. + /// @tparam _Callable The type of the callable object + /// @tparam (enable_if) Ensures that the callable fits within the allocated size N + /// and is invocable with the specified signature. + /// @param callable The callable object to store + template + requires CallableConcept + FatFunction(_Callable &&callable) { + using DecayedCallable = std::decay_t<_Callable>; + new (storage_.data()) DecayedCallable(std::forward<_Callable>(callable)); + invoker_ = [](const std::byte *storage, _FpArgs... args) -> _FpReturnType { + const DecayedCallable *callablePtr = reinterpret_cast(storage); + return (*callablePtr)(std::forward<_FpArgs>(args)...); + }; + if constexpr (std::is_trivially_destructible_v) { + // No custom deleter needed for trivially destructible types + deleter_ = nullptr; + } else { + deleter_ = [](std::byte *storage) { + DecayedCallable *callablePtr = reinterpret_cast(storage); + callablePtr->~DecayedCallable(); + }; + } + if constexpr (std::is_trivially_move_constructible_v) { + // No custom mover needed for trivially moveable types as memcpy is a fallback + mover_ = nullptr; + } else { + mover_ = [](std::byte *dest, std::byte *src) { + DecayedCallable *srcPtr = reinterpret_cast(src); + new (dest) DecayedCallable(std::move(*srcPtr)); + }; + } + } + + /// @brief Move constructor + /// @param other + FatFunction(FatFunction &&other) noexcept { + if (other.invoker_) { + if (other.mover_) { + other.mover_(storage_.data(), other.storage_.data()); + } else { + std::memcpy(storage_.data(), other.storage_.data(), N); + } + invoker_ = other.invoker_; + deleter_ = other.deleter_; + mover_ = other.mover_; + other.reset(); + } + } + + /// @brief Move assignment operator + /// @param other + FatFunction &operator=(FatFunction &&other) noexcept { + if (this != &other) { + reset(); + if (other.invoker_) { + if (other.mover_) { + other.mover_(storage_.data(), other.storage_.data()); + } else { + std::memcpy(storage_.data(), other.storage_.data(), N); + } + invoker_ = other.invoker_; + deleter_ = other.deleter_; + mover_ = other.mover_; + other.reset(); + } + } + return *this; + } + + /// @brief Call operator to invoke the stored callable + /// @param ...args Arguments to pass to the callable + /// @return The result of the callable invocation + _FpReturnType operator()(_FpArgs &&...args) const { + if (!invoker_) { + throw std::bad_function_call(); + } + return invoker_(storage_.data(), std::forward<_FpArgs>(args)...); + } + + /// @brief Destructor + ~FatFunction() { + reset(); + } + + /// @brief Releases the stored callable and returns its storage and deleter. + /// @return A pair containing the storage array and the deleter function + /// @note To clear resources properly after release, the user must call the deleter on the storage. + std::pair, _DeleterType> release() { + std::array storageCopy; + std::memcpy(storageCopy.data(), storage_.data(), N); + _DeleterType deleterCopy = deleter_; + deleter_ = nullptr; + invoker_ = nullptr; + mover_ = nullptr; + return {std::move(storageCopy), deleterCopy}; + } + + private: + alignas(std::max_align_t) std::array storage_; + _InvokerType invoker_ = nullptr; // Function pointer to invoke the stored callable + _DeleterType deleter_ = nullptr; // Function pointer to delete the stored callable + _MoverType mover_ = nullptr; // Function pointer to move the stored callable + + void reset() { + if (deleter_) { + deleter_(storage_.data()); + } + deleter_ = nullptr; + invoker_ = nullptr; + mover_ = nullptr; + } +}; + +} // namespace audioapi From ae6b384eaae5760e6d63454b18f66b148309608d Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:45:02 +0100 Subject: [PATCH 29/73] refactor: audio param --- .../HostObjects/AudioParamHostObject.cpp | 71 ++++++++++++------- .../HostObjects/AudioParamHostObject.h | 3 + .../common/cpp/audioapi/core/AudioParam.cpp | 70 ++++++------------ .../common/cpp/audioapi/core/AudioParam.h | 39 ++++------ .../cpp/audioapi/core/BaseAudioContext.h | 19 +++++ .../cpp/audioapi/core/OfflineAudioContext.h | 4 ++ 6 files changed, 106 insertions(+), 100 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp index 6d501e25d..d3ed6c423 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp @@ -9,7 +9,7 @@ namespace audioapi { AudioParamHostObject::AudioParamHostObject(const std::shared_ptr ¶m) - : param_(param) { + : param_(param), defaultValue_(param->getDefaultValue()), minValue_(param->getMinValue()), maxValue_(param->getMaxValue()) { addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioParamHostObject, value), JSI_EXPORT_PROPERTY_GETTER(AudioParamHostObject, defaultValue), @@ -33,47 +33,58 @@ JSI_PROPERTY_GETTER_IMPL(AudioParamHostObject, value) { } JSI_PROPERTY_GETTER_IMPL(AudioParamHostObject, defaultValue) { - return {param_->getDefaultValue()}; + return {defaultValue_}; } JSI_PROPERTY_GETTER_IMPL(AudioParamHostObject, minValue) { - return {param_->getMinValue()}; + return {minValue_}; } JSI_PROPERTY_GETTER_IMPL(AudioParamHostObject, maxValue) { - return {param_->getMaxValue()}; + return {maxValue_}; } JSI_PROPERTY_SETTER_IMPL(AudioParamHostObject, value) { - param_->setValue(static_cast(value.getNumber())); + auto event = [param = param_, value = static_cast(value.getNumber())](BaseAudioContext &) { + param->setValue(value); + }; + + param_->scheduleAudioEvent(std::move(event)); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, setValueAtTime) { - auto value = static_cast(args[0].getNumber()); - double startTime = args[1].getNumber(); - param_->setValueAtTime(value, startTime); + auto event = [param = param_, value = static_cast(args[0].getNumber()), startTime = args[1].getNumber()](BaseAudioContext &) { + param->setValueAtTime(value, startTime); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, linearRampToValueAtTime) { - auto value = static_cast(args[0].getNumber()); - double endTime = args[1].getNumber(); - param_->linearRampToValueAtTime(value, endTime); + auto event = [param = param_, value = static_cast(args[0].getNumber()), endTime = args[1].getNumber()](BaseAudioContext &) { + param->linearRampToValueAtTime(value, endTime); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, exponentialRampToValueAtTime) { - auto value = static_cast(args[0].getNumber()); - double endTime = args[1].getNumber(); - param_->exponentialRampToValueAtTime(value, endTime); + auto event = [param = param_, value = static_cast(args[0].getNumber()), endTime = args[1].getNumber()](BaseAudioContext &) { + param->exponentialRampToValueAtTime(value, endTime); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, setTargetAtTime) { - auto target = static_cast(args[0].getNumber()); - double startTime = args[1].getNumber(); - double timeConstant = args[2].getNumber(); - param_->setTargetAtTime(target, startTime, timeConstant); + auto event = [param = param_, target = static_cast(args[0].getNumber()), startTime = args[1].getNumber(), timeConstant = args[2].getNumber()](BaseAudioContext &) { + param->setTargetAtTime(target, startTime, timeConstant); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } @@ -82,23 +93,31 @@ JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, setValueCurveAtTime) { args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); auto rawValues = reinterpret_cast(arrayBuffer.data(runtime)); auto length = static_cast(arrayBuffer.size(runtime)); - auto values = std::make_unique(rawValues, length); + auto values = std::make_shared(rawValues, length); + + auto event = [param = param_, values, length, startTime = args[1].getNumber(), duration = args[2].getNumber()](BaseAudioContext &) { + param->setValueCurveAtTime(values, length, startTime, duration); + }; - double startTime = args[1].getNumber(); - double duration = args[2].getNumber(); - param_->setValueCurveAtTime(std::move(values), length, startTime, duration); + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, cancelScheduledValues) { - double cancelTime = args[0].getNumber(); - param_->cancelScheduledValues(cancelTime); + auto event = [param = param_, cancelTime = args[0].getNumber()](BaseAudioContext &) { + param->cancelScheduledValues(cancelTime); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioParamHostObject, cancelAndHoldAtTime) { - double cancelTime = args[0].getNumber(); - param_->cancelAndHoldAtTime(cancelTime); + auto event = [param = param_, cancelTime = args[0].getNumber()](BaseAudioContext &) { + param->cancelAndHoldAtTime(cancelTime); + }; + + param_->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.h index 8afbfaa10..363ec7bcc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioParamHostObject.h @@ -36,5 +36,8 @@ class AudioParamHostObject : public JsiHostObject { friend class AudioNodeHostObject; std::shared_ptr param_; + float defaultValue_; + float minValue_; + float maxValue_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp index be3b6f82c..f4860fd67 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp @@ -19,7 +19,6 @@ AudioParam::AudioParam( minValue_(minValue), maxValue_(maxValue), eventsQueue_(), - eventScheduler_(32), startTime_(0), endTime_(0), startValue_(defaultValue), @@ -48,14 +47,14 @@ float AudioParam::getValueAtTime(double time) { } // Calculate value using the current automation function and clamp to valid - setValue(calculateValue_(startTime_, endTime_, startValue_, endValue_, time)); - return value_; + auto value = calculateValue_(startTime_, endTime_, startValue_, endValue_, time); + setValue(value); + return value; } void AudioParam::setValueAtTime(float value, double startTime) { - auto event = [value, startTime](AudioParam ¶m) { // Ignore events scheduled before the end of existing automation - if (startTime < param.getQueueEndTime()) { + if (startTime < this->getQueueEndTime()) { return; } @@ -69,21 +68,18 @@ void AudioParam::setValueAtTime(float value, double startTime) { return endValue; }; - param.updateQueue(ParamChangeEvent( + this->updateQueue(ParamChangeEvent( startTime, startTime, - param.getQueueEndValue(), + this->getQueueEndValue(), value, std::move(calculateValue), ParamChangeEventType::SET_VALUE)); - }; - eventScheduler_.scheduleEvent(std::move(event)); } void AudioParam::linearRampToValueAtTime(float value, double endTime) { - auto event = [value, endTime](AudioParam ¶m) { // Ignore events scheduled before the end of existing automation - if (endTime < param.getQueueEndTime()) { + if (endTime < this->getQueueEndTime()) { return; } @@ -102,20 +98,17 @@ void AudioParam::linearRampToValueAtTime(float value, double endTime) { return endValue; }; - param.updateQueue(ParamChangeEvent( - param.getQueueEndTime(), + this->updateQueue(ParamChangeEvent( + this->getQueueEndTime(), endTime, - param.getQueueEndValue(), + this->getQueueEndValue(), value, std::move(calculateValue), ParamChangeEventType::LINEAR_RAMP)); - }; - eventScheduler_.scheduleEvent(std::move(event)); } void AudioParam::exponentialRampToValueAtTime(float value, double endTime) { - auto event = [value, endTime](AudioParam ¶m) { - if (endTime <= param.getQueueEndTime()) { + if (endTime <= this->getQueueEndTime()) { return; } @@ -135,20 +128,17 @@ void AudioParam::exponentialRampToValueAtTime(float value, double endTime) { return endValue; }; - param.updateQueue(ParamChangeEvent( - param.getQueueEndTime(), + this->updateQueue(ParamChangeEvent( + this->getQueueEndTime(), endTime, - param.getQueueEndValue(), + this->getQueueEndValue(), value, std::move(calculateValue), ParamChangeEventType::EXPONENTIAL_RAMP)); - }; - eventScheduler_.scheduleEvent(std::move(event)); } void AudioParam::setTargetAtTime(float target, double startTime, double timeConstant) { - auto event = [target, startTime, timeConstant](AudioParam ¶m) { - if (startTime <= param.getQueueEndTime()) { + if (startTime <= this->getQueueEndTime()) { return; } // Exponential decay function towards target value @@ -161,17 +151,14 @@ void AudioParam::setTargetAtTime(float target, double startTime, double timeCons return static_cast( target + (startValue - target) * exp(-(time - startTime) / timeConstant)); }; - param.updateQueue(ParamChangeEvent( + this->updateQueue(ParamChangeEvent( startTime, startTime, // SetTarget events have infinite duration conceptually - param.getQueueEndValue(), - param.getQueueEndValue(), // End value is not meaningful for + this->getQueueEndValue(), + this->getQueueEndValue(), // End value is not meaningful for // infinite events std::move(calculateValue), ParamChangeEventType::SET_TARGET)); - }; - - eventScheduler_.scheduleEvent(std::move(event)); } void AudioParam::setValueCurveAtTime( @@ -179,8 +166,7 @@ void AudioParam::setValueCurveAtTime( size_t length, double startTime, double duration) { - auto event = [values, length, startTime, duration](AudioParam ¶m) { - if (startTime <= param.getQueueEndTime()) { + if (startTime <= this->getQueueEndTime()) { return; } @@ -204,29 +190,21 @@ void AudioParam::setValueCurveAtTime( return endValue; }; - param.updateQueue(ParamChangeEvent( + this->updateQueue(ParamChangeEvent( startTime, startTime + duration, - param.getQueueEndValue(), + this->getQueueEndValue(), values->span()[length - 1], std::move(calculateValue), ParamChangeEventType::SET_VALUE_CURVE)); - }; - - /// Schedules an event that modifies this param - /// It will be executed on next audio render cycle - eventScheduler_.scheduleEvent(std::move(event)); } void AudioParam::cancelScheduledValues(double cancelTime) { - eventScheduler_.scheduleEvent( - [cancelTime](AudioParam ¶m) { param.eventsQueue_.cancelScheduledValues(cancelTime); }); + this->eventsQueue_.cancelScheduledValues(cancelTime); } void AudioParam::cancelAndHoldAtTime(double cancelTime) { - eventScheduler_.scheduleEvent([cancelTime](AudioParam ¶m) { - param.eventsQueue_.cancelAndHoldAtTime(cancelTime, param.endTime_); - }); + this->eventsQueue_.cancelAndHoldAtTime(cancelTime, this->endTime_); } void AudioParam::addInputNode(AudioNode *node) { @@ -256,7 +234,6 @@ std::shared_ptr AudioParam::calculateInputs( } std::shared_ptr AudioParam::processARateParam(int framesToProcess, double time) { - processScheduledEvents(); auto processingBuffer = calculateInputs(audioBuffer_, framesToProcess); std::shared_ptr context = context_.lock(); @@ -278,7 +255,6 @@ std::shared_ptr AudioParam::processARateParam(int framesToProcess, } float AudioParam::processKRateParam(int framesToProcess, double time) { - processScheduledEvents(); auto processingBuffer = calculateInputs(audioBuffer_, framesToProcess); // Return block-rate parameter value plus first sample of input modulation diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index 7f5b7130e..c901d00e0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -23,59 +24,49 @@ class AudioParam { float maxValue, const std::shared_ptr &context); - /// JS-Thread only methods - /// These methods are called only from HostObjects invoked on the JS thread. - - // JS-Thread only [[nodiscard]] inline float getValue() const noexcept { return value_.load(std::memory_order_relaxed); } - // JS-Thread only [[nodiscard]] inline float getDefaultValue() const noexcept { return defaultValue_; } - // JS-Thread only [[nodiscard]] inline float getMinValue() const noexcept { return minValue_; } - // JS-Thread only [[nodiscard]] inline float getMaxValue() const noexcept { return maxValue_; } - // JS-Thread only inline void setValue(float value) { value_.store(std::clamp(value, minValue_, maxValue_), std::memory_order_release); } - // JS-Thread only void setValueAtTime(float value, double startTime); - - // JS-Thread only void linearRampToValueAtTime(float value, double endTime); - - // JS-Thread only void exponentialRampToValueAtTime(float value, double endTime); - - // JS-Thread only void setTargetAtTime(float target, double startTime, double timeConstant); - - // JS-Thread only void setValueCurveAtTime( const std::shared_ptr &values, size_t length, double startTime, double duration); - - // JS-Thread only void cancelScheduledValues(double cancelTime); - - // JS-Thread only void cancelAndHoldAtTime(double cancelTime); + template < + typename F, + typename = std::enable_if_t, BaseAudioContext &>>> + bool inline scheduleAudioEvent(F &&event) noexcept { + if (std::shared_ptr context = context_.lock()) { + return context->scheduleAudioEvent(std::forward(event)); + } + + return false; + } + /// Audio-Thread only methods /// These methods are called only from the Audio rendering thread. @@ -100,7 +91,6 @@ class AudioParam { float maxValue_; AudioParamEventQueue eventsQueue_; - CrossThreadEventScheduler eventScheduler_; // Current automation state (cached for performance) double startTime_; @@ -132,11 +122,6 @@ class AudioParam { return eventsQueue_.back().getEndValue(); } - /// @brief Process all scheduled events. - inline void processScheduledEvents() noexcept(noexcept(eventScheduler_.processAllEvents(*this))) { - eventScheduler_.processAllEvents(*this); - } - /// @brief Update the parameter queue with a new event. /// @param event The new event to add to the queue. /// @note Handles connecting start value of the new event to the end value of the previous event. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index ff7da2d28..c6cad5dc5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -113,6 +115,21 @@ class BaseAudioContext : public std::enable_shared_from_this { virtual void initialize(); + void inline processAudioEvents() { + audioEventScheduler_->processAllEvents(*this); + } + + template + bool inline scheduleAudioEvent(F &&event) noexcept { + if (getState() != ContextState::RUNNING) { + processAudioEvents(); + event(*this); + return true; + } + + return audioEventScheduler_->scheduleEvent(std::forward(event)); + } + protected: std::shared_ptr destination_; @@ -128,6 +145,8 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr cachedSawtoothWave_ = nullptr; std::shared_ptr cachedTriangleWave_ = nullptr; + std::unique_ptr> audioEventScheduler_; + [[nodiscard]] virtual bool isDriverRunning() const = 0; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h index aac15b052..210bff547 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h @@ -23,9 +23,13 @@ class OfflineAudioContext : public BaseAudioContext { const RuntimeRegistry &runtimeRegistry); ~OfflineAudioContext() override; + /// @note JS Thread only void resume(); + + /// @note JS Thread only void suspend(double when, const OfflineAudioContextSuspendCallback &callback); + /// @note JS Thread only void startRendering(OfflineAudioContextResultCallback callback); private: From 6c5595407b1f6bb310231b246939d01e7ce89e4d Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:54:05 +0100 Subject: [PATCH 30/73] refactor: audio node --- .../HostObjects/AudioNodeHostObject.cpp | 18 ++++++++----- .../HostObjects/AudioNodeHostObject.h | 8 ++++++ .../common/cpp/audioapi/core/AudioNode.cpp | 16 ------------ .../common/cpp/audioapi/core/AudioNode.h | 26 +++++++++++++------ 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp index ae841ed4f..a33dd12b2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp @@ -8,7 +8,13 @@ namespace audioapi { AudioNodeHostObject::AudioNodeHostObject(const std::shared_ptr &node, - const AudioNodeOptions &options) : node_(node) { + const AudioNodeOptions &options) + : node_(node), + numberOfInputs_(options.numberOfInputs), + numberOfOutputs_(options.numberOfOutputs), + channelCount_(options.channelCount), + channelCountMode_(options.channelCountMode), + channelInterpretation_(options.channelInterpretation) { addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioNodeHostObject, numberOfInputs), JSI_EXPORT_PROPERTY_GETTER(AudioNodeHostObject, numberOfOutputs), @@ -28,23 +34,23 @@ AudioNodeHostObject::AudioNodeHostObject(const std::shared_ptr &node, AudioNodeHostObject::~AudioNodeHostObject() = default; JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, numberOfInputs) { - return {node_->getNumberOfInputs()}; + return {numberOfInputs_}; } JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, numberOfOutputs) { - return {node_->getNumberOfOutputs()}; + return {numberOfOutputs_}; } JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, channelCount) { - return {static_cast(node_->getChannelCount())}; + return {static_cast(channelCount_)}; } JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, channelCountMode) { - return jsi::String::createFromUtf8(runtime, js_enum_parser::channelCountModeToString(node_->getChannelCountMode())); + return jsi::String::createFromUtf8(runtime, js_enum_parser::channelCountModeToString(channelCountMode_)); } JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, channelInterpretation) { - return jsi::String::createFromUtf8(runtime, js_enum_parser::channelInterpretationToString(node_->getChannelInterpretation())); + return jsi::String::createFromUtf8(runtime, js_enum_parser::channelInterpretationToString(channelInterpretation_)); } JSI_HOST_FUNCTION_IMPL(AudioNodeHostObject, connect) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h index 423f84b6d..c890271cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -30,5 +32,11 @@ class AudioNodeHostObject : public JsiHostObject { protected: std::shared_ptr node_; + + const int numberOfInputs_; + const int numberOfOutputs_; + size_t channelCount_; + const ChannelCountMode channelCountMode_; + const ChannelInterpretation channelInterpretation_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp index 94959779c..837e3f5b2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp @@ -31,26 +31,10 @@ AudioNode::~AudioNode() { } } -int AudioNode::getNumberOfInputs() const { - return numberOfInputs_; -} - -int AudioNode::getNumberOfOutputs() const { - return numberOfOutputs_; -} - size_t AudioNode::getChannelCount() const { return channelCount_; } -ChannelCountMode AudioNode::getChannelCountMode() const { - return channelCountMode_; -} - -ChannelInterpretation AudioNode::getChannelInterpretation() const { - return channelInterpretation_; -} - void AudioNode::connect(const std::shared_ptr &node) { if (std::shared_ptr context = context_.lock()) { context->getGraphManager()->addPendingNodeConnection( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index e63c83c1d..e1224c7b5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,11 +12,12 @@ #include #include #include +#include +#include namespace audioapi { class AudioBuffer; -class BaseAudioContext; class AudioParam; class AudioNode : public std::enable_shared_from_this { @@ -25,11 +27,7 @@ class AudioNode : public std::enable_shared_from_this { const AudioNodeOptions &options = AudioNodeOptions()); virtual ~AudioNode(); - int getNumberOfInputs() const; - int getNumberOfOutputs() const; size_t getChannelCount() const; - ChannelCountMode getChannelCountMode() const; - ChannelInterpretation getChannelInterpretation() const; void connect(const std::shared_ptr &node); void connect(const std::shared_ptr ¶m); void disconnect(); @@ -40,10 +38,19 @@ class AudioNode : public std::enable_shared_from_this { int framesToProcess, bool checkIsAlreadyProcessed); + /// @note JS Thread only bool isEnabled() const; + /// @note JS Thread only bool requiresTailProcessing() const; - void enable(); - virtual void disable(); + + template + bool inline scheduleAudioEvent(F &&event) noexcept { + if (std::shared_ptr context = context_.lock()) { + return context->scheduleAudioEvent(std::forward(event)); + } + + return false; + } protected: friend class AudioGraphManager; @@ -67,11 +74,14 @@ class AudioNode : public std::enable_shared_from_this { int numberOfEnabledInputNodes_ = 0; bool isInitialized_ = false; - bool isEnabled_ = true; std::size_t lastRenderedFrame_{SIZE_MAX}; + void enable(); + virtual void disable(); + private: + bool isEnabled_ = true; std::vector> inputBuffers_ = {}; virtual std::shared_ptr processInputs( From 7e672587555f3f92655baf12bc5178694cfba1b3 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:54:49 +0100 Subject: [PATCH 31/73] ci: lint --- .../common/cpp/audioapi/HostObjects/AudioNodeHostObject.h | 4 ++-- .../common/cpp/audioapi/core/AudioNode.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h index c890271cf..25211cca0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h @@ -1,9 +1,9 @@ #pragma once -#include -#include #include #include +#include +#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index e1224c7b5..489e660ba 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -8,12 +8,12 @@ #include #include +#include #include #include #include -#include -#include #include +#include namespace audioapi { @@ -46,7 +46,7 @@ class AudioNode : public std::enable_shared_from_this { template bool inline scheduleAudioEvent(F &&event) noexcept { if (std::shared_ptr context = context_.lock()) { - return context->scheduleAudioEvent(std::forward(event)); + return context->scheduleAudioEvent(std::forward(event)); } return false; From 9b8d15c5f80f6bf3ec149e1bf73fc75b48124b74 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 15:56:47 +0100 Subject: [PATCH 32/73] refactor: destination --- .../cpp/audioapi/core/destinations/AudioDestinationNode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp index e11763597..2de87d4e4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp @@ -33,6 +33,7 @@ void AudioDestinationNode::renderAudio( } if (std::shared_ptr context = context_.lock()) { + context->processAudioEvents(); context->getGraphManager()->preProcessGraph(); } From 592139a1508454ecd4ffefce2a06a48729336345 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 16:09:16 +0100 Subject: [PATCH 33/73] refactor: audio scheduled source node --- .../AudioScheduledSourceNodeHostObject.cpp | 39 +++++++++++++------ .../AudioScheduledSourceNodeHostObject.h | 5 +++ .../cpp/audioapi/core/BaseAudioContext.cpp | 7 +++- .../cpp/audioapi/core/BaseAudioContext.h | 1 + .../core/sources/AudioScheduledSourceNode.cpp | 13 +++---- .../core/sources/AudioScheduledSourceNode.h | 6 ++- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp index 0f6c06c38..3a0932b20 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace audioapi { @@ -18,33 +19,49 @@ AudioScheduledSourceNodeHostObject::AudioScheduledSourceNodeHostObject( } AudioScheduledSourceNodeHostObject::~AudioScheduledSourceNodeHostObject() { - auto audioScheduledSourceNode = std::static_pointer_cast(node_); - // When JSI object is garbage collected (together with the eventual callback), // underlying source node might still be active and try to call the // non-existing callback. - audioScheduledSourceNode->setOnEndedCallbackId(0); + setOnEndedCallbackId(0); } JSI_PROPERTY_SETTER_IMPL(AudioScheduledSourceNodeHostObject, onEnded) { - auto audioScheduleSourceNode = std::static_pointer_cast(node_); - - audioScheduleSourceNode->setOnEndedCallbackId( - std::stoull(value.getString(runtime).utf8(runtime))); + auto callbackId = std::stoull(value.getString(runtime).utf8(runtime)); + setOnEndedCallbackId(callbackId); } JSI_HOST_FUNCTION_IMPL(AudioScheduledSourceNodeHostObject, start) { - auto when = args[0].getNumber(); auto audioScheduleSourceNode = std::static_pointer_cast(node_); - audioScheduleSourceNode->start(when); + + auto event = [audioScheduleSourceNode, when = args[0].getNumber()] (BaseAudioContext &) { + audioScheduleSourceNode->start(when); + }; + audioScheduleSourceNode->scheduleAudioEvent(std::move(event)); + return jsi::Value::undefined(); } JSI_HOST_FUNCTION_IMPL(AudioScheduledSourceNodeHostObject, stop) { - auto time = args[0].getNumber(); auto audioScheduleSourceNode = std::static_pointer_cast(node_); - audioScheduleSourceNode->stop(time); + + auto event = [audioScheduleSourceNode, when = args[0].getNumber()] (BaseAudioContext &) { + audioScheduleSourceNode->stop(when); + }; + audioScheduleSourceNode->scheduleAudioEvent(std::move(event)); + return jsi::Value::undefined(); } +void AudioScheduledSourceNodeHostObject::setOnEndedCallbackId(uint64_t callbackId) { + auto sourceNode = std::static_pointer_cast(node_); + + auto event = [sourceNode, callbackId](BaseAudioContext &) { + sourceNode->setOnEndedCallbackId(callbackId); + }; + + sourceNode->unregisterOnEndedCallback(onEndedCallbackId_); + sourceNode->scheduleAudioEvent(std::move(event)); + onEndedCallbackId_ = callbackId; +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h index f80caf5b6..c23a880f0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h @@ -23,5 +23,10 @@ class AudioScheduledSourceNodeHostObject : public AudioNodeHostObject { JSI_HOST_FUNCTION_DECL(start); JSI_HOST_FUNCTION_DECL(stop); + + private: + uint64_t onEndedCallbackId_ = 0; + + void setOnEndedCallbackId(uint64_t callbackId); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index f119be1d5..d9057646e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include #if !RN_AUDIO_API_FFMPEG_DISABLED #include #endif // RN_AUDIO_API_FFMPEG_DISABLED @@ -42,7 +42,10 @@ BaseAudioContext::BaseAudioContext( sampleRate_(sampleRate), graphManager_(std::make_shared()), audioEventHandlerRegistry_(audioEventHandlerRegistry), - runtimeRegistry_(runtimeRegistry) {} + runtimeRegistry_(runtimeRegistry), + audioEventScheduler_( + std::make_unique>(AUDIO_SCHEDULER_CAPACITY)) { +} void BaseAudioContext::initialize() { destination_ = std::make_shared(shared_from_this()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index c6cad5dc5..8699f1f6b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -145,6 +145,7 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr cachedSawtoothWave_ = nullptr; std::shared_ptr cachedTriangleWave_ = nullptr; + static constexpr size_t AUDIO_SCHEDULER_CAPACITY = 1024; std::unique_ptr> audioEventScheduler_; [[nodiscard]] virtual bool isDriverRunning() const = 0; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp index a2e048436..85c2c4464 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp @@ -63,11 +63,11 @@ bool AudioScheduledSourceNode::isStopScheduled() { } void AudioScheduledSourceNode::setOnEndedCallbackId(const uint64_t callbackId) { - auto oldCallbackId = onEndedCallbackId_.exchange(callbackId, std::memory_order_acq_rel); + onEndedCallbackId_ = callbackId; +} - if (oldCallbackId != 0) { - audioEventHandlerRegistry_->unregisterHandler(AudioEvent::ENDED, oldCallbackId); - } +void AudioScheduledSourceNode::unregisterOnEndedCallback(uint64_t callbackId) { + audioEventHandlerRegistry_->unregisterHandler(AudioEvent::ENDED, callbackId); } void AudioScheduledSourceNode::updatePlaybackInfo( @@ -165,10 +165,9 @@ void AudioScheduledSourceNode::updatePlaybackInfo( void AudioScheduledSourceNode::disable() { AudioNode::disable(); - auto onEndedCallbackId = onEndedCallbackId_.load(std::memory_order_acquire); - if (onEndedCallbackId != 0) { + if (onEndedCallbackId_ != 0) { audioEventHandlerRegistry_->invokeHandlerWithEventBody( - AudioEvent::ENDED, onEndedCallbackId, {}); + AudioEvent::ENDED, onEndedCallbackId_, {}); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 118f5dc49..104fdc366 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -44,13 +44,17 @@ class AudioScheduledSourceNode : public AudioNode { void disable() override; + /// @note JS Thread only. + /// Thread safe, because does not access state of the node. + void unregisterOnEndedCallback(uint64_t callbackId); + protected: double startTime_; double stopTime_; PlaybackState playbackState_; - std::atomic onEndedCallbackId_ = 0; + uint64_t onEndedCallbackId_ = 0; std::shared_ptr audioEventHandlerRegistry_; void updatePlaybackInfo( From af56e5ea27f35e498100713466d2cf4160df9de7 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 16:44:32 +0100 Subject: [PATCH 34/73] refactor: make isInitialized atomic variable --- .../docs/guides/create-your-own-effect.mdx | 2 +- .../templates/basic/shared/MyProcessorNode.cpp | 2 +- .../common/cpp/audioapi/core/AudioContext.cpp | 9 ++++----- .../common/cpp/audioapi/core/AudioContext.h | 2 +- .../common/cpp/audioapi/core/AudioNode.cpp | 10 +++++----- .../common/cpp/audioapi/core/AudioNode.h | 2 +- .../cpp/audioapi/core/analysis/AnalyserNode.cpp | 2 +- .../core/destinations/AudioDestinationNode.cpp | 4 ++-- .../cpp/audioapi/core/effects/BiquadFilterNode.cpp | 2 +- .../cpp/audioapi/core/effects/ConvolverNode.cpp | 2 +- .../common/cpp/audioapi/core/effects/DelayNode.cpp | 2 +- .../common/cpp/audioapi/core/effects/GainNode.cpp | 2 +- .../cpp/audioapi/core/effects/IIRFilterNode.cpp | 2 +- .../cpp/audioapi/core/effects/StereoPannerNode.cpp | 2 +- .../cpp/audioapi/core/effects/WaveShaperNode.cpp | 6 +----- .../cpp/audioapi/core/effects/WorkletNode.cpp | 3 ++- .../audioapi/core/effects/WorkletProcessingNode.cpp | 3 ++- .../core/sources/AudioBufferQueueSourceNode.cpp | 2 +- .../audioapi/core/sources/AudioBufferSourceNode.cpp | 2 +- .../core/sources/AudioScheduledSourceNode.cpp | 6 ------ .../audioapi/core/sources/ConstantSourceNode.cpp | 2 +- .../cpp/audioapi/core/sources/OscillatorNode.cpp | 2 +- .../audioapi/core/sources/RecorderAdapterNode.cpp | 13 ++++--------- .../cpp/audioapi/core/sources/WorkletSourceNode.cpp | 4 ++-- .../cpp/test/src/AudioScheduledSourceTest.cpp | 2 +- 25 files changed, 38 insertions(+), 52 deletions(-) diff --git a/packages/audiodocs/docs/guides/create-your-own-effect.mdx b/packages/audiodocs/docs/guides/create-your-own-effect.mdx index e195c92f2..bbb6f2c7a 100644 --- a/packages/audiodocs/docs/guides/create-your-own-effect.mdx +++ b/packages/audiodocs/docs/guides/create-your-own-effect.mdx @@ -77,7 +77,7 @@ namespace audioapi { MyProcessorNode::MyProcessorNode(const std::shared_ptr &context) //highlight-next-line : AudioNode(context), gain(0.5) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } void MyProcessorNode::processNode(const std::shared_ptr &buffer, diff --git a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp index b1ffb9581..033597f6c 100644 --- a/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp +++ b/packages/custom-node-generator/templates/basic/shared/MyProcessorNode.cpp @@ -5,7 +5,7 @@ namespace audioapi { MyProcessorNode::MyProcessorNode( const std::shared_ptr &context, ) : AudioNode(context) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp index 853f5633c..22fa27f88 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp @@ -15,8 +15,7 @@ AudioContext::AudioContext( float sampleRate, const std::shared_ptr &audioEventHandlerRegistry, const RuntimeRegistry &runtimeRegistry) - : BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry), - isInitialized_(false) {} + : BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry) {} AudioContext::~AudioContext() { if (getState() != ContextState::CLOSED) { @@ -52,7 +51,7 @@ bool AudioContext::resume() { return true; } - if (isInitialized_ && audioPlayer_->resume()) { + if (isInitialized_.load(std::memory_order_acquire) && audioPlayer_->resume()) { setState(ContextState::RUNNING); return true; } @@ -80,8 +79,8 @@ bool AudioContext::start() { return false; } - if (!isInitialized_ && audioPlayer_->start()) { - isInitialized_ = true; + if (!isInitialized_.load(std::memory_order_acquire) && audioPlayer_->start()) { + isInitialized_.store(true, std::memory_order_release); setState(ContextState::RUNNING); return true; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h index 967b38426..e1926a4d3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h @@ -33,7 +33,7 @@ class AudioContext : public BaseAudioContext { #else std::shared_ptr audioPlayer_; #endif - bool isInitialized_; + std::atomic isInitialized_ = false; bool isDriverRunning() const override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp index 837e3f5b2..fd63a223d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp @@ -26,7 +26,7 @@ AudioNode::AudioNode( } AudioNode::~AudioNode() { - if (isInitialized_) { + if (isInitialized_.load(std::memory_order_acquire)) { cleanup(); } } @@ -106,7 +106,7 @@ std::shared_ptr AudioNode::processAudio( const std::shared_ptr &outputBuffer, int framesToProcess, bool checkIsAlreadyProcessed) { - if (!isInitialized_) { + if (!isInitialized_.load(std::memory_order_acquire)) { return outputBuffer; } @@ -258,7 +258,7 @@ void AudioNode::onInputDisabled() { } void AudioNode::onInputConnected(AudioNode *node) { - if (!isInitialized_) { + if (!isInitialized_.load(std::memory_order_acquire)) { return; } @@ -270,7 +270,7 @@ void AudioNode::onInputConnected(AudioNode *node) { } void AudioNode::onInputDisconnected(AudioNode *node) { - if (!isInitialized_) { + if (!isInitialized_.load(std::memory_order_acquire)) { return; } @@ -286,7 +286,7 @@ void AudioNode::onInputDisconnected(AudioNode *node) { } void AudioNode::cleanup() { - isInitialized_ = false; + isInitialized_.store(false, std::memory_order_release); for (auto it = outputNodes_.begin(), end = outputNodes_.end(); it != end; ++it) { it->get()->onInputDisconnected(this); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 489e660ba..5cf18a806 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -73,7 +73,7 @@ class AudioNode : public std::enable_shared_from_this { std::unordered_set> outputParams_ = {}; int numberOfEnabledInputNodes_ = 0; - bool isInitialized_ = false; + std::atomic isInitialized_ = false; std::size_t lastRenderedFrame_{SIZE_MAX}; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index dd93c628a..c0ff06a98 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -32,7 +32,7 @@ AnalyserNode::AnalyserNode( complexData_(std::vector>(fftSize_)), magnitudeArray_(std::make_unique(fftSize_ / 2)) { setWindowData(windowType_, fftSize_); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } int AnalyserNode::getFftSize() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp index 2de87d4e4..6d86853a8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp @@ -10,7 +10,7 @@ namespace audioapi { AudioDestinationNode::AudioDestinationNode(const std::shared_ptr &context) : AudioNode(context, AudioDestinationOptions()), currentSampleFrame_(0) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::size_t AudioDestinationNode::getCurrentSampleFrame() const { @@ -28,7 +28,7 @@ double AudioDestinationNode::getCurrentTime() const { void AudioDestinationNode::renderAudio( const std::shared_ptr &destinationBuffer, int numFrames) { - if (numFrames < 0 || !destinationBuffer || !isInitialized_) { + if (numFrames < 0 || !destinationBuffer || !isInitialized_.load(std::memory_order_acquire)) { return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index 1e31c345c..dae5c23a2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -59,7 +59,7 @@ BiquadFilterNode::BiquadFilterNode( x2_.resize(MAX_CHANNEL_COUNT, 0.0f); y1_.resize(MAX_CHANNEL_COUNT, 0.0f); y2_.resize(MAX_CHANNEL_COUNT, 0.0f); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } BiquadFilterType BiquadFilterNode::getType() { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index df8794ad8..e6fca4a07 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -26,7 +26,7 @@ ConvolverNode::ConvolverNode( buffer_(nullptr), internalBuffer_(nullptr) { setBuffer(options.buffer); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } bool ConvolverNode::getNormalize_() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 2b54e4656..bd0cb8a0a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -19,7 +19,7 @@ DelayNode::DelayNode(const std::shared_ptr &context, const Del 1), // +1 to enable delayTime equal to maxDelayTime channelCount_, context->getSampleRate())) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr DelayNode::getDelayTimeParam() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp index 69bbf4c9c..48687bc42 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp @@ -16,7 +16,7 @@ GainNode::GainNode(const std::shared_ptr &context, const GainO MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context)) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr GainNode::getGainParam() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 079c066b5..b9659889c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -64,7 +64,7 @@ IIRFilterNode::IIRFilterNode( feedback_[0] = 1.0f; } - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } // Compute Z-transform of the filter diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp index a0bade59c..7846b07cd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.cpp @@ -15,7 +15,7 @@ StereoPannerNode::StereoPannerNode( const StereoPannerOptions &options) : AudioNode(context, options), panParam_(std::make_shared(options.pan, -1.0f, 1.0f, context)) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr StereoPannerNode::getPanParam() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index 7ae64471c..02621b1be 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -21,7 +21,7 @@ WaveShaperNode::WaveShaperNode( waveShapers_.emplace_back(std::make_unique(nullptr)); } setCurve(options.curve); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } OverSampleType WaveShaperNode::getOversample() const { @@ -54,10 +54,6 @@ void WaveShaperNode::setCurve(const std::shared_ptr &curve) { std::shared_ptr WaveShaperNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - if (!isInitialized_) { - return processingBuffer; - } - std::unique_lock lock(mutex_, std::try_to_lock); if (!lock.owns_lock()) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index cac5c720a..0e2ec98c8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -18,7 +18,8 @@ WorkletNode::WorkletNode( bufferLength_(bufferLength), inputChannelCount_(inputChannelCount), curBuffIndex_(0) { - isInitialized_ = true; + + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr WorkletNode::processNode( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index f1bb4c642..683a34df4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -18,7 +18,8 @@ WorkletProcessingNode::WorkletProcessingNode( inputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); } - isInitialized_ = true; + + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr WorkletProcessingNode::processNode( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index dbee512b9..363632046 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -35,7 +35,7 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( tailBuffer_->zero(); } - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } AudioBufferQueueSourceNode::~AudioBufferQueueSourceNode() { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index a2a9ba4c0..41625cbfa 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -24,7 +24,7 @@ AudioBufferSourceNode::AudioBufferSourceNode( buffer_ = std::shared_ptr(options.buffer); alignedBuffer_ = std::shared_ptr(nullptr); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } AudioBufferSourceNode::~AudioBufferSourceNode() { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp index 85c2c4464..e7935f45b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp @@ -77,12 +77,6 @@ void AudioScheduledSourceNode::updatePlaybackInfo( size_t &nonSilentFramesToProcess, float sampleRate, size_t currentSampleFrame) { - if (!isInitialized_) { - startOffset = 0; - nonSilentFramesToProcess = 0; - return; - } - auto firstFrame = currentSampleFrame; size_t lastFrame = firstFrame + framesToProcess - 1; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp index 2516d1b4d..e55fdfaf1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp @@ -13,7 +13,7 @@ ConstantSourceNode::ConstantSourceNode( : AudioScheduledSourceNode(context) { offsetParam_ = std::make_shared( options.offset, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr ConstantSourceNode::getOffsetParam() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index 62ef6bd8a..4819d8468 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -29,7 +29,7 @@ OscillatorNode::OscillatorNode( audioBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context->getSampleRate()); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr OscillatorNode::getFrequencyParam() const { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index fe8acfff4..f24e3e488 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -13,12 +13,12 @@ RecorderAdapterNode::RecorderAdapterNode(const std::shared_ptr : AudioNode(context, AudioScheduledSourceNodeOptions()) { // It should be marked as initialized only after it is connected to the // recorder. Internal buffer size is based on the recorder's buffer length. - isInitialized_ = false; + isInitialized_.store(false, std::memory_order_release); } void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { std::shared_ptr context = context_.lock(); - if (isInitialized_ || context == nullptr) { + if (isInitialized_.load(std::memory_order_acquire) || context == nullptr) { return; } @@ -43,11 +43,11 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // A lot of words for a couple of lines of implementation :shrug: adapterOutputBuffer_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } void RecorderAdapterNode::cleanup() { - isInitialized_ = false; + isInitialized_.store(false, std::memory_order_release); buff_.clear(); adapterOutputBuffer_.reset(); } @@ -55,11 +55,6 @@ void RecorderAdapterNode::cleanup() { std::shared_ptr RecorderAdapterNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - if (!isInitialized_) { - processingBuffer->zero(); - return processingBuffer; - } - readFrames(framesToProcess); processingBuffer->sum(*adapterOutputBuffer_, ChannelInterpretation::SPEAKERS); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp index 09d9450a8..bfaf57555 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp @@ -9,14 +9,14 @@ WorkletSourceNode::WorkletSourceNode( const std::shared_ptr &context, WorkletsRunner &&workletRunner) : AudioScheduledSourceNode(context), workletRunner_(std::move(workletRunner)) { - isInitialized_ = true; - // Prepare buffers for audio processing size_t outputChannelCount = this->getChannelCount(); outputBuffsHandles_.resize(outputChannelCount); for (size_t i = 0; i < outputChannelCount; ++i) { outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); } + + isInitialized_.store(true, std::memory_order_release); } std::shared_ptr WorkletSourceNode::processNode( diff --git a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp index f8ce75501..ed0c9beb5 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/AudioScheduledSourceTest.cpp @@ -29,7 +29,7 @@ class TestableAudioScheduledSourceNode : public AudioScheduledSourceNode { public: explicit TestableAudioScheduledSourceNode(std::shared_ptr context) : AudioScheduledSourceNode(context) { - isInitialized_ = true; + isInitialized_.store(true, std::memory_order_release); } void updatePlaybackInfo( From 03e2d0784c6b8399badf25dcccbc4b23e4a4a7ba Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 16:50:37 +0100 Subject: [PATCH 35/73] refactor: streamer node --- .../src/examples/Streaming/Streaming.tsx | 5 +- .../sources/StreamerNodeHostObject.cpp | 36 ------ .../sources/StreamerNodeHostObject.h | 8 +- .../audioapi/core/sources/StreamerNode.cpp | 117 +++++++++--------- .../cpp/audioapi/core/sources/StreamerNode.h | 15 +-- .../src/core/BaseAudioContext.ts | 4 +- .../src/core/StreamerNode.ts | 31 +---- .../react-native-audio-api/src/interfaces.ts | 7 +- .../react-native-audio-api/src/mock/index.ts | 28 +---- packages/react-native-audio-api/src/types.ts | 2 +- 10 files changed, 87 insertions(+), 166 deletions(-) delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp diff --git a/apps/common-app/src/examples/Streaming/Streaming.tsx b/apps/common-app/src/examples/Streaming/Streaming.tsx index b83009139..ab0393004 100644 --- a/apps/common-app/src/examples/Streaming/Streaming.tsx +++ b/apps/common-app/src/examples/Streaming/Streaming.tsx @@ -30,11 +30,8 @@ const Streaming: FC = () => { console.error('StreamerNode is already initialized'); return; } - streamerRef.current = aCtxRef.current.createStreamer(); + streamerRef.current = aCtxRef.current.createStreamer('https://liveradio.timesa.pl/2980-1.aac/playlist.m3u8'); - streamerRef.current.initialize( - 'https://liveradio.timesa.pl/2980-1.aac/playlist.m3u8' - ); streamerRef.current.connect(gainRef.current); gainRef.current.connect(aCtxRef.current.destination); streamerRef.current.start(aCtxRef.current.currentTime); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp deleted file mode 100644 index 90ca56353..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -namespace audioapi { - -StreamerNodeHostObject::StreamerNodeHostObject( - const std::shared_ptr &context, - const StreamerOptions &options) - : AudioScheduledSourceNodeHostObject(context->createStreamer(options), options) { - addFunctions(JSI_EXPORT_FUNCTION(StreamerNodeHostObject, initialize)); - addGetters(JSI_EXPORT_PROPERTY_GETTER(StreamerNodeHostObject, streamPath)); -} - -JSI_PROPERTY_GETTER_IMPL(StreamerNodeHostObject, streamPath) { - auto streamerNode = std::static_pointer_cast(node_); - return jsi::String::createFromUtf8(runtime, streamerNode->getStreamPath()); -} - -JSI_HOST_FUNCTION_IMPL(StreamerNodeHostObject, initialize) { -#if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamerNode = std::static_pointer_cast(node_); - auto path = args[0].getString(runtime).utf8(runtime); - auto result = streamerNode->initialize(path); - return {result}; -#else - return false; -#endif -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h index 3f2c09f69..6d37195a8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h @@ -1,6 +1,9 @@ #pragma once #include +#include +#include +#include #include #include @@ -16,15 +19,12 @@ class StreamerNodeHostObject : public AudioScheduledSourceNodeHostObject { public: explicit StreamerNodeHostObject( const std::shared_ptr &context, - const StreamerOptions &options); + const StreamerOptions &options) : AudioScheduledSourceNodeHostObject(context->createStreamer(options), options) {} [[nodiscard]] static inline size_t getSizeInBytes() { return SIZE; } - JSI_PROPERTY_GETTER_DECL(streamPath); - JSI_HOST_FUNCTION_DECL(initialize); - private: static constexpr size_t SIZE = 4'000'000; // 4MB }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp index bf8c60729..d54c32506 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.cpp @@ -8,10 +8,10 @@ * FFmpeg, you must comply with the terms of the LGPL for FFmpeg itself. */ -#include #include #include #include +#include #include #include #include @@ -38,7 +38,11 @@ StreamerNode::StreamerNode( bufferedAudioBufferSize_(0), audio_stream_index_(-1), maxResampledSamples_(0), - processedSamples_(0) {} + processedSamples_(0) { +#if !RN_AUDIO_API_FFMPEG_DISABLED + initialize(options.streamPath); +#endif // RN_AUDIO_API_FFMPEG_DISABLED +} #else StreamerNode::StreamerNode( const std::shared_ptr &context, @@ -52,60 +56,6 @@ StreamerNode::~StreamerNode() { #endif // RN_AUDIO_API_FFMPEG_DISABLED } -bool StreamerNode::initialize(const std::string &input_url) { -#if !RN_AUDIO_API_FFMPEG_DISABLED - streamPath_ = input_url; - std::shared_ptr context = context_.lock(); - if (context == nullptr) { - return false; - } - - if (isInitialized_) { - cleanup(); - } - - if (!openInput(input_url)) { - if (VERBOSE) - printf("Failed to open input\n"); - return false; - } - - if (!findAudioStream() || !setupDecoder() || !setupResampler(context->getSampleRate())) { - if (VERBOSE) - printf("Failed to find/setup audio stream\n"); - cleanup(); - return false; - } - - pkt_ = av_packet_alloc(); - frame_ = av_frame_alloc(); - - if (pkt_ == nullptr || frame_ == nullptr) { - if (VERBOSE) - printf("Failed to allocate packet or frame\n"); - cleanup(); - return false; - } - - channelCount_ = codecpar_->ch_layout.nb_channels; - audioBuffer_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); - - auto [sender, receiver] = channels::spsc::channel< - StreamingData, - STREAMER_NODE_SPSC_OVERFLOW_STRATEGY, - STREAMER_NODE_SPSC_WAIT_STRATEGY>(CHANNEL_CAPACITY); - sender_ = std::move(sender); - receiver_ = std::move(receiver); - - streamingThread_ = std::thread(&StreamerNode::streamAudio, this); - isInitialized_ = true; - return true; -#else - return false; -#endif // RN_AUDIO_API_FFMPEG_DISABLED -} - std::shared_ptr StreamerNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { @@ -150,7 +100,8 @@ std::shared_ptr StreamerNode::processNode( } } if (bufferedAudioBuffer_ != nullptr) { - processingBuffer->copy(*bufferedAudioBuffer_, processedSamples_, alreadyProcessed, framesToProcess); + processingBuffer->copy( + *bufferedAudioBuffer_, processedSamples_, alreadyProcessed, framesToProcess); processedSamples_ += framesToProcess; } #endif // RN_AUDIO_API_FFMPEG_DISABLED @@ -159,6 +110,56 @@ std::shared_ptr StreamerNode::processNode( } #if !RN_AUDIO_API_FFMPEG_DISABLED +bool StreamerNode::initialize(const std::string &input_url) { + streamPath_ = input_url; + std::shared_ptr context = context_.lock(); + if (context == nullptr) { + return false; + } + + if (isInitialized_.load(std::memory_order_acquire)) { + return false; + } + + if (!openInput(input_url)) { + if (VERBOSE) + printf("Failed to open input\n"); + return false; + } + + if (!findAudioStream() || !setupDecoder() || !setupResampler(context->getSampleRate())) { + if (VERBOSE) + printf("Failed to find/setup audio stream\n"); + cleanup(); + return false; + } + + pkt_ = av_packet_alloc(); + frame_ = av_frame_alloc(); + + if (pkt_ == nullptr || frame_ == nullptr) { + if (VERBOSE) + printf("Failed to allocate packet or frame\n"); + cleanup(); + return false; + } + + channelCount_ = codecpar_->ch_layout.nb_channels; + audioBuffer_ = + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + + auto [sender, receiver] = channels::spsc::channel< + StreamingData, + STREAMER_NODE_SPSC_OVERFLOW_STRATEGY, + STREAMER_NODE_SPSC_WAIT_STRATEGY>(CHANNEL_CAPACITY); + sender_ = std::move(sender); + receiver_ = std::move(receiver); + + streamingThread_ = std::thread(&StreamerNode::streamAudio, this); + isInitialized_.store(true, std::memory_order_release); + return true; +} + bool StreamerNode::setupResampler(float outSampleRate) { // Allocate resampler context swrCtx_ = swr_alloc(); @@ -346,10 +347,10 @@ void StreamerNode::cleanup() { } audio_stream_index_ = -1; - isInitialized_ = false; decoder_ = nullptr; codecpar_ = nullptr; maxResampledSamples_ = 0; + isInitialized_.store(false, std::memory_order_release); } #endif // RN_AUDIO_API_FFMPEG_DISABLED } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h index 45de420b2..842a00538 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/StreamerNode.h @@ -68,15 +68,6 @@ class StreamerNode : public AudioScheduledSourceNode { const StreamerOptions &options); ~StreamerNode() override; - /** - * @brief Initialize all necessary ffmpeg components for streaming audio - */ - bool initialize(const std::string &inputUrl); - - std::string getStreamPath() const { - return streamPath_; - } - protected: std::shared_ptr processNode( const std::shared_ptr &processingBuffer, @@ -113,6 +104,12 @@ class StreamerNode : public AudioScheduledSourceNode { STREAMER_NODE_SPSC_WAIT_STRATEGY> receiver_; + /// @brief Initialize the StreamerNode by opening the input stream, + /// finding the audio stream, setting up the decoder, and starting the streaming thread. + /// @param inputUrl The URL of the input stream + /// @return true if initialization was successful, false otherwise + bool initialize(const std::string &inputUrl); + /** * @brief Setting up the resampler * @param outSampleRate Sample rate for the output audio diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 452dcbe85..55d354e4e 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -130,8 +130,8 @@ export default class BaseAudioContext { return new OscillatorNode(this); } - createStreamer(): StreamerNode { - return new StreamerNode(this); + createStreamer(streamPath: string): StreamerNode { + return new StreamerNode(this, { streamPath }); } createConstantSource(): ConstantSourceNode { diff --git a/packages/react-native-audio-api/src/core/StreamerNode.ts b/packages/react-native-audio-api/src/core/StreamerNode.ts index 8e82d0ee7..76193ebbf 100644 --- a/packages/react-native-audio-api/src/core/StreamerNode.ts +++ b/packages/react-native-audio-api/src/core/StreamerNode.ts @@ -1,36 +1,17 @@ -import { IStreamerNode } from '../interfaces'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import { TStreamerOptions } from '../types'; -import { InvalidStateError, NotSupportedError } from '../errors'; +import { NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; export default class StreamerNode extends AudioScheduledSourceNode { - private hasBeenSetup: boolean = false; - constructor(context: BaseAudioContext, options?: TStreamerOptions) { - const node = context.context.createStreamer(options || {}); + readonly streamPath: string; + + constructor(context: BaseAudioContext, options: TStreamerOptions) { + const node = context.context.createStreamer(options); if (!node) { throw new NotSupportedError('StreamerNode requires FFmpeg build'); } super(context, node); - if (options?.streamPath) { - if (this.initialize(options.streamPath)) { - this.hasBeenSetup = true; - } - } - } - - public initialize(streamPath: string): boolean { - if (this.hasBeenSetup) { - throw new InvalidStateError('Node is already setup'); - } - const res = (this.node as IStreamerNode).initialize(streamPath); - if (res) { - this.hasBeenSetup = true; - } - return res; - } - - public get streamPath(): string { - return (this.node as IStreamerNode).streamPath; + this.streamPath = options.streamPath; } } diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 6c8b49b1d..57ee64218 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -101,7 +101,7 @@ export interface IBaseAudioContext { ) => IPeriodicWave; createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; createConvolver: (convolverOptions?: TConvolverOptions) => IConvolverNode; - createStreamer: (streamerOptions?: TStreamerOptions) => IStreamerNode | null; // null when FFmpeg is not enabled + createStreamer: (streamerOptions: TStreamerOptions) => IStreamerNode | null; // null when FFmpeg is not enabled createWaveShaper: (waveShaperOptions?: TWaveShaperOptions) => IWaveShaperNode; } @@ -195,10 +195,7 @@ export interface IOscillatorNode extends IAudioScheduledSourceNode { setPeriodicWave(periodicWave: IPeriodicWave): void; } -export interface IStreamerNode extends IAudioNode { - readonly streamPath: string; - initialize(streamPath: string): boolean; -} +export interface IStreamerNode extends IAudioNode {} export interface IConstantSourceNode extends IAudioScheduledSourceNode { readonly offset: IAudioParam; diff --git a/packages/react-native-audio-api/src/mock/index.ts b/packages/react-native-audio-api/src/mock/index.ts index 62554fdd9..a6bc90d48 100644 --- a/packages/react-native-audio-api/src/mock/index.ts +++ b/packages/react-native-audio-api/src/mock/index.ts @@ -461,27 +461,11 @@ class AudioBufferQueueSourceNodeMock extends AudioScheduledSourceNodeMock { } class StreamerNodeMock extends AudioScheduledSourceNodeMock { - private hasBeenSetup: boolean = false; - private _streamPath: string = ''; + readonly streamPath: string = ''; - constructor(context: BaseAudioContextMock, options?: TStreamerOptions) { - super(context, {}); - if (options?.streamPath) { - this.initialize(options.streamPath); - } - } - - initialize(streamPath: string): boolean { - if (this.hasBeenSetup) { - throw new Error('Node is already setup'); - } - this._streamPath = streamPath; - this.hasBeenSetup = true; - return true; - } - - get streamPath(): string { - return this._streamPath; + constructor(context: BaseAudioContextMock, options: TStreamerOptions) { + super(context, options); + this.streamPath = options.streamPath; } pause(): void {} @@ -659,8 +643,8 @@ class BaseAudioContextMock { return new AudioBufferQueueSourceNodeMock(this, options); } - createStreamer(options?: TStreamerOptions): StreamerNodeMock { - return new StreamerNodeMock(this, options); + createStreamer(streamPath: string): StreamerNodeMock { + return new StreamerNodeMock(this, { streamPath }); } createWorkletNode( diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 3383c32e7..a750f37a6 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -178,7 +178,7 @@ export interface TConstantSourceOptions { } export interface TStreamerOptions { - streamPath?: string; + streamPath: string; } export interface TPeriodicWaveConstraints { From a3547b18253816e1a055275d3fb1df0d91259fca Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 17:07:13 +0100 Subject: [PATCH 36/73] refactor: oscillator and constant source node --- .../sources/ConstantSourceNodeHostObject.cpp | 5 +-- .../sources/ConstantSourceNodeHostObject.h | 4 +++ .../sources/OscillatorNodeHostObject.cpp | 33 ++++++++++++------- .../sources/OscillatorNodeHostObject.h | 6 ++++ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp index 57ec5e828..6f024488f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp @@ -11,12 +11,13 @@ ConstantSourceNodeHostObject::ConstantSourceNodeHostObject( const std::shared_ptr &context, const ConstantSourceOptions &options) : AudioScheduledSourceNodeHostObject(context->createConstantSource(options), options) { + auto constantSourceNode = std::static_pointer_cast(node_); + offsetParam_ = std::make_shared(constantSourceNode->getOffsetParam()); + addGetters(JSI_EXPORT_PROPERTY_GETTER(ConstantSourceNodeHostObject, offset)); } JSI_PROPERTY_GETTER_IMPL(ConstantSourceNodeHostObject, offset) { - auto constantSourceNode = std::static_pointer_cast(node_); - auto offsetParam_ = std::make_shared(constantSourceNode->getOffsetParam()); return jsi::Object::createFromHostObject(runtime, offsetParam_); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h index 8cb158d66..966966a56 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h @@ -11,6 +11,7 @@ using namespace facebook; struct ConstantSourceOptions; class BaseAudioContext; +class AudioParamHostObject; class ConstantSourceNodeHostObject : public AudioScheduledSourceNodeHostObject { public: @@ -19,5 +20,8 @@ class ConstantSourceNodeHostObject : public AudioScheduledSourceNodeHostObject { const ConstantSourceOptions &options); JSI_PROPERTY_GETTER_DECL(offset); + + private: + std::shared_ptr offsetParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp index 0dfd11326..e626b2a46 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp @@ -7,13 +7,19 @@ #include #include #include +#include namespace audioapi { OscillatorNodeHostObject::OscillatorNodeHostObject( const std::shared_ptr &context, const OscillatorOptions &options) - : AudioScheduledSourceNodeHostObject(context->createOscillator(options), options) { + : AudioScheduledSourceNodeHostObject(context->createOscillator(options), options), type_(options.type) { + auto oscillatorNode = std::static_pointer_cast(node_); + frequencyParam_ = + std::make_shared(oscillatorNode->getFrequencyParam()); + detuneParam_ = std::make_shared(oscillatorNode->getDetuneParam()); + addGetters( JSI_EXPORT_PROPERTY_GETTER(OscillatorNodeHostObject, frequency), JSI_EXPORT_PROPERTY_GETTER(OscillatorNodeHostObject, detune), @@ -25,35 +31,38 @@ OscillatorNodeHostObject::OscillatorNodeHostObject( } JSI_PROPERTY_GETTER_IMPL(OscillatorNodeHostObject, frequency) { - auto oscillatorNode = std::static_pointer_cast(node_); - auto frequencyParam_ = - std::make_shared(oscillatorNode->getFrequencyParam()); return jsi::Object::createFromHostObject(runtime, frequencyParam_); } JSI_PROPERTY_GETTER_IMPL(OscillatorNodeHostObject, detune) { - auto oscillatorNode = std::static_pointer_cast(node_); - auto detuneParam_ = std::make_shared(oscillatorNode->getDetuneParam()); return jsi::Object::createFromHostObject(runtime, detuneParam_); } JSI_PROPERTY_GETTER_IMPL(OscillatorNodeHostObject, type) { - auto oscillatorNode = std::static_pointer_cast(node_); - auto waveType = oscillatorNode->getType(); - return jsi::String::createFromUtf8(runtime, js_enum_parser::oscillatorTypeToString(waveType)); + return jsi::String::createFromUtf8(runtime, js_enum_parser::oscillatorTypeToString(type_)); } JSI_HOST_FUNCTION_IMPL(OscillatorNodeHostObject, setPeriodicWave) { auto oscillatorNode = std::static_pointer_cast(node_); auto periodicWave = args[0].getObject(runtime).getHostObject(runtime); - oscillatorNode->setPeriodicWave(periodicWave->periodicWave_); + + auto event = [oscillatorNode, periodicWave = periodicWave->periodicWave_](BaseAudioContext &) { + oscillatorNode->setPeriodicWave(periodicWave); + }; + oscillatorNode->scheduleAudioEvent(std::move(event)); + return jsi::Value::undefined(); } JSI_PROPERTY_SETTER_IMPL(OscillatorNodeHostObject, type) { auto oscillatorNode = std::static_pointer_cast(node_); - auto type = value.asString(runtime).utf8(runtime); - oscillatorNode->setType(js_enum_parser::oscillatorTypeFromString(type)); + + auto event = [oscillatorNode, type = value.asString(runtime).utf8(runtime)](BaseAudioContext &) { + oscillatorNode->setType(js_enum_parser::oscillatorTypeFromString(type)); + }; + type_ = js_enum_parser::oscillatorTypeFromString(value.asString(runtime).utf8(runtime)); + + oscillatorNode->scheduleAudioEvent(std::move(event)); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h index 7f6042ea1..039b1b0fc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h @@ -12,6 +12,7 @@ using namespace facebook; struct OscillatorOptions; class BaseAudioContext; +class AudioParamHostObject; class OscillatorNodeHostObject : public AudioScheduledSourceNodeHostObject { public: @@ -26,5 +27,10 @@ class OscillatorNodeHostObject : public AudioScheduledSourceNodeHostObject { JSI_HOST_FUNCTION_DECL(setPeriodicWave); JSI_PROPERTY_SETTER_DECL(type); + + private: + std::shared_ptr frequencyParam_; + std::shared_ptr detuneParam_; + OscillatorType type_; }; } // namespace audioapi From 14b377e1bff8b6fef4bbc515569dcdc5e614ad33 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 17:15:58 +0100 Subject: [PATCH 37/73] refactor: buffer base source node --- .../AudioBufferBaseSourceNodeHostObject.cpp | 63 +++++++++++-------- .../AudioBufferBaseSourceNodeHostObject.h | 14 +++++ .../sources/AudioBufferBaseSourceNode.cpp | 20 +++--- .../core/sources/AudioBufferBaseSourceNode.h | 9 +-- 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp index 9f503d682..d421d980d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp @@ -1,16 +1,24 @@ #include - #include #include #include + #include +#include namespace audioapi { AudioBufferBaseSourceNodeHostObject::AudioBufferBaseSourceNodeHostObject( const std::shared_ptr &node, const BaseAudioBufferSourceOptions &options) - : AudioScheduledSourceNodeHostObject(node, options) { + : AudioScheduledSourceNodeHostObject(node, options), pitchCorrection_(options.pitchCorrection) { + auto sourceNode = std::static_pointer_cast(node_); + detuneParam_ = std::make_shared(sourceNode->getDetuneParam()); + playbackRateParam_ = std::make_shared(sourceNode->getPlaybackRateParam()); + onPositionChangedInterval_ = sourceNode->getOnPositionChangedInterval(); + inputLatency_ = sourceNode->getInputLatency(); + outputLatency_ = sourceNode->getOutputLatency(); + addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, detune), JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, playbackRate), @@ -26,55 +34,60 @@ AudioBufferBaseSourceNodeHostObject::AudioBufferBaseSourceNodeHostObject( } AudioBufferBaseSourceNodeHostObject::~AudioBufferBaseSourceNodeHostObject() { - auto sourceNode = std::static_pointer_cast(node_); - // When JSI object is garbage collected (together with the eventual callback), // underlying source node might still be active and try to call the // non-existing callback. - sourceNode->setOnPositionChangedCallbackId(0); + setOnPositionChangedCallbackId(0); } JSI_PROPERTY_GETTER_IMPL(AudioBufferBaseSourceNodeHostObject, detune) { - auto sourceNode = std::static_pointer_cast(node_); - auto detune = sourceNode->getDetuneParam(); - auto detuneHostObject = std::make_shared(detune); - return jsi::Object::createFromHostObject(runtime, detuneHostObject); + return jsi::Object::createFromHostObject(runtime, detuneParam_); } JSI_PROPERTY_GETTER_IMPL(AudioBufferBaseSourceNodeHostObject, playbackRate) { - auto sourceNode = std::static_pointer_cast(node_); - auto playbackRate = sourceNode->getPlaybackRateParam(); - auto playbackRateHostObject = std::make_shared(playbackRate); - return jsi::Object::createFromHostObject(runtime, playbackRateHostObject); + return jsi::Object::createFromHostObject(runtime, playbackRateParam_); } JSI_PROPERTY_GETTER_IMPL(AudioBufferBaseSourceNodeHostObject, onPositionChangedInterval) { - auto sourceNode = std::static_pointer_cast(node_); - return {sourceNode->getOnPositionChangedInterval()}; + return {onPositionChangedInterval_}; } JSI_PROPERTY_SETTER_IMPL(AudioBufferBaseSourceNodeHostObject, onPositionChanged) { - auto sourceNode = std::static_pointer_cast(node_); - - sourceNode->setOnPositionChangedCallbackId(std::stoull(value.getString(runtime).utf8(runtime))); + auto callbackId = std::stoull(value.getString(runtime).utf8(runtime)); + setOnPositionChangedCallbackId(callbackId); } JSI_PROPERTY_SETTER_IMPL(AudioBufferBaseSourceNodeHostObject, onPositionChangedInterval) { - auto sourceNode = std::static_pointer_cast(node_); + auto sourceNode = std::static_pointer_cast(node_); + auto interval = static_cast(value.getNumber()); + + sourceNode->setOnPositionChangedInterval(static_cast(value.getNumber())); + auto event = [sourceNode, interval](BaseAudioContext &) { + sourceNode->setOnPositionChangedInterval(interval); + }; - sourceNode->setOnPositionChangedInterval(static_cast(value.getNumber())); + sourceNode->scheduleAudioEvent(std::move(event)); + onPositionChangedInterval_ = interval; } JSI_HOST_FUNCTION_IMPL(AudioBufferBaseSourceNodeHostObject, getInputLatency) { - auto audioBufferBaseSourceNode = std::static_pointer_cast(node_); - - return audioBufferBaseSourceNode->getInputLatency(); + return {inputLatency_}; } JSI_HOST_FUNCTION_IMPL(AudioBufferBaseSourceNodeHostObject, getOutputLatency) { - auto audioBufferBaseSourceNode = std::static_pointer_cast(node_); + return {outputLatency_}; +} + +void AudioBufferBaseSourceNodeHostObject::setOnPositionChangedCallbackId(uint64_t callbackId) { + auto sourceNode = std::static_pointer_cast(node_); + + auto event = [sourceNode, callbackId](BaseAudioContext &) { + sourceNode->setOnPositionChangedCallbackId(callbackId); + }; - return audioBufferBaseSourceNode->getOutputLatency(); + sourceNode->unregisterOnPositionChangedCallback(onPositionChangedCallbackId_); + sourceNode->scheduleAudioEvent(std::move(event)); + onPositionChangedCallbackId_ = callbackId; } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h index f65c39a14..4a215b3de 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h @@ -10,6 +10,7 @@ using namespace facebook; class AudioBufferBaseSourceNode; struct BaseAudioBufferSourceOptions; +class AudioParamHostObject; class AudioBufferBaseSourceNodeHostObject : public AudioScheduledSourceNodeHostObject { public: @@ -28,6 +29,19 @@ class AudioBufferBaseSourceNodeHostObject : public AudioScheduledSourceNodeHostO JSI_HOST_FUNCTION_DECL(getInputLatency); JSI_HOST_FUNCTION_DECL(getOutputLatency); + +protected: + std::shared_ptr detuneParam_; + std::shared_ptr playbackRateParam_; + + int onPositionChangedInterval_; + uint64_t onPositionChangedCallbackId_ = 0; + + double inputLatency_; + double outputLatency_; + bool pitchCorrection_; + + void setOnPositionChangedCallbackId(uint64_t callbackId); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index f90b8195d..aacba4c37 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -39,11 +39,7 @@ std::shared_ptr AudioBufferBaseSourceNode::getPlaybackRateParam() co } void AudioBufferBaseSourceNode::setOnPositionChangedCallbackId(uint64_t callbackId) { - auto oldCallbackId = onPositionChangedCallbackId_.exchange(callbackId, std::memory_order_acq_rel); - - if (oldCallbackId != 0) { - audioEventHandlerRegistry_->unregisterHandler(AudioEvent::POSITION_CHANGED, oldCallbackId); - } + onPositionChangedCallbackId_ = callbackId; } void AudioBufferBaseSourceNode::setOnPositionChangedInterval(int interval) { @@ -57,10 +53,6 @@ int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const { return onPositionChangedInterval_; } -std::mutex &AudioBufferBaseSourceNode::getBufferLock() { - return bufferLock_; -} - double AudioBufferBaseSourceNode::getInputLatency() const { if (pitchCorrection_) { if (std::shared_ptr context = context_.lock()) { @@ -83,14 +75,16 @@ double AudioBufferBaseSourceNode::getOutputLatency() const { return 0; } -void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() { - auto onPositionChangedCallbackId = onPositionChangedCallbackId_.load(std::memory_order_acquire); +void AudioBufferBaseSourceNode::unregisterOnPositionChangedCallback(uint64_t callbackId) { + audioEventHandlerRegistry_->unregisterHandler(AudioEvent::POSITION_CHANGED, callbackId); +} - if (onPositionChangedCallbackId != 0 && onPositionChangedTime_ > onPositionChangedInterval_) { +void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() { + if (onPositionChangedCallbackId_ != 0 && onPositionChangedTime_ > onPositionChangedInterval_) { std::unordered_map body = {{"value", getCurrentPosition()}}; audioEventHandlerRegistry_->invokeHandlerWithEventBody( - AudioEvent::POSITION_CHANGED, onPositionChangedCallbackId, body); + AudioEvent::POSITION_CHANGED, onPositionChangedCallbackId_, body); onPositionChangedTime_ = 0; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 139594fdb..f9b75c8f2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -28,12 +28,14 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { [[nodiscard]] double getInputLatency() const; [[nodiscard]] double getOutputLatency() const; + /// @note JS Thread only. + /// Thread safe, because does not access state of the node. + void unregisterOnPositionChangedCallback(uint64_t callbackId); + protected: // pitch correction bool pitchCorrection_; - std::mutex bufferLock_; - // pitch correction std::shared_ptr> stretch_; std::shared_ptr playbackRateBuffer_; @@ -45,11 +47,10 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { // internal helper double vReadIndex_; - std::atomic onPositionChangedCallbackId_ = 0; // 0 means no callback + uint64_t onPositionChangedCallbackId_ = 0; // 0 means no callback int onPositionChangedInterval_; int onPositionChangedTime_ = 0; - std::mutex &getBufferLock(); virtual double getCurrentPosition() const = 0; void sendOnPositionChangedEvent(); From 269f56ab7caac7517b9c9174a8ab9b809650c024 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 17:18:37 +0100 Subject: [PATCH 38/73] fix: nitpicks --- .../AudioBufferBaseSourceNodeHostObject.h | 18 ++++++------ .../sources/OscillatorNodeHostObject.cpp | 7 +++-- .../sources/StreamerNodeHostObject.h | 5 ++-- .../core/sources/AudioBufferBaseSourceNode.h | 2 +- .../sources/AudioBufferQueueSourceNode.cpp | 29 +++++++------------ .../core/sources/AudioBufferSourceNode.cpp | 7 ----- .../core/sources/RecorderAdapterNode.h | 1 - 7 files changed, 27 insertions(+), 42 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h index 4a215b3de..6abb93d85 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h @@ -30,18 +30,18 @@ class AudioBufferBaseSourceNodeHostObject : public AudioScheduledSourceNodeHostO JSI_HOST_FUNCTION_DECL(getInputLatency); JSI_HOST_FUNCTION_DECL(getOutputLatency); -protected: - std::shared_ptr detuneParam_; - std::shared_ptr playbackRateParam_; + protected: + std::shared_ptr detuneParam_; + std::shared_ptr playbackRateParam_; - int onPositionChangedInterval_; - uint64_t onPositionChangedCallbackId_ = 0; + int onPositionChangedInterval_; + uint64_t onPositionChangedCallbackId_ = 0; - double inputLatency_; - double outputLatency_; - bool pitchCorrection_; + double inputLatency_; + double outputLatency_; + bool pitchCorrection_; - void setOnPositionChangedCallbackId(uint64_t callbackId); + void setOnPositionChangedCallbackId(uint64_t callbackId); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp index e626b2a46..838cd77de 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp @@ -56,11 +56,12 @@ JSI_HOST_FUNCTION_IMPL(OscillatorNodeHostObject, setPeriodicWave) { JSI_PROPERTY_SETTER_IMPL(OscillatorNodeHostObject, type) { auto oscillatorNode = std::static_pointer_cast(node_); + auto type = js_enum_parser::oscillatorTypeFromString(value.asString(runtime).utf8(runtime)); - auto event = [oscillatorNode, type = value.asString(runtime).utf8(runtime)](BaseAudioContext &) { - oscillatorNode->setType(js_enum_parser::oscillatorTypeFromString(type)); + auto event = [oscillatorNode, type](BaseAudioContext &) { + oscillatorNode->setType(type); }; - type_ = js_enum_parser::oscillatorTypeFromString(value.asString(runtime).utf8(runtime)); + type_ = type; oscillatorNode->scheduleAudioEvent(std::move(event)); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h index 6d37195a8..ad023fd7f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include #include +#include #include #include @@ -19,7 +19,8 @@ class StreamerNodeHostObject : public AudioScheduledSourceNodeHostObject { public: explicit StreamerNodeHostObject( const std::shared_ptr &context, - const StreamerOptions &options) : AudioScheduledSourceNodeHostObject(context->createStreamer(options), options) {} + const StreamerOptions &options) + : AudioScheduledSourceNodeHostObject(context->createStreamer(options), options) {} [[nodiscard]] static inline size_t getSizeInBytes() { return SIZE; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index f9b75c8f2..e34868c61 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -34,7 +34,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { protected: // pitch correction - bool pitchCorrection_; + const bool pitchCorrection_; // pitch correction std::shared_ptr> stretch_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index 363632046..57d4fb6a3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -39,8 +39,6 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( } AudioBufferQueueSourceNode::~AudioBufferQueueSourceNode() { - Locker locker(getBufferLock()); - buffers_ = {}; } @@ -72,7 +70,6 @@ void AudioBufferQueueSourceNode::pause() { } std::string AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr &buffer) { - auto locker = Locker(getBufferLock()); buffers_.emplace(bufferId_, buffer); if (tailBuffer_ != nullptr) { @@ -83,7 +80,6 @@ std::string AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr AudioBufferQueueSourceNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - if (auto locker = Locker::tryLock(getBufferLock())) { - // no audio data to fill, zero the output and return. - if (buffers_.empty()) { - processingBuffer->zero(); - return processingBuffer; - } - - if (!pitchCorrection_) { - processWithoutPitchCorrection(processingBuffer, framesToProcess); - } else { - processWithPitchCorrection(processingBuffer, framesToProcess); - } + // no audio data to fill, zero the output and return. + if (buffers_.empty()) { + processingBuffer->zero(); + return processingBuffer; + } - handleStopScheduled(); + if (!pitchCorrection_) { + processWithoutPitchCorrection(processingBuffer, framesToProcess); } else { - processingBuffer->zero(); + processWithPitchCorrection(processingBuffer, framesToProcess); } + handleStopScheduled(); + return processingBuffer; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 41625cbfa..4f235c221 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -28,8 +28,6 @@ AudioBufferSourceNode::AudioBufferSourceNode( } AudioBufferSourceNode::~AudioBufferSourceNode() { - Locker locker(getBufferLock()); - buffer_.reset(); alignedBuffer_.reset(); } @@ -76,7 +74,6 @@ void AudioBufferSourceNode::setLoopEnd(double loopEnd) { } void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer) { - Locker locker(getBufferLock()); std::shared_ptr context = context_.lock(); if (buffer == nullptr || context == nullptr) { @@ -148,7 +145,6 @@ void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { std::shared_ptr AudioBufferSourceNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - if (auto locker = Locker::tryLock(getBufferLock())) { // No audio data to fill, zero the output and return. if (!alignedBuffer_) { processingBuffer->zero(); @@ -162,9 +158,6 @@ std::shared_ptr AudioBufferSourceNode::processNode( } handleStopScheduled(); - } else { - processingBuffer->zero(); - } return processingBuffer; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h index 0e17b264d..043fa8690 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.h @@ -28,7 +28,6 @@ class RecorderAdapterNode : public AudioNode { void init(size_t bufferSize, int channelCount); void cleanup(); - int channelCount_{}; // TODO: CircularOverflowableAudioBuffer std::vector> buff_; From f7f063e65f2f1e9b5a006d4c294dc07507de02f9 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 17:35:11 +0100 Subject: [PATCH 39/73] refactor: gain, delay, stereo panner nodes --- .../HostObjects/effects/DelayNodeHostObject.cpp | 12 ++++++------ .../HostObjects/effects/DelayNodeHostObject.h | 4 ++++ .../HostObjects/effects/GainNodeHostObject.cpp | 7 ++++--- .../HostObjects/effects/GainNodeHostObject.h | 4 ++++ .../effects/StereoPannerNodeHostObject.cpp | 5 +++-- .../HostObjects/effects/StereoPannerNodeHostObject.h | 4 ++++ 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp index 17929e069..ff605eebe 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp @@ -10,9 +10,15 @@ namespace audioapi { DelayNodeHostObject::DelayNodeHostObject(const std::shared_ptr& context, const DelayOptions &options) : AudioNodeHostObject(context->createDelay(options), options) { + auto delayNode = std::static_pointer_cast(node_); + delayTimeParam_ = std::make_shared(delayNode->getDelayTimeParam()); addGetters(JSI_EXPORT_PROPERTY_GETTER(DelayNodeHostObject, delayTime)); } +JSI_PROPERTY_GETTER_IMPL(DelayNodeHostObject, delayTime) { + return jsi::Object::createFromHostObject(runtime, delayTimeParam_); +} + size_t DelayNodeHostObject::getSizeInBytes() const { auto delayNode = std::static_pointer_cast(node_); auto base = sizeof(float) * delayNode->getDelayTimeParam()->getMaxValue(); @@ -23,10 +29,4 @@ size_t DelayNodeHostObject::getSizeInBytes() const { } } -JSI_PROPERTY_GETTER_IMPL(DelayNodeHostObject, delayTime) { - auto delayNode = std::static_pointer_cast(node_); - auto delayTimeParam = std::make_shared(delayNode->getDelayTimeParam()); - return jsi::Object::createFromHostObject(runtime, delayTimeParam); -} - } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h index 903e08803..671da3c00 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h @@ -10,6 +10,7 @@ using namespace facebook; struct DelayOptions; class BaseAudioContext; +class AudioParamHostObject; class DelayNodeHostObject : public AudioNodeHostObject { public: @@ -20,5 +21,8 @@ class DelayNodeHostObject : public AudioNodeHostObject { [[nodiscard]] size_t getSizeInBytes() const; JSI_PROPERTY_GETTER_DECL(delayTime); + +private: + std::shared_ptr delayTimeParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp index 4f9c040ed..237ec7551 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp @@ -12,13 +12,14 @@ GainNodeHostObject::GainNodeHostObject( const std::shared_ptr &context, const GainOptions &options) : AudioNodeHostObject(context->createGain(options), options) { + auto gainNode = std::static_pointer_cast(node_); + gainParam_ = std::make_shared(gainNode->getGainParam()); + addGetters(JSI_EXPORT_PROPERTY_GETTER(GainNodeHostObject, gain)); } JSI_PROPERTY_GETTER_IMPL(GainNodeHostObject, gain) { - auto gainNode = std::static_pointer_cast(node_); - auto gainParam = std::make_shared(gainNode->getGainParam()); - return jsi::Object::createFromHostObject(runtime, gainParam); + return jsi::Object::createFromHostObject(runtime, gainParam_); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h index 577a84e2e..d1488b3b2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h @@ -10,6 +10,7 @@ using namespace facebook; struct GainOptions; class BaseAudioContext; +class AudioParamHostObject; class GainNodeHostObject : public AudioNodeHostObject { public: @@ -18,5 +19,8 @@ class GainNodeHostObject : public AudioNodeHostObject { const GainOptions &options); JSI_PROPERTY_GETTER_DECL(gain); + +private: + std::shared_ptr gainParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp index 5170659ef..a5f10016d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp @@ -12,12 +12,13 @@ StereoPannerNodeHostObject::StereoPannerNodeHostObject( const std::shared_ptr &context, const StereoPannerOptions &options) : AudioNodeHostObject(context->createStereoPanner(options), options) { + auto stereoPannerNode = std::static_pointer_cast(node_); + panParam_ = std::make_shared(stereoPannerNode->getPanParam()); + addGetters(JSI_EXPORT_PROPERTY_GETTER(StereoPannerNodeHostObject, pan)); } JSI_PROPERTY_GETTER_IMPL(StereoPannerNodeHostObject, pan) { - auto stereoPannerNode = std::static_pointer_cast(node_); - auto panParam_ = std::make_shared(stereoPannerNode->getPanParam()); return jsi::Object::createFromHostObject(runtime, panParam_); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h index 37eaed62c..70af10928 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h @@ -11,6 +11,7 @@ using namespace facebook; struct StereoPannerOptions; class BaseAudioContext; +class AudioParamHostObject; class StereoPannerNodeHostObject : public AudioNodeHostObject { public: @@ -19,5 +20,8 @@ class StereoPannerNodeHostObject : public AudioNodeHostObject { const StereoPannerOptions &options); JSI_PROPERTY_GETTER_DECL(pan); + +private: + std::shared_ptr panParam_; }; } // namespace audioapi From ff82771ef1b56eebde5229997ef3e23016e2ad7e Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 6 Feb 2026 17:35:58 +0100 Subject: [PATCH 40/73] ci: lint --- .../cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h | 4 ++-- .../cpp/audioapi/HostObjects/effects/GainNodeHostObject.h | 4 ++-- .../audioapi/HostObjects/effects/StereoPannerNodeHostObject.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h index 671da3c00..b06806c4c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h @@ -22,7 +22,7 @@ class DelayNodeHostObject : public AudioNodeHostObject { JSI_PROPERTY_GETTER_DECL(delayTime); -private: - std::shared_ptr delayTimeParam_; + private: + std::shared_ptr delayTimeParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h index d1488b3b2..9503e292f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h @@ -20,7 +20,7 @@ class GainNodeHostObject : public AudioNodeHostObject { JSI_PROPERTY_GETTER_DECL(gain); -private: - std::shared_ptr gainParam_; + private: + std::shared_ptr gainParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h index 70af10928..3b8043a56 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h @@ -21,7 +21,7 @@ class StereoPannerNodeHostObject : public AudioNodeHostObject { JSI_PROPERTY_GETTER_DECL(pan); -private: - std::shared_ptr panParam_; + private: + std::shared_ptr panParam_; }; } // namespace audioapi From 60eb11ee45d3095909bad6459b51107510dad0b7 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 11:16:00 +0100 Subject: [PATCH 41/73] refactor: audio buffer source node and related cleanups --- .../effects/DelayNodeHostObject.cpp | 6 +- .../AudioBufferSourceNodeHostObject.cpp | 177 ++++++++++++------ .../sources/AudioBufferSourceNodeHostObject.h | 13 +- .../common/cpp/audioapi/core/AudioNode.h | 8 + .../destinations/AudioDestinationNode.cpp | 6 +- .../cpp/audioapi/core/effects/DelayNode.cpp | 11 +- .../sources/AudioBufferBaseSourceNode.cpp | 18 +- .../core/sources/AudioBufferBaseSourceNode.h | 3 +- .../sources/AudioBufferQueueSourceNode.cpp | 8 +- .../core/sources/AudioBufferSourceNode.cpp | 156 ++++++--------- .../core/sources/AudioBufferSourceNode.h | 20 +- .../core/sources/AudioScheduledSourceNode.h | 3 +- .../core/sources/RecorderAdapterNode.cpp | 5 +- .../audioapi/core/utils/AudioGraphManager.cpp | 2 +- .../audioapi/core/utils/AudioGraphManager.h | 2 +- .../cpp/audioapi/core/utils/Constants.h | 1 + .../common/cpp/audioapi/types/NodeOptions.h | 6 + .../src/core/AudioBufferSourceNode.ts | 25 ++- .../src/web-core/AudioBufferSourceNode.tsx | 7 + 19 files changed, 264 insertions(+), 213 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp index ff605eebe..da7330987 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp @@ -22,11 +22,7 @@ JSI_PROPERTY_GETTER_IMPL(DelayNodeHostObject, delayTime) { size_t DelayNodeHostObject::getSizeInBytes() const { auto delayNode = std::static_pointer_cast(node_); auto base = sizeof(float) * delayNode->getDelayTimeParam()->getMaxValue(); - if (std::shared_ptr context = delayNode->context_.lock()) { - return base * context->getSampleRate(); - } else { - return base * 44100; // Fallback to common sample rate - } + return base * delayNode->getContextSampleRate(); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp index ac23adb1d..9525550cc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp @@ -1,21 +1,27 @@ #include +#include #include -#include #include #include +#include +#include #include +#include namespace audioapi { AudioBufferSourceNodeHostObject::AudioBufferSourceNodeHostObject( const std::shared_ptr &context, const AudioBufferSourceOptions &options) - : AudioBufferBaseSourceNodeHostObject(context->createBufferSource(options), options) { + : AudioBufferBaseSourceNodeHostObject(context->createBufferSource(options), options), + loop_(options.loop), + loopSkip_(options.loopSkip), + loopStart_(options.loopStart), + loopEnd_(options.loopEnd) { addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loop), JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopSkip), - JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, buffer), JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopStart), JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopEnd)); @@ -35,93 +41,91 @@ AudioBufferSourceNodeHostObject::AudioBufferSourceNodeHostObject( } AudioBufferSourceNodeHostObject::~AudioBufferSourceNodeHostObject() { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - // When JSI object is garbage collected (together with the eventual callback), // underlying source node might still be active and try to call the // non-existing callback. - audioBufferSourceNode->setOnLoopEndedCallbackId(0); + setOnLoopEndedCallbackId(0); } JSI_PROPERTY_GETTER_IMPL(AudioBufferSourceNodeHostObject, loop) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - auto loop = audioBufferSourceNode->getLoop(); - return {loop}; + return {loop_}; } JSI_PROPERTY_GETTER_IMPL(AudioBufferSourceNodeHostObject, loopSkip) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - auto loopSkip = audioBufferSourceNode->getLoopSkip(); - return {loopSkip}; -} - -JSI_PROPERTY_GETTER_IMPL(AudioBufferSourceNodeHostObject, buffer) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - auto buffer = audioBufferSourceNode->getBuffer(); - - if (!buffer) { - return jsi::Value::null(); - } - - auto bufferHostObject = std::make_shared(buffer); - auto jsiObject = jsi::Object::createFromHostObject(runtime, bufferHostObject); - jsiObject.setExternalMemoryPressure(runtime, bufferHostObject->getSizeInBytes() + 16); - return jsiObject; + return {loopSkip_}; } JSI_PROPERTY_GETTER_IMPL(AudioBufferSourceNodeHostObject, loopStart) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - auto loopStart = audioBufferSourceNode->getLoopStart(); - return {loopStart}; + return {loopStart_}; } JSI_PROPERTY_GETTER_IMPL(AudioBufferSourceNodeHostObject, loopEnd) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - auto loopEnd = audioBufferSourceNode->getLoopEnd(); - return {loopEnd}; + return {loopEnd_}; } JSI_PROPERTY_SETTER_IMPL(AudioBufferSourceNodeHostObject, loop) { auto audioBufferSourceNode = std::static_pointer_cast(node_); - audioBufferSourceNode->setLoop(value.getBool()); + auto loop = value.getBool(); + + auto event = [audioBufferSourceNode, loop](BaseAudioContext &) { + audioBufferSourceNode->setLoop(loop); + }; + + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); + loop_ = loop; } JSI_PROPERTY_SETTER_IMPL(AudioBufferSourceNodeHostObject, loopSkip) { auto audioBufferSourceNode = std::static_pointer_cast(node_); - audioBufferSourceNode->setLoopSkip(value.getBool()); + auto loopSkip = value.getBool(); + + auto event = [audioBufferSourceNode, loopSkip](BaseAudioContext &) { + audioBufferSourceNode->setLoopSkip(loopSkip); + }; + + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); + loopSkip_ = loopSkip; } JSI_PROPERTY_SETTER_IMPL(AudioBufferSourceNodeHostObject, loopStart) { auto audioBufferSourceNode = std::static_pointer_cast(node_); - audioBufferSourceNode->setLoopStart(value.getNumber()); + auto loopStart = value.getNumber(); + + auto event = [audioBufferSourceNode, loopStart](BaseAudioContext &) { + audioBufferSourceNode->setLoopStart(loopStart); + }; + + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); + loopStart_ = loopStart; } JSI_PROPERTY_SETTER_IMPL(AudioBufferSourceNodeHostObject, loopEnd) { auto audioBufferSourceNode = std::static_pointer_cast(node_); - audioBufferSourceNode->setLoopEnd(value.getNumber()); + auto loopEnd = value.getNumber(); + + auto event = [audioBufferSourceNode, loopEnd](BaseAudioContext &) { + audioBufferSourceNode->setLoopEnd(loopEnd); + }; + + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); + loopEnd_ = loopEnd; } JSI_PROPERTY_SETTER_IMPL(AudioBufferSourceNodeHostObject, onLoopEnded) { - auto audioBufferSourceNode = std::static_pointer_cast(node_); - - audioBufferSourceNode->setOnLoopEndedCallbackId( - std::stoull(value.getString(runtime).utf8(runtime))); + auto callbackId = std::stoull(value.getString(runtime).utf8(runtime)); + setOnLoopEndedCallbackId(callbackId); } JSI_HOST_FUNCTION_IMPL(AudioBufferSourceNodeHostObject, start) { - auto when = args[0].getNumber(); - auto offset = args[1].getNumber(); - auto audioBufferSourceNode = std::static_pointer_cast(node_); - if (args[2].isUndefined()) { - audioBufferSourceNode->start(when, offset); - - return jsi::Value::undefined(); - } - - auto duration = args[2].getNumber(); - audioBufferSourceNode->start(when, offset, duration); + auto event = [audioBufferSourceNode, + when = args[0].getNumber(), + offset = args[1].getNumber(), + duration = args[2].isUndefined() ? -1 : args[2].getNumber()](BaseAudioContext &) { + audioBufferSourceNode->start(when, offset, duration); + }; + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } @@ -130,15 +134,74 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferSourceNodeHostObject, setBuffer) { auto audioBufferSourceNode = std::static_pointer_cast(node_); if (args[0].isNull()) { - audioBufferSourceNode->setBuffer(std::shared_ptr(nullptr)); - return jsi::Value::undefined(); + setBuffer(nullptr); + } else { + auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); + // *2 because buffer is copied to internal ABSN buffer for processing + thisValue.asObject(runtime).setExternalMemoryPressure( + runtime, bufferHostObject->getSizeInBytes() * 2); + + setBuffer(bufferHostObject->audioBuffer_); } - auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - thisValue.asObject(runtime).setExternalMemoryPressure( - runtime, bufferHostObject->getSizeInBytes() + 16); - audioBufferSourceNode->setBuffer(bufferHostObject->audioBuffer_); return jsi::Value::undefined(); } +void AudioBufferSourceNodeHostObject::setOnLoopEndedCallbackId(uint64_t callbackId) { + auto audioBufferSourceNode = std::static_pointer_cast(node_); + + auto event = [audioBufferSourceNode, callbackId](BaseAudioContext &) { + audioBufferSourceNode->setOnLoopEndedCallbackId(callbackId); + }; + + audioBufferSourceNode->unregisterOnLoopEndedCallback(onLoopEndedCallbackId_); + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); + onLoopEndedCallbackId_ = callbackId; +} + +void AudioBufferSourceNodeHostObject::setBuffer(const std::shared_ptr &buffer) { + // TODO: add optimized memory management for buffer changes, e.g. + // when the same buffer is reused across threads and + // buffer modification is not allowed on JS thread + auto audioBufferSourceNode = std::static_pointer_cast(node_); + + std::shared_ptr copiedBuffer; + std::shared_ptr playbackRateBuffer; + std::shared_ptr audioBuffer; + + if (buffer == nullptr) { + copiedBuffer = nullptr; + playbackRateBuffer = nullptr; + audioBuffer = std::make_shared( + RENDER_QUANTUM_SIZE, 1, audioBufferSourceNode->getContextSampleRate()); + } else { + if (pitchCorrection_) { + int extraTailFrames = + static_cast((inputLatency_ + outputLatency_) * buffer->getSampleRate()); + size_t totalSize = buffer->getSize() + extraTailFrames; + copiedBuffer = std::make_shared( + totalSize, buffer->getNumberOfChannels(), buffer->getSampleRate()); + copiedBuffer->copy(*buffer, 0, 0, buffer->getSize()); + copiedBuffer->zero(buffer->getSize(), extraTailFrames); + } else { + copiedBuffer = std::make_shared(*buffer); + } + + playbackRateBuffer = std::make_shared( + 3 * RENDER_QUANTUM_SIZE, + copiedBuffer->getNumberOfChannels(), + audioBufferSourceNode->getContextSampleRate()); + audioBuffer = std::make_shared( + RENDER_QUANTUM_SIZE, + copiedBuffer->getNumberOfChannels(), + audioBufferSourceNode->getContextSampleRate()); + } + + auto event = + [audioBufferSourceNode, copiedBuffer, playbackRateBuffer, audioBuffer](BaseAudioContext &) { + audioBufferSourceNode->setBuffer(copiedBuffer, playbackRateBuffer, audioBuffer); + }; + audioBufferSourceNode->scheduleAudioEvent(std::move(event)); +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h index ad3359962..24b3552df 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h @@ -10,6 +10,8 @@ using namespace facebook; struct AudioBufferSourceOptions; class BaseAudioContext; +class AudioBufferHostObject; +class AudioBuffer; class AudioBufferSourceNodeHostObject : public AudioBufferBaseSourceNodeHostObject { public: @@ -21,7 +23,6 @@ class AudioBufferSourceNodeHostObject : public AudioBufferBaseSourceNodeHostObje JSI_PROPERTY_GETTER_DECL(loop); JSI_PROPERTY_GETTER_DECL(loopSkip); - JSI_PROPERTY_GETTER_DECL(buffer); JSI_PROPERTY_GETTER_DECL(loopStart); JSI_PROPERTY_GETTER_DECL(loopEnd); @@ -33,6 +34,16 @@ class AudioBufferSourceNodeHostObject : public AudioBufferBaseSourceNodeHostObje JSI_HOST_FUNCTION_DECL(start); JSI_HOST_FUNCTION_DECL(setBuffer); + + protected: + bool loop_; + bool loopSkip_; + double loopStart_; + double loopEnd_; + uint64_t onLoopEndedCallbackId_ = 0; + + void setOnLoopEndedCallbackId(uint64_t callbackId); + void setBuffer(const std::shared_ptr &buffer); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 5cf18a806..3d81ae82a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -38,6 +38,14 @@ class AudioNode : public std::enable_shared_from_this { int framesToProcess, bool checkIsAlreadyProcessed); + float getContextSampleRate() const { + if (std::shared_ptr context = context_.lock()) { + return context->getSampleRate(); + } + + return DEFAULT_SAMPLE_RATE; + } + /// @note JS Thread only bool isEnabled() const; /// @note JS Thread only diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp index 6d86853a8..53628b4c7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp @@ -18,11 +18,7 @@ std::size_t AudioDestinationNode::getCurrentSampleFrame() const { } double AudioDestinationNode::getCurrentTime() const { - if (std::shared_ptr context = context_.lock()) { - return static_cast(getCurrentSampleFrame()) / context->getSampleRate(); - } else { - return 0.0; - } + return static_cast(getCurrentSampleFrame()) / getContextSampleRate(); } void AudioDestinationNode::renderAudio( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index bd0cb8a0a..13fc575d0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -30,11 +30,7 @@ void DelayNode::onInputDisabled() { numberOfEnabledInputNodes_ -= 1; if (isEnabled() && numberOfEnabledInputNodes_ == 0) { signalledToStop_ = true; - if (std::shared_ptr context = context_.lock()) { - remainingFrames_ = delayTimeParam_->getValue() * context->getSampleRate(); - } else { - remainingFrames_ = 0; - } + remainingFrames_ = delayTimeParam_->getValue() * getContextSampleRate(); } } @@ -98,8 +94,11 @@ std::shared_ptr DelayNode::processNode( // normal processing std::shared_ptr context = context_.lock(); - if (context == nullptr) + if (context == nullptr) { + processingBuffer->zero(); return processingBuffer; + } + auto delayTime = delayTimeParam_->processKRateParam(framesToProcess, context->getCurrentTime()); size_t writeIndex = static_cast(readIndex_ + delayTime * context->getSampleRate()) % delayBuffer_->getSize(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index aacba4c37..176fbe95e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -43,10 +43,8 @@ void AudioBufferBaseSourceNode::setOnPositionChangedCallbackId(uint64_t callback } void AudioBufferBaseSourceNode::setOnPositionChangedInterval(int interval) { - if (std::shared_ptr context = context_.lock()) { - onPositionChangedInterval_ = - static_cast(context->getSampleRate() * static_cast(interval) / 1000); - } + onPositionChangedInterval_ = + static_cast(getContextSampleRate() * static_cast(interval) / 1000); } int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const { @@ -55,22 +53,14 @@ int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const { double AudioBufferBaseSourceNode::getInputLatency() const { if (pitchCorrection_) { - if (std::shared_ptr context = context_.lock()) { - return static_cast(stretch_->inputLatency()) / context->getSampleRate(); - } else { - return 0; - } + return static_cast(stretch_->inputLatency()) / getContextSampleRate(); } return 0; } double AudioBufferBaseSourceNode::getOutputLatency() const { if (pitchCorrection_) { - if (std::shared_ptr context = context_.lock()) { - return static_cast(stretch_->outputLatency()) / context->getSampleRate(); - } else { - return 0; - } + return static_cast(stretch_->outputLatency()) / getContextSampleRate(); } return 0; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index e34868c61..9f7b709d4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -28,8 +28,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { [[nodiscard]] double getInputLatency() const; [[nodiscard]] double getOutputLatency() const; - /// @note JS Thread only. - /// Thread safe, because does not access state of the node. + /// @note Thread safe, because does not access state of the node. void unregisterOnPositionChangedCallback(uint64_t callbackId); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index 57d4fb6a3..c7eafa38e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -150,12 +150,8 @@ std::shared_ptr AudioBufferQueueSourceNode::processNode( } double AudioBufferQueueSourceNode::getCurrentPosition() const { - if (std::shared_ptr context = context_.lock()) { - return dsp::sampleFrameToTime(static_cast(vReadIndex_), context->getSampleRate()) + - playedBuffersDuration_; - } else { - return 0.0; - } + return dsp::sampleFrameToTime(static_cast(vReadIndex_), getContextSampleRate()) + + playedBuffersDuration_; } void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool isLastBufferInQueue) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 4f235c221..1ee28f3f7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -1,15 +1,17 @@ -#include #include #include #include +#include #include #include #include #include +#include #include #include #include #include +#include namespace audioapi { @@ -18,38 +20,16 @@ AudioBufferSourceNode::AudioBufferSourceNode( const AudioBufferSourceOptions &options) : AudioBufferBaseSourceNode(context, options), loop_(options.loop), - loopSkip_(false), + loopSkip_(options.loopSkip), loopStart_(options.loopStart), loopEnd_(options.loopEnd) { buffer_ = std::shared_ptr(options.buffer); - alignedBuffer_ = std::shared_ptr(nullptr); isInitialized_.store(true, std::memory_order_release); } AudioBufferSourceNode::~AudioBufferSourceNode() { buffer_.reset(); - alignedBuffer_.reset(); -} - -bool AudioBufferSourceNode::getLoop() const { - return loop_; -} - -bool AudioBufferSourceNode::getLoopSkip() const { - return loopSkip_; -} - -double AudioBufferSourceNode::getLoopStart() const { - return loopStart_; -} - -double AudioBufferSourceNode::getLoopEnd() const { - return loopEnd_; -} - -std::shared_ptr AudioBufferSourceNode::getBuffer() const { - return buffer_; } void AudioBufferSourceNode::setLoop(bool loop) { @@ -62,9 +42,7 @@ void AudioBufferSourceNode::setLoopSkip(bool loopSkip) { void AudioBufferSourceNode::setLoopStart(double loopStart) { if (loopSkip_) { - if (std::shared_ptr context = context_.lock()) { - vReadIndex_ = loopStart * context->getSampleRate(); - } + vReadIndex_ = loopStart * getContextSampleRate(); } loopStart_ = loopStart; } @@ -73,39 +51,45 @@ void AudioBufferSourceNode::setLoopEnd(double loopEnd) { loopEnd_ = loopEnd; } -void AudioBufferSourceNode::setBuffer(const std::shared_ptr &buffer) { +void AudioBufferSourceNode::setBuffer( + const std::shared_ptr &buffer, + const std::shared_ptr &playbackRateBuffer, + const std::shared_ptr &audioBuffer) { std::shared_ptr context = context_.lock(); - if (buffer == nullptr || context == nullptr) { - buffer_ = std::shared_ptr(nullptr); - alignedBuffer_ = std::shared_ptr(nullptr); - loopEnd_ = 0; + if (context == nullptr) { return; } - buffer_ = buffer; - channelCount_ = buffer_->getNumberOfChannels(); + auto graphManager = context->getGraphManager(); - stretch_->presetDefault(static_cast(channelCount_), buffer_->getSampleRate()); + if (buffer_ != nullptr) { + graphManager->addAudioBufferForDestruction(std::move(buffer_)); + } - if (pitchCorrection_) { - int extraTailFrames = - static_cast((getInputLatency() + getOutputLatency()) * context->getSampleRate()); - size_t totalSize = buffer_->getSize() + extraTailFrames; + if (playbackRateBuffer_ != nullptr) { + graphManager->addAudioBufferForDestruction(std::move(playbackRateBuffer_)); + } - alignedBuffer_ = std::make_shared(totalSize, channelCount_, buffer_->getSampleRate()); - alignedBuffer_->copy(*buffer_, 0, 0, buffer_->getSize()); + graphManager->addAudioBufferForDestruction(std::move(audioBuffer_)); - alignedBuffer_->zero(buffer_->getSize(), extraTailFrames); - } else { - alignedBuffer_ = std::make_shared(*buffer_); + if (buffer == nullptr) { + loopEnd_ = 0; + channelCount_ = 1; + + buffer_ = nullptr; + playbackRateBuffer_ = nullptr; + audioBuffer_ = + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + return; } - audioBuffer_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); - playbackRateBuffer_ = std::make_shared( - RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); + buffer_ = buffer; + playbackRateBuffer_ = playbackRateBuffer; + audioBuffer_ = audioBuffer; + channelCount_ = buffer_->getNumberOfChannels(); loopEnd_ = buffer_->getDuration(); + stretch_->presetDefault(static_cast(channelCount_), buffer_->getSampleRate()); } void AudioBufferSourceNode::start(double when, double offset, double duration) { @@ -115,49 +99,48 @@ void AudioBufferSourceNode::start(double when, double offset, double duration) { AudioScheduledSourceNode::stop(when + duration); } - if (!alignedBuffer_) { + if (buffer_ == nullptr) { return; } - offset = - std::min(offset, static_cast(alignedBuffer_->getSize()) / alignedBuffer_->getSampleRate()); + offset = std::min(offset, static_cast(buffer_->getSize()) / buffer_->getSampleRate()); if (loop_) { offset = std::min(offset, loopEnd_); } - vReadIndex_ = static_cast(alignedBuffer_->getSampleRate() * offset); + vReadIndex_ = static_cast(buffer_->getSampleRate() * offset); } void AudioBufferSourceNode::disable() { AudioScheduledSourceNode::disable(); - alignedBuffer_.reset(); + buffer_.reset(); } void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { - auto oldCallbackId = onLoopEndedCallbackId_.exchange(callbackId, std::memory_order_acq_rel); + onLoopEndedCallbackId_ = callbackId; +} - if (oldCallbackId != 0) { - audioEventHandlerRegistry_->unregisterHandler(AudioEvent::LOOP_ENDED, oldCallbackId); - } +void AudioBufferSourceNode::unregisterOnLoopEndedCallback(uint64_t callbackId) { + audioEventHandlerRegistry_->unregisterHandler(AudioEvent::LOOP_ENDED, callbackId); } std::shared_ptr AudioBufferSourceNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - // No audio data to fill, zero the output and return. - if (!alignedBuffer_) { - processingBuffer->zero(); - return processingBuffer; - } + // No audio data to fill, zero the output and return. + if (buffer_ == nullptr) { + processingBuffer->zero(); + return processingBuffer; + } - if (!pitchCorrection_) { - processWithoutPitchCorrection(processingBuffer, framesToProcess); - } else { - processWithPitchCorrection(processingBuffer, framesToProcess); - } + if (!pitchCorrection_) { + processWithoutPitchCorrection(processingBuffer, framesToProcess); + } else { + processWithPitchCorrection(processingBuffer, framesToProcess); + } - handleStopScheduled(); + handleStopScheduled(); return processingBuffer; } @@ -167,10 +150,9 @@ double AudioBufferSourceNode::getCurrentPosition() const { } void AudioBufferSourceNode::sendOnLoopEndedEvent() { - auto onLoopEndedCallbackId = onLoopEndedCallbackId_.load(std::memory_order_acquire); - if (onLoopEndedCallbackId != 0) { + if (onLoopEndedCallbackId_ != 0) { audioEventHandlerRegistry_->invokeHandlerWithEventBody( - AudioEvent::LOOP_ENDED, onLoopEndedCallbackId, {}); + AudioEvent::LOOP_ENDED, onLoopEndedCallbackId_, {}); } } @@ -188,15 +170,8 @@ void AudioBufferSourceNode::processWithoutInterpolation( auto readIndex = static_cast(vReadIndex_); size_t writeIndex = startOffset; - size_t frameStart; - size_t frameEnd; - if (std::shared_ptr context = context_.lock()) { - frameStart = static_cast(getVirtualStartFrame(context->getSampleRate())); - frameEnd = static_cast(getVirtualEndFrame(context->getSampleRate())); - } else { - processingBuffer->zero(); - return; - } + auto frameStart = static_cast(getVirtualStartFrame(getContextSampleRate())); + auto frameEnd = static_cast(getVirtualEndFrame(getContextSampleRate())); size_t frameDelta = frameEnd - frameStart; size_t framesLeft = offsetLength; @@ -216,16 +191,16 @@ void AudioBufferSourceNode::processWithoutInterpolation( assert(readIndex >= 0); assert(writeIndex >= 0); - assert(readIndex + framesToCopy <= alignedBuffer_->getSize()); + assert(readIndex + framesToCopy <= buffer_->getSize()); assert(writeIndex + framesToCopy <= processingBuffer->getSize()); // Direction is forward, we can normally copy the data if (direction == 1) { - processingBuffer->copy(*alignedBuffer_, readIndex, writeIndex, framesToCopy); + processingBuffer->copy(*buffer_, readIndex, writeIndex, framesToCopy); } else { for (size_t ch = 0; ch < processingBuffer->getNumberOfChannels(); ch += 1) { processingBuffer->getChannel(ch)->copyReverse( - *alignedBuffer_->getChannel(ch), readIndex, writeIndex, framesToCopy); + *buffer_->getChannel(ch), readIndex, writeIndex, framesToCopy); } } @@ -261,15 +236,8 @@ void AudioBufferSourceNode::processWithInterpolation( size_t writeIndex = startOffset; - double vFrameStart; - double vFrameEnd; - if (std::shared_ptr context = context_.lock()) { - vFrameStart = getVirtualStartFrame(context->getSampleRate()); - vFrameEnd = getVirtualEndFrame(context->getSampleRate()); - } else { - processingBuffer->zero(); - return; - } + auto vFrameStart = getVirtualStartFrame(getContextSampleRate()); + auto vFrameEnd = getVirtualEndFrame(getContextSampleRate()); auto vFrameDelta = vFrameEnd - vFrameStart; auto frameStart = static_cast(vFrameStart); @@ -293,7 +261,7 @@ void AudioBufferSourceNode::processWithInterpolation( for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i++) { auto destination = processingBuffer->getChannel(i)->span(); - const auto source = alignedBuffer_->getChannel(i)->span(); + const auto source = buffer_->getChannel(i)->span(); destination[writeIndex] = dsp::linearInterpolate(source, readIndex, nextReadIndex, factor); } @@ -322,7 +290,7 @@ double AudioBufferSourceNode::getVirtualStartFrame(float sampleRate) const { } double AudioBufferSourceNode::getVirtualEndFrame(float sampleRate) { - auto inputBufferLength = static_cast(alignedBuffer_->getSize()); + auto inputBufferLength = static_cast(buffer_->getSize()); auto loopEndFrame = loopEnd_ * sampleRate; return loop_ && loopEndFrame > 0 && loopStart_ < loopEnd_ diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index 5a28a2c95..e7576e109 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -22,17 +22,17 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { const AudioBufferSourceOptions &options); ~AudioBufferSourceNode() override; - [[nodiscard]] bool getLoop() const; - [[nodiscard]] bool getLoopSkip() const; - [[nodiscard]] double getLoopStart() const; - [[nodiscard]] double getLoopEnd() const; - [[nodiscard]] std::shared_ptr getBuffer() const; - void setLoop(bool loop); void setLoopSkip(bool loopSkip); void setLoopStart(double loopStart); void setLoopEnd(double loopEnd); - void setBuffer(const std::shared_ptr &buffer); + + /// @note Buffer can be set (not to nullptr) only once. + /// This is consistent with Web Audio API. + void setBuffer( + const std::shared_ptr &buffer, + const std::shared_ptr &playbackRateBuffer, + const std::shared_ptr &audioBuffer); using AudioScheduledSourceNode::start; void start(double when, double offset, double duration = -1); @@ -40,6 +40,9 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { void setOnLoopEndedCallbackId(uint64_t callbackId); + /// @note Thread safe, because does not access state of the node. + void unregisterOnLoopEndedCallback(uint64_t callbackId); + protected: std::shared_ptr processNode( const std::shared_ptr &processingBuffer, @@ -55,9 +58,8 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { // User provided buffer std::shared_ptr buffer_; - std::shared_ptr alignedBuffer_; - std::atomic onLoopEndedCallbackId_ = 0; // 0 means no callback + uint64_t onLoopEndedCallbackId_ = 0; // 0 means no callback void sendOnLoopEndedEvent(); void processWithoutInterpolation( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 104fdc366..54203d6fa 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -44,8 +44,7 @@ class AudioScheduledSourceNode : public AudioNode { void disable() override; - /// @note JS Thread only. - /// Thread safe, because does not access state of the node. + /// @note Thread safe, because does not access state of the node. void unregisterOnEndedCallback(uint64_t callbackId); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp index f24e3e488..374ec5201 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp @@ -17,8 +17,7 @@ RecorderAdapterNode::RecorderAdapterNode(const std::shared_ptr } void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { - std::shared_ptr context = context_.lock(); - if (isInitialized_.load(std::memory_order_acquire) || context == nullptr) { + if (isInitialized_.load(std::memory_order_acquire)) { return; } @@ -42,7 +41,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) { // context output and not enforcing anything on the system output/input configuration. // A lot of words for a couple of lines of implementation :shrug: adapterOutputBuffer_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, getContextSampleRate()); isInitialized_.store(true, std::memory_order_release); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp index f5bc89fcb..3755ec935 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.cpp @@ -151,7 +151,7 @@ void AudioGraphManager::addAudioParam(const std::shared_ptr ¶m) sender_.send(std::move(event)); } -void AudioGraphManager::addAudioBuffeForDestruction(std::shared_ptr buffer) { +void AudioGraphManager::addAudioBufferForDestruction(std::shared_ptr buffer) { // direct access because this is called from the Audio thread audioBuffers_.emplace_back(std::move(buffer)); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h index 401885498..467aa9355 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/AudioGraphManager.h @@ -103,7 +103,7 @@ class AudioGraphManager { /// @brief Adds an audio buffer to the manager for destruction. /// @note Called directly from the Audio thread (bypasses SPSC). - void addAudioBuffeForDestruction(std::shared_ptr buffer); + void addAudioBufferForDestruction(std::shared_ptr buffer); void cleanup(); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h index 5307e837e..ce333c176 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h @@ -11,6 +11,7 @@ namespace audioapi { static constexpr int RENDER_QUANTUM_SIZE = 128; static constexpr size_t MAX_FFT_SIZE = 32768; static constexpr int MAX_CHANNEL_COUNT = 32; +static constexpr float DEFAULT_SAMPLE_RATE = 44100.0f; // stretcher static constexpr float UPPER_FREQUENCY_LIMIT_DETECTION = 333.0f; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h index c3316aeba..e5846f1ec 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h @@ -104,6 +104,12 @@ struct AudioBufferSourceOptions : BaseAudioBufferSourceOptions { float loopStart = 0.0f; float loopEnd = 0.0f; bool loop = false; + bool loopSkip = false; + + explicit AudioBufferSourceOptions(BaseAudioBufferSourceOptions &&options) + : BaseAudioBufferSourceOptions(options) { + channelCount = 1; + } }; struct StreamerOptions : AudioScheduledSourceNodeOptions { diff --git a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts index 594ef77c4..a7978d330 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts @@ -11,26 +11,37 @@ export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode { private onLoopEndedSubscription?: AudioEventSubscription; private onLoopEndedCallback?: (event: EventEmptyType) => void; + private _buffer: AudioBuffer | null; + private bufferHasBeenSet: boolean = false; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { const node = context.context.createBufferSource(options || {}); super(context, node); + this._buffer = options?.buffer ? options.buffer : null; } public get buffer(): AudioBuffer | null { - const buffer = (this.node as IAudioBufferSourceNode).buffer; - if (!buffer) { - return null; - } - return new AudioBuffer(buffer); + return this._buffer; } public set buffer(buffer: AudioBuffer | null) { - if (!buffer) { - (this.node as IAudioBufferSourceNode).setBuffer(null); + if (buffer === null) { + if (this.buffer !== null) { + (this.node as IAudioBufferSourceNode).setBuffer(null); + this._buffer = null; + } + return; } + if (this.bufferHasBeenSet) { + throw new InvalidStateError( + 'The buffer can only be set once and cannot be changed afterwards.' + ); + } + (this.node as IAudioBufferSourceNode).setBuffer(buffer.buffer); + this._buffer = buffer; } public get loopSkip(): boolean { diff --git a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx index 5bca7effd..a6763ddfb 100644 --- a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx @@ -212,6 +212,7 @@ class AudioBufferSourceNodeStretcher implements IAudioAPIBufferSourceNodeWeb { private _loopEnd: number = -1; private _buffer: AudioBuffer | null = null; + private bufferHasBeenSet: boolean = false; constructor(context: BaseAudioContext) { const promise = async () => { @@ -421,6 +422,12 @@ class AudioBufferSourceNodeStretcher implements IAudioAPIBufferSourceNodeWeb { } set buffer(buffer: AudioBuffer | null) { + if (buffer !== null && this.bufferHasBeenSet) { + throw new InvalidStateError( + 'The buffer can only be set once and cannot be changed afterwards.' + ); + } + this._buffer = buffer; const action = (node: IStretcherNode) => { From 1612d47c122400034ca005baf6f2899f3103f372 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 12:02:55 +0100 Subject: [PATCH 42/73] refactor: audio buffer queue source node --- .../AudioBufferQueueSourceNodeHostObject.cpp | 73 ++-- .../AudioBufferQueueSourceNodeHostObject.h | 6 + .../AudioBufferSourceNodeHostObject.cpp | 4 + .../sources/AudioBufferQueueSourceNode.cpp | 317 +++++++++--------- .../core/sources/AudioBufferQueueSourceNode.h | 13 +- .../core/sources/AudioBufferSourceNode.cpp | 7 - .../core/sources/AudioBufferSourceNode.h | 1 - 7 files changed, 230 insertions(+), 191 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp index f5996753c..365ac7533 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp @@ -1,10 +1,12 @@ #include #include -#include #include #include +#include +#include #include +#include namespace audioapi { @@ -25,32 +27,27 @@ AudioBufferQueueSourceNodeHostObject::AudioBufferQueueSourceNodeHostObject( } AudioBufferQueueSourceNodeHostObject::~AudioBufferQueueSourceNodeHostObject() { - auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - // When JSI object is garbage collected (together with the eventual callback), // underlying source node might still be active and try to call the // non-existing callback. - audioBufferQueueSourceNode->setOnBufferEndedCallbackId(0); + setOnBufferEndedCallbackId(0); } JSI_PROPERTY_SETTER_IMPL(AudioBufferQueueSourceNodeHostObject, onBufferEnded) { - auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - - audioBufferQueueSourceNode->setOnBufferEndedCallbackId( - std::stoull(value.getString(runtime).utf8(runtime))); + auto callbackId = std::stoull(value.getString(runtime).utf8(runtime)); + setOnBufferEndedCallbackId(callbackId); } JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, start) { - auto when = args[0].getNumber(); - auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - if (!args[1].isNumber()) { - audioBufferQueueSourceNode->start(when); - } else { - auto offset = args[1].getNumber(); + auto event = [ + audioBufferQueueSourceNode, + when = args[0].getNumber(), + offset = args[1].isNumber() ? args[1].getNumber() : -1](BaseAudioContext &) { audioBufferQueueSourceNode->start(when, offset); - } + }; + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } @@ -58,7 +55,10 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, start) { JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, pause) { auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - audioBufferQueueSourceNode->pause(); + auto event = [audioBufferQueueSourceNode](BaseAudioContext &) { + audioBufferQueueSourceNode->pause(); + }; + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } @@ -68,18 +68,28 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, enqueueBuffer) { auto audioBufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - - auto bufferId = audioBufferQueueSourceNode->enqueueBuffer(audioBufferHostObject->audioBuffer_); - - return jsi::String::createFromUtf8(runtime, bufferId); + // TODO: add optimized memory management for buffer changes, e.g. + // when the same buffer is reused across threads and + // buffer modification is not allowed on JS thread + auto copiedBuffer = std::make_shared(*audioBufferHostObject->audioBuffer_); + auto event = [audioBufferQueueSourceNode, + copiedBuffer, + bufferId = bufferId_](BaseAudioContext &) { + audioBufferQueueSourceNode->enqueueBuffer(copiedBuffer, bufferId); + }; + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); + + return jsi::String::createFromUtf8(runtime, std::to_string(bufferId_++)); } JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, dequeueBuffer) { auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - auto bufferId = static_cast(args[0].getNumber()); - - audioBufferQueueSourceNode->dequeueBuffer(bufferId); + auto event = [audioBufferQueueSourceNode, + bufferId = static_cast(args[0].getNumber())](BaseAudioContext &) { + audioBufferQueueSourceNode->dequeueBuffer(bufferId); + }; + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } @@ -87,9 +97,24 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, dequeueBuffer) { JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, clearBuffers) { auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); - audioBufferQueueSourceNode->clearBuffers(); + auto event = [audioBufferQueueSourceNode](BaseAudioContext &) { + audioBufferQueueSourceNode->clearBuffers(); + }; + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } +void AudioBufferQueueSourceNodeHostObject::setOnBufferEndedCallbackId(uint64_t callbackId) { + auto audioBufferQueueSourceNode = std::static_pointer_cast(node_); + + auto event = [audioBufferQueueSourceNode, callbackId](BaseAudioContext &) { + audioBufferQueueSourceNode->setOnBufferEndedCallbackId(callbackId); + }; + + audioBufferQueueSourceNode->unregisterOnBufferEndedCallback(onBufferEndedCallbackId_); + audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); + onBufferEndedCallbackId_ = callbackId; +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h index 6485a7c3a..b5eb4af37 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h @@ -26,6 +26,12 @@ class AudioBufferQueueSourceNodeHostObject : public AudioBufferBaseSourceNodeHos JSI_HOST_FUNCTION_DECL(enqueueBuffer); JSI_HOST_FUNCTION_DECL(dequeueBuffer); JSI_HOST_FUNCTION_DECL(clearBuffers); + + protected: + size_t bufferId_ = 0; + uint64_t onBufferEndedCallbackId_ = 0; + + void setOnBufferEndedCallbackId(uint64_t callbackId); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp index 9525550cc..11a0cbfcd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp @@ -19,6 +19,10 @@ AudioBufferSourceNodeHostObject::AudioBufferSourceNodeHostObject( loopSkip_(options.loopSkip), loopStart_(options.loopStart), loopEnd_(options.loopEnd) { + if (options.buffer != nullptr) { + setBuffer(options.buffer); + } + addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loop), JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopSkip), diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index c7eafa38e..5b45a11a4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,7 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( const BaseAudioBufferSourceOptions &options) : AudioBufferBaseSourceNode(context, options) { buffers_ = {}; - stretch_->presetDefault(channelCount_, context->getSampleRate()); + stretch_->presetDefault(static_cast(channelCount_), context->getSampleRate()); if (options.pitchCorrection) { // If pitch correction is enabled, add extra frames at the end @@ -38,10 +39,6 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( isInitialized_.store(true, std::memory_order_release); } -AudioBufferQueueSourceNode::~AudioBufferQueueSourceNode() { - buffers_ = {}; -} - void AudioBufferQueueSourceNode::stop(double when) { AudioScheduledSourceNode::stop(when); isPaused_ = false; @@ -56,7 +53,7 @@ void AudioBufferQueueSourceNode::start(double when) { void AudioBufferQueueSourceNode::start(double when, double offset) { start(when); - if (buffers_.empty()) { + if (buffers_.empty() || offset < 0) { return; } @@ -69,42 +66,50 @@ void AudioBufferQueueSourceNode::pause() { isPaused_ = true; } -std::string AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr &buffer) { - buffers_.emplace(bufferId_, buffer); +void AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr &buffer, size_t bufferId) { + buffers_.emplace_back(bufferId, buffer); if (tailBuffer_ != nullptr) { addExtraTailFrames_ = true; } - - return std::to_string(bufferId_++); } void AudioBufferQueueSourceNode::dequeueBuffer(const size_t bufferId) { - if (buffers_.empty()) { - return; - } + if (auto context = context_.lock()) { + if (buffers_.empty()) { + return; + } - if (buffers_.front().first == bufferId) { - buffers_.pop(); - vReadIndex_ = 0.0; - return; - } + auto graphManager = context->getGraphManager(); - // If the buffer is not at the front, we need to remove it from the queue. - // And keep vReadIndex_ at the same position. - std::queue>> newQueue; - while (!buffers_.empty()) { - if (buffers_.front().first != bufferId) { - newQueue.push(buffers_.front()); - } - buffers_.pop(); + if (buffers_.front().first == bufferId) { + graphManager->addAudioBufferForDestruction(std::move(buffers_.front().second)); + buffers_.pop_front(); + vReadIndex_ = 0.0; + return; + } + + // If the buffer is not at the front, we need to remove it from the linked list.. + // And keep vReadIndex_ at the same position. + for (auto it = std::next(buffers_.begin()); it != buffers_.end(); ++it) { + if (it->first == bufferId) { + graphManager->addAudioBufferForDestruction(std::move(it->second)); + buffers_.erase(it); + return; + } + } } - std::swap(buffers_, newQueue); } void AudioBufferQueueSourceNode::clearBuffers() { - buffers_ = {}; - vReadIndex_ = 0.0; + if (auto context = context_.lock()) { + for (auto it = buffers_.begin(); it != buffers_.end(); ++it) { + context->getGraphManager()->addAudioBufferForDestruction(std::move(it->second)); + } + + buffers_.clear(); + vReadIndex_ = 0.0; + } } void AudioBufferQueueSourceNode::disable() { @@ -118,15 +123,15 @@ void AudioBufferQueueSourceNode::disable() { } AudioScheduledSourceNode::disable(); - buffers_ = {}; + clearBuffers(); } void AudioBufferQueueSourceNode::setOnBufferEndedCallbackId(uint64_t callbackId) { - auto oldCallbackId = onBufferEndedCallbackId_.exchange(callbackId, std::memory_order_acq_rel); + onBufferEndedCallbackId_ = callbackId; +} - if (oldCallbackId != 0) { - audioEventHandlerRegistry_->unregisterHandler(AudioEvent::BUFFER_ENDED, oldCallbackId); - } +void AudioBufferQueueSourceNode::unregisterOnBufferEndedCallback(uint64_t callbackId) { + audioEventHandlerRegistry_->unregisterHandler(AudioEvent::BUFFER_ENDED, callbackId); } std::shared_ptr AudioBufferQueueSourceNode::processNode( @@ -155,14 +160,12 @@ double AudioBufferQueueSourceNode::getCurrentPosition() const { } void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool isLastBufferInQueue) { - auto onBufferEndedCallbackId = onBufferEndedCallbackId_.load(std::memory_order_acquire); - - if (onBufferEndedCallbackId != 0) { + if (onBufferEndedCallbackId_ != 0) { std::unordered_map body = { {"bufferId", std::to_string(bufferId)}, {"isLastBufferInQueue", isLastBufferInQueue}}; audioEventHandlerRegistry_->invokeHandlerWithEventBody( - AudioEvent::BUFFER_ENDED, onBufferEndedCallbackId, body); + AudioEvent::BUFFER_ENDED, onBufferEndedCallbackId_, body); } } @@ -175,60 +178,64 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation( size_t startOffset, size_t offsetLength, float playbackRate) { - auto readIndex = static_cast(vReadIndex_); - size_t writeIndex = startOffset; - - auto data = buffers_.front(); - auto bufferId = data.first; - auto buffer = data.second; - - size_t framesLeft = offsetLength; - - while (framesLeft > 0) { - size_t framesToEnd = buffer->getSize() - readIndex; - size_t framesToCopy = std::min(framesToEnd, framesLeft); - framesToCopy = framesToCopy > 0 ? framesToCopy : 0; - - assert(readIndex >= 0); - assert(writeIndex >= 0); - assert(readIndex + framesToCopy <= buffer->getSize()); - assert(writeIndex + framesToCopy <= processingBuffer->getSize()); - - processingBuffer->copy(*buffer, readIndex, writeIndex, framesToCopy); - - writeIndex += framesToCopy; - readIndex += framesToCopy; - framesLeft -= framesToCopy; - - if (readIndex >= buffer->getSize()) { - playedBuffersDuration_ += buffer->getDuration(); - buffers_.pop(); - - if (!(buffers_.empty() && addExtraTailFrames_)) { - sendOnBufferEndedEvent(bufferId, buffers_.empty()); - } - - if (buffers_.empty()) { - if (addExtraTailFrames_) { - buffers_.emplace(bufferId, tailBuffer_); - addExtraTailFrames_ = false; - } else { - processingBuffer->zero(writeIndex, framesLeft); - readIndex = 0; - - break; - } + if (auto context = context_.lock()) { + auto readIndex = static_cast(vReadIndex_); + size_t writeIndex = startOffset; + + auto data = buffers_.front(); + auto bufferId = data.first; + auto buffer = data.second; + + size_t framesLeft = offsetLength; + + while (framesLeft > 0) { + size_t framesToEnd = buffer->getSize() - readIndex; + size_t framesToCopy = std::min(framesToEnd, framesLeft); + framesToCopy = framesToCopy > 0 ? framesToCopy : 0; + + assert(readIndex >= 0); + assert(writeIndex >= 0); + assert(readIndex + framesToCopy <= buffer->getSize()); + assert(writeIndex + framesToCopy <= processingBuffer->getSize()); + + processingBuffer->copy(*buffer, readIndex, writeIndex, framesToCopy); + + writeIndex += framesToCopy; + readIndex += framesToCopy; + framesLeft -= framesToCopy; + + if (readIndex >= buffer->getSize()) { + playedBuffersDuration_ += buffer->getDuration(); + buffers_.pop_front(); + + if (!(buffers_.empty() && addExtraTailFrames_)) { + sendOnBufferEndedEvent(bufferId, buffers_.empty()); + } + + if (buffers_.empty()) { + if (addExtraTailFrames_) { + buffers_.emplace_back(bufferId, tailBuffer_); + addExtraTailFrames_ = false; + } else { + context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer)); + processingBuffer->zero(writeIndex, framesLeft); + readIndex = 0; + + break; + } + } + + context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer)); + data = buffers_.front(); + bufferId = data.first; + buffer = data.second; + readIndex = 0; + } } - data = buffers_.front(); - bufferId = data.first; - buffer = data.second; - readIndex = 0; - } + // update reading index for next render quantum + vReadIndex_ = static_cast(readIndex); } - - // update reading index for next render quantum - vReadIndex_ = static_cast(readIndex); } void AudioBufferQueueSourceNode::processWithInterpolation( @@ -236,71 +243,75 @@ void AudioBufferQueueSourceNode::processWithInterpolation( size_t startOffset, size_t offsetLength, float playbackRate) { - size_t writeIndex = startOffset; - size_t framesLeft = offsetLength; - - auto data = buffers_.front(); - auto bufferId = data.first; - auto buffer = data.second; - - while (framesLeft > 0) { - auto readIndex = static_cast(vReadIndex_); - size_t nextReadIndex = readIndex + 1; - auto factor = static_cast(vReadIndex_ - static_cast(readIndex)); - - bool crossBufferInterpolation = false; - std::shared_ptr nextBuffer = nullptr; - - if (nextReadIndex >= buffer->getSize()) { - if (buffers_.size() > 1) { - auto tempQueue = buffers_; - tempQueue.pop(); - nextBuffer = tempQueue.front().second; - nextReadIndex = 0; - crossBufferInterpolation = true; - } else { - nextReadIndex = readIndex; - } - } - - for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { - const auto destination = processingBuffer->getChannel(i)->span(); - const auto currentSource = buffer->getChannel(i)->span(); - - if (crossBufferInterpolation) { - const auto nextSource = nextBuffer->getChannel(i)->span(); - float currentSample = currentSource[readIndex]; - float nextSample = nextSource[nextReadIndex]; - destination[writeIndex] = currentSample + factor * (nextSample - currentSample); - } else { - destination[writeIndex] = - dsp::linearInterpolate(currentSource, readIndex, nextReadIndex, factor); - } - } - - writeIndex += 1; - // queue source node always use positive playbackRate - vReadIndex_ += std::abs(playbackRate); - framesLeft -= 1; - - if (vReadIndex_ >= static_cast(buffer->getSize())) { - playedBuffersDuration_ += buffer->getDuration(); - buffers_.pop(); - - sendOnBufferEndedEvent(bufferId, buffers_.empty()); - - if (buffers_.empty()) { - processingBuffer->zero(writeIndex, framesLeft); - vReadIndex_ = 0.0; - break; - } - - vReadIndex_ = vReadIndex_ - buffer->getSize(); - data = buffers_.front(); - bufferId = data.first; - buffer = data.second; + if (auto context = context_.lock()) { + size_t writeIndex = startOffset; + size_t framesLeft = offsetLength; + + auto data = buffers_.front(); + auto bufferId = data.first; + auto buffer = data.second; + + while (framesLeft > 0) { + auto readIndex = static_cast(vReadIndex_); + size_t nextReadIndex = readIndex + 1; + auto factor = static_cast(vReadIndex_ - static_cast(readIndex)); + + bool crossBufferInterpolation = false; + std::shared_ptr nextBuffer = nullptr; + + if (nextReadIndex >= buffer->getSize()) { + if (buffers_.size() > 1) { + auto tempQueue = buffers_; + tempQueue.pop_front(); + nextBuffer = tempQueue.front().second; + nextReadIndex = 0; + crossBufferInterpolation = true; + } else { + nextReadIndex = readIndex; + } + } + + for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) { + const auto destination = processingBuffer->getChannel(i)->span(); + const auto currentSource = buffer->getChannel(i)->span(); + + if (crossBufferInterpolation) { + const auto nextSource = nextBuffer->getChannel(i)->span(); + float currentSample = currentSource[readIndex]; + float nextSample = nextSource[nextReadIndex]; + destination[writeIndex] = currentSample + factor * (nextSample - currentSample); + } else { + destination[writeIndex] = + dsp::linearInterpolate(currentSource, readIndex, nextReadIndex, factor); + } + } + + writeIndex += 1; + // queue source node always use positive playbackRate + vReadIndex_ += std::abs(playbackRate); + framesLeft -= 1; + + if (vReadIndex_ >= static_cast(buffer->getSize())) { + playedBuffersDuration_ += buffer->getDuration(); + buffers_.pop_front(); + + sendOnBufferEndedEvent(bufferId, buffers_.empty()); + + if (buffers_.empty()) { + context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer)); + processingBuffer->zero(writeIndex, framesLeft); + vReadIndex_ = 0.0; + break; + } + + context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer)); + vReadIndex_ = vReadIndex_ - buffer->getSize(); + data = buffers_.front(); + bufferId = data.first; + buffer = data.second; + } + } } - } } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index 7ab344fef..d02302f42 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -6,8 +6,8 @@ #include #include +#include #include -#include #include namespace audioapi { @@ -21,7 +21,6 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { explicit AudioBufferQueueSourceNode( const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); - ~AudioBufferQueueSourceNode() override; void stop(double when) override; @@ -29,13 +28,16 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { void start(double when, double offset); void pause(); - std::string enqueueBuffer(const std::shared_ptr &buffer); + void enqueueBuffer(const std::shared_ptr &buffer, size_t bufferId); void dequeueBuffer(size_t bufferId); void clearBuffers(); void disable() override; void setOnBufferEndedCallbackId(uint64_t callbackId); + /// @note Thread safe, because does not access state of the node. + void unregisterOnBufferEndedCallback(uint64_t callbackId); + protected: std::shared_ptr processNode( const std::shared_ptr &processingBuffer, @@ -47,8 +49,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { private: // User provided buffers - std::queue>> buffers_; - size_t bufferId_ = 0; + std::list>> buffers_; bool isPaused_ = false; bool addExtraTailFrames_ = false; @@ -56,7 +57,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { double playedBuffersDuration_ = 0; - std::atomic onBufferEndedCallbackId_ = 0; // 0 means no callback + uint64_t onBufferEndedCallbackId_ = 0; // 0 means no callback void processWithoutInterpolation( const std::shared_ptr &processingBuffer, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 1ee28f3f7..794dc318c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -23,15 +23,9 @@ AudioBufferSourceNode::AudioBufferSourceNode( loopSkip_(options.loopSkip), loopStart_(options.loopStart), loopEnd_(options.loopEnd) { - buffer_ = std::shared_ptr(options.buffer); - isInitialized_.store(true, std::memory_order_release); } -AudioBufferSourceNode::~AudioBufferSourceNode() { - buffer_.reset(); -} - void AudioBufferSourceNode::setLoop(bool loop) { loop_ = loop; } @@ -114,7 +108,6 @@ void AudioBufferSourceNode::start(double when, double offset, double duration) { void AudioBufferSourceNode::disable() { AudioScheduledSourceNode::disable(); - buffer_.reset(); } void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index e7576e109..33d2f93d8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -20,7 +20,6 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { explicit AudioBufferSourceNode( const std::shared_ptr &context, const AudioBufferSourceOptions &options); - ~AudioBufferSourceNode() override; void setLoop(bool loop); void setLoopSkip(bool loopSkip); From 699f75a744780bc9bb557b0209396a779b093ebc Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 12:08:14 +0100 Subject: [PATCH 43/73] fix: nitpicks --- .../react-native-audio-api/src/core/AudioBufferSourceNode.ts | 1 + .../src/web-core/AudioBufferSourceNode.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts index a7978d330..74a68f046 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts @@ -42,6 +42,7 @@ export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode { (this.node as IAudioBufferSourceNode).setBuffer(buffer.buffer); this._buffer = buffer; + this.bufferHasBeenSet = true; } public get loopSkip(): boolean { diff --git a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx index a6763ddfb..fa46a49cf 100644 --- a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx @@ -429,6 +429,9 @@ class AudioBufferSourceNodeStretcher implements IAudioAPIBufferSourceNodeWeb { } this._buffer = buffer; + if (buffer !== null) { + this.bufferHasBeenSet = true; + } const action = (node: IStretcherNode) => { node.dropBuffers(); From 9c651206b15cf4a2dac9bb69b3e349fba03904e1 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 12:25:22 +0100 Subject: [PATCH 44/73] refactor: wave shaper node --- .../effects/WaveShaperNodeHostObject.cpp | 64 ++++++++----------- .../effects/WaveShaperNodeHostObject.h | 4 +- .../audioapi/core/effects/WaveShaperNode.cpp | 23 +------ .../audioapi/core/effects/WaveShaperNode.h | 12 ++-- .../src/core/effects/WaveShaperNodeTest.cpp | 7 +- .../src/core/AudioBufferSourceNode.ts | 8 ++- .../src/core/WaveShaperNode.ts | 8 +-- 7 files changed, 47 insertions(+), 79 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp index 05ff69806..aea7cb004 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp @@ -7,69 +7,55 @@ #include #include +#include namespace audioapi { WaveShaperNodeHostObject::WaveShaperNodeHostObject( const std::shared_ptr &context, const WaveShaperOptions &options) - : AudioNodeHostObject(context->createWaveShaper(options), options) { - addGetters( - JSI_EXPORT_PROPERTY_GETTER(WaveShaperNodeHostObject, oversample), - JSI_EXPORT_PROPERTY_GETTER(WaveShaperNodeHostObject, curve)); - + : AudioNodeHostObject(context->createWaveShaper(options), options), oversample_(options.oversample) { + addGetters(JSI_EXPORT_PROPERTY_GETTER(WaveShaperNodeHostObject, oversample)); addSetters(JSI_EXPORT_PROPERTY_SETTER(WaveShaperNodeHostObject, oversample)); addFunctions(JSI_EXPORT_FUNCTION(WaveShaperNodeHostObject, setCurve)); } JSI_PROPERTY_GETTER_IMPL(WaveShaperNodeHostObject, oversample) { - auto waveShaperNode = std::static_pointer_cast(node_); return jsi::String::createFromUtf8( - runtime, js_enum_parser::overSampleTypeToString(waveShaperNode->getOversample())); -} - -JSI_PROPERTY_GETTER_IMPL(WaveShaperNodeHostObject, curve) { - auto waveShaperNode = std::static_pointer_cast(node_); - auto curve = waveShaperNode->getCurve(); - - if (curve == nullptr) { - return jsi::Value::null(); - } - - // copy AudioArray holding curve data to avoid subsequent modifications - auto audioArrayBuffer = std::make_shared(*curve); - auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer); - - auto float32ArrayCtor = runtime.global().getPropertyAsFunction(runtime, "Float32Array"); - auto float32Array = float32ArrayCtor.callAsConstructor(runtime, arrayBuffer).getObject(runtime); - float32Array.setExternalMemoryPressure(runtime, audioArrayBuffer->size()); - - return float32Array; + runtime, js_enum_parser::overSampleTypeToString(oversample_)); } JSI_PROPERTY_SETTER_IMPL(WaveShaperNodeHostObject, oversample) { auto waveShaperNode = std::static_pointer_cast(node_); - auto type = value.asString(runtime).utf8(runtime); - waveShaperNode->setOversample(js_enum_parser::overSampleTypeFromString(type)); + + auto oversample = js_enum_parser::overSampleTypeFromString(value.asString(runtime).utf8(runtime)); + auto event = [waveShaperNode, oversample](BaseAudioContext &) { + waveShaperNode->setOversample(oversample); + }; + waveShaperNode->scheduleAudioEvent(std::move(event)); + oversample_ = oversample; } JSI_HOST_FUNCTION_IMPL(WaveShaperNodeHostObject, setCurve) { auto waveShaperNode = std::static_pointer_cast(node_); - if (args[0].isNull()) { - waveShaperNode->setCurve(nullptr); - return jsi::Value::undefined(); - } + std::shared_ptr curve = nullptr; - auto arrayBuffer = - args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); + if (args[0].isObject()) { + auto arrayBuffer = + args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); + // *2 because it is copied to internal curve array for processing + thisValue.asObject(runtime).setExternalMemoryPressure(runtime, arrayBuffer.size(runtime) * 2); - auto curve = std::make_shared( - reinterpret_cast(arrayBuffer.data(runtime)), - static_cast(arrayBuffer.size(runtime) / sizeof(float))); + auto size = static_cast(arrayBuffer.size(runtime) / sizeof(float)); + curve = std::make_shared( + reinterpret_cast(arrayBuffer.data(runtime)), size); + } - waveShaperNode->setCurve(curve); - thisValue.asObject(runtime).setExternalMemoryPressure(runtime, arrayBuffer.size(runtime)); + auto event = [waveShaperNode, curve](BaseAudioContext &) { + waveShaperNode->setCurve(curve); + }; + waveShaperNode->scheduleAudioEvent(std::move(event)); return jsi::Value::undefined(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h index 17bae85b7..9911e0fa7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h @@ -20,9 +20,11 @@ class WaveShaperNodeHostObject : public AudioNodeHostObject { const WaveShaperOptions &options); JSI_PROPERTY_GETTER_DECL(oversample); - JSI_PROPERTY_GETTER_DECL(curve); JSI_PROPERTY_SETTER_DECL(oversample); JSI_HOST_FUNCTION_DECL(setCurve); + + private: + OverSampleType oversample_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index 02621b1be..7ba53d6bc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include @@ -24,26 +24,15 @@ WaveShaperNode::WaveShaperNode( isInitialized_.store(true, std::memory_order_release); } -OverSampleType WaveShaperNode::getOversample() const { - return oversample_.load(std::memory_order_acquire); -} - void WaveShaperNode::setOversample(OverSampleType type) { - std::scoped_lock lock(mutex_); - oversample_.store(type, std::memory_order_release); + oversample_ = type; for (int i = 0; i < waveShapers_.size(); i++) { waveShapers_[i]->setOversample(type); } } -std::shared_ptr WaveShaperNode::getCurve() const { - std::scoped_lock lock(mutex_); - return curve_; -} - -void WaveShaperNode::setCurve(const std::shared_ptr &curve) { - std::scoped_lock lock(mutex_); +void WaveShaperNode::setCurve(const std::shared_ptr &curve) { curve_ = curve; for (int i = 0; i < waveShapers_.size(); i++) { @@ -54,12 +43,6 @@ void WaveShaperNode::setCurve(const std::shared_ptr &curve) { std::shared_ptr WaveShaperNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - std::unique_lock lock(mutex_, std::try_to_lock); - - if (!lock.owns_lock()) { - return processingBuffer; - } - if (curve_ == nullptr) { return processingBuffer; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 18a3f96a2..23aabfb92 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -15,7 +15,7 @@ namespace audioapi { class AudioBuffer; -class AudioArrayBuffer; +class AudioArray; struct WaveShaperOptions; class WaveShaperNode : public AudioNode { @@ -24,11 +24,8 @@ class WaveShaperNode : public AudioNode { const std::shared_ptr &context, const WaveShaperOptions &options); - [[nodiscard]] OverSampleType getOversample() const; - [[nodiscard]] std::shared_ptr getCurve() const; - void setOversample(OverSampleType); - void setCurve(const std::shared_ptr &curve); + void setCurve(const std::shared_ptr &curve); protected: std::shared_ptr processNode( @@ -36,9 +33,8 @@ class WaveShaperNode : public AudioNode { int framesToProcess) override; private: - std::atomic oversample_; - std::shared_ptr curve_; - mutable std::mutex mutex_; + OverSampleType oversample_; + std::shared_ptr curve_; std::vector> waveShapers_{}; }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 3382e74e1..6faa6bfe4 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -28,7 +28,7 @@ class TestableWaveShaperNode : public WaveShaperNode { public: explicit TestableWaveShaperNode(std::shared_ptr context) : WaveShaperNode(context, WaveShaperOptions()) { - testCurve_ = std::make_shared(3); + testCurve_ = std::make_shared(3); auto data = testCurve_->span(); data[0] = -2.0f; data[1] = 0.0f; @@ -41,7 +41,7 @@ class TestableWaveShaperNode : public WaveShaperNode { return WaveShaperNode::processNode(processingBuffer, framesToProcess); } - std::shared_ptr testCurve_; + std::shared_ptr testCurve_; }; TEST_F(WaveShaperNodeTest, WaveShaperNodeCanBeCreated) { @@ -52,7 +52,6 @@ TEST_F(WaveShaperNodeTest, WaveShaperNodeCanBeCreated) { TEST_F(WaveShaperNodeTest, NullCanBeAsignedToCurve) { auto waveShaper = context->createWaveShaper(WaveShaperOptions()); ASSERT_NO_THROW(waveShaper->setCurve(nullptr)); - ASSERT_EQ(waveShaper->getCurve(), nullptr); } TEST_F(WaveShaperNodeTest, NoneOverSamplingProcessesCorrectly) { diff --git a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts index 74a68f046..ee69da691 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts @@ -11,13 +11,17 @@ export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode { private onLoopEndedSubscription?: AudioEventSubscription; private onLoopEndedCallback?: (event: EventEmptyType) => void; - private _buffer: AudioBuffer | null; + private _buffer: AudioBuffer | null = null; private bufferHasBeenSet: boolean = false; constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { const node = context.context.createBufferSource(options || {}); super(context, node); - this._buffer = options?.buffer ? options.buffer : null; + + if (options?.buffer) { + this._buffer = options.buffer; + this.bufferHasBeenSet = true; + } } public get buffer(): AudioBuffer | null { diff --git a/packages/react-native-audio-api/src/core/WaveShaperNode.ts b/packages/react-native-audio-api/src/core/WaveShaperNode.ts index ed0670f37..ffccaad57 100644 --- a/packages/react-native-audio-api/src/core/WaveShaperNode.ts +++ b/packages/react-native-audio-api/src/core/WaveShaperNode.ts @@ -6,21 +6,19 @@ import { TWaveShaperOptions } from '../types'; export default class WaveShaperNode extends AudioNode { private isCurveSet: boolean = false; + private _curve: Float32Array | null = null; constructor(context: BaseAudioContext, options?: TWaveShaperOptions) { const node = context.context.createWaveShaper(options || {}); super(context, node); if (options?.curve) { + this._curve = options.curve; this.isCurveSet = true; } } get curve(): Float32Array | null { - if (!this.isCurveSet) { - return null; - } - - return (this.node as IWaveShaperNode).curve; + return this._curve; } get oversample(): OverSampleType { From 1375a9bdfc4386d1080f4cc0845dba749873292a Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 12:49:17 +0100 Subject: [PATCH 45/73] refactor: iir filter node --- .../audioapi/core/effects/IIRFilterNode.cpp | 21 +++++-------------- .../cpp/audioapi/core/effects/IIRFilterNode.h | 20 +++++++++++++++--- .../src/core/IIRFilterNode.ts | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index b9659889c..162259bd5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -23,10 +23,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include +#include #include #include #include @@ -39,7 +39,9 @@ namespace audioapi { IIRFilterNode::IIRFilterNode( const std::shared_ptr &context, const IIRFilterOptions &options) - : AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) { + : AudioNode(context, options), + feedforward_(createNormalizedVector(options.feedforward, options.feedback[0])), + feedback_(createNormalizedVector(options.feedback, options.feedback[0])) { int maxChannels = MAX_CHANNEL_COUNT; xBuffers_.resize(maxChannels); @@ -51,19 +53,6 @@ IIRFilterNode::IIRFilterNode( yBuffers_[c].resize(bufferLength, 0.0f); } - size_t feedforwardLength = feedforward_.size(); - size_t feedbackLength = feedback_.size(); - - if (feedback_[0] != 1) { - float scale = feedback_[0]; - for (unsigned k = 1; k < feedbackLength; ++k) - feedback_[k] /= scale; - - for (unsigned k = 0; k < feedforwardLength; ++k) - feedforward_[k] /= scale; - - feedback_[0] = 1.0f; - } isInitialized_.store(true, std::memory_order_release); } @@ -88,7 +77,7 @@ void IIRFilterNode::getFrequencyResponse( const float *frequencyArray, float *magResponseOutput, float *phaseResponseOutput, - size_t length) { + size_t length) const { std::shared_ptr context = context_.lock(); if (context == nullptr) return; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 28ebcef9d..1f4d10214 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -42,11 +42,12 @@ class IIRFilterNode : public AudioNode { const std::shared_ptr &context, const IIRFilterOptions &options); + /// @note Thread safe, because feedforward_ and feedback_ are readonly after construction. void getFrequencyResponse( const float *frequencyArray, float *magResponseOutput, float *phaseResponseOutput, - size_t length); + size_t length) const; protected: std::shared_ptr processNode( @@ -56,8 +57,8 @@ class IIRFilterNode : public AudioNode { private: static constexpr size_t bufferLength = 32; - std::vector feedforward_; - std::vector feedback_; + const std::vector feedforward_; + const std::vector feedback_; std::vector> xBuffers_; // xBuffers_[channel][index] std::vector> yBuffers_; @@ -71,5 +72,18 @@ class IIRFilterNode : public AudioNode { result = result * z + std::complex(coefficients[k]); return result; } + + static std::vector createNormalizedVector( + const std::vector& inputVector, + float scaleFactor) { + std::vector result = inputVector; + if (scaleFactor != 1.0f && scaleFactor != 0.0f && !result.empty()) { + for (float &val : result) { + val /= scaleFactor; + } + } + + return result; + } }; } // namespace audioapi diff --git a/packages/react-native-audio-api/src/core/IIRFilterNode.ts b/packages/react-native-audio-api/src/core/IIRFilterNode.ts index 94ab1a334..f04a301d0 100644 --- a/packages/react-native-audio-api/src/core/IIRFilterNode.ts +++ b/packages/react-native-audio-api/src/core/IIRFilterNode.ts @@ -6,7 +6,7 @@ import BaseAudioContext from './BaseAudioContext'; export default class IIRFilterNode extends AudioNode { constructor(context: BaseAudioContext, options: TIIRFilterOptions) { - const iirFilterNode = context.context.createIIRFilter(options || {}); + const iirFilterNode = context.context.createIIRFilter(options); super(context, iirFilterNode); } From 0298c33ca380385cfbf9c1227d690e416e732011 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 12:58:23 +0100 Subject: [PATCH 46/73] refactor: biquad filter node --- .../effects/BiquadFilterNodeHostObject.cpp | 31 ++++++++++--------- .../effects/BiquadFilterNodeHostObject.h | 9 ++++++ .../core/effects/BiquadFilterNode.cpp | 4 --- .../audioapi/core/effects/BiquadFilterNode.h | 1 - .../cpp/audioapi/core/effects/IIRFilterNode.h | 20 ++++++------ 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp index b2e785530..6c89897e0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp @@ -7,12 +7,19 @@ #include #include +#include namespace audioapi { BiquadFilterNodeHostObject::BiquadFilterNodeHostObject(const std::shared_ptr& context, const BiquadFilterOptions &options) - : AudioNodeHostObject(context->createBiquadFilter(options), options) { + : AudioNodeHostObject(context->createBiquadFilter(options), options), type_(options.type) { + auto biquadFilterNode = std::static_pointer_cast(node_); + frequencyParam_ = std::make_shared(biquadFilterNode->getFrequencyParam()); + detuneParam_ = std::make_shared(biquadFilterNode->getDetuneParam()); + QParam_ = std::make_shared(biquadFilterNode->getQParam()); + gainParam_ = std::make_shared(biquadFilterNode->getGainParam()); + addGetters( JSI_EXPORT_PROPERTY_GETTER(BiquadFilterNodeHostObject, frequency), JSI_EXPORT_PROPERTY_GETTER(BiquadFilterNodeHostObject, detune), @@ -26,40 +33,34 @@ BiquadFilterNodeHostObject::BiquadFilterNodeHostObject(const std::shared_ptr(node_); - auto frequencyParam_ = - std::make_shared(biquadFilterNode->getFrequencyParam()); return jsi::Object::createFromHostObject(runtime, frequencyParam_); } JSI_PROPERTY_GETTER_IMPL(BiquadFilterNodeHostObject, detune) { - auto biquadFilterNode = std::static_pointer_cast(node_); - auto detuneParam_ = std::make_shared(biquadFilterNode->getDetuneParam()); return jsi::Object::createFromHostObject(runtime, detuneParam_); } JSI_PROPERTY_GETTER_IMPL(BiquadFilterNodeHostObject, Q) { - auto biquadFilterNode = std::static_pointer_cast(node_); - auto QParam_ = std::make_shared(biquadFilterNode->getQParam()); return jsi::Object::createFromHostObject(runtime, QParam_); } JSI_PROPERTY_GETTER_IMPL(BiquadFilterNodeHostObject, gain) { - auto biquadFilterNode = std::static_pointer_cast(node_); - auto gainParam_ = std::make_shared(biquadFilterNode->getGainParam()); return jsi::Object::createFromHostObject(runtime, gainParam_); } JSI_PROPERTY_GETTER_IMPL(BiquadFilterNodeHostObject, type) { - auto biquadFilterNode = std::static_pointer_cast(node_); - auto type = biquadFilterNode->getType(); - return jsi::String::createFromUtf8(runtime, js_enum_parser::filterTypeToString(type)); + return jsi::String::createFromUtf8(runtime, js_enum_parser::filterTypeToString(type_)); } JSI_PROPERTY_SETTER_IMPL(BiquadFilterNodeHostObject, type) { auto biquadFilterNode = std::static_pointer_cast(node_); - auto type = value.asString(runtime).utf8(runtime); - biquadFilterNode->setType(js_enum_parser::filterTypeFromString(type)); + + auto type = js_enum_parser::filterTypeFromString(value.asString(runtime).utf8(runtime)); + auto event = [&biquadFilterNode, type](BaseAudioContext&) { + biquadFilterNode->setType(type); + }; + biquadFilterNode->scheduleAudioEvent(std::move(event)); + type_ = type; } JSI_HOST_FUNCTION_IMPL(BiquadFilterNodeHostObject, getFrequencyResponse) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h index b2a36802d..72076de47 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h @@ -11,6 +11,7 @@ using namespace facebook; struct BiquadFilterOptions; class BaseAudioContext; +class AudioParamHostObject; class BiquadFilterNodeHostObject : public AudioNodeHostObject { public: @@ -27,5 +28,13 @@ class BiquadFilterNodeHostObject : public AudioNodeHostObject { JSI_PROPERTY_SETTER_DECL(type); JSI_HOST_FUNCTION_DECL(getFrequencyResponse); + +private: + std::shared_ptr frequencyParam_; + std::shared_ptr detuneParam_; + std::shared_ptr QParam_; + std::shared_ptr gainParam_; + + BiquadFilterType type_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index dae5c23a2..ea386ef9d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -62,10 +62,6 @@ BiquadFilterNode::BiquadFilterNode( isInitialized_.store(true, std::memory_order_release); } -BiquadFilterType BiquadFilterNode::getType() { - return type_; -} - void BiquadFilterNode::setType(BiquadFilterType type) { type_ = type; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index 3763f9c2e..bed69eaeb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -59,7 +59,6 @@ class BiquadFilterNode : public AudioNode { const std::shared_ptr &context, const BiquadFilterOptions &options); - [[nodiscard]] BiquadFilterType getType(); void setType(BiquadFilterType); [[nodiscard]] std::shared_ptr getFrequencyParam() const; [[nodiscard]] std::shared_ptr getDetuneParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 1f4d10214..deba8fd60 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -73,17 +73,17 @@ class IIRFilterNode : public AudioNode { return result; } - static std::vector createNormalizedVector( - const std::vector& inputVector, - float scaleFactor) { - std::vector result = inputVector; - if (scaleFactor != 1.0f && scaleFactor != 0.0f && !result.empty()) { - for (float &val : result) { - val /= scaleFactor; - } - } + static std::vector createNormalizedVector( + const std::vector &inputVector, + float scaleFactor) { + std::vector result = inputVector; + if (scaleFactor != 1.0f && scaleFactor != 0.0f && !result.empty()) { + for (float &val : result) { + val /= scaleFactor; + } + } - return result; + return result; } }; } // namespace audioapi From 1ca5d4c58a1b82ec4fdbef6adfb233759b340431 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Sat, 7 Feb 2026 13:21:41 +0100 Subject: [PATCH 47/73] refactor: convolver node --- .../effects/BiquadFilterNodeHostObject.h | 2 +- .../effects/ConvolverNodeHostObject.cpp | 47 +++++++++---------- .../effects/ConvolverNodeHostObject.h | 5 +- .../sources/AudioBufferHostObject.h | 3 +- .../AudioBufferSourceNodeHostObject.cpp | 3 +- .../audioapi/core/effects/ConvolverNode.cpp | 8 ---- .../cpp/audioapi/core/effects/ConvolverNode.h | 2 - .../src/core/ConvolverNode.ts | 12 +++-- 8 files changed, 35 insertions(+), 47 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h index 72076de47..2071d6d0b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h @@ -29,7 +29,7 @@ class BiquadFilterNodeHostObject : public AudioNodeHostObject { JSI_HOST_FUNCTION_DECL(getFrequencyResponse); -private: + private: std::shared_ptr frequencyParam_; std::shared_ptr detuneParam_; std::shared_ptr QParam_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp index d6f72a4c2..7bab6f6df 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp @@ -5,48 +5,43 @@ #include #include +#include namespace audioapi { ConvolverNodeHostObject::ConvolverNodeHostObject(const std::shared_ptr& context, const ConvolverOptions &options) - : AudioNodeHostObject(context->createConvolver(options), options) { - addGetters( - JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, normalize), - JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, buffer)); + : AudioNodeHostObject(context->createConvolver(options), options), normalize_(!options.disableNormalization) { addSetters(JSI_EXPORT_PROPERTY_SETTER(ConvolverNodeHostObject, normalize)); addFunctions(JSI_EXPORT_FUNCTION(ConvolverNodeHostObject, setBuffer)); } -JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, normalize) { - auto convolverNode = std::static_pointer_cast(node_); - return {convolverNode->getNormalize_()}; -} - -JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, buffer) { - auto convolverNode = std::static_pointer_cast(node_); - auto buffer = convolverNode->getBuffer(); - auto bufferHostObject = std::make_shared(buffer); - auto jsiObject = jsi::Object::createFromHostObject(runtime, bufferHostObject); - jsiObject.setExternalMemoryPressure(runtime, bufferHostObject->getSizeInBytes() + 16); - return jsiObject; -} - JSI_PROPERTY_SETTER_IMPL(ConvolverNodeHostObject, normalize) { auto convolverNode = std::static_pointer_cast(node_); - convolverNode->setNormalize(value.getBool()); + auto normalize = value.getBool(); + + auto event = [convolverNode, normalize](BaseAudioContext&) { + convolverNode->setNormalize(normalize); + }; + convolverNode->scheduleAudioEvent(std::move(event)); + normalize_ = normalize; } JSI_HOST_FUNCTION_IMPL(ConvolverNodeHostObject, setBuffer) { auto convolverNode = std::static_pointer_cast(node_); - if (args[0].isUndefined()) { - convolverNode->setBuffer(nullptr); - return jsi::Value::undefined(); + + std::shared_ptr copiedBuffer; + + if (args[0].isObject()) { + auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); + thisValue.asObject(runtime).setExternalMemoryPressure( + runtime, bufferHostObject->getSizeInBytes()); + copiedBuffer = std::make_shared(*bufferHostObject->audioBuffer_); } + auto event = [convolverNode, copiedBuffer](BaseAudioContext&) { + convolverNode->setBuffer(copiedBuffer); + }; + convolverNode->scheduleAudioEvent(std::move(event)); - auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - convolverNode->setBuffer(bufferHostObject->audioBuffer_); - thisValue.asObject(runtime).setExternalMemoryPressure( - runtime, bufferHostObject->getSizeInBytes() + 16); return jsi::Value::undefined(); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h index a4366c973..4675d4d8d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h @@ -15,9 +15,10 @@ class ConvolverNodeHostObject : public AudioNodeHostObject { explicit ConvolverNodeHostObject( const std::shared_ptr &context, const ConvolverOptions &options); - JSI_PROPERTY_GETTER_DECL(normalize); - JSI_PROPERTY_GETTER_DECL(buffer); JSI_PROPERTY_SETTER_DECL(normalize); JSI_HOST_FUNCTION_DECL(setBuffer); + + private: + bool normalize_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h index 09d440d55..b441a08fb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h @@ -29,7 +29,8 @@ class AudioBufferHostObject : public JsiHostObject { } [[nodiscard]] inline size_t getSizeInBytes() const { - return audioBuffer_->getSize() * audioBuffer_->getNumberOfChannels() * sizeof(float); + // *2 because every time buffer is passed we create a copy of it. + return audioBuffer_->getSize() * audioBuffer_->getNumberOfChannels() * sizeof(float) * 2; } JSI_PROPERTY_GETTER_DECL(sampleRate); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp index 11a0cbfcd..10112ce84 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp @@ -141,9 +141,8 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferSourceNodeHostObject, setBuffer) { setBuffer(nullptr); } else { auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - // *2 because buffer is copied to internal ABSN buffer for processing thisValue.asObject(runtime).setExternalMemoryPressure( - runtime, bufferHostObject->getSizeInBytes() * 2); + runtime, bufferHostObject->getSizeInBytes()); setBuffer(bufferHostObject->audioBuffer_); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index e6fca4a07..22a8d6b15 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -29,14 +29,6 @@ ConvolverNode::ConvolverNode( isInitialized_.store(true, std::memory_order_release); } -bool ConvolverNode::getNormalize_() const { - return normalize_; -} - -const std::shared_ptr &ConvolverNode::getBuffer() const { - return buffer_; -} - void ConvolverNode::setNormalize(bool normalize) { if (normalize_ != normalize) { normalize_ = normalize; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h index 0f3fcfdbb..b5b410275 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h @@ -25,8 +25,6 @@ class ConvolverNode : public AudioNode { const std::shared_ptr &context, const ConvolverOptions &options); - [[nodiscard]] bool getNormalize_() const; - [[nodiscard]] const std::shared_ptr &getBuffer() const; void setNormalize(bool normalize); void setBuffer(const std::shared_ptr &buffer); diff --git a/packages/react-native-audio-api/src/core/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index 50d4982e0..82638fc93 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -5,20 +5,22 @@ import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; export default class ConvolverNode extends AudioNode { + private _buffer: AudioBuffer | null = null; + constructor(context: BaseAudioContext, options?: TConvolverOptions) { const convolverNode: IConvolverNode = context.context.createConvolver( options || {} ); super(context, convolverNode); + + if (options?.buffer) { + this.buffer = options.buffer; + } this.normalize = convolverNode.normalize; } public get buffer(): AudioBuffer | null { - const buffer = (this.node as IConvolverNode).buffer; - if (!buffer) { - return null; - } - return new AudioBuffer(buffer); + return this._buffer; } public set buffer(buffer: AudioBuffer | null) { From 3def7d7df780d4397d69c92a125a971faecb7984 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 9 Feb 2026 11:42:15 +0100 Subject: [PATCH 48/73] refactor: part of analyser node --- .../analysis/AnalyserNodeHostObject.cpp | 63 ++++++++++++------- .../analysis/AnalyserNodeHostObject.h | 10 ++- .../audioapi/core/analysis/AnalyserNode.cpp | 30 +-------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 11 +--- .../cpp/audioapi/core/types/WindowType.h | 5 ++ .../common/cpp/audioapi/types/NodeOptions.h | 2 + .../src/core/AnalyserNode.ts | 2 +- 7 files changed, 60 insertions(+), 63 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index 3edfd86ce..b621f829d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -5,14 +5,19 @@ #include #include +#include namespace audioapi { AnalyserNodeHostObject::AnalyserNodeHostObject(const std::shared_ptr& context, const AnalyserOptions &options) - : AudioNodeHostObject(context->createAnalyser(options), options) { + : AudioNodeHostObject(context->createAnalyser(options), options), + fftSize_(options.fftSize), + minDecibels_(options.minDecibels), + maxDecibels_(options.maxDecibels), + smoothingTimeConstant_(options.smoothingTimeConstant), + windowType_(options.windowType) { addGetters( JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, fftSize), - JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, frequencyBinCount), JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, minDecibels), JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, maxDecibels), JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, smoothingTimeConstant), @@ -33,64 +38,74 @@ AnalyserNodeHostObject::AnalyserNodeHostObject(const std::shared_ptr(node_); - return {static_cast(analyserNode->getFftSize())}; -} - -JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, frequencyBinCount) { - auto analyserNode = std::static_pointer_cast(node_); - return {static_cast(analyserNode->getFrequencyBinCount())}; + return {fftSize_}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, minDecibels) { - auto analyserNode = std::static_pointer_cast(node_); - return {analyserNode->getMinDecibels()}; + return {minDecibels_}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, maxDecibels) { - auto analyserNode = std::static_pointer_cast(node_); - return {analyserNode->getMaxDecibels()}; + return {maxDecibels_}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, smoothingTimeConstant) { - auto analyserNode = std::static_pointer_cast(node_); - return {analyserNode->getSmoothingTimeConstant()}; + return {smoothingTimeConstant_}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, window) { - auto analyserNode = std::static_pointer_cast(node_); - auto windowType = analyserNode->getWindowType(); - return jsi::String::createFromUtf8(runtime, js_enum_parser::windowTypeToString(windowType)); + return jsi::String::createFromUtf8(runtime, js_enum_parser::windowTypeToString(windowType_)); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, fftSize) { auto analyserNode = std::static_pointer_cast(node_); + auto fftSize = static_cast(value.getNumber()); - analyserNode->setFftSize(fftSize); + auto event = [analyserNode, fftSize](BaseAudioContext&) { + analyserNode->setFftSize(fftSize); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + fftSize_ = fftSize; } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { auto analyserNode = std::static_pointer_cast(node_); auto minDecibels = static_cast(value.getNumber()); - analyserNode->setMinDecibels(minDecibels); + auto event = [analyserNode, minDecibels](BaseAudioContext&) { + analyserNode->setMinDecibels(minDecibels); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + minDecibels_ = minDecibels; } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, maxDecibels) { auto analyserNode = std::static_pointer_cast(node_); auto maxDecibels = static_cast(value.getNumber()); - analyserNode->setMaxDecibels(maxDecibels); + auto event = [analyserNode, maxDecibels](BaseAudioContext&) { + analyserNode->setMaxDecibels(maxDecibels); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + maxDecibels_ = maxDecibels; } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, smoothingTimeConstant) { auto analyserNode = std::static_pointer_cast(node_); auto smoothingTimeConstant = static_cast(value.getNumber()); - analyserNode->setSmoothingTimeConstant(smoothingTimeConstant); + auto event = [analyserNode, smoothingTimeConstant](BaseAudioContext&) { + analyserNode->setSmoothingTimeConstant(smoothingTimeConstant); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + smoothingTimeConstant_ = smoothingTimeConstant; } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, window) { auto analyserNode = std::static_pointer_cast(node_); - auto type = value.asString(runtime).utf8(runtime); - analyserNode->setWindowType(js_enum_parser::windowTypeFromString(type)); + auto windowType = js_enum_parser::windowTypeFromString(value.asString(runtime).utf8(runtime)); + auto event = [analyserNode, windowType](BaseAudioContext&) { + analyserNode->setWindowType(windowType); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + windowType_ = windowType; } JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getFloatFrequencyData) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h index 6b80d6d2c..811480b8c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include -#include namespace audioapi { using namespace facebook; @@ -19,7 +19,6 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { const AnalyserOptions &options); JSI_PROPERTY_GETTER_DECL(fftSize); - JSI_PROPERTY_GETTER_DECL(frequencyBinCount); JSI_PROPERTY_GETTER_DECL(minDecibels); JSI_PROPERTY_GETTER_DECL(maxDecibels); JSI_PROPERTY_GETTER_DECL(smoothingTimeConstant); @@ -35,6 +34,13 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { JSI_HOST_FUNCTION_DECL(getByteFrequencyData); JSI_HOST_FUNCTION_DECL(getFloatTimeDomainData); JSI_HOST_FUNCTION_DECL(getByteTimeDomainData); + + private: + int fftSize_; + float minDecibels_; + float maxDecibels_; + float smoothingTimeConstant_; + WindowType windowType_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index c0ff06a98..9a0fe19df 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -23,7 +23,7 @@ AnalyserNode::AnalyserNode( minDecibels_(options.minDecibels), maxDecibels_(options.maxDecibels), smoothingTimeConstant_(options.smoothingTimeConstant), - windowType_(WindowType::BLACKMAN), + windowType_(options.windowType), inputArray_(std::make_unique(MAX_FFT_SIZE * 2)), downMixBuffer_( std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), @@ -35,30 +35,6 @@ AnalyserNode::AnalyserNode( isInitialized_.store(true, std::memory_order_release); } -int AnalyserNode::getFftSize() const { - return fftSize_; -} - -int AnalyserNode::getFrequencyBinCount() const { - return fftSize_ / 2; -} - -float AnalyserNode::getMinDecibels() const { - return minDecibels_; -} - -float AnalyserNode::getMaxDecibels() const { - return maxDecibels_; -} - -float AnalyserNode::getSmoothingTimeConstant() const { - return smoothingTimeConstant_; -} - -AnalyserNode::WindowType AnalyserNode::getWindowType() const { - return windowType_; -} - void AnalyserNode::setFftSize(int fftSize) { if (fftSize_ == fftSize) { return; @@ -84,7 +60,7 @@ void AnalyserNode::setSmoothingTimeConstant(float smoothingTimeConstant) { smoothingTimeConstant_ = smoothingTimeConstant; } -void AnalyserNode::setWindowType(AnalyserNode::WindowType type) { +void AnalyserNode::setWindowType(WindowType type) { setWindowData(type, fftSize_); } @@ -197,7 +173,7 @@ void AnalyserNode::doFFTAnalysis() { } } -void AnalyserNode::setWindowData(AnalyserNode::WindowType type, int size) { +void AnalyserNode::setWindowData(WindowType type, int size) { if (windowType_ == type && windowData_ != nullptr && windowData_->getSize() == size) { return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index c1f6db736..b80159eab 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -19,23 +20,15 @@ struct AnalyserOptions; class AnalyserNode : public AudioNode { public: - enum class WindowType { BLACKMAN, HANN }; explicit AnalyserNode( const std::shared_ptr &context, const AnalyserOptions &options); - int getFftSize() const; - int getFrequencyBinCount() const; - float getMinDecibels() const; - float getMaxDecibels() const; - float getSmoothingTimeConstant() const; - AnalyserNode::WindowType getWindowType() const; - void setFftSize(int fftSize); void setMinDecibels(float minDecibels); void setMaxDecibels(float maxDecibels); void setSmoothingTimeConstant(float smoothingTimeConstant); - void setWindowType(AnalyserNode::WindowType); + void setWindowType(WindowType); void getFloatFrequencyData(float *data, int length); void getByteFrequencyData(uint8_t *data, int length); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h b/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h new file mode 100644 index 000000000..9af9163cc --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h @@ -0,0 +1,5 @@ +#pragma once + +namespace audioapi { +enum class WindowType { BLACKMAN, HANN }; +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h index e5846f1ec..98fca1aeb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ struct AnalyserOptions : AudioNodeOptions { float minDecibels = -100.0f; float maxDecibels = -30.0f; float smoothingTimeConstant = 0.8f; + WindowType windowType = WindowType::BLACKMAN; }; struct BiquadFilterOptions : AudioNodeOptions { diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts index f2ac9ecfd..93b0014dc 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -83,7 +83,7 @@ export default class AnalyserNode extends AudioNode { } public get frequencyBinCount(): number { - return (this.node as IAnalyserNode).frequencyBinCount; + return Math.floor((this.node as IAnalyserNode).fftSize / 2); } public getFloatFrequencyData(array: Float32Array): void { From 40f2f438b15345091f12271b7360205b3c6f86a3 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 9 Feb 2026 13:11:24 +0100 Subject: [PATCH 49/73] fix: nitpicks --- .../effects/BiquadFilterNodeHostObject.cpp | 2 +- .../effects/ConvolverNodeHostObject.cpp | 20 +++++++++++++------ .../effects/ConvolverNodeHostObject.h | 1 + .../HostObjects/utils/JsEnumParser.cpp | 2 -- .../audioapi/HostObjects/utils/JsEnumParser.h | 5 +++-- .../ios/audioapi/ios/core/IOSAudioRecorder.mm | 2 +- .../src/core/ConvolverNode.ts | 2 ++ 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp index 6c89897e0..45d88869d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp @@ -56,7 +56,7 @@ JSI_PROPERTY_SETTER_IMPL(BiquadFilterNodeHostObject, type) { auto biquadFilterNode = std::static_pointer_cast(node_); auto type = js_enum_parser::filterTypeFromString(value.asString(runtime).utf8(runtime)); - auto event = [&biquadFilterNode, type](BaseAudioContext&) { + auto event = [biquadFilterNode, type](BaseAudioContext&) { biquadFilterNode->setType(type); }; biquadFilterNode->scheduleAudioEvent(std::move(event)); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp index 7bab6f6df..4be5b697a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp @@ -1,25 +1,33 @@ #include #include -#include #include #include +#include #include #include namespace audioapi { -ConvolverNodeHostObject::ConvolverNodeHostObject(const std::shared_ptr& context, const ConvolverOptions &options) - : AudioNodeHostObject(context->createConvolver(options), options), normalize_(!options.disableNormalization) { +ConvolverNodeHostObject::ConvolverNodeHostObject( + const std::shared_ptr &context, + const ConvolverOptions &options) + : AudioNodeHostObject(context->createConvolver(options), options), + normalize_(!options.disableNormalization) { + addGetters(JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, normalize)); addSetters(JSI_EXPORT_PROPERTY_SETTER(ConvolverNodeHostObject, normalize)); addFunctions(JSI_EXPORT_FUNCTION(ConvolverNodeHostObject, setBuffer)); } +JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, normalize) { + return jsi::Value(normalize_); +} + JSI_PROPERTY_SETTER_IMPL(ConvolverNodeHostObject, normalize) { auto convolverNode = std::static_pointer_cast(node_); auto normalize = value.getBool(); - auto event = [convolverNode, normalize](BaseAudioContext&) { + auto event = [convolverNode, normalize](BaseAudioContext &) { convolverNode->setNormalize(normalize); }; convolverNode->scheduleAudioEvent(std::move(event)); @@ -34,10 +42,10 @@ JSI_HOST_FUNCTION_IMPL(ConvolverNodeHostObject, setBuffer) { if (args[0].isObject()) { auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); thisValue.asObject(runtime).setExternalMemoryPressure( - runtime, bufferHostObject->getSizeInBytes()); + runtime, bufferHostObject->getSizeInBytes()); copiedBuffer = std::make_shared(*bufferHostObject->audioBuffer_); } - auto event = [convolverNode, copiedBuffer](BaseAudioContext&) { + auto event = [convolverNode, copiedBuffer](BaseAudioContext &) { convolverNode->setBuffer(copiedBuffer); }; convolverNode->scheduleAudioEvent(std::move(event)); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h index 4675d4d8d..650b29373 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h @@ -15,6 +15,7 @@ class ConvolverNodeHostObject : public AudioNodeHostObject { explicit ConvolverNodeHostObject( const std::shared_ptr &context, const ConvolverOptions &options); + JSI_PROPERTY_GETTER_DECL(normalize); JSI_PROPERTY_SETTER_DECL(normalize); JSI_HOST_FUNCTION_DECL(setBuffer); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp index 312d45af0..4bb8ac12e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp @@ -1,8 +1,6 @@ #include #include -using WindowType = audioapi::AnalyserNode::WindowType; - namespace audioapi::js_enum_parser { WindowType windowTypeFromString(const std::string &type) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h index fadf44720..0a06f9d5e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h @@ -8,11 +8,12 @@ #include #include #include +#include #include namespace audioapi::js_enum_parser { -std::string windowTypeToString(AnalyserNode::WindowType type); -AnalyserNode::WindowType windowTypeFromString(const std::string &type); +std::string windowTypeToString(WindowType type); +WindowType windowTypeFromString(const std::string &type); std::string overSampleTypeToString(OverSampleType type); OverSampleType overSampleTypeFromString(const std::string &type); std::string oscillatorTypeToString(OscillatorType type); diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm index d6fb6e7a2..f23ec9d63 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/IOSAudioRecorder.mm @@ -50,7 +50,7 @@ if (isConnected()) { if (auto lock = Locker::tryLock(adapterNodeMutex_)) { - for (size_t channel = 0; channel < adapterNode_->channelCount_; ++channel) { + for (size_t channel = 0; channel < adapterNode_->getChannelCount(); ++channel) { auto data = (float *)inputBuffer->mBuffers[channel].mData; adapterNode_->buff_[channel]->write(data, numFrames); diff --git a/packages/react-native-audio-api/src/core/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index 82638fc93..bac02de46 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -26,9 +26,11 @@ export default class ConvolverNode extends AudioNode { public set buffer(buffer: AudioBuffer | null) { if (!buffer) { (this.node as IConvolverNode).setBuffer(null); + this._buffer = null; return; } (this.node as IConvolverNode).setBuffer(buffer.buffer); + this._buffer = buffer; } public get normalize(): boolean { From 7f84ecf08d596399123f55bf746de5eea741cee3 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 9 Feb 2026 13:11:50 +0100 Subject: [PATCH 50/73] ci: lint --- .../common/cpp/audioapi/HostObjects/utils/JsEnumParser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h index 0a06f9d5e..c3d92ce37 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include namespace audioapi::js_enum_parser { From 7d079479953446874a1c8f3cce54b54bade23068 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 9 Feb 2026 14:24:46 +0100 Subject: [PATCH 51/73] fix: nitpicks --- .../common/cpp/audioapi/core/effects/WorkletNode.cpp | 1 - .../cpp/audioapi/core/effects/WorkletProcessingNode.cpp | 2 +- .../common/cpp/audioapi/dsp/Convolver.cpp | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp index 0e2ec98c8..d66ef27cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp @@ -18,7 +18,6 @@ WorkletNode::WorkletNode( bufferLength_(bufferLength), inputChannelCount_(inputChannelCount), curBuffIndex_(0) { - isInitialized_.store(true, std::memory_order_release); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp index 683a34df4..27f5d7d0a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp @@ -18,7 +18,7 @@ WorkletProcessingNode::WorkletProcessingNode( inputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); } - + isInitialized_.store(true, std::memory_order_release); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp index ca73e4d5a..d10426919 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp @@ -38,10 +38,10 @@ void Convolver::reset() { _segments.clear(); _segmentsIR.clear(); _preMultiplied.clear(); - if (_fftBuffer) { + if (_fftBuffer != nullptr) { _fftBuffer->zero(); } - if (_inputBuffer) { + if (_inputBuffer != nullptr) { _inputBuffer->zero(); } } From 0552760a1f741b430204e8da0bdf69c8d5c57345 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 26 Feb 2026 19:11:11 +0100 Subject: [PATCH 52/73] ci: lint --- .../common/cpp/audioapi/core/OfflineAudioContext.h | 2 +- .../common/cpp/audioapi/core/effects/IIRFilterNode.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h index 40dacce9a..ce2015d4a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 2c92b06dd..1a2fbb4b8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -29,9 +29,9 @@ #include #include -#include #include #include +#include namespace audioapi { From 8522dd8216c2337e2f7d01e78b019b062541c2ec Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 27 Feb 2026 12:53:33 +0100 Subject: [PATCH 53/73] refactor: thread-safe setFFTSize and drop support for window types --- .../audiodocs/docs/analysis/analyser-node.mdx | 10 --- packages/audiodocs/docs/types/window-type.mdx | 22 ------- .../analysis/AnalyserNodeHostObject.cpp | 66 ++++++++++++------- .../analysis/AnalyserNodeHostObject.h | 6 +- .../HostObjects/utils/JsEnumParser.cpp | 23 ------- .../audioapi/HostObjects/utils/JsEnumParser.h | 3 - .../audioapi/core/analysis/AnalyserNode.cpp | 55 +++++----------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 29 ++++---- .../cpp/audioapi/core/types/WindowType.h | 5 -- .../common/cpp/audioapi/types/NodeOptions.h | 2 - .../react-native-audio-api/src/api.web.ts | 1 - .../src/core/AnalyserNode.ts | 10 +-- .../react-native-audio-api/src/interfaces.ts | 2 - packages/react-native-audio-api/src/types.ts | 2 - .../src/web-core/AnalyserNode.tsx | 12 +--- 15 files changed, 79 insertions(+), 169 deletions(-) delete mode 100644 packages/audiodocs/docs/types/window-type.mdx delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h diff --git a/packages/audiodocs/docs/analysis/analyser-node.mdx b/packages/audiodocs/docs/analysis/analyser-node.mdx index baed61620..db95918ec 100644 --- a/packages/audiodocs/docs/analysis/analyser-node.mdx +++ b/packages/audiodocs/docs/analysis/analyser-node.mdx @@ -51,15 +51,8 @@ It inherits all properties from [`AudioNode`](/docs/core/audio-node#properties). | `minDecibels` | `number` | Float value representing the minimum value for the range of results from [`getByteFrequencyData()`](/docs/analysis/analyser-node#getbytefrequencydata). | | `maxDecibels` | `number` | Float value representing the maximum value for the range of results from [`getByteFrequencyData()`](/docs/analysis/analyser-node#getbytefrequencydata). | | `smoothingTimeConstant` | `number` | Float value representing averaging constant with the last analysis frame. In general the higher value the smoother is the transition between values over time. | -| `window` | [`WindowType`](/docs/types/window-type) | Enumerated value that specifies the type of window function applied when extracting frequency data. | | `frequencyBinCount` | `number` | Integer value representing amount of the data obtained in frequency domain, half of the `fftSize` property. | | -:::caution - -On `Web`, the value of `window` is permanently `'blackman'`, and it cannot be set like on the `Android` or `iOS`. - -::: - ## Methods It inherits all methods from [`AudioNode`](/docs/core/audio-node#methods). @@ -128,6 +121,3 @@ Each value in the array is within the range 0 to 255, where value of 127 indicat - Nominal range is 0 to 1. - 0 means no averaging, 1 means "overlap the previous and current buffer quite a lot while computing the value". - Throws `IndexSizeError` if set value is outside the allowed range. - -#### `window` -- Default value is `'blackman'` diff --git a/packages/audiodocs/docs/types/window-type.mdx b/packages/audiodocs/docs/types/window-type.mdx deleted file mode 100644 index 113934d2c..000000000 --- a/packages/audiodocs/docs/types/window-type.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -sidebar_position: 8 ---- - -# WindowType - -`WindowType` type specifies which [window function](https://en.wikipedia.org/wiki/Window_function) is applied when extracting frequency data. - -**Acceptable values:** - - `blackman` - - Set [Blackman window](https://www.sciencedirect.com/topics/engineering/blackman-window) as window function. - - - `hann` - - Set [Hanning window](https://www.sciencedirect.com/topics/engineering/hanning-window) as window function. - -:::caution - -On `Web`, the value of `window` is permanently `'blackman'`, and it cannot be set like on the `Android` or `iOS`. - -::: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index b621f829d..e530855f4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include +#include namespace audioapi { @@ -14,21 +16,18 @@ AnalyserNodeHostObject::AnalyserNodeHostObject(const std::shared_ptr(node_); auto fftSize = static_cast(value.getNumber()); - auto event = [analyserNode, fftSize](BaseAudioContext&) { - analyserNode->setFftSize(fftSize); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - fftSize_ = fftSize; + + if (fftSize == fftSize_) { + return; + } + + setFFTSize(fftSize); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { @@ -98,16 +94,6 @@ JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, smoothingTimeConstant) { smoothingTimeConstant_ = smoothingTimeConstant; } -JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, window) { - auto analyserNode = std::static_pointer_cast(node_); - auto windowType = js_enum_parser::windowTypeFromString(value.asString(runtime).utf8(runtime)); - auto event = [analyserNode, windowType](BaseAudioContext&) { - analyserNode->setWindowType(windowType); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - windowType_ = windowType; -} - JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getFloatFrequencyData) { auto arrayBuffer = args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); @@ -156,4 +142,34 @@ JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getByteTimeDomainData) { return jsi::Value::undefined(); } +void AnalyserNodeHostObject::setFFTSize(int fftSize) { + auto analyserNode = std::static_pointer_cast(node_); + + auto fft = std::make_shared(fftSize); + auto complexData = std::vector>(fftSize); + auto magnitudeArray = std::make_shared(fftSize / 2); + auto tempArray = std::make_shared(fftSize); + auto windowData = AnalyserNode::createWindowData(fftSize); + + auto event = [ + analyserNode, + fftSize, + fft, + complexData, + magnitudeArray, + tempArray, + windowData](BaseAudioContext&) { + analyserNode->setFFTSize( + fftSize, + fft, + complexData, + magnitudeArray, + tempArray, + windowData); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + + fftSize_ = fftSize; +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h index 8810f48dd..758068243 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -21,13 +20,11 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { JSI_PROPERTY_GETTER_DECL(minDecibels); JSI_PROPERTY_GETTER_DECL(maxDecibels); JSI_PROPERTY_GETTER_DECL(smoothingTimeConstant); - JSI_PROPERTY_GETTER_DECL(window); JSI_PROPERTY_SETTER_DECL(fftSize); JSI_PROPERTY_SETTER_DECL(minDecibels); JSI_PROPERTY_SETTER_DECL(maxDecibels); JSI_PROPERTY_SETTER_DECL(smoothingTimeConstant); - JSI_PROPERTY_SETTER_DECL(window); JSI_HOST_FUNCTION_DECL(getFloatFrequencyData); JSI_HOST_FUNCTION_DECL(getByteFrequencyData); @@ -39,7 +36,8 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { float minDecibels_; float maxDecibels_; float smoothingTimeConstant_; - WindowType windowType_; + + void setFFTSize(int fftSize); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp index 4bb8ac12e..bd7922df7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.cpp @@ -3,29 +3,6 @@ namespace audioapi::js_enum_parser { - WindowType windowTypeFromString(const std::string &type) { - if (type == "blackman") { - return WindowType::BLACKMAN; - } - if (type == "hann") { - return WindowType::HANN; - } - - throw std::invalid_argument("Unknown window type"); - } - - - std::string windowTypeToString(WindowType type) { - switch (type) { - case WindowType::BLACKMAN: - return "blackman"; - case WindowType::HANN: - return "hann"; - default: - throw std::invalid_argument("Unknown window type"); - } - } - BiquadFilterType filterTypeFromString(const std::string &type) { if (type == "lowpass") return BiquadFilterType::LOWPASS; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h index c3d92ce37..089befda7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/JsEnumParser.h @@ -7,13 +7,10 @@ #include #include #include -#include #include #include namespace audioapi::js_enum_parser { -std::string windowTypeToString(WindowType type); -WindowType windowTypeFromString(const std::string &type); std::string overSampleTypeToString(OverSampleType type); OverSampleType overSampleTypeFromString(const std::string &type); std::string oscillatorTypeToString(OscillatorType type); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 62f2a5345..9bd9cac20 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -22,29 +21,30 @@ AnalyserNode::AnalyserNode( minDecibels_(options.minDecibels), maxDecibels_(options.maxDecibels), smoothingTimeConstant_(options.smoothingTimeConstant), - windowType_(options.windowType), inputArray_(std::make_unique(MAX_FFT_SIZE * 2)), downMixBuffer_( std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), - tempArray_(std::make_unique(fftSize_)), - fft_(std::make_unique(fftSize_)), + fft_(std::make_shared(fftSize_)), + tempArray_(std::make_shared(fftSize_)), + windowData_(createWindowData(fftSize_)), complexData_(std::vector>(fftSize_)), - magnitudeArray_(std::make_unique(fftSize_ / 2)) { - setWindowData(windowType_, fftSize_); + magnitudeArray_(std::make_shared(fftSize_ / 2)) { isInitialized_.store(true, std::memory_order_release); } -void AnalyserNode::setFftSize(int fftSize) { - if (fftSize_ == fftSize) { - return; - } - +void AnalyserNode::setFFTSize( + int fftSize, + const std::shared_ptr &fft, + const std::vector> &complexData, + const std::shared_ptr &magnitudeArray, + const std::shared_ptr &tempArray, + const std::shared_ptr &windowData) { fftSize_ = fftSize; - fft_ = std::make_unique(fftSize_); - complexData_ = std::vector>(fftSize_); - magnitudeArray_ = std::make_unique(fftSize_ / 2); - tempArray_ = std::make_unique(fftSize_); - setWindowData(windowType_, fftSize_); + fft_ = fft; + complexData_ = complexData; + magnitudeArray_ = magnitudeArray; + tempArray_ = tempArray; + windowData_ = windowData; } void AnalyserNode::setMinDecibels(float minDecibels) { @@ -59,10 +59,6 @@ void AnalyserNode::setSmoothingTimeConstant(float smoothingTimeConstant) { smoothingTimeConstant_ = smoothingTimeConstant; } -void AnalyserNode::setWindowType(WindowType type) { - setWindowData(type, fftSize_); -} - void AnalyserNode::getFloatFrequencyData(float *data, int length) { doFFTAnalysis(); @@ -172,23 +168,4 @@ void AnalyserNode::doFFTAnalysis() { } } -void AnalyserNode::setWindowData(WindowType type, int size) { - if (windowType_ == type && windowData_ != nullptr && windowData_->getSize() == size) { - return; - } - - windowType_ = type; - if (windowData_ == nullptr || windowData_->getSize() != size) { - windowData_ = std::make_shared(size); - } - - switch (windowType_) { - case WindowType::BLACKMAN: - dsp::Blackman().apply(windowData_->span()); - break; - case WindowType::HANN: - dsp::Hann().apply(windowData_->span()); - break; - } -} } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 0d65c4407..089e90c6f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include #include @@ -22,17 +22,28 @@ class AnalyserNode : public AudioNode { const std::shared_ptr &context, const AnalyserOptions &options); - void setFftSize(int fftSize); + void setFFTSize( + int fftSize, + const std::shared_ptr &fft, + const std::vector> &complexData, + const std::shared_ptr &magnitudeArray, + const std::shared_ptr &tempArray, + const std::shared_ptr &windowData); void setMinDecibels(float minDecibels); void setMaxDecibels(float maxDecibels); void setSmoothingTimeConstant(float smoothingTimeConstant); - void setWindowType(WindowType); void getFloatFrequencyData(float *data, int length); void getByteFrequencyData(uint8_t *data, int length); void getFloatTimeDomainData(float *data, int length); void getByteTimeDomainData(uint8_t *data, int length); + static inline std::shared_ptr createWindowData(int fftSize) { + auto windowData = std::make_shared(fftSize); + dsp::Blackman().apply(windowData->span()); + return windowData; + } + protected: std::shared_ptr processNode( const std::shared_ptr &processingBuffer, @@ -44,21 +55,17 @@ class AnalyserNode : public AudioNode { float maxDecibels_; float smoothingTimeConstant_; - WindowType windowType_; - std::shared_ptr windowData_; - std::unique_ptr inputArray_; std::unique_ptr downMixBuffer_; - std::unique_ptr tempArray_; - std::unique_ptr fft_; + std::shared_ptr fft_; + std::shared_ptr tempArray_; + std::shared_ptr windowData_; std::vector> complexData_; - std::unique_ptr magnitudeArray_; + std::shared_ptr magnitudeArray_; bool shouldDoFFTAnalysis_{true}; void doFFTAnalysis(); - - void setWindowData(WindowType type, int size); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h b/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h deleted file mode 100644 index 9af9163cc..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/types/WindowType.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -namespace audioapi { -enum class WindowType { BLACKMAN, HANN }; -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h index 98fca1aeb..e5846f1ec 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/types/NodeOptions.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -77,7 +76,6 @@ struct AnalyserOptions : AudioNodeOptions { float minDecibels = -100.0f; float maxDecibels = -30.0f; float smoothingTimeConstant = 0.8f; - WindowType windowType = WindowType::BLACKMAN; }; struct BiquadFilterOptions : AudioNodeOptions { diff --git a/packages/react-native-audio-api/src/api.web.ts b/packages/react-native-audio-api/src/api.web.ts index 6452b4b58..a10fa3591 100644 --- a/packages/react-native-audio-api/src/api.web.ts +++ b/packages/react-native-audio-api/src/api.web.ts @@ -25,7 +25,6 @@ export { ChannelCountMode, ChannelInterpretation, ContextState, - WindowType, } from './types'; export { diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts index c28c51c29..9f9a2e728 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -1,7 +1,7 @@ import BaseAudioContext from './BaseAudioContext'; import { IndexSizeError } from '../errors'; import { IAnalyserNode } from '../interfaces'; -import { WindowType, AnalyserOptions } from '../types'; +import { AnalyserOptions } from '../types'; import AudioNode from './AudioNode'; import { AnalyserOptionsValidator } from '../options-validators'; @@ -74,14 +74,6 @@ export default class AnalyserNode extends AudioNode { (this.node as IAnalyserNode).smoothingTimeConstant = value; } - public get window(): WindowType { - return (this.node as IAnalyserNode).window; - } - - public set window(value: WindowType) { - (this.node as IAnalyserNode).window = value; - } - public get frequencyBinCount(): number { return Math.floor((this.node as IAnalyserNode).fftSize / 2); } diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 06069f7a4..bc3ff0481 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -23,7 +23,6 @@ import type { StereoPannerOptions, StreamerOptions, WaveShaperOptions, - WindowType, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -286,7 +285,6 @@ export interface IAnalyserNode extends IAudioNode { minDecibels: number; maxDecibels: number; smoothingTimeConstant: number; - window: WindowType; getFloatFrequencyData: (array: Float32Array) => void; getByteFrequencyData: (array: Uint8Array) => void; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index c461589de..67450bf0b 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -106,8 +106,6 @@ export interface FileInfo { duration: number; } -export type WindowType = 'blackman' | 'hann'; - export type ProcessorMode = 'processInPlace' | 'processThrough'; export interface AudioNodeOptions { diff --git a/packages/react-native-audio-api/src/web-core/AnalyserNode.tsx b/packages/react-native-audio-api/src/web-core/AnalyserNode.tsx index 44da78800..3cdbf1325 100644 --- a/packages/react-native-audio-api/src/web-core/AnalyserNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AnalyserNode.tsx @@ -1,5 +1,5 @@ import AudioNode from './AudioNode'; -import { WindowType, AnalyserOptions } from '../types'; +import { AnalyserOptions } from '../types'; import BaseAudioContext from './BaseAudioContext'; export default class AnalyserNode extends AudioNode { @@ -20,16 +20,6 @@ export default class AnalyserNode extends AudioNode { this.smoothingTimeConstant = node.smoothingTimeConstant; } - public get window(): WindowType { - return 'blackman'; - } - - public set window(value: WindowType) { - console.log( - 'React Native Audio API: setting window is not supported on web' - ); - } - public getByteFrequencyData(array: Uint8Array): void { (this.node as globalThis.AnalyserNode).getByteFrequencyData( array as Uint8Array From 7173b3df3422665de093ea0a8b04b181846c6253 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 27 Feb 2026 15:08:09 +0100 Subject: [PATCH 54/73] refactor: analyser node thread safety with lock-free triple buffer --- .../analysis/AnalyserNodeHostObject.cpp | 33 +------ .../analysis/AnalyserNodeHostObject.h | 2 - .../audioapi/core/analysis/AnalyserNode.cpp | 85 ++++++++++--------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 50 ++++++----- .../cpp/audioapi/utils/TripleBuffer.hpp | 49 +++++++++++ 5 files changed, 122 insertions(+), 97 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index e530855f4..da0d071a9 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -61,7 +61,8 @@ JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, fftSize) { return; } - setFFTSize(fftSize); + fftSize_ = fftSize; + analyserNode->setFFTSize(fftSize); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { @@ -142,34 +143,4 @@ JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getByteTimeDomainData) { return jsi::Value::undefined(); } -void AnalyserNodeHostObject::setFFTSize(int fftSize) { - auto analyserNode = std::static_pointer_cast(node_); - - auto fft = std::make_shared(fftSize); - auto complexData = std::vector>(fftSize); - auto magnitudeArray = std::make_shared(fftSize / 2); - auto tempArray = std::make_shared(fftSize); - auto windowData = AnalyserNode::createWindowData(fftSize); - - auto event = [ - analyserNode, - fftSize, - fft, - complexData, - magnitudeArray, - tempArray, - windowData](BaseAudioContext&) { - analyserNode->setFFTSize( - fftSize, - fft, - complexData, - magnitudeArray, - tempArray, - windowData); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - - fftSize_ = fftSize; -} - } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h index 758068243..c29a8c924 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h @@ -36,8 +36,6 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { float minDecibels_; float maxDecibels_; float smoothingTimeConstant_; - - void setFFTSize(int fftSize); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 9bd9cac20..7c9747e34 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -17,46 +16,36 @@ AnalyserNode::AnalyserNode( const std::shared_ptr &context, const AnalyserOptions &options) : AudioNode(context, options), - fftSize_(options.fftSize), minDecibels_(options.minDecibels), maxDecibels_(options.maxDecibels), smoothingTimeConstant_(options.smoothingTimeConstant), inputArray_(std::make_unique(MAX_FFT_SIZE * 2)), downMixBuffer_( - std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), - fft_(std::make_shared(fftSize_)), - tempArray_(std::make_shared(fftSize_)), - windowData_(createWindowData(fftSize_)), - complexData_(std::vector>(fftSize_)), - magnitudeArray_(std::make_shared(fftSize_ / 2)) { + std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { + setFFTSize(options.fftSize); isInitialized_.store(true, std::memory_order_release); } -void AnalyserNode::setFFTSize( - int fftSize, - const std::shared_ptr &fft, - const std::vector> &complexData, - const std::shared_ptr &magnitudeArray, - const std::shared_ptr &tempArray, - const std::shared_ptr &windowData) { - fftSize_ = fftSize; - fft_ = fft; - complexData_ = complexData; - magnitudeArray_ = magnitudeArray; - tempArray_ = tempArray; - windowData_ = windowData; +void AnalyserNode::setFFTSize(int fftSize) { + fft_ = std::make_unique(fftSize); + complexData_ = std::vector>(fftSize); + magnitudeArray_ = std::make_unique(fftSize / 2); + tempArray_ = std::make_unique(fftSize); + windowData_ = std::make_unique(fftSize); + dsp::Blackman().apply(windowData_->span()); + fftSize_.store(fftSize, std::memory_order_release); } void AnalyserNode::setMinDecibels(float minDecibels) { - minDecibels_ = minDecibels; + minDecibels_.store(minDecibels, std::memory_order_release); } void AnalyserNode::setMaxDecibels(float maxDecibels) { - maxDecibels_ = maxDecibels; + maxDecibels_.store(maxDecibels, std::memory_order_release); } void AnalyserNode::setSmoothingTimeConstant(float smoothingTimeConstant) { - smoothingTimeConstant_ = smoothingTimeConstant; + smoothingTimeConstant_.store(smoothingTimeConstant, std::memory_order_release); } void AnalyserNode::getFloatFrequencyData(float *data, int length) { @@ -76,13 +65,16 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { auto magnitudeBufferData = magnitudeArray_->span(); length = std::min(static_cast(magnitudeArray_->getSize()), length); + auto minDecibels = minDecibels_.load(std::memory_order_acquire); + auto maxDecibels = maxDecibels_.load(std::memory_order_acquire); + const auto rangeScaleFactor = - maxDecibels_ == minDecibels_ ? 1 : 1 / (maxDecibels_ - minDecibels_); + maxDecibels == minDecibels ? 1 : 1 / (maxDecibels - minDecibels); for (int i = 0; i < length; i++) { auto dbMag = - magnitudeBufferData[i] == 0 ? minDecibels_ : dsp::linearToDecibels(magnitudeBufferData[i]); - auto scaledValue = UINT8_MAX * (dbMag - minDecibels_) * rangeScaleFactor; + magnitudeBufferData[i] == 0 ? minDecibels : dsp::linearToDecibels(magnitudeBufferData[i]); + auto scaledValue = UINT8_MAX * (dbMag - minDecibels) * rangeScaleFactor; if (scaledValue < 0) { scaledValue = 0; @@ -96,15 +88,17 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { } void AnalyserNode::getFloatTimeDomainData(float *data, int length) { - auto size = std::min(fftSize_, length); + auto *frame = analysisBuffer_.getForReader(); + auto size = std::min(fftSize_.load(std::memory_order_relaxed), length); - inputArray_->pop_back(data, size, std::max(0, fftSize_ - size), true); + frame->timeDomain.copyTo(data, 0, 0, size); } void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { - auto size = std::min(fftSize_, length); + auto *frame = analysisBuffer_.getForReader(); + auto size = std::min(fftSize_.load(std::memory_order_relaxed), length); - inputArray_->pop_back(*tempArray_, size, std::max(0, fftSize_ - size), true); + tempArray_->copy(frame->timeDomain, 0, 0, size); auto values = tempArray_->span(); @@ -133,23 +127,30 @@ std::shared_ptr AnalyserNode::processNode( // Copy the down mixed buffer to the input buffer (circular buffer) inputArray_->push_back(*downMixBuffer_->getChannel(0), framesToProcess, true); - shouldDoFFTAnalysis_ = true; + // Snapshot the latest fftSize_ samples into the triple buffer for the JS thread. + auto *frame = analysisBuffer_.getForWriter(); + auto fftSize = fftSize_.load(std::memory_order_acquire); + frame->sequenceNumber = ++publishSequence_; + inputArray_->pop_back(frame->timeDomain, fftSize, 0, true); + analysisBuffer_.publish(); return processingBuffer; } void AnalyserNode::doFFTAnalysis() { - if (!shouldDoFFTAnalysis_) { + auto *frame = analysisBuffer_.getForReader(); + + if (frame->sequenceNumber == lastAnalyzedSequence_) { return; } - shouldDoFFTAnalysis_ = false; + lastAnalyzedSequence_ = frame->sequenceNumber; - // We want to copy last fftSize_ elements added to the input buffer to apply - // the window. - inputArray_->pop_back(*tempArray_, fftSize_, 0, true); + auto fftSize = fftSize_.load(std::memory_order_relaxed); - tempArray_->multiply(*windowData_, fftSize_); + // Copy the snapshot from the triple buffer and apply the window. + tempArray_->copy(frame->timeDomain, 0, 0, fftSize); + tempArray_->multiply(*windowData_, fftSize); // do fft analysis - get frequency domain data fft_->doFFT(*tempArray_, complexData_); @@ -157,14 +158,16 @@ void AnalyserNode::doFFTAnalysis() { // Zero out nquist component complexData_[0] = std::complex(complexData_[0].real(), 0); - const float magnitudeScale = 1.0f / static_cast(fftSize_); + const float magnitudeScale = 1.0f / static_cast(fftSize); auto magnitudeBufferData = magnitudeArray_->span(); + auto smoothingTimeConstant = smoothingTimeConstant_.load(std::memory_order_acquire); + for (int i = 0; i < magnitudeArray_->getSize(); i++) { auto scalarMagnitude = std::abs(complexData_[i]) * magnitudeScale; magnitudeBufferData[i] = static_cast( - smoothingTimeConstant_ * magnitudeBufferData[i] + - (1 - smoothingTimeConstant_) * scalarMagnitude); + smoothingTimeConstant * magnitudeBufferData[i] + + (1 - smoothingTimeConstant) * scalarMagnitude); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 089e90c6f..449725d44 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -1,18 +1,22 @@ #pragma once #include +#include #include #include +#include +#include +#include #include #include +#include #include #include namespace audioapi { class AudioBuffer; -class AudioArray; class CircularAudioArray; struct AnalyserOptions; @@ -22,13 +26,7 @@ class AnalyserNode : public AudioNode { const std::shared_ptr &context, const AnalyserOptions &options); - void setFFTSize( - int fftSize, - const std::shared_ptr &fft, - const std::vector> &complexData, - const std::shared_ptr &magnitudeArray, - const std::shared_ptr &tempArray, - const std::shared_ptr &windowData); + void setFFTSize(int fftSize); void setMinDecibels(float minDecibels); void setMaxDecibels(float maxDecibels); void setSmoothingTimeConstant(float smoothingTimeConstant); @@ -38,32 +36,38 @@ class AnalyserNode : public AudioNode { void getFloatTimeDomainData(float *data, int length); void getByteTimeDomainData(uint8_t *data, int length); - static inline std::shared_ptr createWindowData(int fftSize) { - auto windowData = std::make_shared(fftSize); - dsp::Blackman().apply(windowData->span()); - return windowData; - } - protected: std::shared_ptr processNode( const std::shared_ptr &processingBuffer, int framesToProcess) override; private: - int fftSize_; - float minDecibels_; - float maxDecibels_; - float smoothingTimeConstant_; + std::atomic fftSize_; + std::atomic minDecibels_; + std::atomic maxDecibels_; + std::atomic smoothingTimeConstant_; + // Audio Thread data structures std::unique_ptr inputArray_; std::unique_ptr downMixBuffer_; - std::shared_ptr fft_; - std::shared_ptr tempArray_; - std::shared_ptr windowData_; + // JS Thread data structures + std::unique_ptr fft_; + std::unique_ptr tempArray_; + std::unique_ptr windowData_; std::vector> complexData_; - std::shared_ptr magnitudeArray_; - bool shouldDoFFTAnalysis_{true}; + std::unique_ptr magnitudeArray_; + + struct AnalysisFrame { + AudioArray timeDomain; + size_t sequenceNumber = 0; + + AnalysisFrame() : timeDomain(MAX_FFT_SIZE) {} + }; + + TripleBuffer analysisBuffer_; + size_t publishSequence_ = 0; // audio thread only + size_t lastAnalyzedSequence_ = 0; // JS thread only void doFFTAnalysis(); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp new file mode 100644 index 000000000..57c80e596 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace audioapi { + +/// @brief A lock-free triple buffer for single producer and single consumer scenarios. +/// The producer can write to one buffer while the consumer reads from another buffer, and the third buffer is idle. +/// The producer can publish new data by swapping the back buffer with the idle buffer, +/// and the consumer can get the latest data by swapping the front buffer with the idle buffer if there is an update. +/// @tparam T The type of the buffer. +template +class TripleBuffer { + public: + T *getForWriter() { + return &buffers_[backIndex_]; + } + + void publish() { + State newState{backIndex_, true}; + auto prevState = state_.exchange(newState, std::memory_order_acq_rel); + backIndex_ = prevState.index; + } + + T *getForReader() { + auto state = state_.load(std::memory_order_relaxed); + if (state.hasUpdate) { + State newState{frontIndex_, false}; + auto prevState = state_.exchange(newState, std::memory_order_acq_rel); + frontIndex_ = prevState.index; + } + + return &buffers_[frontIndex_]; + } + + private: + struct State { + int index; + bool hasUpdate; + }; + + T buffers_[3]; + int frontIndex_ = 0; + std::atomic state_{{1, false}}; + int backIndex_ = 2; +}; + +} // namespace audioapi From ccd64782db426a3ad24516248e016fd5f6b3ea93 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 27 Feb 2026 15:19:08 +0100 Subject: [PATCH 55/73] ci: lint --- .../common/cpp/audioapi/core/analysis/AnalyserNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 449725d44..8f889b9d1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -66,7 +66,7 @@ class AnalyserNode : public AudioNode { }; TripleBuffer analysisBuffer_; - size_t publishSequence_ = 0; // audio thread only + size_t publishSequence_ = 0; // audio thread only size_t lastAnalyzedSequence_ = 0; // JS thread only void doFFTAnalysis(); From fc26f66846753010c5ee6bab2c9798165c003344 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Fri, 27 Feb 2026 17:09:55 +0100 Subject: [PATCH 56/73] fix: fixed negative latency values in AudioBufferSourceNode --- .../sources/AudioBufferSourceNodeHostObject.cpp | 3 +++ .../core/sources/AudioBufferBaseSourceNode.cpp | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp index 10112ce84..97783ad2d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp @@ -178,6 +178,9 @@ void AudioBufferSourceNodeHostObject::setBuffer(const std::shared_ptr( RENDER_QUANTUM_SIZE, 1, audioBufferSourceNode->getContextSampleRate()); } else { + inputLatency_ = audioBufferSourceNode->getInputLatency(); + outputLatency_ = audioBufferSourceNode->getOutputLatency(); + if (pitchCorrection_) { int extraTailFrames = static_cast((inputLatency_ + outputLatency_) * buffer->getSampleRate()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index 176fbe95e..08004579e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -1,11 +1,12 @@ -#include #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -53,16 +54,16 @@ int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const { double AudioBufferBaseSourceNode::getInputLatency() const { if (pitchCorrection_) { - return static_cast(stretch_->inputLatency()) / getContextSampleRate(); + return std::max(static_cast(stretch_->inputLatency()) / getContextSampleRate(), 0.0); } - return 0; + return 0.0; } double AudioBufferBaseSourceNode::getOutputLatency() const { if (pitchCorrection_) { - return static_cast(stretch_->outputLatency()) / getContextSampleRate(); + return std::max(static_cast(stretch_->outputLatency()) / getContextSampleRate(), 0.0); } - return 0; + return 0.0; } void AudioBufferBaseSourceNode::unregisterOnPositionChangedCallback(uint64_t callbackId) { From 9356844e2e673588e483b9b436c231dc371ed95b Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Mar 2026 11:41:54 +0100 Subject: [PATCH 57/73] refactor: moved stretch init and preset to JS thread --- .../AudioBufferBaseSourceNodeHostObject.cpp | 17 +++++++- .../AudioBufferBaseSourceNodeHostObject.h | 5 ++- .../AudioBufferQueueSourceNodeHostObject.cpp | 16 +++++++- .../AudioBufferQueueSourceNodeHostObject.h | 1 + .../AudioBufferSourceNodeHostObject.cpp | 6 +-- .../sources/AudioBufferBaseSourceNode.cpp | 40 ++++++------------- .../core/sources/AudioBufferBaseSourceNode.h | 4 +- .../sources/AudioBufferQueueSourceNode.cpp | 13 +++--- .../core/sources/AudioBufferQueueSourceNode.h | 7 +++- .../core/sources/AudioBufferSourceNode.cpp | 1 - 10 files changed, 60 insertions(+), 50 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp index d421d980d..fbe69d049 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp @@ -2,9 +2,11 @@ #include #include #include +#include #include #include +#include namespace audioapi { @@ -16,8 +18,6 @@ AudioBufferBaseSourceNodeHostObject::AudioBufferBaseSourceNodeHostObject( detuneParam_ = std::make_shared(sourceNode->getDetuneParam()); playbackRateParam_ = std::make_shared(sourceNode->getPlaybackRateParam()); onPositionChangedInterval_ = sourceNode->getOnPositionChangedInterval(); - inputLatency_ = sourceNode->getInputLatency(); - outputLatency_ = sourceNode->getOutputLatency(); addGetters( JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, detune), @@ -90,4 +90,17 @@ void AudioBufferBaseSourceNodeHostObject::setOnPositionChangedCallbackId(uint64_ onPositionChangedCallbackId_ = callbackId; } +void AudioBufferBaseSourceNodeHostObject::initStretch(int channelCount, float sampleRate) { + auto sourceNode = std::static_pointer_cast(node_); + auto stretch = std::make_shared>(); + stretch->presetDefault(channelCount, sampleRate); + inputLatency_ = std::max(dsp::sampleFrameToTime(stretch->inputLatency(), node_->getContextSampleRate()), 0.0); + outputLatency_ = std::max(dsp::sampleFrameToTime(stretch->outputLatency(), node_->getContextSampleRate()), 0.0); + + auto event = [sourceNode, stretch] (BaseAudioContext&) { + sourceNode->initStretch(stretch); + }; + sourceNode->scheduleAudioEvent(std::move(event)); +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h index a80b2034f..8f27cf2a6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h @@ -36,11 +36,12 @@ class AudioBufferBaseSourceNodeHostObject : public AudioScheduledSourceNodeHostO int onPositionChangedInterval_; uint64_t onPositionChangedCallbackId_ = 0; - double inputLatency_; - double outputLatency_; + double inputLatency_ = 0; + double outputLatency_ = 0; bool pitchCorrection_; void setOnPositionChangedCallbackId(uint64_t callbackId); + void initStretch(int channelCount, float sampleRate); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp index 365ac7533..bbde9fe53 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp @@ -72,10 +72,22 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, enqueueBuffer) { // when the same buffer is reused across threads and // buffer modification is not allowed on JS thread auto copiedBuffer = std::make_shared(*audioBufferHostObject->audioBuffer_); + std::shared_ptr tailBuffer = nullptr; + + if (pitchCorrection_ && !stretchHasBeenInit_) { + initStretch(copiedBuffer->getNumberOfChannels(), copiedBuffer->getSampleRate()); + int extraTailFrames = + static_cast((inputLatency_ + outputLatency_) * copiedBuffer->getSampleRate()); + tailBuffer = std::make_shared(copiedBuffer->getNumberOfChannels(), extraTailFrames, copiedBuffer->getSampleRate()); + tailBuffer->zero(); + stretchHasBeenInit_ = true; + } + auto event = [audioBufferQueueSourceNode, copiedBuffer, - bufferId = bufferId_](BaseAudioContext &) { - audioBufferQueueSourceNode->enqueueBuffer(copiedBuffer, bufferId); + bufferId = bufferId_, + tailBuffer](BaseAudioContext &) { + audioBufferQueueSourceNode->enqueueBuffer(copiedBuffer, bufferId, tailBuffer); }; audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h index 75c77208d..59503ae32 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h @@ -29,6 +29,7 @@ class AudioBufferQueueSourceNodeHostObject : public AudioBufferBaseSourceNodeHos protected: size_t bufferId_ = 0; uint64_t onBufferEndedCallbackId_ = 0; + bool stretchHasBeenInit_ = false; void setOnBufferEndedCallbackId(uint64_t callbackId); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp index 97783ad2d..a3558b435 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp @@ -178,12 +178,10 @@ void AudioBufferSourceNodeHostObject::setBuffer(const std::shared_ptr( RENDER_QUANTUM_SIZE, 1, audioBufferSourceNode->getContextSampleRate()); } else { - inputLatency_ = audioBufferSourceNode->getInputLatency(); - outputLatency_ = audioBufferSourceNode->getOutputLatency(); - if (pitchCorrection_) { + initStretch(static_cast(buffer->getNumberOfChannels()), buffer->getSampleRate()); int extraTailFrames = - static_cast((inputLatency_ + outputLatency_) * buffer->getSampleRate()); + static_cast((inputLatency_ + outputLatency_) * buffer->getSampleRate()); size_t totalSize = buffer->getSize() + extraTailFrames; copiedBuffer = std::make_shared( totalSize, buffer->getNumberOfChannels(), buffer->getSampleRate()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp index 08004579e..aa81cd0f7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp @@ -17,18 +17,18 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( const BaseAudioBufferSourceOptions &options) : AudioScheduledSourceNode(context, options), pitchCorrection_(options.pitchCorrection), - vReadIndex_(0.0) { - onPositionChangedInterval_ = static_cast(context->getSampleRate() * 0.1); - - detuneParam_ = std::make_shared( - options.detune, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); - playbackRateParam_ = std::make_shared( - options.playbackRate, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); - - playbackRateBuffer_ = std::make_shared( - RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate()); - - stretch_ = std::make_shared>(); + playbackRateBuffer_(std::make_shared( + RENDER_QUANTUM_SIZE * 3, channelCount_, context->getSampleRate())), + detuneParam_(std::make_shared( + options.detune, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context)), + playbackRateParam_(std::make_shared( + options.playbackRate, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context)), + vReadIndex_(0.0), + onPositionChangedInterval_(static_cast(context->getSampleRate() * 0.1)) {} + +void AudioBufferBaseSourceNode::initStretch( + const std::shared_ptr> &stretch) { + stretch_ = stretch; } std::shared_ptr AudioBufferBaseSourceNode::getDetuneParam() const { @@ -52,20 +52,6 @@ int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const { return onPositionChangedInterval_; } -double AudioBufferBaseSourceNode::getInputLatency() const { - if (pitchCorrection_) { - return std::max(static_cast(stretch_->inputLatency()) / getContextSampleRate(), 0.0); - } - return 0.0; -} - -double AudioBufferBaseSourceNode::getOutputLatency() const { - if (pitchCorrection_) { - return std::max(static_cast(stretch_->outputLatency()) / getContextSampleRate(), 0.0); - } - return 0.0; -} - void AudioBufferBaseSourceNode::unregisterOnPositionChangedCallback(uint64_t callbackId) { audioEventHandlerRegistry_->unregisterHandler(AudioEvent::POSITION_CHANGED, callbackId); } @@ -112,7 +98,7 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection( context->getSampleRate(), context->getCurrentSampleFrame()); - if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) { + if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled()) || stretch_ == nullptr) { processingBuffer->zero(); return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 9f7b709d4..2ea709444 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -19,14 +19,14 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); + void initStretch(const std::shared_ptr> &stretch); + [[nodiscard]] std::shared_ptr getDetuneParam() const; [[nodiscard]] std::shared_ptr getPlaybackRateParam() const; void setOnPositionChangedCallbackId(uint64_t callbackId); void setOnPositionChangedInterval(int interval); [[nodiscard]] int getOnPositionChangedInterval() const; - [[nodiscard]] double getInputLatency() const; - [[nodiscard]] double getOutputLatency() const; /// @note Thread safe, because does not access state of the node. void unregisterOnPositionChangedCallback(uint64_t callbackId); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index 5b45a11a4..3f2911963 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -23,17 +23,10 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options) : AudioBufferBaseSourceNode(context, options) { - buffers_ = {}; - stretch_->presetDefault(static_cast(channelCount_), context->getSampleRate()); - if (options.pitchCorrection) { // If pitch correction is enabled, add extra frames at the end // to compensate for processing latency. addExtraTailFrames_ = true; - int extraTailFrames = static_cast(stretch_->inputLatency() + stretch_->outputLatency()); - tailBuffer_ = std::make_shared(channelCount_, extraTailFrames, context->getSampleRate()); - - tailBuffer_->zero(); } isInitialized_.store(true, std::memory_order_release); @@ -66,9 +59,13 @@ void AudioBufferQueueSourceNode::pause() { isPaused_ = true; } -void AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr &buffer, size_t bufferId) { +void AudioBufferQueueSourceNode::enqueueBuffer(const std::shared_ptr &buffer, size_t bufferId, const std::shared_ptr &tailBuffer) { buffers_.emplace_back(bufferId, buffer); + if (tailBuffer != nullptr) { + tailBuffer_ = tailBuffer; + } + if (tailBuffer_ != nullptr) { addExtraTailFrames_ = true; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index ee95d5390..0e9b38c12 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -27,7 +27,10 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { void start(double when, double offset); void pause(); - void enqueueBuffer(const std::shared_ptr &buffer, size_t bufferId); + void enqueueBuffer( + const std::shared_ptr &buffer, + size_t bufferId, + const std::shared_ptr &tailBuffer); void dequeueBuffer(size_t bufferId); void clearBuffers(); void disable() override; @@ -48,7 +51,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { private: // User provided buffers - std::list>> buffers_; + std::list>> buffers_{}; bool isPaused_ = false; bool addExtraTailFrames_ = false; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 794dc318c..7eb9c0ae0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -83,7 +83,6 @@ void AudioBufferSourceNode::setBuffer( audioBuffer_ = audioBuffer; channelCount_ = buffer_->getNumberOfChannels(); loopEnd_ = buffer_->getDuration(); - stretch_->presetDefault(static_cast(channelCount_), buffer_->getSampleRate()); } void AudioBufferSourceNode::start(double when, double offset, double duration) { From 0e7e17c1dc896a8a0b8ac52538f446816b165e89 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Mar 2026 11:54:01 +0100 Subject: [PATCH 58/73] docs: added audio node docs --- ghdocs/audio-node.md | 280 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 ghdocs/audio-node.md diff --git a/ghdocs/audio-node.md b/ghdocs/audio-node.md new file mode 100644 index 000000000..69bf1a6ca --- /dev/null +++ b/ghdocs/audio-node.md @@ -0,0 +1,280 @@ +# How to create new AudioNode + +In this docs we present recommended design pattern for creating new AudioNode. + +## Layers + +In common each AudioNode has three layers: + +- Core (C++) + + Class implementing core audio processing logic. Should be implemented in highly performant manner using internal data structures (if possible). + +```cpp +class GainNode : public AudioNode { + public: + explicit GainNode(const std::shared_ptr &context, const GainOptions &options); + + [[nodiscard]] std::shared_ptr getGainParam() const; + + protected: + std::shared_ptr processNode( + const std::shared_ptr &processingBuffer, + int framesToProcess) override; + + private: + std::shared_ptr gainParam_; +}; +``` + +- Host Object (HO) + + Interop class between C++ and JS, implemented on C++ side. HO is returned from C++ to JS from BaseAudioContext factory methods. JS has its own interfaces that works as a counterpart of C++ HO. There is no strong typing mechanism between C++ and JS. Implementation is based on the alignment between C++ HO and JS interface. + +```cpp +class GainNodeHostObject : public AudioNodeHostObject { + public: + explicit GainNodeHostObject( + const std::shared_ptr &context, + const GainOptions &options); + + JSI_PROPERTY_GETTER_DECL(gain); + + private: + std::shared_ptr gainParam_; +}; +``` +```ts +export interface IGainNode extends IAudioNode { + readonly gain: IAudioParam; +} +``` + +- Typescript (JS) + + Elegant typescript wrapper around JS HO interface. + +```ts +class GainNode extends AudioNode { + readonly gain: AudioParam; + + constructor(context: BaseAudioContext, options?: TGainOptions) { + const gainNode: IGainNode = context.context.createGain(options || {}); + super(context, gainNode); + this.gain = new AudioParam(gainNode.gain, context); + } +} +``` + +## Core (C++) implementation + +Each AudioNode should implement one virtual method: +```cpp +std::shared_ptr processNode( + const std::shared_ptr &processingBuffer, + int framesToProcess) +``` + +It is responsible for AudioNode's processing logic. It gets input buffer as argument - `processingBus` and should return processed buffer. + +```cpp +std::shared_ptr GainNode::processNode( + const std::shared_ptr &processingBuffer, + int framesToProcess) { + std::shared_ptr context = context_.lock(); + if (context == nullptr) + return processingBuffer; + double time = context->getCurrentTime(); + auto gainParamValues = gainParam_->processARateParam(framesToProcess, time); + auto gainValues = gainParamValues->getChannel(0); + + for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i++) { + auto channel = processingBuffer->getChannel(i); + channel->multiply(*gainValues, framesToProcess); + } + + return processingBuffer; +} +``` + +There are a few rules that should be followed when implementing C++ AudioNode core. + +- **Thread safety**: Each AudioNode should be created in thread safe manner. + +- **Heap allocations**: Heap allocations are not allowed on Audio Thread, so all necessary data should be allocated in constructor, or pre-allocated on other thread and passed to AudioNode. + +- **Destructions** No destructions are allowed to happen on Audio Thread. In common AudioNode destruction are handled by already active AudioDestructor. If you need to perform some cleanup, you have to delegate it to AudioDestructor. + +- **No locks on Audio Thread**: Locks are not allowed on Audio Thread. Audio procssing have to be highly performant and efficient. + +- **No syscalls** Syscalls are not allowed on Audio Thread, so if you need to perform some work that requires syscalls, you have to delegate it to other thread. + +## HostObject implementation + +We can distinguish three types of AudioNode's JS methods: + +1. **getter** + +```ts +const fftSize = analyserNode.fftSize; +``` + +C++ counter part is `JSI_PROPERTY_GETTER`. It just returns some value. + +```cpp +JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, fftSize) { + return {fftSize_}; +} +``` + +2. **setter** + +C++ counterpart is `JSI_PROPERTY_SETTER`. It just receives some value. + +```ts +analyserNode.fftSize = 2048; +``` + +```cpp +JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { + auto analyserNode = std::static_pointer_cast(node_); + auto minDecibels = static_cast(value.getNumber()); + auto event = [analyserNode, minDecibels](BaseAudioContext&) { + analyserNode->setMinDecibels(minDecibels); + }; + analyserNode->scheduleAudioEvent(std::move(event)); + minDecibels_ = minDecibels; +} +``` + + +1. **function** + +C++ counterpart is `JSI_HOST_FUNCTION`. It is a common function that can receive arguments and return some value. + +```ts +const fftOutput = new Uint8Array(analyser.frequencyBinCount); +analyserNode.getByteFrequencyData(fftOutput); +``` + +```cpp +SI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getByteFrequencyData) { + auto arrayBuffer = + args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); + auto data = arrayBuffer.data(runtime); + auto length = static_cast(arrayBuffer.size(runtime)); + + auto analyserNode = std::static_pointer_cast(node_); + analyserNode->getByteFrequencyData(data, length); + + return jsi::Value::undefined(); +} +``` + +All methods should be registerd in C++ HO constructor: + +```cpp +AnalyserNodeHostObject::AnalyserNodeHostObject(const std::shared_ptr& context, const AnalyserOptions &options) + : /* ... */ { + addGetters(JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, fftSize)); + addSetters(JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, fftSize)); + addFunctions(JSI_EXPORT_FUNCTION(AnalyserNodeHostObject, getByteFrequencyData),); +} +``` + +#### Shadow state (C++) + +Shadow state is mechanism introduced in order to make communication between JS and Audio thread lock-free. AudioNodeHostObject stores the set of properties modified only by JS thread (the same set C++ AudioNode has). Everytime we want to access some property from JS, we can just return property from shadow state, when we modify some property we have to update shadow state and schedule update event on Audio Event Loop (SPSC). By following that manner we can skip accessing AudioNode state, that is also accessed by Audio Thread - no need to lock or use atomic variables. + +```cpp +class OscillatorNodeHostObject : public AudioScheduledSourceNodeHostObject { + public: + /* ... */ + + JSI_PROPERTY_GETTER_DECL(type); + JSI_PROPERTY_SETTER_DECL(type); + + private: + /* ... */ + OscillatorType type_; +}; +``` + +```cpp +JSI_PROPERTY_GETTER_IMPL(OscillatorNodeHostObject, type) { + return jsi::String::createFromUtf8(runtime, js_enum_parser::oscillatorTypeToString(type_)); +} + +JSI_PROPERTY_SETTER_IMPL(OscillatorNodeHostObject, type) { + auto oscillatorNode = std::static_pointer_cast(node_); + auto type = js_enum_parser::oscillatorTypeFromString(value.asString(runtime).utf8(runtime)); + + auto event = [oscillatorNode, type](BaseAudioContext &) { + oscillatorNode->setType(type); + }; + type_ = type; + + oscillatorNode->scheduleAudioEvent(std::move(event)); +} +``` + +#### Communication between JS Thread and Audio Thread + +**getters** and **setters** + +1. Property is primitive and is not modified by Audio Thread. + + Shadow state design pattern should be followed. + +2. Property is not primitive and is not modified by Audio Thread. + + In common it should be stored in TS layer and copied to AudioNode + +3. Property is primitive and can be modified by Audio Thread. + + In C++ core it should be an atomic variable that allows to access it in thread-safe manner from both threads. + +```cpp +class AudioParam { +public: + /* ... */ + + [[nodiscard]] inline float getValue() const noexcept { + return value_.load(std::memory_order_relaxed); + } + + inline void setValue(float value) { + value_.store(std::clamp(value, minValue_, maxValue_), std::memory_order_release); + } + + /* ... */ + +private: + std::atomic value_; + + /* ... */ +}; +``` + +```cpp +JSI_PROPERTY_GETTER_IMPL(AudioParamHostObject, value) { + return {param_->getValue()}; +} + +JSI_PROPERTY_SETTER_IMPL(AudioParamHostObject, value) { + auto event = [param = param_, value = static_cast(value.getNumber())](BaseAudioContext &) { + param->setValue(value); + }; + + param_->scheduleAudioEvent(std::move(event)); +} +``` + +4. Property is not primitive and can be modified by Audio Thread. + In C++ core triple buffer pattern should be followed. It allows to have one copy of property for reader, one for writer and one for pending update. On each update we just swap pending update with writer, and on each read we just read from reader. In that manner we can skip locks and just operate on atomic indices. + + Check AnalyserNode implementation for example. + +**functions** + +Function's should follow the same thread-safe lock-free patterns as getters/setters. Set of properties read/write by the function determines mechanisms that should be used in implementation. From c799908771413531ec58c2b29ba7e878be87118d Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Mar 2026 13:05:26 +0100 Subject: [PATCH 59/73] refactor: biquad filter thread safety improvements --- .../common/cpp/audioapi/core/AudioNode.h | 4 + .../cpp/audioapi/core/BaseAudioContext.cpp | 4 - .../cpp/audioapi/core/BaseAudioContext.h | 1 - .../core/effects/BiquadFilterNode.cpp | 241 ++++++++---------- .../audioapi/core/effects/BiquadFilterNode.h | 39 ++- .../audioapi/core/effects/IIRFilterNode.cpp | 5 +- .../audioapi/core/sources/OscillatorNode.cpp | 2 +- .../core/effects/biquad/BiquadFilterTest.cpp | 63 ++--- .../core/effects/biquad/BiquadFilterTest.h | 2 +- 9 files changed, 155 insertions(+), 206 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 11f4c7434..56a6536d4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -45,6 +45,10 @@ class AudioNode : public std::enable_shared_from_this { return DEFAULT_SAMPLE_RATE; } + float getNyquistFrequency() const { + return getContextSampleRate() / 2.0f; + } + /// @note JS Thread only bool isEnabled() const; /// @note JS Thread only diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index d9057646e..5776d8e72 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -220,10 +220,6 @@ std::shared_ptr BaseAudioContext::createWaveShaper( return waveShaper; } -float BaseAudioContext::getNyquistFrequency() const { - return getSampleRate() / 2.0f; -} - std::shared_ptr BaseAudioContext::getBasicWaveForm(OscillatorType type) { switch (type) { case OscillatorType::SINE: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index 00daf8cc4..e0a54de8b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -107,7 +107,6 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr createWaveShaper(const WaveShaperOptions &options); std::shared_ptr getBasicWaveForm(OscillatorType type); - [[nodiscard]] float getNyquistFrequency() const; std::shared_ptr getGraphManager() const; std::shared_ptr getAudioEventHandlerRegistry() const; const RuntimeRegistry &getRuntimeRegistry() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index a68044029..909e76feb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -43,7 +43,7 @@ BiquadFilterNode::BiquadFilterNode( const std::shared_ptr &context, const BiquadFilterOptions &options) : AudioNode(context, options), - frequencyParam_(std::make_shared(options.frequency, 0.0f, context->getNyquistFrequency(), context)), + frequencyParam_(std::make_shared(options.frequency, 0.0f, getNyquistFrequency(), context)), detuneParam_(std::make_shared(options.detune, -OCTAVE_RANGE * LOG2_MOST_POSITIVE_SINGLE_FLOAT, OCTAVE_RANGE * LOG2_MOST_POSITIVE_SINGLE_FLOAT, context)), QParam_(std::make_shared(options.Q, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context)), gainParam_(std::make_shared(options.gain, MOST_NEGATIVE_SINGLE_FLOAT, BIQUAD_GAIN_DB_FACTOR * LOG10_MOST_POSITIVE_SINGLE_FLOAT, context)), @@ -56,7 +56,7 @@ BiquadFilterNode::BiquadFilterNode( } void BiquadFilterNode::setType(BiquadFilterType type) { - type_ = type; + type_.store(type, std::memory_order_release); } std::shared_ptr BiquadFilterNode::getFrequencyParam() const { @@ -99,21 +99,15 @@ void BiquadFilterNode::getFrequencyResponse( float *magResponseOutput, float *phaseResponseOutput, const size_t length) { -#if !RN_AUDIO_API_TEST - applyFilter(); -#endif - - // Use double precision for later calculations - auto b0 = static_cast(b0_); - auto b1 = static_cast(b1_); - auto b2 = static_cast(b2_); - auto a1 = static_cast(a1_); - auto a2 = static_cast(a2_); - - std::shared_ptr context = context_.lock(); - if (!context) - return; - float nyquist = context->getNyquistFrequency(); + auto frequency = frequencyParam_->getValue(); + auto Q = QParam_->getValue(); + auto gain = gainParam_->getValue(); + auto detune = detuneParam_->getValue(); + auto type = type_.load(std::memory_order_acquire); + + auto coeffs = applyFilter(frequency, Q, gain, detune, type); + + float nyquist = getNyquistFrequency(); for (size_t i = 0; i < length; i++) { // Convert from frequency in Hz to normalized frequency [0, 1] @@ -128,37 +122,20 @@ void BiquadFilterNode::getFrequencyResponse( double omega = -PI * normalizedFreq; auto z = std::complex(std::cos(omega), std::sin(omega)); - auto response = (b0 + (b1 + b2 * z) * z) / (std::complex(1, 0) + (a1 + a2 * z) * z); + auto response = (coeffs.b0 + (coeffs.b1 + coeffs.b2 * z) * z) / (std::complex(1, 0) + (coeffs.a1 + coeffs.a2 * z) * z); magResponseOutput[i] = static_cast(std::abs(response)); phaseResponseOutput[i] = static_cast(atan2(imag(response), real(response))); } } -void BiquadFilterNode::setNormalizedCoefficients( - float b0, - float b1, - float b2, - float a0, - float a1, - float a2) { - auto a0Inverted = 1.0f / a0; - b0_ = b0 * a0Inverted; - b1_ = b1 * a0Inverted; - b2_ = b2 * a0Inverted; - a1_ = a1 * a0Inverted; - a2_ = a2 * a0Inverted; -} - -void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) { // Limit frequency to [0, 1] range if (frequency >= 1.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float g = std::pow(10.0f, 0.05f * Q); @@ -168,17 +145,15 @@ void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) { float cosW = std::cos(theta); float beta = (1 - cosW) / 2; - setNormalizedCoefficients(beta, 2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); + return setNormalizedCoefficients(beta, 2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); } -void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) { if (frequency >= 1.0f) { - setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float g = std::pow(10.0f, 0.05f * Q); @@ -188,40 +163,36 @@ void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) { float cosW = std::cos(theta); float beta = (1 + cosW) / 2; - setNormalizedCoefficients(beta, -2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); + return setNormalizedCoefficients(beta, -2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); } -void BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) { // Limit frequency to [0, 1] range if (frequency <= 0.0f || frequency >= 1.0f) { - setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } // Limit Q to positive values if (Q <= 0.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - setNormalizedCoefficients(alpha, 0.0f, -alpha, 1.0f + alpha, -2 * cosW, 1.0f - alpha); + return setNormalizedCoefficients(alpha, 0.0f, -alpha, 1.0f + alpha, -2 * cosW, 1.0f - alpha); } -void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency >= 1.0f) { - setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; @@ -229,7 +200,7 @@ void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) { float cosW = std::cos(w0); float gamma = 2.0f * std::sqrt(A) * alpha; - setNormalizedCoefficients( + return setNormalizedCoefficients( A * (A + 1 - (A - 1) * cosW + gamma), 2.0f * A * (A - 1 - (A + 1) * cosW), A * (A + 1 - (A - 1) * cosW - gamma), @@ -238,17 +209,15 @@ void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) { A + 1 + (A - 1) * cosW - gamma); } -void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency >= 1.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; @@ -258,7 +227,7 @@ void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) { float cosW = std::cos(w0); float gamma = 2.0f * std::sqrt(A) * alpha; - setNormalizedCoefficients( + return setNormalizedCoefficients( A * (A + 1 + (A - 1) * cosW + gamma), -2.0f * A * (A - 1 + (A + 1) * cosW), A * (A + 1 + (A - 1) * cosW - gamma), @@ -267,159 +236,161 @@ void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) { A + 1 - (A - 1) * cosW - gamma); } -void BiquadFilterNode::setPeakingCoefficients(float frequency, float Q, float gain) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setPeakingCoefficients(float frequency, float Q, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency <= 0.0f || frequency >= 1.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - setNormalizedCoefficients( + return setNormalizedCoefficients( 1 + alpha * A, -2 * cosW, 1 - alpha * A, 1 + alpha / A, -2 * cosW, 1 - alpha / A); } -void BiquadFilterNode::setNotchCoefficients(float frequency, float Q) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setNotchCoefficients(float frequency, float Q) { if (frequency <= 0.0f || frequency >= 1.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - setNormalizedCoefficients(1.0f, -2 * cosW, 1.0f, 1 + alpha, -2 * cosW, 1 - alpha); + return setNormalizedCoefficients(1.0f, -2 * cosW, 1.0f, 1 + alpha, -2 * cosW, 1 - alpha); } -void BiquadFilterNode::setAllpassCoefficients(float frequency, float Q) { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setAllpassCoefficients(float frequency, float Q) { if (frequency <= 0.0f || frequency >= 1.0f) { - setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - setNormalizedCoefficients(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); - return; + return setNormalizedCoefficients(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - setNormalizedCoefficients(1 - alpha, -2 * cosW, 1 + alpha, 1 + alpha, -2 * cosW, 1 - alpha); + return setNormalizedCoefficients(1 - alpha, -2 * cosW, 1 + alpha, 1 + alpha, -2 * cosW, 1 - alpha); } -void BiquadFilterNode::applyFilter() { +BiquadFilterNode::FilterCoefficients BiquadFilterNode::setNormalizedCoefficients( + float b0, + float b1, + float b2, + float a0, + float a1, + float a2) { + auto a0Inverted = 1.0f / a0; + return {b0 * a0Inverted, b1 * a0Inverted, b2 * a0Inverted, a1 * a0Inverted, a2 * a0Inverted}; +} + +BiquadFilterNode::FilterCoefficients BiquadFilterNode::applyFilter(float frequency, float Q, float gain, float detune, BiquadFilterType type) { // NyquistFrequency is half of the sample rate. // Normalized frequency is therefore: // frequency / (sampleRate / 2) = (2 * frequency) / sampleRate - float normalizedFrequency; - double currentTime; - if (std::shared_ptr context = context_.lock()) { - currentTime = context->getCurrentTime(); - float frequency = frequencyParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); - normalizedFrequency = frequency / context->getNyquistFrequency(); - } else { - return; - } - float detune = detuneParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); - auto Q = QParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); - auto gain = gainParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); + float normalizedFrequency = frequency / getNyquistFrequency(); if (detune != 0.0f) { normalizedFrequency *= std::pow(2.0f, detune / 1200.0f); } - switch (type_) { + FilterCoefficients coeffs = {1.0, 0.0, 0.0, 0.0, 0.0}; + + switch (type) { case BiquadFilterType::LOWPASS: - setLowpassCoefficients(normalizedFrequency, Q); + coeffs = setLowpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::HIGHPASS: - setHighpassCoefficients(normalizedFrequency, Q); + coeffs = setHighpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::BANDPASS: - setBandpassCoefficients(normalizedFrequency, Q); + coeffs = setBandpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::LOWSHELF: - setLowshelfCoefficients(normalizedFrequency, gain); + coeffs = setLowshelfCoefficients(normalizedFrequency, gain); break; case BiquadFilterType::HIGHSHELF: - setHighshelfCoefficients(normalizedFrequency, gain); + coeffs = setHighshelfCoefficients(normalizedFrequency, gain); break; case BiquadFilterType::PEAKING: - setPeakingCoefficients(normalizedFrequency, Q, gain); + coeffs = setPeakingCoefficients(normalizedFrequency, Q, gain); break; case BiquadFilterType::NOTCH: - setNotchCoefficients(normalizedFrequency, Q); + coeffs = setNotchCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::ALLPASS: - setAllpassCoefficients(normalizedFrequency, Q); + coeffs = setAllpassCoefficients(normalizedFrequency, Q); break; default: break; } + + return coeffs; } std::shared_ptr BiquadFilterNode::processNode( const std::shared_ptr &processingBuffer, int framesToProcess) { - auto numChannels = processingBuffer->getNumberOfChannels(); + if (std::shared_ptr context = context_.lock()) { + auto currentTime = context->getCurrentTime(); + float frequency = frequencyParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); + float detune = detuneParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); + auto Q = QParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); + auto gain = gainParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); + auto type = type_.load(std::memory_order_relaxed); - applyFilter(); + auto coeffs = applyFilter(frequency, Q, gain, detune, type); - // local copies for micro-optimization - float b0 = b0_; - float b1 = b1_; - float b2 = b2_; - float a1 = a1_; - float a2 = a2_; + float x1, x2, y1, y2; - float x1, x2, y1, y2; + auto numChannels = processingBuffer->getNumberOfChannels(); - for (size_t c = 0; c < numChannels; ++c) { - auto channel = processingBuffer->getChannel(c)->subSpan(framesToProcess); + for (size_t c = 0; c < numChannels; ++c) { + auto channel = processingBuffer->getChannel(c)->subSpan(framesToProcess); - x1 = x1_[c]; - x2 = x2_[c]; - y1 = y1_[c]; - y2 = y2_[c]; + x1 = x1_[c]; + x2 = x2_[c]; + y1 = y1_[c]; + y2 = y2_[c]; - for (float &sample : channel) { - auto input = sample; - auto output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + for (float &sample: channel) { + auto input = sample; + auto output = coeffs.b0 * input + coeffs.b1 * x1 + coeffs.b2 * x2 - coeffs.a1 * y1 - coeffs.a2 * y2; - // Avoid denormalized numbers - if (std::abs(output) < 1e-15f) { - output = 0.0f; - } + // Avoid denormalized numbers + if (std::abs(output) < 1e-15f) { + output = 0.0f; + } - sample = output; + sample = static_cast(output); - x2 = x1; - x1 = input; - y2 = y1; - y1 = output; - } + x2 = x1; + x1 = input; + y2 = y1; + y1 = static_cast(output); + } - x1_[c] = x1; - x2_[c] = x2; - y1_[c] = y1; - y2_[c] = y2; + x1_[c] = x1; + x2_[c] = x2; + y1_[c] = y1; + y2_[c] = y2; + } + } else { + processingBuffer->zero(); } return processingBuffer; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index fb999b1b4..4d97fee9e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -70,11 +70,11 @@ class BiquadFilterNode : public AudioNode { int framesToProcess) override; private: - std::shared_ptr frequencyParam_; - std::shared_ptr detuneParam_; - std::shared_ptr QParam_; - std::shared_ptr gainParam_; - BiquadFilterType type_; + const std::shared_ptr frequencyParam_; + const std::shared_ptr detuneParam_; + const std::shared_ptr QParam_; + const std::shared_ptr gainParam_; + std::atomic type_; // delayed samples, one per channel AudioArray x1_; @@ -82,23 +82,20 @@ class BiquadFilterNode : public AudioNode { AudioArray y1_; AudioArray y2_; - // coefficients - float b0_ = 1.0; - float b1_ = 0; - float b2_ = 0; - float a1_ = 0; - float a2_ = 0; + struct alignas(64) FilterCoefficients { + double b0, b1, b2, a1, a2; + }; - void setNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); - void setLowpassCoefficients(float frequency, float Q); - void setHighpassCoefficients(float frequency, float Q); - void setBandpassCoefficients(float frequency, float Q); - void setLowshelfCoefficients(float frequency, float gain); - void setHighshelfCoefficients(float frequency, float gain); - void setPeakingCoefficients(float frequency, float Q, float gain); - void setNotchCoefficients(float frequency, float Q); - void setAllpassCoefficients(float frequency, float Q); - void applyFilter(); + static FilterCoefficients setLowpassCoefficients(float frequency, float Q); + static FilterCoefficients setHighpassCoefficients(float frequency, float Q); + static FilterCoefficients setBandpassCoefficients(float frequency, float Q); + static FilterCoefficients setLowshelfCoefficients(float frequency, float gain); + static FilterCoefficients setHighshelfCoefficients(float frequency, float gain); + static FilterCoefficients setPeakingCoefficients(float frequency, float Q, float gain); + static FilterCoefficients setNotchCoefficients(float frequency, float Q); + static FilterCoefficients setAllpassCoefficients(float frequency, float Q); + static FilterCoefficients setNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); + FilterCoefficients applyFilter(float frequency, float Q, float gain, float detune, BiquadFilterType type); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 7e12ed78f..3592fb022 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -69,10 +69,7 @@ void IIRFilterNode::getFrequencyResponse( float *magResponseOutput, float *phaseResponseOutput, size_t length) const { - std::shared_ptr context = context_.lock(); - if (context == nullptr) - return; - float nyquist = context->getNyquistFrequency(); + float nyquist = getNyquistFrequency(); for (size_t k = 0; k < length; ++k) { float normalizedFreq = frequencyArray[k] / nyquist; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index 58e5c922b..2f1037ae5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -13,7 +13,7 @@ OscillatorNode::OscillatorNode( const OscillatorOptions &options) : AudioScheduledSourceNode(context, options) { frequencyParam_ = std::make_shared( - options.frequency, -context->getNyquistFrequency(), context->getNyquistFrequency(), context); + options.frequency, -getNyquistFrequency(), getNyquistFrequency(), context); detuneParam_ = std::make_shared( options.detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp index fc8d006b1..a618d5c2f 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp @@ -7,77 +7,61 @@ namespace audioapi { void BiquadFilterTest::expectCoefficientsNear( - const BiquadFilterNode &biquadNode, + const BiquadFilterNode::FilterCoefficients &actual, const BiquadCoefficients &expected) { - EXPECT_NEAR(biquadNode.b0_, expected.b0, tolerance); - EXPECT_NEAR(biquadNode.b1_, expected.b1, tolerance); - EXPECT_NEAR(biquadNode.b2_, expected.b2, tolerance); - EXPECT_NEAR(biquadNode.a1_, expected.a1, tolerance); - EXPECT_NEAR(biquadNode.a2_, expected.a2, tolerance); + EXPECT_NEAR(actual.b0, expected.b0, tolerance); + EXPECT_NEAR(actual.b1, expected.b1, tolerance); + EXPECT_NEAR(actual.b2, expected.b2, tolerance); + EXPECT_NEAR(actual.a1, expected.a1, tolerance); + EXPECT_NEAR(actual.a2, expected.a2, tolerance); } void BiquadFilterTest::testLowpass(float frequency, float Q) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setLowpassCoefficients(normalizedFrequency, Q); - expectCoefficientsNear(node, calculateLowpassCoefficients(normalizedFrequency, Q)); + auto coeffs = BiquadFilterNode::setLowpassCoefficients(normalizedFrequency, Q); + expectCoefficientsNear(coeffs, calculateLowpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testHighpass(float frequency, float Q) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setHighpassCoefficients(normalizedFrequency, Q); - expectCoefficientsNear(node, calculateHighpassCoefficients(normalizedFrequency, Q)); + auto coeffs = BiquadFilterNode::setHighpassCoefficients(normalizedFrequency, Q); + expectCoefficientsNear(coeffs, calculateHighpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testBandpass(float frequency, float Q) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setBandpassCoefficients(normalizedFrequency, Q); - expectCoefficientsNear(node, calculateBandpassCoefficients(normalizedFrequency, Q)); + auto coeffs = BiquadFilterNode::setBandpassCoefficients(normalizedFrequency, Q); + expectCoefficientsNear(coeffs, calculateBandpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testNotch(float frequency, float Q) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setNotchCoefficients(normalizedFrequency, Q); - expectCoefficientsNear(node, calculateNotchCoefficients(normalizedFrequency, Q)); + auto coeffs = BiquadFilterNode::setNotchCoefficients(normalizedFrequency, Q); + expectCoefficientsNear(coeffs, calculateNotchCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testAllpass(float frequency, float Q) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setAllpassCoefficients(normalizedFrequency, Q); - expectCoefficientsNear(node, calculateAllpassCoefficients(normalizedFrequency, Q)); + auto coeffs = BiquadFilterNode::setAllpassCoefficients(normalizedFrequency, Q); + expectCoefficientsNear(coeffs, calculateAllpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setPeakingCoefficients(normalizedFrequency, Q, gain); - expectCoefficientsNear(node, calculatePeakingCoefficients(normalizedFrequency, Q, gain)); + auto coeffs = BiquadFilterNode::setPeakingCoefficients(normalizedFrequency, Q, gain); + expectCoefficientsNear(coeffs, calculatePeakingCoefficients(normalizedFrequency, Q, gain)); } void BiquadFilterTest::testLowshelf(float frequency, float gain) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setLowshelfCoefficients(normalizedFrequency, gain); - expectCoefficientsNear(node, calculateLowshelfCoefficients(normalizedFrequency, gain)); + auto coeffs = BiquadFilterNode::setLowshelfCoefficients(normalizedFrequency, gain); + expectCoefficientsNear(coeffs, calculateLowshelfCoefficients(normalizedFrequency, gain)); } void BiquadFilterTest::testHighshelf(float frequency, float gain) { - auto node = BiquadFilterNode(context, BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; - - node.setHighshelfCoefficients(normalizedFrequency, gain); - expectCoefficientsNear(node, calculateHighshelfCoefficients(normalizedFrequency, gain)); + auto coeffs = BiquadFilterNode::setHighshelfCoefficients(normalizedFrequency, gain); + expectCoefficientsNear(coeffs, calculateHighshelfCoefficients(normalizedFrequency, gain)); } INSTANTIATE_TEST_SUITE_P( @@ -225,7 +209,8 @@ TEST_F(BiquadFilterTest, GetFrequencyResponse) { float Q = 1.0f; float normalizedFrequency = frequency / nyquistFrequency; - node.setLowpassCoefficients(normalizedFrequency, Q); + node.frequencyParam_->setValue(frequency); + node.QParam_->setValue(Q); auto coeffs = calculateLowpassCoefficients(normalizedFrequency, Q); std::vector TestFrequencies = { diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h index b47d00974..952e77fd9 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h @@ -23,7 +23,7 @@ class BiquadFilterTest : public ::testing::Test { 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); } - void expectCoefficientsNear(const BiquadFilterNode &node, const BiquadCoefficients &expected); + void expectCoefficientsNear(const BiquadFilterNode::FilterCoefficients &actual, const BiquadCoefficients &expected); void testLowpass(float frequency, float Q); void testHighpass(float frequency, float Q); void testBandpass(float frequency, float Q); From 9bfd34ad6551dffa5444ef3ed8b83f8c642a7b2a Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Mar 2026 13:06:00 +0100 Subject: [PATCH 60/73] ci: lint --- .../common/cpp/audioapi/core/AudioNode.h | 2 +- .../common/cpp/audioapi/core/effects/BiquadFilterNode.h | 6 ++++-- .../cpp/test/src/core/effects/biquad/BiquadFilterTest.h | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 56a6536d4..841749f8d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -46,7 +46,7 @@ class AudioNode : public std::enable_shared_from_this { } float getNyquistFrequency() const { - return getContextSampleRate() / 2.0f; + return getContextSampleRate() / 2.0f; } /// @note JS Thread only diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index 4d97fee9e..cb9ad291e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -94,8 +94,10 @@ class BiquadFilterNode : public AudioNode { static FilterCoefficients setPeakingCoefficients(float frequency, float Q, float gain); static FilterCoefficients setNotchCoefficients(float frequency, float Q); static FilterCoefficients setAllpassCoefficients(float frequency, float Q); - static FilterCoefficients setNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); - FilterCoefficients applyFilter(float frequency, float Q, float gain, float detune, BiquadFilterType type); + static FilterCoefficients + setNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); + FilterCoefficients + applyFilter(float frequency, float Q, float gain, float detune, BiquadFilterType type); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h index 952e77fd9..17fe99703 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h @@ -23,7 +23,9 @@ class BiquadFilterTest : public ::testing::Test { 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); } - void expectCoefficientsNear(const BiquadFilterNode::FilterCoefficients &actual, const BiquadCoefficients &expected); + void expectCoefficientsNear( + const BiquadFilterNode::FilterCoefficients &actual, + const BiquadCoefficients &expected); void testLowpass(float frequency, float Q); void testHighpass(float frequency, float Q); void testBandpass(float frequency, float Q); From edda4bc3c85f6228763330b6a54f82d3770d6ef4 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Mon, 2 Mar 2026 13:58:27 +0100 Subject: [PATCH 61/73] docs: doxygen comments for AudioThread-only methods --- .../AudioBufferBaseSourceNodeHostObject.cpp | 2 +- .../common/cpp/audioapi/core/AudioParam.h | 21 +++++++++++++++---- .../cpp/audioapi/core/analysis/AnalyserNode.h | 11 +++++++++- .../core/destinations/AudioDestinationNode.h | 4 ++++ .../audioapi/core/effects/BiquadFilterNode.h | 2 ++ .../cpp/audioapi/core/effects/ConvolverNode.h | 3 +++ .../cpp/audioapi/core/effects/DelayNode.h | 2 +- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../cpp/audioapi/core/effects/IIRFilterNode.h | 2 +- .../audioapi/core/effects/StereoPannerNode.h | 2 +- .../audioapi/core/effects/WaveShaperNode.h | 3 +++ .../core/sources/AudioBufferBaseSourceNode.h | 11 +++++++--- .../core/sources/AudioBufferQueueSourceNode.h | 11 +++++++++- .../core/sources/AudioBufferSourceNode.h | 15 ++++++++++--- .../core/sources/AudioScheduledSourceNode.h | 11 +++++++++- .../core/sources/ConstantSourceNode.cpp | 10 ++++++--- .../core/sources/ConstantSourceNode.h | 2 +- .../audioapi/core/sources/OscillatorNode.cpp | 4 ---- .../audioapi/core/sources/OscillatorNode.h | 5 ++++- 19 files changed, 96 insertions(+), 27 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp index fbe69d049..090525bb6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp @@ -13,7 +13,7 @@ namespace audioapi { AudioBufferBaseSourceNodeHostObject::AudioBufferBaseSourceNodeHostObject( const std::shared_ptr &node, const BaseAudioBufferSourceOptions &options) - : AudioScheduledSourceNodeHostObject(node, options), pitchCorrection_(options.pitchCorrection) { + : AudioScheduledSourceNodeHostObject(node, options), pitchCorrection_(options.pitchCorrection) { auto sourceNode = std::static_pointer_cast(node_); detuneParam_ = std::make_shared(sourceNode->getDetuneParam()); playbackRateParam_ = std::make_shared(sourceNode->getPlaybackRateParam()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index dbc208754..2b4623c93 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -43,16 +43,29 @@ class AudioParam { value_.store(std::clamp(value, minValue_, maxValue_), std::memory_order_release); } + /// @note Audio Thread only void setValueAtTime(float value, double startTime); + + /// @note Audio Thread only void linearRampToValueAtTime(float value, double endTime); + + /// @note Audio Thread only void exponentialRampToValueAtTime(float value, double endTime); + + /// @note Audio Thread only void setTargetAtTime(float target, double startTime, double timeConstant); + + /// @note Audio Thread only void setValueCurveAtTime( const std::shared_ptr &values, size_t length, double startTime, double duration); + + /// @note Audio Thread only void cancelScheduledValues(double cancelTime); + + /// @note Audio Thread only void cancelAndHoldAtTime(double cancelTime); template < @@ -69,16 +82,16 @@ class AudioParam { /// Audio-Thread only methods /// These methods are called only from the Audio rendering thread. - // Audio-Thread only (indirectly through AudioNode::connectParam by AudioGraphManager) + /// @note Audio Thread only void addInputNode(AudioNode *node); - // Audio-Thread only (indirectly through AudioNode::disconnectParam by AudioGraphManager) + /// @note Audio Thread only void removeInputNode(AudioNode *node); - // Audio-Thread only + /// @note Audio Thread only std::shared_ptr processARateParam(int framesToProcess, double time); - // Audio-Thread only + /// @note Audio Thread only float processKRateParam(int framesToProcess, double time); private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 8f889b9d1..2c2efe939 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -26,14 +26,23 @@ class AnalyserNode : public AudioNode { const std::shared_ptr &context, const AnalyserOptions &options); - void setFFTSize(int fftSize); void setMinDecibels(float minDecibels); void setMaxDecibels(float maxDecibels); void setSmoothingTimeConstant(float smoothingTimeConstant); + /// @note JS Thread only + void setFFTSize(int fftSize); + + /// @note JS Thread only void getFloatFrequencyData(float *data, int length); + + /// @note JS Thread only void getByteFrequencyData(uint8_t *data, int length); + + /// @note JS Thread only void getFloatTimeDomainData(float *data, int length); + + /// @note JS Thread only void getByteTimeDomainData(uint8_t *data, int length); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h index 59a7111da..42d54b689 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/destinations/AudioDestinationNode.h @@ -16,9 +16,13 @@ class AudioDestinationNode : public AudioNode { public: explicit AudioDestinationNode(const std::shared_ptr &context); + /// @note Thread safe std::size_t getCurrentSampleFrame() const; + + /// @note Thread safe double getCurrentTime() const; + /// @note Audio Thread only void renderAudio(const std::shared_ptr &audioData, int numFrames); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index cb9ad291e..b1db098fe 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -58,6 +58,8 @@ class BiquadFilterNode : public AudioNode { [[nodiscard]] std::shared_ptr getDetuneParam() const; [[nodiscard]] std::shared_ptr getQParam() const; [[nodiscard]] std::shared_ptr getGainParam() const; + + /// @note JS Thread only void getFrequencyResponse( const float *frequencyArray, float *magResponseOutput, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h index b5b410275..48ebf088a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h @@ -25,7 +25,10 @@ class ConvolverNode : public AudioNode { const std::shared_ptr &context, const ConvolverOptions &options); + /// @note Audio Thread only void setNormalize(bool normalize); + + /// @note Audio Thread only void setBuffer(const std::shared_ptr &buffer); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index 5c0e96545..d380952c1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -29,7 +29,7 @@ class DelayNode : public AudioNode { int framesToProcess, size_t &operationStartingIndex, BufferAction action); - std::shared_ptr delayTimeParam_; + const std::shared_ptr delayTimeParam_; std::shared_ptr delayBuffer_; size_t readIndex_ = 0; bool signalledToStop_ = false; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h index 86b18cc77..192a69979 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h @@ -22,7 +22,7 @@ class GainNode : public AudioNode { int framesToProcess) override; private: - std::shared_ptr gainParam_; + const std::shared_ptr gainParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 1a2fbb4b8..2d4639b24 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -44,7 +44,7 @@ class IIRFilterNode : public AudioNode { const std::shared_ptr &context, const IIRFilterOptions &options); - /// @note Thread safe, because feedforward_ and feedback_ are readonly after construction. + /// @note Audio Thread only void getFrequencyResponse( const float *frequencyArray, float *magResponseOutput, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h index 2a5c862f8..f78134619 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/StereoPannerNode.h @@ -25,7 +25,7 @@ class StereoPannerNode : public AudioNode { int framesToProcess) override; private: - std::shared_ptr panParam_; + const std::shared_ptr panParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 23aabfb92..3beba7a48 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -24,7 +24,10 @@ class WaveShaperNode : public AudioNode { const std::shared_ptr &context, const WaveShaperOptions &options); + /// @note Audio Thread only void setOversample(OverSampleType); + + /// @note Audio Thread only void setCurve(const std::shared_ptr &curve); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 2ea709444..498a50589 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -19,16 +19,21 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); + /// @note JS Thread only void initStretch(const std::shared_ptr> &stretch); [[nodiscard]] std::shared_ptr getDetuneParam() const; [[nodiscard]] std::shared_ptr getPlaybackRateParam() const; + /// @note Audio Thread only void setOnPositionChangedCallbackId(uint64_t callbackId); + + /// @note Audio Thread only void setOnPositionChangedInterval(int interval); + + /// TODO remove and refactor [[nodiscard]] int getOnPositionChangedInterval() const; - /// @note Thread safe, because does not access state of the node. void unregisterOnPositionChangedCallback(uint64_t callbackId); protected: @@ -40,8 +45,8 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { std::shared_ptr playbackRateBuffer_; // k-rate params - std::shared_ptr detuneParam_; - std::shared_ptr playbackRateParam_; + const std::shared_ptr detuneParam_; + const std::shared_ptr playbackRateParam_; // internal helper double vReadIndex_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index 0e9b38c12..3ee406d2c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -21,23 +21,32 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); + /// @note Audio Thread only void stop(double when) override; void start(double when) override; + /// @note Audio Thread only void start(double when, double offset); + /// @note Audio Thread only void pause(); + /// @note Audio Thread only void enqueueBuffer( const std::shared_ptr &buffer, size_t bufferId, const std::shared_ptr &tailBuffer); + + /// @note Audio Thread only void dequeueBuffer(size_t bufferId); + /// @note Audio Thread only void clearBuffers(); + + /// @note Audio Thread only void disable() override; + /// @note Audio Thread only void setOnBufferEndedCallbackId(uint64_t callbackId); - /// @note Thread safe, because does not access state of the node. void unregisterOnBufferEndedCallback(uint64_t callbackId); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index 0a9177fe4..ca923847c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -19,25 +19,34 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { const std::shared_ptr &context, const AudioBufferSourceOptions &options); + /// @note Audio Thread only void setLoop(bool loop); + + /// @note Audio Thread only void setLoopSkip(bool loopSkip); + + /// @note Audio Thread only void setLoopStart(double loopStart); + + /// @note Audio Thread only void setLoopEnd(double loopEnd); - /// @note Buffer can be set (not to nullptr) only once. - /// This is consistent with Web Audio API. + /// @note Audio Thread only void setBuffer( const std::shared_ptr &buffer, const std::shared_ptr &playbackRateBuffer, const std::shared_ptr &audioBuffer); using AudioScheduledSourceNode::start; + /// @note Audio Thread only void start(double when, double offset, double duration = -1); + + /// @note Audio Thread only void disable() override; + /// @note Audio Thread only void setOnLoopEndedCallbackId(uint64_t callbackId); - /// @note Thread safe, because does not access state of the node. void unregisterOnLoopEndedCallback(uint64_t callbackId); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 4c0ba81ff..b573b43d7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -27,17 +27,26 @@ class AudioScheduledSourceNode : public AudioNode { virtual void start(double when); virtual void stop(double when); + /// @note Audio Thread only bool isUnscheduled(); + + /// @note Audio Thread only bool isScheduled(); + + /// @note Audio Thread only bool isPlaying(); + + /// @note Audio Thread only bool isFinished(); + + /// @note Audio Thread only bool isStopScheduled(); + /// @note Audio Thread only void setOnEndedCallbackId(uint64_t callbackId); void disable() override; - /// @note Thread safe, because does not access state of the node. void unregisterOnEndedCallback(uint64_t callbackId); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp index e55fdfaf1..40cb8e7df 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp @@ -10,9 +10,13 @@ namespace audioapi { ConstantSourceNode::ConstantSourceNode( const std::shared_ptr &context, const ConstantSourceOptions &options) - : AudioScheduledSourceNode(context) { - offsetParam_ = std::make_shared( - options.offset, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + : AudioScheduledSourceNode(context), + offsetParam_( + std::make_shared( + options.offset, + MOST_NEGATIVE_SINGLE_FLOAT, + MOST_POSITIVE_SINGLE_FLOAT, + context)) { isInitialized_.store(true, std::memory_order_release); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h index edb536b27..5c030c59f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h @@ -24,6 +24,6 @@ class ConstantSourceNode : public AudioScheduledSourceNode { int framesToProcess) override; private: - std::shared_ptr offsetParam_; + const std::shared_ptr offsetParam_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp index 2f1037ae5..56ac97a12 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.cpp @@ -39,10 +39,6 @@ std::shared_ptr OscillatorNode::getDetuneParam() const { return detuneParam_; } -OscillatorType OscillatorNode::getType() { - return type_; -} - void OscillatorNode::setType(OscillatorType type) { if (std::shared_ptr context = context_.lock()) { type_ = type; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h index 184643a06..dfca6a919 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h @@ -20,8 +20,11 @@ class OscillatorNode : public AudioScheduledSourceNode { [[nodiscard]] std::shared_ptr getFrequencyParam() const; [[nodiscard]] std::shared_ptr getDetuneParam() const; - [[nodiscard]] OscillatorType getType(); + + /// @note Audio Thread only void setType(OscillatorType); + + /// @note Audio Thread only void setPeriodicWave(const std::shared_ptr &periodicWave); protected: From 3a75582547dc5d8abd4db32dde21e8db566dc487 Mon Sep 17 00:00:00 2001 From: michal Date: Mon, 2 Mar 2026 20:23:36 +0100 Subject: [PATCH 62/73] refactor: removed unnecessary headers --- .../audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp | 1 - .../audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp | 1 - .../react-native-audio-api/common/cpp/audioapi/core/AudioNode.h | 1 - .../common/cpp/audioapi/core/BaseAudioContext.cpp | 1 - .../common/cpp/audioapi/core/BaseAudioContext.h | 2 -- .../cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp | 1 - .../common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp | 1 - 7 files changed, 8 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index da0d071a9..44b27933d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -7,7 +7,6 @@ #include #include -#include namespace audioapi { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp index aea7cb004..8d0ae5f87 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp @@ -6,7 +6,6 @@ #include #include -#include #include namespace audioapi { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h index 841749f8d..4890f3848 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index 5776d8e72..76a39b0c4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index e0a54de8b..012a1063e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp index 3f2911963..3cf92a018 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp index 1ea06bd75..aa691eac5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp @@ -3,7 +3,6 @@ #include #include -#include #include namespace audioapi { From a798e896a34f59fc0128011dad06cc58ec03b445 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 07:03:04 +0100 Subject: [PATCH 63/73] ci: lint --- .../common/cpp/audioapi/core/AudioParam.h | 20 +++++++++---------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 4 ++-- .../audioapi/core/effects/WaveShaperNode.h | 4 ++-- .../core/sources/AudioBufferBaseSourceNode.h | 2 +- .../core/sources/AudioBufferQueueSourceNode.h | 14 ++++++------- .../core/sources/AudioBufferSourceNode.h | 14 ++++++------- .../core/sources/AudioScheduledSourceNode.h | 12 +++++------ .../audioapi/core/sources/OscillatorNode.h | 4 ++-- 8 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h index 2b4623c93..06abccab8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h @@ -43,29 +43,29 @@ class AudioParam { value_.store(std::clamp(value, minValue_, maxValue_), std::memory_order_release); } - /// @note Audio Thread only + /// @note Audio Thread only void setValueAtTime(float value, double startTime); - /// @note Audio Thread only + /// @note Audio Thread only void linearRampToValueAtTime(float value, double endTime); - /// @note Audio Thread only + /// @note Audio Thread only void exponentialRampToValueAtTime(float value, double endTime); - /// @note Audio Thread only + /// @note Audio Thread only void setTargetAtTime(float target, double startTime, double timeConstant); - /// @note Audio Thread only + /// @note Audio Thread only void setValueCurveAtTime( const std::shared_ptr &values, size_t length, double startTime, double duration); - /// @note Audio Thread only + /// @note Audio Thread only void cancelScheduledValues(double cancelTime); - /// @note Audio Thread only + /// @note Audio Thread only void cancelAndHoldAtTime(double cancelTime); template < @@ -85,13 +85,13 @@ class AudioParam { /// @note Audio Thread only void addInputNode(AudioNode *node); - /// @note Audio Thread only + /// @note Audio Thread only void removeInputNode(AudioNode *node); - /// @note Audio Thread only + /// @note Audio Thread only std::shared_ptr processARateParam(int framesToProcess, double time); - /// @note Audio Thread only + /// @note Audio Thread only float processKRateParam(int framesToProcess, double time); private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 2c2efe939..6232110eb 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -39,10 +39,10 @@ class AnalyserNode : public AudioNode { /// @note JS Thread only void getByteFrequencyData(uint8_t *data, int length); - /// @note JS Thread only + /// @note JS Thread only void getFloatTimeDomainData(float *data, int length); - /// @note JS Thread only + /// @note JS Thread only void getByteTimeDomainData(uint8_t *data, int length); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 3beba7a48..66d949905 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -24,10 +24,10 @@ class WaveShaperNode : public AudioNode { const std::shared_ptr &context, const WaveShaperOptions &options); - /// @note Audio Thread only + /// @note Audio Thread only void setOversample(OverSampleType); - /// @note Audio Thread only + /// @note Audio Thread only void setCurve(const std::shared_ptr &curve); protected: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h index 498a50589..f8330650c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h @@ -19,7 +19,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); - /// @note JS Thread only + /// @note JS Thread only void initStretch(const std::shared_ptr> &stretch); [[nodiscard]] std::shared_ptr getDetuneParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h index 3ee406d2c..1d56886c9 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h @@ -21,27 +21,27 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { const std::shared_ptr &context, const BaseAudioBufferSourceOptions &options); - /// @note Audio Thread only + /// @note Audio Thread only void stop(double when) override; void start(double when) override; - /// @note Audio Thread only + /// @note Audio Thread only void start(double when, double offset); - /// @note Audio Thread only + /// @note Audio Thread only void pause(); - /// @note Audio Thread only + /// @note Audio Thread only void enqueueBuffer( const std::shared_ptr &buffer, size_t bufferId, const std::shared_ptr &tailBuffer); - /// @note Audio Thread only + /// @note Audio Thread only void dequeueBuffer(size_t bufferId); - /// @note Audio Thread only + /// @note Audio Thread only void clearBuffers(); - /// @note Audio Thread only + /// @note Audio Thread only void disable() override; /// @note Audio Thread only diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h index ca923847c..a63d640f0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h @@ -19,16 +19,16 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { const std::shared_ptr &context, const AudioBufferSourceOptions &options); - /// @note Audio Thread only + /// @note Audio Thread only void setLoop(bool loop); - /// @note Audio Thread only + /// @note Audio Thread only void setLoopSkip(bool loopSkip); - /// @note Audio Thread only + /// @note Audio Thread only void setLoopStart(double loopStart); - /// @note Audio Thread only + /// @note Audio Thread only void setLoopEnd(double loopEnd); /// @note Audio Thread only @@ -38,13 +38,13 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { const std::shared_ptr &audioBuffer); using AudioScheduledSourceNode::start; - /// @note Audio Thread only + /// @note Audio Thread only void start(double when, double offset, double duration = -1); - /// @note Audio Thread only + /// @note Audio Thread only void disable() override; - /// @note Audio Thread only + /// @note Audio Thread only void setOnLoopEndedCallbackId(uint64_t callbackId); void unregisterOnLoopEndedCallback(uint64_t callbackId); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index b573b43d7..67bdbb094 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -27,22 +27,22 @@ class AudioScheduledSourceNode : public AudioNode { virtual void start(double when); virtual void stop(double when); - /// @note Audio Thread only + /// @note Audio Thread only bool isUnscheduled(); - /// @note Audio Thread only + /// @note Audio Thread only bool isScheduled(); - /// @note Audio Thread only + /// @note Audio Thread only bool isPlaying(); - /// @note Audio Thread only + /// @note Audio Thread only bool isFinished(); - /// @note Audio Thread only + /// @note Audio Thread only bool isStopScheduled(); - /// @note Audio Thread only + /// @note Audio Thread only void setOnEndedCallbackId(uint64_t callbackId); void disable() override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h index dfca6a919..3b6eb6bb4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/OscillatorNode.h @@ -21,10 +21,10 @@ class OscillatorNode : public AudioScheduledSourceNode { [[nodiscard]] std::shared_ptr getFrequencyParam() const; [[nodiscard]] std::shared_ptr getDetuneParam() const; - /// @note Audio Thread only + /// @note Audio Thread only void setType(OscillatorType); - /// @note Audio Thread only + /// @note Audio Thread only void setPeriodicWave(const std::shared_ptr &periodicWave); protected: From f7624e11c0263c348335c9f57e059e5c9e459706 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 07:36:11 +0100 Subject: [PATCH 64/73] fix: nitpicks --- ghdocs/audio-node.md | 32 +++++++++---------- .../docs/core/base-audio-context.mdx | 4 +++ .../audiodocs/docs/sources/streamer-node.mdx | 16 ++-------- .../audioapi/core/analysis/AnalyserNode.cpp | 8 +---- 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/ghdocs/audio-node.md b/ghdocs/audio-node.md index 69bf1a6ca..d765348cd 100644 --- a/ghdocs/audio-node.md +++ b/ghdocs/audio-node.md @@ -1,10 +1,10 @@ # How to create new AudioNode -In this docs we present recommended design pattern for creating new AudioNode. +In this docs we present recommended patterns for creating new AudioNodes. ## Layers -In common each AudioNode has three layers: +Ususally, each AudioNode has three layers: - Core (C++) @@ -59,7 +59,7 @@ class GainNode extends AudioNode { readonly gain: AudioParam; constructor(context: BaseAudioContext, options?: TGainOptions) { - const gainNode: IGainNode = context.context.createGain(options || {}); + const gainNode: IGainNode = context.context.createGain(options || {}); // context.context is C++ HO super(context, gainNode); this.gain = new AudioParam(gainNode.gain, context); } @@ -101,13 +101,13 @@ There are a few rules that should be followed when implementing C++ AudioNode co - **Thread safety**: Each AudioNode should be created in thread safe manner. -- **Heap allocations**: Heap allocations are not allowed on Audio Thread, so all necessary data should be allocated in constructor, or pre-allocated on other thread and passed to AudioNode. +- **Heap allocations**: Heap allocations are not allowed on the Audio Thread, so all necessary data should be allocated in constructor, or pre-allocated on other thread and passed to AudioNode. -- **Destructions** No destructions are allowed to happen on Audio Thread. In common AudioNode destruction are handled by already active AudioDestructor. If you need to perform some cleanup, you have to delegate it to AudioDestructor. +- **Destructions** No destructions are allowed to happen on the Audio Thread. AudioNode destruction are handled by already active AudioDestructor. If you need to perform some cleanup, you have to delegate it to AudioDestructor. -- **No locks on Audio Thread**: Locks are not allowed on Audio Thread. Audio procssing have to be highly performant and efficient. +- **No locks on Audio Thread**: Locks are not allowed on the Audio Thread. Audio procssing have to be highly performant and efficient. -- **No syscalls** Syscalls are not allowed on Audio Thread, so if you need to perform some work that requires syscalls, you have to delegate it to other thread. +- **No syscalls** Syscalls are not allowed on the Audio Thread, so if you need to perform some work that requires syscalls, you have to delegate it to other thread. ## HostObject implementation @@ -148,7 +148,7 @@ JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { ``` -1. **function** +3. **function** C++ counterpart is `JSI_HOST_FUNCTION`. It is a common function that can receive arguments and return some value. @@ -158,7 +158,7 @@ analyserNode.getByteFrequencyData(fftOutput); ``` ```cpp -SI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getByteFrequencyData) { +JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getByteFrequencyData) { auto arrayBuffer = args[0].getObject(runtime).getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); auto data = arrayBuffer.data(runtime); @@ -184,7 +184,7 @@ AnalyserNodeHostObject::AnalyserNodeHostObject(const std::shared_ptr | [`StreamerOptions`](/docs/sources/streamer-node#streameroptions) | Streamer options to initialize. | + #### Returns `StreamerNode`. ### `createWaveShaper` diff --git a/packages/audiodocs/docs/sources/streamer-node.mdx b/packages/audiodocs/docs/sources/streamer-node.mdx index d3cb176f9..51303e78e 100644 --- a/packages/audiodocs/docs/sources/streamer-node.mdx +++ b/packages/audiodocs/docs/sources/streamer-node.mdx @@ -21,18 +21,18 @@ Similar to all of `AudioScheduledSourceNodes`, it can be started only once. If y ## Constructor ```tsx -constructor(context: BaseAudioContext, options?: StreamerOptions) +constructor(context: BaseAudioContext, options: StreamerOptions) ``` ### `StreamerOptions` | Parameter | Type | Default | | | :---: | :---: | :----: | :---- | -| `streamPath` | `string` | - | Initial value for [`streamPath`](/docs/sources/streamer-node#properties) | +| `streamPath` | `string` | - | Value for [`streamPath`](/docs/sources/streamer-node#properties) | Or by using `BaseAudioContext` factory method: -[`BaseAudioContext.createStreamer()`](/docs/core/base-audio-context#createstreamer-). +[`BaseAudioContext.createStreamer()`](/docs/core/base-audio-context#createstreamer). ## Example @@ -65,13 +65,3 @@ It inherits all properties from [`AudioScheduledSourceNode`](/docs/sources/audio ## Methods It inherits all methods from [`AudioScheduledSourceNode`](/docs/sources/audio-scheduled-source-node#methods). - -### `initialize` - -Initializes the streamer with a link to an external source. - -| Parameter | Type | Description | -| :---: | :---: | :---- | -| `streamPath` | `string` | Link pointing to an external source | - -#### Returns `boolean` indicating if setup of streaming has worked. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 7c9747e34..d1856f7a0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -104,13 +104,7 @@ void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { for (int i = 0; i < size; i++) { float scaledValue = 128 * (values[i] + 1); - - if (scaledValue < 0) { - scaledValue = 0; - } - if (scaledValue > UINT8_MAX) { - scaledValue = UINT8_MAX; - } + scaledValue = std::clamp(scaledValue, 0.0f, static_cast(UINT8_MAX)); data[i] = static_cast(scaledValue); } From cb0252eb3e0504f8b4e9f61d9b30e481c706f07e Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 11:08:07 +0100 Subject: [PATCH 65/73] fix: nitpicks --- .gitignore | 3 ++ .../analysis/AnalyserNodeHostObject.cpp | 43 +++++------------- .../analysis/AnalyserNodeHostObject.h | 6 --- .../effects/BiquadFilterNodeHostObject.cpp | 3 +- .../common/cpp/audioapi/core/AudioContext.h | 2 +- .../cpp/audioapi/core/BaseAudioContext.cpp | 4 +- .../cpp/audioapi/core/BaseAudioContext.h | 6 +-- .../audioapi/core/analysis/AnalyserNode.cpp | 40 ++++++----------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 45 ++++++++++++++++--- .../core/effects/BiquadFilterNode.cpp | 9 ++-- .../audioapi/core/effects/BiquadFilterNode.h | 5 ++- .../core/sources/AudioScheduledSourceNode.h | 3 +- .../events/AudioEventHandlerRegistry.cpp | 18 ++++---- .../events/AudioEventHandlerRegistry.h | 2 +- .../core/effects/biquad/BiquadFilterTest.cpp | 3 +- 15 files changed, 94 insertions(+), 98 deletions(-) diff --git a/.gitignore b/.gitignore index 7afbe629d..c8951115a 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,6 @@ packages/react-native-audio-api/android/src/main/jniLibs/ packages/react-native-audio-api/common/cpp/audioapi/external/**/*.a packages/react-native-audio-api/common/cpp/audioapi/external/*.xcframework packages/react-native-audio-api/common/cpp/audioapi/external/ffmpeg_ios/ + +# Clangd cache +.cache diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index c9779c696..f9f3c3e24 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -6,18 +6,13 @@ #include #include -#include namespace audioapi { AnalyserNodeHostObject::AnalyserNodeHostObject( const std::shared_ptr &context, const AnalyserOptions &options) - : AudioNodeHostObject(context->createAnalyser(options), options), - fftSize_(options.fftSize), - minDecibels_(options.minDecibels), - maxDecibels_(options.maxDecibels), - smoothingTimeConstant_(options.smoothingTimeConstant) { + : AudioNodeHostObject(context->createAnalyser(options), options) { addGetters( JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, fftSize), JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, minDecibels), @@ -38,62 +33,48 @@ AnalyserNodeHostObject::AnalyserNodeHostObject( } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, fftSize) { - return {fftSize_}; + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getFFTSize()}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, minDecibels) { - return {minDecibels_}; + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getMinDecibels()}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, maxDecibels) { - return {maxDecibels_}; + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getMaxDecibels()}; } JSI_PROPERTY_GETTER_IMPL(AnalyserNodeHostObject, smoothingTimeConstant) { - return {smoothingTimeConstant_}; + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getSmoothingTimeConstant()}; } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, fftSize) { auto analyserNode = std::static_pointer_cast(node_); auto fftSize = static_cast(value.getNumber()); - - if (fftSize == fftSize_) { - return; - } - - fftSize_ = fftSize; analyserNode->setFFTSize(fftSize); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, minDecibels) { auto analyserNode = std::static_pointer_cast(node_); auto minDecibels = static_cast(value.getNumber()); - auto event = [analyserNode, minDecibels](BaseAudioContext &) { - analyserNode->setMinDecibels(minDecibels); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - minDecibels_ = minDecibels; + analyserNode->setMinDecibels(minDecibels); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, maxDecibels) { auto analyserNode = std::static_pointer_cast(node_); auto maxDecibels = static_cast(value.getNumber()); - auto event = [analyserNode, maxDecibels](BaseAudioContext &) { - analyserNode->setMaxDecibels(maxDecibels); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - maxDecibels_ = maxDecibels; + analyserNode->setMaxDecibels(maxDecibels); } JSI_PROPERTY_SETTER_IMPL(AnalyserNodeHostObject, smoothingTimeConstant) { auto analyserNode = std::static_pointer_cast(node_); auto smoothingTimeConstant = static_cast(value.getNumber()); - auto event = [analyserNode, smoothingTimeConstant](BaseAudioContext &) { - analyserNode->setSmoothingTimeConstant(smoothingTimeConstant); - }; - analyserNode->scheduleAudioEvent(std::move(event)); - smoothingTimeConstant_ = smoothingTimeConstant; + analyserNode->setSmoothingTimeConstant(smoothingTimeConstant); } JSI_HOST_FUNCTION_IMPL(AnalyserNodeHostObject, getFloatFrequencyData) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h index c29a8c924..fe9405d1a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h @@ -30,12 +30,6 @@ class AnalyserNodeHostObject : public AudioNodeHostObject { JSI_HOST_FUNCTION_DECL(getByteFrequencyData); JSI_HOST_FUNCTION_DECL(getFloatTimeDomainData); JSI_HOST_FUNCTION_DECL(getByteTimeDomainData); - - private: - int fftSize_; - float minDecibels_; - float maxDecibels_; - float smoothingTimeConstant_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp index 1e0d94f0d..21f87e99b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp @@ -80,7 +80,8 @@ JSI_HOST_FUNCTION_IMPL(BiquadFilterNodeHostObject, getFrequencyResponse) { auto phaseResponseOut = reinterpret_cast(arrayBufferPhase.data(runtime)); auto biquadFilterNode = std::static_pointer_cast(node_); - biquadFilterNode->getFrequencyResponse(frequencyArray, magResponseOut, phaseResponseOut, length); + biquadFilterNode->getFrequencyResponse( + frequencyArray, magResponseOut, phaseResponseOut, length, type_); return jsi::Value::undefined(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h index e1926a4d3..57295baec 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h @@ -33,7 +33,7 @@ class AudioContext : public BaseAudioContext { #else std::shared_ptr audioPlayer_; #endif - std::atomic isInitialized_ = false; + std::atomic isInitialized_{false}; bool isDriverRunning() const override; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index 76a39b0c4..5d0584b31 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -42,9 +42,7 @@ BaseAudioContext::BaseAudioContext( graphManager_(std::make_shared()), audioEventHandlerRegistry_(audioEventHandlerRegistry), runtimeRegistry_(runtimeRegistry), - audioEventScheduler_( - std::make_unique>(AUDIO_SCHEDULER_CAPACITY)) { -} + audioEventScheduler_(AUDIO_SCHEDULER_CAPACITY) {} void BaseAudioContext::initialize() { destination_ = std::make_shared(shared_from_this()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index 012a1063e..fa3c9efad 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -112,7 +112,7 @@ class BaseAudioContext : public std::enable_shared_from_this { virtual void initialize(); void inline processAudioEvents() { - audioEventScheduler_->processAllEvents(*this); + audioEventScheduler_.processAllEvents(*this); } template @@ -123,7 +123,7 @@ class BaseAudioContext : public std::enable_shared_from_this { return true; } - return audioEventScheduler_->scheduleEvent(std::forward(event)); + return audioEventScheduler_.scheduleEvent(std::forward(event)); } protected: @@ -142,7 +142,7 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr cachedTriangleWave_ = nullptr; static constexpr size_t AUDIO_SCHEDULER_CAPACITY = 1024; - std::unique_ptr> audioEventScheduler_; + CrossThreadEventScheduler audioEventScheduler_; [[nodiscard]] virtual bool isDriverRunning() const = 0; }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index ed4fe245f..63609cf98 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -16,17 +16,21 @@ AnalyserNode::AnalyserNode( const std::shared_ptr &context, const AnalyserOptions &options) : AudioNode(context, options), - minDecibels_(options.minDecibels), - maxDecibels_(options.maxDecibels), - smoothingTimeConstant_(options.smoothingTimeConstant), inputArray_(std::make_unique(MAX_FFT_SIZE * 2)), downMixBuffer_( - std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())) { + std::make_unique(RENDER_QUANTUM_SIZE, 1, context->getSampleRate())), + minDecibels_(options.minDecibels), + maxDecibels_(options.maxDecibels), + smoothingTimeConstant_(options.smoothingTimeConstant) { setFFTSize(options.fftSize); isInitialized_.store(true, std::memory_order_release); } void AnalyserNode::setFFTSize(int fftSize) { + if (fftSize == fftSize_.load(std::memory_order_acquire)) { + return; + } + fft_ = std::make_unique(fftSize); complexData_ = std::vector>(fftSize); magnitudeArray_ = std::make_unique(fftSize / 2); @@ -36,18 +40,6 @@ void AnalyserNode::setFFTSize(int fftSize) { fftSize_.store(fftSize, std::memory_order_release); } -void AnalyserNode::setMinDecibels(float minDecibels) { - minDecibels_.store(minDecibels, std::memory_order_release); -} - -void AnalyserNode::setMaxDecibels(float maxDecibels) { - maxDecibels_.store(maxDecibels, std::memory_order_release); -} - -void AnalyserNode::setSmoothingTimeConstant(float smoothingTimeConstant) { - smoothingTimeConstant_.store(smoothingTimeConstant, std::memory_order_release); -} - void AnalyserNode::getFloatFrequencyData(float *data, int length) { doFFTAnalysis(); @@ -65,15 +57,13 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { auto magnitudeBufferData = magnitudeArray_->span(); length = std::min(static_cast(magnitudeArray_->getSize()), length); - auto minDecibels = minDecibels_.load(std::memory_order_acquire); - auto maxDecibels = maxDecibels_.load(std::memory_order_acquire); - - const auto rangeScaleFactor = maxDecibels == minDecibels ? 1 : 1 / (maxDecibels - minDecibels); + const auto rangeScaleFactor = + maxDecibels_ == minDecibels_ ? 1 : 1 / (maxDecibels_ - minDecibels_); for (int i = 0; i < length; i++) { auto dbMag = - magnitudeBufferData[i] == 0 ? minDecibels : dsp::linearToDecibels(magnitudeBufferData[i]); - auto scaledValue = UINT8_MAX * (dbMag - minDecibels) * rangeScaleFactor; + magnitudeBufferData[i] == 0 ? minDecibels_ : dsp::linearToDecibels(magnitudeBufferData[i]); + auto scaledValue = UINT8_MAX * (dbMag - minDecibels_) * rangeScaleFactor; if (scaledValue < 0) { scaledValue = 0; @@ -154,13 +144,11 @@ void AnalyserNode::doFFTAnalysis() { const float magnitudeScale = 1.0f / static_cast(fftSize); auto magnitudeBufferData = magnitudeArray_->span(); - auto smoothingTimeConstant = smoothingTimeConstant_.load(std::memory_order_acquire); - for (int i = 0; i < magnitudeArray_->getSize(); i++) { auto scalarMagnitude = std::abs(complexData_[i]) * magnitudeScale; magnitudeBufferData[i] = static_cast( - smoothingTimeConstant * magnitudeBufferData[i] + - (1 - smoothingTimeConstant) * scalarMagnitude); + smoothingTimeConstant_ * magnitudeBufferData[i] + + (1 - smoothingTimeConstant_) * scalarMagnitude); } } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 6232110eb..3e9d71da5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -26,9 +26,39 @@ class AnalyserNode : public AudioNode { const std::shared_ptr &context, const AnalyserOptions &options); - void setMinDecibels(float minDecibels); - void setMaxDecibels(float maxDecibels); - void setSmoothingTimeConstant(float smoothingTimeConstant); + /// @note JS Thread only + float getMinDecibels() const { + return minDecibels_; + } + + /// @note JS Thread only + float getMaxDecibels() const { + return maxDecibels_; + } + + /// @note JS Thread only + float getSmoothingTimeConstant() const { + return smoothingTimeConstant_; + } + + int getFFTSize() const { + return fftSize_.load(std::memory_order_acquire); + } + + /// @note JS Thread only + void setMinDecibels(float minDecibels) { + minDecibels_ = minDecibels; + } + + /// @note JS Thread only + void setMaxDecibels(float maxDecibels) { + maxDecibels_ = maxDecibels; + } + + /// @note JS Thread only + void setSmoothingTimeConstant(float smoothingTimeConstant) { + smoothingTimeConstant_ = smoothingTimeConstant; + } /// @note JS Thread only void setFFTSize(int fftSize); @@ -51,15 +81,18 @@ class AnalyserNode : public AudioNode { int framesToProcess) override; private: + // Audio Thread parameters std::atomic fftSize_; - std::atomic minDecibels_; - std::atomic maxDecibels_; - std::atomic smoothingTimeConstant_; // Audio Thread data structures std::unique_ptr inputArray_; std::unique_ptr downMixBuffer_; + // JS Thread parameters + float minDecibels_; + float maxDecibels_; + float smoothingTimeConstant_; + // JS Thread data structures std::unique_ptr fft_; std::unique_ptr tempArray_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index 0d99a09ce..4f44ebdaa 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -72,7 +72,7 @@ BiquadFilterNode::BiquadFilterNode( } void BiquadFilterNode::setType(BiquadFilterType type) { - type_.store(type, std::memory_order_release); + type_ = type; } std::shared_ptr BiquadFilterNode::getFrequencyParam() const { @@ -114,12 +114,12 @@ void BiquadFilterNode::getFrequencyResponse( const float *frequencyArray, float *magResponseOutput, float *phaseResponseOutput, - const size_t length) { + const size_t length, + BiquadFilterType type) { auto frequency = frequencyParam_->getValue(); auto Q = QParam_->getValue(); auto gain = gainParam_->getValue(); auto detune = detuneParam_->getValue(); - auto type = type_.load(std::memory_order_acquire); auto coeffs = applyFilter(frequency, Q, gain, detune, type); @@ -389,9 +389,8 @@ std::shared_ptr BiquadFilterNode::processNode( float detune = detuneParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); auto Q = QParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); auto gain = gainParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime); - auto type = type_.load(std::memory_order_relaxed); - auto coeffs = applyFilter(frequency, Q, gain, detune, type); + auto coeffs = applyFilter(frequency, Q, gain, detune, type_); float x1, x2, y1, y2; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index b1db098fe..3fbd0a607 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -64,7 +64,8 @@ class BiquadFilterNode : public AudioNode { const float *frequencyArray, float *magResponseOutput, float *phaseResponseOutput, - size_t length); + size_t length, + BiquadFilterType type); protected: std::shared_ptr processNode( @@ -76,7 +77,7 @@ class BiquadFilterNode : public AudioNode { const std::shared_ptr detuneParam_; const std::shared_ptr QParam_; const std::shared_ptr gainParam_; - std::atomic type_; + BiquadFilterType type_; // delayed samples, one per channel AudioArray x1_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h index 67bdbb094..d4129dcd7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -56,7 +55,7 @@ class AudioScheduledSourceNode : public AudioNode { PlaybackState playbackState_; uint64_t onEndedCallbackId_ = 0; - std::shared_ptr audioEventHandlerRegistry_; + const std::shared_ptr audioEventHandlerRegistry_; void updatePlaybackInfo( const std::shared_ptr &processingBuffer, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp b/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp index 796d064b3..e1a055f00 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp @@ -21,8 +21,8 @@ uint64_t AudioEventHandlerRegistry::registerHandler( const std::shared_ptr &handler) { auto listenerId = listenerIdCounter_.fetch_add(1, std::memory_order_relaxed); - if (callInvoker_ == nullptr || runtime_ == nullptr) { - // If callInvoker or runtime is not valid, we cannot register the handler + if (runtime_ == nullptr) { + // If runtime is not valid, we cannot register the handler return 0; } @@ -39,8 +39,8 @@ uint64_t AudioEventHandlerRegistry::registerHandler( } void AudioEventHandlerRegistry::unregisterHandler(AudioEvent eventName, uint64_t listenerId) { - if (callInvoker_ == nullptr || runtime_ == nullptr) { - // If callInvoker or runtime is not valid, we cannot unregister the handler + if (runtime_ == nullptr) { + // If runtime is not valid, we cannot unregister the handler return; } @@ -68,9 +68,8 @@ void AudioEventHandlerRegistry::unregisterHandler(AudioEvent eventName, uint64_t void AudioEventHandlerRegistry::invokeHandlerWithEventBody( AudioEvent eventName, const std::unordered_map &body) { - // callInvoker_ and runtime_ must be valid to invoke handlers - // this might happen when react-native is reloaded or the app is closed - if (callInvoker_ == nullptr || runtime_ == nullptr) { + if (runtime_ == nullptr) { + // If runtime is not valid, we cannot unregister the handler return; } @@ -127,9 +126,8 @@ void AudioEventHandlerRegistry::invokeHandlerWithEventBody( AudioEvent eventName, uint64_t listenerId, const std::unordered_map &body) { - // callInvoker_ and runtime_ must be valid to invoke handlers - // this might happen when react-native is reloaded or the app is closed - if (callInvoker_ == nullptr || runtime_ == nullptr) { + if (runtime_ == nullptr) { + // If runtime is not valid, we cannot unregister the handler return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.h b/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.h index cca44180f..0ddac36f8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/events/AudioEventHandlerRegistry.h @@ -58,7 +58,7 @@ class AudioEventHandlerRegistry : public IAudioEventHandlerRegistry, private: std::atomic listenerIdCounter_{1}; // Atomic counter for listener IDs - std::shared_ptr callInvoker_; + const std::shared_ptr callInvoker_; jsi::Runtime *runtime_; std::unordered_map>> eventHandlers_; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp index a618d5c2f..7dd982686 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp @@ -233,7 +233,8 @@ TEST_F(BiquadFilterTest, GetFrequencyResponse) { TestFrequencies.data(), magResponseNode.data(), phaseResponseNode.data(), - TestFrequencies.size()); + TestFrequencies.size(), + BiquadFilterType::LOWPASS); getFrequencyResponse( coeffs, TestFrequencies, magResponseExpected, phaseResponseExpected, nyquistFrequency); From 03f7ee729fbd642bb03e433bd4ab2ca6a2d9d01b Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 13:20:38 +0100 Subject: [PATCH 66/73] refactor: refined TripleBuffer and AnalyserNode for better performance and correctness --- .../audioapi/core/analysis/AnalyserNode.cpp | 27 ++++++------- .../cpp/audioapi/core/analysis/AnalyserNode.h | 9 +++-- .../cpp/audioapi/core/utils/Constants.h | 10 +++++ .../common/cpp/audioapi/utils/SpscChannel.hpp | 9 +---- .../cpp/audioapi/utils/TripleBuffer.hpp | 40 ++++++++++++++----- 5 files changed, 60 insertions(+), 35 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index 63609cf98..bda6c8c3d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -65,31 +65,22 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) { magnitudeBufferData[i] == 0 ? minDecibels_ : dsp::linearToDecibels(magnitudeBufferData[i]); auto scaledValue = UINT8_MAX * (dbMag - minDecibels_) * rangeScaleFactor; - if (scaledValue < 0) { - scaledValue = 0; - } - if (scaledValue > UINT8_MAX) { - scaledValue = UINT8_MAX; - } - - data[i] = static_cast(scaledValue); + data[i] = static_cast(std::clamp(scaledValue, 0.0f, static_cast(UINT8_MAX))); } } void AnalyserNode::getFloatTimeDomainData(float *data, int length) { auto *frame = analysisBuffer_.getForReader(); - auto size = std::min(fftSize_.load(std::memory_order_relaxed), length); + auto size = std::min(frame->fftSize, length); frame->timeDomain.copyTo(data, 0, 0, size); } void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) { auto *frame = analysisBuffer_.getForReader(); - auto size = std::min(fftSize_.load(std::memory_order_relaxed), length); + auto size = std::min(frame->fftSize, length); - tempArray_->copy(frame->timeDomain, 0, 0, size); - - auto values = tempArray_->span(); + auto values = frame->timeDomain.span(); for (int i = 0; i < size; i++) { float scaledValue = 128 * (values[i] + 1); @@ -113,6 +104,7 @@ std::shared_ptr AnalyserNode::processNode( // Snapshot the latest fftSize_ samples into the triple buffer for the JS thread. auto *frame = analysisBuffer_.getForWriter(); auto fftSize = fftSize_.load(std::memory_order_acquire); + frame->fftSize = fftSize; frame->sequenceNumber = ++publishSequence_; inputArray_->pop_back(frame->timeDomain, fftSize, 0, true); analysisBuffer_.publish(); @@ -127,9 +119,14 @@ void AnalyserNode::doFFTAnalysis() { return; } - lastAnalyzedSequence_ = frame->sequenceNumber; + auto fftSize = frame->fftSize; - auto fftSize = fftSize_.load(std::memory_order_relaxed); + // relaxed because fftSize_ is only updated on the JS thread. + if (fftSize != fftSize_.load(std::memory_order_relaxed)) { + return; + } + + lastAnalyzedSequence_ = frame->sequenceNumber; // Copy the snapshot from the triple buffer and apply the window. tempArray_->copy(frame->timeDomain, 0, 0, fftSize); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 3e9d71da5..0c871e006 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -81,7 +81,6 @@ class AnalyserNode : public AudioNode { int framesToProcess) override; private: - // Audio Thread parameters std::atomic fftSize_; // Audio Thread data structures @@ -103,11 +102,15 @@ class AnalyserNode : public AudioNode { struct AnalysisFrame { AudioArray timeDomain; size_t sequenceNumber = 0; + int fftSize = 0; - AnalysisFrame() : timeDomain(MAX_FFT_SIZE) {} + explicit AnalysisFrame(size_t size) : timeDomain(size) {} + + AnalysisFrame(const AnalysisFrame &) = delete; + AnalysisFrame &operator=(const AnalysisFrame &) = delete; }; - TripleBuffer analysisBuffer_; + TripleBuffer analysisBuffer_{MAX_FFT_SIZE}; size_t publishSequence_ = 0; // audio thread only size_t lastAnalyzedSequence_ = 0; // JS thread only diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h index e3f1e3186..a75700924 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h @@ -3,6 +3,7 @@ #include #include #include +#include // https://webaudio.github.io/web-audio-api/ @@ -32,4 +33,13 @@ static constexpr float PI = std::numbers::pi_v; static constexpr size_t PROMISE_VENDOR_THREAD_POOL_WORKER_COUNT = 4; static constexpr size_t PROMISE_VENDOR_THREAD_POOL_LOAD_BALANCER_QUEUE_SIZE = 32; static constexpr size_t PROMISE_VENDOR_THREAD_POOL_WORKER_QUEUE_SIZE = 32; + +// Cache line size +#ifdef __cpp_lib_hardware_interference_size +using std::hardware_constructive_interference_size; +using std::hardware_destructive_interference_size; +#else +constexpr std::size_t hardware_constructive_interference_size = 64; +constexpr std::size_t hardware_destructive_interference_size = 64; +#endif } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp index e4a0c4696..df9cf27d8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,14 +9,6 @@ #include #include -#ifdef __cpp_lib_hardware_interference_size -using std::hardware_constructive_interference_size; -using std::hardware_destructive_interference_size; -#else -constexpr std::size_t hardware_constructive_interference_size = 64; -constexpr std::size_t hardware_destructive_interference_size = 64; -#endif - namespace audioapi::channels::spsc { /// @brief Overflow strategy for sender when the channel is full diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp index 57c80e596..78decc8cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include -#include +#include +#include namespace audioapi { @@ -13,8 +15,24 @@ namespace audioapi { template class TripleBuffer { public: + template + explicit TripleBuffer(Args &&...args) { + new (&buffers_[0]) T(args...); + new (&buffers_[1]) T(args...); + new (&buffers_[2]) T(args...); + } + + ~TripleBuffer() { + std::launder(reinterpret_cast(&buffers_[0]))->~T(); + std::launder(reinterpret_cast(&buffers_[1]))->~T(); + std::launder(reinterpret_cast(&buffers_[2]))->~T(); + } + + TripleBuffer(const TripleBuffer &) = delete; + TripleBuffer &operator=(const TripleBuffer &) = delete; + T *getForWriter() { - return &buffers_[backIndex_]; + return std::launder(reinterpret_cast(&buffers_[backIndex_])); } void publish() { @@ -31,19 +49,23 @@ class TripleBuffer { frontIndex_ = prevState.index; } - return &buffers_[frontIndex_]; + return std::launder(reinterpret_cast(&buffers_[frontIndex_])); } private: struct State { - int index; - bool hasUpdate; + uint32_t index : 2; + uint32_t hasUpdate : 1; + }; + + struct alignas(hardware_constructive_interference_size) AlignedBuffer { + alignas(T) std::byte data[sizeof(T)]; }; - T buffers_[3]; - int frontIndex_ = 0; - std::atomic state_{{1, false}}; - int backIndex_ = 2; + AlignedBuffer buffers_[3]; + alignas(hardware_constructive_interference_size) uint32_t frontIndex_{0}; + alignas(hardware_constructive_interference_size) std::atomic state_{{1, false}}; + alignas(hardware_constructive_interference_size) uint32_t backIndex_{2}; }; } // namespace audioapi From 0ed1093c16f608de88246bbf18616d37efe50805 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 13:21:36 +0100 Subject: [PATCH 67/73] test: tests for TripleBuffer utility class --- .../cpp/audioapi/core/utils/Constants.h | 2 +- .../cpp/test/src/utils/TripleBufferTest.cpp | 267 ++++++++++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 packages/react-native-audio-api/common/cpp/test/src/utils/TripleBufferTest.cpp diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h index a75700924..dd12c78cd 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/Constants.h @@ -2,8 +2,8 @@ #include #include -#include #include +#include // https://webaudio.github.io/web-audio-api/ diff --git a/packages/react-native-audio-api/common/cpp/test/src/utils/TripleBufferTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/utils/TripleBufferTest.cpp new file mode 100644 index 000000000..339fad378 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/test/src/utils/TripleBufferTest.cpp @@ -0,0 +1,267 @@ +#include +#include + +#include +#include +#include + +using namespace audioapi; + +struct IntVal { + int value{0}; + explicit IntVal(int v = 0) : value(v) {} +}; + +// --------------------------------------------------------------------------- +// Functional Tests +// --------------------------------------------------------------------------- + +class TripleBufferTest : public ::testing::Test {}; + +TEST_F(TripleBufferTest, GetForWriterReturnsNonNull) { + TripleBuffer buf; + EXPECT_NE(buf.getForWriter(), nullptr); +} + +TEST_F(TripleBufferTest, GetForReaderReturnsNonNull) { + TripleBuffer buf; + EXPECT_NE(buf.getForReader(), nullptr); +} + +TEST_F(TripleBufferTest, ConstructorForwardsArguments) { + TripleBuffer buf(42); + EXPECT_EQ(buf.getForWriter()->value, 42); + EXPECT_EQ(buf.getForReader()->value, 42); +} + +TEST_F(TripleBufferTest, ReaderAndWriterPointToDifferentBuffers) { + TripleBuffer buf; + EXPECT_NE(buf.getForWriter(), buf.getForReader()); +} + +TEST_F(TripleBufferTest, PublishMakesWrittenDataVisibleToReader) { + TripleBuffer buf; + + buf.getForWriter()->value = 99; + buf.publish(); + + EXPECT_EQ(buf.getForReader()->value, 99); +} + +TEST_F(TripleBufferTest, ReaderReturnsSameBufferWithoutPublish) { + TripleBuffer buf; + + auto *first = buf.getForReader(); + auto *second = buf.getForReader(); + EXPECT_EQ(first, second); +} + +TEST_F(TripleBufferTest, MultiplePublishesWithoutReadShowLatestValue) { + TripleBuffer buf; + + for (int i = 1; i <= 5; ++i) { + buf.getForWriter()->value = i; + buf.publish(); + } + + EXPECT_EQ(buf.getForReader()->value, 5); +} + +TEST_F(TripleBufferTest, ReadClearsUpdateFlag) { + TripleBuffer buf; + + buf.getForWriter()->value = 42; + buf.publish(); + + auto *first = buf.getForReader(); + auto *second = buf.getForReader(); + + EXPECT_EQ(first, second); +} + +TEST_F(TripleBufferTest, WriterPointerIsStableBeforePublish) { + TripleBuffer buf; + + auto *w1 = buf.getForWriter(); + auto *w2 = buf.getForWriter(); + EXPECT_EQ(w1, w2); +} + +TEST_F(TripleBufferTest, WriterBufferChangesAfterPublish) { + TripleBuffer buf; + + auto *before = buf.getForWriter(); + buf.publish(); + auto *after = buf.getForWriter(); + + EXPECT_NE(before, after); +} + +TEST_F(TripleBufferTest, AlternatingWriteReadPreservesData) { + TripleBuffer buf; + + for (int i = 0; i < 10; ++i) { + buf.getForWriter()->value = i; + buf.publish(); + EXPECT_EQ(buf.getForReader()->value, i); + } +} + +TEST_F(TripleBufferTest, ReaderWithNoUpdateSeesInitialValue) { + TripleBuffer buf(7); + EXPECT_EQ(buf.getForReader()->value, 7); +} + +TEST_F(TripleBufferTest, DestructorCalledExactlyThreeTimes) { + struct Tracker { + std::atomic *count; + explicit Tracker(std::atomic *c) : count(c) {} + ~Tracker() { + ++(*count); + } + }; + + std::atomic count{0}; + { TripleBuffer buf(&count); } + EXPECT_EQ(count.load(), 3); +} + +TEST_F(TripleBufferTest, WriteThenReadSequenceIsConsistent) { + TripleBuffer buf; + + buf.getForWriter()->value = 10; + buf.publish(); + EXPECT_EQ(buf.getForReader()->value, 10); + + buf.getForWriter()->value = 20; + buf.publish(); + EXPECT_EQ(buf.getForReader()->value, 20); + + buf.getForWriter()->value = 30; + buf.publish(); + EXPECT_EQ(buf.getForReader()->value, 30); +} + +TEST_F(TripleBufferTest, ReaderPointerAfterReadIsDistinctFromWriter) { + TripleBuffer buf; + + buf.getForWriter()->value = 1; + buf.publish(); + + auto *reader = buf.getForReader(); + auto *writer = buf.getForWriter(); + EXPECT_NE(reader, writer); +} + +TEST_F(TripleBufferTest, AllThreeBufferAddressesAreDistinct) { + TripleBuffer buf; + + // Initial state: front=0, idle=1, back=2 + auto *front = buf.getForReader(); // buffers_[0] + auto *back = buf.getForWriter(); // buffers_[2] + + // publish: back (2) goes to state, old idle (1) becomes new back + buf.getForWriter()->value = 1; + buf.publish(); + + // consume the update: reader gets old back (2) as new front + buf.getForReader(); + + // after consume: front=2, state=0, back=1 — writer is now on the idle buffer + auto *idle = buf.getForWriter(); // buffers_[1] + + EXPECT_NE(front, back); + EXPECT_NE(front, idle); + EXPECT_NE(back, idle); +} + +// --------------------------------------------------------------------------- +// Stress Tests +// --------------------------------------------------------------------------- + +TEST_F(TripleBufferTest, StressReaderValueNeverGoesBackward) { + // Writer publishes strictly increasing values; reader must never see a + // decrease (the triple buffer may skip values but never go backwards). + TripleBuffer buf; + std::atomic stop{false}; + std::atomic error{false}; + + std::thread writer([&] { + for (int i = 1; !stop.load(std::memory_order_relaxed); ++i) { + buf.getForWriter()->value = i; + buf.publish(); + } + }); + + std::thread reader([&] { + int lastSeen = 0; + while (!stop.load(std::memory_order_relaxed)) { + int val = buf.getForReader()->value; + if (val < lastSeen) { + error.store(true, std::memory_order_relaxed); + } + lastSeen = val; + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + stop.store(true); + writer.join(); + reader.join(); + + EXPECT_FALSE(error.load()); +} + +TEST_F(TripleBufferTest, StressWriterNeverBlocks) { + TripleBuffer buf; + + constexpr int N = 1'000'000; + for (int i = 0; i < N; ++i) { + buf.getForWriter()->value = i; + buf.publish(); + } + + SUCCEED(); +} + +TEST_F(TripleBufferTest, StressReaderNeverBlocks) { + TripleBuffer buf; + + constexpr int N = 1'000'000; + for (int i = 0; i < N; ++i) { + (void)buf.getForReader()->value; + } + + SUCCEED(); +} + +TEST_F(TripleBufferTest, StressBothSidesMakeProgress) { + // Verify that neither thread starves the other. + TripleBuffer buf; + std::atomic stop{false}; + std::atomic writeCount{0}; + std::atomic readCount{0}; + + std::thread writer([&] { + for (int i = 1; !stop.load(std::memory_order_relaxed); ++i) { + buf.getForWriter()->value = i; + buf.publish(); + writeCount.fetch_add(1, std::memory_order_relaxed); + } + }); + + std::thread reader([&] { + while (!stop.load(std::memory_order_relaxed)) { + (void)buf.getForReader()->value; + readCount.fetch_add(1, std::memory_order_relaxed); + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + stop.store(true); + writer.join(); + reader.join(); + + EXPECT_GT(writeCount.load(), 1'000); + EXPECT_GT(readCount.load(), 1'000); +} From 48f15d7bac1d5926f148f843056ce790c096e6d4 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 3 Mar 2026 14:03:08 +0100 Subject: [PATCH 68/73] refactor: thread safe convolver setup --- .../effects/ConvolverNodeHostObject.cpp | 97 ++++++++++++--- .../effects/ConvolverNodeHostObject.h | 2 + .../audioapi/core/effects/ConvolverNode.cpp | 115 +++++++++--------- .../cpp/audioapi/core/effects/ConvolverNode.h | 15 ++- 4 files changed, 147 insertions(+), 82 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp index 4be5b697a..af1bcf22d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp @@ -2,10 +2,16 @@ #include #include #include +#include +#include #include +#include +#include +#include #include #include +#include namespace audioapi { @@ -14,6 +20,10 @@ ConvolverNodeHostObject::ConvolverNodeHostObject( const ConvolverOptions &options) : AudioNodeHostObject(context->createConvolver(options), options), normalize_(!options.disableNormalization) { + if (options.buffer != nullptr) { + setBuffer(options.buffer); + } + addGetters(JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, normalize)); addSetters(JSI_EXPORT_PROPERTY_SETTER(ConvolverNodeHostObject, normalize)); addFunctions(JSI_EXPORT_FUNCTION(ConvolverNodeHostObject, setBuffer)); @@ -24,32 +34,83 @@ JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, normalize) { } JSI_PROPERTY_SETTER_IMPL(ConvolverNodeHostObject, normalize) { - auto convolverNode = std::static_pointer_cast(node_); - auto normalize = value.getBool(); - - auto event = [convolverNode, normalize](BaseAudioContext &) { - convolverNode->setNormalize(normalize); - }; - convolverNode->scheduleAudioEvent(std::move(event)); - normalize_ = normalize; + normalize_ = value.getBool(); + ; } JSI_HOST_FUNCTION_IMPL(ConvolverNodeHostObject, setBuffer) { + if (!args[0].isObject()) { + return jsi::Value::undefined(); + } + + auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); + thisValue.asObject(runtime).setExternalMemoryPressure( + runtime, bufferHostObject->getSizeInBytes()); + + setBuffer(bufferHostObject->audioBuffer_); + + return jsi::Value::undefined(); +} + +void ConvolverNodeHostObject::setBuffer(const std::shared_ptr &buffer) { + if (buffer == nullptr) { + return; + } + auto convolverNode = std::static_pointer_cast(node_); - std::shared_ptr copiedBuffer; + auto copiedBuffer = std::make_shared(*buffer); + + float scaleFactor = 1.0f; + if (normalize_) { + scaleFactor = convolverNode->calculateNormalizationScale(copiedBuffer); + } - if (args[0].isObject()) { - auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - thisValue.asObject(runtime).setExternalMemoryPressure( - runtime, bufferHostObject->getSizeInBytes()); - copiedBuffer = std::make_shared(*bufferHostObject->audioBuffer_); + auto threadPool = std::make_shared(4); + std::vector convolvers; + for (size_t i = 0; i < copiedBuffer->getNumberOfChannels(); ++i) { + AudioArray channelData(*copiedBuffer->getChannel(i)); + convolvers.emplace_back(); + convolvers.back().init(RENDER_QUANTUM_SIZE, channelData, copiedBuffer->getSize()); + } + if (copiedBuffer->getNumberOfChannels() == 1) { + // add one more convolver, because right now input is always stereo + AudioArray channelData(*copiedBuffer->getChannel(0)); + convolvers.emplace_back(); + convolvers.back().init(RENDER_QUANTUM_SIZE, channelData, copiedBuffer->getSize()); } - auto event = [convolverNode, copiedBuffer](BaseAudioContext &) { - convolverNode->setBuffer(copiedBuffer); + + auto internalBuffer = std::make_shared( + RENDER_QUANTUM_SIZE * 2, convolverNode->getChannelCount(), copiedBuffer->getSampleRate()); + auto intermediateBuffer = std::make_shared( + RENDER_QUANTUM_SIZE, convolvers.size(), copiedBuffer->getSampleRate()); + + struct SetupData { + std::shared_ptr buffer; + std::vector convolvers; + std::shared_ptr threadPool; + std::shared_ptr internalBuffer; + std::shared_ptr intermediateBuffer; + float scaleFactor; }; - convolverNode->scheduleAudioEvent(std::move(event)); - return jsi::Value::undefined(); + auto setupData = std::make_shared(SetupData{ + copiedBuffer, + std::move(convolvers), + threadPool, + internalBuffer, + intermediateBuffer, + scaleFactor}); + + auto event = [convolverNode, setupData](BaseAudioContext &) { + convolverNode->setBuffer( + setupData->buffer, + std::move(setupData->convolvers), + setupData->threadPool, + setupData->internalBuffer, + setupData->intermediateBuffer, + setupData->scaleFactor); + }; + convolverNode->scheduleAudioEvent(std::move(event)); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h index 650b29373..fc10c9cd1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h @@ -9,6 +9,7 @@ using namespace facebook; struct ConvolverOptions; class BaseAudioContext; +class AudioBuffer; class ConvolverNodeHostObject : public AudioNodeHostObject { public: @@ -21,5 +22,6 @@ class ConvolverNodeHostObject : public AudioNodeHostObject { private: bool normalize_; + void setBuffer(const std::shared_ptr &buffer); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 45a567827..5415eb6f3 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp @@ -1,12 +1,12 @@ #include #include +#include #include -#include -#include #include #include #include #include +#include #include namespace audioapi { @@ -17,51 +17,75 @@ ConvolverNode::ConvolverNode( gainCalibrationSampleRate_(context->getSampleRate()), remainingSegments_(0), internalBufferIndex_(0), - normalize_(!options.disableNormalization), signalledToStop_(false), scaleFactor_(1.0f), intermediateBuffer_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - setBuffer(options.buffer); isInitialized_.store(true, std::memory_order_release); } -void ConvolverNode::setNormalize(bool normalize) { - if (normalize_ != normalize) { - normalize_ = normalize; - if (normalize_ && buffer_) - calculateNormalizationScale(); +void ConvolverNode::setBuffer( + const std::shared_ptr &buffer, + std::vector convolvers, + const std::shared_ptr &threadPool, + const std::shared_ptr &internalBuffer, + const std::shared_ptr &intermediateBuffer, + float scaleFactor) { + std::shared_ptr context = context_.lock(); + if (context == nullptr) { + return; } - if (!normalize_) { - scaleFactor_ = 1.0f; + + auto graphManager = context->getGraphManager(); + + if (buffer_) { + graphManager->addAudioBufferForDestruction(std::move(buffer_)); + } + if (internalBuffer_) { + graphManager->addAudioBufferForDestruction(std::move(internalBuffer_)); } + if (intermediateBuffer_) { + graphManager->addAudioBufferForDestruction(std::move(intermediateBuffer_)); + } + + // TODO move convolvers and thread destruction to graph manager as well + + buffer_ = buffer; + convolvers_ = std::move(convolvers); + threadPool_ = threadPool; + internalBuffer_ = internalBuffer; + intermediateBuffer_ = intermediateBuffer; + scaleFactor_ = scaleFactor; + internalBufferIndex_ = 0; } -void ConvolverNode::setBuffer(const std::shared_ptr &buffer) { - if (buffer_ != buffer && buffer != nullptr) { - buffer_ = buffer; - if (normalize_) - calculateNormalizationScale(); - threadPool_ = std::make_shared(4); - convolvers_.clear(); - for (size_t i = 0; i < buffer->getNumberOfChannels(); ++i) { - convolvers_.emplace_back(); - AudioArray channelData(*buffer->getChannel(i)); - convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getSize()); - } - if (buffer->getNumberOfChannels() == 1) { - // add one more convolver, because right now input is always stereo - convolvers_.emplace_back(); - AudioArray channelData(*buffer->getChannel(0)); - convolvers_.back().init(RENDER_QUANTUM_SIZE, channelData, buffer->getSize()); +float ConvolverNode::calculateNormalizationScale(const std::shared_ptr &buffer) { + int numberOfChannels = buffer->getNumberOfChannels(); + auto length = buffer->getSize(); + + float power = 0; + + for (size_t channel = 0; channel < numberOfChannels; ++channel) { + float channelPower = 0; + auto channelData = buffer->getChannel(channel)->span(); + for (int i = 0; i < length; ++i) { + float sample = channelData[i]; + channelPower += sample * sample; } - internalBuffer_ = std::make_shared( - RENDER_QUANTUM_SIZE * 2, channelCount_, buffer->getSampleRate()); - intermediateBuffer_ = std::make_shared( - RENDER_QUANTUM_SIZE, convolvers_.size(), buffer->getSampleRate()); - internalBufferIndex_ = 0; + power += channelPower; + } + + power = std::sqrt(power / (numberOfChannels * length)); + if (power < MIN_IR_POWER) { + power = MIN_IR_POWER; } + + auto scaleFactor = 1 / power; + scaleFactor *= std::pow(10, GAIN_CALIBRATION * 0.05f); + scaleFactor *= gainCalibrationSampleRate_ / buffer->getSampleRate(); + + return scaleFactor; } void ConvolverNode::onInputDisabled() { @@ -122,31 +146,6 @@ std::shared_ptr ConvolverNode::processNode( return audioBuffer_; } -void ConvolverNode::calculateNormalizationScale() { - int numberOfChannels = buffer_->getNumberOfChannels(); - auto length = buffer_->getSize(); - - float power = 0; - - for (size_t channel = 0; channel < numberOfChannels; ++channel) { - float channelPower = 0; - auto channelData = buffer_->getChannel(channel)->span(); - for (int i = 0; i < length; ++i) { - float sample = channelData[i]; - channelPower += sample * sample; - } - power += channelPower; - } - - power = std::sqrt(power / (numberOfChannels * length)); - if (power < MIN_IR_POWER) { - power = MIN_IR_POWER; - } - scaleFactor_ = 1 / power; - scaleFactor_ *= std::pow(10, GAIN_CALIBRATION * 0.05f); - scaleFactor_ *= gainCalibrationSampleRate_ / buffer_->getSampleRate(); -} - void ConvolverNode::performConvolution(const std::shared_ptr &processingBuffer) { if (processingBuffer->getNumberOfChannels() == 1) { for (int i = 0; i < convolvers_.size(); ++i) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h index 48ebf088a..4041d3c5b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h @@ -26,10 +26,15 @@ class ConvolverNode : public AudioNode { const ConvolverOptions &options); /// @note Audio Thread only - void setNormalize(bool normalize); + void setBuffer( + const std::shared_ptr &buffer, + std::vector convolvers, + const std::shared_ptr &threadPool, + const std::shared_ptr &internalBuffer, + const std::shared_ptr &intermediateBuffer, + float scaleFactor); - /// @note Audio Thread only - void setBuffer(const std::shared_ptr &buffer); + float calculateNormalizationScale(const std::shared_ptr &buffer); protected: std::shared_ptr processNode( @@ -42,10 +47,9 @@ class ConvolverNode : public AudioNode { int framesToProcess, bool checkIsAlreadyProcessed) override; void onInputDisabled() override; - float gainCalibrationSampleRate_; + const float gainCalibrationSampleRate_; size_t remainingSegments_; size_t internalBufferIndex_; - bool normalize_; bool signalledToStop_; float scaleFactor_; std::shared_ptr intermediateBuffer_; @@ -58,7 +62,6 @@ class ConvolverNode : public AudioNode { std::vector convolvers_; std::shared_ptr threadPool_; - void calculateNormalizationScale(); void performConvolution(const std::shared_ptr &processingBuffer); }; From 9da5b7b1a0966b59b49c00e05a70e64edb78cca7 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Wed, 4 Mar 2026 17:42:12 +0100 Subject: [PATCH 69/73] fix: triple buffer polishing --- .../common/cpp/audioapi/utils/TripleBuffer.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp index 78decc8cf..b8b986e5e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp @@ -31,6 +31,9 @@ class TripleBuffer { TripleBuffer(const TripleBuffer &) = delete; TripleBuffer &operator=(const TripleBuffer &) = delete; + TripleBuffer(TripleBuffer &&) = delete; + TripleBuffer &operator=(TripleBuffer &&) = delete; + T *getForWriter() { return std::launder(reinterpret_cast(&buffers_[backIndex_])); } @@ -58,14 +61,14 @@ class TripleBuffer { uint32_t hasUpdate : 1; }; - struct alignas(hardware_constructive_interference_size) AlignedBuffer { + struct alignas(hardware_destructive_interference_size) AlignedBuffer { alignas(T) std::byte data[sizeof(T)]; }; AlignedBuffer buffers_[3]; - alignas(hardware_constructive_interference_size) uint32_t frontIndex_{0}; - alignas(hardware_constructive_interference_size) std::atomic state_{{1, false}}; - alignas(hardware_constructive_interference_size) uint32_t backIndex_{2}; + alignas(hardware_destructive_interference_size) uint32_t frontIndex_{0}; + alignas(hardware_destructive_interference_size) std::atomic state_{{1, false}}; + alignas(hardware_destructive_interference_size) uint32_t backIndex_{2}; }; } // namespace audioapi From e65aa82c8e24089ddc99bfe63f629af28d0365e8 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Mar 2026 07:43:53 +0100 Subject: [PATCH 70/73] refactor: added concept to ensure TripleBuffer is only instantiated with copyable types --- .../common/cpp/audioapi/utils/TripleBuffer.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp index b8b986e5e..9545a0a14 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp @@ -4,9 +4,13 @@ #include #include #include +#include namespace audioapi { +template +concept ConstructibleFromCopyable = std::constructible_from && (std::copy_constructible> && ...); + /// @brief A lock-free triple buffer for single producer and single consumer scenarios. /// The producer can write to one buffer while the consumer reads from another buffer, and the third buffer is idle. /// The producer can publish new data by swapping the back buffer with the idle buffer, @@ -16,6 +20,7 @@ template class TripleBuffer { public: template + requires ConstructibleFromCopyable explicit TripleBuffer(Args &&...args) { new (&buffers_[0]) T(args...); new (&buffers_[1]) T(args...); From 6c36e5a08fa4d0b2ca84947184e65ef54c5e69e6 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Mar 2026 07:44:15 +0100 Subject: [PATCH 71/73] ci: lint --- .../common/cpp/audioapi/utils/TripleBuffer.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp index 9545a0a14..88a358e37 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/utils/TripleBuffer.hpp @@ -2,14 +2,15 @@ #include #include +#include #include #include -#include namespace audioapi { template -concept ConstructibleFromCopyable = std::constructible_from && (std::copy_constructible> && ...); +concept ConstructibleFromCopyable = std::constructible_from && + (std::copy_constructible> && ...); /// @brief A lock-free triple buffer for single producer and single consumer scenarios. /// The producer can write to one buffer while the consumer reads from another buffer, and the third buffer is idle. @@ -20,7 +21,7 @@ template class TripleBuffer { public: template - requires ConstructibleFromCopyable + requires ConstructibleFromCopyable explicit TripleBuffer(Args &&...args) { new (&buffers_[0]) T(args...); new (&buffers_[1]) T(args...); From 76220fd5d8c8eddc1c4c000e9b945bd77c3040a2 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Mar 2026 15:58:57 +0100 Subject: [PATCH 72/73] chore: requested changes --- .../audiodocs/docs/sources/streamer-node.mdx | 2 +- .../analysis/AnalyserNodeHostObject.cpp | 1 - .../AudioBufferQueueSourceNodeHostObject.cpp | 2 +- .../audioapi/core/analysis/AnalyserNode.cpp | 19 +- .../cpp/audioapi/core/analysis/AnalyserNode.h | 3 +- .../core/effects/BiquadFilterNode.cpp | 82 ++++---- .../audioapi/core/effects/BiquadFilterNode.h | 18 +- .../cpp/audioapi/core/effects/IIRFilterNode.h | 4 +- .../core/sources/AudioBufferSourceNode.cpp | 2 - .../common/cpp/audioapi/dsp/Windows.hpp | 197 ------------------ .../core/effects/biquad/BiquadFilterTest.cpp | 50 ++--- .../src/core/AudioBufferQueueSourceNode.ts | 2 +- 12 files changed, 98 insertions(+), 284 deletions(-) delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp diff --git a/packages/audiodocs/docs/sources/streamer-node.mdx b/packages/audiodocs/docs/sources/streamer-node.mdx index 51303e78e..a619917f7 100644 --- a/packages/audiodocs/docs/sources/streamer-node.mdx +++ b/packages/audiodocs/docs/sources/streamer-node.mdx @@ -28,7 +28,7 @@ constructor(context: BaseAudioContext, options: StreamerOptions) | Parameter | Type | Default | | | :---: | :---: | :----: | :---- | -| `streamPath` | `string` | - | Value for [`streamPath`](/docs/sources/streamer-node#properties) | +| `streamPath` | `string` | - | Value for [`streamPath`](/docs/sources/streamer-node#properties) | Or by using `BaseAudioContext` factory method: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp index f9f3c3e24..c5262e959 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp index 5bdf41e0b..57b071d8c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp @@ -43,7 +43,7 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, start) { auto event = [audioBufferQueueSourceNode, when = args[0].getNumber(), - offset = args[1].isNumber() ? args[1].getNumber() : -1](BaseAudioContext &) { + offset = args[1].getNumber()](BaseAudioContext &) { audioBufferQueueSourceNode->start(when, offset); }; audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event)); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index bda6c8c3d..ed6b19209 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -35,8 +35,7 @@ void AnalyserNode::setFFTSize(int fftSize) { complexData_ = std::vector>(fftSize); magnitudeArray_ = std::make_unique(fftSize / 2); tempArray_ = std::make_unique(fftSize); - windowData_ = std::make_unique(fftSize); - dsp::Blackman().apply(windowData_->span()); + initializeWindowData(fftSize); fftSize_.store(fftSize, std::memory_order_release); } @@ -149,4 +148,20 @@ void AnalyserNode::doFFTAnalysis() { } } +void AnalyserNode::initializeWindowData(int fftSize) { + windowData_ = std::make_unique(fftSize); + auto data = windowData_->span(); + auto size = windowData_->getSize(); + + const auto invSizeMinusOne = 1.0f / static_cast(size - 1); + const auto alpha = 2.0f * std::numbers::pi_v * invSizeMinusOne; + + for (size_t i = 0; i < size; ++i) { + const auto phase = alpha * i; + // 4*PI*x is just 2 * (2*PI*x) + const auto window = 0.42f - 0.50f * std::cos(phase) + 0.08f * std::cos(2.0f * phase); + data[i] = window; + } +} + } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h index 0c871e006..c806e820b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -115,6 +114,8 @@ class AnalyserNode : public AudioNode { size_t lastAnalyzedSequence_ = 0; // JS thread only void doFFTAnalysis(); + + void initializeWindowData(int fftSize); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp index 4f44ebdaa..5c77ffe48 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp @@ -145,16 +145,16 @@ void BiquadFilterNode::getFrequencyResponse( } } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowpassCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getLowpassCoefficients( float frequency, float Q) { // Limit frequency to [0, 1] range if (frequency >= 1.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float g = std::pow(10.0f, 0.05f * Q); @@ -164,17 +164,17 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowpassCoefficients( float cosW = std::cos(theta); float beta = (1 - cosW) / 2; - return setNormalizedCoefficients(beta, 2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); + return getNormalizedCoefficients(beta, 2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighpassCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getHighpassCoefficients( float frequency, float Q) { if (frequency >= 1.0f) { - return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float g = std::pow(10.0f, 0.05f * Q); @@ -184,40 +184,40 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighpassCoefficients( float cosW = std::cos(theta); float beta = (1 + cosW) / 2; - return setNormalizedCoefficients(beta, -2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); + return getNormalizedCoefficients(beta, -2 * beta, beta, 1 + alpha, -2 * cosW, 1 - alpha); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setBandpassCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getBandpassCoefficients( float frequency, float Q) { // Limit frequency to [0, 1] range if (frequency <= 0.0f || frequency >= 1.0f) { - return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } // Limit Q to positive values if (Q <= 0.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - return setNormalizedCoefficients(alpha, 0.0f, -alpha, 1.0f + alpha, -2 * cosW, 1.0f - alpha); + return getNormalizedCoefficients(alpha, 0.0f, -alpha, 1.0f + alpha, -2 * cosW, 1.0f - alpha); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowshelfCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getLowshelfCoefficients( float frequency, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency >= 1.0f) { - return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; @@ -225,7 +225,7 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowshelfCoefficients( float cosW = std::cos(w0); float gamma = 2.0f * std::sqrt(A) * alpha; - return setNormalizedCoefficients( + return getNormalizedCoefficients( A * (A + 1 - (A - 1) * cosW + gamma), 2.0f * A * (A - 1 - (A + 1) * cosW), A * (A + 1 - (A - 1) * cosW - gamma), @@ -234,17 +234,17 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setLowshelfCoefficients( A + 1 + (A - 1) * cosW - gamma); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighshelfCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getHighshelfCoefficients( float frequency, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency >= 1.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (frequency <= 0.0f) { - return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; @@ -254,7 +254,7 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighshelfCoefficients( float cosW = std::cos(w0); float gamma = 2.0f * std::sqrt(A) * alpha; - return setNormalizedCoefficients( + return getNormalizedCoefficients( A * (A + 1 + (A - 1) * cosW + gamma), -2.0f * A * (A - 1 + (A + 1) * cosW), A * (A + 1 + (A - 1) * cosW - gamma), @@ -264,63 +264,63 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::setHighshelfCoefficients( } BiquadFilterNode::FilterCoefficients -BiquadFilterNode::setPeakingCoefficients(float frequency, float Q, float gain) { +BiquadFilterNode::getPeakingCoefficients(float frequency, float Q, float gain) { float A = std::pow(10.0f, gain / 40.0f); if (frequency <= 0.0f || frequency >= 1.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - return setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - return setNormalizedCoefficients( + return getNormalizedCoefficients( 1 + alpha * A, -2 * cosW, 1 - alpha * A, 1 + alpha / A, -2 * cosW, 1 - alpha / A); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setNotchCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getNotchCoefficients( float frequency, float Q) { if (frequency <= 0.0f || frequency >= 1.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - return setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - return setNormalizedCoefficients(1.0f, -2 * cosW, 1.0f, 1 + alpha, -2 * cosW, 1 - alpha); + return getNormalizedCoefficients(1.0f, -2 * cosW, 1.0f, 1 + alpha, -2 * cosW, 1 - alpha); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setAllpassCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getAllpassCoefficients( float frequency, float Q) { if (frequency <= 0.0f || frequency >= 1.0f) { - return setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } if (Q <= 0.0f) { - return setNormalizedCoefficients(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); + return getNormalizedCoefficients(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } float w0 = PI * frequency; float alpha = std::sin(w0) / (2 * Q); float cosW = std::cos(w0); - return setNormalizedCoefficients( + return getNormalizedCoefficients( 1 - alpha, -2 * cosW, 1 + alpha, 1 + alpha, -2 * cosW, 1 - alpha); } -BiquadFilterNode::FilterCoefficients BiquadFilterNode::setNormalizedCoefficients( +BiquadFilterNode::FilterCoefficients BiquadFilterNode::getNormalizedCoefficients( float b0, float b1, float b2, @@ -350,28 +350,28 @@ BiquadFilterNode::FilterCoefficients BiquadFilterNode::applyFilter( switch (type) { case BiquadFilterType::LOWPASS: - coeffs = setLowpassCoefficients(normalizedFrequency, Q); + coeffs = getLowpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::HIGHPASS: - coeffs = setHighpassCoefficients(normalizedFrequency, Q); + coeffs = getHighpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::BANDPASS: - coeffs = setBandpassCoefficients(normalizedFrequency, Q); + coeffs = getBandpassCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::LOWSHELF: - coeffs = setLowshelfCoefficients(normalizedFrequency, gain); + coeffs = getLowshelfCoefficients(normalizedFrequency, gain); break; case BiquadFilterType::HIGHSHELF: - coeffs = setHighshelfCoefficients(normalizedFrequency, gain); + coeffs = getHighshelfCoefficients(normalizedFrequency, gain); break; case BiquadFilterType::PEAKING: - coeffs = setPeakingCoefficients(normalizedFrequency, Q, gain); + coeffs = getPeakingCoefficients(normalizedFrequency, Q, gain); break; case BiquadFilterType::NOTCH: - coeffs = setNotchCoefficients(normalizedFrequency, Q); + coeffs = getNotchCoefficients(normalizedFrequency, Q); break; case BiquadFilterType::ALLPASS: - coeffs = setAllpassCoefficients(normalizedFrequency, Q); + coeffs = getAllpassCoefficients(normalizedFrequency, Q); break; default: break; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h index 3fbd0a607..e5dcc830a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/BiquadFilterNode.h @@ -89,16 +89,16 @@ class BiquadFilterNode : public AudioNode { double b0, b1, b2, a1, a2; }; - static FilterCoefficients setLowpassCoefficients(float frequency, float Q); - static FilterCoefficients setHighpassCoefficients(float frequency, float Q); - static FilterCoefficients setBandpassCoefficients(float frequency, float Q); - static FilterCoefficients setLowshelfCoefficients(float frequency, float gain); - static FilterCoefficients setHighshelfCoefficients(float frequency, float gain); - static FilterCoefficients setPeakingCoefficients(float frequency, float Q, float gain); - static FilterCoefficients setNotchCoefficients(float frequency, float Q); - static FilterCoefficients setAllpassCoefficients(float frequency, float Q); + static FilterCoefficients getLowpassCoefficients(float frequency, float Q); + static FilterCoefficients getHighpassCoefficients(float frequency, float Q); + static FilterCoefficients getBandpassCoefficients(float frequency, float Q); + static FilterCoefficients getLowshelfCoefficients(float frequency, float gain); + static FilterCoefficients getHighshelfCoefficients(float frequency, float gain); + static FilterCoefficients getPeakingCoefficients(float frequency, float Q, float gain); + static FilterCoefficients getNotchCoefficients(float frequency, float Q); + static FilterCoefficients getAllpassCoefficients(float frequency, float Q); static FilterCoefficients - setNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); + getNormalizedCoefficients(float b0, float b1, float b2, float a0, float a1, float a2); FilterCoefficients applyFilter(float frequency, float Q, float gain, float detune, BiquadFilterType type); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 2d4639b24..a5f6416cf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -80,9 +80,7 @@ class IIRFilterNode : public AudioNode { float scaleFactor) { AudioArray result(inputVector.data(), inputVector.size()); if (scaleFactor != 1.0f && scaleFactor != 0.0f && result.getSize() > 0) { - for (float &val : result) { - val /= scaleFactor; - } + result.scale(1.0f / scaleFactor); } return result; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp index 7eb9c0ae0..c4fddbe80 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp @@ -73,8 +73,6 @@ void AudioBufferSourceNode::setBuffer( buffer_ = nullptr; playbackRateBuffer_ = nullptr; - audioBuffer_ = - std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); return; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp b/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp deleted file mode 100644 index 8a5db033d..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/dsp/Windows.hpp +++ /dev/null @@ -1,197 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace audioapi::dsp { - -// https://en.wikipedia.org/wiki/Window_function -// https://personalpages.hs-kempten.de/~vollratj/InEl/pdf/Window%20function%20-%20Wikipedia.pdf -class WindowFunction { - public: - explicit WindowFunction(float amplitude = 1.0f) : amplitude_(amplitude) {} - - virtual void apply(std::span data) const noexcept = 0; - // forces STFT perfect-reconstruction (WOLA) on an existing window, for a given STFT interval. - static void forcePerfectReconstruction(std::span data, int interval) { - int windowLength = static_cast(data.size()); - - for (int i = 0; i < interval; ++i) { - float sum2 = 0; - - for (int index = i; index < windowLength; index += interval) { - sum2 += data[index] * data[index]; - } - - float factor = 1 / std::sqrt(sum2); - - for (int index = i; index < windowLength; index += interval) { - data[index] *= factor; - } - } - } - - protected: - // 1/L = amplitude - float amplitude_; -}; - -//https://en.wikipedia.org/wiki/Hann_function -// https://www.sciencedirect.com/topics/engineering/hanning-window -// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.hann.html#scipy.signal.windows.hann -class Hann : public WindowFunction { - public: - explicit Hann(float amplitude = 1.0f) : WindowFunction(amplitude) {} - - void apply(std::span data) const noexcept override { - const size_t size = data.size(); - if (size < 2) { - return; - } - - const float invSizeMinusOne = 1.0f / static_cast(size - 1); - const float constantPart = 2.0f * std::numbers::pi_v * invSizeMinusOne; - - for (size_t i = 0; i < size; ++i) { - float window = 0.5f * (1.0f - std::cos(constantPart * i)); - data[i] = window * amplitude_; - } - } -}; - -// https://www.sciencedirect.com/topics/engineering/blackman-window -// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.blackman.html#scipy.signal.windows.blackman -class Blackman : public WindowFunction { - public: - explicit Blackman(float amplitude = 1.0f) : WindowFunction(amplitude) {} - - void apply(std::span data) const noexcept override { - const size_t size = data.size(); - if (size < 2) { - return; - } - - const float invSizeMinusOne = 1.0f / static_cast(size - 1); - const float alpha = 2.0f * std::numbers::pi_v * invSizeMinusOne; - - for (size_t i = 0; i < size; ++i) { - const float phase = alpha * i; - // 4*PI*x is just 2 * (2*PI*x) - const float window = 0.42f - 0.50f * std::cos(phase) + 0.08f * std::cos(2.0f * phase); - data[i] = window * amplitude_; - } - } -}; - -// https://en.wikipedia.org/wiki/Kaiser_window -class Kaiser : public WindowFunction { - public: - explicit Kaiser(float beta, float amplitude = 1.0f) - : WindowFunction(amplitude), beta_(beta), invB0_(1.0f / bessel0(beta)) {} - - static Kaiser - withBandwidth(float bandwidth, bool heuristicOptimal = false, float amplitude = 1.0f) { - return Kaiser(bandwidthToBeta(bandwidth, heuristicOptimal), amplitude); - } - - void apply(std::span data) const noexcept override { - const size_t size = data.size(); - if (size == 0) { - return; - } - - const float invSize = 1.0f / static_cast(size); - const float commonScale = invB0_ * amplitude_; - - for (size_t i = 0; i < size; ++i) { - // Optimized 'r' calculation: (2i+1)/size - 1 - const float r = (static_cast(2 * i + 1) * invSize) - 1.0f; - const float arg = std::sqrt(std::max(0.0f, 1.0f - r * r)); - - data[i] = bessel0(beta_ * arg) * commonScale; - } - } - - private: - // beta = pi * alpha - // invB0 = 1 / I0(beta) - float beta_; - float invB0_; - - // https://en.wikipedia.org/wiki/Bessel_function#Modified_Bessel_functions:_I%CE%B1,_K%CE%B1 - static inline float bessel0(float x) { - const double significanceLimit = 1e-4; - auto result = 0.0f; - auto term = 1.0f; - auto m = 1.0f; - while (term > significanceLimit) { - result += term; - ++m; - term *= (x * x) / (4 * m * m); - } - - return result; - } - inline static float bandwidthToBeta(float bandwidth, bool heuristicOptimal = false) { - if (heuristicOptimal) { // Heuristic based on numerical search - return bandwidth + 8.0f / (bandwidth + 3.0f) * (bandwidth + 3.0f) + - 0.25f * std::max(3.0f - bandwidth, 0.0f); - } - - bandwidth = std::max(bandwidth, 2.0f); - auto alpha = std::sqrt(bandwidth * bandwidth * 0.25f - 1.0f); - return alpha * std::numbers::pi_v; - } -}; - -// https://www.recordingblogs.com/wiki/gaussian-window -class ApproximateConfinedGaussian : public WindowFunction { - public: - explicit ApproximateConfinedGaussian(float sigma, float amplitude = 1.0f) - : WindowFunction(amplitude), gaussianFactor_(0.0625f / (sigma * sigma)) {} - - static ApproximateConfinedGaussian withBandwidth(float bandwidth, float amplitude = 1.0f) { - return ApproximateConfinedGaussian(bandwidthToSigma(bandwidth), amplitude); - } - - void apply(std::span data) const noexcept override { - const size_t size = data.size(); - if (size == 0) - return; - - const float g1 = getGaussian(1.0f); - const float g3 = getGaussian(3.0f); - const float g_1 = getGaussian(-1.0f); - const float g2 = getGaussian(2.0f); - - const float offsetScale = g1 / (g3 + g_1); - const float norm = 1.0f / (g1 - 2.0f * offsetScale * g2); - - const float invSize = 1.0f / static_cast(size); - const float totalAmplitude = norm * amplitude_; - - for (size_t i = 0; i < size; ++i) { - const float r = (static_cast(2 * i + 1) * invSize) - 1.0f; - - const float gR = getGaussian(r); - const float gRMinus2 = getGaussian(r - 2.0f); - const float gRPlus2 = getGaussian(r + 2.0f); - - data[i] = totalAmplitude * (gR - offsetScale * (gRMinus2 + gRPlus2)); - } - } - - private: - float gaussianFactor_; - - inline static float bandwidthToSigma(float bandwidth) noexcept { - return 0.3f / std::sqrt(bandwidth); - } - - [[nodiscard]] inline float getGaussian(float x) const noexcept { - return std::exp(-x * x * gaussianFactor_); - } -}; -} // namespace audioapi::dsp diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp index 7dd982686..e4953169d 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.cpp @@ -18,49 +18,49 @@ void BiquadFilterTest::expectCoefficientsNear( void BiquadFilterTest::testLowpass(float frequency, float Q) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setLowpassCoefficients(normalizedFrequency, Q); + auto coeffs = BiquadFilterNode::getLowpassCoefficients(normalizedFrequency, Q); expectCoefficientsNear(coeffs, calculateLowpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testHighpass(float frequency, float Q) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setHighpassCoefficients(normalizedFrequency, Q); + auto coeffs = BiquadFilterNode::getHighpassCoefficients(normalizedFrequency, Q); expectCoefficientsNear(coeffs, calculateHighpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testBandpass(float frequency, float Q) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setBandpassCoefficients(normalizedFrequency, Q); + auto coeffs = BiquadFilterNode::getBandpassCoefficients(normalizedFrequency, Q); expectCoefficientsNear(coeffs, calculateBandpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testNotch(float frequency, float Q) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setNotchCoefficients(normalizedFrequency, Q); + auto coeffs = BiquadFilterNode::getNotchCoefficients(normalizedFrequency, Q); expectCoefficientsNear(coeffs, calculateNotchCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testAllpass(float frequency, float Q) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setAllpassCoefficients(normalizedFrequency, Q); + auto coeffs = BiquadFilterNode::getAllpassCoefficients(normalizedFrequency, Q); expectCoefficientsNear(coeffs, calculateAllpassCoefficients(normalizedFrequency, Q)); } void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setPeakingCoefficients(normalizedFrequency, Q, gain); + auto coeffs = BiquadFilterNode::getPeakingCoefficients(normalizedFrequency, Q, gain); expectCoefficientsNear(coeffs, calculatePeakingCoefficients(normalizedFrequency, Q, gain)); } void BiquadFilterTest::testLowshelf(float frequency, float gain) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setLowshelfCoefficients(normalizedFrequency, gain); + auto coeffs = BiquadFilterNode::getLowshelfCoefficients(normalizedFrequency, gain); expectCoefficientsNear(coeffs, calculateLowshelfCoefficients(normalizedFrequency, gain)); } void BiquadFilterTest::testHighshelf(float frequency, float gain) { float normalizedFrequency = frequency / nyquistFrequency; - auto coeffs = BiquadFilterNode::setHighshelfCoefficients(normalizedFrequency, gain); + auto coeffs = BiquadFilterNode::getHighshelfCoefficients(normalizedFrequency, gain); expectCoefficientsNear(coeffs, calculateHighshelfCoefficients(normalizedFrequency, gain)); } @@ -97,106 +97,106 @@ INSTANTIATE_TEST_SUITE_P( 0.0f, // default 40.0f)); -TEST_P(BiquadFilterFrequencyTest, SetLowpassCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestLowpassCoefficients) { float frequency = GetParam(); float Q = 1.0f; testLowpass(frequency, Q); } -TEST_P(BiquadFilterFrequencyTest, SetHighpassCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestHighpassCoefficients) { float frequency = GetParam(); float Q = 1.0f; testHighpass(frequency, Q); } -TEST_P(BiquadFilterFrequencyTest, SetBandpassCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestBandpassCoefficients) { float frequency = GetParam(); float Q = 1.0f; testBandpass(frequency, Q); } -TEST_P(BiquadFilterFrequencyTest, SetNotchCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestNotchCoefficients) { float frequency = GetParam(); float Q = 1.0f; testNotch(frequency, Q); } -TEST_P(BiquadFilterFrequencyTest, SetAllpassCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestAllpassCoefficients) { float frequency = GetParam(); float Q = 1.0f; testAllpass(frequency, Q); } -TEST_P(BiquadFilterFrequencyTest, SetPeakingCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestPeakingCoefficients) { float frequency = GetParam(); float Q = 1.0f; float gain = 2.0f; testPeaking(frequency, Q, gain); } -TEST_P(BiquadFilterFrequencyTest, SetLowshelfCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestLowshelfCoefficients) { float frequency = GetParam(); float gain = 2.0f; testLowshelf(frequency, gain); } -TEST_P(BiquadFilterFrequencyTest, SetHighshelfCoefficients) { +TEST_P(BiquadFilterFrequencyTest, TestHighshelfCoefficients) { float frequency = GetParam(); float gain = 2.0f; testHighshelf(frequency, gain); } -TEST_P(BiquadFilterQTestLowpassHighpass, SetLowpassCoefficients) { +TEST_P(BiquadFilterQTestLowpassHighpass, TestLowpassCoefficients) { float frequency = 1000.0f; float Q = GetParam(); testLowpass(frequency, Q); } -TEST_P(BiquadFilterQTestLowpassHighpass, SetHighpassCoefficients) { +TEST_P(BiquadFilterQTestLowpassHighpass, TestHighpassCoefficients) { float frequency = 1000.0f; float Q = GetParam(); testHighpass(frequency, Q); } -TEST_P(BiquadFilterQTestRestTypes, SetBandpassCoefficients) { +TEST_P(BiquadFilterQTestRestTypes, TestBandpassCoefficients) { float frequency = 1000.0f; float Q = GetParam(); testBandpass(frequency, Q); } -TEST_P(BiquadFilterQTestRestTypes, SetNotchCoefficients) { +TEST_P(BiquadFilterQTestRestTypes, TestNotchCoefficients) { float frequency = 1000.0f; float Q = GetParam(); testNotch(frequency, Q); } -TEST_P(BiquadFilterQTestRestTypes, SetAllpassCoefficients) { +TEST_P(BiquadFilterQTestRestTypes, TestAllpassCoefficients) { float frequency = 1000.0f; float Q = GetParam(); testAllpass(frequency, Q); } -TEST_P(BiquadFilterQTestRestTypes, SetPeakingCoefficients) { +TEST_P(BiquadFilterQTestRestTypes, TestPeakingCoefficients) { float frequency = 1000.0f; float Q = GetParam(); float gain = 2.0f; testPeaking(frequency, Q, gain); } -TEST_P(BiquadFilterGainTest, SetPeakingCoefficients) { +TEST_P(BiquadFilterGainTest, TestPeakingCoefficients) { float frequency = 1000.0f; float Q = 1.0f; float gain = GetParam(); testPeaking(frequency, Q, gain); } -TEST_P(BiquadFilterGainTest, SetLowshelfCoefficients) { +TEST_P(BiquadFilterGainTest, TestLowshelfCoefficients) { float frequency = 1000.0f; float gain = GetParam(); testLowshelf(frequency, gain); } -TEST_P(BiquadFilterGainTest, SetHighshelfCoefficients) { +TEST_P(BiquadFilterGainTest, TestHighshelfCoefficients) { float frequency = 1000.0f; float gain = GetParam(); testHighshelf(frequency, gain); diff --git a/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts index e81e4768c..ea01fc288 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts @@ -39,7 +39,7 @@ export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNod (this.node as IAudioBufferQueueSourceNode).clearBuffers(); } - public override start(when: number = 0, offset?: number): void { + public override start(when: number = 0, offset: number = -1): void { if (when < 0) { throw new RangeError( `when must be a finite non-negative number: ${when}` From 23cb062221d4f9a50a2866f19061d28d81313b4b Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Thu, 5 Mar 2026 15:59:20 +0100 Subject: [PATCH 73/73] chore: yarn format --- .../audioapi/core/analysis/AnalyserNode.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp index ed6b19209..8f1f3bdb5 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/analysis/AnalyserNode.cpp @@ -149,19 +149,19 @@ void AnalyserNode::doFFTAnalysis() { } void AnalyserNode::initializeWindowData(int fftSize) { - windowData_ = std::make_unique(fftSize); - auto data = windowData_->span(); - auto size = windowData_->getSize(); - - const auto invSizeMinusOne = 1.0f / static_cast(size - 1); - const auto alpha = 2.0f * std::numbers::pi_v * invSizeMinusOne; - - for (size_t i = 0; i < size; ++i) { - const auto phase = alpha * i; - // 4*PI*x is just 2 * (2*PI*x) - const auto window = 0.42f - 0.50f * std::cos(phase) + 0.08f * std::cos(2.0f * phase); - data[i] = window; - } + windowData_ = std::make_unique(fftSize); + auto data = windowData_->span(); + auto size = windowData_->getSize(); + + const auto invSizeMinusOne = 1.0f / static_cast(size - 1); + const auto alpha = 2.0f * std::numbers::pi_v * invSizeMinusOne; + + for (size_t i = 0; i < size; ++i) { + const auto phase = alpha * i; + // 4*PI*x is just 2 * (2*PI*x) + const auto window = 0.42f - 0.50f * std::cos(phase) + 0.08f * std::cos(2.0f * phase); + data[i] = window; + } } } // namespace audioapi