-
-
Notifications
You must be signed in to change notification settings - Fork 34
feat: audio node options in ctor #807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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
TAudioNodeOptionsandTGainOptionstypes for channel configuration - Updates
GainNodeconstructor 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.
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp
Outdated
Show resolved
Hide resolved
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h
Outdated
Show resolved
Hide resolved
packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h
Outdated
Show resolved
Hide resolved
maciejmakowski2003
left a comment
There was a problem hiding this 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?
| : AudioNode(context, options), feedforward_(options.feedforward), feedback_(options.feedback) { | ||
| isInitialized_ = true; |
There was a problem hiding this comment.
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
| struct AudioNodeOptions { | ||
| int channelCount = 2; | ||
| ChannelCountMode channelCountMode = ChannelCountMode::MAX; | ||
| ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS; | ||
| }; | ||
|
|
||
| struct GainOptions : AudioNodeOptions { | ||
| float gain = 1.0f; | ||
| }; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 & ?
| 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); |
There was a problem hiding this comment.
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
| 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); |
There was a problem hiding this comment.
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
| 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); |
There was a problem hiding this comment.
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
| ): 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 { |
There was a problem hiding this comment.
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
poneciak57
left a comment
There was a problem hiding this 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
Closes RNAA-297, RNAA-293
Introduced changes
LIST:
Checklist