From d74dee660ca08a6f8487769d4fed7238a642fb90 Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 13 Nov 2025 18:12:41 +0100 Subject: [PATCH 01/35] feat: gain impl --- .../BaseAudioContextHostObject.cpp | 7 ++- .../audioapi/HostObjects/utils/NodeOptions.h | 18 ++++++ .../HostObjects/utils/NodeOptionsParser.h | 63 +++++++++++++++++++ .../common/cpp/audioapi/core/AudioNode.cpp | 11 +++- .../common/cpp/audioapi/core/AudioNode.h | 3 +- .../cpp/audioapi/core/BaseAudioContext.cpp | 6 +- .../cpp/audioapi/core/BaseAudioContext.h | 3 +- .../cpp/audioapi/core/effects/GainNode.cpp | 11 +++- .../cpp/audioapi/core/effects/GainNode.h | 3 +- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/GainNode.ts | 13 +++- .../react-native-audio-api/src/defaults.ts | 12 ++++ .../react-native-audio-api/src/interfaces.ts | 3 +- packages/react-native-audio-api/src/types.ts | 10 +++ 14 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h create mode 100644 packages/react-native-audio-api/src/defaults.ts 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 0f7565926..7c6c64182 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,8 @@ #include #include +#include + namespace audioapi { BaseAudioContextHostObject::BaseAudioContextHostObject( @@ -184,7 +186,10 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { - auto gain = context_->createGain(); + auto object = args[0].asObject(runtime); + GainOptions options = + audioapi::option_parser::parseGainOptions(runtime, object); + auto gain = context_->createGain(std::make_shared(options)); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); } 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..64fb7ea32 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace audioapi { +struct AudioNodeOptions { + int channelCount; + ChannelCountMode channelCountMode; + ChannelInterpretation channelInterpretation; +}; + +struct GainOptions : AudioNodeOptions { + float gain; + explicit GainOptions(AudioNodeOptions nodeOptions) + : AudioNodeOptions(nodeOptions) {} +}; +} // 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..a5a50fdb3 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h @@ -0,0 +1,63 @@ +#pragma once + +#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); + } + + GainOptions 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 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 dcc9d34a3..40f15b609 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 @@ -7,7 +8,15 @@ 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 4b726ff1b..5f276e667 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,11 @@ 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 fa05c13e2..1ddca0845 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 @@ -128,8 +129,9 @@ std::shared_ptr BaseAudioContext::createStreamer() { } #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; } 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 eba8b3460..2aa92f9dd 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,7 @@ class WorkletSourceNode; class WorkletNode; class WorkletProcessingNode; class StreamerNode; +class GainOptions; class BaseAudioContext { public: @@ -65,7 +66,7 @@ class BaseAudioContext { std::shared_ptr createOscillator(); std::shared_ptr createConstantSource(); std::shared_ptr createStreamer(); - std::shared_ptr createGain(); + std::shared_ptr createGain(std::shared_ptr options); std::shared_ptr createStereoPanner(); std::shared_ptr createBiquadFilter(); std::shared_ptr createBufferSource(bool pitchCorrection); 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 166def7ec..1608ec3e5 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 @@ -6,9 +7,15 @@ namespace audioapi { -GainNode::GainNode(BaseAudioContext *context) : AudioNode(context) { +GainNode::GainNode( + BaseAudioContext *context, + std::shared_ptr options) + : AudioNode(context, std::static_pointer_cast(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 7dcdf0c40..0ed3ec810 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, std::shared_ptr options); [[nodiscard]] std::shared_ptr getGainParam() const; diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 86938d40c..a5a0bc273 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -193,7 +193,7 @@ export default class BaseAudioContext { } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createStereoPanner(): StereoPannerNode { diff --git a/packages/react-native-audio-api/src/core/GainNode.ts b/packages/react-native-audio-api/src/core/GainNode.ts index a216f1365..65a9ead68 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, gainOptions?: TGainOptions) { + const finalOptions: TGainOptions = { + ...GainOptions, + ...gainOptions, + }; + 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/defaults.ts b/packages/react-native-audio-api/src/defaults.ts new file mode 100644 index 000000000..d3eb746df --- /dev/null +++ b/packages/react-native-audio-api/src/defaults.ts @@ -0,0 +1,12 @@ +import { TAudioNodeOptions, TGainOptions } from './types'; + +export const AudioNodeOptions: TAudioNodeOptions = { + channelCount: 2, + channelCountMode: 'max', + channelInterpretation: 'speakers', +}; + +export const GainOptions: TGainOptions = { + ...AudioNodeOptions, + gain: 1, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 8133ddcfd..b49891e7c 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -6,6 +6,7 @@ import { ContextState, OscillatorType, WindowType, + TGainOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -59,7 +60,7 @@ export interface IBaseAudioContext { ): IWorkletProcessingNode; createOscillator(): IOscillatorNode; createConstantSource(): IConstantSourceNode; - createGain(): IGainNode; + createGain(gainOptions: TGainOptions): IGainNode; createStereoPanner(): IStereoPannerNode; createBiquadFilter: () => IBiquadFilterNode; createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index da7d0675b..57731c7e8 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -56,3 +56,13 @@ export interface ConvolverNodeOptions { buffer?: AudioBuffer | null; disableNormalization?: boolean; } + +export type TAudioNodeOptions = { + channelCount?: number; + channelCountMode?: ChannelCountMode; + channelInterpretation?: ChannelInterpretation; +}; + +export type TGainOptions = TAudioNodeOptions & { + gain?: number; +}; From 62ae75f7d6715cb5fc535a8572ed7243be836031 Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 13 Nov 2025 18:23:15 +0100 Subject: [PATCH 02/35] feat: better consistency --- .../cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp | 4 ++-- .../common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 7c6c64182..3303d2fb1 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 @@ -187,9 +187,9 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { auto object = args[0].asObject(runtime); - GainOptions options = + std::shared_ptr options = audioapi::option_parser::parseGainOptions(runtime, object); - auto gain = context_->createGain(std::make_shared(options)); + auto gain = context_->createGain(options); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); } 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 index a5a50fdb3..6f6efc517 100644 --- 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 @@ -48,7 +48,7 @@ namespace audioapi::option_parser { return std::make_shared(options); } - GainOptions parseGainOptions( + std::shared_ptr parseGainOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); @@ -56,7 +56,7 @@ namespace audioapi::option_parser { options.gain = static_cast(optionsObject .getProperty(runtime, "gain") .getNumber()); - return options; + return std::make_shared(options); } } // namespace audioapi::option_parser From 5790ce58a9d26029d629930defd2086bbc076a6c Mon Sep 17 00:00:00 2001 From: michal Date: Fri, 14 Nov 2025 12:49:38 +0100 Subject: [PATCH 03/35] docs: docs proposition --- packages/audiodocs/docs/core/audio-node.mdx | 12 ++++++++++++ packages/audiodocs/docs/effects/gain-node.mdx | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) 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..4606edc31 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 of gain value | + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createGain()`](/docs/core/base-audio-context#creategain) ## Properties From 4fd54875ce85544697ad0b3e85d6bf161f5963c8 Mon Sep 17 00:00:00 2001 From: michal Date: Sun, 16 Nov 2025 14:44:37 +0100 Subject: [PATCH 04/35] feat: stereo panner options --- packages/audiodocs/docs/effects/gain-node.mdx | 2 +- .../audiodocs/docs/effects/stereo-panner-node.mdx | 15 ++++++++++++++- .../HostObjects/BaseAudioContextHostObject.cpp | 13 ++++++++----- .../cpp/audioapi/HostObjects/utils/NodeOptions.h | 6 ++++++ .../HostObjects/utils/NodeOptionsParser.h | 11 +++++++++++ .../common/cpp/audioapi/core/BaseAudioContext.cpp | 5 +++-- .../common/cpp/audioapi/core/BaseAudioContext.h | 5 +++-- .../audioapi/core/effects/StereoPannerNode.cpp | 10 ++++++---- .../cpp/audioapi/core/effects/StereoPannerNode.h | 3 ++- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/StereoPannerNode.ts | 13 ++++++++++++- packages/react-native-audio-api/src/defaults.ts | 8 +++++++- packages/react-native-audio-api/src/interfaces.ts | 5 ++++- packages/react-native-audio-api/src/types.ts | 4 ++++ 14 files changed, 82 insertions(+), 20 deletions(-) diff --git a/packages/audiodocs/docs/effects/gain-node.mdx b/packages/audiodocs/docs/effects/gain-node.mdx index 4606edc31..adfa4eb19 100644 --- a/packages/audiodocs/docs/effects/gain-node.mdx +++ b/packages/audiodocs/docs/effects/gain-node.mdx @@ -53,7 +53,7 @@ Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionod | Parameter | Type | Default | Description | | :---: | :---: | :----: | :---- | -| `gain` | `number` | 1.0 | Number of gain value | +| `gain` | `number` | 1.0 | Number representing gain value | Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createGain()`](/docs/core/base-audio-context#creategain) 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/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index 3303d2fb1..bfab71e91 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 @@ -186,16 +186,19 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { - auto object = args[0].asObject(runtime); - std::shared_ptr options = - audioapi::option_parser::parseGainOptions(runtime, object); - auto gain = context_->createGain(options); + 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); 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 index 64fb7ea32..d8f90cef7 100644 --- 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 @@ -15,4 +15,10 @@ struct GainOptions : AudioNodeOptions { explicit GainOptions(AudioNodeOptions nodeOptions) : AudioNodeOptions(nodeOptions) {} }; + +struct StereoPannerOptions : AudioNodeOptions { + float pan; + explicit StereoPannerOptions(AudioNodeOptions nodeOptions) + : AudioNodeOptions(nodeOptions) {} +}; } // 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 index 6f6efc517..dee971a0b 100644 --- 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 @@ -58,6 +58,17 @@ namespace audioapi::option_parser { .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); + } } // namespace audioapi::option_parser 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 1ddca0845..b9f576806 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 @@ -136,8 +136,9 @@ std::shared_ptr BaseAudioContext::createGain( return gain; } -std::shared_ptr BaseAudioContext::createStereoPanner() { - auto stereoPanner = std::make_shared(this); +std::shared_ptr BaseAudioContext::createStereoPanner( + const std::shared_ptr options) { + auto stereoPanner = std::make_shared(this, options); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; } 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 2aa92f9dd..ffe9ace48 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 @@ -36,6 +36,7 @@ class WorkletNode; class WorkletProcessingNode; class StreamerNode; class GainOptions; +class StereoPannerOptions; class BaseAudioContext { public: @@ -66,8 +67,8 @@ class BaseAudioContext { std::shared_ptr createOscillator(); std::shared_ptr createConstantSource(); std::shared_ptr createStreamer(); - std::shared_ptr createGain(std::shared_ptr options); - std::shared_ptr createStereoPanner(); + std::shared_ptr createGain(const std::shared_ptr options); + std::shared_ptr createStereoPanner(const std::shared_ptr options); std::shared_ptr createBiquadFilter(); std::shared_ptr createBufferSource(bool pitchCorrection); std::shared_ptr createBufferQueueSource(bool pitchCorrection); 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 74532ef2e..0c6f7e3fd 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 @@ -8,10 +9,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 ebde86c4c..12badbafd 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,11 @@ 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/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index a5a0bc273..10d2d866b 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -197,7 +197,7 @@ export default class BaseAudioContext { } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { diff --git a/packages/react-native-audio-api/src/core/StereoPannerNode.ts b/packages/react-native-audio-api/src/core/StereoPannerNode.ts index a8fa77006..307b6a1fa 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,16 @@ import BaseAudioContext from './BaseAudioContext'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor(context: BaseAudioContext, pan: IStereoPannerNode) { + constructor( + context: BaseAudioContext, + stereoPannerOptions?: TSteroPannerOptions + ) { + const finalOptions: TSteroPannerOptions = { + ...SteroPannerOptions, + ...stereoPannerOptions, + }; + 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/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index d3eb746df..01c01a9e7 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -1,4 +1,4 @@ -import { TAudioNodeOptions, TGainOptions } from './types'; +import { TAudioNodeOptions, TGainOptions, TSteroPannerOptions } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { channelCount: 2, @@ -10,3 +10,9 @@ export const GainOptions: TGainOptions = { ...AudioNodeOptions, gain: 1, }; + +export const SteroPannerOptions: TSteroPannerOptions = { + ...AudioNodeOptions, + channelCountMode: 'clamped-max', + pan: 0, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index b49891e7c..2ac7af8c1 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -7,6 +7,7 @@ import { OscillatorType, WindowType, TGainOptions, + TSteroPannerOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -61,7 +62,9 @@ export interface IBaseAudioContext { createOscillator(): IOscillatorNode; createConstantSource(): IConstantSourceNode; createGain(gainOptions: TGainOptions): IGainNode; - createStereoPanner(): IStereoPannerNode; + createStereoPanner( + stereoPannerOptions: TSteroPannerOptions + ): IStereoPannerNode; createBiquadFilter: () => IBiquadFilterNode; createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; createBufferQueueSource: ( diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 57731c7e8..fe7083f8d 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -66,3 +66,7 @@ export type TAudioNodeOptions = { export type TGainOptions = TAudioNodeOptions & { gain?: number; }; + +export type TSteroPannerOptions = TAudioNodeOptions & { + pan?: number; +}; From 5d7cc6958ca377a5db731c829e581e9e85f1278b Mon Sep 17 00:00:00 2001 From: michal Date: Sun, 16 Nov 2025 15:15:52 +0100 Subject: [PATCH 05/35] feat: added web --- .../src/web-core/AudioContext.tsx | 4 ++-- .../react-native-audio-api/src/web-core/GainNode.tsx | 4 +++- .../src/web-core/OfflineAudioContext.tsx | 4 ++-- .../src/web-core/StereoPannerNode.tsx | 10 +++++++++- 4 files changed, 16 insertions(+), 6 deletions(-) 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..324daee32 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -61,11 +61,11 @@ export default class AudioContext implements BaseAudioContext { } 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 { 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..b5941a0d1 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -67,11 +67,11 @@ export default class OfflineAudioContext implements BaseAudioContext { } 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 { 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); } From add1958ad3d625ed20710e6e635c709fdf0d043d Mon Sep 17 00:00:00 2001 From: michal Date: Sun, 16 Nov 2025 17:01:08 +0100 Subject: [PATCH 06/35] feat: convolver options --- .../BaseAudioContextHostObject.cpp | 14 +-- .../audioapi/HostObjects/utils/NodeOptions.h | 10 ++ .../HostObjects/utils/NodeOptionsParser.h | 110 ++++++++++-------- .../cpp/audioapi/core/BaseAudioContext.cpp | 6 +- .../cpp/audioapi/core/BaseAudioContext.h | 3 +- .../audioapi/core/effects/ConvolverNode.cpp | 10 +- .../cpp/audioapi/core/effects/ConvolverNode.h | 3 +- .../cpp/audioapi/core/effects/GainNode.cpp | 2 +- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../src/core/BaseAudioContext.ts | 22 +--- .../src/core/ConvolverNode.ts | 27 ++++- .../react-native-audio-api/src/defaults.ts | 12 +- .../react-native-audio-api/src/interfaces.ts | 6 +- packages/react-native-audio-api/src/types.ts | 5 + 14 files changed, 131 insertions(+), 101 deletions(-) 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 bfab71e91..dafed4cfe 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 @@ -281,16 +281,10 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { } 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); return jsi::Object::createFromHostObject(runtime, convolverHostObject); 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 index d8f90cef7..82bb86fb4 100644 --- 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 @@ -1,7 +1,10 @@ #pragma once +#include + #include #include +#include namespace audioapi { struct AudioNodeOptions { @@ -21,4 +24,11 @@ struct StereoPannerOptions : AudioNodeOptions { explicit StereoPannerOptions(AudioNodeOptions nodeOptions) : AudioNodeOptions(nodeOptions) {} }; + +struct ConvolverOptions : AudioNodeOptions { + std::shared_ptr bus; + bool disableNormalization; + explicit ConvolverOptions(AudioNodeOptions nodeOptions) + : AudioNodeOptions(nodeOptions) {} +}; } // 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 index dee971a0b..34459fff1 100644 --- 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 @@ -2,73 +2,83 @@ #include #include +#include #include #include #include -#include #include +#include namespace audioapi::option_parser { - std::shared_ptr parseAudioNodeOptions( +std::shared_ptr parseAudioNodeOptions( jsi::Runtime &runtime, - const jsi::Object &optionsObject - ) { - AudioNodeOptions options; - - options.channelCount = - static_cast(optionsObject - .getProperty(runtime, "channelCount") - .getNumber()); + const jsi::Object &optionsObject) { + AudioNodeOptions options; - auto channelCountModeStr = optionsObject - .getProperty(runtime, "channelCountMode") - .asString(runtime) - .utf8(runtime); + options.channelCount = static_cast( + optionsObject.getProperty(runtime, "channelCount").getNumber()); - 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 channelCountModeStr = + optionsObject.getProperty(runtime, "channelCountMode") + .asString(runtime) + .utf8(runtime); - auto channelInterpretationStr = optionsObject - .getProperty(runtime, "channelInterpretation") - .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; + } - if (channelInterpretationStr == "speakers") { - options.channelInterpretation = ChannelInterpretation::SPEAKERS; - } else if (channelInterpretationStr == "discrete") { - options.channelInterpretation = ChannelInterpretation::DISCRETE; - } + auto channelInterpretationStr = + optionsObject.getProperty(runtime, "channelInterpretation") + .asString(runtime) + .utf8(runtime); - return std::make_shared(options); + if (channelInterpretationStr == "speakers") { + options.channelInterpretation = ChannelInterpretation::SPEAKERS; + } else if (channelInterpretationStr == "discrete") { + options.channelInterpretation = ChannelInterpretation::DISCRETE; } - 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); - } + return std::make_shared(options); +} - std::shared_ptr parseStereoPannerOptions ( +std::shared_ptr parseGainOptions( 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); - } -} // namespace audioapi::option_parser + 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); +} +} // namespace audioapi::option_parser 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 b9f576806..0ab9351bc 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 @@ -187,10 +187,8 @@ std::shared_ptr BaseAudioContext::createAnalyser() { } std::shared_ptr BaseAudioContext::createConvolver( - std::shared_ptr buffer, - bool disableNormalization) { - auto convolver = - std::make_shared(this, buffer, disableNormalization); + const 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 ffe9ace48..9fb28f715 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 @@ -37,6 +37,7 @@ class WorkletProcessingNode; class StreamerNode; class GainOptions; class StereoPannerOptions; +class ConvolverOptions; class BaseAudioContext { public: @@ -79,7 +80,7 @@ class BaseAudioContext { bool disableNormalization, int length); std::shared_ptr createAnalyser(); - std::shared_ptr createConvolver(std::shared_ptr buffer, bool disableNormalization); + std::shared_ptr createConvolver(const 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/effects/ConvolverNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp index 28f457f8c..40a0359d3 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 @@ -11,9 +12,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,9 +23,9 @@ ConvolverNode::ConvolverNode( 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 d1208bea0..be78df27c 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 @@ -16,10 +16,11 @@ namespace audioapi { class AudioBus; class AudioBuffer; +class ConvolverOptions; class ConvolverNode : public AudioNode { public: - explicit ConvolverNode(BaseAudioContext *context, const std::shared_ptr& buffer, bool disableNormalization); + explicit ConvolverNode(BaseAudioContext *context, 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 1608ec3e5..97854ff79 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 @@ -10,7 +10,7 @@ namespace audioapi { GainNode::GainNode( BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, std::static_pointer_cast(options)) { + : AudioNode(context, options) { gainParam_ = std::make_shared( options->gain, MOST_NEGATIVE_SINGLE_FLOAT, 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 0ed3ec810..fc15c5f78 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 @@ -12,7 +12,7 @@ class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(BaseAudioContext *context, std::shared_ptr options); + explicit GainNode(BaseAudioContext *context, const std::shared_ptr options); [[nodiscard]] std::shared_ptr getGainParam() const; diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 10d2d866b..aa8af2561 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -5,7 +5,6 @@ import { ContextState, PeriodicWaveConstraints, AudioWorkletRuntime, - ConvolverNodeOptions, } from '../types'; import { assertWorkletsEnabled, workletsModule } from '../utils'; import WorkletSourceNode from './WorkletSourceNode'; @@ -276,24 +275,7 @@ export default class BaseAudioContext { return new AnalyserNode(this, this.context.createAnalyser()); } - 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/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index a2b71592f..2cac2629c 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, convolverOptions?: TConvolverOptions) { + const finalOptions: TConvolverOptions = { + ...ConvolverOptions, + ...convolverOptions, + }; + 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/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 01c01a9e7..12fd42f1b 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -1,4 +1,9 @@ -import { TAudioNodeOptions, TGainOptions, TSteroPannerOptions } from './types'; +import { + TAudioNodeOptions, + TGainOptions, + TSteroPannerOptions, + TConvolverOptions, +} from './types'; export const AudioNodeOptions: TAudioNodeOptions = { channelCount: 2, @@ -16,3 +21,8 @@ export const SteroPannerOptions: TSteroPannerOptions = { channelCountMode: 'clamped-max', pan: 0, }; + +export const ConvolverOptions: TConvolverOptions = { + ...AudioNodeOptions, + disableNormalization: false, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 2ac7af8c1..dcac2379b 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -8,6 +8,7 @@ import { WindowType, TGainOptions, TSteroPannerOptions, + TConvolverOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -81,10 +82,7 @@ export interface IBaseAudioContext { disableNormalization: boolean ) => IPeriodicWave; createAnalyser: () => IAnalyserNode; - createConvolver: ( - buffer: IAudioBuffer | undefined, - disableNormalization: boolean - ) => IConvolverNode; + createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; createStreamer: () => IStreamerNode; } diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index fe7083f8d..bcf48f5d3 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -70,3 +70,8 @@ export type TGainOptions = TAudioNodeOptions & { export type TSteroPannerOptions = TAudioNodeOptions & { pan?: number; }; + +export type TConvolverOptions = TAudioNodeOptions & { + buffer?: AudioBuffer; + disableNormalization?: boolean; +}; From 022683be60c0d156f032c640f86de09ca851c310 Mon Sep 17 00:00:00 2001 From: michal Date: Mon, 17 Nov 2025 13:02:09 +0100 Subject: [PATCH 07/35] test: tests --- .../audioapi/HostObjects/utils/NodeOptions.h | 20 +++++++----------- .../common/cpp/test/src/GainTest.cpp | 6 ++++-- .../common/cpp/test/src/StereoPannerTest.cpp | 6 ++++-- packages/react-native-audio-api/src/types.ts | 21 +++++++------------ 4 files changed, 23 insertions(+), 30 deletions(-) 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 index 82bb86fb4..0ddd28b65 100644 --- 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 @@ -8,27 +8,21 @@ namespace audioapi { struct AudioNodeOptions { - int channelCount; - ChannelCountMode channelCountMode; - ChannelInterpretation channelInterpretation; + int channelCount = 2; + ChannelCountMode channelCountMode = ChannelCountMode::MAX; + ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS; }; struct GainOptions : AudioNodeOptions { - float gain; - explicit GainOptions(AudioNodeOptions nodeOptions) - : AudioNodeOptions(nodeOptions) {} + float gain = 1.0f; }; struct StereoPannerOptions : AudioNodeOptions { - float pan; - explicit StereoPannerOptions(AudioNodeOptions nodeOptions) - : AudioNodeOptions(nodeOptions) {} + float pan = 0.0f; }; struct ConvolverOptions : AudioNodeOptions { - std::shared_ptr bus; - bool disableNormalization; - explicit ConvolverOptions(AudioNodeOptions nodeOptions) - : AudioNodeOptions(nodeOptions) {} + std::shared_ptr bus = nullptr; + bool disableNormalization = false; }; } // namespace audioapi 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 30f00c385..4d5dc51a8 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 @@ -23,7 +24,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); @@ -37,7 +39,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 62a1ea268..68bda8990 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,7 @@ class StereoPannerTest : public ::testing::Test { class TestableStereoPannerNode : public StereoPannerNode { public: explicit TestableStereoPannerNode(BaseAudioContext *context) - : StereoPannerNode(context) {} + : StereoPannerNode(context, std::make_shared()) {} void setPanParam(float value) { getPanParam()->setValue(value); @@ -38,7 +39,8 @@ 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/types.ts b/packages/react-native-audio-api/src/types.ts index bcf48f5d3..483f0058b 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -52,26 +52,21 @@ export interface AudioBufferBaseSourceNodeOptions { export type ProcessorMode = 'processInPlace' | 'processThrough'; -export interface ConvolverNodeOptions { - buffer?: AudioBuffer | null; - disableNormalization?: boolean; -} - -export type TAudioNodeOptions = { +export interface TAudioNodeOptions { channelCount?: number; channelCountMode?: ChannelCountMode; channelInterpretation?: ChannelInterpretation; -}; +} -export type TGainOptions = TAudioNodeOptions & { +export interface TGainOptions extends TAudioNodeOptions { gain?: number; -}; +} -export type TSteroPannerOptions = TAudioNodeOptions & { +export interface TSteroPannerOptions extends TAudioNodeOptions { pan?: number; -}; +} -export type TConvolverOptions = TAudioNodeOptions & { +export interface TConvolverOptions extends TAudioNodeOptions { buffer?: AudioBuffer; disableNormalization?: boolean; -}; +} From 0a539d161f88b26c565175e6b95c0ffc7f1dc1c0 Mon Sep 17 00:00:00 2001 From: michal Date: Mon, 17 Nov 2025 13:49:21 +0100 Subject: [PATCH 08/35] feat: constant source options --- .../HostObjects/BaseAudioContextHostObject.cpp | 5 ++++- .../cpp/audioapi/HostObjects/utils/NodeOptions.h | 4 ++++ .../audioapi/HostObjects/utils/NodeOptionsParser.h | 12 ++++++++++-- .../common/cpp/audioapi/core/BaseAudioContext.cpp | 5 +++-- .../common/cpp/audioapi/core/BaseAudioContext.h | 3 ++- .../cpp/audioapi/core/sources/ConstantSourceNode.cpp | 10 ++++++++-- .../cpp/audioapi/core/sources/ConstantSourceNode.h | 3 ++- .../common/cpp/test/src/ConstantSourceTest.cpp | 7 +++++-- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/ConstantSourceNode.ts | 10 +++++++++- .../react-native-audio-api/src/core/ConvolverNode.ts | 4 ++-- packages/react-native-audio-api/src/core/GainNode.ts | 4 ++-- .../src/core/StereoPannerNode.ts | 7 ++----- packages/react-native-audio-api/src/defaults.ts | 5 +++++ packages/react-native-audio-api/src/interfaces.ts | 5 ++++- packages/react-native-audio-api/src/types.ts | 4 ++++ 16 files changed, 67 insertions(+), 23 deletions(-) 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 dafed4cfe..e67e3708a 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 @@ -179,7 +179,10 @@ 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); 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 index 0ddd28b65..8348adc2e 100644 --- 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 @@ -25,4 +25,8 @@ struct ConvolverOptions : AudioNodeOptions { std::shared_ptr bus = nullptr; bool disableNormalization = false; }; + +struct ConstantSourceOptions { + float offset = 1.0f; +}; } // 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 index 34459fff1..ea4c4d157 100644 --- 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 @@ -8,7 +8,7 @@ #include #include -#include +#include namespace audioapi::option_parser { std::shared_ptr parseAudioNodeOptions( @@ -81,4 +81,12 @@ std::shared_ptr parseConvolverOptions( } return std::make_shared(options); } -} // namespace audioapi::option_parser + +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); +} +}// namespace audioapi::option_parser 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 0ab9351bc..e11d53583 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 @@ -115,8 +115,9 @@ std::shared_ptr BaseAudioContext::createOscillator() { return oscillator; } -std::shared_ptr BaseAudioContext::createConstantSource() { - auto constantSource = std::make_shared(this); +std::shared_ptr BaseAudioContext::createConstantSource( + const std::shared_ptr options) { + auto constantSource = std::make_shared(this, options); nodeManager_->addSourceNode(constantSource); return constantSource; } 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 9fb28f715..502134cc4 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 @@ -38,6 +38,7 @@ class StreamerNode; class GainOptions; class StereoPannerOptions; class ConvolverOptions; +class ConstantSourceOptions; class BaseAudioContext { public: @@ -66,7 +67,7 @@ class BaseAudioContext { std::weak_ptr runtime, bool shouldLockRuntime = true); std::shared_ptr createOscillator(); - std::shared_ptr createConstantSource(); + std::shared_ptr createConstantSource(const std::shared_ptr options); std::shared_ptr createStreamer(); std::shared_ptr createGain(const std::shared_ptr options); std::shared_ptr createStereoPanner(const std::shared_ptr options); 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 ed0f12d3b..c64b5dec3 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 @@ -5,10 +6,15 @@ #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 f0b520a16..dd5a719e9 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,11 @@ 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/test/src/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp index 7f16ab392..2cf867e3a 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) {} + : ConstantSourceNode(context, std::make_shared()) { + } void setOffsetParam(float value) { getOffsetParam()->setValue(value); @@ -38,7 +40,8 @@ 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/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index aa8af2561..40ec3b953 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -188,7 +188,7 @@ export default class BaseAudioContext { } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { 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 2cac2629c..d942cc77b 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -7,10 +7,10 @@ import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; export default class ConvolverNode extends AudioNode { - constructor(context: BaseAudioContext, convolverOptions?: TConvolverOptions) { + constructor(context: BaseAudioContext, options?: TConvolverOptions) { const finalOptions: TConvolverOptions = { ...ConvolverOptions, - ...convolverOptions, + ...options, }; if (finalOptions.buffer) { const numberOfChannels = finalOptions.buffer.numberOfChannels; diff --git a/packages/react-native-audio-api/src/core/GainNode.ts b/packages/react-native-audio-api/src/core/GainNode.ts index 65a9ead68..1c2b8f93b 100644 --- a/packages/react-native-audio-api/src/core/GainNode.ts +++ b/packages/react-native-audio-api/src/core/GainNode.ts @@ -8,10 +8,10 @@ import BaseAudioContext from './BaseAudioContext'; export default class GainNode extends AudioNode { readonly gain: AudioParam; - constructor(context: BaseAudioContext, gainOptions?: TGainOptions) { + constructor(context: BaseAudioContext, options?: TGainOptions) { const finalOptions: TGainOptions = { ...GainOptions, - ...gainOptions, + ...options, }; const gainNode: IGainNode = context.context.createGain(finalOptions); super(context, gainNode); diff --git a/packages/react-native-audio-api/src/core/StereoPannerNode.ts b/packages/react-native-audio-api/src/core/StereoPannerNode.ts index 307b6a1fa..040e9f840 100644 --- a/packages/react-native-audio-api/src/core/StereoPannerNode.ts +++ b/packages/react-native-audio-api/src/core/StereoPannerNode.ts @@ -8,13 +8,10 @@ import BaseAudioContext from './BaseAudioContext'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor( - context: BaseAudioContext, - stereoPannerOptions?: TSteroPannerOptions - ) { + constructor(context: BaseAudioContext, options?: TSteroPannerOptions) { const finalOptions: TSteroPannerOptions = { ...SteroPannerOptions, - ...stereoPannerOptions, + ...options, }; const pan: IStereoPannerNode = context.context.createStereoPanner(finalOptions); diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 12fd42f1b..7ed81d960 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -3,6 +3,7 @@ import { TGainOptions, TSteroPannerOptions, TConvolverOptions, + TConstantSourceOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -26,3 +27,7 @@ export const ConvolverOptions: TConvolverOptions = { ...AudioNodeOptions, disableNormalization: false, }; + +export const ConstantSourceOptions: TConstantSourceOptions = { + offset: 1, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index dcac2379b..fdfb208ad 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -9,6 +9,7 @@ import { TGainOptions, TSteroPannerOptions, TConvolverOptions, + TConstantSourceOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -61,7 +62,9 @@ export interface IBaseAudioContext { shouldUseUiRuntime: boolean ): IWorkletProcessingNode; createOscillator(): IOscillatorNode; - createConstantSource(): IConstantSourceNode; + createConstantSource( + constantSourceOptions: TConstantSourceOptions + ): IConstantSourceNode; createGain(gainOptions: TGainOptions): IGainNode; createStereoPanner( stereoPannerOptions: TSteroPannerOptions diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 483f0058b..de142df50 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -70,3 +70,7 @@ export interface TConvolverOptions extends TAudioNodeOptions { buffer?: AudioBuffer; disableNormalization?: boolean; } + +export interface TConstantSourceOptions { + offset?: number; +} From a052c456b2cc5181bc417950f503f385fb5edabd Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 18 Nov 2025 16:54:32 +0100 Subject: [PATCH 09/35] feat: web for convolver --- .../BaseAudioContextHostObject.cpp | 2 +- packages/react-native-audio-api/src/types.ts | 7 +++++- .../src/web-core/AudioContext.tsx | 24 ++----------------- .../src/web-core/ConvolverNode.tsx | 16 ++++++------- .../src/web-core/OfflineAudioContext.tsx | 24 ++----------------- 5 files changed, 19 insertions(+), 54 deletions(-) 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 7ce90741d..c6b74471f 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 @@ -20,8 +20,8 @@ #include #include -#include #include +#include namespace audioapi { diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index de142df50..0f14284a7 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -67,10 +67,15 @@ export interface TSteroPannerOptions extends TAudioNodeOptions { } export interface TConvolverOptions extends TAudioNodeOptions { - buffer?: AudioBuffer; + buffer?: AudioBuffer | null; disableNormalization?: boolean; } +export interface TWebConvolverOptions { + buffer?: globalThis.AudioBuffer | null; + normalize?: boolean; +} + export interface TConstantSourceOptions { offset?: number; } 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 324daee32..9165a8154 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -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'; @@ -72,27 +71,8 @@ export default class AudioContext implements BaseAudioContext { 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 - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } async createBufferSource( 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/OfflineAudioContext.tsx b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx index b5941a0d1..8d71b85cb 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -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; @@ -78,27 +77,8 @@ export default class OfflineAudioContext implements BaseAudioContext { 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 - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } async createBufferSource( From a47447b75f968d5e3dc2872f9f175273516d35e2 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 19 Nov 2025 10:41:40 +0100 Subject: [PATCH 10/35] feat: periodic wave --- packages/react-native-audio-api/src/api.ts | 2 +- .../react-native-audio-api/src/api.web.ts | 2 +- .../src/core/BaseAudioContext.ts | 8 +-- .../src/core/PeriodicWave.ts | 56 ++++++++++++++++++- .../react-native-audio-api/src/defaults.ts | 5 ++ packages/react-native-audio-api/src/types.ts | 13 +++-- .../src/web-core/AudioContext.tsx | 6 +- .../src/web-core/BaseAudioContext.tsx | 2 +- .../src/web-core/OfflineAudioContext.tsx | 5 +- .../src/web-core/PeriodicWave.tsx | 15 ++++- 10 files changed, 89 insertions(+), 25 deletions(-) diff --git a/packages/react-native-audio-api/src/api.ts b/packages/react-native-audio-api/src/api.ts index 9a575300d..6f7500e01 100644 --- a/packages/react-native-audio-api/src/api.ts +++ b/packages/react-native-audio-api/src/api.ts @@ -78,6 +78,7 @@ 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, @@ -86,7 +87,6 @@ export { 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..bdf1474c0 100644 --- a/packages/react-native-audio-api/src/api.web.ts +++ b/packages/react-native-audio-api/src/api.web.ts @@ -14,6 +14,7 @@ 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'; @@ -24,7 +25,6 @@ export { ChannelInterpretation, ContextState, WindowType, - PeriodicWaveConstraints, } from './types'; export { diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 40ec3b953..9c0e4b724 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -3,7 +3,6 @@ import { IBaseAudioContext } from '../interfaces'; import { AudioBufferBaseSourceNodeOptions, ContextState, - PeriodicWaveConstraints, AudioWorkletRuntime, } from '../types'; import { assertWorkletsEnabled, workletsModule } from '../utils'; @@ -263,12 +262,7 @@ 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 { diff --git a/packages/react-native-audio-api/src/core/PeriodicWave.ts b/packages/react-native-audio-api/src/core/PeriodicWave.ts index b00c4505c..2452f7ba3 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -1,10 +1,62 @@ 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 + ); + console.log('finalOptions', finalOptions); + this.periodicWave = context.context.createPeriodicWave( + finalOptions.real, + finalOptions.imag, + finalOptions.disableNormalization! + ); } } diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 7ed81d960..d1dbd5c3e 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -4,6 +4,7 @@ import { TSteroPannerOptions, TConvolverOptions, TConstantSourceOptions, + TPeriodicWaveConstraints, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -31,3 +32,7 @@ export const ConvolverOptions: TConvolverOptions = { export const ConstantSourceOptions: TConstantSourceOptions = { offset: 1, }; + +export const PeriodicWaveConstraints: TPeriodicWaveConstraints = { + disableNormalization: false, +}; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 0f14284a7..7a86b0286 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -25,10 +25,6 @@ export type OscillatorType = | 'triangle' | 'custom'; -export interface PeriodicWaveConstraints { - disableNormalization: boolean; -} - export interface AudioContextOptions { sampleRate?: number; } @@ -79,3 +75,12 @@ export interface TWebConvolverOptions { export interface TConstantSourceOptions { offset?: number; } + +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/AudioContext.tsx b/packages/react-native-audio-api/src/web-core/AudioContext.tsx index 9165a8154..cad51bd90 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -1,6 +1,5 @@ import { ContextState, - PeriodicWaveConstraints, AudioContextOptions, AudioBufferBaseSourceNodeOptions, } from '../types'; @@ -19,6 +18,7 @@ import ConvolverNode from './ConvolverNode'; import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConstantSourceNode from './ConstantSourceNode'; +import { PeriodicWaveConstraints } from '../defaults'; export default class AudioContext implements BaseAudioContext { readonly context: globalThis.AudioContext; @@ -132,9 +132,7 @@ export default class AudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { 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/OfflineAudioContext.tsx b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx index 8d71b85cb..146679ec1 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -1,6 +1,5 @@ import { ContextState, - PeriodicWaveConstraints, OfflineAudioContextOptions, AudioBufferBaseSourceNodeOptions, } from '../types'; @@ -138,9 +137,7 @@ export default class OfflineAudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { 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; } } From 1aa64432271dbe861559583a5ab212da1f01083f Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 19 Nov 2025 11:23:27 +0100 Subject: [PATCH 11/35] feat: analyser node --- .../AudioVisualizer/AudioVisualizer.tsx | 5 +--- .../BaseAudioContextHostObject.cpp | 5 +++- .../audioapi/HostObjects/utils/NodeOptions.h | 7 ++++++ .../HostObjects/utils/NodeOptionsParser.h | 15 ++++++++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 13 ++++++----- .../cpp/audioapi/core/BaseAudioContext.h | 13 ++++++----- .../audioapi/core/analysis/AnalyserNode.cpp | 15 +++++++----- .../cpp/audioapi/core/analysis/AnalyserNode.h | 3 ++- .../src/core/AnalyserNode.ts | 23 ++++++++++++++++++- .../src/core/BaseAudioContext.ts | 2 +- .../react-native-audio-api/src/defaults.ts | 9 ++++++++ .../react-native-audio-api/src/interfaces.ts | 3 ++- packages/react-native-audio-api/src/types.ts | 7 ++++++ .../src/web-core/AnalyserNode.tsx | 21 ++++++++++++----- .../src/web-core/AudioContext.tsx | 3 +-- .../src/web-core/OfflineAudioContext.tsx | 2 +- 16 files changed, 110 insertions(+), 36 deletions(-) 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/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index c6b74471f..a6810a9dc 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 @@ -253,7 +253,10 @@ 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); } 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 index 64a99cfad..dc104e0fb 100644 --- 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 @@ -29,4 +29,11 @@ struct ConvolverOptions : AudioNodeOptions { struct ConstantSourceOptions { float offset = 1.0f; }; + +struct AnalyserOptions : AudioNodeOptions { + int fftSize = 2048; + float minDecibels = -100.0f; + float maxDecibels = -30.0f; + float smoothingTimeConstant = 0.8f; +}; } // 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 index 0c5bbd76c..a70c1a94e 100644 --- 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 @@ -83,4 +83,19 @@ std::shared_ptr parseConstantSourceOptions( 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); +} } // namespace audioapi::option_parser 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 a75747c61..3a681843d 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 @@ -66,7 +66,7 @@ double BaseAudioContext::getCurrentTime() const { return destination_->getCurrentTime(); } -std::shared_ptr BaseAudioContext::getDestination() { +std::shared_ptr BaseAudioContext::getDestination() const { return destination_; } @@ -117,7 +117,7 @@ std::shared_ptr BaseAudioContext::createOscillator() { } std::shared_ptr BaseAudioContext::createConstantSource( - const std::shared_ptr options) { + std::shared_ptr options) { auto constantSource = std::make_shared(this, options); nodeManager_->addSourceNode(constantSource); return constantSource; @@ -138,7 +138,7 @@ std::shared_ptr BaseAudioContext::createGain(std::shared_ptr BaseAudioContext::createStereoPanner( - const std::shared_ptr options) { + std::shared_ptr options) { auto stereoPanner = std::make_shared(this, options); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; @@ -175,14 +175,15 @@ 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( - const std::shared_ptr options) { + 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 0b72f638a..dd470a497 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 @@ -39,6 +39,7 @@ class GainOptions; class StereoPannerOptions; class ConvolverOptions; class ConstantSourceOptions; +class AnalyserOptions; class BaseAudioContext { public: @@ -51,7 +52,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( @@ -70,11 +71,11 @@ class BaseAudioContext { bool shouldLockRuntime = true); std::shared_ptr createOscillator(); std::shared_ptr createConstantSource( - const std::shared_ptr options); + std::shared_ptr options); std::shared_ptr createStreamer(); - std::shared_ptr createGain(const std::shared_ptr options); + std::shared_ptr createGain(std::shared_ptr options); std::shared_ptr createStereoPanner( - const std::shared_ptr options); + std::shared_ptr options); std::shared_ptr createBiquadFilter(); std::shared_ptr createBufferSource(bool pitchCorrection); std::shared_ptr createBufferQueueSource(bool pitchCorrection); @@ -84,8 +85,8 @@ class BaseAudioContext { const std::vector> &complexData, bool disableNormalization, int length); - std::shared_ptr createAnalyser(); - std::shared_ptr createConvolver(const std::shared_ptr options); + 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/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/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 9c0e4b724..00be97846 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -266,7 +266,7 @@ export default class BaseAudioContext { } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } createConvolver(): ConvolverNode { diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index d1dbd5c3e..627611784 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -5,6 +5,7 @@ import { TConvolverOptions, TConstantSourceOptions, TPeriodicWaveConstraints, + TAnalyserOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -24,6 +25,14 @@ export const SteroPannerOptions: TSteroPannerOptions = { pan: 0, }; +export const AnalyserOptions: TAnalyserOptions = { + ...AudioNodeOptions, + fftSize: 2048, + minDecibels: -100, + maxDecibels: -30, + smoothingTimeConstant: 0.8, +}; + export const ConvolverOptions: TConvolverOptions = { ...AudioNodeOptions, disableNormalization: false, diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 3777bb496..c4a84f66b 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -10,6 +10,7 @@ import { TSteroPannerOptions, TConvolverOptions, TConstantSourceOptions, + TAnalyserOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -84,7 +85,7 @@ export interface IBaseAudioContext { imag: Float32Array, disableNormalization: boolean ) => IPeriodicWave; - createAnalyser: () => IAnalyserNode; + createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; createStreamer: () => IStreamerNode; } diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 7a86b0286..b8c3df165 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -62,6 +62,13 @@ export interface TSteroPannerOptions extends TAudioNodeOptions { pan?: number; } +export interface TAnalyserOptions extends TAudioNodeOptions { + fftSize?: number; + minDecibels?: number; + maxDecibels?: number; + smoothingTimeConstant?: number; +} + export interface TConvolverOptions extends TAudioNodeOptions { buffer?: AudioBuffer | null; disableNormalization?: boolean; 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 cad51bd90..089dd3e45 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -18,7 +18,6 @@ import ConvolverNode from './ConvolverNode'; import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConstantSourceNode from './ConstantSourceNode'; -import { PeriodicWaveConstraints } from '../defaults'; export default class AudioContext implements BaseAudioContext { readonly context: globalThis.AudioContext; @@ -136,7 +135,7 @@ export default class AudioContext implements BaseAudioContext { } 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/OfflineAudioContext.tsx b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx index 146679ec1..eb3e0cde9 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -141,7 +141,7 @@ export default class OfflineAudioContext implements BaseAudioContext { } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } async decodeAudioDataSource(source: string): Promise { From 3b0b1b781944fb1e7f98aeb0379e8d9be2a07937 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 19 Nov 2025 12:36:18 +0100 Subject: [PATCH 12/35] feat: biquad filter --- .../BaseAudioContextHostObject.cpp | 5 ++- .../audioapi/HostObjects/utils/NodeOptions.h | 10 ++++++ .../HostObjects/utils/NodeOptionsParser.h | 35 +++++++++++++++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 5 +-- .../cpp/audioapi/core/BaseAudioContext.h | 4 ++- .../core/effects/BiquadFilterNode.cpp | 19 +++++----- .../audioapi/core/effects/BiquadFilterNode.h | 5 ++- packages/react-native-audio-api/src/api.ts | 1 - .../react-native-audio-api/src/api.web.ts | 1 - .../src/core/BaseAudioContext.ts | 2 +- .../src/core/BiquadFilterNode.ts | 11 ++++-- .../react-native-audio-api/src/defaults.ts | 10 ++++++ .../react-native-audio-api/src/interfaces.ts | 6 ++-- packages/react-native-audio-api/src/types.ts | 10 +++++- .../src/web-core/AudioContext.tsx | 4 +-- .../src/web-core/BiquadFilterNode.tsx | 8 +++-- .../src/web-core/ConstantSourceNode.tsx | 4 ++- .../src/web-core/OfflineAudioContext.tsx | 4 +-- 18 files changed, 116 insertions(+), 28 deletions(-) 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 a6810a9dc..e70be7a16 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 @@ -195,7 +195,10 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { } 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); } 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 index dc104e0fb..a98a5f7c4 100644 --- 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 @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -36,4 +37,13 @@ struct AnalyserOptions : AudioNodeOptions { float maxDecibels = -30.0f; float smoothingTimeConstant = 0.8f; }; + +struct BiquadFilterOptions : AudioNodeOptions { + BiquadFilterType type = + BiquadFilterType::LOWPASS; // Uncomment and define BiquadFilterType enum as needed + float frequency = 350.0f; + float detune = 0.0f; + float Q = 1.0f; + float gain = 0.0f; +}; } // 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 index a70c1a94e..56ff61528 100644 --- 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 @@ -98,4 +98,39 @@ std::shared_ptr parseAnalyserOptions( 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); +} } // namespace audioapi::option_parser 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 3a681843d..94f20bd06 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 @@ -144,8 +144,9 @@ std::shared_ptr BaseAudioContext::createStereoPanner( 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; } 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 dd470a497..3dbc8c92a 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 @@ -40,6 +40,7 @@ class StereoPannerOptions; class ConvolverOptions; class ConstantSourceOptions; class AnalyserOptions; +class BiquadFilterOptions; class BaseAudioContext { public: @@ -76,7 +77,8 @@ class BaseAudioContext { std::shared_ptr createGain(std::shared_ptr options); std::shared_ptr createStereoPanner( std::shared_ptr options); - std::shared_ptr createBiquadFilter(); + std::shared_ptr createBiquadFilter( + std::shared_ptr options); std::shared_ptr createBufferSource(bool pitchCorrection); std::shared_ptr createBufferQueueSource(bool pitchCorrection); static std::shared_ptr 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/src/api.ts b/packages/react-native-audio-api/src/api.ts index 6f7500e01..c667c0ff6 100644 --- a/packages/react-native-audio-api/src/api.ts +++ b/packages/react-native-audio-api/src/api.ts @@ -82,7 +82,6 @@ export { default as PeriodicWave } from './core/PeriodicWave'; export { OscillatorType, - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, diff --git a/packages/react-native-audio-api/src/api.web.ts b/packages/react-native-audio-api/src/api.web.ts index bdf1474c0..09259d993 100644 --- a/packages/react-native-audio-api/src/api.web.ts +++ b/packages/react-native-audio-api/src/api.web.ts @@ -20,7 +20,6 @@ export * from './web-core/custom'; export { OscillatorType, - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 00be97846..a12b62937 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -199,7 +199,7 @@ export default class BaseAudioContext { } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } createBufferSource( 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/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 627611784..064ea0220 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -6,6 +6,7 @@ import { TConstantSourceOptions, TPeriodicWaveConstraints, TAnalyserOptions, + TBiquadFilterOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -33,6 +34,15 @@ export const AnalyserOptions: TAnalyserOptions = { 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, diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index c4a84f66b..7cd746a55 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -1,6 +1,5 @@ import { AudioEventCallback, AudioEventName } from './events/types'; import { - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, @@ -11,6 +10,7 @@ import { TConvolverOptions, TConstantSourceOptions, TAnalyserOptions, + TBiquadFilterOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -70,7 +70,9 @@ export interface IBaseAudioContext { createStereoPanner( stereoPannerOptions: TSteroPannerOptions ): IStereoPannerNode; - createBiquadFilter: () => IBiquadFilterNode; + createBiquadFilter: ( + biquadFilterOptions: TBiquadFilterOptions + ) => IBiquadFilterNode; createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; createBufferQueueSource: ( pitchCorrection: boolean diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index b8c3df165..3be45fc91 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -4,7 +4,7 @@ export type ChannelCountMode = 'max' | 'clamped-max' | 'explicit'; export type ChannelInterpretation = 'speakers' | 'discrete'; -export type BiquadFilterType = +type BiquadFilterType = | 'lowpass' | 'highpass' | 'bandpass' @@ -69,6 +69,14 @@ export interface TAnalyserOptions extends TAudioNodeOptions { smoothingTimeConstant?: number; } +export interface TBiquadFilterOptions extends TAudioNodeOptions { + type?: BiquadFilterType; + frequency?: number; + detune?: number; + Q?: number; + gain?: number; +} + export interface TConvolverOptions extends TAudioNodeOptions { buffer?: AudioBuffer | null; disableNormalization?: boolean; 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 089dd3e45..2cd837244 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -55,7 +55,7 @@ export default class AudioContext implements BaseAudioContext { } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { @@ -67,7 +67,7 @@ export default class AudioContext implements BaseAudioContext { } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } createConvolver(): ConvolverNode { 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/OfflineAudioContext.tsx b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx index eb3e0cde9..ee3cc4ae8 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -61,7 +61,7 @@ export default class OfflineAudioContext implements BaseAudioContext { } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { @@ -73,7 +73,7 @@ export default class OfflineAudioContext implements BaseAudioContext { } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } createConvolver(): ConvolverNode { From 1dcdaa66aafe0b62da5d95b0fc91d17ec2a85ded Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 20 Nov 2025 16:10:44 +0100 Subject: [PATCH 13/35] feat: oscillator --- .../BaseAudioContextHostObject.cpp | 5 ++- .../audioapi/HostObjects/utils/NodeOptions.h | 12 +++++-- .../HostObjects/utils/NodeOptionsParser.h | 35 +++++++++++++++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 5 +-- .../cpp/audioapi/core/BaseAudioContext.h | 3 +- .../audioapi/core/sources/OscillatorNode.cpp | 21 ++++++++--- .../audioapi/core/sources/OscillatorNode.h | 3 +- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/OscillatorNode.ts | 21 +++++++++-- .../src/core/PeriodicWave.ts | 1 - .../react-native-audio-api/src/defaults.ts | 8 +++++ .../react-native-audio-api/src/interfaces.ts | 3 +- packages/react-native-audio-api/src/types.ts | 8 +++++ .../src/web-core/AudioContext.tsx | 2 +- .../src/web-core/OfflineAudioContext.tsx | 2 +- .../src/web-core/OscillatorNode.tsx | 5 +-- 16 files changed, 115 insertions(+), 21 deletions(-) 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 e70be7a16..24f4b637b 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 @@ -154,7 +154,10 @@ 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); } 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 index a98a5f7c4..751a0096a 100644 --- 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 @@ -2,10 +2,12 @@ #include +#include #include #include #include #include +#include namespace audioapi { struct AudioNodeOptions { @@ -39,11 +41,17 @@ struct AnalyserOptions : AudioNodeOptions { }; struct BiquadFilterOptions : AudioNodeOptions { - BiquadFilterType type = - BiquadFilterType::LOWPASS; // Uncomment and define BiquadFilterType enum as needed + 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; +}; } // 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 index 56ff61528..6abc0f415 100644 --- 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 @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -133,4 +134,38 @@ std::shared_ptr parseBiquadFilterOptions( 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); +} } // namespace audioapi::option_parser 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 94f20bd06..d76c312f5 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 @@ -110,8 +110,9 @@ 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; } 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 3dbc8c92a..d0d0dc398 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 @@ -41,6 +41,7 @@ class ConvolverOptions; class ConstantSourceOptions; class AnalyserOptions; class BiquadFilterOptions; +class OscillatorOptions; class BaseAudioContext { public: @@ -70,7 +71,7 @@ class BaseAudioContext { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createOscillator(); + std::shared_ptr createOscillator(std::shared_ptr options); std::shared_ptr createConstantSource( std::shared_ptr options); std::shared_ptr createStreamer(); 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/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index a12b62937..9686d0c94 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -179,7 +179,7 @@ export default class BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createStreamer(): StreamerNode { 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 2452f7ba3..58c45c014 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -52,7 +52,6 @@ export default class PeriodicWave { context.sampleRate, options ); - console.log('finalOptions', finalOptions); this.periodicWave = context.context.createPeriodicWave( finalOptions.real, finalOptions.imag, diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 064ea0220..fdb582550 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -7,6 +7,7 @@ import { TPeriodicWaveConstraints, TAnalyserOptions, TBiquadFilterOptions, + TOscillatorOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -55,3 +56,10 @@ export const ConstantSourceOptions: TConstantSourceOptions = { export const PeriodicWaveConstraints: TPeriodicWaveConstraints = { disableNormalization: false, }; + +export const OscillatorOptions: TOscillatorOptions = { + ...AudioNodeOptions, + type: 'sine', + frequency: 440, + detune: 0, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 7cd746a55..0e890f7ac 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -11,6 +11,7 @@ import { TConstantSourceOptions, TAnalyserOptions, TBiquadFilterOptions, + TOscillatorOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -62,7 +63,7 @@ export interface IBaseAudioContext { shareableWorklet: ShareableWorkletCallback, shouldUseUiRuntime: boolean ): IWorkletProcessingNode; - createOscillator(): IOscillatorNode; + createOscillator(oscillatorOptions: TOscillatorOptions): IOscillatorNode; createConstantSource( constantSourceOptions: TConstantSourceOptions ): IConstantSourceNode; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 3be45fc91..ba5ed4ae3 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -1,4 +1,5 @@ import AudioBuffer from './core/AudioBuffer'; +import PeriodicWave from './core/PeriodicWave'; export type ChannelCountMode = 'max' | 'clamped-max' | 'explicit'; @@ -77,6 +78,13 @@ export interface TBiquadFilterOptions extends TAudioNodeOptions { gain?: number; } +export interface TOscillatorOptions { + type?: OscillatorType; + frequency?: number; + detune?: number; + periodicWave?: PeriodicWave; +} + export interface TConvolverOptions extends TAudioNodeOptions { buffer?: AudioBuffer | null; disableNormalization?: boolean; 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 2cd837244..66689558d 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -51,7 +51,7 @@ export default class AudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { 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 ee3cc4ae8..8bc7eef84 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -57,7 +57,7 @@ export default class OfflineAudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { 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); From 1f62ba85b978bc796b14cde2f181a9869ebb3149 Mon Sep 17 00:00:00 2001 From: michal Date: Mon, 24 Nov 2025 13:49:29 +0100 Subject: [PATCH 14/35] feat: mobile audio buffer sources --- .../BaseAudioContextHostObject.cpp | 12 ++++--- .../audioapi/HostObjects/utils/NodeOptions.h | 13 ++++++++ .../HostObjects/utils/NodeOptionsParser.h | 31 +++++++++++++++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 9 +++--- .../cpp/audioapi/core/BaseAudioContext.h | 8 +++-- .../audioapi/core/effects/ConvolverNode.cpp | 2 -- .../sources/AudioBufferBaseSourceNode.cpp | 11 ++++--- .../core/sources/AudioBufferBaseSourceNode.h | 5 ++- .../sources/AudioBufferQueueSourceNode.cpp | 7 +++-- .../core/sources/AudioBufferQueueSourceNode.h | 5 ++- .../core/sources/AudioBufferSourceNode.cpp | 15 +++++---- .../core/sources/AudioBufferSourceNode.h | 5 ++- .../src/core/AudioBufferQueueSourceNode.ts | 15 +++++++++ .../src/core/AudioBufferSourceNode.ts | 12 +++++++ .../src/core/BaseAudioContext.ts | 28 +++-------------- .../react-native-audio-api/src/defaults.ts | 15 +++++++++ .../react-native-audio-api/src/interfaces.ts | 8 +++-- packages/react-native-audio-api/src/types.ts | 20 +++++++++--- .../src/web-core/AudioContext.tsx | 1 + .../src/web-core/OfflineAudioContext.tsx | 1 + 20 files changed, 165 insertions(+), 58 deletions(-) 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 24f4b637b..b8994118c 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 @@ -207,15 +207,19 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBiquadFilter) { } 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); 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 index 751a0096a..1f8e20be9 100644 --- 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 @@ -54,4 +54,17 @@ struct OscillatorOptions { 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; +}; } // 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 index 6abc0f415..0999d8295 100644 --- 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 @@ -168,4 +168,35 @@ std::shared_ptr parseOscillatorOptions( 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); +} } // namespace audioapi::option_parser 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 d76c312f5..e0bfaf3e9 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 @@ -152,15 +152,16 @@ std::shared_ptr BaseAudioContext::createBiquadFilter( 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; } 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 d0d0dc398..9952613de 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 @@ -42,6 +42,8 @@ class ConstantSourceOptions; class AnalyserOptions; class BiquadFilterOptions; class OscillatorOptions; +class BaseAudioBufferSourceOptions; +class AudioBufferSourceOptions; class BaseAudioContext { public: @@ -80,8 +82,10 @@ class BaseAudioContext { std::shared_ptr options); std::shared_ptr createBiquadFilter( std::shared_ptr options); - std::shared_ptr createBufferSource(bool pitchCorrection); - std::shared_ptr createBufferQueueSource(bool pitchCorrection); + 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( 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 03fb3e1f3..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 @@ -23,8 +23,6 @@ ConvolverNode::ConvolverNode( intermediateBus_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - channelCount_ = 2; - channelCountMode_ = ChannelCountMode::CLAMPED_MAX; normalize_ = !options->disableNormalization; gainCalibrationSampleRate_ = context->getSampleRate(); setBuffer(options->bus); 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/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 9686d0c94..0cba1605b 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -1,10 +1,6 @@ import { InvalidAccessError, NotSupportedError } from '../errors'; import { IBaseAudioContext } from '../interfaces'; -import { - AudioBufferBaseSourceNodeOptions, - ContextState, - AudioWorkletRuntime, -} from '../types'; +import { ContextState, AudioWorkletRuntime } from '../types'; import { assertWorkletsEnabled, workletsModule } from '../utils'; import WorkletSourceNode from './WorkletSourceNode'; import WorkletProcessingNode from './WorkletProcessingNode'; @@ -202,26 +198,12 @@ export default class BaseAudioContext { 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( diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index fdb582550..9459a2b00 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -8,6 +8,8 @@ import { TAnalyserOptions, TBiquadFilterOptions, TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -63,3 +65,16 @@ export const OscillatorOptions: TOscillatorOptions = { 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 0e890f7ac..0658c008a 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -12,6 +12,8 @@ import { TAnalyserOptions, TBiquadFilterOptions, TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -74,9 +76,11 @@ export interface IBaseAudioContext { createBiquadFilter: ( biquadFilterOptions: TBiquadFilterOptions ) => IBiquadFilterNode; - createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; + createBufferSource: ( + audioBufferSourceOptions: TAudioBufferSourceOptions + ) => IAudioBufferSourceNode; createBufferQueueSource: ( - pitchCorrection: boolean + audioBufferQueueSourceOptions: TBaseAudioBufferSourceOptions ) => IAudioBufferQueueSourceNode; createBuffer: ( channels: number, diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index ba5ed4ae3..5db4e9fdd 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -43,10 +43,6 @@ export interface AudioRecorderOptions { export type WindowType = 'blackman' | 'hann'; -export interface AudioBufferBaseSourceNodeOptions { - pitchCorrection: boolean; -} - export type ProcessorMode = 'processInPlace' | 'processThrough'; export interface TAudioNodeOptions { @@ -85,8 +81,22 @@ export interface TOscillatorOptions { 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 | null; + buffer?: AudioBuffer; disableNormalization?: boolean; } 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 66689558d..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,6 +1,7 @@ import { ContextState, AudioContextOptions, + // @ts-ignore only-for-this-commit AudioBufferBaseSourceNodeOptions, } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; 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 8bc7eef84..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,6 +1,7 @@ import { ContextState, OfflineAudioContextOptions, + // @ts-ignore only-for-this-commit AudioBufferBaseSourceNodeOptions, } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; From f80292ab402a2b5b180da57266d3bb75287e7e3e Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 26 Nov 2025 12:08:36 +0100 Subject: [PATCH 15/35] feat: streamer node options --- .../audiodocs/docs/sources/streamer-node.mdx | 9 ++++--- .../BaseAudioContextHostObject.cpp | 7 ++++- .../sources/StreamerNodeHostObject.cpp | 6 +++++ .../sources/StreamerNodeHostObject.h | 1 + .../audioapi/HostObjects/utils/NodeOptions.h | 6 +++++ .../HostObjects/utils/NodeOptionsParser.h | 13 ++++++++- .../cpp/audioapi/core/BaseAudioContext.cpp | 5 ++-- .../cpp/audioapi/core/BaseAudioContext.h | 3 ++- .../audioapi/core/sources/StreamerNode.cpp | 7 +++-- .../cpp/audioapi/core/sources/StreamerNode.h | 8 +++++- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/StreamerNode.ts | 27 ++++++++++++++++++- .../react-native-audio-api/src/interfaces.ts | 4 ++- packages/react-native-audio-api/src/types.ts | 4 +++ 14 files changed, 88 insertions(+), 14 deletions(-) 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 b8994118c..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 @@ -163,7 +163,12 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { } 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()); 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 index 1f8e20be9..fc05192a9 100644 --- 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 @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -67,4 +68,9 @@ struct AudioBufferSourceOptions : BaseAudioBufferSourceOptions { 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 index 0999d8295..e51f47c6e 100644 --- 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 @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include @@ -199,4 +199,15 @@ std::shared_ptr parseAudioBufferSourceOptions( 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/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index e0bfaf3e9..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 @@ -125,8 +125,9 @@ std::shared_ptr BaseAudioContext::createConstantSource( } #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; } 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 9952613de..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 @@ -44,6 +44,7 @@ class BiquadFilterOptions; class OscillatorOptions; class BaseAudioBufferSourceOptions; class AudioBufferSourceOptions; +class StreamerOptions; class BaseAudioContext { public: @@ -76,7 +77,7 @@ class BaseAudioContext { std::shared_ptr createOscillator(std::shared_ptr options); std::shared_ptr createConstantSource( std::shared_ptr options); - std::shared_ptr createStreamer(); + std::shared_ptr createStreamer(std::shared_ptr options); std::shared_ptr createGain(std::shared_ptr options); std::shared_ptr createStereoPanner( std::shared_ptr options); 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/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 0cba1605b..f140551ea 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -179,7 +179,7 @@ export default class BaseAudioContext { } createStreamer(): StreamerNode { - return new StreamerNode(this, this.context.createStreamer()); + return new StreamerNode(this); } createConstantSource(): ConstantSourceNode { 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/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 0658c008a..3d3ddb684 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -14,6 +14,7 @@ import { TOscillatorOptions, TBaseAudioBufferSourceOptions, TAudioBufferSourceOptions, + TStreamerOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -94,7 +95,7 @@ export interface IBaseAudioContext { ) => IPeriodicWave; createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; - createStreamer: () => IStreamerNode; + createStreamer: (streamerOptions?: TStreamerOptions) => IStreamerNode; } export interface IAudioContext extends IBaseAudioContext { @@ -175,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 5db4e9fdd..84f36bd66 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -109,6 +109,10 @@ export interface TConstantSourceOptions { offset?: number; } +export interface TStreamerOptions { + streamPath?: string; +} + export interface TPeriodicWaveConstraints { disableNormalization?: boolean; } From 67df87dcb6baa1153c6e2dad2719da7763beab9b Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 26 Nov 2025 12:24:30 +0100 Subject: [PATCH 16/35] feat: recorder adapter --- packages/react-native-audio-api/src/core/BaseAudioContext.ts | 2 +- .../react-native-audio-api/src/core/RecorderAdapterNode.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index f140551ea..6c0d6b6bb 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -171,7 +171,7 @@ export default class BaseAudioContext { } createRecorderAdapter(): RecorderAdapterNode { - return new RecorderAdapterNode(this, this.context.createRecorderAdapter()); + return new RecorderAdapterNode(this); } createOscillator(): OscillatorNode { 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; From 617188a04f1dd9714488a0efe12ad7b049d0c41f Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 2 Dec 2025 10:28:54 +0100 Subject: [PATCH 17/35] feat: worklets --- .../src/core/BaseAudioContext.ts | 68 ++----------------- .../src/core/WorkletNode.ts | 30 +++++++- .../src/core/WorkletProcessingNode.ts | 39 ++++++++++- .../src/core/WorkletSourceNode.ts | 36 +++++++++- 4 files changed, 109 insertions(+), 64 deletions(-) diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 6c0d6b6bb..ca7defcbb 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -1,7 +1,7 @@ import { InvalidAccessError, NotSupportedError } from '../errors'; import { IBaseAudioContext } from '../interfaces'; import { ContextState, AudioWorkletRuntime } from '../types'; -import { assertWorkletsEnabled, workletsModule } from '../utils'; +import { assertWorkletsEnabled } from '../utils'; import WorkletSourceNode from './WorkletSourceNode'; import WorkletProcessingNode from './WorkletProcessingNode'; import AnalyserNode from './AnalyserNode'; @@ -81,23 +81,12 @@ export default class BaseAudioContext { ); } assertWorkletsEnabled(); - const shareableWorklet = workletsModule.makeShareableCloneRecursive( - (audioBuffers: Array, channelCount: number) => { - 'worklet'; - const floatAudioData: Array = audioBuffers.map( - (buffer) => new Float32Array(buffer) - ); - callback(floatAudioData, channelCount); - } - ); return new WorkletNode( this, - this.context.createWorkletNode( - shareableWorklet, - workletRuntime === 'UIRuntime', - bufferLength, - inputChannelCount - ) + workletRuntime, + callback, + bufferLength, + inputChannelCount ); } @@ -111,30 +100,7 @@ export default class BaseAudioContext { workletRuntime: AudioWorkletRuntime = 'AudioRuntime' ): WorkletProcessingNode { assertWorkletsEnabled(); - const shareableWorklet = workletsModule.makeShareableCloneRecursive( - ( - inputBuffers: Array, - outputBuffers: Array, - framesToProcess: number, - currentTime: number - ) => { - 'worklet'; - const inputData: Array = inputBuffers.map( - (buffer) => new Float32Array(buffer, 0, framesToProcess) - ); - const outputData: Array = outputBuffers.map( - (buffer) => new Float32Array(buffer, 0, framesToProcess) - ); - callback(inputData, outputData, framesToProcess, currentTime); - } - ); - return new WorkletProcessingNode( - this, - this.context.createWorkletProcessingNode( - shareableWorklet, - workletRuntime === 'UIRuntime' - ) - ); + return new WorkletProcessingNode(this, workletRuntime, callback); } createWorkletSourceNode( @@ -147,27 +113,7 @@ export default class BaseAudioContext { workletRuntime: AudioWorkletRuntime = 'AudioRuntime' ): WorkletSourceNode { assertWorkletsEnabled(); - const shareableWorklet = workletsModule.makeShareableCloneRecursive( - ( - audioBuffers: Array, - framesToProcess: number, - currentTime: number, - startOffset: number - ) => { - 'worklet'; - const floatAudioData: Array = audioBuffers.map( - (buffer) => new Float32Array(buffer) - ); - callback(floatAudioData, framesToProcess, currentTime, startOffset); - } - ); - return new WorkletSourceNode( - this, - this.context.createWorkletSourceNode( - shareableWorklet, - workletRuntime === 'UIRuntime' - ) - ); + return new WorkletSourceNode(this, workletRuntime, callback); } createRecorderAdapter(): RecorderAdapterNode { diff --git a/packages/react-native-audio-api/src/core/WorkletNode.ts b/packages/react-native-audio-api/src/core/WorkletNode.ts index c017228ba..fa5362308 100644 --- a/packages/react-native-audio-api/src/core/WorkletNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletNode.ts @@ -1,3 +1,31 @@ import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; +import { workletsModule } from '../utils'; +import { AudioWorkletRuntime } from '../types'; -export default class WorkletNode extends AudioNode {} +export default class WorkletNode extends AudioNode { + constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: (audioData: Array, channelCount: number) => void, + bufferLength: number, + inputChannelCount: number + ) { + const shareableWorklet = workletsModule.makeShareableCloneRecursive( + (audioBuffers: Array, channelCount: number) => { + 'worklet'; + const floatAudioData: Array = audioBuffers.map( + (buffer) => new Float32Array(buffer) + ); + callback(floatAudioData, channelCount); + } + ); + const node = context.context.createWorkletNode( + shareableWorklet, + runtime === 'UIRuntime', + bufferLength, + inputChannelCount + ); + super(context, node); + } +} diff --git a/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts b/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts index 2b30e63fd..ee0003a92 100644 --- a/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts @@ -1,3 +1,40 @@ import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; +import { workletsModule } from '../utils'; +import { AudioWorkletRuntime } from '../types'; -export default class WorkletProcessingNode extends AudioNode {} +export default class WorkletProcessingNode extends AudioNode { + constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: ( + inputData: Array, + outputData: Array, + framesToProcess: number, + currentTime: number + ) => void + ) { + const shareableWorklet = workletsModule.makeShareableCloneRecursive( + ( + inputBuffers: Array, + outputBuffers: Array, + framesToProcess: number, + currentTime: number + ) => { + 'worklet'; + const inputData: Array = inputBuffers.map( + (buffer) => new Float32Array(buffer, 0, framesToProcess) + ); + const outputData: Array = outputBuffers.map( + (buffer) => new Float32Array(buffer, 0, framesToProcess) + ); + callback(inputData, outputData, framesToProcess, currentTime); + } + ); + const node = context.context.createWorkletProcessingNode( + shareableWorklet, + runtime === 'UIRuntime' + ); + super(context, node); + } +} diff --git a/packages/react-native-audio-api/src/core/WorkletSourceNode.ts b/packages/react-native-audio-api/src/core/WorkletSourceNode.ts index a8df8d249..f7a0e283e 100644 --- a/packages/react-native-audio-api/src/core/WorkletSourceNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletSourceNode.ts @@ -1,3 +1,37 @@ import AudioScheduledSourceNode from './AudioScheduledSourceNode'; +import BaseAudioContext from './BaseAudioContext'; +import { workletsModule } from '../utils'; +import { AudioWorkletRuntime } from '../types'; -export default class WorkletSourceNode extends AudioScheduledSourceNode {} +export default class WorkletSourceNode extends AudioScheduledSourceNode { + constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: ( + audioData: Array, + framesToProcess: number, + currentTime: number, + startOffset: number + ) => void + ) { + const shareableWorklet = workletsModule.makeShareableCloneRecursive( + ( + audioBuffers: Array, + framesToProcess: number, + currentTime: number, + startOffset: number + ) => { + 'worklet'; + const floatAudioData: Array = audioBuffers.map( + (buffer) => new Float32Array(buffer) + ); + callback(floatAudioData, framesToProcess, currentTime, startOffset); + } + ); + const node = context.context.createWorkletSourceNode( + shareableWorklet, + runtime === 'UIRuntime' + ); + super(context, node); + } +} From 0a6f8d899d9c2af998144c7d2a6075629131c8cb Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 2 Dec 2025 11:25:13 +0100 Subject: [PATCH 18/35] feat: audiobuffer --- .../BaseAudioContextHostObject.cpp | 33 +++++++---------- .../audioapi/HostObjects/utils/NodeOptions.h | 6 ++++ .../HostObjects/utils/NodeOptionsParser.h | 13 +++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 6 ++-- .../cpp/audioapi/core/BaseAudioContext.h | 4 +-- .../cpp/audioapi/core/sources/AudioBuffer.cpp | 6 ++-- .../cpp/audioapi/core/sources/AudioBuffer.h | 3 +- .../src/core/AudioBuffer.ts | 35 +++++++++++++++---- .../src/core/BaseAudioContext.ts | 10 +++--- .../react-native-audio-api/src/defaults.ts | 7 ++++ .../react-native-audio-api/src/interfaces.ts | 7 ++-- packages/react-native-audio-api/src/types.ts | 6 ++++ 12 files changed, 91 insertions(+), 45 deletions(-) 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 b2ff7449c..318d0df80 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 @@ -155,15 +155,14 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createRecorderAdapter) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { auto options = args[0].asObject(runtime); - std::shared_ptr oscillatorOptions = - audioapi::option_parser::parseOscillatorOptions(runtime, options); + auto 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) { - std::shared_ptr streamerOptions = std::make_shared(); + auto streamerOptions = std::make_shared(); if (!args[0].isUndefined()) { auto options = args[0].asObject(runtime); streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); @@ -177,7 +176,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { auto options = args[0].asObject(runtime); - std::shared_ptr constantSourceOptions = + auto constantSourceOptions = audioapi::option_parser::parseConstantSourceOptions(runtime, options); auto constantSource = context_->createConstantSource(constantSourceOptions); auto constantSourceHostObject = std::make_shared(constantSource); @@ -186,8 +185,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { auto options = args[0].asObject(runtime); - std::shared_ptr gainOptions = - audioapi::option_parser::parseGainOptions(runtime, options); + auto gainOptions = audioapi::option_parser::parseGainOptions(runtime, options); auto gain = context_->createGain(gainOptions); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); @@ -195,8 +193,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { auto options = args[0].asObject(runtime); - std::shared_ptr stereoPannerOptions = - audioapi::option_parser::parseStereoPannerOptions(runtime, options); + auto stereoPannerOptions = audioapi::option_parser::parseStereoPannerOptions(runtime, options); auto stereoPanner = context_->createStereoPanner(stereoPannerOptions); auto stereoPannerHostObject = std::make_shared(stereoPanner); return jsi::Object::createFromHostObject(runtime, stereoPannerHostObject); @@ -204,8 +201,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBiquadFilter) { auto options = args[0].asObject(runtime); - std::shared_ptr biquadFilterOptions = - audioapi::option_parser::parseBiquadFilterOptions(runtime, options); + auto biquadFilterOptions = audioapi::option_parser::parseBiquadFilterOptions(runtime, options); auto biquadFilter = context_->createBiquadFilter(biquadFilterOptions); auto biquadFilterHostObject = std::make_shared(biquadFilter); return jsi::Object::createFromHostObject(runtime, biquadFilterHostObject); @@ -213,7 +209,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBiquadFilter) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { auto options = args[0].asObject(runtime); - std::shared_ptr audioBufferSourceOptions = + auto audioBufferSourceOptions = audioapi::option_parser::parseAudioBufferSourceOptions(runtime, options); auto bufferSource = context_->createBufferSource(audioBufferSourceOptions); auto bufferSourceHostObject = std::make_shared(bufferSource); @@ -222,7 +218,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) { auto options = args[0].asObject(runtime); - std::shared_ptr baseAudioBufferSourceOptions = + auto baseAudioBufferSourceOptions = audioapi::option_parser::parseBaseAudioBufferSourceOptions(runtime, options); auto bufferSource = context_->createBufferQueueSource(baseAudioBufferSourceOptions); auto bufferStreamSourceHostObject = @@ -231,10 +227,9 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBuffer) { - auto numberOfChannels = static_cast(args[0].getNumber()); - auto length = static_cast(args[1].getNumber()); - auto sampleRate = static_cast(args[2].getNumber()); - auto buffer = BaseAudioContext::createBuffer(numberOfChannels, length, sampleRate); + auto options = args[0].asObject(runtime); + auto audioBufferOptions = audioapi::option_parser::parseAudioBufferOptions(runtime, options); + auto buffer = BaseAudioContext::createBuffer(audioBufferOptions); auto bufferHostObject = std::make_shared(buffer); auto jsiObject = jsi::Object::createFromHostObject(runtime, bufferHostObject); @@ -269,8 +264,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createPeriodicWave) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { auto options = args[0].asObject(runtime); - std::shared_ptr analyserOptions = - audioapi::option_parser::parseAnalyserOptions(runtime, options); + auto analyserOptions = audioapi::option_parser::parseAnalyserOptions(runtime, options); auto analyser = context_->createAnalyser(analyserOptions); auto analyserHostObject = std::make_shared(analyser); return jsi::Object::createFromHostObject(runtime, analyserHostObject); @@ -278,8 +272,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) { auto options = args[0].asObject(runtime); - std::shared_ptr convolverOptions = - audioapi::option_parser::parseConvolverOptions(runtime, options); + auto 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); 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 index fc05192a9..de581e83e 100644 --- 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 @@ -73,4 +73,10 @@ struct StreamerOptions { std::string streamPath = ""; }; +struct AudioBufferOptions { + int numberOfChannels = 1; + size_t length = 0; + float sampleRate = 44100.0f; +}; + } // 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 index e51f47c6e..3366d53dc 100644 --- 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 @@ -210,4 +210,17 @@ std::shared_ptr parseStreamerOptions( } return std::make_shared(options); } + +std::shared_ptr parseAudioBufferOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + AudioBufferOptions options; + options.numberOfChannels = + static_cast(optionsObject.getProperty(runtime, "numberOfChannels").getNumber()); + options.length = static_cast(optionsObject.getProperty(runtime, "length").getNumber()); + options.sampleRate = + static_cast(optionsObject.getProperty(runtime, "sampleRate").getNumber()); + return std::make_shared(options); +} + } // namespace audioapi::option_parser 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 38e2735f1..a7bd6c5db 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 @@ -167,9 +167,9 @@ std::shared_ptr BaseAudioContext::createBufferQueueS return bufferSource; } -std::shared_ptr -BaseAudioContext::createBuffer(int numberOfChannels, size_t length, float sampleRate) { - return std::make_shared(numberOfChannels, length, sampleRate); +std::shared_ptr BaseAudioContext::createBuffer( + std::shared_ptr options) { + return std::make_shared(options); } std::shared_ptr BaseAudioContext::createPeriodicWave( 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 2032142aa..dd7d3e653 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 @@ -45,6 +45,7 @@ class OscillatorOptions; class BaseAudioBufferSourceOptions; class AudioBufferSourceOptions; class StreamerOptions; +class AudioBufferOptions; class BaseAudioContext { public: @@ -87,8 +88,7 @@ class BaseAudioContext { std::shared_ptr options); std::shared_ptr createBufferQueueSource( std::shared_ptr options); - static std::shared_ptr - createBuffer(int numberOfChannels, size_t length, float sampleRate); + static std::shared_ptr createBuffer(std::shared_ptr options); std::shared_ptr createPeriodicWave( const std::vector> &complexData, bool disableNormalization, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp index 17c159068..a2856b4b7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,8 +9,9 @@ namespace audioapi { -AudioBuffer::AudioBuffer(int numberOfChannels, size_t length, float sampleRate) { - bus_ = std::make_shared(length, numberOfChannels, sampleRate); +AudioBuffer::AudioBuffer(std::shared_ptr options) { + bus_ = + std::make_shared(options->length, options->numberOfChannels, options->sampleRate); } AudioBuffer::AudioBuffer(std::shared_ptr bus) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h index 2d3a860c9..abca23477 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h @@ -10,10 +10,11 @@ namespace audioapi { class AudioBus; +class AudioBufferOptions; class AudioBuffer : public std::enable_shared_from_this { public: - explicit AudioBuffer(int numberOfChannels, size_t length, float sampleRate); + explicit AudioBuffer(std::shared_ptr options); explicit AudioBuffer(std::shared_ptr bus); [[nodiscard]] size_t getLength() const; diff --git a/packages/react-native-audio-api/src/core/AudioBuffer.ts b/packages/react-native-audio-api/src/core/AudioBuffer.ts index 671e48d9d..e67c9c2cf 100644 --- a/packages/react-native-audio-api/src/core/AudioBuffer.ts +++ b/packages/react-native-audio-api/src/core/AudioBuffer.ts @@ -1,5 +1,8 @@ import { IAudioBuffer } from '../interfaces'; import { IndexSizeError } from '../errors'; +import BaseAudioContext from './BaseAudioContext'; +import { TAudioBufferOptions } from '../types'; +import { AudioBufferOptions } from '../defaults'; export default class AudioBuffer { readonly length: number; @@ -9,12 +12,32 @@ export default class AudioBuffer { /** @internal */ public readonly buffer: IAudioBuffer; - constructor(buffer: IAudioBuffer) { - this.buffer = buffer; - this.length = buffer.length; - this.duration = buffer.duration; - this.sampleRate = buffer.sampleRate; - this.numberOfChannels = buffer.numberOfChannels; + constructor(buffer: IAudioBuffer); + constructor(context: BaseAudioContext, options: TAudioBufferOptions); + + constructor( + contextOrBuffer: BaseAudioContext | IAudioBuffer, + options?: TAudioBufferOptions + ) { + if (contextOrBuffer instanceof BaseAudioContext) { + const finalOptions = { + ...AudioBufferOptions, + ...options, + }; + const buffer = contextOrBuffer.context.createBuffer(finalOptions); + this.buffer = buffer; + this.length = buffer.length; + this.duration = buffer.duration; + this.sampleRate = buffer.sampleRate; + this.numberOfChannels = buffer.numberOfChannels; + } else { + const buffer = contextOrBuffer; + this.buffer = buffer; + this.length = buffer.length; + this.duration = buffer.duration; + this.sampleRate = buffer.sampleRate; + this.numberOfChannels = buffer.numberOfChannels; + } } public getChannelData(channel: number): Float32Array { diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index ca7defcbb..2d7a1cb4d 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -153,13 +153,13 @@ export default class BaseAudioContext { } createBuffer( - numOfChannels: number, + numberOfChannels: number, length: number, sampleRate: number ): AudioBuffer { - if (numOfChannels < 1 || numOfChannels >= 32) { + if (numberOfChannels < 1 || numberOfChannels >= 32) { throw new NotSupportedError( - `The number of channels provided (${numOfChannels}) is outside the range [1, 32]` + `The number of channels provided (${numberOfChannels}) is outside the range [1, 32]` ); } @@ -175,9 +175,7 @@ export default class BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 9459a2b00..729454c83 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -10,6 +10,7 @@ import { TOscillatorOptions, TBaseAudioBufferSourceOptions, TAudioBufferSourceOptions, + TAudioBufferOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -78,3 +79,9 @@ export const AudioBufferSourceOptions: TAudioBufferSourceOptions = { loopStart: 0, loopEnd: 0, }; + +export const AudioBufferOptions: TAudioBufferOptions = { + numberOfChannels: 1, + length: 0, // always overwritten by provided value, only placeholder + sampleRate: 44100, // always overwritten by provided value, only placeholder +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 3d3ddb684..34fc80793 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -15,6 +15,7 @@ import { TBaseAudioBufferSourceOptions, TAudioBufferSourceOptions, TStreamerOptions, + TAudioBufferOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -83,11 +84,7 @@ export interface IBaseAudioContext { createBufferQueueSource: ( audioBufferQueueSourceOptions: TBaseAudioBufferSourceOptions ) => IAudioBufferQueueSourceNode; - createBuffer: ( - channels: number, - length: number, - sampleRate: number - ) => IAudioBuffer; + createBuffer: (audioBufferOptions: TAudioBufferOptions) => IAudioBuffer; createPeriodicWave: ( real: Float32Array, imag: Float32Array, diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 84f36bd66..0628d1935 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -121,3 +121,9 @@ export interface TPeriodicWaveOptions extends TPeriodicWaveConstraints { real: Float32Array; imag: Float32Array; } + +export interface TAudioBufferOptions { + numberOfChannels?: number; + length: number; + sampleRate: number; +} From f074c1278f5db9776353bf3c725b51bc87061fa1 Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 2 Dec 2025 11:37:58 +0100 Subject: [PATCH 19/35] feat: audiobuffer on web --- .../src/core/AudioBuffer.ts | 21 +++++++-------- .../src/web-core/AudioBuffer.tsx | 26 ++++++++++++++----- .../src/web-core/AudioContext.tsx | 10 +++---- .../src/web-core/OfflineAudioContext.tsx | 10 +++---- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/react-native-audio-api/src/core/AudioBuffer.ts b/packages/react-native-audio-api/src/core/AudioBuffer.ts index e67c9c2cf..eeeb4d95c 100644 --- a/packages/react-native-audio-api/src/core/AudioBuffer.ts +++ b/packages/react-native-audio-api/src/core/AudioBuffer.ts @@ -19,25 +19,22 @@ export default class AudioBuffer { contextOrBuffer: BaseAudioContext | IAudioBuffer, options?: TAudioBufferOptions ) { + let buf: IAudioBuffer; if (contextOrBuffer instanceof BaseAudioContext) { const finalOptions = { ...AudioBufferOptions, ...options, }; - const buffer = contextOrBuffer.context.createBuffer(finalOptions); - this.buffer = buffer; - this.length = buffer.length; - this.duration = buffer.duration; - this.sampleRate = buffer.sampleRate; - this.numberOfChannels = buffer.numberOfChannels; + const context = contextOrBuffer; + buf = context.context.createBuffer(finalOptions); } else { - const buffer = contextOrBuffer; - this.buffer = buffer; - this.length = buffer.length; - this.duration = buffer.duration; - this.sampleRate = buffer.sampleRate; - this.numberOfChannels = buffer.numberOfChannels; + buf = contextOrBuffer; } + this.buffer = buf; + this.length = buf.length; + this.duration = buf.duration; + this.sampleRate = buf.sampleRate; + this.numberOfChannels = buf.numberOfChannels; } public getChannelData(channel: number): Float32Array { diff --git a/packages/react-native-audio-api/src/web-core/AudioBuffer.tsx b/packages/react-native-audio-api/src/web-core/AudioBuffer.tsx index d007a6dd4..33220e971 100644 --- a/packages/react-native-audio-api/src/web-core/AudioBuffer.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioBuffer.tsx @@ -1,4 +1,6 @@ import { IndexSizeError } from '../errors'; +import BaseAudioContext from './BaseAudioContext'; +import { TAudioBufferOptions } from '../types'; export default class AudioBuffer { readonly length: number; @@ -9,12 +11,24 @@ export default class AudioBuffer { /** @internal */ public readonly buffer: globalThis.AudioBuffer; - constructor(buffer: globalThis.AudioBuffer) { - this.buffer = buffer; - this.length = buffer.length; - this.duration = buffer.duration; - this.sampleRate = buffer.sampleRate; - this.numberOfChannels = buffer.numberOfChannels; + constructor(buffer: globalThis.AudioBuffer); + constructor(context: BaseAudioContext, options: TAudioBufferOptions); + + constructor( + contextOrBuffer: BaseAudioContext | globalThis.AudioBuffer, + options?: TAudioBufferOptions + ) { + let buf: globalThis.AudioBuffer; + if (contextOrBuffer instanceof BaseAudioContext) { + buf = new globalThis.AudioBuffer(options!); + } else { + buf = contextOrBuffer as globalThis.AudioBuffer; + } + this.buffer = buf; + this.length = buf.length; + this.duration = buf.duration; + this.sampleRate = buf.sampleRate; + this.numberOfChannels = buf.numberOfChannels; } public getChannelData(channel: number): 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 f8d8209b2..8c709bbe5 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -94,13 +94,13 @@ export default class AudioContext implements BaseAudioContext { } createBuffer( - numOfChannels: number, + numberOfChannels: number, length: number, sampleRate: number ): AudioBuffer { - if (numOfChannels < 1 || numOfChannels >= 32) { + if (numberOfChannels < 1 || numberOfChannels >= 32) { throw new NotSupportedError( - `The number of channels provided (${numOfChannels}) is outside the range [1, 32]` + `The number of channels provided (${numberOfChannels}) is outside the range [1, 32]` ); } @@ -116,9 +116,7 @@ export default class AudioContext implements BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( 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 b860f5375..ed41d0969 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -100,13 +100,13 @@ export default class OfflineAudioContext implements BaseAudioContext { } createBuffer( - numOfChannels: number, + numberOfChannels: number, length: number, sampleRate: number ): AudioBuffer { - if (numOfChannels < 1 || numOfChannels >= 32) { + if (numberOfChannels < 1 || numberOfChannels >= 32) { throw new NotSupportedError( - `The number of channels provided (${numOfChannels}) is outside the range [1, 32]` + `The number of channels provided (${numberOfChannels}) is outside the range [1, 32]` ); } @@ -122,9 +122,7 @@ export default class OfflineAudioContext implements BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( From 461772050e0ad27ba03f70c66b5815e933874ea8 Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 2 Dec 2025 17:35:15 +0100 Subject: [PATCH 20/35] docs: documentation for new constructors --- .../audiodocs/docs/analysis/analyser-node.mdx | 24 +++++++++++---- .../docs/effects/biquad-filter-node.mdx | 30 ++++++++++++------- .../audiodocs/docs/effects/convolver-node.mdx | 29 +++++++++++++----- packages/audiodocs/docs/effects/gain-node.mdx | 9 +++--- .../audiodocs/docs/effects/periodic-wave.mdx | 29 +++++++++++++----- .../audiodocs/docs/sources/audio-buffer.mdx | 15 +++++++++- .../docs/sources/constant-source-node.mdx | 26 +++++++++------- .../docs/sources/oscillator-node.mdx | 15 ++++++++++ .../docs/sources/recorder-adapter-node.mdx | 5 ++++ .../audiodocs/docs/sources/streamer-node.mdx | 14 ++++++++- .../docs/spatialization/_category_.json | 7 ----- .../audiodocs/docs/worklets/worklet-node.mdx | 9 ++++++ .../docs/worklets/worklet-processing-node.mdx | 12 ++++++++ .../docs/worklets/worklet-source-node.mdx | 12 ++++++++ .../src/core/PeriodicWave.ts | 4 +-- packages/react-native-audio-api/src/types.ts | 4 +-- .../src/web-core/PeriodicWave.tsx | 4 +-- 17 files changed, 186 insertions(+), 62 deletions(-) delete mode 100644 packages/audiodocs/docs/spatialization/_category_.json diff --git a/packages/audiodocs/docs/analysis/analyser-node.mdx b/packages/audiodocs/docs/analysis/analyser-node.mdx index dbb01c474..baed61620 100644 --- a/packages/audiodocs/docs/analysis/analyser-node.mdx +++ b/packages/audiodocs/docs/analysis/analyser-node.mdx @@ -3,7 +3,7 @@ sidebar_position: 1 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" -import { ReadOnly } from '@site/src/components/Badges'; +import { Optional, ReadOnly } from '@site/src/components/Badges'; # AnalyserNode @@ -23,7 +23,23 @@ In contrast, a frequency-domain graph reveals how the signal's energy or power i ## Constructor -[`BaseAudioContext.createAnalyser()`](/docs/core/base-audio-context#createanalyser) +```tsx +constructor(context: BaseAudioContext, options?: AnalyserOptions) +``` + +### `AnalyserOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `fftSize` | `number` | 2048 | Number representing size of fast fourier transform | +| `minDecibels` | `number` | -100 | Initial minimum power in dB for FFT analysis | +| `maxDecibels` | `number` | -30 | Initial maximum power in dB for FFT analysis | +| `smoothingTimeConstant` | `number` | 0.8 | Initial smoothing constant for the FFT analysis | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createAnalyser()`](/docs/core/base-audio-context#createanalyser) that creates node with default values. ## Properties @@ -95,24 +111,20 @@ Each value in the array is within the range 0 to 255, where value of 127 indicat ## Remarks #### `fftSize` -- Default value is 2048. - Must be a power of 2 between 32 and 32768. - Throws `IndexSizeError` if set value is not power of 2, or is outside the allowed range. #### `minDecibels` -- Default value is -100 dB. - 0 dB([decibel](https://en.wikipedia.org/wiki/Decibel)) is the loudest possible sound, -10 dB is a 10th of that. - When getting data from [`getByteFrequencyData()`](/docs/analysis/analyser-node#getbytefrequencydata), any frequency with amplitude lower then `minDecibels` will be returned as 0. - Throws `IndexSizeError` if set value is greater than or equal to `maxDecibels`. #### `maxDecibels` -- Default value is -30 dB. - 0 dB([decibel](https://en.wikipedia.org/wiki/Decibel)) is the loudest possible sound, -10 dB is a 10th of that. - When getting data from [`getByteFrequencyData()`](/docs/analysis/analyser-node#getbytefrequencydata), any frequency with amplitude higher then `maxDecibels` will be returned as 255. - Throws `IndexSizeError` if set value is less then or equal to `minDecibels`. #### `smoothingTimeConstant` -- Default value is 0.8. - Nominal range is 0 to 1. - 0 means no averaging, 1 means "overlap the previous and current buffer quite a lot while computing the value". - Throws `IndexSizeError` if set value is outside the allowed range. diff --git a/packages/audiodocs/docs/effects/biquad-filter-node.mdx b/packages/audiodocs/docs/effects/biquad-filter-node.mdx index aa125904e..8d123bb4e 100644 --- a/packages/audiodocs/docs/effects/biquad-filter-node.mdx +++ b/packages/audiodocs/docs/effects/biquad-filter-node.mdx @@ -3,7 +3,7 @@ sidebar_position: 1 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" -import { ReadOnly } from '@site/src/components/Badges'; +import { Optional, ReadOnly } from '@site/src/components/Badges'; import InteractiveExample from '@site/src/components/InteractiveExample'; import AudioApiExample from '@site/src/components/AudioApiExample' import VinylPlayer from '@site/src/components/RecordPlayerExample/VinylAnimation'; @@ -23,7 +23,24 @@ Multiple `BiquadFilterNode` instances can be combined to create more complex fil ## Constructor -[`BaseAudioContext.createBiquadFilter()`](/docs/core/base-audio-context#createbiquadfilter) +```tsx +constructor(context: BaseAudioContext, options?: BiquadFilterOptions) +``` + +### `BiquadFilterOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `Q` | `number` | 1 | Initial value for [`Q`](/docs/effects/biquad-filter-node#properties) | +| `detune` | `number` | 0 | Initial value for [`detune`](/docs/effects/biquad-filter-node#properties) | +| `frequency` | `number` | 350 | Initial value for [`frequency`](/docs/effects/biquad-filter-node#properties) | +| `gain` | `number` | 0 | Initial value for [`gain`](/docs/effects/biquad-filter-node#properties) | +| `type` | `BiquadFilterType` | `lowpass` | Initial value for [`type`](/docs/effects/biquad-filter-node#properties) | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createBiquadFilter()`](/docs/core/base-audio-context#createbiquadfilter) that creates node with default values. ## Properties @@ -72,14 +89,9 @@ It inherits all methods from [`AudioNode`](/docs/core/audio-node#methods). ## Remarks #### `frequency` -- Float. Default: 350. - Range: [10, $\frac{sampleRate}{2}$]. -#### `detune` -- Float. Default: 0. - #### `Q` -- Float. Default: 1. - Range: - For `lowpass` and `highpass` is [-Q, Q], where Q is the largest value for which $10^{Q/20}$ does not overflow the single-precision floating-point representation. Numerically: Q ≈ 770.63678. @@ -87,9 +99,5 @@ Numerically: Q ≈ 770.63678. - Not used for `lowshelf` and `highshelf`. #### `gain` -- Float. Default: 0. - Range: [-40, 40]. - Positive values correspond to amplification; negative to attenuation. - -#### `type` -- [`BiquadFilterType`](#biquadfiltertype-enumeration-description). Default: `"lowpass"`. diff --git a/packages/audiodocs/docs/effects/convolver-node.mdx b/packages/audiodocs/docs/effects/convolver-node.mdx index 9a146d9be..ea01e6def 100644 --- a/packages/audiodocs/docs/effects/convolver-node.mdx +++ b/packages/audiodocs/docs/effects/convolver-node.mdx @@ -3,6 +3,7 @@ sidebar_position: 5 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" +import { Optional } from '@site/src/components/Badges'; # ConvolverNode @@ -19,18 +20,30 @@ Convolver is a node with tail-time, which means, that it continues to output non ## Constructor -[`BaseAudioContext.createConvolver(options: ConvolverNodeOptions)`](/docs/core/base-audio-context#createconvolver) - -```jsx -interface ConvolverNodeOptions { - buffer?: AudioBuffer | null; // impulse response - disableNormalization?: boolean; // if normalization of output should be applied, true by default -} +```tsx +constructor(context: BaseAudioContext, options?: ConvolverOptions) ``` +### `ConvolverOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `buffer` | `number` | | Initial value for [`buffer`](/docs/effects/convolver-node#properties). | +| `normalize` | `boolean` | true | Initial value for [`normalize`](/docs/effects/convolver-node#properties). | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createConvolver()`](/docs/core/base-audio-context#createconvolver) + ## Properties -It inherits all properties from [`AudioNode`](/docs/core/audio-node#properties) and has no individual ones. +It inherits all properties from [`AudioNode`](/docs/core/audio-node#properties). + +| Name | Type | Description | +| :----: | :----: | :-------- | +| `buffer` | [`AudioBuffer`](/docs/sources/audio-buffer) | Associated AudioBuffer. | +| `normalize` | `boolean` | Whether the impulse response from the buffer will be scaled by an equal-power normalization when the buffer attribute is set. | :::caution Linear convolution is a heavy computational process, so if your audio has some weird artefacts that should not be there, try to decrease the duration of impulse response buffer. diff --git a/packages/audiodocs/docs/effects/gain-node.mdx b/packages/audiodocs/docs/effects/gain-node.mdx index adfa4eb19..d4aef7993 100644 --- a/packages/audiodocs/docs/effects/gain-node.mdx +++ b/packages/audiodocs/docs/effects/gain-node.mdx @@ -44,19 +44,19 @@ You can read more about envelopes and ADSR on [Wikipedia]( | `number` | 1.0 | Number representing gain value | +| `gain` | `number` | 1.0 | Initial value for [`gain`](/docs/effects/gain-node#properties) | Or by using `BaseAudioContext` factory method: -[`BaseAudioContext.createGain()`](/docs/core/base-audio-context#creategain) +[`BaseAudioContext.createGain()`](/docs/core/base-audio-context#creategain) that creates node with default values. ## Properties @@ -74,5 +74,4 @@ It inherits all methods from [`AudioNode`](/docs/core/audio-node#methods). ## Remarks #### `gain` -- Default value is 1.0. - Nominal range is -∞ to ∞. diff --git a/packages/audiodocs/docs/effects/periodic-wave.mdx b/packages/audiodocs/docs/effects/periodic-wave.mdx index df7fca975..f7ddba2c6 100644 --- a/packages/audiodocs/docs/effects/periodic-wave.mdx +++ b/packages/audiodocs/docs/effects/periodic-wave.mdx @@ -2,24 +2,39 @@ sidebar_position: 3 --- +import { Optional } from '@site/src/components/Badges'; + # PeriodicWave The `PeriodicWave` interface defines a periodic waveform that can be used to shape the output of an OscillatorNode. ## Constructor -[`BaseAudioContext.createPeriodicWave(real, imag)`](/docs/core/base-audio-context#createperiodicwave) +```tsx +constructor(context: BaseAudioContext, options: PeriodicWaveOptions) +``` -[`BaseAudioContext.createPeriodicWave(real, imag, constraints: PeriodicWaveConstraints)`](/docs/core/base-audio-context#createperiodicwave) +### `PeriodicWaveOptions` -```jsx -interface PeriodicWaveConstraints { - disableNormalization: boolean; // default set to false (normalization is enabled) -} -``` +| Parameter | Type | Default | Description | +| :---: | :---: | :----: | :---- | +| `real` | `Float32Array` | - | [Cosine terms](/docs/core/base-audio-context#createperiodicwave) | +| `imag` | `Float32Array` | - | [Sine terms](/docs/core/base-audio-context#createperiodicwave) | +| `disableNormalization` | `boolean` | false | Whether the periodic wave is [normalized](/docs/core/base-audio-context#createperiodicwave) or not. | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createPeriodicWave(real, imag, constraints?: PeriodicWaveConstraints)`](/docs/core/base-audio-context#createperiodicwave) ## Properties None. `PeriodicWave` has no own or inherited properties. ## Methods None. `PeriodicWave` has no own or inherited methods. + +## Remarks + +#### `real` and `imag` +- if only one is specified, the other one is treated as array of 0s of the same length +- if neither is given values are equivalent to the sine wave +- if both given, they have to have the same length +- to see how values corresponds to the output wave [see](https://webaudio.github.io/web-audio-api/#waveform-generation) for more information diff --git a/packages/audiodocs/docs/sources/audio-buffer.mdx b/packages/audiodocs/docs/sources/audio-buffer.mdx index 8451207c4..26efefb1f 100644 --- a/packages/audiodocs/docs/sources/audio-buffer.mdx +++ b/packages/audiodocs/docs/sources/audio-buffer.mdx @@ -17,7 +17,20 @@ Once you have data in `AudioBuffer`, audio can be played by passing it to [`Audi ## Constructor -[`BaseAudioContext.createBuffer(numChannels, length, sampleRate)`](/docs/core/base-audio-context#createbuffer) +```tsx +constructor(context: BaseAudioContext, options: AudioBufferOptions) +``` + +### `AudioBufferOptions` + +| Parameter | Type | Default | Description | +| :---: | :---: | :----: | :---- | +| `numberOfChannels` | `number` | 1.0 | Number of [`channels`](/docs/sources/audio-buffer#properties) in buffer | +| `length` | `number` | - | [`Length`](/docs/sources/audio-buffer#properties) of the buffer | +| `sampleRate` | `number` | - | [`Sample rate`](/docs/sources/audio-buffer#properties) of the buffer in Hz | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createBuffer(numChannels, length, sampleRate)`](/docs/core/base-audio-context#createbuffer) that creates buffer with default values. ## Decoding diff --git a/packages/audiodocs/docs/sources/constant-source-node.mdx b/packages/audiodocs/docs/sources/constant-source-node.mdx index 87b89c81a..98d73c5e9 100644 --- a/packages/audiodocs/docs/sources/constant-source-node.mdx +++ b/packages/audiodocs/docs/sources/constant-source-node.mdx @@ -18,7 +18,18 @@ Just like `AudioScheduledSourceNode`, it can be started only once. ## Constructor -[`BaseAudioContext.createConstantSource()`](/docs/core/base-audio-context#createconstantsource) +```tsx +constructor(context: BaseAudioContext, options?: ConstantSourceOptions) +``` + +### `ConstantSourceOptions` + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `offset` | `number` | 1 | Initial value for [`offset`](/docs/sources/constant-source-node#properties) | + +Or by using `BaseAudioContext` factory method: +[`BaseAudioContext.createConstantSource()`](/docs/core/base-audio-context#createconstantsource) that creates node with default values. ## Example @@ -27,7 +38,7 @@ import React, { useRef } from 'react'; import { Text } from 'react-native'; import { AudioContext, - OscillatorNode, + OscillatorNode, GainNode, ConstantSourceNode } from 'react-native-audio-api'; @@ -55,7 +66,7 @@ function App() { oscillator2.connect(gainNode2); gainNode2.connect(audioContext.destination); - // We connect the constant source to the gain nodes gain AudioParams + // We connect the constant source to the gain nodes gain AudioParams // to control both of them at the same time constantSource.connect(gainNode1.gain); constantSource.connect(gainNode2.gain); @@ -71,14 +82,9 @@ function App() { It inherits all properties from [`AudioScheduledSourceNode`](/docs/sources/audio-scheduled-source-node#properties). | Name | Type | Default value | Description | -| :----: | :----: | :-------- | :------- | -| `offset` | [`AudioParam`](/docs/core/audio-param) | 1 |[`a-rate`](/docs/core/audio-param#a-rate-vs-k-rate) `AudioParam` representing the value which the node constantly outputs. | +| :----: | :----: | :--------: | :------- | +| `offset` | [`AudioParam`](/docs/core/audio-param) | 1.0 |[`a-rate`](/docs/core/audio-param#a-rate-vs-k-rate) `AudioParam` representing the value which the node constantly outputs. | ## Methods It inherits all methods from [`AudioScheduledSourceNode`](/docs/sources/audio-scheduled-source-node#methods). - -## Remarks - -#### `offset` -- Float. Default: 1. diff --git a/packages/audiodocs/docs/sources/oscillator-node.mdx b/packages/audiodocs/docs/sources/oscillator-node.mdx index e36a7f339..ad9075862 100644 --- a/packages/audiodocs/docs/sources/oscillator-node.mdx +++ b/packages/audiodocs/docs/sources/oscillator-node.mdx @@ -25,6 +25,21 @@ Similar to all of `AudioScheduledSourceNodes`, it can be started only once. If y ## Constructor +```tsx +constructor(context: BaseAudioContext, options?: OscillatorOptions) +``` + +### `OscillatorOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `type` | [`OscillatorType`](/docs/types/oscillator-type) | `sine` | Initial value for [`type`](/docs/sources/oscillator-node#properties). | +| `frequency` | `number` | 440 | Initial value for [`frequency`](/docs/sources/oscillator-node#properties). | +| `detune` | `number` | 0 | Initial value for [`detune`](/docs/sources/oscillator-node#properties). | + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createOscillator()`](/docs/core/base-audio-context#createoscillator) ## Example diff --git a/packages/audiodocs/docs/sources/recorder-adapter-node.mdx b/packages/audiodocs/docs/sources/recorder-adapter-node.mdx index 6f3d57cd9..cbc03c48e 100644 --- a/packages/audiodocs/docs/sources/recorder-adapter-node.mdx +++ b/packages/audiodocs/docs/sources/recorder-adapter-node.mdx @@ -12,6 +12,11 @@ It lets you compose audio input from recorder into an audio graph. ## Constructor +```tsx +constructor(context: BaseAudioContext) +``` + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createRecorderAdapter()`](/docs/core/base-audio-context#createrecorderadapter) ## Example diff --git a/packages/audiodocs/docs/sources/streamer-node.mdx b/packages/audiodocs/docs/sources/streamer-node.mdx index 479da9f02..d3cb176f9 100644 --- a/packages/audiodocs/docs/sources/streamer-node.mdx +++ b/packages/audiodocs/docs/sources/streamer-node.mdx @@ -20,7 +20,19 @@ Similar to all of `AudioScheduledSourceNodes`, it can be started only once. If y ## Constructor -[`BaseAudioContext.createStreamer()`](/docs/core/base-audio-context#createostreamer) +```tsx +constructor(context: BaseAudioContext, options?: StreamerOptions) +``` + +### `StreamerOptions` + +| Parameter | Type | Default | | +| :---: | :---: | :----: | :---- | +| `streamPath` | `string` | - | Initial value for [`streamPath`](/docs/sources/streamer-node#properties) | + +Or by using `BaseAudioContext` factory method: + +[`BaseAudioContext.createStreamer()`](/docs/core/base-audio-context#createstreamer-). ## Example diff --git a/packages/audiodocs/docs/spatialization/_category_.json b/packages/audiodocs/docs/spatialization/_category_.json deleted file mode 100644 index b988e18a3..000000000 --- a/packages/audiodocs/docs/spatialization/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Spatialization", - "position": 6, - "link": { - "type": "generated-index" - } -} diff --git a/packages/audiodocs/docs/worklets/worklet-node.mdx b/packages/audiodocs/docs/worklets/worklet-node.mdx index 8f06fed10..ccd04ce6b 100644 --- a/packages/audiodocs/docs/worklets/worklet-node.mdx +++ b/packages/audiodocs/docs/worklets/worklet-node.mdx @@ -17,6 +17,15 @@ This node lets you execute a worklet on the UI thread. bufferLength specifies th ## Constructor +```tsx +constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: (audioData: Array, channelCount: number) => void, + bufferLength: number, + inputChannelCount: number) +``` +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createWorkletNode(worklet, bufferLength, inputChannelCount, workletRuntime)`](/docs/core/base-audio-context#createworkletnode-) ## Example diff --git a/packages/audiodocs/docs/worklets/worklet-processing-node.mdx b/packages/audiodocs/docs/worklets/worklet-processing-node.mdx index f98ff5343..3ae2725d1 100644 --- a/packages/audiodocs/docs/worklets/worklet-processing-node.mdx +++ b/packages/audiodocs/docs/worklets/worklet-processing-node.mdx @@ -18,6 +18,18 @@ For more information about worklets, see our [Introduction to worklets](/docs/wo ## Constructor +```tsx +constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: ( + inputData: Array, + outputData: Array, + framesToProcess: number, + currentTime: number + ) => void) +``` +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createWorkletProcessingNode(worklet, workletRuntime)`](/docs/core/base-audio-context#createworkletprocessingnode-) ## Example diff --git a/packages/audiodocs/docs/worklets/worklet-source-node.mdx b/packages/audiodocs/docs/worklets/worklet-source-node.mdx index 3ebef4a02..f223cfada 100644 --- a/packages/audiodocs/docs/worklets/worklet-source-node.mdx +++ b/packages/audiodocs/docs/worklets/worklet-source-node.mdx @@ -18,6 +18,18 @@ For more information about worklets, see our [Introduction to worklets](/docs/wo ## Constructor +```tsx +constructor( + context: BaseAudioContext, + runtime: AudioWorkletRuntime, + callback: ( + audioData: Array, + framesToProcess: number, + currentTime: number, + startOffset: number + ) => void) +``` +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createWorkletSourceNode(worklet, workletRuntime)`](/docs/core/base-audio-context#createworkletsourcenode-) ## Example diff --git a/packages/react-native-audio-api/src/core/PeriodicWave.ts b/packages/react-native-audio-api/src/core/PeriodicWave.ts index 58c45c014..34dd8b8cb 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -53,8 +53,8 @@ export default class PeriodicWave { options ); this.periodicWave = context.context.createPeriodicWave( - finalOptions.real, - finalOptions.imag, + finalOptions.real!, + finalOptions.imag!, finalOptions.disableNormalization! ); } diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 0628d1935..2e77113ec 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -118,8 +118,8 @@ export interface TPeriodicWaveConstraints { } export interface TPeriodicWaveOptions extends TPeriodicWaveConstraints { - real: Float32Array; - imag: Float32Array; + real?: Float32Array; + imag?: Float32Array; } export interface TAudioBufferOptions { 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 89a7fe050..dba3398fa 100644 --- a/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx +++ b/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx @@ -12,8 +12,8 @@ export default class PeriodicWave { options ); const periodicWave = context.context.createPeriodicWave( - finalOptions.real, - finalOptions.imag, + finalOptions.real!, + finalOptions.imag!, { disableNormalization: finalOptions.disableNormalization } ); this.periodicWave = periodicWave; From 002b5c3e817d06603242445c7ac7ac347737d009 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 11:01:14 +0100 Subject: [PATCH 21/35] feat: absn on web --- .../src/web-core/AudioBufferSourceNode.tsx | 474 ++++++++++++------ .../src/web-core/AudioContext.tsx | 26 +- .../src/web-core/AudioNode.tsx | 6 +- .../src/web-core/BaseAudioContext.tsx | 2 +- .../src/web-core/OfflineAudioContext.tsx | 26 +- 5 files changed, 339 insertions(+), 195 deletions(-) diff --git a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx index 31ff30221..90483889e 100644 --- a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx @@ -6,7 +6,9 @@ import BaseAudioContext from './BaseAudioContext'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import { clamp } from '../utils'; -import { globalTag } from './custom/LoadCustomWasm'; +import { TAudioBufferSourceOptions } from '../types'; +import { AudioBufferSourceOptions } from '../defaults'; +import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; interface ScheduleOptions { rate?: number; @@ -173,8 +175,6 @@ class IStretcherNodeAudioParam implements globalThis.AudioParam { type DefaultSource = globalThis.AudioBufferSourceNode; -type IAudioBufferSourceNode = DefaultSource | IStretcherNode; - declare global { interface Window { [globalTag]: ( @@ -183,10 +183,30 @@ declare global { } } -export default class AudioBufferSourceNode< - T extends IAudioBufferSourceNode = DefaultSource, -> extends AudioScheduledSourceNode { - private _pitchCorrection: boolean; +interface IAudioAPIBufferSourceNodeWeb { + connect(destination: AudioNode | AudioParam): AudioNode | AudioParam; + disconnect(destination?: AudioNode | AudioParam): void; + start(when?: number, offset?: number, duration?: number): void; + stop(when: number): void; + setDetune(value: number, when?: number): void; + setPlaybackRate(value: number, when?: number): void; + get buffer(): AudioBuffer | null; + set buffer(buffer: AudioBuffer | null); + get loop(): boolean; + set loop(value: boolean); + get loopStart(): number; + set loopStart(value: number); + get loopEnd(): number; + set loopEnd(value: number); +} + +class AudioBufferSourceNodeStretcher + extends globalThis.AudioNode + implements IAudioAPIBufferSourceNodeWeb +{ + private stretcherPromise: Promise | null = null; + private node: IStretcherNode | null = null; + private hasBeenStarted: boolean = false; readonly playbackRate: AudioParam; readonly detune: AudioParam; @@ -196,98 +216,175 @@ export default class AudioBufferSourceNode< private _buffer: AudioBuffer | null = null; - constructor(context: BaseAudioContext, node: T, pitchCorrection: boolean) { - super(context, node); - - this._pitchCorrection = pitchCorrection; - - if (pitchCorrection) { - this.detune = new AudioParam( - new IStretcherNodeAudioParam( - 0, - this.setDetune.bind(this), - 'a-rate', - -1200, - 1200, - 0 - ), - context + constructor(context: BaseAudioContext) { + super(); + const promise = async () => { + await globalWasmPromise; + return window[globalTag](new window.AudioContext()); + }; + this.stretcherPromise = promise(); + this.stretcherPromise.then((node) => { + this.node = node; + }); + + this.detune = new AudioParam( + new IStretcherNodeAudioParam( + 0, + this.setDetune.bind(this), + 'a-rate', + -1200, + 1200, + 0 + ), + context + ); + + this.playbackRate = new AudioParam( + new IStretcherNodeAudioParam( + 1, + this.setPlaybackRate.bind(this), + 'a-rate', + 0, + Infinity, + 1 + ), + context + ); + } + + start(when?: number, offset?: number, duration?: number): void { + if (when && when < 0) { + throw new RangeError( + `when must be a finite non-negative number: ${when}` ); + } - this.playbackRate = new AudioParam( - new IStretcherNodeAudioParam( - 1, - this.setPlaybackRate.bind(this), - 'a-rate', - 0, - Infinity, - 1 - ), - context + if (offset && offset < 0) { + throw new RangeError( + `offset must be a finite non-negative number: ${offset}` ); - } else { - this.detune = new AudioParam((node as DefaultSource).detune, context); - this.playbackRate = new AudioParam( - (node as DefaultSource).playbackRate, - context + } + + if (duration && duration < 0) { + throw new RangeError( + `duration must be a finite non-negative number: ${duration}` ); } - } - private isStretcherNode() { - return this._pitchCorrection; - } + if (this.hasBeenStarted) { + throw new InvalidStateError('Cannot call start more than once'); + } - private asStretcher(): IStretcherNode { - return this.node as IStretcherNode; - } + this.hasBeenStarted = true; + const startAt = + !when || when < this.context.currentTime + ? this.context.currentTime + : when; + + const scheduleAction = (node: IStretcherNode) => { + node.schedule({ + loopStart: this._loopStart, + loopEnd: this._loopEnd, + }); + }; + + if (this.loop && this._loopStart !== -1 && this._loopEnd !== -1) { + if (!this.node) { + this.stretcherPromise!.then((node) => { + scheduleAction(node); + }); + } else { + scheduleAction(this.node); + } + } - private asBufferSource(): DefaultSource { - return this.node as DefaultSource; + const startAction = (node: IStretcherNode) => { + node.start( + startAt, + offset, + duration, + this.playbackRate.value, + Math.floor(clamp(this.detune.value / 100, -12, 12)) + ); + }; + if (!this.node) { + this.stretcherPromise!.then((node) => { + startAction(node); + }); + } else { + startAction(this.node); + } } - public setDetune(value: number, when: number = 0): void { - if (!this.isStretcherNode() || !this.hasBeenStarted) { + stop(when: number): void { + if (when < 0) { + throw new RangeError( + `when must be a finite non-negative number: ${when}` + ); + } + const action = (node: IStretcherNode) => { + node.stop(when); + }; + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); return; } - - this.asStretcher().schedule({ - semitones: Math.floor(clamp(value / 100, -12, 12)), - output: when, - }); + action(this.node); } - public setPlaybackRate(value: number, when: number = 0): void { - if (!this.isStretcherNode() || !this.hasBeenStarted) { + setDetune(value: number, when?: number): void { + if (!this.hasBeenStarted) { return; } + const action = (node: IStretcherNode) => { + node.schedule({ + semitones: Math.floor(clamp(value / 100, -12, 12)), + output: when, + }); + }; - this.asStretcher().schedule({ - rate: value, - output: when, - }); + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); + return; + } + + action(this.node); } - public get buffer(): AudioBuffer | null { - if (this.isStretcherNode()) { - return this._buffer; + setPlaybackRate(value: number, when?: number): void { + if (!this.hasBeenStarted) { + return; } + const action = (node: IStretcherNode) => { + node.schedule({ + rate: value, + output: when, + }); + }; - const buffer = this.asBufferSource().buffer; - - if (!buffer) { - return null; + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); + return; } - return new AudioBuffer(buffer); + action(this.node); } - public set buffer(buffer: AudioBuffer | null) { - if (this.isStretcherNode()) { - this._buffer = buffer; + get buffer(): AudioBuffer | null { + return this._buffer; + } - const stretcher = this.asStretcher(); - stretcher.dropBuffers(); + set buffer(buffer: AudioBuffer | null) { + this._buffer = buffer; + + const action = (node: IStretcherNode) => { + node.dropBuffers(); if (!buffer) { return; @@ -299,70 +396,60 @@ export default class AudioBufferSourceNode< channelArrays.push(buffer.getChannelData(i)); } - stretcher.addBuffers(channelArrays); - return; - } + node.addBuffers(channelArrays); + }; - if (!buffer) { - this.asBufferSource().buffer = null; + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); return; } - - this.asBufferSource().buffer = buffer.buffer; + action(this.node); } - public get loop(): boolean { - if (this.isStretcherNode()) { - return this._loop; - } - - return this.asBufferSource().loop; + get loop(): boolean { + return this._loop; } - public set loop(value: boolean) { - if (this.isStretcherNode()) { - this._loop = value; - return; - } - - this.asBufferSource().loop = value; + set loop(value: boolean) { + this._loop = value; } - public get loopStart(): number { - if (this.isStretcherNode()) { - return this._loopStart; - } - - return this.asBufferSource().loopStart; + get loopStart(): number { + return this._loopStart; } - public set loopStart(value: number) { - if (this.isStretcherNode()) { - this._loopStart = value; - return; - } - - this.asBufferSource().loopStart = value; + set loopStart(value: number) { + this._loopStart = value; } - public get loopEnd(): number { - if (this.isStretcherNode()) { - return this._loopEnd; - } + get loopEnd(): number { + return this._loopEnd; + } - return this.asBufferSource().loopEnd; + set loopEnd(value: number) { + this._loopEnd = value; } +} - public set loopEnd(value: number) { - if (this.isStretcherNode()) { - this._loopEnd = value; - return; - } +class AudioBufferSourceNodeWeb + extends globalThis.AudioNode + implements IAudioAPIBufferSourceNodeWeb +{ + private node: DefaultSource; + private hasBeenStarted: boolean = false; + readonly playbackRate: AudioParam; + readonly detune: AudioParam; - this.asBufferSource().loopEnd = value; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { + super(); + this.node = new globalThis.AudioBufferSourceNode(context.context, options); + this.detune = new AudioParam(this.node.detune, context); + this.playbackRate = new AudioParam(this.node.playbackRate, context); } - public start(when?: number, offset?: number, duration?: number): void { + start(when: number = 0, offset?: number, duration?: number): void { if (when && when < 0) { throw new RangeError( `when must be a finite non-negative number: ${when}` @@ -386,34 +473,10 @@ export default class AudioBufferSourceNode< } this.hasBeenStarted = true; - - if (!this.isStretcherNode()) { - this.asBufferSource().start(when, offset, duration); - return; - } - - const startAt = - !when || when < this.context.currentTime - ? this.context.currentTime - : when; - - if (this.loop && this._loopStart !== -1 && this._loopEnd !== -1) { - this.asStretcher().schedule({ - loopStart: this._loopStart, - loopEnd: this._loopEnd, - }); - } - - this.asStretcher().start( - startAt, - offset, - duration, - this.playbackRate.value, - Math.floor(clamp(this.detune.value / 100, -12, 12)) - ); + this.node.start(when, offset, duration); } - public stop(when: number = 0): void { + stop(when: number = 0): void { if (when < 0) { throw new RangeError( `when must be a finite non-negative number: ${when}` @@ -425,12 +488,129 @@ export default class AudioBufferSourceNode< 'Cannot call stop without calling start first' ); } + this.node.stop(when); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setDetune(value: number, when?: number): void { + console.warn('setDetune is not implemented for non-pitch-correction mode'); + } - if (!this.isStretcherNode()) { - this.asBufferSource().stop(when); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setPlaybackRate(value: number, when?: number): void { + console.warn( + 'setPlaybackRate is not implemented for non-pitch-correction mode' + ); + } + + get buffer(): AudioBuffer | null { + const buffer = this.node.buffer; + if (!buffer) { + return null; + } + + return new AudioBuffer(buffer); + } + + set buffer(buffer: AudioBuffer | null) { + if (!buffer) { + this.node.buffer = null; return; } - this.asStretcher().stop(when); + this.node.buffer = buffer.buffer; + } + + get loop(): boolean { + return this.node.loop; + } + + set loop(value: boolean) { + this.node.loop = value; + } + + get loopStart(): number { + return this.node.loopStart; + } + + set loopStart(value: number) { + this.node.loopStart = value; + } + + get loopEnd(): number { + return this.node.loopEnd; + } + + set loopEnd(value: number) { + this.node.loopEnd = value; + } +} + +export default class AudioBufferSourceNode + extends globalThis.AudioNode + implements IAudioAPIBufferSourceNodeWeb +{ + private node: AudioBufferSourceNodeStretcher | AudioBufferSourceNodeWeb; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { + const finalOptions: TAudioBufferSourceOptions = { + ...AudioBufferSourceOptions, + ...options, + }; + super(); + this.node = finalOptions.pitchCorrection + ? new AudioBufferSourceNodeStretcher(context) + : new AudioBufferSourceNodeWeb(context, options); + } + + asAudioBufferSourceNodeWeb(): IAudioAPIBufferSourceNodeWeb { + return this as unknown as IAudioAPIBufferSourceNodeWeb; + } + + start(when: number = 0, offset?: number, duration?: number): void { + this.asAudioBufferSourceNodeWeb().start(when, offset, duration); + } + + stop(when: number = 0): void { + this.asAudioBufferSourceNodeWeb().stop(when); + } + + setDetune(value: number, when?: number): void { + this.asAudioBufferSourceNodeWeb().setDetune(value, when); + } + + setPlaybackRate(value: number, when?: number): void { + this.asAudioBufferSourceNodeWeb().setPlaybackRate(value, when); + } + + get buffer(): AudioBuffer | null { + return this.asAudioBufferSourceNodeWeb().buffer; + } + + set buffer(buffer: AudioBuffer | null) { + this.asAudioBufferSourceNodeWeb().buffer = buffer; + } + + get loop(): boolean { + return this.asAudioBufferSourceNodeWeb().loop; + } + + set loop(value: boolean) { + this.asAudioBufferSourceNodeWeb().loop = value; + } + + get loopStart(): number { + return this.asAudioBufferSourceNodeWeb().loopStart; + } + + set loopStart(value: number) { + this.asAudioBufferSourceNodeWeb().loopStart = value; + } + + get loopEnd(): number { + return this.asAudioBufferSourceNodeWeb().loopEnd; + } + + set loopEnd(value: number) { + this.asAudioBufferSourceNodeWeb().loopEnd = value; } } 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 8c709bbe5..61a9f7137 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -1,9 +1,4 @@ -import { - ContextState, - AudioContextOptions, - // @ts-ignore only-for-this-commit - AudioBufferBaseSourceNodeOptions, -} from '../types'; +import { ContextState, AudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -17,7 +12,6 @@ import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import ConvolverNode from './ConvolverNode'; -import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConstantSourceNode from './ConstantSourceNode'; export default class AudioContext implements BaseAudioContext { @@ -75,22 +69,8 @@ export default class AudioContext implements BaseAudioContext { return new ConvolverNode(this); } - async createBufferSource( - options?: AudioBufferBaseSourceNodeOptions - ): Promise { - if (!options || !options.pitchCorrection) { - return new AudioBufferSourceNode( - this, - this.context.createBufferSource(), - false - ); - } - - await globalWasmPromise; - - const wasmStretch = await window[globalTag](this.context); - - return new AudioBufferSourceNode(this, wasmStretch, true); + createBufferSource(): AudioBufferSourceNode { + return new AudioBufferSourceNode(this); } createBuffer( diff --git a/packages/react-native-audio-api/src/web-core/AudioNode.tsx b/packages/react-native-audio-api/src/web-core/AudioNode.tsx index a66113f1f..a66bb798c 100644 --- a/packages/react-native-audio-api/src/web-core/AudioNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioNode.tsx @@ -38,12 +38,16 @@ export default class AudioNode { return destination; } - public disconnect(destination?: AudioNode): void { + public disconnect(destination?: AudioNode | AudioParam): void { if (destination === undefined) { this.node.disconnect(); return; } + if (destination instanceof AudioParam) { + this.node.disconnect(destination.param); + return; + } this.node.disconnect(destination.node); } } 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 701bea3d1..b82234a1d 100644 --- a/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/BaseAudioContext.tsx @@ -25,7 +25,7 @@ export default interface BaseAudioContext { createStereoPanner(): StereoPannerNode; createBiquadFilter(): BiquadFilterNode; createConvolver(): ConvolverNode; - createBufferSource(): Promise; + createBufferSource(): AudioBufferSourceNode; createBuffer( numOfChannels: number, length: number, 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 ed41d0969..4f4fce5ff 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -1,9 +1,4 @@ -import { - ContextState, - OfflineAudioContextOptions, - // @ts-ignore only-for-this-commit - AudioBufferBaseSourceNodeOptions, -} from '../types'; +import { ContextState, OfflineAudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -17,7 +12,6 @@ import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import ConstantSourceNode from './ConstantSourceNode'; -import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConvolverNode from './ConvolverNode'; export default class OfflineAudioContext implements BaseAudioContext { @@ -81,22 +75,8 @@ export default class OfflineAudioContext implements BaseAudioContext { return new ConvolverNode(this); } - async createBufferSource( - options?: AudioBufferBaseSourceNodeOptions - ): Promise { - if (!options || !options.pitchCorrection) { - return new AudioBufferSourceNode( - this, - this.context.createBufferSource(), - false - ); - } - - await globalWasmPromise; - - const wasmStretch = await window[globalTag](this.context); - - return new AudioBufferSourceNode(this, wasmStretch, true); + createBufferSource(): AudioBufferSourceNode { + return new AudioBufferSourceNode(this); } createBuffer( From 829c0197fe175541e7016b527de9337afb58fe5c Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 11:51:45 +0100 Subject: [PATCH 22/35] feat: delay node --- .../cpp/audioapi/HostObjects/utils/NodeOptions.h | 5 +++++ .../audioapi/HostObjects/utils/NodeOptionsParser.h | 11 +++++++++++ .../common/cpp/audioapi/core/BaseAudioContext.cpp | 4 ++-- .../common/cpp/audioapi/core/BaseAudioContext.h | 3 ++- .../common/cpp/audioapi/core/effects/DelayNode.cpp | 9 ++++++--- .../common/cpp/audioapi/core/effects/DelayNode.h | 3 ++- .../src/core/BaseAudioContext.ts | 3 +-- packages/react-native-audio-api/src/core/DelayNode.ts | 7 +++++-- packages/react-native-audio-api/src/defaults.ts | 7 +++++++ packages/react-native-audio-api/src/interfaces.ts | 3 ++- packages/react-native-audio-api/src/types.ts | 5 +++++ .../src/web-core/AudioContext.tsx | 2 +- .../react-native-audio-api/src/web-core/DelayNode.tsx | 6 +++++- .../src/web-core/OfflineAudioContext.tsx | 2 +- 14 files changed, 55 insertions(+), 15 deletions(-) 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 index de581e83e..f47cf2d04 100644 --- 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 @@ -79,4 +79,9 @@ struct AudioBufferOptions { float sampleRate = 44100.0f; }; +struct DelayOptions : AudioNodeOptions { + float maxDelayTime = 1.0f; + float delayTime = 0.0f; +}; + } // 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 index 3366d53dc..0efa856f4 100644 --- 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 @@ -223,4 +223,15 @@ std::shared_ptr parseAudioBufferOptions( return std::make_shared(options); } +std::shared_ptr parseDelayOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + DelayOptions options(*nodeOptions.get()); + options.maxDelayTime = + static_cast(optionsObject.getProperty(runtime, "maxDelayTime").getNumber()); + options.delayTime = + static_cast(optionsObject.getProperty(runtime, "delayTime").getNumber()); + return std::make_shared(options); +} } // namespace audioapi::option_parser 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 67e8c8cdf..8fdf283db 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 @@ -153,8 +153,8 @@ std::shared_ptr BaseAudioContext::createStereoPanner( return stereoPanner; } -std::shared_ptr BaseAudioContext::createDelay(float maxDelayTime) { - auto delay = std::make_shared(this, maxDelayTime); +std::shared_ptr BaseAudioContext::createDelay(std::shared_ptr options) { + auto delay = std::make_shared(this, options); nodeManager_->addProcessingNode(delay); return delay; } 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 6d228d093..c3e6f0957 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 @@ -48,6 +48,7 @@ class BaseAudioBufferSourceOptions; class AudioBufferSourceOptions; class StreamerOptions; class AudioBufferOptions; +class DelayOptions; class BaseAudioContext { public: @@ -77,7 +78,7 @@ class BaseAudioContext { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createDelay(float maxDelayTime); + std::shared_ptr createDelay(std::shared_ptr options); std::shared_ptr createIIRFilter( const std::vector &feedforward, const std::vector &feedback); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index cf2e44c5b..1f7a559b0 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,11 +8,13 @@ namespace audioapi { -DelayNode::DelayNode(BaseAudioContext *context, float maxDelayTime) : AudioNode(context) { - delayTimeParam_ = std::make_shared(0, 0, maxDelayTime, context); +DelayNode::DelayNode(BaseAudioContext *context, std::shared_ptr options) + : AudioNode(context, options) { + delayTimeParam_ = + std::make_shared(options->delayTime, 0, options->maxDelayTime, context); delayBuffer_ = std::make_shared( static_cast( - maxDelayTime * context->getSampleRate() + + options->maxDelayTime * context->getSampleRate() + 1), // +1 to enable delayTime equal to maxDelayTime channelCount_, context->getSampleRate()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index 15ab28f10..8ace8551d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -9,10 +9,11 @@ namespace audioapi { class AudioBus; +class DelayOptions; class DelayNode : public AudioNode { public: - explicit DelayNode(BaseAudioContext *context, float maxDelayTime); + explicit DelayNode(BaseAudioContext *context, std::shared_ptr options); [[nodiscard]] std::shared_ptr getDelayTimeParam() const; diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 6bb503a88..f4277cb25 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -149,8 +149,7 @@ export default class BaseAudioContext { } createDelay(maxDelayTime?: number): DelayNode { - const maxTime = maxDelayTime ?? 1.0; - return new DelayNode(this, this.context.createDelay(maxTime)); + return new DelayNode(this, { maxDelayTime }); } createStereoPanner(): StereoPannerNode { diff --git a/packages/react-native-audio-api/src/core/DelayNode.ts b/packages/react-native-audio-api/src/core/DelayNode.ts index ac4ab9d86..5cb645086 100644 --- a/packages/react-native-audio-api/src/core/DelayNode.ts +++ b/packages/react-native-audio-api/src/core/DelayNode.ts @@ -1,12 +1,15 @@ -import { IDelayNode } from '../interfaces'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; +import { TDelayOptions } from '../types'; +import { DelayOptions } from '../defaults'; export default class DelayNode extends AudioNode { readonly delayTime: AudioParam; - constructor(context: BaseAudioContext, delay: IDelayNode) { + constructor(context: BaseAudioContext, options?: TDelayOptions) { + const finalOptions = { ...DelayOptions, ...options }; + const delay = context.context.createDelay(finalOptions); super(context, delay); this.delayTime = new AudioParam(delay.delayTime, context); } diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index 729454c83..cc6f0eee2 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -11,6 +11,7 @@ import { TBaseAudioBufferSourceOptions, TAudioBufferSourceOptions, TAudioBufferOptions, + TDelayOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -85,3 +86,9 @@ export const AudioBufferOptions: TAudioBufferOptions = { length: 0, // always overwritten by provided value, only placeholder sampleRate: 44100, // always overwritten by provided value, only placeholder }; + +export const DelayOptions: TDelayOptions = { + ...AudioNodeOptions, + maxDelayTime: 1.0, + delayTime: 0.0, +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index de1b0dd6b..54af1d1cb 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -16,6 +16,7 @@ import { TAudioBufferSourceOptions, TStreamerOptions, TAudioBufferOptions, + TDelayOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -81,7 +82,7 @@ export interface IBaseAudioContext { createBufferSource: ( audioBufferSourceOptions: TAudioBufferSourceOptions ) => IAudioBufferSourceNode; - createDelay(maxDelayTime: number): IDelayNode; + createDelay(delayOptions: TDelayOptions): IDelayNode; createIIRFilter: ( feedforward: number[], feedback: number[] diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 211c6d269..c40019fb5 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -132,3 +132,8 @@ export interface IIRFilterNodeOptions { feedforward: number[]; feedback: number[]; } + +export interface TDelayOptions extends TAudioNodeOptions { + maxDelayTime?: number; + delayTime?: number; +} 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 b82d3e0fc..f1919dd0c 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -63,7 +63,7 @@ export default class AudioContext implements BaseAudioContext { } createDelay(maxDelayTime?: number): DelayNode { - return new DelayNode(this, this.context.createDelay(maxDelayTime)); + return new DelayNode(this, { maxDelayTime }); } createStereoPanner(): StereoPannerNode { diff --git a/packages/react-native-audio-api/src/web-core/DelayNode.tsx b/packages/react-native-audio-api/src/web-core/DelayNode.tsx index 68e8e54a6..48942cf7f 100644 --- a/packages/react-native-audio-api/src/web-core/DelayNode.tsx +++ b/packages/react-native-audio-api/src/web-core/DelayNode.tsx @@ -1,11 +1,15 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; +import { TDelayOptions } from '../types'; +import { DelayOptions } from '../defaults'; export default class DelayNode extends AudioNode { readonly delayTime: AudioParam; - constructor(context: BaseAudioContext, delay: globalThis.DelayNode) { + constructor(context: BaseAudioContext, options?: TDelayOptions) { + const finalOptions = { ...DelayOptions, ...options }; + const delay = new globalThis.DelayNode(context.context, finalOptions); super(context, delay); this.delayTime = new AudioParam(delay.delayTime, 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 151d0ede7..e61b9091a 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -70,7 +70,7 @@ export default class OfflineAudioContext implements BaseAudioContext { } createDelay(maxDelayTime?: number): DelayNode { - return new DelayNode(this, this.context.createDelay(maxDelayTime)); + return new DelayNode(this, { maxDelayTime }); } createStereoPanner(): StereoPannerNode { From 2c6a512da7975d335ed3d83055c6b3fdaa13c022 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 12:14:15 +0100 Subject: [PATCH 23/35] feat: iirfilter --- .../BaseAudioContextHostObject.cpp | 33 +++++-------------- .../audioapi/HostObjects/utils/NodeOptions.h | 6 ++++ .../HostObjects/utils/NodeOptionsParser.h | 27 +++++++++++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 5 ++- .../cpp/audioapi/core/BaseAudioContext.h | 5 ++- .../audioapi/core/effects/IIRFilterNode.cpp | 11 +++---- .../cpp/audioapi/core/effects/IIRFilterNode.h | 7 ++-- .../src/core/BaseAudioContext.ts | 15 ++------- .../src/core/IIRFilterNode.ts | 12 +++++++ .../react-native-audio-api/src/interfaces.ts | 6 ++-- packages/react-native-audio-api/src/types.ts | 2 +- .../src/web-core/AudioContext.tsx | 13 ++------ .../src/web-core/BaseAudioContext.tsx | 4 +-- .../src/web-core/IIRFilterNode.tsx | 15 +++++++++ .../src/web-core/OfflineAudioContext.tsx | 13 ++------ 15 files changed, 95 insertions(+), 79 deletions(-) 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 391202f75..3b3a1a53a 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 @@ -167,7 +167,9 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = context_->createStreamer(); + auto options = args[0].asObject(runtime); + auto streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); + auto streamer = context_->createStreamer(streamerOptions); auto streamerOptions = std::make_shared(); if (!args[0].isUndefined()) { auto options = args[0].asObject(runtime); @@ -201,8 +203,9 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createDelay) { - auto maxDelayTime = static_cast(args[0].getNumber()); - auto delayNode = context_->createDelay(maxDelayTime); + auto options = args[0].asObject(runtime); + auto delayOptions = audioapi::option_parser::parseDelayOptions(runtime, options); + auto delayNode = context_->createDelay(delayOptions); auto delayNodeHostObject = std::make_shared(delayNode); auto jsiObject = jsi::Object::createFromHostObject(runtime, delayNodeHostObject); jsiObject.setExternalMemoryPressure(runtime, delayNodeHostObject->getSizeInBytes()); @@ -226,27 +229,9 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBiquadFilter) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createIIRFilter) { - auto feedforwardArray = args[0].asObject(runtime).asArray(runtime); - auto feedbackArray = args[1].asObject(runtime).asArray(runtime); - - size_t feedforwardLength = feedforwardArray.length(runtime); - size_t feedbackLength = feedbackArray.length(runtime); - - std::vector feedforward; - std::vector feedback; - - feedforward.reserve(feedforwardLength); - feedback.reserve(feedbackLength); - - for (size_t i = 0; i < feedforwardLength; ++i) { - feedforward.push_back(feedforwardArray.getValueAtIndex(runtime, i).asNumber()); - } - - for (size_t i = 0; i < feedbackLength; ++i) { - feedback.push_back(feedbackArray.getValueAtIndex(runtime, i).asNumber()); - } - - auto iirFilter = context_->createIIRFilter(feedforward, feedback); + auto options = args[0].asObject(runtime); + auto iirFilterOptions = audioapi::option_parser::parseIIRFilterOptions(runtime, options); + auto iirFilter = context_->createIIRFilter(iirFilterOptions); auto iirFilterHostObject = std::make_shared(iirFilter); return jsi::Object::createFromHostObject(runtime, iirFilterHostObject); } 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 index f47cf2d04..07a0f7f69 100644 --- 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 @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -84,4 +85,9 @@ struct DelayOptions : AudioNodeOptions { float delayTime = 0.0f; }; +struct IIRFilterOptions : AudioNodeOptions { + std::vector feedforward; + std::vector feedback; +}; + } // 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 index 0efa856f4..23d9bee8e 100644 --- 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 @@ -234,4 +234,31 @@ std::shared_ptr parseDelayOptions( static_cast(optionsObject.getProperty(runtime, "delayTime").getNumber()); return std::make_shared(options); } + +std::shared_ptr parseIIRFilterOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + IIRFilterOptions options(*nodeOptions.get()); + + auto feedforwardArray = + optionsObject.getProperty(runtime, "feedforward").asObject(runtime).asArray(runtime); + size_t feedforwardLength = feedforwardArray.size(runtime); + options.feedforward.reserve(feedforwardLength); + for (size_t i = 0; i < feedforwardLength; ++i) { + options.feedforward.push_back( + static_cast(feedforwardArray.getValueAtIndex(runtime, i).getNumber())); + } + + auto feedbackArray = + optionsObject.getProperty(runtime, "feedback").asObject(runtime).asArray(runtime); + size_t feedbackLength = feedbackArray.size(runtime); + options.feedback.reserve(feedbackLength); + for (size_t i = 0; i < feedbackLength; ++i) { + options.feedback.push_back( + static_cast(feedbackArray.getValueAtIndex(runtime, i).getNumber())); + } + + return std::make_shared(options); +} } // namespace audioapi::option_parser 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 8fdf283db..9cd00071c 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 @@ -174,9 +174,8 @@ std::shared_ptr BaseAudioContext::createBufferSource( } std::shared_ptr BaseAudioContext::createIIRFilter( - const std::vector &feedforward, - const std::vector &feedback) { - auto iirFilter = std::make_shared(this, feedforward, feedback); + std::shared_ptr options) { + auto iirFilter = std::make_shared(this, options); nodeManager_->addProcessingNode(iirFilter); return iirFilter; } 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 c3e6f0957..910d35f6b 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 @@ -49,6 +49,7 @@ class AudioBufferSourceOptions; class StreamerOptions; class AudioBufferOptions; class DelayOptions; +class IIRFilterOptions; class BaseAudioContext { public: @@ -79,9 +80,7 @@ class BaseAudioContext { std::weak_ptr runtime, bool shouldLockRuntime = true); std::shared_ptr createDelay(std::shared_ptr options); - std::shared_ptr createIIRFilter( - const std::vector &feedforward, - const std::vector &feedback); + std::shared_ptr createIIRFilter(std::shared_ptr options); std::shared_ptr createOscillator(std::shared_ptr options); std::shared_ptr createConstantSource( std::shared_ptr options); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index b9d22aea3..28f105aaf 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -23,6 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -34,13 +35,11 @@ namespace audioapi { -IIRFilterNode::IIRFilterNode( - BaseAudioContext *context, - const std::vector &feedforward, - const std::vector &feedback) - : AudioNode(context), feedforward_(feedforward), feedback_(feedback) { +IIRFilterNode::IIRFilterNode(BaseAudioContext *context, std::shared_ptr options) + : AudioNode(context, options), + feedforward_(options->feedforward), + feedback_(options->feedback) { isInitialized_ = true; - channelCountMode_ = ChannelCountMode::MAX; int maxChannels = MAX_CHANNEL_COUNT; xBuffers_.resize(maxChannels); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index f9ec07a19..7ea662813 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -33,13 +33,12 @@ namespace audioapi { +class IIRFilterOptions; + class IIRFilterNode : public AudioNode { public: - explicit IIRFilterNode( - BaseAudioContext *context, - const std::vector &feedforward, - const std::vector &feedback); + explicit IIRFilterNode(BaseAudioContext *context, std::shared_ptr options); void getFrequencyResponse( const float *frequencyArray, diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index f4277cb25..607921a93 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -4,11 +4,7 @@ import { NotSupportedError, } from '../errors'; import { IBaseAudioContext } from '../interfaces'; -import { - AudioWorkletRuntime, - ContextState, - IIRFilterNodeOptions, -} from '../types'; +import { AudioWorkletRuntime, ContextState } from '../types'; import { assertWorkletsEnabled } from '../utils'; import AnalyserNode from './AnalyserNode'; import AudioBuffer from './AudioBuffer'; @@ -164,9 +160,7 @@ export default class BaseAudioContext { return new AudioBufferSourceNode(this); } - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode { - const feedforward = options.feedforward; - const feedback = options.feedback; + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { if (feedforward.length < 1 || feedforward.length > 20) { throw new NotSupportedError( `The provided feedforward array has length (${feedforward.length}) outside the range [1, 20]` @@ -190,10 +184,7 @@ export default class BaseAudioContext { ); } - return new IIRFilterNode( - this, - this.context.createIIRFilter(feedforward, feedback) - ); + return new IIRFilterNode(this, { feedforward, feedback }); } createBufferQueueSource(): AudioBufferQueueSourceNode { diff --git a/packages/react-native-audio-api/src/core/IIRFilterNode.ts b/packages/react-native-audio-api/src/core/IIRFilterNode.ts index abc330293..e7bf4c01d 100644 --- a/packages/react-native-audio-api/src/core/IIRFilterNode.ts +++ b/packages/react-native-audio-api/src/core/IIRFilterNode.ts @@ -1,8 +1,20 @@ import { NotSupportedError } from '../errors'; import { IIIRFilterNode } from '../interfaces'; import AudioNode from './AudioNode'; +import { TIIRFilterOptions } from '../types'; +import { AudioNodeOptions } from '../defaults'; +import BaseAudioContext from './BaseAudioContext'; export default class IIRFilterNode extends AudioNode { + constructor(context: BaseAudioContext, options: TIIRFilterOptions) { + const finalOptions: TIIRFilterOptions = { + ...AudioNodeOptions, + ...options, + }; + const iirFilterNode = context.context.createIIRFilter(finalOptions); + super(context, iirFilterNode); + } + public getFrequencyResponse( frequencyArray: Float32Array, magResponseOutput: Float32Array, diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 54af1d1cb..2f48416ee 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -17,6 +17,7 @@ import { TStreamerOptions, TAudioBufferOptions, TDelayOptions, + TIIRFilterOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -83,10 +84,7 @@ export interface IBaseAudioContext { audioBufferSourceOptions: TAudioBufferSourceOptions ) => IAudioBufferSourceNode; createDelay(delayOptions: TDelayOptions): IDelayNode; - createIIRFilter: ( - feedforward: number[], - feedback: number[] - ) => IIIRFilterNode; + createIIRFilter: (IIRFilterOptions: TIIRFilterOptions) => IIIRFilterNode; createBufferQueueSource: ( audioBufferQueueSourceOptions: TBaseAudioBufferSourceOptions ) => IAudioBufferQueueSourceNode; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index c40019fb5..b4cf35c98 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -128,7 +128,7 @@ export interface TAudioBufferOptions { sampleRate: number; } -export interface IIRFilterNodeOptions { +export interface TIIRFilterOptions extends TAudioNodeOptions { feedforward: number[]; feedback: number[]; } 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 f1919dd0c..855a1b511 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -1,8 +1,4 @@ -import { - ContextState, - AudioContextOptions, - IIRFilterNodeOptions, -} from '../types'; +import { ContextState, AudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -78,11 +74,8 @@ export default class AudioContext implements BaseAudioContext { return new ConvolverNode(this); } - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode { - return new IIRFilterNode( - this, - this.context.createIIRFilter(options.feedforward, options.feedback) - ); + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { + return new IIRFilterNode(this, { feedforward, feedback }); } createBufferSource(): AudioBufferSourceNode { 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 aea3b0289..07b53e183 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, IIRFilterNodeOptions } from '../types'; +import { ContextState } from '../types'; import AnalyserNode from './AnalyserNode'; import AudioDestinationNode from './AudioDestinationNode'; import AudioBuffer from './AudioBuffer'; @@ -27,7 +27,7 @@ export default interface BaseAudioContext { createDelay(maxDelayTime?: number): DelayNode; createStereoPanner(): StereoPannerNode; createBiquadFilter(): BiquadFilterNode; - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode; + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode; createConvolver(): ConvolverNode; createBufferSource(): AudioBufferSourceNode; createBuffer( diff --git a/packages/react-native-audio-api/src/web-core/IIRFilterNode.tsx b/packages/react-native-audio-api/src/web-core/IIRFilterNode.tsx index 325d33229..df4a680c1 100644 --- a/packages/react-native-audio-api/src/web-core/IIRFilterNode.tsx +++ b/packages/react-native-audio-api/src/web-core/IIRFilterNode.tsx @@ -1,7 +1,22 @@ import { NotSupportedError } from '../errors'; import AudioNode from './AudioNode'; +import { TIIRFilterOptions } from '../types'; +import { AudioNodeOptions } from '../defaults'; +import BaseAudioContext from './BaseAudioContext'; export default class IIRFilterNode extends AudioNode { + constructor(context: BaseAudioContext, options: TIIRFilterOptions) { + const finalOptions: TIIRFilterOptions = { + ...AudioNodeOptions, + ...options, + }; + const iirFilterNode = new globalThis.IIRFilterNode(context.context, { + feedforward: finalOptions.feedforward, + feedback: finalOptions.feedback, + }); + super(context, iirFilterNode); + } + public getFrequencyResponse( frequencyArray: Float32Array, magResponseOutput: Float32Array, 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 e61b9091a..53f89552e 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -1,8 +1,4 @@ -import { - ContextState, - OfflineAudioContextOptions, - IIRFilterNodeOptions, -} from '../types'; +import { ContextState, OfflineAudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -85,11 +81,8 @@ export default class OfflineAudioContext implements BaseAudioContext { return new ConvolverNode(this); } - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode { - return new IIRFilterNode( - this, - this.context.createIIRFilter(options.feedforward, options.feedback) - ); + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { + return new IIRFilterNode(this, { feedforward, feedback }); } createBufferSource(): AudioBufferSourceNode { From e4877099488a3e94af9502476a6b8b22ad9fb061 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 12:44:11 +0100 Subject: [PATCH 24/35] test: test fix --- .../audioapi/HostObjects/utils/NodeOptions.h | 9 ++++++ .../cpp/audioapi/core/BaseAudioContext.cpp | 3 +- .../sources/AudioBufferQueueSourceNode.cpp | 7 +++-- .../common/cpp/test/src/DelayTest.cpp | 6 ++-- .../common/cpp/test/src/IIRFilterTest.cpp | 6 ++-- .../common/cpp/test/src/OscillatorTest.cpp | 3 +- .../cpp/test/src/biquad/BiquadFilterTest.cpp | 28 +++++++++++++------ 7 files changed, 43 insertions(+), 19 deletions(-) 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 index 07a0f7f69..2e60010d2 100644 --- 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 @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -88,6 +89,14 @@ struct DelayOptions : AudioNodeOptions { struct IIRFilterOptions : AudioNodeOptions { std::vector feedforward; std::vector feedback; + + IIRFilterOptions() = default; + + IIRFilterOptions(const std::vector &ff, const std::vector &fb) + : feedforward(ff), feedback(fb) {} + + IIRFilterOptions(std::vector &&ff, std::vector &&fb) + : feedforward(std::move(ff)), feedback(std::move(fb)) {} }; } // namespace audioapi 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 9cd00071c..0cec3fcbb 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 @@ -130,9 +130,8 @@ std::shared_ptr BaseAudioContext::createConstantSource( std::shared_ptr BaseAudioContext::createStreamer( std::shared_ptr options) { - auto streamer = std::make_shared(this, options); #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = std::make_shared(this); + auto streamer = std::make_shared(this, options); nodeManager_->addSourceNode(streamer); return streamer; #else 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 57fcd8988..21d787daa 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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,7 +9,6 @@ #include #include -#include #include #include #include @@ -31,8 +31,9 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( addExtraTailFrames_ = true; int extraTailFrames = static_cast(stretch_->inputLatency() + stretch_->outputLatency()); - tailBuffer_ = - std::make_shared(channelCount_, extraTailFrames, context_->getSampleRate()); + auto audioBufferOptions = std::make_shared( + extraTailFrames, static_cast(channelCount_), context->getSampleRate()); + tailBuffer_ = std::make_shared(audioBufferOptions); tailBuffer_->bus_->zero(); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index 8f247607f..d0c2942b6 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class DelayTest : public ::testing::Test { class TestableDelayNode : public DelayNode { public: - explicit TestableDelayNode(BaseAudioContext *context) : DelayNode(context, 1) {} + explicit TestableDelayNode(BaseAudioContext *context) + : DelayNode(context, std::make_shared()) {} void setDelayTimeParam(float value) { getDelayTimeParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableDelayNode : public DelayNode { }; TEST_F(DelayTest, DelayCanBeCreated) { - auto delay = context->createDelay(1.0f); + auto delay = context->createDelay(std::make_shared()); ASSERT_NE(delay, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp index 17e263f8a..f71fbcf35 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -91,7 +92,7 @@ class IIRFilterTest : public ::testing::Test { TEST_F(IIRFilterTest, IIRFilterCanBeCreated) { const std::vector feedforward = {1.0}; const std::vector feedback = {1.0}; - auto node = context->createIIRFilter(feedforward, feedback); + auto node = context->createIIRFilter(std::make_shared(feedforward, feedback)); ASSERT_NE(node, nullptr); } @@ -99,7 +100,8 @@ TEST_F(IIRFilterTest, GetFrequencyResponse) { const std::vector feedforward = {0.0050662636, 0.0101325272, 0.0050662636}; const std::vector feedback = {1.0632762845, -1.9797349456, 0.9367237155}; - auto node = std::make_shared(context.get(), feedforward, feedback); + auto node = std::make_shared( + context.get(), std::make_shared(feedforward, feedback)); float frequency = 1000.0f; float normalizedFrequency = frequency / nyquistFrequency; diff --git a/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp index a8d732665..0a6abf9d5 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -21,6 +22,6 @@ class OscillatorTest : public ::testing::Test { }; TEST_F(OscillatorTest, OscillatorCanBeCreated) { - auto osc = context->createOscillator(); + auto osc = context->createOscillator(std::make_shared()); ASSERT_NE(osc, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp index f68677075..fe833ea23 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -16,7 +17,8 @@ void BiquadFilterTest::expectCoefficientsNear( } void BiquadFilterTest::testLowpass(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowpassCoefficients(normalizedFrequency, Q); @@ -24,7 +26,8 @@ void BiquadFilterTest::testLowpass(float frequency, float Q) { } void BiquadFilterTest::testHighpass(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighpassCoefficients(normalizedFrequency, Q); @@ -32,7 +35,8 @@ void BiquadFilterTest::testHighpass(float frequency, float Q) { } void BiquadFilterTest::testBandpass(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setBandpassCoefficients(normalizedFrequency, Q); @@ -40,7 +44,8 @@ void BiquadFilterTest::testBandpass(float frequency, float Q) { } void BiquadFilterTest::testNotch(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setNotchCoefficients(normalizedFrequency, Q); @@ -48,7 +53,8 @@ void BiquadFilterTest::testNotch(float frequency, float Q) { } void BiquadFilterTest::testAllpass(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setAllpassCoefficients(normalizedFrequency, Q); @@ -56,7 +62,8 @@ void BiquadFilterTest::testAllpass(float frequency, float Q) { } void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setPeakingCoefficients(normalizedFrequency, Q, gain); @@ -64,7 +71,8 @@ void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { } void BiquadFilterTest::testLowshelf(float frequency, float gain) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowshelfCoefficients(normalizedFrequency, gain); @@ -72,7 +80,8 @@ void BiquadFilterTest::testLowshelf(float frequency, float gain) { } void BiquadFilterTest::testHighshelf(float frequency, float gain) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighshelfCoefficients(normalizedFrequency, gain); @@ -218,7 +227,8 @@ TEST_P(BiquadFilterGainTest, SetHighshelfCoefficients) { } TEST_F(BiquadFilterTest, GetFrequencyResponse) { - auto node = std::make_shared(context.get()); + auto node = + std::make_shared(context.get(), std::make_shared()); float frequency = 1000.0f; float Q = 1.0f; From bf1ad3de1225dbb796e125b305c95a3756e0a555 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 15:05:45 +0100 Subject: [PATCH 25/35] feat: better absn for web --- apps/fabric-example/ios/Podfile.lock | 10 +- .../BaseAudioContextHostObject.cpp | 3 - .../audioapi/HostObjects/utils/NodeOptions.h | 2 + .../HostObjects/utils/NodeOptionsParser.h | 4 +- .../src/web-core/AudioBufferSourceNode.tsx | 93 ++++++++++++++++--- .../src/web-core/AudioNode.tsx | 2 +- 6 files changed, 89 insertions(+), 25 deletions(-) diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index fd9a098a5..fd0bbb755 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2675,7 +2675,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.18.0): + - RNScreens (4.17.1): - boost - DoubleConversion - fast_float @@ -2702,10 +2702,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.18.0) + - RNScreens/common (= 4.17.1) - SocketRocket - Yoga - - RNScreens/common (4.18.0): + - RNScreens/common (4.17.1): - boost - DoubleConversion - fast_float @@ -3216,10 +3216,10 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: c5c4f5280e4ae0f9f4a739c64c4260fe0b3edaf1 ReactCodegen: 096bbbb2498ca55f385e2fbd465bfa0211ee8295 ReactCommon: 25c7f94aee74ddd93a8287756a8ac0830a309544 - RNAudioAPI: c763dbacdb8d89b7ce829484306df54322a7d951 + RNAudioAPI: c7dc7b491a0e4b23535a55fd9b4a00d0f803f4bb RNGestureHandler: f1dd7f92a0faa2868a919ab53bb9d66eb4ebfcf5 RNReanimated: e4993dd98196c698cbacc1441a4ac5b855ae56dc - RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479 + RNScreens: 833237c48c756d40764540246a501b47dadb2cac RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6 RNWorklets: d4553da98908962b6b834d5f2d26525b0d6840ad SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 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 3b3a1a53a..bb3f1e837 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 @@ -167,9 +167,6 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto options = args[0].asObject(runtime); - auto streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); - auto streamer = context_->createStreamer(streamerOptions); auto streamerOptions = std::make_shared(); if (!args[0].isUndefined()) { auto options = args[0].asObject(runtime); 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 index 2e60010d2..b1eb35453 100644 --- 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 @@ -92,6 +92,8 @@ struct IIRFilterOptions : AudioNodeOptions { IIRFilterOptions() = default; + explicit IIRFilterOptions(const AudioNodeOptions options) : AudioNodeOptions(options) {} + IIRFilterOptions(const std::vector &ff, const std::vector &fb) : feedforward(ff), feedback(fb) {} 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 index 23d9bee8e..59fcf8328 100644 --- 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 @@ -177,7 +177,7 @@ std::shared_ptr parseBaseAudioBufferSourceOptions( options.playbackRate = static_cast(optionsObject.getProperty(runtime, "playbackRate").getNumber()); options.pitchCorrection = - static_cast(optionsObject.getProperty(runtime, "pitchCorrection").getNumber()); + static_cast(optionsObject.getProperty(runtime, "pitchCorrection").getBool()); return std::make_shared(options); } @@ -193,7 +193,7 @@ std::shared_ptr parseAudioBufferSourceOptions( .asHostObject(runtime); options.buffer = bufferHostObject->audioBuffer_; } - options.loop = static_cast(optionsObject.getProperty(runtime, "loop").getNumber()); + options.loop = static_cast(optionsObject.getProperty(runtime, "loop").getBool()); options.loopStart = static_cast(optionsObject.getProperty(runtime, "loopStart").getNumber()); options.loopEnd = static_cast(optionsObject.getProperty(runtime, "loopEnd").getNumber()); diff --git a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx index 90483889e..afe18daad 100644 --- a/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx @@ -3,7 +3,7 @@ import { InvalidStateError, RangeError } from '../errors'; import AudioParam from './AudioParam'; import AudioBuffer from './AudioBuffer'; import BaseAudioContext from './BaseAudioContext'; -import AudioScheduledSourceNode from './AudioScheduledSourceNode'; +import AudioNode from './AudioNode'; import { clamp } from '../utils'; import { TAudioBufferSourceOptions } from '../types'; @@ -200,13 +200,11 @@ interface IAudioAPIBufferSourceNodeWeb { set loopEnd(value: number); } -class AudioBufferSourceNodeStretcher - extends globalThis.AudioNode - implements IAudioAPIBufferSourceNodeWeb -{ +class AudioBufferSourceNodeStretcher implements IAudioAPIBufferSourceNodeWeb { private stretcherPromise: Promise | null = null; private node: IStretcherNode | null = null; private hasBeenStarted: boolean = false; + private context: BaseAudioContext; readonly playbackRate: AudioParam; readonly detune: AudioParam; @@ -217,11 +215,11 @@ class AudioBufferSourceNodeStretcher private _buffer: AudioBuffer | null = null; constructor(context: BaseAudioContext) { - super(); const promise = async () => { await globalWasmPromise; return window[globalTag](new window.AudioContext()); }; + this.context = context; this.stretcherPromise = promise(); this.stretcherPromise.then((node) => { this.node = node; @@ -252,6 +250,49 @@ class AudioBufferSourceNodeStretcher ); } + connect(destination: AudioNode | AudioParam): AudioNode | AudioParam { + const action = (node: IStretcherNode) => { + if (destination instanceof AudioParam) { + node.connect(destination.param); + return; + } + node.connect(destination.node); + }; + + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); + } else { + action(this.node); + } + + return destination; + } + + disconnect(destination?: AudioNode | AudioParam): void { + const action = (node: IStretcherNode) => { + if (destination === undefined) { + node.disconnect(); + return; + } + + if (destination instanceof AudioParam) { + node.disconnect(destination.param); + return; + } + node.disconnect(destination.node); + }; + + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); + } else { + action(this.node); + } + } + start(when?: number, offset?: number, duration?: number): void { if (when && when < 0) { throw new RangeError( @@ -433,17 +474,13 @@ class AudioBufferSourceNodeStretcher } } -class AudioBufferSourceNodeWeb - extends globalThis.AudioNode - implements IAudioAPIBufferSourceNodeWeb -{ +class AudioBufferSourceNodeWeb implements IAudioAPIBufferSourceNodeWeb { private node: DefaultSource; private hasBeenStarted: boolean = false; readonly playbackRate: AudioParam; readonly detune: AudioParam; constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { - super(); this.node = new globalThis.AudioBufferSourceNode(context.context, options); this.detune = new AudioParam(this.node.detune, context); this.playbackRate = new AudioParam(this.node.playbackRate, context); @@ -544,10 +581,31 @@ class AudioBufferSourceNodeWeb set loopEnd(value: number) { this.node.loopEnd = value; } + + connect(destination: AudioNode | AudioParam): AudioNode | AudioParam { + if (destination instanceof AudioParam) { + this.node.connect(destination.param); + } else { + this.node.connect(destination.node); + } + return destination; + } + + disconnect(destination?: AudioNode | AudioParam): void { + if (destination === undefined) { + this.node.disconnect(); + return; + } + + if (destination instanceof AudioParam) { + this.node.disconnect(destination.param); + return; + } + this.node.disconnect(destination.node); + } } export default class AudioBufferSourceNode - extends globalThis.AudioNode implements IAudioAPIBufferSourceNodeWeb { private node: AudioBufferSourceNodeStretcher | AudioBufferSourceNodeWeb; @@ -556,14 +614,21 @@ export default class AudioBufferSourceNode ...AudioBufferSourceOptions, ...options, }; - super(); this.node = finalOptions.pitchCorrection ? new AudioBufferSourceNodeStretcher(context) : new AudioBufferSourceNodeWeb(context, options); } + connect(destination: AudioNode | AudioParam): AudioNode | AudioParam { + return this.asAudioBufferSourceNodeWeb().connect(destination); + } + + disconnect(destination?: AudioNode | AudioParam): void { + this.asAudioBufferSourceNodeWeb().disconnect(destination); + } + asAudioBufferSourceNodeWeb(): IAudioAPIBufferSourceNodeWeb { - return this as unknown as IAudioAPIBufferSourceNodeWeb; + return this.node as unknown as IAudioAPIBufferSourceNodeWeb; } start(when: number = 0, offset?: number, duration?: number): void { diff --git a/packages/react-native-audio-api/src/web-core/AudioNode.tsx b/packages/react-native-audio-api/src/web-core/AudioNode.tsx index a66bb798c..458cf62f9 100644 --- a/packages/react-native-audio-api/src/web-core/AudioNode.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioNode.tsx @@ -10,7 +10,7 @@ export default class AudioNode { readonly channelCountMode: ChannelCountMode; readonly channelInterpretation: ChannelInterpretation; - protected readonly node: globalThis.AudioNode; + readonly node: globalThis.AudioNode; constructor(context: BaseAudioContext, node: globalThis.AudioNode) { this.context = context; From 5632d6804ea592d36f6288cf16cad73898badeee Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 3 Dec 2025 15:14:15 +0100 Subject: [PATCH 26/35] feat: reverted pitch correction option in factory method for absn --- .../docs/core/base-audio-context.mdx | 25 ++----------------- .../docs/effects/iir-filter-node.mdx | 7 ++++++ .../src/core/BaseAudioContext.ts | 10 +++++--- .../src/web-core/AudioContext.tsx | 4 +-- .../src/web-core/OfflineAudioContext.tsx | 4 +-- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/audiodocs/docs/core/base-audio-context.mdx b/packages/audiodocs/docs/core/base-audio-context.mdx index 2e6a098bf..e75511f49 100644 --- a/packages/audiodocs/docs/core/base-audio-context.mdx +++ b/packages/audiodocs/docs/core/base-audio-context.mdx @@ -84,7 +84,7 @@ Creates [`AudioBufferSourceNode`](/docs/sources/audio-buffer-source-node). | Parameter | Type | Description | | :---: | :---: | :---- | -| `options` | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. | +| `pitchCorrection` | `boolean` | Boolean that specifies if pitch correction has to be available. | #### Returns `AudioBufferSourceNode`. @@ -94,7 +94,7 @@ Creates [`AudioBufferQueueSourceNode`](/docs/sources/audio-buffer-queue-source-n | Parameter | Type | Description | | :---: | :---: | :---- | -| `options` | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-queue-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. | +| `pitchCorrection` | `boolean` | Boolean that specifies if pitch correction has to be available. | #### Returns `AudioBufferQueueSourceNode`. @@ -108,16 +108,6 @@ Creates [`ConstantSourceNode`](/docs/sources/constant-source-node). Creates [`ConvolverNode`](/docs/effects/convolver-node). -| Parameter | Type | Description | -| :---: | :---: | :---- | -| `options` | [`ConvolverNodeOptions`](/docs/effects/convolver-node#constructor) | Dictionary object that specifies associated buffer and normalization. | - -#### Errors - -| Error type | Description | -| :---: | :---- | -| `NotSupportedError` | `numOfChannels` of buffer is not 1, 2 or 4. | - #### Returns `ConvolverNode`. ### `createDelay` @@ -140,17 +130,6 @@ Creates [`GainNode`](/docs/effects/gain-node). Creates [`IIRFilterNode`](/docs/effects/iir-filter-node). -| Parameter | Type | Description | -| :---: | :---: | :---- | -| `options` | [`IIRFilterNodeOptions`](/docs/effects/iir-filter-node#constructor) | Dictionary object that specifies the feedforward (numerator) and feedback (denominator) coefficients for the transfer function of the IIR filter. | - -#### Errors - -| Error type | Description | -| :---: | :---- | -| `NotSupportedError` | One or both of the input arrays exceeds 20 members. | -| `InvalidStateError` | All of the feedforward coefficients are 0, or the first feedback coefficient is 0. | - #### Returns `IIRFilterNode`. ### `createOscillator` diff --git a/packages/audiodocs/docs/effects/iir-filter-node.mdx b/packages/audiodocs/docs/effects/iir-filter-node.mdx index 660209321..dba61194c 100644 --- a/packages/audiodocs/docs/effects/iir-filter-node.mdx +++ b/packages/audiodocs/docs/effects/iir-filter-node.mdx @@ -24,6 +24,13 @@ interface IIRFilterNodeOptions { } ``` +#### Errors + +| Error type | Description | +| :---: | :---- | +| `NotSupportedError` | One or both of the input arrays exceeds 20 members. | +| `InvalidStateError` | All of the feedforward coefficients are 0, or the first feedback coefficient is 0. | + ## Properties It inherits all properties from [`AudioNode`](/docs/core/audio-node#properties). diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 607921a93..2239dd276 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -156,8 +156,8 @@ export default class BaseAudioContext { return new BiquadFilterNode(this); } - createBufferSource(): AudioBufferSourceNode { - return new AudioBufferSourceNode(this); + createBufferSource(pitchCorrection?: boolean): AudioBufferSourceNode { + return new AudioBufferSourceNode(this, { pitchCorrection }); } createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { @@ -187,8 +187,10 @@ export default class BaseAudioContext { return new IIRFilterNode(this, { feedforward, feedback }); } - createBufferQueueSource(): AudioBufferQueueSourceNode { - return new AudioBufferQueueSourceNode(this); + createBufferQueueSource( + pitchCorrection?: boolean + ): AudioBufferQueueSourceNode { + return new AudioBufferQueueSourceNode(this, { pitchCorrection }); } createBuffer( 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 855a1b511..3aa8f2aed 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -78,8 +78,8 @@ export default class AudioContext implements BaseAudioContext { return new IIRFilterNode(this, { feedforward, feedback }); } - createBufferSource(): AudioBufferSourceNode { - return new AudioBufferSourceNode(this); + createBufferSource(pitchCorrection?: boolean): AudioBufferSourceNode { + return new AudioBufferSourceNode(this, { pitchCorrection }); } createBuffer( 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 53f89552e..d1939d0b2 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -85,8 +85,8 @@ export default class OfflineAudioContext implements BaseAudioContext { return new IIRFilterNode(this, { feedforward, feedback }); } - createBufferSource(): AudioBufferSourceNode { - return new AudioBufferSourceNode(this); + createBufferSource(pitchCorrection?: boolean): AudioBufferSourceNode { + return new AudioBufferSourceNode(this, { pitchCorrection }); } createBuffer( From 97eb239ce15b8e3dee18b649aac732a2e4f2468d Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 4 Dec 2025 15:59:15 +0100 Subject: [PATCH 27/35] feat: slowly removing shared ptr in options --- .../BaseAudioContextHostObject.cpp | 3 +- .../HostObjects/utils/NodeOptionsParser.h | 42 +++++++++---------- .../common/cpp/audioapi/core/AudioNode.cpp | 17 ++++---- .../common/cpp/audioapi/core/AudioNode.h | 5 +-- .../cpp/audioapi/core/BaseAudioContext.cpp | 4 +- .../cpp/audioapi/core/BaseAudioContext.h | 2 +- .../audioapi/core/analysis/AnalyserNode.cpp | 7 +++- .../core/effects/BiquadFilterNode.cpp | 7 +++- .../audioapi/core/effects/ConvolverNode.cpp | 7 +++- .../cpp/audioapi/core/effects/DelayNode.cpp | 7 +++- .../cpp/audioapi/core/effects/GainNode.cpp | 5 +-- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../audioapi/core/effects/IIRFilterNode.cpp | 7 +++- .../core/effects/StereoPannerNode.cpp | 7 +++- .../common/cpp/test/src/GainTest.cpp | 5 +-- .../src/core/BaseAudioContext.ts | 18 ++++++-- 16 files changed, 92 insertions(+), 53 deletions(-) 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 bb3f1e837..ec9bb334a 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 @@ -23,6 +23,7 @@ #include #include +#include #include namespace audioapi { @@ -194,7 +195,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { auto options = args[0].asObject(runtime); auto gainOptions = audioapi::option_parser::parseGainOptions(runtime, options); - auto gain = context_->createGain(gainOptions); + auto gain = context_->createGain(std::move(gainOptions)); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); } 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 index 59fcf8328..21c72b459 100644 --- 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 @@ -12,9 +12,7 @@ #include namespace audioapi::option_parser { -std::shared_ptr parseAudioNodeOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { +AudioNodeOptions parseAudioNodeOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { AudioNodeOptions options; options.channelCount = @@ -40,23 +38,21 @@ std::shared_ptr parseAudioNodeOptions( options.channelInterpretation = ChannelInterpretation::DISCRETE; } - return std::make_shared(options); + return options; } -std::shared_ptr parseGainOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - GainOptions options(*nodeOptions.get()); +GainOptions parseGainOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + GainOptions options(nodeOptions); options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); - return std::make_shared(options); + return options; } std::shared_ptr parseStereoPannerOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - StereoPannerOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + StereoPannerOptions options(nodeOptions); options.pan = static_cast(optionsObject.getProperty(runtime, "pan").getNumber()); return std::make_shared(options); } @@ -64,8 +60,8 @@ std::shared_ptr parseStereoPannerOptions( std::shared_ptr parseConvolverOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - ConvolverOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + ConvolverOptions options(nodeOptions); options.disableNormalization = static_cast(optionsObject.getProperty(runtime, "disableNormalization").getNumber()); if (optionsObject.hasProperty(runtime, "buffer")) { @@ -88,8 +84,8 @@ std::shared_ptr parseConstantSourceOptions( std::shared_ptr parseAnalyserOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - AnalyserOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + AnalyserOptions options(nodeOptions); options.fftSize = static_cast(optionsObject.getProperty(runtime, "fftSize").getNumber()); options.minDecibels = static_cast(optionsObject.getProperty(runtime, "minDecibels").getNumber()); @@ -103,8 +99,8 @@ std::shared_ptr parseAnalyserOptions( std::shared_ptr parseBiquadFilterOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - BiquadFilterOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + BiquadFilterOptions options(nodeOptions); auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); @@ -138,7 +134,7 @@ std::shared_ptr parseBiquadFilterOptions( std::shared_ptr parseOscillatorOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); OscillatorOptions options; auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); @@ -226,8 +222,8 @@ std::shared_ptr parseAudioBufferOptions( std::shared_ptr parseDelayOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - DelayOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + DelayOptions options(nodeOptions); options.maxDelayTime = static_cast(optionsObject.getProperty(runtime, "maxDelayTime").getNumber()); options.delayTime = @@ -238,8 +234,8 @@ std::shared_ptr parseDelayOptions( std::shared_ptr parseIIRFilterOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - IIRFilterOptions options(*nodeOptions.get()); + auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); + IIRFilterOptions options(nodeOptions); auto feedforwardArray = optionsObject.getProperty(runtime, "feedforward").asObject(runtime).asArray(runtime); 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 70c6bb94b..c616d5c92 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 @@ -11,13 +11,16 @@ namespace audioapi { -AudioNode::AudioNode(BaseAudioContext *context, std::shared_ptr options) - : context_(context) { - if (options != nullptr) { - channelCount_ = options->channelCount; - channelCountMode_ = options->channelCountMode; - channelInterpretation_ = options->channelInterpretation; - } +AudioNode::AudioNode(BaseAudioContext *context) : context_(context) { + audioBus_ = + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); +} + +AudioNode::AudioNode(BaseAudioContext *context, const AudioNodeOptions &options) + : context_(context), + 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 3f73de4bb..b025a339d 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 @@ -20,9 +20,8 @@ class AudioNodeOptions; class AudioNode : public std::enable_shared_from_this { public: - explicit AudioNode( - BaseAudioContext *context, - std::shared_ptr options = nullptr); + explicit AudioNode(BaseAudioContext *context); + explicit AudioNode(BaseAudioContext *context, const AudioNodeOptions &options); 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 0cec3fcbb..9ab31c739 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 @@ -139,8 +139,8 @@ std::shared_ptr BaseAudioContext::createStreamer( #endif // RN_AUDIO_API_FFMPEG_DISABLED } -std::shared_ptr BaseAudioContext::createGain(std::shared_ptr options) { - auto gain = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createGain(GainOptions &&options) { + auto gain = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(gain); return gain; } 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 910d35f6b..53eae0f9a 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 @@ -85,7 +85,7 @@ class BaseAudioContext { 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 createGain(GainOptions &&options); std::shared_ptr createStereoPanner( std::shared_ptr options); std::shared_ptr createBiquadFilter( 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 860ae0636..96dd7af7b 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 @@ -16,7 +16,12 @@ namespace audioapi { AnalyserNode::AnalyserNode( audioapi::BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, options), + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)), fftSize_(options->fftSize), minDecibels_(options->minDecibels), maxDecibels_(options->maxDecibels), 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 b502e2cca..43395f2bf 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 @@ -42,7 +42,12 @@ namespace audioapi { BiquadFilterNode::BiquadFilterNode( BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, options) { + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)) { frequencyParam_ = std::make_shared( options->frequency, 0.0f, context->getNyquistFrequency(), context); detuneParam_ = std::make_shared( 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 7a5ffff86..d6b2d2e3d 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 @@ -15,7 +15,12 @@ namespace audioapi { ConvolverNode::ConvolverNode( BaseAudioContext *context, const std::shared_ptr options) - : AudioNode(context, options), + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)), remainingSegments_(0), internalBufferIndex_(0), signalledToStop_(false), diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 1f7a559b0..65633c4d7 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -9,7 +9,12 @@ namespace audioapi { DelayNode::DelayNode(BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, options) { + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)) { delayTimeParam_ = std::make_shared(options->delayTime, 0, options->maxDelayTime, context); delayBuffer_ = std::make_shared( 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 33bb716fd..64b3de1d6 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 @@ -8,10 +8,9 @@ namespace audioapi { -GainNode::GainNode(BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, options) { +GainNode::GainNode(BaseAudioContext *context, GainOptions &&options) : AudioNode(context, options) { gainParam_ = std::make_shared( - options->gain, 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 361dffe5a..2da6abd0e 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 @@ -12,7 +12,7 @@ class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(BaseAudioContext *context, const std::shared_ptr options); + explicit GainNode(BaseAudioContext *context, GainOptions &&options); [[nodiscard]] std::shared_ptr getGainParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 28f105aaf..6c10e8d1c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -36,7 +36,12 @@ namespace audioapi { IIRFilterNode::IIRFilterNode(BaseAudioContext *context, std::shared_ptr options) - : AudioNode(context, options), + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)), feedforward_(options->feedforward), feedback_(options->feedback) { isInitialized_ = true; 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 1bfc4a58f..bddea2a08 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 @@ -13,7 +13,12 @@ namespace audioapi { StereoPannerNode::StereoPannerNode( BaseAudioContext *context, const std::shared_ptr options) - : AudioNode(context, options) { + : AudioNode( + context, + AudioNodeOptions( + options->channelCount, + options->channelCountMode, + options->channelInterpretation)) { panParam_ = std::make_shared(options->pan, -1.0f, 1.0f, context); isInitialized_ = true; } 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 26d1764f0..cc040d846 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 @@ -25,8 +25,7 @@ class GainTest : public ::testing::Test { class TestableGainNode : public GainNode { public: - explicit TestableGainNode(BaseAudioContext *context) - : GainNode(context, std::make_shared()) {} + explicit TestableGainNode(BaseAudioContext *context) : GainNode(context, GainOptions()) {} void setGainParam(float value) { getGainParam()->setValue(value); @@ -40,7 +39,7 @@ class TestableGainNode : public GainNode { }; TEST_F(GainTest, GainCanBeCreated) { - auto gain = context->createGain(std::make_shared()); + auto gain = context->createGain(GainOptions()); ASSERT_NE(gain, nullptr); } diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 2239dd276..28e7c4afc 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -145,7 +145,11 @@ export default class BaseAudioContext { } createDelay(maxDelayTime?: number): DelayNode { - return new DelayNode(this, { maxDelayTime }); + if (maxDelayTime !== undefined) { + return new DelayNode(this, { maxDelayTime }); + } else { + return new DelayNode(this); + } } createStereoPanner(): StereoPannerNode { @@ -157,7 +161,11 @@ export default class BaseAudioContext { } createBufferSource(pitchCorrection?: boolean): AudioBufferSourceNode { - return new AudioBufferSourceNode(this, { pitchCorrection }); + if (pitchCorrection !== undefined) { + return new AudioBufferSourceNode(this, { pitchCorrection }); + } else { + return new AudioBufferSourceNode(this); + } } createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { @@ -190,7 +198,11 @@ export default class BaseAudioContext { createBufferQueueSource( pitchCorrection?: boolean ): AudioBufferQueueSourceNode { - return new AudioBufferQueueSourceNode(this, { pitchCorrection }); + if (pitchCorrection !== undefined) { + return new AudioBufferQueueSourceNode(this, { pitchCorrection }); + } else { + return new AudioBufferQueueSourceNode(this); + } } createBuffer( From 7a99505b583293273870db945eaefd3bf5c39510 Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 4 Dec 2025 17:19:14 +0100 Subject: [PATCH 28/35] feat: removed options as shared ptr --- .../src/examples/AudioFile/AudioPlayer.ts | 4 +- apps/fabric-example/ios/Podfile.lock | 8 +- .../BaseAudioContextHostObject.cpp | 2 +- .../HostObjects/utils/NodeOptionsParser.h | 86 +++++++------------ .../cpp/audioapi/core/BaseAudioContext.cpp | 56 ++++++------ .../cpp/audioapi/core/BaseAudioContext.h | 30 +++---- .../audioapi/core/analysis/AnalyserNode.cpp | 19 ++-- .../cpp/audioapi/core/analysis/AnalyserNode.h | 2 +- .../core/effects/BiquadFilterNode.cpp | 21 ++--- .../audioapi/core/effects/BiquadFilterNode.h | 4 +- .../audioapi/core/effects/ConvolverNode.cpp | 15 +--- .../cpp/audioapi/core/effects/ConvolverNode.h | 4 +- .../cpp/audioapi/core/effects/DelayNode.cpp | 13 +-- .../cpp/audioapi/core/effects/DelayNode.h | 2 +- .../audioapi/core/effects/IIRFilterNode.cpp | 11 +-- .../cpp/audioapi/core/effects/IIRFilterNode.h | 2 +- .../core/effects/StereoPannerNode.cpp | 13 +-- .../audioapi/core/effects/StereoPannerNode.h | 4 +- .../cpp/audioapi/core/sources/AudioBuffer.cpp | 5 +- .../cpp/audioapi/core/sources/AudioBuffer.h | 2 +- .../sources/AudioBufferBaseSourceNode.cpp | 8 +- .../core/sources/AudioBufferBaseSourceNode.h | 2 +- .../sources/AudioBufferQueueSourceNode.cpp | 6 +- .../core/sources/AudioBufferQueueSourceNode.h | 2 +- .../core/sources/AudioBufferSourceNode.cpp | 10 +-- .../core/sources/AudioBufferSourceNode.h | 4 +- .../core/sources/ConstantSourceNode.cpp | 6 +- .../core/sources/ConstantSourceNode.h | 4 +- .../audioapi/core/sources/OscillatorNode.cpp | 14 ++- .../audioapi/core/sources/OscillatorNode.h | 2 +- .../audioapi/core/sources/StreamerNode.cpp | 4 +- .../cpp/audioapi/core/sources/StreamerNode.h | 2 +- .../src/core/AnalyserNode.ts | 1 - 33 files changed, 139 insertions(+), 229 deletions(-) diff --git a/apps/common-app/src/examples/AudioFile/AudioPlayer.ts b/apps/common-app/src/examples/AudioFile/AudioPlayer.ts index 4e8a757cf..a71faecf7 100644 --- a/apps/common-app/src/examples/AudioFile/AudioPlayer.ts +++ b/apps/common-app/src/examples/AudioFile/AudioPlayer.ts @@ -37,9 +37,7 @@ class AudioPlayer { await this.audioContext.resume(); } - this.sourceNode = this.audioContext.createBufferSource({ - pitchCorrection: true, - }); + this.sourceNode = this.audioContext.createBufferSource(true); this.sourceNode.buffer = this.audioBuffer; this.sourceNode.playbackRate.value = this.playbackRate; diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index fd0bbb755..bbba5c8eb 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2675,7 +2675,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.17.1): + - RNScreens (4.18.0): - boost - DoubleConversion - fast_float @@ -2702,10 +2702,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.17.1) + - RNScreens/common (= 4.18.0) - SocketRocket - Yoga - - RNScreens/common (4.17.1): + - RNScreens/common (4.18.0): - boost - DoubleConversion - fast_float @@ -3219,7 +3219,7 @@ SPEC CHECKSUMS: RNAudioAPI: c7dc7b491a0e4b23535a55fd9b4a00d0f803f4bb RNGestureHandler: f1dd7f92a0faa2868a919ab53bb9d66eb4ebfcf5 RNReanimated: e4993dd98196c698cbacc1441a4ac5b855ae56dc - RNScreens: 833237c48c756d40764540246a501b47dadb2cac + RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479 RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6 RNWorklets: d4553da98908962b6b834d5f2d26525b0d6840ad SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 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 ec9bb334a..fe9e921cc 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 @@ -168,7 +168,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamerOptions = std::make_shared(); + auto streamerOptions = StreamerOptions(); if (!args[0].isUndefined()) { auto options = args[0].asObject(runtime); streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); 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 index 21c72b459..109410987 100644 --- 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 @@ -42,26 +42,21 @@ AudioNodeOptions parseAudioNodeOptions(jsi::Runtime &runtime, const jsi::Object } GainOptions parseGainOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - GainOptions options(nodeOptions); + GainOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); return options; } -std::shared_ptr parseStereoPannerOptions( +StereoPannerOptions parseStereoPannerOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - StereoPannerOptions options(nodeOptions); + StereoPannerOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.pan = static_cast(optionsObject.getProperty(runtime, "pan").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseConvolverOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - ConvolverOptions options(nodeOptions); +ConvolverOptions parseConvolverOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + ConvolverOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.disableNormalization = static_cast(optionsObject.getProperty(runtime, "disableNormalization").getNumber()); if (optionsObject.hasProperty(runtime, "buffer")) { @@ -70,22 +65,19 @@ std::shared_ptr parseConvolverOptions( .asHostObject(runtime); options.bus = bufferHostObject->audioBuffer_; } - return std::make_shared(options); + return options; } -std::shared_ptr parseConstantSourceOptions( +ConstantSourceOptions parseConstantSourceOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { ConstantSourceOptions options; options.offset = static_cast(optionsObject.getProperty(runtime, "offset").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseAnalyserOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - AnalyserOptions options(nodeOptions); +AnalyserOptions parseAnalyserOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + AnalyserOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.fftSize = static_cast(optionsObject.getProperty(runtime, "fftSize").getNumber()); options.minDecibels = static_cast(optionsObject.getProperty(runtime, "minDecibels").getNumber()); @@ -93,14 +85,13 @@ std::shared_ptr parseAnalyserOptions( static_cast(optionsObject.getProperty(runtime, "maxDecibels").getNumber()); options.smoothingTimeConstant = static_cast(optionsObject.getProperty(runtime, "smoothingTimeConstant").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseBiquadFilterOptions( +BiquadFilterOptions parseBiquadFilterOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - BiquadFilterOptions options(nodeOptions); + BiquadFilterOptions options(parseAudioNodeOptions(runtime, optionsObject)); auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); @@ -128,13 +119,10 @@ std::shared_ptr parseBiquadFilterOptions( options.Q = static_cast(optionsObject.getProperty(runtime, "Q").getNumber()); options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseOscillatorOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); +OscillatorOptions parseOscillatorOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { OscillatorOptions options; auto typeStr = optionsObject.getProperty(runtime, "type").asString(runtime).utf8(runtime); @@ -162,10 +150,10 @@ std::shared_ptr parseOscillatorOptions( options.periodicWave = periodicWaveHostObject->periodicWave_; } - return std::make_shared(options); + return options; } -std::shared_ptr parseBaseAudioBufferSourceOptions( +BaseAudioBufferSourceOptions parseBaseAudioBufferSourceOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { BaseAudioBufferSourceOptions options; @@ -174,15 +162,13 @@ std::shared_ptr parseBaseAudioBufferSourceOptions( static_cast(optionsObject.getProperty(runtime, "playbackRate").getNumber()); options.pitchCorrection = static_cast(optionsObject.getProperty(runtime, "pitchCorrection").getBool()); - return std::make_shared(options); + return options; } -std::shared_ptr parseAudioBufferSourceOptions( +AudioBufferSourceOptions parseAudioBufferSourceOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { - std::shared_ptr baseOptions = - parseBaseAudioBufferSourceOptions(runtime, optionsObject); - AudioBufferSourceOptions options(*baseOptions.get()); + AudioBufferSourceOptions options(parseBaseAudioBufferSourceOptions(runtime, optionsObject)); if (optionsObject.hasProperty(runtime, "buffer")) { auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") .getObject(runtime) @@ -193,21 +179,19 @@ std::shared_ptr parseAudioBufferSourceOptions( options.loopStart = static_cast(optionsObject.getProperty(runtime, "loopStart").getNumber()); options.loopEnd = static_cast(optionsObject.getProperty(runtime, "loopEnd").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseStreamerOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { +StreamerOptions 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); + return options; } -std::shared_ptr parseAudioBufferOptions( +AudioBufferOptions parseAudioBufferOptions( jsi::Runtime &runtime, const jsi::Object &optionsObject) { AudioBufferOptions options; @@ -216,26 +200,20 @@ std::shared_ptr parseAudioBufferOptions( options.length = static_cast(optionsObject.getProperty(runtime, "length").getNumber()); options.sampleRate = static_cast(optionsObject.getProperty(runtime, "sampleRate").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseDelayOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - DelayOptions options(nodeOptions); +DelayOptions parseDelayOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + DelayOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.maxDelayTime = static_cast(optionsObject.getProperty(runtime, "maxDelayTime").getNumber()); options.delayTime = static_cast(optionsObject.getProperty(runtime, "delayTime").getNumber()); - return std::make_shared(options); + return options; } -std::shared_ptr parseIIRFilterOptions( - jsi::Runtime &runtime, - const jsi::Object &optionsObject) { - auto nodeOptions = parseAudioNodeOptions(runtime, optionsObject); - IIRFilterOptions options(nodeOptions); +IIRFilterOptions parseIIRFilterOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + IIRFilterOptions options(parseAudioNodeOptions(runtime, optionsObject)); auto feedforwardArray = optionsObject.getProperty(runtime, "feedforward").asObject(runtime).asArray(runtime); @@ -255,6 +233,6 @@ std::shared_ptr parseIIRFilterOptions( static_cast(feedbackArray.getValueAtIndex(runtime, i).getNumber())); } - return std::make_shared(options); + return options; } } // namespace audioapi::option_parser 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 9ab31c739..0389a25dc 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 @@ -114,24 +114,22 @@ std::shared_ptr BaseAudioContext::createRecorderAdapter() { return recorderAdapter; } -std::shared_ptr BaseAudioContext::createOscillator( - std::shared_ptr options) { - auto oscillator = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createOscillator(OscillatorOptions options) { + auto oscillator = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(oscillator); return oscillator; } std::shared_ptr BaseAudioContext::createConstantSource( - std::shared_ptr options) { - auto constantSource = std::make_shared(this, options); + ConstantSourceOptions options) { + auto constantSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(constantSource); return constantSource; } -std::shared_ptr BaseAudioContext::createStreamer( - std::shared_ptr options) { +std::shared_ptr BaseAudioContext::createStreamer(StreamerOptions options) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = std::make_shared(this, options); + auto streamer = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(streamer); return streamer; #else @@ -139,56 +137,54 @@ std::shared_ptr BaseAudioContext::createStreamer( #endif // RN_AUDIO_API_FFMPEG_DISABLED } -std::shared_ptr BaseAudioContext::createGain(GainOptions &&options) { +std::shared_ptr BaseAudioContext::createGain(GainOptions options) { auto gain = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(gain); return gain; } std::shared_ptr BaseAudioContext::createStereoPanner( - std::shared_ptr options) { - auto stereoPanner = std::make_shared(this, options); + StereoPannerOptions options) { + auto stereoPanner = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; } -std::shared_ptr BaseAudioContext::createDelay(std::shared_ptr options) { - auto delay = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createDelay(DelayOptions options) { + auto delay = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(delay); return delay; } std::shared_ptr BaseAudioContext::createBiquadFilter( - std::shared_ptr options) { - auto biquadFilter = std::make_shared(this, options); + BiquadFilterOptions options) { + auto biquadFilter = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(biquadFilter); return biquadFilter; } std::shared_ptr BaseAudioContext::createBufferSource( - std::shared_ptr options) { - auto bufferSource = std::make_shared(this, options); + AudioBufferSourceOptions options) { + auto bufferSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(bufferSource); return bufferSource; } -std::shared_ptr BaseAudioContext::createIIRFilter( - std::shared_ptr options) { - auto iirFilter = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createIIRFilter(IIRFilterOptions options) { + auto iirFilter = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(iirFilter); return iirFilter; } std::shared_ptr BaseAudioContext::createBufferQueueSource( - std::shared_ptr options) { - auto bufferSource = std::make_shared(this, options); + BaseAudioBufferSourceOptions options) { + auto bufferSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(bufferSource); return bufferSource; } -std::shared_ptr BaseAudioContext::createBuffer( - std::shared_ptr options) { - return std::make_shared(options); +std::shared_ptr BaseAudioContext::createBuffer(AudioBufferOptions options) { + return std::make_shared(std::move(options)); } std::shared_ptr BaseAudioContext::createPeriodicWave( @@ -198,16 +194,14 @@ std::shared_ptr BaseAudioContext::createPeriodicWave( return std::make_shared(sampleRate_, complexData, length, disableNormalization); } -std::shared_ptr BaseAudioContext::createAnalyser( - std::shared_ptr options) { - auto analyser = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createAnalyser(AnalyserOptions options) { + auto analyser = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(analyser); return analyser; } -std::shared_ptr BaseAudioContext::createConvolver( - std::shared_ptr options) { - auto convolver = std::make_shared(this, options); +std::shared_ptr BaseAudioContext::createConvolver(ConvolverOptions options) { + auto convolver = std::make_shared(this, std::move(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 53eae0f9a..902df1b9f 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 @@ -79,28 +79,24 @@ class BaseAudioContext { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createDelay(std::shared_ptr options); - std::shared_ptr createIIRFilter(std::shared_ptr options); - 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(GainOptions &&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 createDelay(DelayOptions options); + std::shared_ptr createIIRFilter(IIRFilterOptions options); + std::shared_ptr createOscillator(OscillatorOptions options); + std::shared_ptr createConstantSource(ConstantSourceOptions options); + std::shared_ptr createStreamer(StreamerOptions options); + std::shared_ptr createGain(GainOptions options); + std::shared_ptr createStereoPanner(StereoPannerOptions options); + std::shared_ptr createBiquadFilter(BiquadFilterOptions options); + std::shared_ptr createBufferSource(AudioBufferSourceOptions options); std::shared_ptr createBufferQueueSource( - std::shared_ptr options); - static std::shared_ptr createBuffer(std::shared_ptr options); + BaseAudioBufferSourceOptions options); + static std::shared_ptr createBuffer(AudioBufferOptions options); std::shared_ptr createPeriodicWave( const std::vector> &complexData, bool disableNormalization, int length); - std::shared_ptr createAnalyser(std::shared_ptr options); - std::shared_ptr createConvolver(std::shared_ptr options); + std::shared_ptr createAnalyser(AnalyserOptions options); + std::shared_ptr createConvolver(ConvolverOptions 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 96dd7af7b..9738de3c6 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 @@ -13,19 +13,12 @@ #include namespace audioapi { -AnalyserNode::AnalyserNode( - audioapi::BaseAudioContext *context, - std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)), - fftSize_(options->fftSize), - minDecibels_(options->minDecibels), - maxDecibels_(options->maxDecibels), - smoothingTimeConstant_(options->smoothingTimeConstant), +AnalyserNode::AnalyserNode(audioapi::BaseAudioContext *context, AnalyserOptions 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 d8dc2691b..cc85bd2d7 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 @@ -20,7 +20,7 @@ class AnalyserOptions; class AnalyserNode : public AudioNode { public: enum class WindowType { BLACKMAN, HANN }; - explicit AnalyserNode(BaseAudioContext *context, std::shared_ptr options); + explicit AnalyserNode(BaseAudioContext *context, AnalyserOptions 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 43395f2bf..0bb2a1767 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 @@ -39,27 +39,20 @@ namespace audioapi { -BiquadFilterNode::BiquadFilterNode( - BaseAudioContext *context, - std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)) { +BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context, BiquadFilterOptions options) + : AudioNode(context, options) { frequencyParam_ = std::make_shared( - options->frequency, 0.0f, context->getNyquistFrequency(), context); + options.frequency, 0.0f, context->getNyquistFrequency(), context); detuneParam_ = std::make_shared( - options->detune, + options.detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, context); QParam_ = std::make_shared( - options->Q, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options.Q, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); gainParam_ = std::make_shared( - options->gain, MOST_NEGATIVE_SINGLE_FLOAT, 40 * LOG10_MOST_POSITIVE_SINGLE_FLOAT, context); - type_ = options->type; + options.gain, MOST_NEGATIVE_SINGLE_FLOAT, 40 * LOG10_MOST_POSITIVE_SINGLE_FLOAT, context); + type_ = options.type; isInitialized_ = true; } 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 3d48377ad..90ffe00ec 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 @@ -55,9 +55,7 @@ class BiquadFilterNode : public AudioNode { #endif public: - explicit BiquadFilterNode( - BaseAudioContext *context, - std::shared_ptr options); + explicit BiquadFilterNode(BaseAudioContext *context, BiquadFilterOptions 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 d6b2d2e3d..492d8eeff 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 @@ -12,15 +12,8 @@ #include namespace audioapi { -ConvolverNode::ConvolverNode( - BaseAudioContext *context, - const std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)), +ConvolverNode::ConvolverNode(BaseAudioContext *context, ConvolverOptions options) + : AudioNode(context, options), remainingSegments_(0), internalBufferIndex_(0), signalledToStop_(false), @@ -28,9 +21,9 @@ ConvolverNode::ConvolverNode( intermediateBus_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - normalize_ = !options->disableNormalization; + normalize_ = !options.disableNormalization; gainCalibrationSampleRate_ = context->getSampleRate(); - setBuffer(options->bus); + setBuffer(options.bus); audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); requiresTailProcessing_ = 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 006be093b..42460b17f 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 @@ -21,9 +21,7 @@ class ConvolverOptions; class ConvolverNode : public AudioNode { public: - explicit ConvolverNode( - BaseAudioContext *context, - const std::shared_ptr options); + explicit ConvolverNode(BaseAudioContext *context, ConvolverOptions 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/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 65633c4d7..728e68f93 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -8,18 +8,13 @@ namespace audioapi { -DelayNode::DelayNode(BaseAudioContext *context, std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)) { +DelayNode::DelayNode(BaseAudioContext *context, DelayOptions options) + : AudioNode(context, options) { delayTimeParam_ = - std::make_shared(options->delayTime, 0, options->maxDelayTime, context); + std::make_shared(options.delayTime, 0, options.maxDelayTime, context); delayBuffer_ = std::make_shared( static_cast( - options->maxDelayTime * context->getSampleRate() + + options.maxDelayTime * context->getSampleRate() + 1), // +1 to enable delayTime equal to maxDelayTime channelCount_, context->getSampleRate()); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index 8ace8551d..d564035a2 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -13,7 +13,7 @@ class DelayOptions; class DelayNode : public AudioNode { public: - explicit DelayNode(BaseAudioContext *context, std::shared_ptr options); + explicit DelayNode(BaseAudioContext *context, DelayOptions options); [[nodiscard]] std::shared_ptr getDelayTimeParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 6c10e8d1c..d3204be29 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -35,15 +35,8 @@ namespace audioapi { -IIRFilterNode::IIRFilterNode(BaseAudioContext *context, std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)), - feedforward_(options->feedforward), - feedback_(options->feedback) { +IIRFilterNode::IIRFilterNode(BaseAudioContext *context, IIRFilterOptions options) + : AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) { isInitialized_ = true; int maxChannels = MAX_CHANNEL_COUNT; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index 7ea662813..c9b1d7916 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -38,7 +38,7 @@ class IIRFilterOptions; class IIRFilterNode : public AudioNode { public: - explicit IIRFilterNode(BaseAudioContext *context, std::shared_ptr options); + explicit IIRFilterNode(BaseAudioContext *context, IIRFilterOptions options); void getFrequencyResponse( const float *frequencyArray, 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 bddea2a08..1bea85a78 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 @@ -10,16 +10,9 @@ namespace audioapi { -StereoPannerNode::StereoPannerNode( - BaseAudioContext *context, - const std::shared_ptr options) - : AudioNode( - context, - AudioNodeOptions( - options->channelCount, - options->channelCountMode, - options->channelInterpretation)) { - panParam_ = std::make_shared(options->pan, -1.0f, 1.0f, context); +StereoPannerNode::StereoPannerNode(BaseAudioContext *context, StereoPannerOptions 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 a24428f44..49515bdbe 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 @@ -14,9 +14,7 @@ class StereoPannerOptions; class StereoPannerNode : public AudioNode { public: - explicit StereoPannerNode( - BaseAudioContext *context, - const std::shared_ptr options); + explicit StereoPannerNode(BaseAudioContext *context, StereoPannerOptions options); [[nodiscard]] std::shared_ptr getPanParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp index a2856b4b7..6aaf0f971 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp @@ -9,9 +9,8 @@ namespace audioapi { -AudioBuffer::AudioBuffer(std::shared_ptr options) { - bus_ = - std::make_shared(options->length, options->numberOfChannels, options->sampleRate); +AudioBuffer::AudioBuffer(AudioBufferOptions options) { + bus_ = std::make_shared(options.length, options.numberOfChannels, options.sampleRate); } AudioBuffer::AudioBuffer(std::shared_ptr bus) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h index abca23477..67cdefc2f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h @@ -14,7 +14,7 @@ class AudioBufferOptions; class AudioBuffer : public std::enable_shared_from_this { public: - explicit AudioBuffer(std::shared_ptr options); + explicit AudioBuffer(AudioBufferOptions options); explicit AudioBuffer(std::shared_ptr bus); [[nodiscard]] size_t getLength() 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 69a8775e8..28178a7d9 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 @@ -13,16 +13,16 @@ namespace audioapi { AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( BaseAudioContext *context, - std::shared_ptr options) + BaseAudioBufferSourceOptions options) : AudioScheduledSourceNode(context), - pitchCorrection_(options->pitchCorrection), + pitchCorrection_(options.pitchCorrection), vReadIndex_(0.0) { onPositionChangedInterval_ = static_cast(context->getSampleRate() * 0.1); detuneParam_ = std::make_shared( - options->detune, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options.detune, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); playbackRateParam_ = std::make_shared( - options->playbackRate, 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 46dc1ecdd..9a6904d96 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 @@ -17,7 +17,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { public: explicit AudioBufferBaseSourceNode( BaseAudioContext *context, - std::shared_ptr options); + BaseAudioBufferSourceOptions 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 21d787daa..4d4e59d7f 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 @@ -20,18 +20,18 @@ namespace audioapi { AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( BaseAudioContext *context, - std::shared_ptr options) + BaseAudioBufferSourceOptions options) : AudioBufferBaseSourceNode(context, options) { buffers_ = {}; stretch_->presetDefault(channelCount_, context_->getSampleRate()); - if (options->pitchCorrection) { + if (options.pitchCorrection) { // If pitch correction is enabled, add extra frames at the end // to compensate for processing latency. addExtraTailFrames_ = true; int extraTailFrames = static_cast(stretch_->inputLatency() + stretch_->outputLatency()); - auto audioBufferOptions = std::make_shared( + auto audioBufferOptions = AudioBufferOptions( extraTailFrames, static_cast(channelCount_), context->getSampleRate()); tailBuffer_ = std::make_shared(audioBufferOptions); 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 467ee193f..336938c3a 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 @@ -20,7 +20,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { public: explicit AudioBufferQueueSourceNode( BaseAudioContext *context, - std::shared_ptr options); + BaseAudioBufferSourceOptions 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 2a055ad3d..74738f231 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 @@ -15,13 +15,13 @@ namespace audioapi { AudioBufferSourceNode::AudioBufferSourceNode( BaseAudioContext *context, - std::shared_ptr options) + AudioBufferSourceOptions options) : AudioBufferBaseSourceNode(context, options), - loop_(options->loop), + loop_(options.loop), loopSkip_(false), - loopStart_(options->loopStart), - loopEnd_(options->loopEnd) { - buffer_ = std::shared_ptr(options->buffer); + 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 5a92bf050..dc85e30b6 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 @@ -17,9 +17,7 @@ class AudioBufferSourceOptions; class AudioBufferSourceNode : public AudioBufferBaseSourceNode { public: - explicit AudioBufferSourceNode( - BaseAudioContext *context, - std::shared_ptr options); + explicit AudioBufferSourceNode(BaseAudioContext *context, AudioBufferSourceOptions 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 19ec7f4fb..7b4a5c1d0 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 @@ -7,12 +7,10 @@ #include namespace audioapi { -ConstantSourceNode::ConstantSourceNode( - BaseAudioContext *context, - const std::shared_ptr options) +ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context, ConstantSourceOptions options) : AudioScheduledSourceNode(context) { offsetParam_ = std::make_shared( - options->offset, 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 9d3c6cb51..49dcc2c86 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 @@ -14,9 +14,7 @@ class ConstantSourceOptions; class ConstantSourceNode : public AudioScheduledSourceNode { public: - explicit ConstantSourceNode( - BaseAudioContext *context, - const std::shared_ptr options); + explicit ConstantSourceNode(BaseAudioContext *context, ConstantSourceOptions 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 810e6ade1..4ac354c4b 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 @@ -9,23 +9,21 @@ namespace audioapi { -OscillatorNode::OscillatorNode( - BaseAudioContext *context, - std::shared_ptr options) +OscillatorNode::OscillatorNode(BaseAudioContext *context, OscillatorOptions options) : AudioScheduledSourceNode(context) { frequencyParam_ = std::make_shared( - options->frequency, + options.frequency, -context_->getNyquistFrequency(), context_->getNyquistFrequency(), context); detuneParam_ = std::make_shared( - options->detune, + options.detune, -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT, context); - type_ = options->type; - if (options->periodicWave) { - periodicWave_ = options->periodicWave; + type_ = options.type; + if (options.periodicWave) { + periodicWave_ = options.periodicWave; } else { periodicWave_ = context_->getBasicWaveForm(type_); } 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 3a643e456..1d7da422e 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 @@ -17,7 +17,7 @@ class OscillatorOptions; class OscillatorNode : public AudioScheduledSourceNode { public: - explicit OscillatorNode(BaseAudioContext *context, std::shared_ptr options); + explicit OscillatorNode(BaseAudioContext *context, OscillatorOptions 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 b906fe4d9..f527c675c 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 @@ -22,7 +22,7 @@ #include namespace audioapi { -StreamerNode::StreamerNode(BaseAudioContext *context, std::shared_ptr options) +StreamerNode::StreamerNode(BaseAudioContext *context, StreamerOptions options) : AudioScheduledSourceNode(context), fmtCtx_(nullptr), codecCtx_(nullptr), @@ -36,7 +36,7 @@ StreamerNode::StreamerNode(BaseAudioContext *context, std::shared_ptrstreamPath) {} + streamPath_(options.streamPath) {} StreamerNode::~StreamerNode() { #if !RN_AUDIO_API_FFMPEG_DISABLED 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 af51ca77f..8723a00bf 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 @@ -65,7 +65,7 @@ class StreamerOptions; class StreamerNode : public AudioScheduledSourceNode { public: - explicit StreamerNode(BaseAudioContext *context, std::shared_ptr options); + explicit StreamerNode(BaseAudioContext *context, StreamerOptions options); ~StreamerNode() override; /** diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts index 6af3e4bdb..a02ba91b8 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -23,7 +23,6 @@ export default class AnalyserNode extends AudioNode { )}` ); } - console.log('finalOptions', finalOptions); const analyserNode: IAnalyserNode = context.context.createAnalyser(finalOptions); super(context, analyserNode); From b19e1fdd1cd5e464a121b65424f871afbab09404 Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 4 Dec 2025 17:30:34 +0100 Subject: [PATCH 29/35] test: test fix --- .../cpp/test/src/ConstantSourceTest.cpp | 4 +-- .../common/cpp/test/src/DelayTest.cpp | 5 ++-- .../common/cpp/test/src/IIRFilterTest.cpp | 6 ++--- .../common/cpp/test/src/OscillatorTest.cpp | 2 +- .../common/cpp/test/src/StereoPannerTest.cpp | 4 +-- .../cpp/test/src/biquad/BiquadFilterTest.cpp | 27 +++++++------------ 6 files changed, 19 insertions(+), 29 deletions(-) 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 c11e04c9b..1344b34ea 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 @@ -26,7 +26,7 @@ class ConstantSourceTest : public ::testing::Test { class TestableConstantSourceNode : public ConstantSourceNode { public: explicit TestableConstantSourceNode(BaseAudioContext *context) - : ConstantSourceNode(context, std::make_shared()) {} + : ConstantSourceNode(context, ConstantSourceOptions()) {} void setOffsetParam(float value) { getOffsetParam()->setValue(value); @@ -40,7 +40,7 @@ class TestableConstantSourceNode : public ConstantSourceNode { }; TEST_F(ConstantSourceTest, ConstantSourceCanBeCreated) { - auto constantSource = context->createConstantSource(std::make_shared()); + auto constantSource = context->createConstantSource(ConstantSourceOptions()); ASSERT_NE(constantSource, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index d0c2942b6..a05307df5 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -25,8 +25,7 @@ class DelayTest : public ::testing::Test { class TestableDelayNode : public DelayNode { public: - explicit TestableDelayNode(BaseAudioContext *context) - : DelayNode(context, std::make_shared()) {} + explicit TestableDelayNode(BaseAudioContext *context) : DelayNode(context, DelayOptions()) {} void setDelayTimeParam(float value) { getDelayTimeParam()->setValue(value); @@ -40,7 +39,7 @@ class TestableDelayNode : public DelayNode { }; TEST_F(DelayTest, DelayCanBeCreated) { - auto delay = context->createDelay(std::make_shared()); + auto delay = context->createDelay(DelayOptions()); ASSERT_NE(delay, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp index f71fbcf35..9a9a308ab 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/IIRFilterTest.cpp @@ -92,7 +92,7 @@ class IIRFilterTest : public ::testing::Test { TEST_F(IIRFilterTest, IIRFilterCanBeCreated) { const std::vector feedforward = {1.0}; const std::vector feedback = {1.0}; - auto node = context->createIIRFilter(std::make_shared(feedforward, feedback)); + auto node = context->createIIRFilter(IIRFilterOptions(feedforward, feedback)); ASSERT_NE(node, nullptr); } @@ -100,8 +100,8 @@ TEST_F(IIRFilterTest, GetFrequencyResponse) { const std::vector feedforward = {0.0050662636, 0.0101325272, 0.0050662636}; const std::vector feedback = {1.0632762845, -1.9797349456, 0.9367237155}; - auto node = std::make_shared( - context.get(), std::make_shared(feedforward, feedback)); + auto node = + std::make_shared(context.get(), IIRFilterOptions(feedforward, feedback)); float frequency = 1000.0f; float normalizedFrequency = frequency / nyquistFrequency; diff --git a/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp index 0a6abf9d5..f52902fa8 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/OscillatorTest.cpp @@ -22,6 +22,6 @@ class OscillatorTest : public ::testing::Test { }; TEST_F(OscillatorTest, OscillatorCanBeCreated) { - auto osc = context->createOscillator(std::make_shared()); + auto osc = context->createOscillator(OscillatorOptions()); ASSERT_NE(osc, 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 8f92e70a4..9e49eff08 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 @@ -26,7 +26,7 @@ class StereoPannerTest : public ::testing::Test { class TestableStereoPannerNode : public StereoPannerNode { public: explicit TestableStereoPannerNode(BaseAudioContext *context) - : StereoPannerNode(context, std::make_shared()) {} + : StereoPannerNode(context, StereoPannerOptions()) {} void setPanParam(float value) { getPanParam()->setValue(value); @@ -40,7 +40,7 @@ class TestableStereoPannerNode : public StereoPannerNode { }; TEST_F(StereoPannerTest, StereoPannerCanBeCreated) { - auto panner = context->createStereoPanner(std::make_shared()); + auto panner = context->createStereoPanner(StereoPannerOptions()); ASSERT_NE(panner, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp index fe833ea23..0ef193b3e 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/biquad/BiquadFilterTest.cpp @@ -17,8 +17,7 @@ void BiquadFilterTest::expectCoefficientsNear( } void BiquadFilterTest::testLowpass(float frequency, float Q) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowpassCoefficients(normalizedFrequency, Q); @@ -26,8 +25,7 @@ void BiquadFilterTest::testLowpass(float frequency, float Q) { } void BiquadFilterTest::testHighpass(float frequency, float Q) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighpassCoefficients(normalizedFrequency, Q); @@ -35,8 +33,7 @@ void BiquadFilterTest::testHighpass(float frequency, float Q) { } void BiquadFilterTest::testBandpass(float frequency, float Q) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setBandpassCoefficients(normalizedFrequency, Q); @@ -44,8 +41,7 @@ void BiquadFilterTest::testBandpass(float frequency, float Q) { } void BiquadFilterTest::testNotch(float frequency, float Q) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setNotchCoefficients(normalizedFrequency, Q); @@ -53,8 +49,7 @@ void BiquadFilterTest::testNotch(float frequency, float Q) { } void BiquadFilterTest::testAllpass(float frequency, float Q) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setAllpassCoefficients(normalizedFrequency, Q); @@ -62,8 +57,7 @@ void BiquadFilterTest::testAllpass(float frequency, float Q) { } void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setPeakingCoefficients(normalizedFrequency, Q, gain); @@ -71,8 +65,7 @@ void BiquadFilterTest::testPeaking(float frequency, float Q, float gain) { } void BiquadFilterTest::testLowshelf(float frequency, float gain) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowshelfCoefficients(normalizedFrequency, gain); @@ -80,8 +73,7 @@ void BiquadFilterTest::testLowshelf(float frequency, float gain) { } void BiquadFilterTest::testHighshelf(float frequency, float gain) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighshelfCoefficients(normalizedFrequency, gain); @@ -227,8 +219,7 @@ TEST_P(BiquadFilterGainTest, SetHighshelfCoefficients) { } TEST_F(BiquadFilterTest, GetFrequencyResponse) { - auto node = - std::make_shared(context.get(), std::make_shared()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float frequency = 1000.0f; float Q = 1.0f; From e12c3daceb507c30b01c2e6aba498a4b0e1bef39 Mon Sep 17 00:00:00 2001 From: michal Date: Fri, 5 Dec 2025 13:27:20 +0100 Subject: [PATCH 30/35] feat: changes after review --- apps/fabric-example/ios/Podfile.lock | 8 +-- .../common/cpp/audioapi/core/AudioNode.h | 1 + .../cpp/audioapi/core/effects/GainNode.cpp | 2 +- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../audioapi/core/effects/IIRFilterNode.cpp | 5 +- .../src/core/AnalyserNode.ts | 9 +-- .../src/core/ConvolverNode.ts | 13 ---- .../src/core/OscillatorNode.ts | 6 -- .../src/core/PeriodicWave.ts | 33 +++------- .../src/options-validators.ts | 66 +++++++++++++++++++ packages/react-native-audio-api/src/types.ts | 4 ++ .../src/web-core/PeriodicWave.tsx | 7 +- 12 files changed, 93 insertions(+), 63 deletions(-) create mode 100644 packages/react-native-audio-api/src/options-validators.ts diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index bbba5c8eb..fd0bbb755 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2675,7 +2675,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.18.0): + - RNScreens (4.17.1): - boost - DoubleConversion - fast_float @@ -2702,10 +2702,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.18.0) + - RNScreens/common (= 4.17.1) - SocketRocket - Yoga - - RNScreens/common (4.18.0): + - RNScreens/common (4.17.1): - boost - DoubleConversion - fast_float @@ -3219,7 +3219,7 @@ SPEC CHECKSUMS: RNAudioAPI: c7dc7b491a0e4b23535a55fd9b4a00d0f803f4bb RNGestureHandler: f1dd7f92a0faa2868a919ab53bb9d66eb4ebfcf5 RNReanimated: e4993dd98196c698cbacc1441a4ac5b855ae56dc - RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479 + RNScreens: 833237c48c756d40764540246a501b47dadb2cac RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6 RNWorklets: d4553da98908962b6b834d5f2d26525b0d6840ad SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 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 b025a339d..8110e226a 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 @@ -21,6 +21,7 @@ class AudioNodeOptions; class AudioNode : public std::enable_shared_from_this { public: explicit AudioNode(BaseAudioContext *context); + // usually options are passed as derived class, keep in mind that object passed as options will be sliced explicit AudioNode(BaseAudioContext *context, const AudioNodeOptions &options); virtual ~AudioNode(); 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 64b3de1d6..17741b238 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 @@ -8,7 +8,7 @@ namespace audioapi { -GainNode::GainNode(BaseAudioContext *context, GainOptions &&options) : AudioNode(context, options) { +GainNode::GainNode(BaseAudioContext *context, GainOptions options) : AudioNode(context, options) { gainParam_ = std::make_shared( 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 2da6abd0e..93c321654 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 @@ -12,7 +12,7 @@ class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(BaseAudioContext *context, GainOptions &&options); + explicit GainNode(BaseAudioContext *context, GainOptions options); [[nodiscard]] std::shared_ptr getGainParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index d3204be29..cafd1dbfa 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -31,12 +31,15 @@ #include #include #include +#include #include namespace audioapi { IIRFilterNode::IIRFilterNode(BaseAudioContext *context, IIRFilterOptions options) - : AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) { + : AudioNode(context, options), + feedforward_(std::move(options.feedforward)), + feedback_(std::move(options.feedback)) { isInitialized_ = true; int maxChannels = MAX_CHANNEL_COUNT; diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts index a02ba91b8..b6faaf6ef 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -4,6 +4,7 @@ import { IndexSizeError } from '../errors'; import { IAnalyserNode } from '../interfaces'; import { WindowType, TAnalyserOptions } from '../types'; import AudioNode from './AudioNode'; +import { AnalyserOptionsValidator } from '../options-validators'; export default class AnalyserNode extends AudioNode { private static allowedFFTSize: number[] = [ @@ -16,13 +17,7 @@ export default class AnalyserNode extends AudioNode { ...options, }; - if (!AnalyserNode.allowedFFTSize.includes(finalOptions.fftSize!)) { - throw new IndexSizeError( - `fftSize must be one of the following values: ${AnalyserNode.allowedFFTSize.join( - ', ' - )}` - ); - } + AnalyserOptionsValidator.validate(finalOptions); const analyserNode: IAnalyserNode = context.context.createAnalyser(finalOptions); super(context, analyserNode); diff --git a/packages/react-native-audio-api/src/core/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index e05605b58..3c283fa04 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -1,7 +1,6 @@ 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'; @@ -12,18 +11,6 @@ export default class ConvolverNode extends AudioNode { ...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); diff --git a/packages/react-native-audio-api/src/core/OscillatorNode.ts b/packages/react-native-audio-api/src/core/OscillatorNode.ts index df6253b03..b60aff874 100644 --- a/packages/react-native-audio-api/src/core/OscillatorNode.ts +++ b/packages/react-native-audio-api/src/core/OscillatorNode.ts @@ -18,12 +18,6 @@ export default class OscillatorNode extends AudioScheduledSourceNode { ...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'; } diff --git a/packages/react-native-audio-api/src/core/PeriodicWave.ts b/packages/react-native-audio-api/src/core/PeriodicWave.ts index 34dd8b8cb..7e9e7fa40 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -2,35 +2,21 @@ import { IPeriodicWave } from '../interfaces'; import BaseAudioContext from './BaseAudioContext'; import { TPeriodicWaveOptions } from '../types'; import { PeriodicWaveConstraints } from '../defaults'; -import { NotSupportedError } from '../errors'; +import { PeriodicWaveOptionsValidator } from '../options-validators'; -export function validatePeriodicWaveOptions( - sampleRate: number, +export function generateRealAndImag( 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); - } + real = new Float32Array(2); + imag = new Float32Array(2); 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" - ); - } + real = options.real; + imag = options.imag; + PeriodicWaveOptionsValidator.validate(options); if (real && !imag) { imag = new Float32Array(real.length); } else if (!real && imag) { @@ -48,10 +34,7 @@ export default class PeriodicWave { public readonly periodicWave: IPeriodicWave; constructor(context: BaseAudioContext, options?: TPeriodicWaveOptions) { - const finalOptions = validatePeriodicWaveOptions( - context.sampleRate, - options - ); + const finalOptions = generateRealAndImag(options); this.periodicWave = context.context.createPeriodicWave( finalOptions.real!, finalOptions.imag!, diff --git a/packages/react-native-audio-api/src/options-validators.ts b/packages/react-native-audio-api/src/options-validators.ts new file mode 100644 index 000000000..b3a7fe8bd --- /dev/null +++ b/packages/react-native-audio-api/src/options-validators.ts @@ -0,0 +1,66 @@ +import { IndexSizeError, NotSupportedError } from './errors'; +import { + OptionsValidator, + TAnalyserOptions, + TConvolverOptions, + TOscillatorOptions, + TPeriodicWaveOptions, +} from './types'; + +export const AnalyserOptionsValidator: OptionsValidator = { + validate(options: TAnalyserOptions): void { + const allowedFFTSize: number[] = [ + 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, + ]; + if (options.fftSize && !allowedFFTSize.includes(options.fftSize)) { + throw new IndexSizeError( + `fftSize must be one of the following values: ${allowedFFTSize.join( + ', ' + )}` + ); + } + }, +}; + +export const ConvolverOptionsValidator: OptionsValidator = { + validate(options: TConvolverOptions): void { + 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.` + ); + } + } + }, +}; + +export const OscillatorOptionsValidator: OptionsValidator = + { + validate(options: TOscillatorOptions): void { + if (options.type === 'custom' && !options.periodicWave) { + throw new NotSupportedError( + "'type' cannot be set to 'custom' without providing a 'periodicWave'." + ); + } + }, + }; + +export const PeriodicWaveOptionsValidator: OptionsValidator = + { + validate(options: TPeriodicWaveOptions): void { + if ( + options.real && + options.imag && + options.real.length !== options.imag.length + ) { + throw new NotSupportedError( + "'real' and 'imag' arrays must have the same length" + ); + } + }, + }; diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index b4cf35c98..aa371284f 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -66,6 +66,10 @@ export interface TAnalyserOptions extends TAudioNodeOptions { smoothingTimeConstant?: number; } +export interface OptionsValidator { + validate(options: T): void; +} + export interface TBiquadFilterOptions extends TAudioNodeOptions { type?: BiquadFilterType; frequency?: number; 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 dba3398fa..471d77c6e 100644 --- a/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx +++ b/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx @@ -1,16 +1,13 @@ import BaseAudioContext from './BaseAudioContext'; import { TPeriodicWaveOptions } from '../types'; -import { validatePeriodicWaveOptions } from '../core/PeriodicWave'; +import { generateRealAndImag } from '../core/PeriodicWave'; export default class PeriodicWave { /** @internal */ readonly periodicWave: globalThis.PeriodicWave; constructor(context: BaseAudioContext, options?: TPeriodicWaveOptions) { - const finalOptions = validatePeriodicWaveOptions( - context.sampleRate, - options - ); + const finalOptions = generateRealAndImag(options); const periodicWave = context.context.createPeriodicWave( finalOptions.real!, finalOptions.imag!, From d2019dd31a02b7e8d8ed82af09a92e948c7fdb7e Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 16 Dec 2025 12:59:02 +0100 Subject: [PATCH 31/35] fix: tests --- .../common/cpp/audioapi/core/sources/StreamerNode.cpp | 7 +++++-- .../common/cpp/audioapi/core/sources/StreamerNode.h | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) 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 86e590aa2..d1b7f400d 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 @@ -22,6 +22,7 @@ #include namespace audioapi { +#if !RN_AUDIO_API_FFMPEG_DISABLED StreamerNode::StreamerNode(BaseAudioContext *context, StreamerOptions options) : AudioScheduledSourceNode(context), fmtCtx_(nullptr), @@ -35,8 +36,10 @@ StreamerNode::StreamerNode(BaseAudioContext *context, StreamerOptions options) bufferedBus_(nullptr), audio_stream_index_(-1), maxResampledSamples_(0), - processedSamples_(0), - streamPath_(options.streamPath) {} + processedSamples_(0) {} +#else +StreamerNode::StreamerNode(BaseAudioContext *context, StreamerOptions options) : AudioScheduledSourceNode(context) {} +#endif // RN_AUDIO_API_FFMPEG_DISABLED StreamerNode::~StreamerNode() { #if !RN_AUDIO_API_FFMPEG_DISABLED 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 545a7cffd..79749c2ca 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 @@ -72,7 +72,11 @@ class StreamerNode : public AudioScheduledSourceNode { bool initialize(const std::string &inputUrl); std::string getStreamPath() const { +#if !RN_AUDIO_API_TEST return streamPath_; +#else + return ""; +#endif // RN_AUDIO_API_TEST } protected: From 953c4e5e182e98415650cdbd64e35ebde187356b Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 16 Dec 2025 13:25:44 +0100 Subject: [PATCH 32/35] fix: playback speed app --- .../common-app/src/examples/PlaybackSpeed/PlaybackSpeed.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/common-app/src/examples/PlaybackSpeed/PlaybackSpeed.tsx b/apps/common-app/src/examples/PlaybackSpeed/PlaybackSpeed.tsx index 3bef6d49e..9779b8f4d 100644 --- a/apps/common-app/src/examples/PlaybackSpeed/PlaybackSpeed.tsx +++ b/apps/common-app/src/examples/PlaybackSpeed/PlaybackSpeed.tsx @@ -58,11 +58,7 @@ const PlaybackSpeed: FC = () => { ) ); - const source = audioContext.createBufferSource({ - pitchCorrection: audioSettings.PSOLA - ? false - : audioSettings.pitchCorrection, - }); + const source = audioContext.createBufferSource(audioSettings.PSOLA ? false : audioSettings.pitchCorrection); source.buffer = buffer; source.playbackRate.value = audioSettings.PSOLA ? 1 : playbackSpeed; From 97d464e2dd5589d14c3a45fa2a01e7f5834e18a2 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 7 Jan 2026 14:20:36 +0100 Subject: [PATCH 33/35] feat: options in wave shaper --- .../src/demos/PedalBoard/EchoPedal.tsx | 16 +++--------- .../BaseAudioContextHostObject.cpp | 8 +++--- .../audioapi/HostObjects/utils/NodeOptions.h | 7 +++++ .../HostObjects/utils/NodeOptionsParser.h | 26 ++++++++++++++++++- .../common/cpp/audioapi/core/AudioNode.cpp | 13 ++++------ .../common/cpp/audioapi/core/AudioNode.h | 1 + .../cpp/audioapi/core/BaseAudioContext.cpp | 4 +-- .../audioapi/core/effects/WaveShaperNode.cpp | 7 ++--- .../audioapi/core/effects/WaveShaperNode.h | 3 ++- .../src/core/effects/WaveShaperNodeTest.cpp | 7 ++--- .../src/core/BaseAudioContext.ts | 2 +- .../src/core/WaveShaperNode.ts | 17 ++++++++++++ .../react-native-audio-api/src/defaults.ts | 6 +++++ .../react-native-audio-api/src/interfaces.ts | 3 ++- packages/react-native-audio-api/src/types.ts | 5 ++++ 15 files changed, 89 insertions(+), 36 deletions(-) diff --git a/apps/common-app/src/demos/PedalBoard/EchoPedal.tsx b/apps/common-app/src/demos/PedalBoard/EchoPedal.tsx index c5683a827..72bbef8f4 100644 --- a/apps/common-app/src/demos/PedalBoard/EchoPedal.tsx +++ b/apps/common-app/src/demos/PedalBoard/EchoPedal.tsx @@ -1,8 +1,7 @@ import React, { useRef, useState, useEffect } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { GestureDetector, Gesture } from 'react-native-gesture-handler'; -import { VerticalSlider } from '../../components'; -import { ConvolverNode, AudioNode, AudioContext, GainNode } from 'react-native-audio-api'; +import { ConvolverNode, AudioNode, AudioContext } from 'react-native-audio-api'; import { makeEchoCurve } from './curves'; interface EchoPedalProps { @@ -17,7 +16,6 @@ export default function EchoPedal({ outputNode, }: EchoPedalProps) { const [isActive, setIsActive] = useState(false); - const [time, setTime] = useState(0.5); // Echo delay time (0-1 mapped to actual delay) const convolverNodeRef = useRef(null); @@ -32,23 +30,15 @@ export default function EchoPedal({ } }, [isActive, inputNode, outputNode]); - useEffect(() => { - if (isActive && inputNode && outputNode) { - applyEffect(context, inputNode, outputNode); - } - }, [time]); - const applyEffect = (context: AudioContext, inputNode: AudioNode, outputNode: AudioNode) => { if (convolverNodeRef.current) { inputNode.disconnect(convolverNodeRef.current); convolverNodeRef.current.disconnect(); } - // Map slider value (0-1) to delay time (0.1s - 2.0s) - const delayTime = 0.1 + time * 1.9; // Create new convolver with echo curve - const convolver = context.createConvolver({ disableNormalization: true}); - convolver.buffer = makeEchoCurve(delayTime, context); + const convolver = new ConvolverNode(context, { disableNormalization: true}) + convolver.buffer = makeEchoCurve(0.7, context); convolverNodeRef.current = convolver; // Reconnect audio graph 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 c846eb74f..e01c88a27 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 @@ -305,15 +305,17 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) { auto convolver = context_->createConvolver(convolverOptions); auto convolverHostObject = std::make_shared(convolver); auto jsiObject = jsi::Object::createFromHostObject(runtime, convolverHostObject); - if (!args[0].isUndefined()) { - auto bufferHostObject = args[0].getObject(runtime).asHostObject(runtime); + if (convolverOptions.bus != nullptr) { + auto bufferHostObject = options.getProperty(runtime, "buffer").getObject(runtime).asHostObject(runtime); jsiObject.setExternalMemoryPressure(runtime, bufferHostObject->getSizeInBytes()); } return jsiObject; } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWaveShaper) { - auto waveShaper = context_->createWaveShaper(); + auto options = args[0].asObject(runtime); + auto waveShaperOptions = audioapi::option_parser::parseWaveShaperOptions(runtime, options); + auto waveShaper = context_->createWaveShaper(waveShaperOptions); auto waveShaperHostObject = std::make_shared(waveShaper); return jsi::Object::createFromHostObject(runtime, waveShaperHostObject); } 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 index b1eb35453..090a0a2fa 100644 --- 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 @@ -11,6 +11,8 @@ #include #include #include +#include +#include namespace audioapi { struct AudioNodeOptions { @@ -101,4 +103,9 @@ struct IIRFilterOptions : AudioNodeOptions { : feedforward(std::move(ff)), feedback(std::move(fb)) {} }; +struct WaveShaperOptions : AudioNodeOptions { + std::shared_ptr curve = nullptr; + OverSampleType oversample = OverSampleType::OVERSAMPLE_NONE; +}; + } // 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 index 109410987..ede0e4a94 100644 --- 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 @@ -58,7 +58,7 @@ StereoPannerOptions parseStereoPannerOptions( ConvolverOptions parseConvolverOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { ConvolverOptions options(parseAudioNodeOptions(runtime, optionsObject)); options.disableNormalization = - static_cast(optionsObject.getProperty(runtime, "disableNormalization").getNumber()); + optionsObject.getProperty(runtime, "disableNormalization").getBool(); if (optionsObject.hasProperty(runtime, "buffer")) { auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") .getObject(runtime) @@ -235,4 +235,28 @@ IIRFilterOptions parseIIRFilterOptions(jsi::Runtime &runtime, const jsi::Object return options; } + +WaveShaperOptions parseWaveShaperOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + WaveShaperOptions options(parseAudioNodeOptions(runtime, optionsObject)); + + auto oversampleStr = + optionsObject.getProperty(runtime, "oversample").asString(runtime).utf8(runtime); + + if (oversampleStr == "none") { + options.oversample = OverSampleType::OVERSAMPLE_NONE; + } else if (oversampleStr == "2x") { + options.oversample = OverSampleType::OVERSAMPLE_2X; + } else if (oversampleStr == "4x") { + options.oversample = OverSampleType::OVERSAMPLE_4X; + } + + if (optionsObject.hasProperty(runtime, "buffer")) { + auto arrayBuffer = optionsObject.getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime); + + options.curve = std::make_shared( + reinterpret_cast(arrayBuffer.data(runtime)), + static_cast(arrayBuffer.size(runtime) / sizeof(float))); + } + return 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 fba7858df..308b27aa4 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 @@ -11,15 +11,12 @@ namespace audioapi { -AudioNode::AudioNode(std::shared_ptr context) - : context_(context), - audioBus_( - std::make_shared( - RENDER_QUANTUM_SIZE, - channelCount_, - context->getSampleRate())) {} +AudioNode::AudioNode(std::shared_ptr context) : context_(context) { + audioBus_ = + std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); +} -AudioNode::AudioNode(BaseAudioContext *context, const AudioNodeOptions &options) +AudioNode::AudioNode(std::shared_ptr context, const AudioNodeOptions &options) : context_(context), channelCount_(options.channelCount), channelCountMode_(options.channelCountMode), 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 3fee65dca..a809a9e97 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 @@ -20,6 +20,7 @@ class AudioNodeOptions; class AudioNode : public std::enable_shared_from_this { public: + explicit AudioNode(std::shared_ptr context); explicit AudioNode(std::shared_ptr context, const AudioNodeOptions &options); virtual ~AudioNode(); 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 1e9f8e32c..9da5365a0 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 @@ -209,8 +209,8 @@ std::shared_ptr BaseAudioContext::createConvolver(ConvolverOption return convolver; } -std::shared_ptr BaseAudioContext::createWaveShaper() { - auto waveShaper = std::make_shared(shared_from_this()); +std::shared_ptr BaseAudioContext::createWaveShaper(WaveShaperOptions options) { + auto waveShaper = std::make_shared(shared_from_this(), std::move(options)); nodeManager_->addProcessingNode(waveShaper); return waveShaper; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index d3abd0d5e..a59af4e33 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -10,14 +11,14 @@ namespace audioapi { -WaveShaperNode::WaveShaperNode(std::shared_ptr context) - : AudioNode(context), oversample_(OverSampleType::OVERSAMPLE_NONE) { +WaveShaperNode::WaveShaperNode(std::shared_ptr context, WaveShaperOptions options) + : AudioNode(context, options), oversample_(options.oversample) { waveShapers_.reserve(6); for (int i = 0; i < channelCount_; i++) { waveShapers_.emplace_back(std::make_unique(nullptr)); } - + setCurve(options.curve); // to change after graph processing improvement - should be max channelCountMode_ = ChannelCountMode::CLAMPED_MAX; isInitialized_ = true; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 00c1ddc35..43f4ee77c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -16,10 +16,11 @@ namespace audioapi { class AudioBus; class AudioArray; +class WaveShaperOptions; class WaveShaperNode : public AudioNode { public: - explicit WaveShaperNode(std::shared_ptr context); + explicit WaveShaperNode(std::shared_ptr context, WaveShaperOptions options); [[nodiscard]] std::string getOversample() const; [[nodiscard]] std::shared_ptr getCurve() const; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 223a0e3ae..86b1b7ab0 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -25,7 +26,7 @@ class WaveShaperNodeTest : public ::testing::Test { class TestableWaveShaperNode : public WaveShaperNode { public: explicit TestableWaveShaperNode(std::shared_ptr context) - : WaveShaperNode(context) { + : WaveShaperNode(context, WaveShaperOptions()) { testCurve_ = std::make_shared(3); auto data = testCurve_->getData(); data[0] = -2.0f; @@ -43,12 +44,12 @@ class TestableWaveShaperNode : public WaveShaperNode { }; TEST_F(WaveShaperNodeTest, WaveShaperNodeCanBeCreated) { - auto waveShaper = context->createWaveShaper(); + auto waveShaper = context->createWaveShaper(WaveShaperOptions()); ASSERT_NE(waveShaper, nullptr); } TEST_F(WaveShaperNodeTest, NullCanBeAsignedToCurve) { - auto waveShaper = context->createWaveShaper(); + auto waveShaper = context->createWaveShaper(WaveShaperOptions()); ASSERT_NO_THROW(waveShaper->setCurve(nullptr)); ASSERT_EQ(waveShaper->getCurve(), nullptr); } diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 7d7b1a8f5..ba0604f12 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -254,6 +254,6 @@ export default class BaseAudioContext { } createWaveShaper(): WaveShaperNode { - return new WaveShaperNode(this, this.context.createWaveShaper()); + return new WaveShaperNode(this); } } diff --git a/packages/react-native-audio-api/src/core/WaveShaperNode.ts b/packages/react-native-audio-api/src/core/WaveShaperNode.ts index 069c4d52b..c971b1591 100644 --- a/packages/react-native-audio-api/src/core/WaveShaperNode.ts +++ b/packages/react-native-audio-api/src/core/WaveShaperNode.ts @@ -1,10 +1,27 @@ import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; import { InvalidStateError } from '../errors'; import { IWaveShaperNode } from '../interfaces'; +import { WaveShaperOptions } from '../defaults'; +import { TWaveShaperOptions } from '../types'; export default class WaveShaperNode extends AudioNode { private isCurveSet: boolean = false; + constructor(context: BaseAudioContext, options?: TWaveShaperOptions) { + const finalOptions: TWaveShaperOptions = { + ...WaveShaperOptions, + ...options, + }; + + const node = context.context.createWaveShaper(finalOptions); + super(context, node); + + if (finalOptions.curve) { + this.isCurveSet = true; + } + } + get curve(): Float32Array | null { if (!this.isCurveSet) { return null; diff --git a/packages/react-native-audio-api/src/defaults.ts b/packages/react-native-audio-api/src/defaults.ts index cc6f0eee2..e40433ddb 100644 --- a/packages/react-native-audio-api/src/defaults.ts +++ b/packages/react-native-audio-api/src/defaults.ts @@ -12,6 +12,7 @@ import { TAudioBufferSourceOptions, TAudioBufferOptions, TDelayOptions, + TWaveShaperOptions, } from './types'; export const AudioNodeOptions: TAudioNodeOptions = { @@ -92,3 +93,8 @@ export const DelayOptions: TDelayOptions = { maxDelayTime: 1.0, delayTime: 0.0, }; + +export const WaveShaperOptions: TWaveShaperOptions = { + ...AudioNodeOptions, + oversample: 'none', +}; diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 1083966d9..b1edbab86 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -24,6 +24,7 @@ import type { TDelayOptions, TIIRFilterOptions, OverSampleType, + TWaveShaperOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -103,7 +104,7 @@ export interface IBaseAudioContext { createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; createStreamer: (streamerOptions?: TStreamerOptions) => IStreamerNode | null; // null when FFmpeg is not enabled - createWaveShaper: () => IWaveShaperNode; + createWaveShaper: (waveShaperOptions?: TWaveShaperOptions) => IWaveShaperNode; } export interface IAudioContext extends IBaseAudioContext { diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 4b408dcc6..bff2a4c41 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -228,3 +228,8 @@ export interface TDelayOptions extends TAudioNodeOptions { maxDelayTime?: number; delayTime?: number; } + +export interface TWaveShaperOptions extends TAudioNodeOptions { + curve?: Float32Array; + oversample?: OverSampleType; +} From 234ec6ef6e1a86a31733590b0769cde426fd82cf Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 8 Jan 2026 14:09:39 +0100 Subject: [PATCH 34/35] feat: options as references --- .../BaseAudioContextHostObject.cpp | 54 +++++++------- .../audioapi/HostObjects/utils/NodeOptions.h | 72 +++++++++---------- .../cpp/audioapi/core/BaseAudioContext.cpp | 56 +++++++-------- .../cpp/audioapi/core/BaseAudioContext.h | 29 ++++---- .../audioapi/core/analysis/AnalyserNode.cpp | 2 +- .../cpp/audioapi/core/analysis/AnalyserNode.h | 2 +- .../core/effects/BiquadFilterNode.cpp | 2 +- .../audioapi/core/effects/BiquadFilterNode.h | 4 +- .../audioapi/core/effects/ConvolverNode.cpp | 2 +- .../cpp/audioapi/core/effects/ConvolverNode.h | 4 +- .../cpp/audioapi/core/effects/DelayNode.cpp | 2 +- .../cpp/audioapi/core/effects/DelayNode.h | 2 +- .../cpp/audioapi/core/effects/GainNode.cpp | 2 +- .../cpp/audioapi/core/effects/GainNode.h | 2 +- .../audioapi/core/effects/IIRFilterNode.cpp | 2 +- .../cpp/audioapi/core/effects/IIRFilterNode.h | 4 +- .../core/effects/StereoPannerNode.cpp | 2 +- .../audioapi/core/effects/StereoPannerNode.h | 4 +- .../audioapi/core/effects/WaveShaperNode.cpp | 2 +- .../audioapi/core/effects/WaveShaperNode.h | 4 +- .../cpp/audioapi/core/sources/AudioBuffer.cpp | 2 +- .../cpp/audioapi/core/sources/AudioBuffer.h | 2 +- .../sources/AudioBufferBaseSourceNode.cpp | 2 +- .../core/sources/AudioBufferBaseSourceNode.h | 2 +- .../sources/AudioBufferQueueSourceNode.cpp | 2 +- .../core/sources/AudioBufferQueueSourceNode.h | 2 +- .../core/sources/AudioBufferSourceNode.cpp | 2 +- .../core/sources/AudioBufferSourceNode.h | 2 +- .../core/sources/ConstantSourceNode.cpp | 2 +- .../core/sources/ConstantSourceNode.h | 2 +- .../audioapi/core/sources/OscillatorNode.cpp | 2 +- .../audioapi/core/sources/OscillatorNode.h | 4 +- .../audioapi/core/sources/StreamerNode.cpp | 2 +- .../cpp/audioapi/core/sources/StreamerNode.h | 2 +- 34 files changed, 148 insertions(+), 135 deletions(-) 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 e01c88a27..73f51daa5 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 @@ -161,8 +161,8 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createRecorderAdapter) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { - auto options = args[0].asObject(runtime); - auto oscillatorOptions = audioapi::option_parser::parseOscillatorOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto oscillatorOptions = audioapi::option_parser::parseOscillatorOptions(runtime, options); auto oscillator = context_->createOscillator(oscillatorOptions); auto oscillatorHostObject = std::make_shared(oscillator); return jsi::Object::createFromHostObject(runtime, oscillatorHostObject); @@ -172,7 +172,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { #if !RN_AUDIO_API_FFMPEG_DISABLED auto streamerOptions = StreamerOptions(); if (!args[0].isUndefined()) { - auto options = args[0].asObject(runtime); + const auto options = args[0].asObject(runtime); streamerOptions = audioapi::option_parser::parseStreamerOptions(runtime, options); } auto streamer = context_->createStreamer(streamerOptions); @@ -186,8 +186,8 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { - auto options = args[0].asObject(runtime); - auto constantSourceOptions = + const auto options = args[0].asObject(runtime); + const auto constantSourceOptions = audioapi::option_parser::parseConstantSourceOptions(runtime, options); auto constantSource = context_->createConstantSource(constantSourceOptions); auto constantSourceHostObject = std::make_shared(constantSource); @@ -195,16 +195,16 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { - auto options = args[0].asObject(runtime); - auto gainOptions = audioapi::option_parser::parseGainOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto gainOptions = audioapi::option_parser::parseGainOptions(runtime, options); auto gain = context_->createGain(std::move(gainOptions)); auto gainHostObject = std::make_shared(gain); return jsi::Object::createFromHostObject(runtime, gainHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createDelay) { - auto options = args[0].asObject(runtime); - auto delayOptions = audioapi::option_parser::parseDelayOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto delayOptions = audioapi::option_parser::parseDelayOptions(runtime, options); auto delayNode = context_->createDelay(delayOptions); auto delayNodeHostObject = std::make_shared(delayNode); auto jsiObject = jsi::Object::createFromHostObject(runtime, delayNodeHostObject); @@ -213,32 +213,32 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createDelay) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { - auto options = args[0].asObject(runtime); - auto stereoPannerOptions = audioapi::option_parser::parseStereoPannerOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto 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 options = args[0].asObject(runtime); - auto biquadFilterOptions = audioapi::option_parser::parseBiquadFilterOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto 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, createIIRFilter) { - auto options = args[0].asObject(runtime); - auto iirFilterOptions = audioapi::option_parser::parseIIRFilterOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto iirFilterOptions = audioapi::option_parser::parseIIRFilterOptions(runtime, options); auto iirFilter = context_->createIIRFilter(iirFilterOptions); auto iirFilterHostObject = std::make_shared(iirFilter); return jsi::Object::createFromHostObject(runtime, iirFilterHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { - auto options = args[0].asObject(runtime); - auto audioBufferSourceOptions = + const auto options = args[0].asObject(runtime); + const auto audioBufferSourceOptions = audioapi::option_parser::parseAudioBufferSourceOptions(runtime, options); auto bufferSource = context_->createBufferSource(audioBufferSourceOptions); auto bufferSourceHostObject = std::make_shared(bufferSource); @@ -246,8 +246,8 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) { - auto options = args[0].asObject(runtime); - auto baseAudioBufferSourceOptions = + const auto options = args[0].asObject(runtime); + const auto baseAudioBufferSourceOptions = audioapi::option_parser::parseBaseAudioBufferSourceOptions(runtime, options); auto bufferSource = context_->createBufferQueueSource(baseAudioBufferSourceOptions); auto bufferStreamSourceHostObject = @@ -256,8 +256,8 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBuffer) { - auto options = args[0].asObject(runtime); - auto audioBufferOptions = audioapi::option_parser::parseAudioBufferOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto audioBufferOptions = audioapi::option_parser::parseAudioBufferOptions(runtime, options); auto buffer = BaseAudioContext::createBuffer(audioBufferOptions); auto bufferHostObject = std::make_shared(buffer); @@ -292,16 +292,16 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createPeriodicWave) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { - auto options = args[0].asObject(runtime); - auto analyserOptions = audioapi::option_parser::parseAnalyserOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto 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 options = args[0].asObject(runtime); - auto convolverOptions = audioapi::option_parser::parseConvolverOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto 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); @@ -313,8 +313,8 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWaveShaper) { - auto options = args[0].asObject(runtime); - auto waveShaperOptions = audioapi::option_parser::parseWaveShaperOptions(runtime, options); + const auto options = args[0].asObject(runtime); + const auto waveShaperOptions = audioapi::option_parser::parseWaveShaperOptions(runtime, options); auto waveShaper = context_->createWaveShaper(waveShaperOptions); auto waveShaperHostObject = std::make_shared(waveShaper); return jsi::Object::createFromHostObject(runtime, waveShaperHostObject); 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 index 090a0a2fa..5f988810e 100644 --- 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 @@ -16,76 +16,76 @@ namespace audioapi { struct AudioNodeOptions { - int channelCount = 2; - ChannelCountMode channelCountMode = ChannelCountMode::MAX; - ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS; + int channelCount; + ChannelCountMode channelCountMode; + ChannelInterpretation channelInterpretation; }; struct GainOptions : AudioNodeOptions { - float gain = 1.0f; + float gain; }; struct StereoPannerOptions : AudioNodeOptions { - float pan = 0.0f; + float pan; }; struct ConvolverOptions : AudioNodeOptions { - std::shared_ptr bus = nullptr; - bool disableNormalization = false; + std::shared_ptr bus; + bool disableNormalization; }; struct ConstantSourceOptions { - float offset = 1.0f; + float offset; }; struct AnalyserOptions : AudioNodeOptions { - int fftSize = 2048; - float minDecibels = -100.0f; - float maxDecibels = -30.0f; - float smoothingTimeConstant = 0.8f; + int fftSize; + float minDecibels; + float maxDecibels; + float smoothingTimeConstant; }; struct BiquadFilterOptions : AudioNodeOptions { - BiquadFilterType type = BiquadFilterType::LOWPASS; - float frequency = 350.0f; - float detune = 0.0f; - float Q = 1.0f; - float gain = 0.0f; + BiquadFilterType type; + float frequency; + float detune; + float Q; + float gain; }; struct OscillatorOptions { - std::shared_ptr periodicWave = nullptr; - float frequency = 440.0f; - float detune = 0.0f; - OscillatorType type = OscillatorType::SINE; + std::shared_ptr periodicWave; + float frequency; + float detune; + OscillatorType type; }; struct BaseAudioBufferSourceOptions { - float detune = 0.0f; - bool pitchCorrection = false; - float playbackRate = 1.0f; + float detune; + bool pitchCorrection; + float playbackRate; }; struct AudioBufferSourceOptions : BaseAudioBufferSourceOptions { - std::shared_ptr buffer = nullptr; - bool loop = false; - float loopStart = 0.0f; - float loopEnd = 0.0f; + std::shared_ptr buffer; + bool loop; + float loopStart; + float loopEnd; }; struct StreamerOptions { - std::string streamPath = ""; + std::string streamPath; }; struct AudioBufferOptions { - int numberOfChannels = 1; - size_t length = 0; - float sampleRate = 44100.0f; + int numberOfChannels; + size_t length; + float sampleRate; }; struct DelayOptions : AudioNodeOptions { - float maxDelayTime = 1.0f; - float delayTime = 0.0f; + float maxDelayTime; + float delayTime; }; struct IIRFilterOptions : AudioNodeOptions { @@ -104,8 +104,8 @@ struct IIRFilterOptions : AudioNodeOptions { }; struct WaveShaperOptions : AudioNodeOptions { - std::shared_ptr curve = nullptr; - OverSampleType oversample = OverSampleType::OVERSAMPLE_NONE; + std::shared_ptr curve; + OverSampleType oversample; }; } // namespace audioapi 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 9da5365a0..ef9f7bf55 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 @@ -117,22 +117,22 @@ std::shared_ptr BaseAudioContext::createRecorderAdapter() { return recorderAdapter; } -std::shared_ptr BaseAudioContext::createOscillator(OscillatorOptions options) { - auto oscillator = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createOscillator(const OscillatorOptions &options) { + auto oscillator = std::make_shared(shared_from_this(), options); nodeManager_->addSourceNode(oscillator); return oscillator; } std::shared_ptr BaseAudioContext::createConstantSource( - ConstantSourceOptions options) { - auto constantSource = std::make_shared(shared_from_this(), std::move(options)); + const ConstantSourceOptions &options) { + auto constantSource = std::make_shared(shared_from_this(), options); nodeManager_->addSourceNode(constantSource); return constantSource; } -std::shared_ptr BaseAudioContext::createStreamer(StreamerOptions options) { +std::shared_ptr BaseAudioContext::createStreamer(const StreamerOptions &options) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = std::make_shared(shared_from_this(), std::move(options)); + auto streamer = std::make_shared(shared_from_this(), options); nodeManager_->addSourceNode(streamer); return streamer; #else @@ -140,54 +140,54 @@ std::shared_ptr BaseAudioContext::createStreamer(StreamerOptions o #endif // RN_AUDIO_API_FFMPEG_DISABLED } -std::shared_ptr BaseAudioContext::createGain(GainOptions options) { - auto gain = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createGain(const GainOptions &options) { + auto gain = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(gain); return gain; } std::shared_ptr BaseAudioContext::createStereoPanner( - StereoPannerOptions options) { - auto stereoPanner = std::make_shared(shared_from_this(), std::move(options)); + const StereoPannerOptions &options) { + auto stereoPanner = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; } -std::shared_ptr BaseAudioContext::createDelay(DelayOptions options) { - auto delay = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createDelay(const DelayOptions &options) { + auto delay = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(delay); return delay; } std::shared_ptr BaseAudioContext::createBiquadFilter( - BiquadFilterOptions options) { - auto biquadFilter = std::make_shared(shared_from_this(), std::move(options)); + const BiquadFilterOptions &options) { + auto biquadFilter = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(biquadFilter); return biquadFilter; } std::shared_ptr BaseAudioContext::createBufferSource( - AudioBufferSourceOptions options) { - auto bufferSource = std::make_shared(shared_from_this(), std::move(options)); + const AudioBufferSourceOptions &options) { + auto bufferSource = std::make_shared(shared_from_this(), options); nodeManager_->addSourceNode(bufferSource); return bufferSource; } -std::shared_ptr BaseAudioContext::createIIRFilter(IIRFilterOptions options) { - auto iirFilter = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createIIRFilter(const IIRFilterOptions &options) { + auto iirFilter = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(iirFilter); return iirFilter; } std::shared_ptr BaseAudioContext::createBufferQueueSource( - BaseAudioBufferSourceOptions options) { - auto bufferSource = std::make_shared(shared_from_this(), std::move(options)); + const BaseAudioBufferSourceOptions &options) { + auto bufferSource = std::make_shared(shared_from_this(), options); nodeManager_->addSourceNode(bufferSource); return bufferSource; } -std::shared_ptr BaseAudioContext::createBuffer(AudioBufferOptions options) { - return std::make_shared(std::move(options)); +std::shared_ptr BaseAudioContext::createBuffer(const AudioBufferOptions &options) { + return std::make_shared(options); } std::shared_ptr BaseAudioContext::createPeriodicWave( @@ -197,20 +197,20 @@ std::shared_ptr BaseAudioContext::createPeriodicWave( return std::make_shared(sampleRate_, complexData, length, disableNormalization); } -std::shared_ptr BaseAudioContext::createAnalyser(AnalyserOptions options) { - auto analyser = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createAnalyser(const AnalyserOptions &options) { + auto analyser = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(analyser); return analyser; } -std::shared_ptr BaseAudioContext::createConvolver(ConvolverOptions options) { - auto convolver = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createConvolver(const ConvolverOptions &options) { + auto convolver = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(convolver); return convolver; } -std::shared_ptr BaseAudioContext::createWaveShaper(WaveShaperOptions options) { - auto waveShaper = std::make_shared(shared_from_this(), std::move(options)); +std::shared_ptr BaseAudioContext::createWaveShaper(const WaveShaperOptions &options) { + auto waveShaper = std::make_shared(shared_from_this(), options); nodeManager_->addProcessingNode(waveShaper); return waveShaper; } 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 3cafa7e3a..93d369fab 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 @@ -83,25 +83,26 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createDelay(DelayOptions options); - std::shared_ptr createIIRFilter(IIRFilterOptions options); - std::shared_ptr createOscillator(OscillatorOptions options); - std::shared_ptr createConstantSource(ConstantSourceOptions options); - std::shared_ptr createStreamer(StreamerOptions options); - std::shared_ptr createGain(GainOptions options); - std::shared_ptr createStereoPanner(StereoPannerOptions options); - std::shared_ptr createBiquadFilter(BiquadFilterOptions options); - std::shared_ptr createBufferSource(AudioBufferSourceOptions options); + std::shared_ptr createDelay(const DelayOptions &options); + std::shared_ptr createIIRFilter(const IIRFilterOptions &options); + std::shared_ptr createOscillator(const OscillatorOptions &options); + std::shared_ptr createConstantSource(const ConstantSourceOptions &options); + std::shared_ptr createStreamer(const StreamerOptions &options); + std::shared_ptr createGain(const GainOptions &options); + std::shared_ptr createStereoPanner(const StereoPannerOptions &options); + std::shared_ptr createBiquadFilter(const BiquadFilterOptions &options); + std::shared_ptr createBufferSource( + const AudioBufferSourceOptions &options); std::shared_ptr createBufferQueueSource( - BaseAudioBufferSourceOptions options); - static std::shared_ptr createBuffer(AudioBufferOptions options); + const BaseAudioBufferSourceOptions &options); + static std::shared_ptr createBuffer(const AudioBufferOptions &options); std::shared_ptr createPeriodicWave( const std::vector> &complexData, bool disableNormalization, int length); - std::shared_ptr createAnalyser(AnalyserOptions options); - std::shared_ptr createConvolver(ConvolverOptions options); - std::shared_ptr createWaveShaper(WaveShaperOptions options); + std::shared_ptr createAnalyser(const AnalyserOptions &options); + std::shared_ptr createConvolver(const ConvolverOptions &options); + std::shared_ptr createWaveShaper(const WaveShaperOptions &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 ca408244f..f5bf0fcd2 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 @@ -14,7 +14,7 @@ namespace audioapi { -AnalyserNode::AnalyserNode(std::shared_ptr context, AnalyserOptions options) +AnalyserNode::AnalyserNode(std::shared_ptr context, const AnalyserOptions &options) : AudioNode(context, options), fftSize_(options.fftSize), minDecibels_(options.minDecibels), 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 29ef339fc..8b64f44b3 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 @@ -20,7 +20,7 @@ class AnalyserOptions; class AnalyserNode : public AudioNode { public: enum class WindowType { BLACKMAN, HANN }; - explicit AnalyserNode(std::shared_ptr context, AnalyserOptions options); + explicit AnalyserNode(std::shared_ptr context, const AnalyserOptions &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 a4f4f90ac..c4758ac64 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 @@ -39,7 +39,7 @@ namespace audioapi { -BiquadFilterNode::BiquadFilterNode(std::shared_ptr context, BiquadFilterOptions options) : AudioNode(context, options) { +BiquadFilterNode::BiquadFilterNode(std::shared_ptr context, const BiquadFilterOptions &options) : AudioNode(context, options) { frequencyParam_ = std::make_shared(options.frequency, 0.0f, context->getNyquistFrequency(), context); detuneParam_ = std::make_shared( 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 a906fe9a5..23834139a 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 @@ -55,7 +55,9 @@ class BiquadFilterNode : public AudioNode { #endif // RN_AUDIO_API_TEST public: - explicit BiquadFilterNode(std::shared_ptr context, BiquadFilterOptions options); + explicit BiquadFilterNode( + std::shared_ptr context, + const BiquadFilterOptions &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 93ddc6677..a3bbd4f29 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 @@ -12,7 +12,7 @@ #include namespace audioapi { -ConvolverNode::ConvolverNode(std::shared_ptr context, ConvolverOptions options) +ConvolverNode::ConvolverNode(std::shared_ptr context, const ConvolverOptions &options) : AudioNode(context, options), gainCalibrationSampleRate_(context->getSampleRate()), remainingSegments_(0), 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 07e96f19a..84d52291e 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 @@ -21,7 +21,9 @@ class ConvolverOptions; class ConvolverNode : public AudioNode { public: - explicit ConvolverNode(std::shared_ptr context, ConvolverOptions options); + explicit ConvolverNode( + std::shared_ptr context, + const ConvolverOptions &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/DelayNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp index 58128bc10..9de3d3244 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.cpp @@ -8,7 +8,7 @@ namespace audioapi { -DelayNode::DelayNode(std::shared_ptr context, DelayOptions options) +DelayNode::DelayNode(std::shared_ptr context, const DelayOptions &options) : AudioNode(context, options), delayTimeParam_(std::make_shared(options.delayTime, 0, options.maxDelayTime, context)), delayBuffer_( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h index fcade025a..060f4c648 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/DelayNode.h @@ -13,7 +13,7 @@ class DelayOptions; class DelayNode : public AudioNode { public: - explicit DelayNode(std::shared_ptr context, DelayOptions options); + explicit DelayNode(std::shared_ptr context, const DelayOptions &options); [[nodiscard]] std::shared_ptr getDelayTimeParam() 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 8016d153c..94739645a 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 @@ -8,7 +8,7 @@ namespace audioapi { -GainNode::GainNode(std::shared_ptr context, GainOptions options) +GainNode::GainNode(std::shared_ptr context, const GainOptions &options) : AudioNode(context, options), gainParam_( std::make_shared( 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 12c4663a2..cb7d5d5ca 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 @@ -12,7 +12,7 @@ class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(std::shared_ptr context, GainOptions options); + explicit GainNode(std::shared_ptr context, const GainOptions &options); [[nodiscard]] std::shared_ptr getGainParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp index 13548d5c3..bc91ea8ce 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.cpp @@ -36,7 +36,7 @@ namespace audioapi { -IIRFilterNode::IIRFilterNode(std::shared_ptr context, IIRFilterOptions options) +IIRFilterNode::IIRFilterNode(std::shared_ptr context, const IIRFilterOptions &options) : AudioNode(context, options), feedforward_(std::move(options.feedforward)), feedback_(std::move(options.feedback)) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h index d0fca5989..8c0b67a9a 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/IIRFilterNode.h @@ -38,7 +38,9 @@ class IIRFilterOptions; class IIRFilterNode : public AudioNode { public: - explicit IIRFilterNode(std::shared_ptr context, IIRFilterOptions options); + explicit IIRFilterNode( + std::shared_ptr context, + const IIRFilterOptions &options); void getFrequencyResponse( const float *frequencyArray, 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 b2038302f..20f4ba7e6 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 @@ -10,7 +10,7 @@ namespace audioapi { -StereoPannerNode::StereoPannerNode(std::shared_ptr context, StereoPannerOptions options) +StereoPannerNode::StereoPannerNode(std::shared_ptr context, const StereoPannerOptions &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 3633f7c6d..cd8aa9deb 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 @@ -14,7 +14,9 @@ class StereoPannerOptions; class StereoPannerNode : public AudioNode { public: - explicit StereoPannerNode(std::shared_ptr context, StereoPannerOptions options); + explicit StereoPannerNode( + std::shared_ptr context, + const StereoPannerOptions &options); [[nodiscard]] std::shared_ptr getPanParam() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp index a59af4e33..fe4fabdfe 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.cpp @@ -11,7 +11,7 @@ namespace audioapi { -WaveShaperNode::WaveShaperNode(std::shared_ptr context, WaveShaperOptions options) +WaveShaperNode::WaveShaperNode(std::shared_ptr context, const WaveShaperOptions &options) : AudioNode(context, options), oversample_(options.oversample) { waveShapers_.reserve(6); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h index 43f4ee77c..14f38f6d6 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WaveShaperNode.h @@ -20,7 +20,9 @@ class WaveShaperOptions; class WaveShaperNode : public AudioNode { public: - explicit WaveShaperNode(std::shared_ptr context, WaveShaperOptions options); + explicit WaveShaperNode( + std::shared_ptr context, + const WaveShaperOptions &options); [[nodiscard]] std::string getOversample() const; [[nodiscard]] std::shared_ptr getCurve() const; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp index 6e2122f03..528ca7703 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.cpp @@ -9,7 +9,7 @@ namespace audioapi { -AudioBuffer::AudioBuffer(AudioBufferOptions options) +AudioBuffer::AudioBuffer(const AudioBufferOptions &options) : bus_(std::make_shared(options.length, options.numberOfChannels, options.sampleRate)) {} AudioBuffer::AudioBuffer(std::shared_ptr bus) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h index d55dd59ef..34ad18baa 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h @@ -14,7 +14,7 @@ class AudioBufferOptions; class AudioBuffer { public: - explicit AudioBuffer(AudioBufferOptions options); + explicit AudioBuffer(const AudioBufferOptions &options); explicit AudioBuffer(std::shared_ptr bus); [[nodiscard]] size_t getLength() 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 b8cb079bd..2fee4f0fe 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 @@ -13,7 +13,7 @@ namespace audioapi { AudioBufferBaseSourceNode::AudioBufferBaseSourceNode( std::shared_ptr context, - BaseAudioBufferSourceOptions options) + const BaseAudioBufferSourceOptions &options) : AudioScheduledSourceNode(context), pitchCorrection_(options.pitchCorrection), vReadIndex_(0.0) { 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 26d33f623..e731a34b4 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 @@ -17,7 +17,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode { public: explicit AudioBufferBaseSourceNode( std::shared_ptr context, - BaseAudioBufferSourceOptions options); + const BaseAudioBufferSourceOptions &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 4b97793d4..6663a6ef9 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 @@ -20,7 +20,7 @@ namespace audioapi { AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( std::shared_ptr context, - BaseAudioBufferSourceOptions options) + const BaseAudioBufferSourceOptions &options) : AudioBufferBaseSourceNode(context, options) { buffers_ = {}; stretch_->presetDefault(channelCount_, context->getSampleRate()); 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 33440d202..92ced53e6 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 @@ -20,7 +20,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode { public: explicit AudioBufferQueueSourceNode( std::shared_ptr context, - BaseAudioBufferSourceOptions options); + const BaseAudioBufferSourceOptions &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 814d7e0c3..596355994 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 @@ -15,7 +15,7 @@ namespace audioapi { AudioBufferSourceNode::AudioBufferSourceNode( std::shared_ptr context, - AudioBufferSourceOptions options) + const AudioBufferSourceOptions &options) : AudioBufferBaseSourceNode(context, options), loop_(options.loop), loopSkip_(false), 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 1998485d3..f84f8e559 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 @@ -19,7 +19,7 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode { public: explicit AudioBufferSourceNode( std::shared_ptr context, - AudioBufferSourceOptions options); + const AudioBufferSourceOptions &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 f7362387c..3f79d815c 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 @@ -7,7 +7,7 @@ #include namespace audioapi { -ConstantSourceNode::ConstantSourceNode(std::shared_ptr context, ConstantSourceOptions options) +ConstantSourceNode::ConstantSourceNode(std::shared_ptr context, const ConstantSourceOptions &options) : AudioScheduledSourceNode(context) { offsetParam_ = std::make_shared( options.offset, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); 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 b52704046..4b2e92442 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 @@ -16,7 +16,7 @@ class ConstantSourceNode : public AudioScheduledSourceNode { public: explicit ConstantSourceNode( std::shared_ptr context, - ConstantSourceOptions options); + const ConstantSourceOptions &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 2bbf95114..a2a379db0 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 @@ -9,7 +9,7 @@ namespace audioapi { -OscillatorNode::OscillatorNode(std::shared_ptr context, OscillatorOptions options) +OscillatorNode::OscillatorNode(std::shared_ptr context, const OscillatorOptions &options) : AudioScheduledSourceNode(context) { frequencyParam_ = std::make_shared( options.frequency, 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 c9b5b4230..9dc45c5d1 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 @@ -17,7 +17,9 @@ class OscillatorOptions; class OscillatorNode : public AudioScheduledSourceNode { public: - explicit OscillatorNode(std::shared_ptr context, OscillatorOptions options); + explicit OscillatorNode( + std::shared_ptr context, + const OscillatorOptions &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 407fd1e2b..393ec594e 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 @@ -23,7 +23,7 @@ namespace audioapi { #if !RN_AUDIO_API_FFMPEG_DISABLED -StreamerNode::StreamerNode(std::shared_ptr context, StreamerOptions options) +StreamerNode::StreamerNode(std::shared_ptr context, const StreamerOptions &options) : AudioScheduledSourceNode(context), fmtCtx_(nullptr), codecCtx_(nullptr), 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 61666e75f..03f557600 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 @@ -63,7 +63,7 @@ class StreamerOptions; class StreamerNode : public AudioScheduledSourceNode { public: - explicit StreamerNode(std::shared_ptr context, StreamerOptions options); + explicit StreamerNode(std::shared_ptr context, const StreamerOptions &options); ~StreamerNode() override; /** From 96c7181201b52af9b5619c78f2f66e372848ea62 Mon Sep 17 00:00:00 2001 From: michal Date: Thu, 8 Jan 2026 14:48:04 +0100 Subject: [PATCH 35/35] feat: removed default options in c++ --- .../cpp/audioapi/HostObjects/utils/NodeOptions.h | 2 +- .../cpp/audioapi/core/sources/StreamerNode.cpp | 2 +- .../common/cpp/test/src/DelayTest.cpp | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) 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 index 5f988810e..b3fcd250c 100644 --- 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 @@ -16,7 +16,7 @@ namespace audioapi { struct AudioNodeOptions { - int channelCount; + int channelCount = 2; ChannelCountMode channelCountMode; ChannelInterpretation channelInterpretation; }; 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 393ec594e..cddd6cc59 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 @@ -38,7 +38,7 @@ StreamerNode::StreamerNode(std::shared_ptr context, const Stre maxResampledSamples_(0), processedSamples_(0) {} #else -StreamerNode::StreamerNode(std::shared_ptr context, StreamerOptions options) : AudioScheduledSourceNode(context) {} +StreamerNode::StreamerNode(std::shared_ptr context, const StreamerOptions &options) : AudioScheduledSourceNode(context) {} #endif // RN_AUDIO_API_FFMPEG_DISABLED StreamerNode::~StreamerNode() { diff --git a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp index 40e1c0c7f..80f57b80c 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/DelayTest.cpp @@ -26,7 +26,7 @@ class DelayTest : public ::testing::Test { class TestableDelayNode : public DelayNode { public: - explicit TestableDelayNode(std::shared_ptr context) : DelayNode(context, DelayOptions()) {} + explicit TestableDelayNode(std::shared_ptr context, const DelayOptions& options) : DelayNode(context, options) {} void setDelayTimeParam(float value) { getDelayTimeParam()->setValue(value); @@ -47,7 +47,9 @@ TEST_F(DelayTest, DelayCanBeCreated) { TEST_F(DelayTest, DelayWithZeroDelayOutputsInputSignal) { static constexpr float DELAY_TIME = 0.0f; static constexpr int FRAMES_TO_PROCESS = 4; - auto delayNode = TestableDelayNode(context); + auto options = DelayOptions(); + options.maxDelayTime = 1.0f; + auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); @@ -64,7 +66,9 @@ TEST_F(DelayTest, DelayWithZeroDelayOutputsInputSignal) { TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) { float DELAY_TIME = (128.0 / context->getSampleRate()) * 0.5; static constexpr int FRAMES_TO_PROCESS = 128; - auto delayNode = TestableDelayNode(context); + auto options = DelayOptions(); + options.maxDelayTime = 1.0f; + auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate); @@ -88,7 +92,9 @@ TEST_F(DelayTest, DelayAppliesTimeShiftCorrectly) { TEST_F(DelayTest, DelayHandlesTailCorrectly) { float DELAY_TIME = (128.0 / context->getSampleRate()) * 0.5; static constexpr int FRAMES_TO_PROCESS = 128; - auto delayNode = TestableDelayNode(context); + auto options = DelayOptions(); + options.maxDelayTime = 1.0f; + auto delayNode = TestableDelayNode(context, options); delayNode.setDelayTimeParam(DELAY_TIME); auto bus = std::make_shared(FRAMES_TO_PROCESS, 1, sampleRate);