diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000..66185151 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,156 @@ +name: Bug report +description: Report an issue with React Native Audio API here. +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Before you proceed: + + - Make sure to check whether there are similar issues in the repository + - Make sure to clean cache in your project. Depending on your setup this could be done by: + - `yarn start --reset-cache` or + - `npm start -- --reset-cache` or + - `expo start --clear` + + - type: markdown + attributes: + value: | + ## Required information + - type: textarea + id: description + attributes: + label: Description + description: Please provide a clear, concise and descriptive explanation of what the bug is. Include screenshots or a video if needed. Tell us what were you expecting to happen instead of what is happening now. + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to reproduce + description: Provide a detailed list of steps that reproduce the issue. + placeholder: | + 1. + 2. + 3. + validations: + required: true + + - type: input + id: repro + attributes: + label: Snack or a link to a repository + description: | + Please provide a Snack (https://snack.expo.io/) or a link to a repository on GitHub under your username that reproduces the issue. + Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve. + Issues without a reproduction are likely to stale. + placeholder: Link to a Snack or a GitHub repository + validations: + required: true + + - type: input + id: react-native-audio-api-version + attributes: + label: React Native Audio API version + description: What version of react-native-audio-api are you using? + placeholder: 0.1.0 + validations: + required: true + + - type: input + id: react-native-version + attributes: + label: React Native version + description: What version of react-native are you using? + placeholder: 0.75.3 + validations: + required: true + + - type: dropdown + id: platforms + attributes: + label: Platforms + description: On what platform your application is running on? + multiple: true + options: + - Android + - iOS + validations: + required: true + + - type: markdown + attributes: + value: | + ## Additonal information + + Providing as much information as possible greatly helps us with reproducting the issues. + + - type: dropdown + id: runtime + attributes: + label: JavaScript runtime + description: What runtime is your application using? + options: + - JSC + - Hermes + - V8 + + - type: dropdown + id: workflow + attributes: + label: Workflow + description: How is your application managed? + options: + - React Native + - Expo Go + - Expo Dev Client + + - type: dropdown + id: architecture + attributes: + label: Architecture + description: What React Native architecture your application is running on? Currently, the default architecture on React Native is Paper so if you haven't changed it in your application select this option. + options: + - Paper (Old Architecture) + - Fabric (New Architecture) + + - type: dropdown + id: build-type + attributes: + label: Build type + description: What is the build configuration/variant of your native app and JavaScript bundle mode? + options: + - Debug app & dev bundle + - Release app & production bundle + - Debug app & production bundle + - Release app & dev bundle + - Other (please specify) + + - type: dropdown + id: emulator + attributes: + label: Device + description: How are you running your application? + options: + - iOS simulator + - Android emulator + - Real device + + - type: input + id: device-model + attributes: + label: Device model + description: What device you are experiencing this problem on? Specify full device name along with the version of the operating system it's running. + placeholder: ex. Samsung Galaxy A22 (Android 12) + + - type: dropdown + id: acknowledgements + attributes: + label: Acknowledgements + description: I searched for similar issues in the repository. + options: + - 'Yes' + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 00000000..e4c2339f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,10 @@ +name: Feature request +description: Suggest a feature +body: + - type: textarea + id: Suggestion + attributes: + label: Feature Request + description: Describe the feature(s) you would like to be added. + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..1fc1de0d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,18 @@ + + +Closes # + +## Introduced changes + + + +- + +## Checklist + + + +- [ ] Linked relevant issue +- [ ] Updated relevant documentation +- [ ] Added/Conducted relevant tests +- [ ] Performed self-review of the code 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/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 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..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,9 @@ 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")); return propertyNames; } @@ -64,26 +66,63 @@ jsi::Value AudioBufferHostObject::get( }); } - if (propName == "setChannelData") { + if (propName == "copyFromChannel") { return jsi::Function::createFromHostFunction( runtime, propNameId, - 2, + 3, [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()]; + 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])); + } - for (int i = 0; i < wrapper_->getLength(); i++) { - channelData[i] = - static_cast(array.getValueAtIndex(rt, i).getNumber()); + 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_->setChannelData(channel, channelData, wrapper_->getLength()); + wrapper_->copyToChannel( + sourceData, sourceLength, channelNumber, startInChannel); return jsi::Value::undefined(); }); 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..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++) { @@ -85,4 +73,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..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,16 @@ 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, + 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..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,8 +26,21 @@ 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, + 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..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,6 +16,15 @@ 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, + 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/core/AudioBuffer.ts b/packages/react-native-audio-api/src/core/AudioBuffer.ts index ade210f6..713ead88 100644 --- a/packages/react-native-audio-api/src/core/AudioBuffer.ts +++ b/packages/react-native-audio-api/src/core/AudioBuffer.ts @@ -20,7 +20,19 @@ export default class AudioBuffer { return this.buffer.getChannelData(channel); } - public setChannelData(channel: number, data: number[]): void { - this.buffer.setChannelData(channel, data); + public copyFromChannel( + destination: number[], + channelNumber: number, + startInChannel: number + ): void { + this.buffer.copyFromChannel(destination, channelNumber, startInChannel); + } + + public copyToChannel( + source: number[], + channelNumber: number, + startInChannel: number + ): void { + this.buffer.copyToChannel(source, channelNumber, startInChannel); } } diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 96d965db..b0d261f7 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -78,7 +78,16 @@ export interface IAudioBuffer { readonly sampleRate: number; readonly numberOfChannels: number; getChannelData(channel: number): number[]; - setChannelData(channel: number, data: number[]): void; + copyFromChannel( + destination: number[], + channelNumber: number, + startInChannel: number + ): void; + copyToChannel( + source: number[], + channelNumber: number, + startInChannel: number + ): void; } export interface IAudioParam {