From e9107839cd9919d1795fd6405170862cfb7368ff Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Thu, 24 Oct 2024 11:07:51 +0200 Subject: [PATCH 1/3] feat: implemented copyFromChannel and copyToChannel --- .../cpp/HostObjects/AudioBufferHostObject.cpp | 65 +++++++++++++++++++ .../common/cpp/core/AudioBuffer.cpp | 39 +++++++++++ .../common/cpp/core/AudioBuffer.h | 10 +++ .../cpp/wrappers/AudioBufferWrapper.cpp | 18 +++++ .../common/cpp/wrappers/AudioBufferWrapper.h | 10 +++ packages/react-native-audio-api/src/types.ts | 12 +++- 6 files changed, 153 insertions(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp index 5e445b3c..7920f162 100644 --- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp @@ -18,6 +18,9 @@ std::vector AudioBufferHostObject::getPropertyNames( jsi::PropNameID::forAscii(runtime, "numberOfChannels")); propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "getChannelData")); propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "setChannelData")); + propertyNames.push_back( + jsi::PropNameID::forAscii(runtime, "copyFromChannel")); + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "copyToChannel")); return propertyNames; } @@ -89,6 +92,68 @@ jsi::Value AudioBufferHostObject::get( }); } + if (propName == "copyFromChannel") { + return jsi::Function::createFromHostFunction( + runtime, + propNameId, + 3, + [this]( + jsi::Runtime &rt, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) -> jsi::Value { + auto destination = args[0].getObject(rt).asArray(rt); + auto destinationLength = static_cast( + destination.getProperty(rt, "length").asNumber()); + auto channelNumber = static_cast(args[1].getNumber()); + auto startInChannel = static_cast(args[2].getNumber()); + + auto *destinationData = new float[destinationLength]; + + wrapper_->copyFromChannel( + destinationData, + destinationLength, + channelNumber, + startInChannel); + + for (int i = 0; i < destinationLength; i++) { + destination.setValueAtIndex(rt, i, jsi::Value(destinationData[i])); + } + + return jsi::Value::undefined(); + }); + } + + if (propName == "copyToChannel") { + return jsi::Function::createFromHostFunction( + runtime, + propNameId, + 3, + [this]( + jsi::Runtime &rt, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) -> jsi::Value { + auto source = args[0].getObject(rt).asArray(rt); + auto sourceLength = + static_cast(source.getProperty(rt, "length").asNumber()); + auto channelNumber = static_cast(args[1].getNumber()); + auto startInChannel = static_cast(args[2].getNumber()); + + auto *sourceData = new float[sourceLength]; + + for (int i = 0; i < sourceLength; i++) { + sourceData[i] = + static_cast(source.getValueAtIndex(rt, i).getNumber()); + } + + wrapper_->copyToChannel( + sourceData, sourceLength, channelNumber, startInChannel); + + return jsi::Value::undefined(); + }); + } + throw std::runtime_error("Not yet implemented!"); } 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 cab6607e..f9696f4a 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp @@ -85,4 +85,43 @@ std::shared_ptr AudioBuffer::mix(int outputNumberOfChannels) { return mixedBuffer; } + +void AudioBuffer::copyFromChannel( + float *destination, + int destinationLength, + int channelNumber, + int startInChannel) const { + if (channelNumber < 0 || channelNumber >= numberOfChannels_) { + throw std::invalid_argument("Invalid channel number"); + } + + if (startInChannel < 0 || startInChannel >= length_) { + throw std::invalid_argument("Invalid start in channel"); + } + + 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) { + if (channelNumber < 0 || channelNumber >= numberOfChannels_) { + throw std::invalid_argument("Invalid channel number"); + } + + if (startInChannel < 0 || startInChannel >= length_) { + throw std::invalid_argument("Invalid start in channel"); + } + + std::copy( + source, + source + std::min(sourceLength, length_ - startInChannel), + channels_[channelNumber] + startInChannel); +} } // namespace audioapi 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 ba2e5514..bf691754 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h @@ -17,6 +17,16 @@ class AudioBuffer : public std::enable_shared_from_this { double getDuration() const; float *getChannelData(int channel) const; void setChannelData(int channel, const float *data, int length); + void copyFromChannel( + float *destination, + int destinationLength, + int channelNumber, + int startInChannel) const; + void copyToChannel( + const float *source, + int sourceLength, + int channelNumber, + int startInChannel); private: friend class AudioBufferSourceNode; diff --git a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp index 5869d887..caa492cd 100644 --- a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp +++ b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp @@ -30,4 +30,22 @@ void AudioBufferWrapper::setChannelData(int channel, float *data, int length) const { audioBuffer_->setChannelData(channel, data, length); } + +void AudioBufferWrapper::copyFromChannel( + float *destination, + int destinationLength, + int channelNumber, + int startInChannel) const { + audioBuffer_->copyFromChannel( + destination, destinationLength, channelNumber, startInChannel); +} + +void AudioBufferWrapper::copyToChannel( + float *source, + int sourceLength, + int channelNumber, + int startInChannel) const { + audioBuffer_->copyToChannel( + source, sourceLength, channelNumber, startInChannel); +} } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h index 7b08b0a0..fe20c25f 100644 --- a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h +++ b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h @@ -17,5 +17,15 @@ class AudioBufferWrapper { int getSampleRate() const; float *getChannelData(int channel) const; void setChannelData(int channel, float *data, int length) const; + void copyFromChannel( + float *destination, + int destinationLength, + int channelNumber, + int startInChannel = 0) const; + void copyToChannel( + float *source, + int sourceLength, + int channelNumber, + int startInChannel = 0) const; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 463994a8..4b4acdcc 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -82,7 +82,7 @@ export interface BiquadFilterNode extends AudioNode { type: FilterType; } -export type ContextState = 'running' | 'closed'; +export type ContextState = 'running' | 'closed' | 'suspended'; export interface AudioBuffer { readonly length: number; @@ -91,6 +91,16 @@ export interface AudioBuffer { readonly numberOfChannels: number; getChannelData(channel: number): number[]; setChannelData(channel: number, data: number[]): void; + copyFromChannel( + destination: Float32Array, + channelNumber: number, + startInChannel: number + ): void; + copyToChannel( + source: Float32Array, + channelNumber: number, + startInChannel: number + ): void; } export interface AudioBufferSourceNode extends AudioScheduledSourceNode { From ad550b6de13125fc3fe6f68078bfa011bb52c04f Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Thu, 24 Oct 2024 11:52:23 +0200 Subject: [PATCH 2/3] refactor: removed setChannelData --- .../examples/SharedUtils/soundEngines/Clap.ts | 2 +- .../cpp/HostObjects/AudioBufferHostObject.cpp | 26 ------------------- .../common/cpp/core/AudioBuffer.cpp | 16 ++---------- .../common/cpp/core/AudioBuffer.h | 1 - .../cpp/wrappers/AudioBufferWrapper.cpp | 5 ---- .../common/cpp/wrappers/AudioBufferWrapper.h | 1 - packages/react-native-audio-api/src/types.ts | 5 ++-- 7 files changed, 5 insertions(+), 51 deletions(-) diff --git a/apps/common-app/src/examples/SharedUtils/soundEngines/Clap.ts b/apps/common-app/src/examples/SharedUtils/soundEngines/Clap.ts index 1f3cc91d..77cc6940 100644 --- a/apps/common-app/src/examples/SharedUtils/soundEngines/Clap.ts +++ b/apps/common-app/src/examples/SharedUtils/soundEngines/Clap.ts @@ -33,7 +33,7 @@ class Clap implements SoundEngine { output[i] = Math.random() * 2 - 1; } - buffer.setChannelData(0, output); + buffer.copyToChannel(output, 0, 0); return buffer; } diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp index 7920f162..5272b333 100644 --- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp @@ -17,7 +17,6 @@ std::vector AudioBufferHostObject::getPropertyNames( propertyNames.push_back( jsi::PropNameID::forAscii(runtime, "numberOfChannels")); propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "getChannelData")); - propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "setChannelData")); propertyNames.push_back( jsi::PropNameID::forAscii(runtime, "copyFromChannel")); propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "copyToChannel")); @@ -67,31 +66,6 @@ jsi::Value AudioBufferHostObject::get( }); } - if (propName == "setChannelData") { - return jsi::Function::createFromHostFunction( - runtime, - propNameId, - 2, - [this]( - jsi::Runtime &rt, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count) -> jsi::Value { - int channel = static_cast(args[0].getNumber()); - auto array = args[1].getObject(rt).asArray(rt); - auto *channelData = new float[wrapper_->getLength()]; - - for (int i = 0; i < wrapper_->getLength(); i++) { - channelData[i] = - static_cast(array.getValueAtIndex(rt, i).getNumber()); - } - - wrapper_->setChannelData(channel, channelData, wrapper_->getLength()); - - return jsi::Value::undefined(); - }); - } - if (propName == "copyFromChannel") { return jsi::Function::createFromHostFunction( runtime, 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 f9696f4a..495a10bc 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.cpp @@ -46,18 +46,6 @@ float *AudioBuffer::getChannelData(int channel) const { return channels_[channel]; } -void AudioBuffer::setChannelData(int channel, const float *data, int length) { - if (channel < 0 || channel >= numberOfChannels_) { - throw std::invalid_argument("Invalid channel number"); - } - - if (length != length_) { - throw std::invalid_argument("Invalid data length"); - } - - std::copy(data, data + length, channels_[channel]); -} - std::shared_ptr AudioBuffer::mix(int outputNumberOfChannels) { if (outputNumberOfChannels != 1 && outputNumberOfChannels != 2) { throw std::invalid_argument("Invalid number of channels"); @@ -72,8 +60,8 @@ std::shared_ptr AudioBuffer::mix(int outputNumberOfChannels) { switch (this->numberOfChannels_) { case 1: - mixedBuffer->setChannelData(0, this->channels_[0], length_); - mixedBuffer->setChannelData(1, this->channels_[0], length_); + 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++) { 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 bf691754..999aa22b 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioBuffer.h @@ -16,7 +16,6 @@ class AudioBuffer : public std::enable_shared_from_this { int getSampleRate() const; double getDuration() const; float *getChannelData(int channel) const; - void setChannelData(int channel, const float *data, int length); void copyFromChannel( float *destination, int destinationLength, diff --git a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp index caa492cd..6e89c0b7 100644 --- a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp +++ b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.cpp @@ -26,11 +26,6 @@ float *AudioBufferWrapper::getChannelData(int channel) const { return audioBuffer_->getChannelData(channel); } -void AudioBufferWrapper::setChannelData(int channel, float *data, int length) - const { - audioBuffer_->setChannelData(channel, data, length); -} - void AudioBufferWrapper::copyFromChannel( float *destination, int destinationLength, diff --git a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h index fe20c25f..b4cd1e65 100644 --- a/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h +++ b/packages/react-native-audio-api/common/cpp/wrappers/AudioBufferWrapper.h @@ -16,7 +16,6 @@ class AudioBufferWrapper { double getDuration() const; int getSampleRate() const; float *getChannelData(int channel) const; - void setChannelData(int channel, float *data, int length) const; void copyFromChannel( float *destination, int destinationLength, diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 4b4acdcc..e62afbcd 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -90,14 +90,13 @@ export interface AudioBuffer { readonly sampleRate: number; readonly numberOfChannels: number; getChannelData(channel: number): number[]; - setChannelData(channel: number, data: number[]): void; copyFromChannel( - destination: Float32Array, + destination: number[], channelNumber: number, startInChannel: number ): void; copyToChannel( - source: Float32Array, + source: number[], channelNumber: number, startInChannel: number ): void; From f616a92d7011eac66cb1ddec7785ac5c41f14596 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Thu, 24 Oct 2024 12:02:56 +0200 Subject: [PATCH 3/3] docs: readme update --- docs/web-audio-coverage.md | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/docs/web-audio-coverage.md b/docs/web-audio-coverage.md index f3108cf3..681906a7 100644 --- a/docs/web-audio-coverage.md +++ b/docs/web-audio-coverage.md @@ -7,25 +7,28 @@ Some of the noticeable implementation details that are still in progress or not - Support of different number of channels (current approach in most of the audio-graph nodes assumes working with two channel audio) - Multi-input for each node and input mixing (Although specification suggests that most of the nodes can cave only one input or output, common use-cases proves otherwise). Only node that mixes multiple inputs is `DestinationNode`. -## ✅ Completed (**5** out of 33) +## ✅ Completed (**6** out of 33)
- AudioScheduledSourceNode + AudioBuffer
AudioDestinationNode
- GainNode + AudioNode
- StereoPannerNode + AudioScheduledSourceNode
- AudioNode + GainNode +
+
+ StereoPannerNode
-## 🚧 In Progress (**7** out of 33) +## 🚧 In Progress (**6** out of 33)
AudioContext @@ -49,27 +52,6 @@ Some of the noticeable implementation details that are still in progress or not
-
- AudioBuffer - -
- -| Property 🔹/ Method 🔘 | state | -| ---------------------- | ----- | -| 🔹 sampleRate | ✅ | -| 🔹 length | ✅ | -| 🔹 duration | ✅ | -| 🔹 numberOfChannels | ✅ | -| 🔘 getChannelData | ✅ | -| 🔘 getChannelData | ✅ | -| 🔘 setChannelData | ✅ | -| 🔘 copyFromChannel | ❌ | -| 🔘 copyToChannel | ❌ | - -
- -
-
AudioBufferSourceNode