diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AnalyserNodeHostObject.h b/packages/react-native-audio-api/common/cpp/HostObjects/AnalyserNodeHostObject.h new file mode 100644 index 00000000..99c25364 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/HostObjects/AnalyserNodeHostObject.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include + +#include "AnalyserNodeHostObject.h" +#include "AudioNodeHostObject.h" + +namespace audioapi { +using namespace facebook; + +class AnalyserNodeHostObject : public AudioNodeHostObject { + public: + explicit AnalyserNodeHostObject(const std::shared_ptr &node) + : AudioNodeHostObject(node) { + 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)); + ); + + addFunctions( + JSI_EXPORT_FUNCTION( + AnalyserNodeHostObject, getFloatFrequencyData), + JSI_EXPORT_FUNCTION( + AnalyserNodeHostObject, getByteFrequencyData), + JSI_EXPORT_FUNCTION( + AnalyserNodeHostObject, getFloatTimeDomainData), + JSI_EXPORT_FUNCTION( + AnalyserNodeHostObject, getByteTimeDomainData)); + ); + + addSetters( + JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, fftSize), + JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, minDecibels), + JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, maxDecibels), + JSI_EXPORT_PROPERTY_SETTER( + AnalyserNodeHostObject, smoothingTimeConstant)); + ); + } + + JSI_PROPERTY_GETTER(fftSize) { + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getFftSize()}; + } + + JSI_PROPERTY_GETTER(frequencyBinCount) { + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getFrequencyBinCount()}; + } + + JSI_PROPERTY_GETTER(minDecibels) { + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getMinDecibels()}; + } + + JSI_PROPERTY_GETTER(maxDecibels) { + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getMaxDecibels()}; + } + + JSI_PROPERTY_GETTER(smoothingTimeConstant) { + auto analyserNode = std::static_pointer_cast(node_); + return {analyserNode->getSmoothingTimeConstant()}; + } + + JSI_HOST_FUNCTION(getFloatFrequencyData) { + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(getByteFrequencyData) { + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(getFloatTimeDomainData) { + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(getByteTimeDomainData) { + return jsi::Value::undefined(); + } + + JSI_PROPERTY_SETTER(fftSize) { + auto analyserNode = std::static_pointer_cast(node_); + analyserNode->setFftSize(args[0].getNumber()); + } + + JSI_PROPERTY_SETTER(minDecibels) { + auto analyserNode = std::static_pointer_cast(node_); + analyserNode->setMinDecibels(args[0].getNumber()); + } + + JSI_PROPERTY_SETTER(maxDecibels) { + auto analyserNode = std::static_pointer_cast(node_); + analyserNode->setMaxDecibels(args[0].getNumber()); + } + + JSI_PROPERTY_SETTER(smoothingTimeConstant) { + auto analyserNode = std::static_pointer_cast(node_); + analyserNode->setSmoothingTimeConstant(args[0].getNumber()); + } +}; +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AnalyserNode.cpp b/packages/react-native-audio-api/common/cpp/core/AnalyserNode.cpp new file mode 100644 index 00000000..5bb7beda --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/core/AnalyserNode.cpp @@ -0,0 +1,75 @@ +#include "AnalyserNode.h" +#include "AudioArray.h" +#include "AudioBus.h" +#include "BaseAudioContext.h" + +namespace audioapi { +AnalyserNode::AnalyserNode(audioapi::BaseAudioContext *context) + : AudioNode(context) { + channelCount_ = 1; + fftSize_ = 2048; + minDecibels_ = -100; + maxDecibels_ = -30; + smoothingTimeConstant_ = 0.8; + inputBus_ = + std::make_unique(context->getSampleRate(), MAX_FFT_SIZE * 2, 1); +} + +int AnalyserNode::getFftSize() const { + return fftSize_; +} + +int AnalyserNode::getFrequencyBinCount() const { + return fftSize_ / 2; +} + +double AnalyserNode::getMinDecibels() const { + return minDecibels_; +} + +double AnalyserNode::getMaxDecibels() const { + return maxDecibels_; +} + +double AnalyserNode::getSmoothingTimeConstant() const { + return smoothingTimeConstant_; +} + +void AnalyserNode::setFftSize(int fftSize) { + fftSize_ = fftSize; +} + +void AnalyserNode::setMinDecibels(double minDecibels) { + minDecibels_ = minDecibels; +} + +void AnalyserNode::setMaxDecibels(double maxDecibels) { + maxDecibels_ = maxDecibels; +} + +void AnalyserNode::setSmoothingTimeConstant(double smoothingTimeConstant) { + smoothingTimeConstant_ = smoothingTimeConstant; +} + +float *AnalyserNode::getFloatFrequencyData() { + return nullptr; +} + +uint8_t *AnalyserNode::getByteFrequencyData() { + return nullptr; +} + +float *AnalyserNode::getFloatTimeDomainData() { + return nullptr; +} + +uint8_t *AnalyserNode::getByteTimeDomainData() { + return nullptr; +} + +void AnalyserNode::processNode( + audioapi::AudioBus *processingBus, + int framesToProcess) { + // m_analyser.writeInput(inputBus, framesToProcess); +} +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AnalyserNode.h b/packages/react-native-audio-api/common/cpp/core/AnalyserNode.h new file mode 100644 index 00000000..b3adc4bf --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/core/AnalyserNode.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "AudioNode.h" + +namespace audioapi { + +class AudioBus; + +class AnalyserNode : public AudioNode { + public: + explicit AnalyserNode(BaseAudioContext *context); + + int getFftSize() const; + int getFrequencyBinCount() const; + double getMinDecibels() const; + double getMaxDecibels() const; + + double getSmoothingTimeConstant() const; + void setFftSize(int fftSize); + void setMinDecibels(double minDecibels); + void setMaxDecibels(double maxDecibels); + void setSmoothingTimeConstant(double smoothingTimeConstant); + + float *getFloatFrequencyData(); + uint8_t *getByteFrequencyData(); + float *getFloatTimeDomainData(); + uint8_t *getByteTimeDomainData(); + + protected: + void processNode(AudioBus *processingBus, int framesToProcess) override; + + private: + int fftSize_; + double minDecibels_; + double maxDecibels_; + double smoothingTimeConstant_; + + std::unique_ptr inputBus_; +}; + +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioNode.cpp b/packages/react-native-audio-api/common/cpp/core/AudioNode.cpp index 19a204ed..abdde5bd 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioNode.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioNode.cpp @@ -126,7 +126,7 @@ AudioBus *AudioNode::processAudio(AudioBus *outputBus, int framesToProcess) { // - it has more than one output, so each output node can get the processed // data without re-calculating the node. bool canUseOutputBus = - outputBus != 0 && inputNodes_.size() < 2 && outputNodes_.size() < 2; + outputBus != nullptr && inputNodes_.size() < 2 && outputNodes_.size() < 2; if (isAlreadyProcessed) { // If it was already processed in the rendering quantum, return it. @@ -214,7 +214,7 @@ void AudioNode::onInputDisconnected(AudioNode *node) { inputNodes_.erase(position); } - if (inputNodes_.size() > 0) { + if (!inputNodes_.empty()) { return; } @@ -222,7 +222,7 @@ void AudioNode::onInputDisconnected(AudioNode *node) { node->onInputDisabled(); } - for (auto outputNode : outputNodes_) { + for (const auto &outputNode : outputNodes_) { disconnectNode(outputNode); } } diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/Constants.h b/packages/react-native-audio-api/common/cpp/core/Constants.h similarity index 92% rename from packages/react-native-audio-api/common/cpp/HostObjects/Constants.h rename to packages/react-native-audio-api/common/cpp/core/Constants.h index 8b187578..052bfcf9 100644 --- a/packages/react-native-audio-api/common/cpp/HostObjects/Constants.h +++ b/packages/react-native-audio-api/common/cpp/core/Constants.h @@ -21,4 +21,7 @@ constexpr float MAX_FILTER_FREQUENCY = NYQUIST_FREQUENCY; constexpr float MIN_FILTER_FREQUENCY = 0.0; static float MAX_FILTER_GAIN = 40 * std::log10(MOST_POSITIVE_SINGLE_FLOAT); constexpr float MIN_FILTER_GAIN = -MAX_GAIN; + +constexpr int MAX_FFT_SIZE = 32768; +constexpr int MIN_FFT_SIZE = 32; } // namespace audioapi diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts new file mode 100644 index 00000000..4f585d69 --- /dev/null +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -0,0 +1,37 @@ +import { IAnalyserNode } from '../interfaces'; +import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; + +export default class AnalyserNode extends AudioNode { + fftSize: number; + readonly frequencyBinCount: number; + minDecibels: number; + maxDecibels: number; + smoothingTimeConstant: number; + + constructor(context: BaseAudioContext, analyser: IAnalyserNode) { + super(context, analyser); + + this.fftSize = analyser.fftSize; + this.frequencyBinCount = analyser.frequencyBinCount; + this.minDecibels = analyser.minDecibels; + this.maxDecibels = analyser.maxDecibels; + this.smoothingTimeConstant = analyser.smoothingTimeConstant; + } + + getFloatFrequencyData(array: number[]): void { + (this.node as IAnalyserNode).getFloatFrequencyData(array); + } + + getByteFrequencyData(array: number[]): void { + (this.node as IAnalyserNode).getByteFrequencyData(array); + } + + getFloatTimeDomainData(array: number[]): void { + (this.node as IAnalyserNode).getFloatTimeDomainData(array); + } + + getByteTimeDomainData(array: number[]): void { + (this.node as IAnalyserNode).getByteTimeDomainData(array); + } +} diff --git a/packages/react-native-audio-api/src/index.native.ts b/packages/react-native-audio-api/src/index.native.ts index e276dfde..ac281138 100644 --- a/packages/react-native-audio-api/src/index.native.ts +++ b/packages/react-native-audio-api/src/index.native.ts @@ -9,6 +9,7 @@ export { default as AudioBufferSourceNode } from './core/AudioBufferSourceNode'; export { default as AudioContext } from './core/AudioContext'; export { default as AudioDestinationNode } from './core/AudioDestinationNode'; export { default as AudioNode } from './core/AudioNode'; +export { default as AnalyserNode } from './core/AnalyserNode'; export { default as AudioParam } from './core/AudioParam'; export { default as AudioScheduledSourceNode } from './core/AudioScheduledSourceNode'; export { default as BaseAudioContext } from './core/BaseAudioContext'; diff --git a/packages/react-native-audio-api/src/index.ts b/packages/react-native-audio-api/src/index.ts index f9dfe049..1531f7a8 100644 --- a/packages/react-native-audio-api/src/index.ts +++ b/packages/react-native-audio-api/src/index.ts @@ -110,6 +110,40 @@ export class AudioNode { } } +export class AnalyserNode extends AudioNode { + readonly fftSize: number; + readonly frequencyBinCount: number; + readonly minDecibels: number; + readonly maxDecibels: number; + readonly smoothingTimeConstant: number; + + constructor(context: AudioContext, node: globalThis.AnalyserNode) { + super(context, node); + + this.fftSize = node.fftSize; + this.frequencyBinCount = node.frequencyBinCount; + this.minDecibels = node.minDecibels; + this.maxDecibels = node.maxDecibels; + this.smoothingTimeConstant = node.smoothingTimeConstant; + } + + public getByteFrequencyData(array: Uint8Array): void { + (this.node as globalThis.AnalyserNode).getByteFrequencyData(array); + } + + public getByteTimeDomainData(array: Uint8Array): void { + (this.node as globalThis.AnalyserNode).getByteTimeDomainData(array); + } + + public getFloatFrequencyData(array: Float32Array): void { + (this.node as globalThis.AnalyserNode).getFloatFrequencyData(array); + } + + public getFloatTimeDomainData(array: Float32Array): void { + (this.node as globalThis.AnalyserNode).getFloatTimeDomainData(array); + } +} + export class AudioScheduledSourceNode extends AudioNode { private hasBeenStarted: boolean = false; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 58a7637c..c6b09e0c 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -136,3 +136,16 @@ export interface IAudioParam { } export interface IPeriodicWave {} + +export interface IAnalyserNode extends IAudioNode { + fftSize: number; + readonly frequencyBinCount: number; + minDecibels: number; + maxDecibels: number; + smoothingTimeConstant: number; + + getFloatFrequencyData: (array: number[]) => void; + getByteFrequencyData: (array: number[]) => void; + getFloatTimeDomainData: (array: number[]) => void; + getByteTimeDomainData: (array: number[]) => void; +}