Skip to content

Conversation

@mdydek
Copy link
Contributor

@mdydek mdydek commented Nov 13, 2025

Closes RNAA-297, RNAA-293

⚠️ Breaking changes ⚠️

Introduced changes

LIST:

  • AudioBuffer
  • AudioNode
  • AnalyserNode
  • AudioBufferBaseSourceNode
  • AudioBufferQueueSourceNode
  • AudioBufferSourceNode
  • BiquadFilterNode
  • ConstantSourceNode
  • ConvolverNode
  • DelayNode
  • GainNode
  • IIRFilterNode
  • OscillatorNode
  • PeriodicWave
  • RecorderAdapterNode
  • StereoPannerNode
  • StreamerNode
  • WorkletNode
  • WorkletSourceNode
  • WorkletProcessingNode

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added/Conducted relevant tests
  • Performed self-review of the code
  • Updated Web Audio API coverage
  • Added support for web

@mdydek mdydek requested a review from Copilot November 13, 2025 17:13
Copilot finished reviewing on behalf of mdydek November 13, 2025 17:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for passing audio node options (channel configuration) in the GainNode constructor, aligning with the Web Audio API specification. The implementation spans TypeScript and C++ layers, introducing new type definitions and updating constructors to accept optional configuration parameters.

  • Introduces TAudioNodeOptions and TGainOptions types for channel configuration
  • Updates GainNode constructor to accept optional configuration parameters
  • Adds C++ parsing logic for audio node options from JavaScript

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/react-native-audio-api/src/types.ts Adds new type definitions TAudioNodeOptions and TGainOptions for audio node configuration
packages/react-native-audio-api/src/interfaces.ts Updates createGain method signature in IBaseAudioContext to accept TGainOptions parameter
packages/react-native-audio-api/src/defaults.ts Defines default values for audio node and gain options
packages/react-native-audio-api/src/core/GainNode.ts Refactors constructor to accept and merge options with defaults before creating the native node
packages/react-native-audio-api/src/core/BaseAudioContext.ts Simplifies createGain method to delegate parameter handling to GainNode constructor
packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h Updates constructor signature to accept GainOptions parameter
packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.cpp Updates constructor implementation to use options for initializing gain parameter and audio node properties
packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h Updates createGain method signature to accept GainOptions parameter
packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp Updates createGain implementation to pass options to GainNode constructor
packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.h Updates constructor to accept optional AudioNodeOptions parameter
packages/react-native-audio-api/common/cpp/audioapi/core/AudioNode.cpp Implements conditional initialization of channel properties from options
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h Adds parsing functions to convert JavaScript option objects to C++ structs
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h Defines C++ structs for AudioNodeOptions and GainOptions
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp Updates JSI binding to parse options from JavaScript before calling createGain

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Collaborator

@maciejmakowski2003 maciejmakowski2003 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks promising, @michalsek wdyt?

@mdydek mdydek marked this pull request as ready for review December 3, 2025 10:03
Comment on lines 39 to 40
: AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) {
isInitialized_ = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you copy possibly large vector

Comment on lines +16 to +24
struct AudioNodeOptions {
int channelCount = 2;
ChannelCountMode channelCountMode = ChannelCountMode::MAX;
ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS;
};

struct GainOptions : AudioNodeOptions {
float gain = 1.0f;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not a change per se but just a quick reminder.
Remember when you have method

void method(AudioNodeOptions opts) {
    //
}

// and you call it with type extending the node options like so
GainOptions opts;
method(opts);

This type is sliced so it is copied here and it copies only part of AudioNodeOptions. Everything is fine with it but it is worth noting as it has some limitations and in future someone might not know this. So like leave some explanation here on how it works or in AudioNode.

#include <audioapi/core/types/OscillatorType.h>

namespace audioapi {
struct AudioNodeOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can make all types extend some empty base like

struct BaseOption {}

it does not cost anything compilers optimize this and might be helpfull in the future if all options types can be virtualized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like writing features "for the future". Adding some complexity because "maybe someday something will change and it will be helpful" for me should not be done.

explicit AudioBufferBaseSourceNode(BaseAudioContext *context, bool pitchCorrection);
explicit AudioBufferBaseSourceNode(
BaseAudioContext *context,
BaseAudioBufferSourceOptions options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here type gets sliced as explained before maybe const & ?

Comment on lines 19 to 27
if (!AnalyserNode.allowedFFTSize.includes(finalOptions.fftSize!)) {
throw new IndexSizeError(
`fftSize must be one of the following values: ${AnalyserNode.allowedFFTSize.join(
', '
)}`
);
}
const analyserNode: IAnalyserNode =
context.context.createAnalyser(finalOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you have base option interface might be a good idea to add some virtual method validate so option type would be responsible for its validation that might be helpfull if you need to validate like a base type or something and is a good principle

Comment on lines 15 to 28
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here validation should be implemented for option type

Comment on lines 16 to 31
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here validation should be implemented for option type

Comment on lines 10 to 26
): 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here validation should be implemented for option type

Copy link
Contributor

@poneciak57 poneciak57 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job overall but i see some possible improvements

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants