From 3b4c9c78b5074bf0d699d42a84d7052b39b95612 Mon Sep 17 00:00:00 2001 From: Michal Sek Date: Fri, 15 Nov 2024 10:59:45 +0100 Subject: [PATCH] feat: channel mixer --- .../common/cpp/core/AudioArray.cpp | 16 +- .../common/cpp/core/AudioArray.h | 6 +- .../common/cpp/core/AudioBuffer.cpp | 146 ++++---- .../common/cpp/core/AudioBuffer.h | 9 +- .../common/cpp/core/AudioBus.cpp | 316 ++++++++++++++++++ .../common/cpp/core/AudioBus.h | 57 ++++ .../common/cpp/core/AudioChannel.cpp | 18 - .../common/cpp/core/AudioChannel.h | 37 -- .../common/cpp/core/AudioNode.h | 4 +- .../common/cpp/core/AudioNodeBuffer.h | 31 -- .../common/cpp/utils/VectorMath.cpp | 73 +++- .../common/cpp/utils/VectorMath.h | 6 + 12 files changed, 550 insertions(+), 169 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/core/AudioBus.cpp create mode 100644 packages/react-native-audio-api/common/cpp/core/AudioBus.h delete mode 100644 packages/react-native-audio-api/common/cpp/core/AudioChannel.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/core/AudioChannel.h delete mode 100644 packages/react-native-audio-api/common/cpp/core/AudioNodeBuffer.h diff --git a/packages/react-native-audio-api/common/cpp/core/AudioArray.cpp b/packages/react-native-audio-api/common/cpp/core/AudioArray.cpp index b88028f0..c137db83 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioArray.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioArray.cpp @@ -15,6 +15,10 @@ int AudioArray::getSize() const { return size_; } +float* AudioArray::getData() const { + return data_; +} + float& AudioArray::operator[](int index) { return data_[index]; } @@ -40,12 +44,12 @@ void AudioArray::resize(int size) { zero(); } -void AudioArray::copy(const AudioArray &source) { - if (size_ != source.size_) { - resize(source.size_); +void AudioArray::copy(const AudioArray* source) { + if (size_ != source->getSize()) { + resize(source->getSize()); } - memcpy(data_, source.data_, size_ * sizeof(float)); + memcpy(data_, source->getData(), size_ * sizeof(float)); } float AudioArray::getMaxAbsValue() const { @@ -66,8 +70,8 @@ void AudioArray::scale(float value) { VectorMath::multiplyByScalar(data_, value, data_, size_); }; -void AudioArray::sum(const AudioArray &source) { - VectorMath::add(data_, source.data_, data_, size_); +void AudioArray::sum(const AudioArray* source) { + VectorMath::add(data_, source->getData(), data_, size_); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioArray.h b/packages/react-native-audio-api/common/cpp/core/AudioArray.h index a1cd1e30..0b79060a 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioArray.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioArray.h @@ -10,6 +10,7 @@ class AudioArray { ~AudioArray(); [[nodiscard]] int getSize() const; + float* getData() const; float& operator[](int index); @@ -17,13 +18,14 @@ class AudioArray { void zero(); void resize(int size); - void copy(const AudioArray &source); + void copy(const AudioArray* source); float getMaxAbsValue() const; void normalize(); void scale(float value); - void sum(const AudioArray &source); + void sum(const AudioArray* source); + private: float *data_; diff --git a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp index 28a28baa..0a7a3e7f 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp @@ -7,81 +7,91 @@ AudioBuffer::AudioBuffer(int numberOfChannels, int length, int sampleRate) length_(length), sampleRate_(sampleRate), duration_(static_cast(length) / sampleRate) { - channels_ = new float *[numberOfChannels]; - for (int i = 0; i < numberOfChannels; i++) { - channels_[i] = new float[length]; - - for (int j = 0; j < length; j++) { - channels_[i][j] = 0.0f; - } - } } -int AudioBuffer::getNumberOfChannels() const { - return numberOfChannels_; -} +} // namespace audioapi -int AudioBuffer::getLength() const { - return length_; -} -int AudioBuffer::getSampleRate() const { - return sampleRate_; -} +// AudioBuffer::AudioBuffer(int numberOfChannels, int length, int sampleRate) +// : numberOfChannels_(numberOfChannels), +// length_(length), +// sampleRate_(sampleRate), +// duration_(static_cast(length) / sampleRate) { +// channels_ = new float *[numberOfChannels]; -double AudioBuffer::getDuration() const { - return duration_; -} +// for (int i = 0; i < numberOfChannels; i++) { +// channels_[i] = new float[length]; -float *AudioBuffer::getChannelData(int channel) const { - return channels_[channel]; -} +// for (int j = 0; j < length; j++) { +// channels_[i][j] = 0.0f; +// } +// } +// } -std::shared_ptr AudioBuffer::mix(int outputNumberOfChannels) { - if (outputNumberOfChannels == numberOfChannels_) { - return shared_from_this(); - } - - auto mixedBuffer = std::make_shared( - outputNumberOfChannels, length_, sampleRate_); - - switch (this->numberOfChannels_) { - case 1: - mixedBuffer->copyToChannel(this->channels_[0], length_, 0, 0); - mixedBuffer->copyToChannel(this->channels_[0], length_, 1, 0); - break; - case 2: - for (int i = 0; i < length_; i++) { - mixedBuffer->channels_[0][i] = - (this->channels_[0][i] + this->channels_[1][i]) / 2; - } - break; - } - - return mixedBuffer; -} +// int AudioBuffer::getNumberOfChannels() const { +// return numberOfChannels_; +// } -void AudioBuffer::copyFromChannel( - float *destination, - int destinationLength, - int channelNumber, - int startInChannel) const { - std::copy( - channels_[channelNumber] + startInChannel, - channels_[channelNumber] + startInChannel + - std::min(destinationLength, length_ - startInChannel), - destination); -} +// int AudioBuffer::getLength() const { +// return length_; +// } -void AudioBuffer::copyToChannel( - const float *source, - int sourceLength, - int channelNumber, - int startInChannel) { - std::copy( - source, - source + std::min(sourceLength, length_ - startInChannel), - channels_[channelNumber] + startInChannel); -} -} // namespace audioapi +// int AudioBuffer::getSampleRate() const { +// return sampleRate_; +// } + +// double AudioBuffer::getDuration() const { +// return duration_; +// } + +// float *AudioBuffer::getChannelData(int channel) const { +// return channels_[channel]; +// } + +// std::shared_ptr AudioBuffer::mix(int outputNumberOfChannels) { +// if (outputNumberOfChannels == numberOfChannels_) { +// return shared_from_this(); +// } + +// auto mixedBuffer = std::make_shared( +// outputNumberOfChannels, length_, sampleRate_); + +// switch (this->numberOfChannels_) { +// case 1: +// mixedBuffer->copyToChannel(this->channels_[0], length_, 0, 0); +// mixedBuffer->copyToChannel(this->channels_[0], length_, 1, 0); +// break; +// case 2: +// for (int i = 0; i < length_; i++) { +// mixedBuffer->channels_[0][i] = +// (this->channels_[0][i] + this->channels_[1][i]) / 2; +// } +// break; +// } + +// return mixedBuffer; +// } + +// void AudioBuffer::copyFromChannel( +// float *destination, +// int destinationLength, +// int channelNumber, +// int startInChannel) const { +// std::copy( +// channels_[channelNumber] + startInChannel, +// channels_[channelNumber] + startInChannel + +// std::min(destinationLength, length_ - startInChannel), +// destination); +// } + +// void AudioBuffer::copyToChannel( +// const float *source, +// int sourceLength, +// int channelNumber, +// int startInChannel) { +// std::copy( +// source, +// source + std::min(sourceLength, length_ - startInChannel), +// channels_[channelNumber] + startInChannel); +// } diff --git a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h index 3e04f1ac..642519ec 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h @@ -7,14 +7,17 @@ namespace audioapi { +class AudioBus; + class AudioBuffer : public std::enable_shared_from_this { public: explicit AudioBuffer(int numberOfChannels, int length, int sampleRate); - [[nodiscard]] int getNumberOfChannels() const; [[nodiscard]] int getLength() const; [[nodiscard]] int getSampleRate() const; [[nodiscard]] double getDuration() const; + + [[nodiscard]] int getNumberOfChannels() const; [[nodiscard]] float *getChannelData(int channel) const; void copyFromChannel( @@ -34,9 +37,7 @@ class AudioBuffer : public std::enable_shared_from_this { int length_; int sampleRate_; double duration_; - float **channels_; - - std::shared_ptr mix(int outputNumberOfChannels); + std::unique_ptr bus_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioBus.cpp b/packages/react-native-audio-api/common/cpp/core/AudioBus.cpp new file mode 100644 index 00000000..795e1a1c --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/core/AudioBus.cpp @@ -0,0 +1,316 @@ + +#include "AudioBus.h" +#include "Constants.h" +#include "AudioArray.h" +#include "VectorMath.h" +#include "BaseAudioContext.h" + +// 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 +const float SQRT_HALF = sqrtf(0.5f); + +namespace audioapi { + +AudioBus::AudioBus(BaseAudioContext *context) + : context_(context), numberOfChannels_(CHANNEL_COUNT) { + sampleRate_ = context_->getSampleRate(); + size_ = context_->getBufferSizeInFrames(); + + + createChannels(); +}; + +AudioBus::AudioBus(BaseAudioContext *context, int numberOfChannels) + : context_(context), numberOfChannels_(numberOfChannels) { + sampleRate_ = context_->getSampleRate(); + size_ = context_->getBufferSizeInFrames(); + + createChannels(); +}; + +AudioBus::AudioBus(BaseAudioContext *context, int numberOfChannels, int size) + : context_(context), numberOfChannels_(numberOfChannels), size_(size) { + sampleRate_ = context_->getSampleRate(); + + createChannels(); +}; + + +AudioBus::~AudioBus() { + channels_.clear(); +}; + +void AudioBus::createChannels() { + channels_ = std::vector>(numberOfChannels_, std::make_unique(size_)); +}; + +int AudioBus::getNumberOfChannels() const { + return numberOfChannels_; +}; + +int AudioBus::getSampleRate() const { + return sampleRate_; +}; + +int AudioBus::getSize() const { + return size_; +}; + +AudioArray* AudioBus::getChannel(int index) const { + return channels_[index].get(); +}; + +AudioArray* AudioBus::getChannelByType(int channelType) const { + switch (getNumberOfChannels()) { + case 1: // mono + if (channelType == ChannelMono || channelType == ChannelLeft) { + return getChannel(0); + } + return 0; + + case 2: // stereo + switch (channelType) { + case ChannelLeft: return getChannel(0); + case ChannelRight: return getChannel(1); + default: return 0; + } + + case 4: // quad + switch (channelType) { + case ChannelLeft: return getChannel(0); + case ChannelRight: return getChannel(1); + case ChannelSurroundLeft: return getChannel(2); + case ChannelSurroundRight: return getChannel(3); + default: return 0; + } + + case 5: // 5.0 + switch (channelType) { + case ChannelLeft: return getChannel(0); + case ChannelRight: return getChannel(1); + case ChannelCenter: return getChannel(2); + case ChannelSurroundLeft: return getChannel(3); + case ChannelSurroundRight: return getChannel(4); + default: return 0; + } + + case 6: // 5.1 + switch (channelType) { + case ChannelLeft: return getChannel(0); + case ChannelRight: return getChannel(1); + case ChannelCenter: return getChannel(2); + case ChannelLFE: return getChannel(3); + case ChannelSurroundLeft: return getChannel(4); + case ChannelSurroundRight: return getChannel(5); + default: return 0; + } + default: + return 0; + } +}; + +void AudioBus::zero() { + for (auto it = channels_.begin(); it != channels_.end(); it += 1) { + it->get()->zero(); + } +}; + +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 it = channels_.begin(); it != channels_.end(); it += 1) { + it->get()->scale(value); + } +}; + +float AudioBus::maxAbsValue() const { + float maxAbsValue = 0.0f; + + for (auto it = channels_.begin(); it != channels_.end(); it += 1) { + float channelMaxAbsValue = it->get()->getMaxAbsValue(); + maxAbsValue = std::max(maxAbsValue, channelMaxAbsValue); + } + + return maxAbsValue; +}; + +void AudioBus::copy(const AudioBus &source) { + if (&source == this) { + return; + } + + zero(); + sum(source); +}; + +void AudioBus::sum(const AudioBus &source) { + if (&source == this) { + return; + } + + int numberOfSourceChannels = source.getNumberOfChannels(); + int numberOfChannels = getNumberOfChannels(); + + // TODO: consider adding ability to enforce discrete summing (if/when it will be useful). + if (numberOfSourceChannels < numberOfChannels) { + sumByDownMixing(source); + return; + } + + if (numberOfSourceChannels > numberOfChannels) { + sumByUpMixing(source); + return; + } + + for (int i = 0; i < numberOfChannels_; i++) { + channels_[i]->sum(source.getChannel(i)); + } +}; + +void AudioBus::discreteSum(const AudioBus &source) { + 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)); + } +}; + +void AudioBus::sumByUpMixing(const AudioBus &source) { + 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); + + getChannelByType(ChannelLeft)->sum(sourceChannel); + getChannelByType(ChannelRight)->sum(sourceChannel); + return; + } + + // Mono to 5.1 (1 -> 6) + if (numberOfSourceChannels == 1 && numberOfChannels == 6) { + AudioArray* sourceChannel = source.getChannel(0); + + getChannelByType(ChannelCenter)->sum(sourceChannel); + 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)); + getChannelByType(ChannelRight)->sum(source.getChannelByType(ChannelRight)); + return; + } + + // Stereo 4 to 5.1 (4 -> 6) + if (numberOfSourceChannels == 4 && numberOfChannels == 6) { + getChannelByType(ChannelLeft)->sum(source.getChannelByType(ChannelLeft)); + getChannelByType(ChannelRight)->sum(source.getChannelByType(ChannelRight)); + getChannelByType(ChannelSurroundLeft)->sum(source.getChannelByType(ChannelSurroundLeft)); + getChannelByType(ChannelSurroundRight)->sum(source.getChannelByType(ChannelSurroundRight)); + return; + } + + discreteSum(source); +}; + +void AudioBus::sumByDownMixing(const AudioBus &source) { + 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(); + + float* destinationData = getChannelByType(ChannelMono)->getData(); + + VectorMath::multiplyByScalarThenAddToOutput(sourceLeft, 0.5f, destinationData, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceRight, 0.5f, destinationData, getSize()); + return; + } + + // Stereo 4 to mono: 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(); + + VectorMath::multiplyByScalarThenAddToOutput(sourceLeft, 0.25f, destinationData, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceRight, 0.25f, destinationData, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceSurroundLeft, 0.25f, destinationData, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceSurroundRight, 0.25f, destinationData, getSize()); + return; + } + + // 5.1 to stereo: + // output.left += input.left + sqrt(1/2) * (input.center + input.surroundLeft) + // 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(); + + VectorMath::add(sourceLeft, destinationLeft, destinationLeft, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceCenter, SQRT_HALF, destinationLeft, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceSurroundLeft, SQRT_HALF, destinationLeft, getSize()); + + VectorMath::add(sourceRight, destinationRight, destinationRight, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceCenter, SQRT_HALF, destinationRight, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceSurroundRight, SQRT_HALF, destinationRight, getSize()); + return; + } + + // 5.1 to stereo 4: + // output.left += input.left + sqrt(1/2) * input.center + // output.right += input.right + sqrt(1/2) * input.center + // 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(); + + VectorMath::add(sourceLeft, destinationLeft, destinationLeft, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceCenter, SQRT_HALF, destinationLeft, getSize()); + + VectorMath::add(sourceRight, destinationRight, destinationRight, getSize()); + VectorMath::multiplyByScalarThenAddToOutput(sourceCenter, SQRT_HALF, destinationRight, getSize()); + + VectorMath::add(sourceSurroundLeft, destinationSurroundLeft, destinationSurroundLeft, getSize()); + VectorMath::add(sourceSurroundRight, destinationSurroundRight, destinationSurroundRight, getSize()); + return; + } + + discreteSum(source); +}; + +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioBus.h b/packages/react-native-audio-api/common/cpp/core/AudioBus.h new file mode 100644 index 00000000..1c74b179 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/core/AudioBus.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +namespace audioapi { + +class BaseAudioContext; +class AudioArray; + +class AudioBus { + public: + enum { + ChannelMono = 0, + ChannelLeft = 0, + ChannelRight = 1, + ChannelCenter = 2, + ChannelLFE = 3, + ChannelSurroundLeft = 4, + ChannelSurroundRight = 5, + }; + + explicit AudioBus(BaseAudioContext *context); + explicit AudioBus(BaseAudioContext *context, int numberOfChannels); + explicit AudioBus(BaseAudioContext *context, int numberOfChannels, int size); + ~AudioBus(); + + [[nodiscard]] int getNumberOfChannels() const; + [[nodiscard]] int getSampleRate() const; + [[nodiscard]] int getSize() const; + AudioArray* getChannel(int index) const; + AudioArray* getChannelByType(int channelType) const; + + void zero(); + void normalize(); + void scale(float value); + float maxAbsValue() const; + + void copy(const AudioBus &source); + void sum(const AudioBus &source); + + private: + BaseAudioContext *context_; + std::vector> channels_; + + + int numberOfChannels_; + int sampleRate_; + int size_; + + void createChannels(); + void discreteSum(const AudioBus &source); + void sumByUpMixing(const AudioBus &source); + void sumByDownMixing(const AudioBus &source); +}; + +} diff --git a/packages/react-native-audio-api/common/cpp/core/AudioChannel.cpp b/packages/react-native-audio-api/common/cpp/core/AudioChannel.cpp deleted file mode 100644 index a0880f53..00000000 --- a/packages/react-native-audio-api/common/cpp/core/AudioChannel.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "AudioChannel.h" -#include "AudioArray.h" - -namespace audioapi { - -explicit AudioChannel::AudioChannel(int length, int sampleRate) : length_(length), sampleRate_(sampleRate) { - data_ = std::make_unique(length_); -}; - -int AudioChannel::getLength() const { - return length_; -}; - -int AudioChannel::getSampleRate() const { - return sampleRate_; -}; - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioChannel.h b/packages/react-native-audio-api/common/cpp/core/AudioChannel.h deleted file mode 100644 index 78054fc5..00000000 --- a/packages/react-native-audio-api/common/cpp/core/AudioChannel.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include "AudioArray.h" - -namespace audioapi { - -class AudioChannel { - public: - explicit AudioChannel(int length, int sampleRate); - - [[nodiscard]] int getLength() const; - [[nodiscard]] int getSampleRate() const; - - void zero(); - void copy(const AudioChannel &source); - - float& operator[](int index); - const float& operator[](int index) const; - - - float getMaxAbsValue() const; - - void normalize(); - void scale(float scale); - void sum(const AudioChannel &source); - - - private: - - int length_; - int sampleRate_; - - std::unique_ptr data_; -}; - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioNode.h b/packages/react-native-audio-api/common/cpp/core/AudioNode.h index 619080ef..d96210cb 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioNode.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioNode.h @@ -4,13 +4,13 @@ #include #include #include "Constants.h" -#include "AudioNodeBuffer.h" // channelCount always equal to 2 namespace audioapi { class BaseAudioContext; +class AudioBus; class AudioNode : public std::enable_shared_from_this { public: @@ -58,7 +58,7 @@ class AudioNode : public std::enable_shared_from_this { protected: BaseAudioContext *context_; - std::unique_ptr buffer_; + std::unique_ptr buffer_; int numberOfInputs_ = 1; int numberOfOutputs_ = 1; diff --git a/packages/react-native-audio-api/common/cpp/core/AudioNodeBuffer.h b/packages/react-native-audio-api/common/cpp/core/AudioNodeBuffer.h deleted file mode 100644 index 831ccd81..00000000 --- a/packages/react-native-audio-api/common/cpp/core/AudioNodeBuffer.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -// namespace audioapi { - -// class AudioNodeBuffer { -// public: -// explicit AudioNodeBuffer(BaseAudioContext * context, int channelCount); - -// void zero(); -// void normalize(); -// void scale(float scale); - -// void sumFrom(const AudioNodeBuffer &source); -// void copyFrom(const AudioNodeBuffer &source); - -// float maxAbsValue() const; - -// private: -// BaseAudioContext *context_; -// std::unique_ptr buffer_; - -// int bufferSizeInFrames_; -// int channelCount_; -// }; - -// } diff --git a/packages/react-native-audio-api/common/cpp/utils/VectorMath.cpp b/packages/react-native-audio-api/common/cpp/utils/VectorMath.cpp index 8eeb2ea1..9e2692e4 100644 --- a/packages/react-native-audio-api/common/cpp/utils/VectorMath.cpp +++ b/packages/react-native-audio-api/common/cpp/utils/VectorMath.cpp @@ -115,6 +115,11 @@ float maximumMagnitude( return maximumValue; } +void multiplyByScalarThenAddToOutput(const float* inputVector, float scalar, float* outputVector, size_t numberOfElementsToProcess) +{ + vDSP_vsma(inputVector, 1, &scalar, outputVector, 1, outputVector, 1, numberOfElementsToProcess); +} + #else #if defined(HAVE_X86_SSE2) @@ -578,7 +583,7 @@ float maximumMagnitude( max = std::max(max, groupMaxP[3]); n = tailFrames; -#elif defined(HAVE_ARM_NEON_INTRINSICS) +#elif defined(c) size_t tailFrames = n % 4; const float *endP = inputVector + n - tailFrames; @@ -605,5 +610,71 @@ float maximumMagnitude( return max; } +void multiplyByScalarThenAddToOutput(const float* inputVector, float scalar, float* outputVector, size_t numberOfElementsToProcess) +{ + size_t n = numberOfElementsToProcess; + +#if HAVE_X86_SSE2 + // If the inputVector address is not 16-byte aligned, the first several frames (at most three) should be processed separately. + while (!is16ByteAligned(inputVector) && n) { + *outputVector += scalar * *inputVector; + inputVector++; + outputVector++; + n--; + } + + // Now the inputVector is aligned, use SSE. + size_t tailFrames = n % 4; + const float* endP = outputVector + n - tailFrames; + + __m128 pSource; + __m128 dest; + __m128 temp; + __m128 mScale = _mm_set_ps1(scalar); + + bool destAligned = is16ByteAligned(outputVector); + +#define SSE2_MULT_ADD(loadInstr, storeInstr) \ + while (outputVector < endP) \ + { \ + pSource = _mm_load_ps(inputVector); \ + temp = _mm_mul_ps(pSource, mScale); \ + dest = _mm_##loadInstr##_ps(outputVector); \ + dest = _mm_add_ps(dest, temp); \ + _mm_##storeInstr##_ps(outputVector, dest); \ + inputVector += 4; \ + outputVector += 4; \ + } + + if (destAligned) + SSE2_MULT_ADD(load, store) + else + SSE2_MULT_ADD(loadu, storeu) + + n = tailFrames; +#elif HAVE_ARM_NEON_INTRINSICS + size_t tailFrames = n % 4; + const float* endP = outputVector + n - tailFrames; + + float32x4_t k = vdupq_n_f32(scalar); + while (outputVector < endP) { + float32x4_t source = vld1q_f32(inputVector); + float32x4_t dest = vld1q_f32(outputVector); + + dest = vmlaq_f32(dest, source, k); + vst1q_f32(outputVector, dest); + + inputVector += 4; + outputVector += 4; + } + n = tailFrames; +#endif + while (n--) { + *outputVector += *inputVector * scalar; + ++inputVector; + ++outputVector; + } +} + #endif } // namespace audioapi::VectorMath diff --git a/packages/react-native-audio-api/common/cpp/utils/VectorMath.h b/packages/react-native-audio-api/common/cpp/utils/VectorMath.h index 5a6464a5..c29f58f8 100644 --- a/packages/react-native-audio-api/common/cpp/utils/VectorMath.h +++ b/packages/react-native-audio-api/common/cpp/utils/VectorMath.h @@ -32,6 +32,12 @@ namespace audioapi::VectorMath { +void multiplyByScalarThenAddToOutput( + const float *inputVector, + float scalar, + float *outputVector, + size_t numberOfElementsToProcess); + void multiplyByScalar( const float *inputVector, float scalar,