diff --git a/apps/common-app/src/examples/AudioVisualizer/AudioVisualizer.tsx b/apps/common-app/src/examples/AudioVisualizer/AudioVisualizer.tsx index 638239b00..9a11f9af2 100644 --- a/apps/common-app/src/examples/AudioVisualizer/AudioVisualizer.tsx +++ b/apps/common-app/src/examples/AudioVisualizer/AudioVisualizer.tsx @@ -105,10 +105,7 @@ const AudioVisualizer: React.FC = () => { } if (!analyserRef.current) { - analyserRef.current = audioContextRef.current.createAnalyser(); - analyserRef.current.fftSize = FFT_SIZE; - analyserRef.current.smoothingTimeConstant = 0.2; - + analyserRef.current = new AnalyserNode(audioContextRef.current, { fftSize: FFT_SIZE, smoothingTimeConstant: 0.2 }); analyserRef.current.connect(audioContextRef.current.destination); } diff --git a/packages/audiodocs/docs/core/audio-node.mdx b/packages/audiodocs/docs/core/audio-node.mdx index 26aa21223..b6db096bc 100644 --- a/packages/audiodocs/docs/core/audio-node.mdx +++ b/packages/audiodocs/docs/core/audio-node.mdx @@ -111,6 +111,18 @@ If no arguments provided node disconnects from all outgoing connections. #### Returns `undefined`. +### `AudioNodeOptions` + +It is used to constructing majority of all `AudioNodes`. + +| Parameter | Type | Default | Description | +| :---: | :---: | :----: | :---- | +| `channelCount` | `number` | 2 | Indicates number of channels used in mixing of node. | +| `channelCountMode` | [`ChannelCountMode`](/docs/types/channel-count-mode) | `max` | Determines how the number of input channels affects the number of output channels in an audio node. | +| `channelInterpretation` | [`ChannelInterpretation`](/docs/types/channel-interpretation) | `speakers` | Specifies how input channels are mapped out to output channels when the number of them are different. | + +If any of these values are not provided, default values are used. + ## Remarks #### `numberOfInputs` diff --git a/packages/audiodocs/docs/effects/gain-node.mdx b/packages/audiodocs/docs/effects/gain-node.mdx index 42fb43d57..adfa4eb19 100644 --- a/packages/audiodocs/docs/effects/gain-node.mdx +++ b/packages/audiodocs/docs/effects/gain-node.mdx @@ -3,7 +3,7 @@ sidebar_position: 2 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" -import { ReadOnly } from '@site/src/components/Badges'; +import { Optional, ReadOnly } from '@site/src/components/Badges'; import { useGainAdsrPlayground } from '@site/src/components/InteractivePlayground/GainAdsrExample/useGainAdsrPlayground'; import InteractivePlayground from '@site/src/components/InteractivePlayground'; @@ -43,6 +43,19 @@ You can read more about envelopes and ADSR on [Wikipedia]( | `number` | 1.0 | Number representing gain value | + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createGain()`](/docs/core/base-audio-context#creategain) ## Properties diff --git a/packages/audiodocs/docs/effects/stereo-panner-node.mdx b/packages/audiodocs/docs/effects/stereo-panner-node.mdx index 438feff59..64438ff33 100644 --- a/packages/audiodocs/docs/effects/stereo-panner-node.mdx +++ b/packages/audiodocs/docs/effects/stereo-panner-node.mdx @@ -3,7 +3,7 @@ sidebar_position: 4 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" -import { ReadOnly } from '@site/src/components/Badges'; +import { Optional, ReadOnly } from '@site/src/components/Badges'; # StereoPannerNode @@ -15,6 +15,19 @@ The `StereoPannerNode` interface represents the change in ratio between two outp ## Constructor +```tsx +constructor(context: BaseAudioContext, stereoPannerOptions?: StereoPannerOptions) +``` + +### `StereoPannerOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | Description | +| :---: | :---: | :----: | :---- | +| `pan` | `number` | 0.0 | Number representing pan value | + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createStereoPanner()`](/docs/core/base-audio-context#createstereopanner) ## Properties diff --git a/packages/audiodocs/docs/sources/streamer-node.mdx b/packages/audiodocs/docs/sources/streamer-node.mdx index 0d22e0e62..479da9f02 100644 --- a/packages/audiodocs/docs/sources/streamer-node.mdx +++ b/packages/audiodocs/docs/sources/streamer-node.mdx @@ -45,18 +45,21 @@ function App() { ## Properties -`StreamerNode` does not define any additional properties. It inherits all properties from [`AudioScheduledSourceNode`](/docs/sources/audio-scheduled-source-node#properties). +| Name | Type | Description | +| :----: | :----: | :------- | +| `streamPath` | `string` | String value representing url to stream. | + ## Methods It inherits all methods from [`AudioScheduledSourceNode`](/docs/sources/audio-scheduled-source-node#methods). ### `initialize` -Initializes the streamer with a link to an external HLS source. +Initializes the streamer with a link to an external source. | Parameter | Type | Description | | :---: | :---: | :---- | -| `streamPath` | `string` | Link pointing to an external HLS source | +| `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/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index e61d01bc1..b2ff7449c 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 @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -153,13 +154,21 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createRecorderAdapter) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { - auto oscillator = context_->createOscillator(); + auto options = args[0].asObject(runtime); + std::shared_ptr oscillatorOptions = + audioapi::option_parser::parseOscillatorOptions(runtime, options); + auto oscillator = context_->createOscillator(oscillatorOptions); auto oscillatorHostObject = std::make_shared(oscillator); return jsi::Object::createFromHostObject(runtime, oscillatorHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { - auto streamer = context_->createStreamer(); + std::shared_ptr streamerOptions = std::make_shared(); + if (!args[0].isUndefined()) { + auto options = args[0].asObject(runtime); + streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); + } + auto streamer = context_->createStreamer(streamerOptions); auto streamerHostObject = std::make_shared(streamer); auto object = jsi::Object::createFromHostObject(runtime, streamerHostObject); object.setExternalMemoryPressure(runtime, StreamerNodeHostObject::getSizeInBytes()); @@ -167,39 +176,55 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { - auto constantSource = context_->createConstantSource(); + auto options = args[0].asObject(runtime); + std::shared_ptr constantSourceOptions = + audioapi::option_parser::parseConstantSourceOptions(runtime, options); + auto constantSource = context_->createConstantSource(constantSourceOptions); auto constantSourceHostObject = std::make_shared(constantSource); return jsi::Object::createFromHostObject(runtime, constantSourceHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { - auto gain = context_->createGain(); + auto options = args[0].asObject(runtime); + std::shared_ptr gainOptions = + audioapi::option_parser::parseGainOptions(runtime, options); + auto gain = context_->createGain(gainOptions); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { - auto stereoPanner = context_->createStereoPanner(); + auto options = args[0].asObject(runtime); + std::shared_ptr stereoPannerOptions = + audioapi::option_parser::parseStereoPannerOptions(runtime, options); + auto stereoPanner = context_->createStereoPanner(stereoPannerOptions); auto stereoPannerHostObject = std::make_shared(stereoPanner); return jsi::Object::createFromHostObject(runtime, stereoPannerHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBiquadFilter) { - auto biquadFilter = context_->createBiquadFilter(); + auto options = args[0].asObject(runtime); + std::shared_ptr biquadFilterOptions = + audioapi::option_parser::parseBiquadFilterOptions(runtime, options); + auto biquadFilter = context_->createBiquadFilter(biquadFilterOptions); auto biquadFilterHostObject = std::make_shared(biquadFilter); return jsi::Object::createFromHostObject(runtime, biquadFilterHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { - auto pitchCorrection = args[0].asBool(); - auto bufferSource = context_->createBufferSource(pitchCorrection); + auto options = args[0].asObject(runtime); + std::shared_ptr audioBufferSourceOptions = + audioapi::option_parser::parseAudioBufferSourceOptions(runtime, options); + auto bufferSource = context_->createBufferSource(audioBufferSourceOptions); auto bufferSourceHostObject = std::make_shared(bufferSource); return jsi::Object::createFromHostObject(runtime, bufferSourceHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) { - auto pitchCorrection = args[0].asBool(); - auto bufferSource = context_->createBufferQueueSource(pitchCorrection); + auto options = args[0].asObject(runtime); + std::shared_ptr baseAudioBufferSourceOptions = + audioapi::option_parser::parseBaseAudioBufferSourceOptions(runtime, options); + auto bufferSource = context_->createBufferQueueSource(baseAudioBufferSourceOptions); auto bufferStreamSourceHostObject = std::make_shared(bufferSource); return jsi::Object::createFromHostObject(runtime, bufferStreamSourceHostObject); @@ -243,20 +268,19 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createPeriodicWave) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { - auto analyser = context_->createAnalyser(); + auto options = args[0].asObject(runtime); + std::shared_ptr analyserOptions = + audioapi::option_parser::parseAnalyserOptions(runtime, options); + auto analyser = context_->createAnalyser(analyserOptions); auto analyserHostObject = std::make_shared(analyser); return jsi::Object::createFromHostObject(runtime, analyserHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) { - auto disableNormalization = args[1].getBool(); - std::shared_ptr convolver; - if (args[0].isUndefined()) { - convolver = context_->createConvolver(nullptr, disableNormalization); - } else { - auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); - convolver = context_->createConvolver(bufferHostObject->audioBuffer_, disableNormalization); - } + auto options = args[0].asObject(runtime); + std::shared_ptr convolverOptions = + audioapi::option_parser::parseConvolverOptions(runtime, options); + auto convolver = context_->createConvolver(convolverOptions); auto convolverHostObject = std::make_shared(convolver); auto jsiObject = jsi::Object::createFromHostObject(runtime, convolverHostObject); if (!args[0].isUndefined()) { 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 index bee7fc82e..6da7c85e1 100644 --- 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 @@ -10,6 +10,12 @@ namespace audioapi { StreamerNodeHostObject::StreamerNodeHostObject(const std::shared_ptr &node) : AudioScheduledSourceNodeHostObject(node) { 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) { 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 33e691679..43118bc0e 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 @@ -19,6 +19,7 @@ class StreamerNodeHostObject : public AudioScheduledSourceNodeHostObject { return SIZE; } + JSI_PROPERTY_GETTER_DECL(streamPath); JSI_HOST_FUNCTION_DECL(initialize); private: 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 new file mode 100644 index 000000000..fc05192a9 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace audioapi { +struct AudioNodeOptions { + int channelCount = 2; + ChannelCountMode channelCountMode = ChannelCountMode::MAX; + ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS; +}; + +struct GainOptions : AudioNodeOptions { + float gain = 1.0f; +}; + +struct StereoPannerOptions : AudioNodeOptions { + float pan = 0.0f; +}; + +struct ConvolverOptions : AudioNodeOptions { + std::shared_ptr bus = nullptr; + bool disableNormalization = false; +}; + +struct ConstantSourceOptions { + float offset = 1.0f; +}; + +struct AnalyserOptions : AudioNodeOptions { + int fftSize = 2048; + float minDecibels = -100.0f; + float maxDecibels = -30.0f; + float smoothingTimeConstant = 0.8f; +}; + +struct BiquadFilterOptions : AudioNodeOptions { + BiquadFilterType type = BiquadFilterType::LOWPASS; + float frequency = 350.0f; + float detune = 0.0f; + float Q = 1.0f; + float gain = 0.0f; +}; + +struct OscillatorOptions { + std::shared_ptr periodicWave = nullptr; + float frequency = 440.0f; + float detune = 0.0f; + OscillatorType type = OscillatorType::SINE; +}; + +struct BaseAudioBufferSourceOptions { + float detune = 0.0f; + bool pitchCorrection = false; + float playbackRate = 1.0f; +}; + +struct AudioBufferSourceOptions : BaseAudioBufferSourceOptions { + std::shared_ptr buffer = nullptr; + bool loop = false; + float loopStart = 0.0f; + float loopEnd = 0.0f; +}; + +struct StreamerOptions { + std::string streamPath = ""; +}; + +} // namespace audioapi 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 new file mode 100644 index 000000000..e51f47c6e --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h @@ -0,0 +1,213 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace audioapi::option_parser { +std::shared_ptr parseAudioNodeOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + AudioNodeOptions options; + + options.channelCount = + static_cast(optionsObject.getProperty(runtime, "channelCount").getNumber()); + + auto channelCountModeStr = + optionsObject.getProperty(runtime, "channelCountMode").asString(runtime).utf8(runtime); + + if (channelCountModeStr == "max") { + options.channelCountMode = ChannelCountMode::MAX; + } else if (channelCountModeStr == "clamped-max") { + options.channelCountMode = ChannelCountMode::CLAMPED_MAX; + } else if (channelCountModeStr == "explicit") { + options.channelCountMode = ChannelCountMode::EXPLICIT; + } + + auto channelInterpretationStr = + optionsObject.getProperty(runtime, "channelInterpretation").asString(runtime).utf8(runtime); + + if (channelInterpretationStr == "speakers") { + options.channelInterpretation = ChannelInterpretation::SPEAKERS; + } else if (channelInterpretationStr == "discrete") { + options.channelInterpretation = ChannelInterpretation::DISCRETE; + } + + return std::make_shared(options); +} + +std::shared_ptr parseGainOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + GainOptions options(*nodeOptions.get()); + options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseStereoPannerOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + StereoPannerOptions options(*nodeOptions.get()); + options.pan = static_cast(optionsObject.getProperty(runtime, "pan").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseConvolverOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + ConvolverOptions options(*nodeOptions.get()); + options.disableNormalization = + static_cast(optionsObject.getProperty(runtime, "disableNormalization").getNumber()); + if (optionsObject.hasProperty(runtime, "buffer")) { + auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") + .getObject(runtime) + .asHostObject(runtime); + options.bus = bufferHostObject->audioBuffer_; + } + return std::make_shared(options); +} + +std::shared_ptr parseConstantSourceOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + ConstantSourceOptions options; + options.offset = static_cast(optionsObject.getProperty(runtime, "offset").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseAnalyserOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + AnalyserOptions options(*nodeOptions.get()); + options.fftSize = static_cast(optionsObject.getProperty(runtime, "fftSize").getNumber()); + options.minDecibels = + static_cast(optionsObject.getProperty(runtime, "minDecibels").getNumber()); + options.maxDecibels = + static_cast(optionsObject.getProperty(runtime, "maxDecibels").getNumber()); + options.smoothingTimeConstant = + static_cast(optionsObject.getProperty(runtime, "smoothingTimeConstant").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseBiquadFilterOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + BiquadFilterOptions options(*nodeOptions.get()); + + auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); + + if (typeStr == "lowpass") { + options.type = BiquadFilterType::LOWPASS; + } else if (typeStr == "highpass") { + options.type = BiquadFilterType::HIGHPASS; + } else if (typeStr == "bandpass") { + options.type = BiquadFilterType::BANDPASS; + } else if (typeStr == "lowshelf") { + options.type = BiquadFilterType::LOWSHELF; + } else if (typeStr == "highshelf") { + options.type = BiquadFilterType::HIGHSHELF; + } else if (typeStr == "peaking") { + options.type = BiquadFilterType::PEAKING; + } else if (typeStr == "notch") { + options.type = BiquadFilterType::NOTCH; + } else if (typeStr == "allpass") { + options.type = BiquadFilterType::ALLPASS; + } + + options.frequency = + static_cast(optionsObject.getProperty(runtime, "frequency").getNumber()); + options.detune = static_cast(optionsObject.getProperty(runtime, "detune").getNumber()); + options.Q = static_cast(optionsObject.getProperty(runtime, "Q").getNumber()); + options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); + + return std::make_shared(options); +} + +std::shared_ptr parseOscillatorOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + OscillatorOptions options; + + auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); + + if (typeStr == "sine") { + options.type = OscillatorType::SINE; + } else if (typeStr == "square") { + options.type = OscillatorType::SQUARE; + } else if (typeStr == "sawtooth") { + options.type = OscillatorType::SAWTOOTH; + } else if (typeStr == "triangle") { + options.type = OscillatorType::TRIANGLE; + } else if (typeStr == "custom") { + options.type = OscillatorType::CUSTOM; + } + + options.frequency = + static_cast(optionsObject.getProperty(runtime, "frequency").getNumber()); + options.detune = static_cast(optionsObject.getProperty(runtime, "detune").getNumber()); + + if (optionsObject.hasProperty(runtime, "periodicWave")) { + auto periodicWaveHostObject = optionsObject.getProperty(runtime, "periodicWave") + .getObject(runtime) + .asHostObject(runtime); + options.periodicWave = periodicWaveHostObject->periodicWave_; + } + + return std::make_shared(options); +} + +std::shared_ptr parseBaseAudioBufferSourceOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + BaseAudioBufferSourceOptions options; + options.detune = static_cast(optionsObject.getProperty(runtime, "detune").getNumber()); + options.playbackRate = + static_cast(optionsObject.getProperty(runtime, "playbackRate").getNumber()); + options.pitchCorrection = + static_cast(optionsObject.getProperty(runtime, "pitchCorrection").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseAudioBufferSourceOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr baseOptions = + parseBaseAudioBufferSourceOptions(runtime, optionsObject); + AudioBufferSourceOptions options(*baseOptions.get()); + if (optionsObject.hasProperty(runtime, "buffer")) { + auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") + .getObject(runtime) + .asHostObject(runtime); + options.buffer = bufferHostObject->audioBuffer_; + } + options.loop = static_cast(optionsObject.getProperty(runtime, "loop").getNumber()); + options.loopStart = + static_cast(optionsObject.getProperty(runtime, "loopStart").getNumber()); + options.loopEnd = static_cast(optionsObject.getProperty(runtime, "loopEnd").getNumber()); + return std::make_shared(options); +} + +std::shared_ptr parseStreamerOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + auto options = StreamerOptions(); + if (optionsObject.hasProperty(runtime, "streamPath")) { + options.streamPath = + optionsObject.getProperty(runtime, "streamPath").asString(runtime).utf8(runtime); + } + return std::make_shared(options); +} +} // namespace audioapi::option_parser 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 9035ab02a..fc47670a6 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,7 +11,13 @@ namespace audioapi { -AudioNode::AudioNode(BaseAudioContext *context) : context_(context) { +AudioNode::AudioNode(BaseAudioContext *context, std::shared_ptr options) + : context_(context) { + if (options != nullptr) { + channelCount_ = options->channelCount; + channelCountMode_ = options->channelCountMode; + channelInterpretation_ = options->channelInterpretation; + } audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); } 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 3673fd81e..02d9227d8 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 @@ -16,10 +16,13 @@ namespace audioapi { class AudioBus; class BaseAudioContext; class AudioParam; +class AudioNodeOptions; class AudioNode : public std::enable_shared_from_this { public: - explicit AudioNode(BaseAudioContext *context); + explicit AudioNode( + BaseAudioContext *context, + std::shared_ptr options = nullptr); virtual ~AudioNode(); int getNumberOfInputs() const; 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 a3c941216..38e2735f1 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,3 +1,4 @@ +#include #include #include #include @@ -65,7 +66,7 @@ double BaseAudioContext::getCurrentTime() const { return destination_->getCurrentTime(); } -std::shared_ptr BaseAudioContext::getDestination() { +std::shared_ptr BaseAudioContext::getDestination() const { return destination_; } @@ -109,53 +110,59 @@ std::shared_ptr BaseAudioContext::createRecorderAdapter() { return recorderAdapter; } -std::shared_ptr BaseAudioContext::createOscillator() { - auto oscillator = std::make_shared(this); +std::shared_ptr BaseAudioContext::createOscillator( + std::shared_ptr options) { + auto oscillator = std::make_shared(this, options); nodeManager_->addSourceNode(oscillator); return oscillator; } -std::shared_ptr BaseAudioContext::createConstantSource() { - auto constantSource = std::make_shared(this); +std::shared_ptr BaseAudioContext::createConstantSource( + std::shared_ptr options) { + auto constantSource = std::make_shared(this, options); nodeManager_->addSourceNode(constantSource); return constantSource; } #ifndef AUDIO_API_TEST_SUITE -std::shared_ptr BaseAudioContext::createStreamer() { - auto streamer = std::make_shared(this); +std::shared_ptr BaseAudioContext::createStreamer( + std::shared_ptr options) { + auto streamer = std::make_shared(this, options); nodeManager_->addSourceNode(streamer); return streamer; } #endif -std::shared_ptr BaseAudioContext::createGain() { - auto gain = std::make_shared(this); +std::shared_ptr BaseAudioContext::createGain(std::shared_ptr options) { + auto gain = std::make_shared(this, options); nodeManager_->addProcessingNode(gain); return gain; } -std::shared_ptr BaseAudioContext::createStereoPanner() { - auto stereoPanner = std::make_shared(this); +std::shared_ptr BaseAudioContext::createStereoPanner( + std::shared_ptr options) { + auto stereoPanner = std::make_shared(this, options); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; } -std::shared_ptr BaseAudioContext::createBiquadFilter() { - auto biquadFilter = std::make_shared(this); +std::shared_ptr BaseAudioContext::createBiquadFilter( + std::shared_ptr options) { + auto biquadFilter = std::make_shared(this, options); nodeManager_->addProcessingNode(biquadFilter); return biquadFilter; } -std::shared_ptr BaseAudioContext::createBufferSource(bool pitchCorrection) { - auto bufferSource = std::make_shared(this, pitchCorrection); +std::shared_ptr BaseAudioContext::createBufferSource( + std::shared_ptr options) { + auto bufferSource = std::make_shared(this, options); nodeManager_->addSourceNode(bufferSource); return bufferSource; } std::shared_ptr BaseAudioContext::createBufferQueueSource( - bool pitchCorrection) { - auto bufferSource = std::make_shared(this, pitchCorrection); + std::shared_ptr options) { + auto bufferSource = std::make_shared(this, options); nodeManager_->addSourceNode(bufferSource); return bufferSource; } @@ -172,16 +179,16 @@ std::shared_ptr BaseAudioContext::createPeriodicWave( return std::make_shared(sampleRate_, complexData, length, disableNormalization); } -std::shared_ptr BaseAudioContext::createAnalyser() { - auto analyser = std::make_shared(this); +std::shared_ptr BaseAudioContext::createAnalyser( + std::shared_ptr options) { + auto analyser = std::make_shared(this, options); nodeManager_->addProcessingNode(analyser); return analyser; } std::shared_ptr BaseAudioContext::createConvolver( - std::shared_ptr buffer, - bool disableNormalization) { - auto convolver = std::make_shared(this, buffer, disableNormalization); + std::shared_ptr options) { + auto convolver = std::make_shared(this, options); nodeManager_->addProcessingNode(convolver); return convolver; } 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 713d4082c..2032142aa 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 @@ -35,6 +35,16 @@ class WorkletSourceNode; class WorkletNode; class WorkletProcessingNode; class StreamerNode; +class GainOptions; +class StereoPannerOptions; +class ConvolverOptions; +class ConstantSourceOptions; +class AnalyserOptions; +class BiquadFilterOptions; +class OscillatorOptions; +class BaseAudioBufferSourceOptions; +class AudioBufferSourceOptions; +class StreamerOptions; class BaseAudioContext { public: @@ -47,7 +57,7 @@ class BaseAudioContext { [[nodiscard]] float getSampleRate() const; [[nodiscard]] double getCurrentTime() const; [[nodiscard]] std::size_t getCurrentSampleFrame() const; - std::shared_ptr getDestination(); + std::shared_ptr getDestination() const; std::shared_ptr createRecorderAdapter(); std::shared_ptr createWorkletSourceNode( @@ -64,24 +74,27 @@ class BaseAudioContext { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createOscillator(); - std::shared_ptr createConstantSource(); - std::shared_ptr createStreamer(); - std::shared_ptr createGain(); - std::shared_ptr createStereoPanner(); - std::shared_ptr createBiquadFilter(); - std::shared_ptr createBufferSource(bool pitchCorrection); - std::shared_ptr createBufferQueueSource(bool pitchCorrection); + std::shared_ptr createOscillator(std::shared_ptr options); + std::shared_ptr createConstantSource( + std::shared_ptr options); + std::shared_ptr createStreamer(std::shared_ptr options); + std::shared_ptr createGain(std::shared_ptr options); + std::shared_ptr createStereoPanner( + std::shared_ptr options); + std::shared_ptr createBiquadFilter( + std::shared_ptr options); + std::shared_ptr createBufferSource( + std::shared_ptr options); + std::shared_ptr createBufferQueueSource( + std::shared_ptr options); static std::shared_ptr createBuffer(int numberOfChannels, size_t length, float sampleRate); std::shared_ptr createPeriodicWave( const std::vector> &complexData, bool disableNormalization, int length); - std::shared_ptr createAnalyser(); - std::shared_ptr createConvolver( - std::shared_ptr buffer, - bool disableNormalization); + std::shared_ptr createAnalyser(std::shared_ptr options); + std::shared_ptr createConvolver(std::shared_ptr options); std::shared_ptr getBasicWaveForm(OscillatorType type); [[nodiscard]] float getNyquistFrequency() const; 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 c01d3f5bd..860ae0636 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,3 +1,4 @@ +#include #include #include #include @@ -12,12 +13,14 @@ #include namespace audioapi { -AnalyserNode::AnalyserNode(audioapi::BaseAudioContext *context) - : AudioNode(context), - fftSize_(2048), - minDecibels_(-100), - maxDecibels_(-30), - smoothingTimeConstant_(0.8), +AnalyserNode::AnalyserNode( + audioapi::BaseAudioContext *context, + std::shared_ptr options) + : AudioNode(context, options), + fftSize_(options->fftSize), + minDecibels_(options->minDecibels), + maxDecibels_(options->maxDecibels), + smoothingTimeConstant_(options->smoothingTimeConstant), windowType_(WindowType::BLACKMAN) { inputBuffer_ = std::make_unique(MAX_FFT_SIZE * 2); tempBuffer_ = std::make_unique(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 b62d0058a..d8dc2691b 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 @@ -15,11 +15,12 @@ namespace audioapi { class AudioBus; class AudioArray; class CircularAudioArray; +class AnalyserOptions; class AnalyserNode : public AudioNode { public: enum class WindowType { BLACKMAN, HANN }; - explicit AnalyserNode(BaseAudioContext *context); + explicit AnalyserNode(BaseAudioContext *context, std::shared_ptr options); int getFftSize() const; int getFrequencyBinCount() 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 33f1f3aaa..b502e2cca 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 @@ -26,6 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -38,21 +39,23 @@ namespace audioapi { -BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context) : AudioNode(context) { - frequencyParam_ = - std::make_shared(350.0, 0.0f, context->getNyquistFrequency(), context); +BiquadFilterNode::BiquadFilterNode( + BaseAudioContext *context, + std::shared_ptr options) + : AudioNode(context, options) { + frequencyParam_ = std::make_shared( + options->frequency, 0.0f, context->getNyquistFrequency(), context); detuneParam_ = std::make_shared( - 0.0f, + options->detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, context); QParam_ = std::make_shared( - 1.0f, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options->Q, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); gainParam_ = std::make_shared( - 0.0f, MOST_NEGATIVE_SINGLE_FLOAT, 40 * LOG10_MOST_POSITIVE_SINGLE_FLOAT, context); - type_ = BiquadFilterType::LOWPASS; + options->gain, MOST_NEGATIVE_SINGLE_FLOAT, 40 * LOG10_MOST_POSITIVE_SINGLE_FLOAT, context); + type_ = options->type; isInitialized_ = true; - channelCountMode_ = ChannelCountMode::MAX; } std::string BiquadFilterNode::getType() { 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 6a7a606d1..3d48377ad 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 @@ -46,6 +46,7 @@ namespace audioapi { class AudioBus; +class BiquadFilterOptions; class BiquadFilterNode : public AudioNode { #ifdef AUDIO_API_TEST_SUITE @@ -54,7 +55,9 @@ class BiquadFilterNode : public AudioNode { #endif public: - explicit BiquadFilterNode(BaseAudioContext *context); + explicit BiquadFilterNode( + BaseAudioContext *context, + std::shared_ptr options); [[nodiscard]] std::string getType(); void setType(const std::string &type); 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 a09bc0ced..e27e1c7b7 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,3 +1,4 @@ +#include #include #include #include @@ -13,9 +14,8 @@ namespace audioapi { ConvolverNode::ConvolverNode( BaseAudioContext *context, - const std::shared_ptr &buffer, - bool disableNormalization) - : AudioNode(context), + const std::shared_ptr options) + : AudioNode(context, options), remainingSegments_(0), internalBufferIndex_(0), signalledToStop_(false), @@ -23,11 +23,9 @@ ConvolverNode::ConvolverNode( intermediateBus_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - channelCount_ = 2; - channelCountMode_ = ChannelCountMode::CLAMPED_MAX; - normalize_ = !disableNormalization; + normalize_ = !options->disableNormalization; gainCalibrationSampleRate_ = context->getSampleRate(); - setBuffer(buffer); + setBuffer(options->bus); audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); isInitialized_ = true; 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 92a45d609..006be093b 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 @@ -17,13 +17,13 @@ namespace audioapi { class AudioBus; class AudioBuffer; +class ConvolverOptions; class ConvolverNode : public AudioNode { public: explicit ConvolverNode( BaseAudioContext *context, - const std::shared_ptr &buffer, - bool disableNormalization); + const std::shared_ptr options); [[nodiscard]] bool getNormalize_() const; [[nodiscard]] const std::shared_ptr &getBuffer() 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 6e1c01d45..33bb716fd 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,9 +8,10 @@ namespace audioapi { -GainNode::GainNode(BaseAudioContext *context) : AudioNode(context) { +GainNode::GainNode(BaseAudioContext *context, std::shared_ptr options) + : AudioNode(context, options) { gainParam_ = std::make_shared( - 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options->gain, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); isInitialized_ = true; } 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 de3b0d7b0..361dffe5a 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 @@ -8,10 +8,11 @@ namespace audioapi { class AudioBus; +class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(BaseAudioContext *context); + explicit GainNode(BaseAudioContext *context, const std::shared_ptr options); [[nodiscard]] std::shared_ptr getGainParam() const; 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 5a362a180..1bfc4a58f 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,9 +10,11 @@ namespace audioapi { -StereoPannerNode::StereoPannerNode(BaseAudioContext *context) : AudioNode(context) { - channelCountMode_ = ChannelCountMode::CLAMPED_MAX; - panParam_ = std::make_shared(0.0, -1.0f, 1.0f, context); +StereoPannerNode::StereoPannerNode( + BaseAudioContext *context, + const std::shared_ptr options) + : AudioNode(context, options) { + panParam_ = std::make_shared(options->pan, -1.0f, 1.0f, context); isInitialized_ = true; } 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 320f7dab4..a24428f44 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 @@ -10,10 +10,13 @@ namespace audioapi { class AudioBus; +class StereoPannerOptions; class StereoPannerNode : public AudioNode { public: - explicit StereoPannerNode(BaseAudioContext *context); + explicit StereoPannerNode( + BaseAudioContext *context, + const std::shared_ptr options); [[nodiscard]] std::shared_ptr getPanParam() const; 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 709b14f6c..69a8775e8 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,3 +1,4 @@ +#include #include #include #include @@ -12,14 +13,16 @@ namespace audioapi { AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( BaseAudioContext *context, - bool pitchCorrection) - : AudioScheduledSourceNode(context), pitchCorrection_(pitchCorrection), vReadIndex_(0.0) { + std::shared_ptr options) + : AudioScheduledSourceNode(context), + pitchCorrection_(options->pitchCorrection), + vReadIndex_(0.0) { onPositionChangedInterval_ = static_cast(context->getSampleRate() * 0.1); detuneParam_ = std::make_shared( - 0.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options->detune, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); playbackRateParam_ = std::make_shared( - 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options->playbackRate, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); playbackRateBus_ = std::make_shared(RENDER_QUANTUM_SIZE * 3, channelCount_, context_->getSampleRate()); 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 38e5c792f..46dc1ecdd 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 @@ -11,10 +11,13 @@ namespace audioapi { class AudioBus; class AudioParam; +class BaseAudioBufferSourceOptions; class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { public: - explicit AudioBufferBaseSourceNode(BaseAudioContext *context, bool pitchCorrection); + explicit AudioBufferBaseSourceNode( + BaseAudioContext *context, + std::shared_ptr options); [[nodiscard]] std::shared_ptr getDetuneParam() const; [[nodiscard]] std::shared_ptr getPlaybackRateParam() const; 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 489dff9bc..4fa949f73 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 @@ -19,12 +20,12 @@ namespace audioapi { AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( BaseAudioContext *context, - bool pitchCorrection) - : AudioBufferBaseSourceNode(context, pitchCorrection) { + std::shared_ptr options) + : AudioBufferBaseSourceNode(context, options) { buffers_ = {}; stretch_->presetDefault(channelCount_, context_->getSampleRate()); - if (pitchCorrection) { + if (options->pitchCorrection) { // If pitch correction is enabled, add extra frames at the end // to compensate for processing latency. 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 0d1efa449..1003b9d31 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 @@ -14,10 +14,13 @@ namespace audioapi { class AudioBus; class AudioParam; +class BaseAudioBufferSourceOptions; class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { public: - explicit AudioBufferQueueSourceNode(BaseAudioContext *context, bool pitchCorrection); + explicit AudioBufferQueueSourceNode( + BaseAudioContext *context, + std::shared_ptr options); ~AudioBufferQueueSourceNode() override; void stop(double when) 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 86cbaace7..2a055ad3d 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,3 +1,4 @@ +#include #include #include #include @@ -12,13 +13,15 @@ namespace audioapi { -AudioBufferSourceNode::AudioBufferSourceNode(BaseAudioContext *context, bool pitchCorrection) - : AudioBufferBaseSourceNode(context, pitchCorrection), - loop_(false), +AudioBufferSourceNode::AudioBufferSourceNode( + BaseAudioContext *context, + std::shared_ptr options) + : AudioBufferBaseSourceNode(context, options), + loop_(options->loop), loopSkip_(false), - loopStart_(0), - loopEnd_(0) { - buffer_ = std::shared_ptr(nullptr); + loopStart_(options->loopStart), + loopEnd_(options->loopEnd) { + buffer_ = std::shared_ptr(options->buffer); alignedBus_ = std::shared_ptr(nullptr); isInitialized_ = true; 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 b0851f0e8..5a92bf050 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 @@ -13,10 +13,13 @@ namespace audioapi { class AudioBus; class AudioParam; +class AudioBufferSourceOptions; class AudioBufferSourceNode : public AudioBufferBaseSourceNode { public: - explicit AudioBufferSourceNode(BaseAudioContext *context, bool pitchCorrection); + explicit AudioBufferSourceNode( + BaseAudioContext *context, + std::shared_ptr options); ~AudioBufferSourceNode() override; [[nodiscard]] bool getLoop() const; 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 67676f192..19ec7f4fb 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,3 +1,4 @@ +#include #include #include #include @@ -6,10 +7,12 @@ #include namespace audioapi { -ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context) +ConstantSourceNode::ConstantSourceNode( + BaseAudioContext *context, + const std::shared_ptr options) : AudioScheduledSourceNode(context) { offsetParam_ = std::make_shared( - 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options->offset, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); isInitialized_ = true; } 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 eabcfb5da..9d3c6cb51 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 @@ -10,10 +10,13 @@ namespace audioapi { class AudioBus; +class ConstantSourceOptions; class ConstantSourceNode : public AudioScheduledSourceNode { public: - explicit ConstantSourceNode(BaseAudioContext *context); + explicit ConstantSourceNode( + BaseAudioContext *context, + const std::shared_ptr options); [[nodiscard]] std::shared_ptr 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 fbf7d29fd..810e6ade1 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,3 +1,4 @@ +#include #include #include #include @@ -8,16 +9,26 @@ namespace audioapi { -OscillatorNode::OscillatorNode(BaseAudioContext *context) : AudioScheduledSourceNode(context) { +OscillatorNode::OscillatorNode( + BaseAudioContext *context, + std::shared_ptr options) + : AudioScheduledSourceNode(context) { frequencyParam_ = std::make_shared( - 444.0, -context_->getNyquistFrequency(), context_->getNyquistFrequency(), context); + options->frequency, + -context_->getNyquistFrequency(), + context_->getNyquistFrequency(), + context); detuneParam_ = std::make_shared( - 0.0, + options->detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, context); - type_ = OscillatorType::SINE; - periodicWave_ = context_->getBasicWaveForm(type_); + type_ = options->type; + if (options->periodicWave) { + periodicWave_ = options->periodicWave; + } else { + periodicWave_ = context_->getBasicWaveForm(type_); + } audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, 1, context_->getSampleRate()); 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 47ac310d0..3a643e456 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 @@ -13,10 +13,11 @@ namespace audioapi { class AudioBus; +class OscillatorOptions; class OscillatorNode : public AudioScheduledSourceNode { public: - explicit OscillatorNode(BaseAudioContext *context); + explicit OscillatorNode(BaseAudioContext *context, std::shared_ptr options); [[nodiscard]] std::shared_ptr getFrequencyParam() const; [[nodiscard]] std::shared_ptr getDetuneParam() const; 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 deccee21f..1e94c6b41 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,6 +8,7 @@ * FFmpeg, you must comply with the terms of the LGPL for FFmpeg itself. */ +#include #include #include #include @@ -21,7 +22,7 @@ #include namespace audioapi { -StreamerNode::StreamerNode(BaseAudioContext *context) +StreamerNode::StreamerNode(BaseAudioContext *context, std::shared_ptr options) : AudioScheduledSourceNode(context), fmtCtx_(nullptr), codecCtx_(nullptr), @@ -34,13 +35,15 @@ StreamerNode::StreamerNode(BaseAudioContext *context) bufferedBus_(nullptr), audio_stream_index_(-1), maxResampledSamples_(0), - processedSamples_(0) {} + processedSamples_(0), + streamPath_(options->streamPath) {} StreamerNode::~StreamerNode() { cleanup(); } bool StreamerNode::initialize(const std::string &input_url) { + streamPath_ = input_url; if (isInitialized_) { cleanup(); } 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 4f5cd413b..b38e0bcc1 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 @@ -61,10 +61,11 @@ struct StreamingData { namespace audioapi { class AudioBus; +class StreamerOptions; class StreamerNode : public AudioScheduledSourceNode { public: - explicit StreamerNode(BaseAudioContext *context); + explicit StreamerNode(BaseAudioContext *context, std::shared_ptr options); ~StreamerNode() override; /** @@ -72,6 +73,10 @@ class StreamerNode : public AudioScheduledSourceNode { */ bool initialize(const std::string &inputUrl); + std::string getStreamPath() const { + return streamPath_; + } + protected: std::shared_ptr processNode( const std::shared_ptr &processingBus, @@ -105,6 +110,7 @@ class StreamerNode : public AudioScheduledSourceNode { STREAMER_NODE_SPSC_OVERFLOW_STRATEGY, STREAMER_NODE_SPSC_WAIT_STRATEGY> receiver_; + std::string streamPath_; /** * @brief Setting up the resampler 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 26bc10788..c11e04c9b 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class ConstantSourceTest : public ::testing::Test { class TestableConstantSourceNode : public ConstantSourceNode { public: - explicit TestableConstantSourceNode(BaseAudioContext *context) : ConstantSourceNode(context) {} + explicit TestableConstantSourceNode(BaseAudioContext *context) + : ConstantSourceNode(context, std::make_shared()) {} void setOffsetParam(float value) { getOffsetParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableConstantSourceNode : public ConstantSourceNode { }; TEST_F(ConstantSourceTest, ConstantSourceCanBeCreated) { - auto constantSource = context->createConstantSource(); + auto constantSource = context->createConstantSource(std::make_shared()); ASSERT_NE(constantSource, nullptr); } 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 9ab434638..26d1764f0 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class GainTest : public ::testing::Test { class TestableGainNode : public GainNode { public: - explicit TestableGainNode(BaseAudioContext *context) : GainNode(context) {} + explicit TestableGainNode(BaseAudioContext *context) + : GainNode(context, std::make_shared()) {} void setGainParam(float value) { getGainParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableGainNode : public GainNode { }; TEST_F(GainTest, GainCanBeCreated) { - auto gain = context->createGain(); + auto gain = context->createGain(std::make_shared()); ASSERT_NE(gain, nullptr); } 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 c6ff93d31..8f92e70a4 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class StereoPannerTest : public ::testing::Test { class TestableStereoPannerNode : public StereoPannerNode { public: - explicit TestableStereoPannerNode(BaseAudioContext *context) : StereoPannerNode(context) {} + explicit TestableStereoPannerNode(BaseAudioContext *context) + : StereoPannerNode(context, std::make_shared()) {} void setPanParam(float value) { getPanParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableStereoPannerNode : public StereoPannerNode { }; TEST_F(StereoPannerTest, StereoPannerCanBeCreated) { - auto panner = context->createStereoPanner(); + auto panner = context->createStereoPanner(std::make_shared()); ASSERT_NE(panner, nullptr); } diff --git a/packages/react-native-audio-api/src/api.ts b/packages/react-native-audio-api/src/api.ts index 9a575300d..c667c0ff6 100644 --- a/packages/react-native-audio-api/src/api.ts +++ b/packages/react-native-audio-api/src/api.ts @@ -78,15 +78,14 @@ export { default as ConvolverNode } from './core/ConvolverNode'; export { default as useSystemVolume } from './hooks/useSystemVolume'; export { decodeAudioData, decodePCMInBase64 } from './core/AudioDecoder'; export { default as changePlaybackSpeed } from './core/AudioStretcher'; +export { default as PeriodicWave } from './core/PeriodicWave'; export { OscillatorType, - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, WindowType, - PeriodicWaveConstraints, AudioWorkletRuntime, } from './types'; diff --git a/packages/react-native-audio-api/src/api.web.ts b/packages/react-native-audio-api/src/api.web.ts index 292f86993..09259d993 100644 --- a/packages/react-native-audio-api/src/api.web.ts +++ b/packages/react-native-audio-api/src/api.web.ts @@ -14,17 +14,16 @@ export { default as OscillatorNode } from './web-core/OscillatorNode'; export { default as StereoPannerNode } from './web-core/StereoPannerNode'; export { default as ConstantSourceNode } from './web-core/ConstantSourceNode'; export { default as ConvolverNode } from './web-core/ConvolverNode'; +export { default as PeriodicWave } from './web-core/PeriodicWave'; export * from './web-core/custom'; export { OscillatorType, - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, WindowType, - PeriodicWaveConstraints, } 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 d2d8176e1..6af3e4bdb 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -1,6 +1,8 @@ +import BaseAudioContext from './BaseAudioContext'; +import { AnalyserOptions } from '../defaults'; import { IndexSizeError } from '../errors'; import { IAnalyserNode } from '../interfaces'; -import { WindowType } from '../types'; +import { WindowType, TAnalyserOptions } from '../types'; import AudioNode from './AudioNode'; export default class AnalyserNode extends AudioNode { @@ -8,6 +10,25 @@ export default class AnalyserNode extends AudioNode { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, ]; + constructor(context: BaseAudioContext, options?: TAnalyserOptions) { + const finalOptions: TAnalyserOptions = { + ...AnalyserOptions, + ...options, + }; + + if (!AnalyserNode.allowedFFTSize.includes(finalOptions.fftSize!)) { + throw new IndexSizeError( + `fftSize must be one of the following values: ${AnalyserNode.allowedFFTSize.join( + ', ' + )}` + ); + } + console.log('finalOptions', finalOptions); + const analyserNode: IAnalyserNode = + context.context.createAnalyser(finalOptions); + super(context, analyserNode); + } + public get fftSize(): number { return (this.node as IAnalyserNode).fftSize; } diff --git a/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts index c780859c1..91935ffca 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts @@ -2,8 +2,23 @@ import { IAudioBufferQueueSourceNode } from '../interfaces'; import AudioBufferBaseSourceNode from './AudioBufferBaseSourceNode'; import AudioBuffer from './AudioBuffer'; import { RangeError } from '../errors'; +import BaseAudioContext from './BaseAudioContext'; +import { TBaseAudioBufferSourceOptions } from '../types'; +import { BaseAudioBufferSourceOptions } from '../defaults'; export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNode { + constructor( + context: BaseAudioContext, + options?: TBaseAudioBufferSourceOptions + ) { + const finalOptions: TBaseAudioBufferSourceOptions = { + ...BaseAudioBufferSourceOptions, + ...options, + }; + const node = context.context.createBufferQueueSource(finalOptions); + super(context, node); + } + public enqueueBuffer(buffer: AudioBuffer): string { return (this.node as IAudioBufferQueueSourceNode).enqueueBuffer( buffer.buffer diff --git a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts index e8dba4a23..3aff9ac7e 100644 --- a/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts +++ b/packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts @@ -4,11 +4,23 @@ import AudioBuffer from './AudioBuffer'; import { InvalidStateError, RangeError } from '../errors'; import { EventEmptyType } from '../events/types'; import { AudioEventSubscription } from '../events'; +import { TAudioBufferSourceOptions } from '../types'; +import BaseAudioContext from './BaseAudioContext'; +import { AudioBufferSourceOptions } from '../defaults'; export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode { private onLoopEndedSubscription?: AudioEventSubscription; private onLoopEndedCallback?: (event: EventEmptyType) => void; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { + const finalOptions: TAudioBufferSourceOptions = { + ...AudioBufferSourceOptions, + ...options, + }; + const node = context.context.createBufferSource(finalOptions); + super(context, node); + } + public get buffer(): AudioBuffer | null { const buffer = (this.node as IAudioBufferSourceNode).buffer; if (!buffer) { diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 86938d40c..6c0d6b6bb 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -1,12 +1,6 @@ import { InvalidAccessError, NotSupportedError } from '../errors'; import { IBaseAudioContext } from '../interfaces'; -import { - AudioBufferBaseSourceNodeOptions, - ContextState, - PeriodicWaveConstraints, - AudioWorkletRuntime, - ConvolverNodeOptions, -} from '../types'; +import { ContextState, AudioWorkletRuntime } from '../types'; import { assertWorkletsEnabled, workletsModule } from '../utils'; import WorkletSourceNode from './WorkletSourceNode'; import WorkletProcessingNode from './WorkletProcessingNode'; @@ -177,53 +171,39 @@ export default class BaseAudioContext { } createRecorderAdapter(): RecorderAdapterNode { - return new RecorderAdapterNode(this, this.context.createRecorderAdapter()); + return new RecorderAdapterNode(this); } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createStreamer(): StreamerNode { - return new StreamerNode(this, this.context.createStreamer()); + return new StreamerNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } - createBufferSource( - options?: AudioBufferBaseSourceNodeOptions - ): AudioBufferSourceNode { - const pitchCorrection = options?.pitchCorrection ?? false; - - return new AudioBufferSourceNode( - this, - this.context.createBufferSource(pitchCorrection) - ); + createBufferSource(): AudioBufferSourceNode { + return new AudioBufferSourceNode(this); } - createBufferQueueSource( - options?: AudioBufferBaseSourceNodeOptions - ): AudioBufferQueueSourceNode { - const pitchCorrection = options?.pitchCorrection ?? false; - - return new AudioBufferQueueSourceNode( - this, - this.context.createBufferQueueSource(pitchCorrection) - ); + createBufferQueueSource(): AudioBufferQueueSourceNode { + return new AudioBufferQueueSourceNode(this); } createBuffer( @@ -264,36 +244,14 @@ export default class BaseAudioContext { `The lengths of the real (${real.length}) and imaginary (${imag.length}) arrays must match.` ); } - - const disableNormalization = constraints?.disableNormalization ?? false; - - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, disableNormalization) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(buffer?.buffer, disableNormalization) - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } } diff --git a/packages/react-native-audio-api/src/core/BiquadFilterNode.ts b/packages/react-native-audio-api/src/core/BiquadFilterNode.ts index 68db7c02a..b85475fc5 100644 --- a/packages/react-native-audio-api/src/core/BiquadFilterNode.ts +++ b/packages/react-native-audio-api/src/core/BiquadFilterNode.ts @@ -3,7 +3,8 @@ import { IBiquadFilterNode } from '../interfaces'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; -import { BiquadFilterType } from '../types'; +import { BiquadFilterOptions } from '../defaults'; +import { TBiquadFilterOptions } from '../types'; export default class BiquadFilterNode extends AudioNode { readonly frequency: AudioParam; @@ -11,7 +12,13 @@ export default class BiquadFilterNode extends AudioNode { readonly Q: AudioParam; readonly gain: AudioParam; - constructor(context: BaseAudioContext, biquadFilter: IBiquadFilterNode) { + constructor(context: BaseAudioContext, options?: TBiquadFilterOptions) { + const finalOptions: TBiquadFilterOptions = { + ...BiquadFilterOptions, + ...options, + }; + const biquadFilter: IBiquadFilterNode = + context.context.createBiquadFilter(finalOptions); super(context, biquadFilter); this.frequency = new AudioParam(biquadFilter.frequency, context); this.detune = new AudioParam(biquadFilter.detune, context); diff --git a/packages/react-native-audio-api/src/core/ConstantSourceNode.ts b/packages/react-native-audio-api/src/core/ConstantSourceNode.ts index 1a36c4b9f..65b26f517 100644 --- a/packages/react-native-audio-api/src/core/ConstantSourceNode.ts +++ b/packages/react-native-audio-api/src/core/ConstantSourceNode.ts @@ -1,4 +1,6 @@ import { IConstantSourceNode } from '../interfaces'; +import { ConstantSourceOptions } from '../defaults'; +import { TConstantSourceOptions } from '../types'; import AudioParam from './AudioParam'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; @@ -6,7 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class ConstantSourceNode extends AudioScheduledSourceNode { readonly offset: AudioParam; - constructor(context: BaseAudioContext, node: IConstantSourceNode) { + constructor(context: BaseAudioContext, options?: TConstantSourceOptions) { + const finalOptions: TConstantSourceOptions = { + ...ConstantSourceOptions, + ...options, + }; + const node: IConstantSourceNode = + context.context.createConstantSource(finalOptions); super(context, node); this.offset = new AudioParam(node.offset, context); } diff --git a/packages/react-native-audio-api/src/core/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index 975f98fa6..e05605b58 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -1,12 +1,33 @@ import { IConvolverNode } from '../interfaces'; +import { ConvolverOptions } from '../defaults'; +import { TConvolverOptions } from '../types'; +import { NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; export default class ConvolverNode extends AudioNode { - constructor(context: BaseAudioContext, node: IConvolverNode) { - super(context, node); - this.normalize = node.normalize; + constructor(context: BaseAudioContext, options?: TConvolverOptions) { + const finalOptions: TConvolverOptions = { + ...ConvolverOptions, + ...options, + }; + if (finalOptions.buffer) { + const numberOfChannels = finalOptions.buffer.numberOfChannels; + if ( + numberOfChannels !== 1 && + numberOfChannels !== 2 && + numberOfChannels !== 4 + ) { + throw new NotSupportedError( + `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` + ); + } + } + const convolverNode: IConvolverNode = + context.context.createConvolver(finalOptions); + super(context, convolverNode); + this.normalize = convolverNode.normalize; } public get buffer(): AudioBuffer | null { diff --git a/packages/react-native-audio-api/src/core/GainNode.ts b/packages/react-native-audio-api/src/core/GainNode.ts index a216f1365..1c2b8f93b 100644 --- a/packages/react-native-audio-api/src/core/GainNode.ts +++ b/packages/react-native-audio-api/src/core/GainNode.ts @@ -1,4 +1,6 @@ import { IGainNode } from '../interfaces'; +import { GainOptions } from '../defaults'; +import { TGainOptions } from '../types'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; @@ -6,8 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class GainNode extends AudioNode { readonly gain: AudioParam; - constructor(context: BaseAudioContext, gain: IGainNode) { - super(context, gain); - this.gain = new AudioParam(gain.gain, context); + constructor(context: BaseAudioContext, options?: TGainOptions) { + const finalOptions: TGainOptions = { + ...GainOptions, + ...options, + }; + const gainNode: IGainNode = context.context.createGain(finalOptions); + super(context, gainNode); + this.gain = new AudioParam(gainNode.gain, context); } } diff --git a/packages/react-native-audio-api/src/core/OscillatorNode.ts b/packages/react-native-audio-api/src/core/OscillatorNode.ts index 48296831c..df6253b03 100644 --- a/packages/react-native-audio-api/src/core/OscillatorNode.ts +++ b/packages/react-native-audio-api/src/core/OscillatorNode.ts @@ -1,17 +1,34 @@ import { IOscillatorNode } from '../interfaces'; -import { OscillatorType } from '../types'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; import PeriodicWave from './PeriodicWave'; import { InvalidStateError } from '../errors'; import { EventEmptyType } from '../events/types'; +import { OscillatorOptions } from '../defaults'; +import { TOscillatorOptions } from '../types'; export default class OscillatorNode extends AudioScheduledSourceNode { readonly frequency: AudioParam; readonly detune: AudioParam; - constructor(context: BaseAudioContext, node: IOscillatorNode) { + constructor(context: BaseAudioContext, options?: TOscillatorOptions) { + const finalOptions: TOscillatorOptions = { + ...OscillatorOptions, + ...options, + }; + + if (finalOptions.type === 'custom' && !finalOptions.periodicWave) { + throw new InvalidStateError( + "'type' cannot be set to 'custom' without providing a 'periodicWave'." + ); + } + + if (finalOptions.periodicWave) { + finalOptions.type = 'custom'; + } + + const node = context.context.createOscillator(finalOptions); super(context, node); this.frequency = new AudioParam(node.frequency, context); this.detune = new AudioParam(node.detune, context); diff --git a/packages/react-native-audio-api/src/core/PeriodicWave.ts b/packages/react-native-audio-api/src/core/PeriodicWave.ts index b00c4505c..58c45c014 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -1,10 +1,61 @@ import { IPeriodicWave } from '../interfaces'; +import BaseAudioContext from './BaseAudioContext'; +import { TPeriodicWaveOptions } from '../types'; +import { PeriodicWaveConstraints } from '../defaults'; +import { NotSupportedError } from '../errors'; + +export function validatePeriodicWaveOptions( + sampleRate: number, + options?: TPeriodicWaveOptions +): TPeriodicWaveOptions { + let real: Float32Array | undefined; + let imag: Float32Array | undefined; + if (!options || (!options.real && !options.imag)) { + // default to a sine wave + if (sampleRate < 24000) { + real = new Float32Array(2048); + imag = new Float32Array(2048); + } else if (sampleRate < 88200) { + real = new Float32Array(4096); + imag = new Float32Array(4096); + } else { + real = new Float32Array(16384); + imag = new Float32Array(16384); + } + imag[1] = 1; + } else { + real = options?.real; + imag = options?.imag; + if (real && imag && real.length !== imag.length) { + throw new NotSupportedError( + "'real' and 'imag' arrays must have the same length" + ); + } + if (real && !imag) { + imag = new Float32Array(real.length); + } else if (!real && imag) { + real = new Float32Array(imag.length); + } + } + const norm: boolean = options?.disableNormalization + ? options.disableNormalization + : PeriodicWaveConstraints.disableNormalization!; + return { real, imag, disableNormalization: norm }; +} export default class PeriodicWave { /** @internal */ public readonly periodicWave: IPeriodicWave; - constructor(periodicWave: IPeriodicWave) { - this.periodicWave = periodicWave; + constructor(context: BaseAudioContext, options?: TPeriodicWaveOptions) { + const finalOptions = validatePeriodicWaveOptions( + context.sampleRate, + options + ); + this.periodicWave = context.context.createPeriodicWave( + finalOptions.real, + finalOptions.imag, + finalOptions.disableNormalization! + ); } } diff --git a/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts b/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts index 75c194944..ecad274bc 100644 --- a/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts +++ b/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts @@ -1,10 +1,15 @@ import { IRecorderAdapterNode } from '../interfaces'; import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; export default class RecorderAdapterNode extends AudioNode { /** @internal */ public wasConnected: boolean = false; + constructor(context: BaseAudioContext) { + super(context, context.context.createRecorderAdapter()); + } + /** @internal */ public getNode(): IRecorderAdapterNode { return this.node as IRecorderAdapterNode; diff --git a/packages/react-native-audio-api/src/core/StereoPannerNode.ts b/packages/react-native-audio-api/src/core/StereoPannerNode.ts index a8fa77006..040e9f840 100644 --- a/packages/react-native-audio-api/src/core/StereoPannerNode.ts +++ b/packages/react-native-audio-api/src/core/StereoPannerNode.ts @@ -1,4 +1,6 @@ import { IStereoPannerNode } from '../interfaces'; +import { SteroPannerOptions } from '../defaults'; +import { TSteroPannerOptions } from '../types'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; @@ -6,7 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor(context: BaseAudioContext, pan: IStereoPannerNode) { + constructor(context: BaseAudioContext, options?: TSteroPannerOptions) { + const finalOptions: TSteroPannerOptions = { + ...SteroPannerOptions, + ...options, + }; + const pan: IStereoPannerNode = + context.context.createStereoPanner(finalOptions); super(context, pan); this.pan = new AudioParam(pan.pan, context); } diff --git a/packages/react-native-audio-api/src/core/StreamerNode.ts b/packages/react-native-audio-api/src/core/StreamerNode.ts index fcbb61d20..f1efb9f2e 100644 --- a/packages/react-native-audio-api/src/core/StreamerNode.ts +++ b/packages/react-native-audio-api/src/core/StreamerNode.ts @@ -1,8 +1,33 @@ import { IStreamerNode } from '../interfaces'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; +import { TStreamerOptions } from '../types'; +import { InvalidStateError } 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); + super(context, node); + if (options?.streamPath) { + if (this.initialize(options.streamPath)) { + this.hasBeenSetup = true; + } + } + } + public initialize(streamPath: string): boolean { - return (this.node as IStreamerNode).initialize(streamPath); + 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; } } diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts new file mode 100644 index 000000000..9459a2b00 --- /dev/null +++ b/packages/react-native-audio-api/src/defaults.ts @@ -0,0 +1,80 @@ +import { + TAudioNodeOptions, + TGainOptions, + TSteroPannerOptions, + TConvolverOptions, + TConstantSourceOptions, + TPeriodicWaveConstraints, + TAnalyserOptions, + TBiquadFilterOptions, + TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, +} from './types'; + +export const AudioNodeOptions: TAudioNodeOptions = { + channelCount: 2, + channelCountMode: 'max', + channelInterpretation: 'speakers', +}; + +export const GainOptions: TGainOptions = { + ...AudioNodeOptions, + gain: 1, +}; + +export const SteroPannerOptions: TSteroPannerOptions = { + ...AudioNodeOptions, + channelCountMode: 'clamped-max', + pan: 0, +}; + +export const AnalyserOptions: TAnalyserOptions = { + ...AudioNodeOptions, + fftSize: 2048, + minDecibels: -100, + maxDecibels: -30, + smoothingTimeConstant: 0.8, +}; + +export const BiquadFilterOptions: TBiquadFilterOptions = { + ...AudioNodeOptions, + Q: 1, + detune: 0, + frequency: 350, + gain: 0, + type: 'lowpass', +}; + +export const ConvolverOptions: TConvolverOptions = { + ...AudioNodeOptions, + disableNormalization: false, +}; + +export const ConstantSourceOptions: TConstantSourceOptions = { + offset: 1, +}; + +export const PeriodicWaveConstraints: TPeriodicWaveConstraints = { + disableNormalization: false, +}; + +export const OscillatorOptions: TOscillatorOptions = { + ...AudioNodeOptions, + type: 'sine', + frequency: 440, + detune: 0, +}; + +export const BaseAudioBufferSourceOptions: TBaseAudioBufferSourceOptions = { + playbackRate: 1, + detune: 0, + pitchCorrection: false, +}; + +export const AudioBufferSourceOptions: TAudioBufferSourceOptions = { + ...BaseAudioBufferSourceOptions, + loop: false, + loopStart: 0, + loopEnd: 0, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 5c1bc8a0a..3d3ddb684 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -1,11 +1,20 @@ import { AudioEventCallback, AudioEventName } from './events/types'; import { - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, OscillatorType, WindowType, + TGainOptions, + TSteroPannerOptions, + TConvolverOptions, + TConstantSourceOptions, + TAnalyserOptions, + TBiquadFilterOptions, + TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, + TStreamerOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -57,14 +66,22 @@ export interface IBaseAudioContext { shareableWorklet: ShareableWorkletCallback, shouldUseUiRuntime: boolean ): IWorkletProcessingNode; - createOscillator(): IOscillatorNode; - createConstantSource(): IConstantSourceNode; - createGain(): IGainNode; - createStereoPanner(): IStereoPannerNode; - createBiquadFilter: () => IBiquadFilterNode; - createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; + createOscillator(oscillatorOptions: TOscillatorOptions): IOscillatorNode; + createConstantSource( + constantSourceOptions: TConstantSourceOptions + ): IConstantSourceNode; + createGain(gainOptions: TGainOptions): IGainNode; + createStereoPanner( + stereoPannerOptions: TSteroPannerOptions + ): IStereoPannerNode; + createBiquadFilter: ( + biquadFilterOptions: TBiquadFilterOptions + ) => IBiquadFilterNode; + createBufferSource: ( + audioBufferSourceOptions: TAudioBufferSourceOptions + ) => IAudioBufferSourceNode; createBufferQueueSource: ( - pitchCorrection: boolean + audioBufferQueueSourceOptions: TBaseAudioBufferSourceOptions ) => IAudioBufferQueueSourceNode; createBuffer: ( channels: number, @@ -76,12 +93,9 @@ export interface IBaseAudioContext { imag: Float32Array, disableNormalization: boolean ) => IPeriodicWave; - createAnalyser: () => IAnalyserNode; - createConvolver: ( - buffer: IAudioBuffer | undefined, - disableNormalization: boolean - ) => IConvolverNode; - createStreamer: () => IStreamerNode; + createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; + createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; + createStreamer: (streamerOptions?: TStreamerOptions) => IStreamerNode; } export interface IAudioContext extends IBaseAudioContext { @@ -162,6 +176,7 @@ export interface IOscillatorNode extends IAudioScheduledSourceNode { } export interface IStreamerNode extends IAudioNode { + readonly streamPath: string; initialize(streamPath: string): boolean; } diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index da7d0675b..84f36bd66 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -1,10 +1,11 @@ import AudioBuffer from './core/AudioBuffer'; +import PeriodicWave from './core/PeriodicWave'; export type ChannelCountMode = 'max' | 'clamped-max' | 'explicit'; export type ChannelInterpretation = 'speakers' | 'discrete'; -export type BiquadFilterType = +type BiquadFilterType = | 'lowpass' | 'highpass' | 'bandpass' @@ -25,10 +26,6 @@ export type OscillatorType = | 'triangle' | 'custom'; -export interface PeriodicWaveConstraints { - disableNormalization: boolean; -} - export interface AudioContextOptions { sampleRate?: number; } @@ -46,13 +43,81 @@ export interface AudioRecorderOptions { export type WindowType = 'blackman' | 'hann'; -export interface AudioBufferBaseSourceNodeOptions { - pitchCorrection: boolean; +export type ProcessorMode = 'processInPlace' | 'processThrough'; + +export interface TAudioNodeOptions { + channelCount?: number; + channelCountMode?: ChannelCountMode; + channelInterpretation?: ChannelInterpretation; } -export type ProcessorMode = 'processInPlace' | 'processThrough'; +export interface TGainOptions extends TAudioNodeOptions { + gain?: number; +} + +export interface TSteroPannerOptions extends TAudioNodeOptions { + pan?: number; +} -export interface ConvolverNodeOptions { - buffer?: AudioBuffer | null; +export interface TAnalyserOptions extends TAudioNodeOptions { + fftSize?: number; + minDecibels?: number; + maxDecibels?: number; + smoothingTimeConstant?: number; +} + +export interface TBiquadFilterOptions extends TAudioNodeOptions { + type?: BiquadFilterType; + frequency?: number; + detune?: number; + Q?: number; + gain?: number; +} + +export interface TOscillatorOptions { + type?: OscillatorType; + frequency?: number; + detune?: number; + periodicWave?: PeriodicWave; +} + +export interface TBaseAudioBufferSourceOptions { + detune?: number; + playbackRate?: number; + pitchCorrection?: boolean; +} + +export interface TAudioBufferSourceOptions + extends TBaseAudioBufferSourceOptions { + buffer?: AudioBuffer; + loop?: boolean; + loopStart?: number; + loopEnd?: number; +} + +export interface TConvolverOptions extends TAudioNodeOptions { + buffer?: AudioBuffer; + disableNormalization?: boolean; +} + +export interface TWebConvolverOptions { + buffer?: globalThis.AudioBuffer | null; + normalize?: boolean; +} + +export interface TConstantSourceOptions { + offset?: number; +} + +export interface TStreamerOptions { + streamPath?: string; +} + +export interface TPeriodicWaveConstraints { disableNormalization?: boolean; } + +export interface TPeriodicWaveOptions extends TPeriodicWaveConstraints { + real: Float32Array; + imag: Float32Array; +} 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 c74b67e05..e8a360163 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 } from '../types'; +import { WindowType, TAnalyserOptions } from '../types'; import BaseAudioContext from './BaseAudioContext'; export default class AnalyserNode extends AudioNode { @@ -9,7 +9,8 @@ export default class AnalyserNode extends AudioNode { maxDecibels: number; smoothingTimeConstant: number; - constructor(context: BaseAudioContext, node: globalThis.AnalyserNode) { + constructor(context: BaseAudioContext, analyserOptions?: TAnalyserOptions) { + const node = new globalThis.AnalyserNode(context.context, analyserOptions); super(context, node); this.fftSize = node.fftSize; @@ -30,18 +31,26 @@ export default class AnalyserNode extends AudioNode { } public getByteFrequencyData(array: Uint8Array): void { - (this.node as globalThis.AnalyserNode).getByteFrequencyData(array); + (this.node as globalThis.AnalyserNode).getByteFrequencyData( + array as Uint8Array + ); } public getByteTimeDomainData(array: Uint8Array): void { - (this.node as globalThis.AnalyserNode).getByteTimeDomainData(array); + (this.node as globalThis.AnalyserNode).getByteTimeDomainData( + array as Uint8Array + ); } public getFloatFrequencyData(array: Float32Array): void { - (this.node as globalThis.AnalyserNode).getFloatFrequencyData(array); + (this.node as globalThis.AnalyserNode).getFloatFrequencyData( + array as Float32Array + ); } public getFloatTimeDomainData(array: Float32Array): void { - (this.node as globalThis.AnalyserNode).getFloatTimeDomainData(array); + (this.node as globalThis.AnalyserNode).getFloatTimeDomainData( + array as Float32Array + ); } } diff --git a/packages/react-native-audio-api/src/web-core/AudioContext.tsx b/packages/react-native-audio-api/src/web-core/AudioContext.tsx index d7ab486a4..f8d8209b2 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -1,7 +1,7 @@ import { ContextState, - PeriodicWaveConstraints, AudioContextOptions, + // @ts-ignore only-for-this-commit AudioBufferBaseSourceNodeOptions, } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; @@ -16,7 +16,6 @@ import OscillatorNode from './OscillatorNode'; import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import ConvolverNode from './ConvolverNode'; -import { ConvolverNodeOptions } from './ConvolverNodeOptions'; import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConstantSourceNode from './ConstantSourceNode'; @@ -53,46 +52,27 @@ export default class AudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); - } - - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(), - buffer, - disableNormalization - ); + return new BiquadFilterNode(this); + } + + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } async createBufferSource( @@ -152,13 +132,11 @@ export default class AudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } async decodeAudioDataSource(source: string): Promise { diff --git a/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx b/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx index 5d2fa9a74..701bea3d1 100644 --- a/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx @@ -1,4 +1,4 @@ -import { ContextState, PeriodicWaveConstraints } from '../types'; +import { ContextState } from '../types'; import AnalyserNode from './AnalyserNode'; import AudioDestinationNode from './AudioDestinationNode'; import AudioBuffer from './AudioBuffer'; diff --git a/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx b/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx index 4a8a4df21..dd540369a 100644 --- a/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx +++ b/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx @@ -1,8 +1,8 @@ import AudioParam from './AudioParam'; import AudioNode from './AudioNode'; import BaseAudioContext from './BaseAudioContext'; -import { BiquadFilterType } from '../types'; import { InvalidAccessError } from '../errors'; +import { TBiquadFilterOptions } from '../types'; export default class BiquadFilterNode extends AudioNode { readonly frequency: AudioParam; @@ -12,8 +12,12 @@ export default class BiquadFilterNode extends AudioNode { constructor( context: BaseAudioContext, - biquadFilter: globalThis.BiquadFilterNode + biquadFilterOptions?: TBiquadFilterOptions ) { + const biquadFilter = new globalThis.BiquadFilterNode( + context.context, + biquadFilterOptions + ); super(context, biquadFilter); this.frequency = new AudioParam(biquadFilter.frequency, context); this.detune = new AudioParam(biquadFilter.detune, context); diff --git a/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx b/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx index 25560b4a6..9d8e745c1 100644 --- a/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx @@ -1,11 +1,13 @@ import AudioParam from './AudioParam'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; +import { TConstantSourceOptions } from '../types'; export default class ConstantSourceNode extends AudioScheduledSourceNode { readonly offset: AudioParam; - constructor(context: BaseAudioContext, node: globalThis.ConstantSourceNode) { + constructor(context: BaseAudioContext, options?: TConstantSourceOptions) { + const node = new globalThis.ConstantSourceNode(context.context, options); super(context, node); this.offset = new AudioParam(node.offset, context); } diff --git a/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx b/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx index 6bc910514..8a9488ed1 100644 --- a/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx +++ b/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx @@ -1,20 +1,20 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; +import { TWebConvolverOptions } from '../types'; export default class ConvolverNode extends AudioNode { constructor( context: BaseAudioContext, - node: globalThis.ConvolverNode, - buffer: AudioBuffer | null = null, - disableNormalization: boolean = false + convolverOptions?: TWebConvolverOptions ) { - super(context, node); + const convolver = new globalThis.ConvolverNode( + context.context, + convolverOptions + ); - (this.node as globalThis.ConvolverNode).normalize = !disableNormalization; - if (buffer) { - (this.node as globalThis.ConvolverNode).buffer = buffer.buffer; - } + const node = convolver; + super(context, node); } public get buffer(): AudioBuffer | null { diff --git a/packages/react-native-audio-api/src/web-core/GainNode.tsx b/packages/react-native-audio-api/src/web-core/GainNode.tsx index 601de5920..4f8c98a79 100644 --- a/packages/react-native-audio-api/src/web-core/GainNode.tsx +++ b/packages/react-native-audio-api/src/web-core/GainNode.tsx @@ -1,11 +1,13 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; +import { TGainOptions } from '../types'; export default class GainNode extends AudioNode { readonly gain: AudioParam; - constructor(context: BaseAudioContext, gain: globalThis.GainNode) { + constructor(context: BaseAudioContext, gainOptions?: TGainOptions) { + const gain = new globalThis.GainNode(context.context, gainOptions); super(context, gain); this.gain = new AudioParam(gain.gain, context); } diff --git a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx index cf9eec733..b860f5375 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -1,7 +1,7 @@ import { ContextState, - PeriodicWaveConstraints, OfflineAudioContextOptions, + // @ts-ignore only-for-this-commit AudioBufferBaseSourceNodeOptions, } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; @@ -19,7 +19,6 @@ import ConstantSourceNode from './ConstantSourceNode'; import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConvolverNode from './ConvolverNode'; -import { ConvolverNodeOptions } from './ConvolverNodeOptions'; export default class OfflineAudioContext implements BaseAudioContext { readonly context: globalThis.OfflineAudioContext; @@ -59,46 +58,27 @@ export default class OfflineAudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); - } - - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(), - buffer, - disableNormalization - ); + return new BiquadFilterNode(this); + } + + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } async createBufferSource( @@ -158,13 +138,11 @@ export default class OfflineAudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } async decodeAudioDataSource(source: string): Promise { diff --git a/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx b/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx index 612cdd9f6..d180aa596 100644 --- a/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx +++ b/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx @@ -1,4 +1,4 @@ -import { OscillatorType } from '../types'; +import { OscillatorType, TOscillatorOptions } from '../types'; import { InvalidStateError } from '../errors'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; @@ -9,7 +9,8 @@ export default class OscillatorNode extends AudioScheduledSourceNode { readonly frequency: AudioParam; readonly detune: AudioParam; - constructor(context: BaseAudioContext, node: globalThis.OscillatorNode) { + constructor(context: BaseAudioContext, options?: TOscillatorOptions) { + const node = new globalThis.OscillatorNode(context.context, options); super(context, node); this.detune = new AudioParam(node.detune, context); diff --git a/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx b/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx index bdf8979e1..89a7fe050 100644 --- a/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx +++ b/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx @@ -1,8 +1,21 @@ +import BaseAudioContext from './BaseAudioContext'; +import { TPeriodicWaveOptions } from '../types'; +import { validatePeriodicWaveOptions } from '../core/PeriodicWave'; + export default class PeriodicWave { /** @internal */ readonly periodicWave: globalThis.PeriodicWave; - constructor(periodicWave: globalThis.PeriodicWave) { + constructor(context: BaseAudioContext, options?: TPeriodicWaveOptions) { + const finalOptions = validatePeriodicWaveOptions( + context.sampleRate, + options + ); + const periodicWave = context.context.createPeriodicWave( + finalOptions.real, + finalOptions.imag, + { disableNormalization: finalOptions.disableNormalization } + ); this.periodicWave = periodicWave; } } diff --git a/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx b/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx index 2d468a205..5c968c8ff 100644 --- a/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx +++ b/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx @@ -1,11 +1,19 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; +import { TSteroPannerOptions } from '../types'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor(context: BaseAudioContext, pan: globalThis.StereoPannerNode) { + constructor( + context: BaseAudioContext, + stereoPannerOptions?: TSteroPannerOptions + ) { + const pan = new globalThis.StereoPannerNode( + context.context, + stereoPannerOptions + ); super(context, pan); this.pan = new AudioParam(pan.pan, context); }