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/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/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/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/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/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/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 88a57983e..c6ea63fcc 100644 --- a/packages/audiodocs/docs/effects/convolver-node.mdx +++ b/packages/audiodocs/docs/effects/convolver-node.mdx @@ -3,6 +3,7 @@ sidebar_position: 2 --- 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 fb816544b..c3f87c13b 100644 --- a/packages/audiodocs/docs/effects/gain-node.mdx +++ b/packages/audiodocs/docs/effects/gain-node.mdx @@ -3,7 +3,7 @@ sidebar_position: 3 --- 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,7 +43,20 @@ You can read more about envelopes and ADSR on [Wikipedia]( | `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) that creates node with default values. ## Properties @@ -61,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/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/audiodocs/docs/effects/periodic-wave.mdx b/packages/audiodocs/docs/effects/periodic-wave.mdx index 9f7e2cd9f..f59b9a993 100644 --- a/packages/audiodocs/docs/effects/periodic-wave.mdx +++ b/packages/audiodocs/docs/effects/periodic-wave.mdx @@ -2,24 +2,39 @@ sidebar_position: 5 --- +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/effects/stereo-panner-node.mdx b/packages/audiodocs/docs/effects/stereo-panner-node.mdx index f0a1db0c0..81495a002 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: 6 --- import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable" -import { ReadOnly } from '@site/src/components/Badges'; +import { Optional, ReadOnly } from '@site/src/components/Badges'; # StereoPannerNode @@ -15,6 +15,19 @@ The `StereoPannerNode` interface represents the change in ratio between two outp ## Constructor +```tsx +constructor(context: BaseAudioContext, stereoPannerOptions?: StereoPannerOptions) +``` + +### `StereoPannerOptions` + +Inherits all properties from [`AudioNodeOptions`](/docs/core/audio-node#audionodeoptions) + +| Parameter | Type | Default | Description | +| :---: | :---: | :----: | :---- | +| `pan` | `number` | 0.0 | Number representing pan value | + +Or by using `BaseAudioContext` factory method: [`BaseAudioContext.createStereoPanner()`](/docs/core/base-audio-context#createstereopanner) ## Properties diff --git a/packages/audiodocs/docs/sources/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 0d22e0e62..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 @@ -45,18 +57,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/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/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index 8487e859c..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 @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include namespace audioapi { @@ -157,14 +159,21 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createRecorderAdapter) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) { - auto oscillator = context_->createOscillator(); + auto options = args[0].asObject(runtime); + 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) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = context_->createStreamer(); + auto streamerOptions = StreamerOptions(); + 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()); @@ -175,20 +184,26 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) { - auto constantSource = context_->createConstantSource(); + auto options = args[0].asObject(runtime); + auto constantSourceOptions = + audioapi::option_parser::parseConstantSourceOptions(runtime, options); + auto constantSource = context_->createConstantSource(constantSourceOptions); auto constantSourceHostObject = std::make_shared(constantSource); return jsi::Object::createFromHostObject(runtime, constantSourceHostObject); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createGain) { - auto gain = context_->createGain(); + auto options = args[0].asObject(runtime); + 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 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()); @@ -196,63 +211,52 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createDelay) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStereoPanner) { - auto stereoPanner = context_->createStereoPanner(); + auto options = args[0].asObject(runtime); + 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 biquadFilter = context_->createBiquadFilter(); + auto options = args[0].asObject(runtime); + 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 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); } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) { - auto pitchCorrection = args[0].asBool(); - auto bufferSource = context_->createBufferSource(pitchCorrection); + auto options = args[0].asObject(runtime); + auto 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); + auto baseAudioBufferSourceOptions = + audioapi::option_parser::parseBaseAudioBufferSourceOptions(runtime, options); + auto bufferSource = context_->createBufferQueueSource(baseAudioBufferSourceOptions); auto bufferStreamSourceHostObject = std::make_shared(bufferSource); return jsi::Object::createFromHostObject(runtime, bufferStreamSourceHostObject); } 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); @@ -286,20 +290,17 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createPeriodicWave) { } JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) { - auto analyser = context_->createAnalyser(); + auto options = args[0].asObject(runtime); + 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 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); + 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); if (!args[0].isUndefined()) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp index fad23a17e..3a9da1c3c 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp @@ -10,6 +10,12 @@ namespace audioapi { StreamerNodeHostObject::StreamerNodeHostObject(const std::shared_ptr &node) : AudioScheduledSourceNodeHostObject(node) { addFunctions(JSI_EXPORT_FUNCTION(StreamerNodeHostObject, initialize)); + addGetters(JSI_EXPORT_PROPERTY_GETTER(StreamerNodeHostObject, streamPath)); +} + +JSI_PROPERTY_GETTER_IMPL(StreamerNodeHostObject, streamPath) { + auto streamerNode = std::static_pointer_cast(node_); + return jsi::String::createFromUtf8(runtime, streamerNode->getStreamPath()); } JSI_HOST_FUNCTION_IMPL(StreamerNodeHostObject, initialize) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h index 33e691679..43118bc0e 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h @@ -19,6 +19,7 @@ class StreamerNodeHostObject : public AudioScheduledSourceNodeHostObject { return SIZE; } + JSI_PROPERTY_GETTER_DECL(streamPath); JSI_HOST_FUNCTION_DECL(initialize); private: diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h new file mode 100644 index 000000000..b1eb35453 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptions.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace audioapi { +struct AudioNodeOptions { + int channelCount = 2; + ChannelCountMode channelCountMode = ChannelCountMode::MAX; + ChannelInterpretation channelInterpretation = ChannelInterpretation::SPEAKERS; +}; + +struct GainOptions : AudioNodeOptions { + float gain = 1.0f; +}; + +struct StereoPannerOptions : AudioNodeOptions { + float pan = 0.0f; +}; + +struct ConvolverOptions : AudioNodeOptions { + std::shared_ptr bus = nullptr; + bool disableNormalization = false; +}; + +struct ConstantSourceOptions { + float offset = 1.0f; +}; + +struct AnalyserOptions : AudioNodeOptions { + int fftSize = 2048; + float minDecibels = -100.0f; + float maxDecibels = -30.0f; + float smoothingTimeConstant = 0.8f; +}; + +struct BiquadFilterOptions : AudioNodeOptions { + BiquadFilterType type = BiquadFilterType::LOWPASS; + float frequency = 350.0f; + float detune = 0.0f; + float Q = 1.0f; + float gain = 0.0f; +}; + +struct OscillatorOptions { + std::shared_ptr periodicWave = nullptr; + float frequency = 440.0f; + float detune = 0.0f; + OscillatorType type = OscillatorType::SINE; +}; + +struct BaseAudioBufferSourceOptions { + float detune = 0.0f; + bool pitchCorrection = false; + float playbackRate = 1.0f; +}; + +struct AudioBufferSourceOptions : BaseAudioBufferSourceOptions { + std::shared_ptr buffer = nullptr; + bool loop = false; + float loopStart = 0.0f; + float loopEnd = 0.0f; +}; + +struct StreamerOptions { + std::string streamPath = ""; +}; + +struct AudioBufferOptions { + int numberOfChannels = 1; + size_t length = 0; + float sampleRate = 44100.0f; +}; + +struct DelayOptions : AudioNodeOptions { + float maxDelayTime = 1.0f; + float delayTime = 0.0f; +}; + +struct IIRFilterOptions : AudioNodeOptions { + std::vector feedforward; + std::vector feedback; + + IIRFilterOptions() = default; + + explicit IIRFilterOptions(const AudioNodeOptions options) : AudioNodeOptions(options) {} + + 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/HostObjects/utils/NodeOptionsParser.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h new file mode 100644 index 000000000..109410987 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h @@ -0,0 +1,238 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace audioapi::option_parser { +AudioNodeOptions 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 options; +} + +GainOptions parseGainOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + GainOptions options(parseAudioNodeOptions(runtime, optionsObject)); + options.gain = static_cast(optionsObject.getProperty(runtime, "gain").getNumber()); + return options; +} + +StereoPannerOptions parseStereoPannerOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + StereoPannerOptions options(parseAudioNodeOptions(runtime, optionsObject)); + options.pan = static_cast(optionsObject.getProperty(runtime, "pan").getNumber()); + return options; +} + +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")) { + auto bufferHostObject = optionsObject.getProperty(runtime, "buffer") + .getObject(runtime) + .asHostObject(runtime); + options.bus = bufferHostObject->audioBuffer_; + } + return options; +} + +ConstantSourceOptions parseConstantSourceOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + ConstantSourceOptions options; + options.offset = static_cast(optionsObject.getProperty(runtime, "offset").getNumber()); + return options; +} + +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()); + options.maxDecibels = + static_cast(optionsObject.getProperty(runtime, "maxDecibels").getNumber()); + options.smoothingTimeConstant = + static_cast(optionsObject.getProperty(runtime, "smoothingTimeConstant").getNumber()); + return options; +} + +BiquadFilterOptions parseBiquadFilterOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + BiquadFilterOptions options(parseAudioNodeOptions(runtime, optionsObject)); + + 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 options; +} + +OscillatorOptions parseOscillatorOptions(jsi::Runtime &runtime, const jsi::Object &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 options; +} + +BaseAudioBufferSourceOptions 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").getBool()); + return options; +} + +AudioBufferSourceOptions parseAudioBufferSourceOptions( + jsi::Runtime &runtime, + const jsi::Object &optionsObject) { + AudioBufferSourceOptions options(parseBaseAudioBufferSourceOptions(runtime, optionsObject)); + 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").getBool()); + options.loopStart = + static_cast(optionsObject.getProperty(runtime, "loopStart").getNumber()); + options.loopEnd = static_cast(optionsObject.getProperty(runtime, "loopEnd").getNumber()); + return options; +} + +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 options; +} + +AudioBufferOptions 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 options; +} + +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 options; +} + +IIRFilterOptions parseIIRFilterOptions(jsi::Runtime &runtime, const jsi::Object &optionsObject) { + IIRFilterOptions options(parseAudioNodeOptions(runtime, optionsObject)); + + 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 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 47ff549b3..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -15,6 +16,15 @@ AudioNode::AudioNode(BaseAudioContext *context) : context_(context) { 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()); +} + AudioNode::~AudioNode() { if (isInitialized_) { cleanup(); 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 a56e79454..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 @@ -16,10 +16,13 @@ namespace audioapi { class AudioBus; class BaseAudioContext; class AudioParam; +class AudioNodeOptions; class AudioNode : public std::enable_shared_from_this { public: explicit AudioNode(BaseAudioContext *context); + // 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(); 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 afebaf2b5..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -69,7 +70,7 @@ double BaseAudioContext::getCurrentTime() const { return destination_->getCurrentTime(); } -std::shared_ptr BaseAudioContext::getDestination() { +std::shared_ptr BaseAudioContext::getDestination() const { return destination_; } @@ -113,21 +114,22 @@ std::shared_ptr BaseAudioContext::createRecorderAdapter() { return recorderAdapter; } -std::shared_ptr BaseAudioContext::createOscillator() { - auto oscillator = std::make_shared(this); +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() { - auto constantSource = std::make_shared(this); +std::shared_ptr BaseAudioContext::createConstantSource( + ConstantSourceOptions options) { + auto constantSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(constantSource); return constantSource; } -std::shared_ptr BaseAudioContext::createStreamer() { +std::shared_ptr BaseAudioContext::createStreamer(StreamerOptions options) { #if !RN_AUDIO_API_FFMPEG_DISABLED - auto streamer = std::make_shared(this); + auto streamer = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(streamer); return streamer; #else @@ -135,54 +137,54 @@ std::shared_ptr BaseAudioContext::createStreamer() { #endif // RN_AUDIO_API_FFMPEG_DISABLED } -std::shared_ptr BaseAudioContext::createGain() { - auto gain = std::make_shared(this); +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::createDelay(float maxDelayTime) { - auto delay = std::make_shared(this, maxDelayTime); - nodeManager_->addProcessingNode(delay); - return delay; -} - -std::shared_ptr BaseAudioContext::createStereoPanner() { - auto stereoPanner = std::make_shared(this); +std::shared_ptr BaseAudioContext::createStereoPanner( + StereoPannerOptions options) { + auto stereoPanner = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(stereoPanner); return stereoPanner; } -std::shared_ptr BaseAudioContext::createBiquadFilter() { - auto biquadFilter = std::make_shared(this); - nodeManager_->addProcessingNode(biquadFilter); - return biquadFilter; +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::createIIRFilter( - const std::vector &feedforward, - const std::vector &feedback) { - auto iirFilter = std::make_shared(this, feedforward, feedback); - nodeManager_->addProcessingNode(iirFilter); - return iirFilter; +std::shared_ptr BaseAudioContext::createBiquadFilter( + BiquadFilterOptions options) { + auto biquadFilter = std::make_shared(this, std::move(options)); + nodeManager_->addProcessingNode(biquadFilter); + return biquadFilter; } -std::shared_ptr BaseAudioContext::createBufferSource(bool pitchCorrection) { - auto bufferSource = std::make_shared(this, pitchCorrection); +std::shared_ptr BaseAudioContext::createBufferSource( + AudioBufferSourceOptions options) { + auto bufferSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(bufferSource); return bufferSource; } +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( - bool pitchCorrection) { - auto bufferSource = std::make_shared(this, pitchCorrection); + BaseAudioBufferSourceOptions options) { + auto bufferSource = std::make_shared(this, std::move(options)); nodeManager_->addSourceNode(bufferSource); 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(AudioBufferOptions options) { + return std::make_shared(std::move(options)); } std::shared_ptr BaseAudioContext::createPeriodicWave( @@ -192,16 +194,14 @@ 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(AnalyserOptions options) { + auto analyser = std::make_shared(this, std::move(options)); nodeManager_->addProcessingNode(analyser); return analyser; } -std::shared_ptr BaseAudioContext::createConvolver( - std::shared_ptr buffer, - bool disableNormalization) { - auto convolver = std::make_shared(this, buffer, disableNormalization); +std::shared_ptr 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 b17304977..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 @@ -37,6 +37,19 @@ class WorkletSourceNode; class WorkletNode; class WorkletProcessingNode; class StreamerNode; +class GainOptions; +class StereoPannerOptions; +class ConvolverOptions; +class ConstantSourceOptions; +class AnalyserOptions; +class BiquadFilterOptions; +class OscillatorOptions; +class BaseAudioBufferSourceOptions; +class AudioBufferSourceOptions; +class StreamerOptions; +class AudioBufferOptions; +class DelayOptions; +class IIRFilterOptions; class BaseAudioContext { public: @@ -49,7 +62,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( @@ -66,28 +79,24 @@ class BaseAudioContext { std::shared_ptr &shareableWorklet, std::weak_ptr runtime, bool shouldLockRuntime = true); - std::shared_ptr createOscillator(); - std::shared_ptr createConstantSource(); - std::shared_ptr createStreamer(); - std::shared_ptr createGain(); - std::shared_ptr createDelay(float maxDelayTime); - std::shared_ptr createStereoPanner(); - std::shared_ptr createBiquadFilter(); - std::shared_ptr createIIRFilter( - const std::vector &feedforward, - const std::vector &feedback); - std::shared_ptr createBufferSource(bool pitchCorrection); - std::shared_ptr createBufferQueueSource(bool pitchCorrection); - static std::shared_ptr - createBuffer(int numberOfChannels, size_t length, float sampleRate); + 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( + 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 createConvolver( - std::shared_ptr buffer, - bool disableNormalization); + 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 c01d3f5bd..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,12 +13,12 @@ #include namespace audioapi { -AnalyserNode::AnalyserNode(audioapi::BaseAudioContext *context) - : AudioNode(context), - fftSize_(2048), - minDecibels_(-100), - maxDecibels_(-30), - smoothingTimeConstant_(0.8), +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 b62d0058a..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 @@ -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, 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 33f1f3aaa..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 @@ -26,6 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -38,21 +39,21 @@ namespace audioapi { -BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context) : AudioNode(context) { - frequencyParam_ = - std::make_shared(350.0, 0.0f, context->getNyquistFrequency(), context); +BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context, BiquadFilterOptions 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..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 @@ -46,6 +46,7 @@ namespace audioapi { class AudioBus; +class BiquadFilterOptions; class BiquadFilterNode : public AudioNode { #ifdef AUDIO_API_TEST_SUITE @@ -54,7 +55,7 @@ class BiquadFilterNode : public AudioNode { #endif public: - explicit BiquadFilterNode(BaseAudioContext *context); + 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 2692beeab..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,11 +12,8 @@ #include namespace audioapi { -ConvolverNode::ConvolverNode( - BaseAudioContext *context, - const std::shared_ptr &buffer, - bool disableNormalization) - : AudioNode(context), +ConvolverNode::ConvolverNode(BaseAudioContext *context, ConvolverOptions options) + : AudioNode(context, options), remainingSegments_(0), internalBufferIndex_(0), signalledToStop_(false), @@ -23,11 +21,9 @@ ConvolverNode::ConvolverNode( intermediateBus_(nullptr), buffer_(nullptr), internalBuffer_(nullptr) { - channelCount_ = 2; - channelCountMode_ = ChannelCountMode::CLAMPED_MAX; - normalize_ = !disableNormalization; + normalize_ = !options.disableNormalization; gainCalibrationSampleRate_ = context->getSampleRate(); - setBuffer(buffer); + setBuffer(options.bus); audioBus_ = std::make_shared(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate()); 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 92a45d609..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 @@ -17,13 +17,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, 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 cf2e44c5b..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 @@ -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, DelayOptions 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..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 @@ -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, 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 6e1c01d45..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,9 +8,9 @@ namespace audioapi { -GainNode::GainNode(BaseAudioContext *context) : AudioNode(context) { +GainNode::GainNode(BaseAudioContext *context, GainOptions options) : AudioNode(context, options) { gainParam_ = std::make_shared( - 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options.gain, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); isInitialized_ = true; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/GainNode.h index de3b0d7b0..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 @@ -8,10 +8,11 @@ namespace audioapi { class AudioBus; +class GainOptions; class GainNode : public AudioNode { public: - explicit GainNode(BaseAudioContext *context); + 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 b9d22aea3..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 @@ -23,6 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -30,17 +31,16 @@ #include #include #include +#include #include namespace audioapi { -IIRFilterNode::IIRFilterNode( - BaseAudioContext *context, - const std::vector &feedforward, - const std::vector &feedback) - : AudioNode(context), feedforward_(feedforward), feedback_(feedback) { +IIRFilterNode::IIRFilterNode(BaseAudioContext *context, IIRFilterOptions options) + : AudioNode(context, options), + feedforward_(std::move(options.feedforward)), + feedback_(std::move(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..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 @@ -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, 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 5a362a180..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,9 +10,9 @@ 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, 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 320f7dab4..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 @@ -10,10 +10,11 @@ namespace audioapi { class AudioBus; +class StereoPannerOptions; class StereoPannerNode : public AudioNode { public: - explicit StereoPannerNode(BaseAudioContext *context); + 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 17c159068..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,8 +9,8 @@ namespace audioapi { -AudioBuffer::AudioBuffer(int numberOfChannels, size_t length, float sampleRate) { - bus_ = std::make_shared(length, numberOfChannels, 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 2d3a860c9..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 @@ -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(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 709b14f6c..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 @@ -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) { + BaseAudioBufferSourceOptions 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..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 @@ -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, + 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 fb8298968..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -19,19 +20,20 @@ namespace audioapi { AudioBufferQueueSourceNode::AudioBufferQueueSourceNode( BaseAudioContext *context, - bool pitchCorrection) - : AudioBufferBaseSourceNode(context, pitchCorrection) { + BaseAudioBufferSourceOptions 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; int extraTailFrames = static_cast(stretch_->inputLatency() + stretch_->outputLatency()); - tailBuffer_ = - std::make_shared(channelCount_, extraTailFrames, context_->getSampleRate()); + auto audioBufferOptions = AudioBufferOptions( + extraTailFrames, static_cast(channelCount_), context->getSampleRate()); + tailBuffer_ = std::make_shared(audioBufferOptions); tailBuffer_->bus_->zero(); } 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 23e81ddd1..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 @@ -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, + 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 86cbaace7..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 @@ -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, + AudioBufferSourceOptions 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..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 @@ -13,10 +13,11 @@ namespace audioapi { class AudioBus; class AudioParam; +class AudioBufferSourceOptions; class AudioBufferSourceNode : public AudioBufferBaseSourceNode { public: - explicit AudioBufferSourceNode(BaseAudioContext *context, bool pitchCorrection); + 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 67676f192..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,10 +7,10 @@ #include namespace audioapi { -ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context) +ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context, ConstantSourceOptions options) : AudioScheduledSourceNode(context) { offsetParam_ = std::make_shared( - 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); + options.offset, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context); isInitialized_ = true; } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/ConstantSourceNode.h index eabcfb5da..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 @@ -10,10 +10,11 @@ namespace audioapi { class AudioBus; +class ConstantSourceOptions; class ConstantSourceNode : public AudioScheduledSourceNode { public: - explicit ConstantSourceNode(BaseAudioContext *context); + 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 fbf7d29fd..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,16 +9,24 @@ namespace audioapi { -OscillatorNode::OscillatorNode(BaseAudioContext *context) : AudioScheduledSourceNode(context) { +OscillatorNode::OscillatorNode(BaseAudioContext *context, OscillatorOptions 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..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 @@ -13,10 +13,11 @@ namespace audioapi { class AudioBus; +class OscillatorOptions; class OscillatorNode : public AudioScheduledSourceNode { public: - explicit OscillatorNode(BaseAudioContext *context); + 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 103836ef0..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 @@ -8,6 +8,7 @@ * FFmpeg, you must comply with the terms of the LGPL for FFmpeg itself. */ +#include #include #include #include @@ -21,8 +22,7 @@ #include namespace audioapi { -#if !RN_AUDIO_API_FFMPEG_DISABLED -StreamerNode::StreamerNode(BaseAudioContext *context) +StreamerNode::StreamerNode(BaseAudioContext *context, StreamerOptions options) : AudioScheduledSourceNode(context), fmtCtx_(nullptr), codecCtx_(nullptr), @@ -35,10 +35,8 @@ StreamerNode::StreamerNode(BaseAudioContext *context) bufferedBus_(nullptr), audio_stream_index_(-1), maxResampledSamples_(0), - processedSamples_(0) {} -#else -StreamerNode::StreamerNode(BaseAudioContext *context) : AudioScheduledSourceNode(context) {} -#endif // RN_AUDIO_API_FFMPEG_DISABLED + processedSamples_(0), + streamPath_(options.streamPath) {} StreamerNode::~StreamerNode() { #if !RN_AUDIO_API_FFMPEG_DISABLED @@ -48,6 +46,7 @@ StreamerNode::~StreamerNode() { bool StreamerNode::initialize(const std::string &input_url) { #if !RN_AUDIO_API_FFMPEG_DISABLED + 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 87d8697b2..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 @@ -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, StreamerOptions options); ~StreamerNode() override; /** @@ -72,6 +73,10 @@ class StreamerNode : public AudioScheduledSourceNode { */ bool initialize(const std::string &inputUrl); + std::string getStreamPath() const { + return streamPath_; + } + protected: std::shared_ptr processNode( const std::shared_ptr &processingBus, @@ -105,6 +110,7 @@ class StreamerNode : public AudioScheduledSourceNode { STREAMER_NODE_SPSC_OVERFLOW_STRATEGY, STREAMER_NODE_SPSC_WAIT_STRATEGY> receiver_; + std::string streamPath_; /** * @brief Setting up the resampler diff --git a/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/ConstantSourceTest.cpp index 26bc10788..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class ConstantSourceTest : public ::testing::Test { class TestableConstantSourceNode : public ConstantSourceNode { public: - explicit TestableConstantSourceNode(BaseAudioContext *context) : ConstantSourceNode(context) {} + explicit TestableConstantSourceNode(BaseAudioContext *context) + : ConstantSourceNode(context, ConstantSourceOptions()) {} void setOffsetParam(float value) { getOffsetParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableConstantSourceNode : public ConstantSourceNode { }; TEST_F(ConstantSourceTest, ConstantSourceCanBeCreated) { - auto constantSource = context->createConstantSource(); + auto constantSource = context->createConstantSource(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 8f247607f..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,7 @@ class DelayTest : public ::testing::Test { class TestableDelayNode : public DelayNode { public: - explicit TestableDelayNode(BaseAudioContext *context) : DelayNode(context, 1) {} + explicit TestableDelayNode(BaseAudioContext *context) : DelayNode(context, DelayOptions()) {} void setDelayTimeParam(float value) { getDelayTimeParam()->setValue(value); @@ -38,7 +39,7 @@ class TestableDelayNode : public DelayNode { }; TEST_F(DelayTest, DelayCanBeCreated) { - auto delay = context->createDelay(1.0f); + auto delay = context->createDelay(DelayOptions()); ASSERT_NE(delay, nullptr); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/GainTest.cpp index 9ab434638..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,7 @@ class GainTest : public ::testing::Test { class TestableGainNode : public GainNode { public: - explicit TestableGainNode(BaseAudioContext *context) : GainNode(context) {} + explicit TestableGainNode(BaseAudioContext *context) : GainNode(context, GainOptions()) {} void setGainParam(float value) { getGainParam()->setValue(value); @@ -38,7 +39,7 @@ class TestableGainNode : public GainNode { }; TEST_F(GainTest, GainCanBeCreated) { - auto gain = context->createGain(); + auto gain = context->createGain(GainOptions()); ASSERT_NE(gain, 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..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 @@ -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(IIRFilterOptions(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(), 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 a8d732665..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 @@ -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(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 c6ff93d31..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,7 +25,8 @@ class StereoPannerTest : public ::testing::Test { class TestableStereoPannerNode : public StereoPannerNode { public: - explicit TestableStereoPannerNode(BaseAudioContext *context) : StereoPannerNode(context) {} + explicit TestableStereoPannerNode(BaseAudioContext *context) + : StereoPannerNode(context, StereoPannerOptions()) {} void setPanParam(float value) { getPanParam()->setValue(value); @@ -38,7 +40,7 @@ class TestableStereoPannerNode : public StereoPannerNode { }; TEST_F(StereoPannerTest, StereoPannerCanBeCreated) { - auto panner = context->createStereoPanner(); + auto panner = context->createStereoPanner(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 f68677075..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 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -16,7 +17,7 @@ void BiquadFilterTest::expectCoefficientsNear( } void BiquadFilterTest::testLowpass(float frequency, float Q) { - auto node = std::make_shared(context.get()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowpassCoefficients(normalizedFrequency, Q); @@ -24,7 +25,7 @@ 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(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighpassCoefficients(normalizedFrequency, Q); @@ -32,7 +33,7 @@ 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(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setBandpassCoefficients(normalizedFrequency, Q); @@ -40,7 +41,7 @@ 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(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setNotchCoefficients(normalizedFrequency, Q); @@ -48,7 +49,7 @@ 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(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setAllpassCoefficients(normalizedFrequency, Q); @@ -56,7 +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()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setPeakingCoefficients(normalizedFrequency, Q, gain); @@ -64,7 +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()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setLowshelfCoefficients(normalizedFrequency, gain); @@ -72,7 +73,7 @@ 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(), BiquadFilterOptions()); float normalizedFrequency = frequency / nyquistFrequency; node->setHighshelfCoefficients(normalizedFrequency, gain); @@ -218,7 +219,7 @@ TEST_P(BiquadFilterGainTest, SetHighshelfCoefficients) { } TEST_F(BiquadFilterTest, GetFrequencyResponse) { - auto node = std::make_shared(context.get()); + auto node = std::make_shared(context.get(), BiquadFilterOptions()); float frequency = 1000.0f; float Q = 1.0f; diff --git a/packages/react-native-audio-api/src/api.ts b/packages/react-native-audio-api/src/api.ts index 6c9441d90..3ff6c4e94 100644 --- a/packages/react-native-audio-api/src/api.ts +++ b/packages/react-native-audio-api/src/api.ts @@ -26,19 +26,19 @@ export { default as WorkletNode } from './core/WorkletNode'; export { default as WorkletProcessingNode } from './core/WorkletProcessingNode'; export { default as WorkletSourceNode } from './core/WorkletSourceNode'; export { default as useSystemVolume } from './hooks/useSystemVolume'; -export { default as AudioManager } from './system'; +export { default as PeriodicWave } from './core/PeriodicWave'; export { - AudioWorkletRuntime, - BiquadFilterType, + OscillatorType, ChannelCountMode, ChannelInterpretation, ContextState, - OscillatorType, - PeriodicWaveConstraints, WindowType, + AudioWorkletRuntime, } from './types'; +export { default as AudioManager } from './system'; + export { IOSCategory, IOSMode, diff --git a/packages/react-native-audio-api/src/api.web.ts b/packages/react-native-audio-api/src/api.web.ts index 292f86993..09259d993 100644 --- a/packages/react-native-audio-api/src/api.web.ts +++ b/packages/react-native-audio-api/src/api.web.ts @@ -14,17 +14,16 @@ export { default as OscillatorNode } from './web-core/OscillatorNode'; export { default as StereoPannerNode } from './web-core/StereoPannerNode'; export { default as ConstantSourceNode } from './web-core/ConstantSourceNode'; export { default as ConvolverNode } from './web-core/ConvolverNode'; +export { default as PeriodicWave } from './web-core/PeriodicWave'; export * from './web-core/custom'; export { OscillatorType, - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, WindowType, - PeriodicWaveConstraints, } from './types'; export { diff --git a/packages/react-native-audio-api/src/core/AnalyserNode.ts b/packages/react-native-audio-api/src/core/AnalyserNode.ts index d2d8176e1..b6faaf6ef 100644 --- a/packages/react-native-audio-api/src/core/AnalyserNode.ts +++ b/packages/react-native-audio-api/src/core/AnalyserNode.ts @@ -1,13 +1,28 @@ +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'; +import { AnalyserOptionsValidator } from '../options-validators'; export default class AnalyserNode extends AudioNode { private static allowedFFTSize: number[] = [ 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, ]; + constructor(context: BaseAudioContext, options?: TAnalyserOptions) { + const finalOptions: TAnalyserOptions = { + ...AnalyserOptions, + ...options, + }; + + AnalyserOptionsValidator.validate(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/AudioBuffer.ts b/packages/react-native-audio-api/src/core/AudioBuffer.ts index 671e48d9d..eeeb4d95c 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,29 @@ 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 + ) { + let buf: IAudioBuffer; + if (contextOrBuffer instanceof BaseAudioContext) { + const finalOptions = { + ...AudioBufferOptions, + ...options, + }; + const context = contextOrBuffer; + buf = context.context.createBuffer(finalOptions); + } else { + 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/core/AudioBufferQueueSourceNode.ts b/packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts index 33665be3c..8a03cddcf 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 ed60cf395..28e7c4afc 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -1,18 +1,10 @@ -import AudioAPIModule from '../AudioAPIModule'; import { InvalidAccessError, InvalidStateError, NotSupportedError, } from '../errors'; import { IBaseAudioContext } from '../interfaces'; -import { - AudioBufferBaseSourceNodeOptions, - AudioWorkletRuntime, - ContextState, - ConvolverNodeOptions, - IIRFilterNodeOptions, - PeriodicWaveConstraints, -} from '../types'; +import { AudioWorkletRuntime, ContextState } from '../types'; import { assertWorkletsEnabled } from '../utils'; import AnalyserNode from './AnalyserNode'; import AudioBuffer from './AudioBuffer'; @@ -97,26 +89,12 @@ export default class BaseAudioContext { } assertWorkletsEnabled(); - - const shareableWorklet = - AudioAPIModule.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 ); } @@ -130,33 +108,7 @@ export default class BaseAudioContext { workletRuntime: AudioWorkletRuntime = 'AudioRuntime' ): WorkletProcessingNode { assertWorkletsEnabled(); - - const shareableWorklet = - AudioAPIModule.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( @@ -169,71 +121,54 @@ export default class BaseAudioContext { workletRuntime: AudioWorkletRuntime = 'AudioRuntime' ): WorkletSourceNode { assertWorkletsEnabled(); - const shareableWorklet = - AudioAPIModule.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 { - return new RecorderAdapterNode(this, this.context.createRecorderAdapter()); + return new RecorderAdapterNode(this); } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createStreamer(): StreamerNode { - const streamer = this.context.createStreamer(); - if (!streamer) { - throw new NotSupportedError('StreamerNode requires FFmpeg build'); - } - return new StreamerNode(this, streamer); + return new StreamerNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createDelay(maxDelayTime?: number): DelayNode { - const maxTime = maxDelayTime ?? 1.0; - return new DelayNode(this, this.context.createDelay(maxTime)); + if (maxDelayTime !== undefined) { + return new DelayNode(this, { maxDelayTime }); + } else { + return new DelayNode(this); + } } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); + } + + createBufferSource(pitchCorrection?: boolean): AudioBufferSourceNode { + if (pitchCorrection !== undefined) { + return new AudioBufferSourceNode(this, { pitchCorrection }); + } else { + 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]` @@ -257,42 +192,27 @@ export default class BaseAudioContext { ); } - return new IIRFilterNode( - this, - this.context.createIIRFilter(feedforward, feedback) - ); - } - - createBufferSource( - options?: AudioBufferBaseSourceNodeOptions - ): AudioBufferSourceNode { - const pitchCorrection = options?.pitchCorrection ?? false; - - return new AudioBufferSourceNode( - this, - this.context.createBufferSource(pitchCorrection) - ); + return new IIRFilterNode(this, { feedforward, feedback }); } createBufferQueueSource( - options?: AudioBufferBaseSourceNodeOptions + pitchCorrection?: boolean ): AudioBufferQueueSourceNode { - const pitchCorrection = options?.pitchCorrection ?? false; - - return new AudioBufferQueueSourceNode( - this, - this.context.createBufferQueueSource(pitchCorrection) - ); + if (pitchCorrection !== undefined) { + return new AudioBufferQueueSourceNode(this, { pitchCorrection }); + } else { + return new AudioBufferQueueSourceNode(this); + } } 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]` ); } @@ -308,9 +228,7 @@ export default class BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( @@ -323,36 +241,14 @@ export default class BaseAudioContext { `The lengths of the real (${real.length}) and imaginary (${imag.length}) arrays must match.` ); } - - const disableNormalization = constraints?.disableNormalization ?? false; - - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, disableNormalization) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(buffer?.buffer, disableNormalization) - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } } diff --git a/packages/react-native-audio-api/src/core/BiquadFilterNode.ts b/packages/react-native-audio-api/src/core/BiquadFilterNode.ts index 68db7c02a..b85475fc5 100644 --- a/packages/react-native-audio-api/src/core/BiquadFilterNode.ts +++ b/packages/react-native-audio-api/src/core/BiquadFilterNode.ts @@ -3,7 +3,8 @@ import { IBiquadFilterNode } from '../interfaces'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; -import { BiquadFilterType } from '../types'; +import { BiquadFilterOptions } from '../defaults'; +import { TBiquadFilterOptions } from '../types'; export default class BiquadFilterNode extends AudioNode { readonly frequency: AudioParam; @@ -11,7 +12,13 @@ export default class BiquadFilterNode extends AudioNode { readonly Q: AudioParam; readonly gain: AudioParam; - constructor(context: BaseAudioContext, biquadFilter: IBiquadFilterNode) { + constructor(context: BaseAudioContext, options?: TBiquadFilterOptions) { + const finalOptions: TBiquadFilterOptions = { + ...BiquadFilterOptions, + ...options, + }; + const biquadFilter: IBiquadFilterNode = + context.context.createBiquadFilter(finalOptions); super(context, biquadFilter); this.frequency = new AudioParam(biquadFilter.frequency, context); this.detune = new AudioParam(biquadFilter.detune, context); diff --git a/packages/react-native-audio-api/src/core/ConstantSourceNode.ts b/packages/react-native-audio-api/src/core/ConstantSourceNode.ts index 1a36c4b9f..65b26f517 100644 --- a/packages/react-native-audio-api/src/core/ConstantSourceNode.ts +++ b/packages/react-native-audio-api/src/core/ConstantSourceNode.ts @@ -1,4 +1,6 @@ import { IConstantSourceNode } from '../interfaces'; +import { ConstantSourceOptions } from '../defaults'; +import { TConstantSourceOptions } from '../types'; import AudioParam from './AudioParam'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; @@ -6,7 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class ConstantSourceNode extends AudioScheduledSourceNode { readonly offset: AudioParam; - constructor(context: BaseAudioContext, node: IConstantSourceNode) { + constructor(context: BaseAudioContext, options?: TConstantSourceOptions) { + const finalOptions: TConstantSourceOptions = { + ...ConstantSourceOptions, + ...options, + }; + const node: IConstantSourceNode = + context.context.createConstantSource(finalOptions); super(context, node); this.offset = new AudioParam(node.offset, context); } diff --git a/packages/react-native-audio-api/src/core/ConvolverNode.ts b/packages/react-native-audio-api/src/core/ConvolverNode.ts index 975f98fa6..3c283fa04 100644 --- a/packages/react-native-audio-api/src/core/ConvolverNode.ts +++ b/packages/react-native-audio-api/src/core/ConvolverNode.ts @@ -1,12 +1,20 @@ import { IConvolverNode } from '../interfaces'; +import { ConvolverOptions } from '../defaults'; +import { TConvolverOptions } from '../types'; import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; export default class ConvolverNode extends AudioNode { - constructor(context: BaseAudioContext, node: IConvolverNode) { - super(context, node); - this.normalize = node.normalize; + constructor(context: BaseAudioContext, options?: TConvolverOptions) { + const finalOptions: TConvolverOptions = { + ...ConvolverOptions, + ...options, + }; + const convolverNode: IConvolverNode = + context.context.createConvolver(finalOptions); + super(context, convolverNode); + this.normalize = convolverNode.normalize; } public get buffer(): AudioBuffer | null { diff --git a/packages/react-native-audio-api/src/core/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/core/GainNode.ts b/packages/react-native-audio-api/src/core/GainNode.ts index a216f1365..1c2b8f93b 100644 --- a/packages/react-native-audio-api/src/core/GainNode.ts +++ b/packages/react-native-audio-api/src/core/GainNode.ts @@ -1,4 +1,6 @@ import { IGainNode } from '../interfaces'; +import { GainOptions } from '../defaults'; +import { TGainOptions } from '../types'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; @@ -6,8 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class GainNode extends AudioNode { readonly gain: AudioParam; - constructor(context: BaseAudioContext, gain: IGainNode) { - super(context, gain); - this.gain = new AudioParam(gain.gain, context); + constructor(context: BaseAudioContext, options?: TGainOptions) { + const finalOptions: TGainOptions = { + ...GainOptions, + ...options, + }; + const gainNode: IGainNode = context.context.createGain(finalOptions); + super(context, gainNode); + this.gain = new AudioParam(gainNode.gain, context); } } diff --git a/packages/react-native-audio-api/src/core/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/core/OscillatorNode.ts b/packages/react-native-audio-api/src/core/OscillatorNode.ts index 48296831c..b60aff874 100644 --- a/packages/react-native-audio-api/src/core/OscillatorNode.ts +++ b/packages/react-native-audio-api/src/core/OscillatorNode.ts @@ -1,17 +1,28 @@ 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.periodicWave) { + finalOptions.type = 'custom'; + } + + const node = context.context.createOscillator(finalOptions); super(context, node); this.frequency = new AudioParam(node.frequency, context); this.detune = new AudioParam(node.detune, context); diff --git a/packages/react-native-audio-api/src/core/PeriodicWave.ts b/packages/react-native-audio-api/src/core/PeriodicWave.ts index b00c4505c..7e9e7fa40 100644 --- a/packages/react-native-audio-api/src/core/PeriodicWave.ts +++ b/packages/react-native-audio-api/src/core/PeriodicWave.ts @@ -1,10 +1,44 @@ import { IPeriodicWave } from '../interfaces'; +import BaseAudioContext from './BaseAudioContext'; +import { TPeriodicWaveOptions } from '../types'; +import { PeriodicWaveConstraints } from '../defaults'; +import { PeriodicWaveOptionsValidator } from '../options-validators'; + +export function generateRealAndImag( + options?: TPeriodicWaveOptions +): TPeriodicWaveOptions { + let real: Float32Array | undefined; + let imag: Float32Array | undefined; + if (!options || (!options.real && !options.imag)) { + real = new Float32Array(2); + imag = new Float32Array(2); + imag[1] = 1; + } else { + real = options.real; + imag = options.imag; + PeriodicWaveOptionsValidator.validate(options); + 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 = generateRealAndImag(options); + this.periodicWave = context.context.createPeriodicWave( + finalOptions.real!, + finalOptions.imag!, + finalOptions.disableNormalization! + ); } } diff --git a/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts b/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts index 75c194944..ecad274bc 100644 --- a/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts +++ b/packages/react-native-audio-api/src/core/RecorderAdapterNode.ts @@ -1,10 +1,15 @@ import { IRecorderAdapterNode } from '../interfaces'; import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; export default class RecorderAdapterNode extends AudioNode { /** @internal */ public wasConnected: boolean = false; + constructor(context: BaseAudioContext) { + super(context, context.context.createRecorderAdapter()); + } + /** @internal */ public getNode(): IRecorderAdapterNode { return this.node as IRecorderAdapterNode; diff --git a/packages/react-native-audio-api/src/core/StereoPannerNode.ts b/packages/react-native-audio-api/src/core/StereoPannerNode.ts index a8fa77006..040e9f840 100644 --- a/packages/react-native-audio-api/src/core/StereoPannerNode.ts +++ b/packages/react-native-audio-api/src/core/StereoPannerNode.ts @@ -1,4 +1,6 @@ import { IStereoPannerNode } from '../interfaces'; +import { SteroPannerOptions } from '../defaults'; +import { TSteroPannerOptions } from '../types'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; import BaseAudioContext from './BaseAudioContext'; @@ -6,7 +8,13 @@ import BaseAudioContext from './BaseAudioContext'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor(context: BaseAudioContext, pan: IStereoPannerNode) { + constructor(context: BaseAudioContext, options?: TSteroPannerOptions) { + const finalOptions: TSteroPannerOptions = { + ...SteroPannerOptions, + ...options, + }; + const pan: IStereoPannerNode = + context.context.createStereoPanner(finalOptions); super(context, pan); this.pan = new AudioParam(pan.pan, context); } diff --git a/packages/react-native-audio-api/src/core/StreamerNode.ts b/packages/react-native-audio-api/src/core/StreamerNode.ts index fcbb61d20..a15cdbeb6 100644 --- a/packages/react-native-audio-api/src/core/StreamerNode.ts +++ b/packages/react-native-audio-api/src/core/StreamerNode.ts @@ -1,8 +1,36 @@ import { IStreamerNode } from '../interfaces'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; +import { TStreamerOptions } from '../types'; +import { InvalidStateError, NotSupportedError } 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); + if (!node) { + throw new NotSupportedError('StreamerNode requires FFmpeg build'); + } + 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/core/WorkletNode.ts b/packages/react-native-audio-api/src/core/WorkletNode.ts index c017228ba..c162de81a 100644 --- a/packages/react-native-audio-api/src/core/WorkletNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletNode.ts @@ -1,3 +1,32 @@ import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; +import { AudioWorkletRuntime } from '../types'; +import AudioAPIModule from '../AudioAPIModule'; -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 = + AudioAPIModule.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..8c28932e4 100644 --- a/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts @@ -1,3 +1,41 @@ import AudioNode from './AudioNode'; +import BaseAudioContext from './BaseAudioContext'; +import { AudioWorkletRuntime } from '../types'; +import AudioAPIModule from '../AudioAPIModule'; -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 = + AudioAPIModule.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..e04037851 100644 --- a/packages/react-native-audio-api/src/core/WorkletSourceNode.ts +++ b/packages/react-native-audio-api/src/core/WorkletSourceNode.ts @@ -1,3 +1,38 @@ import AudioScheduledSourceNode from './AudioScheduledSourceNode'; +import BaseAudioContext from './BaseAudioContext'; +import { AudioWorkletRuntime } from '../types'; +import AudioAPIModule from '../AudioAPIModule'; -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 = + AudioAPIModule.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); + } +} 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..cc6f0eee2 --- /dev/null +++ b/packages/react-native-audio-api/src/defaults.ts @@ -0,0 +1,94 @@ +import { + TAudioNodeOptions, + TGainOptions, + TSteroPannerOptions, + TConvolverOptions, + TConstantSourceOptions, + TPeriodicWaveConstraints, + TAnalyserOptions, + TBiquadFilterOptions, + TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, + TAudioBufferOptions, + TDelayOptions, +} from './types'; + +export const AudioNodeOptions: TAudioNodeOptions = { + channelCount: 2, + channelCountMode: 'max', + channelInterpretation: 'speakers', +}; + +export const GainOptions: TGainOptions = { + ...AudioNodeOptions, + gain: 1, +}; + +export const SteroPannerOptions: TSteroPannerOptions = { + ...AudioNodeOptions, + channelCountMode: 'clamped-max', + pan: 0, +}; + +export const AnalyserOptions: TAnalyserOptions = { + ...AudioNodeOptions, + fftSize: 2048, + minDecibels: -100, + maxDecibels: -30, + smoothingTimeConstant: 0.8, +}; + +export const BiquadFilterOptions: TBiquadFilterOptions = { + ...AudioNodeOptions, + Q: 1, + detune: 0, + frequency: 350, + gain: 0, + type: 'lowpass', +}; + +export const ConvolverOptions: TConvolverOptions = { + ...AudioNodeOptions, + disableNormalization: false, +}; + +export const ConstantSourceOptions: TConstantSourceOptions = { + offset: 1, +}; + +export const PeriodicWaveConstraints: TPeriodicWaveConstraints = { + disableNormalization: false, +}; + +export const OscillatorOptions: TOscillatorOptions = { + ...AudioNodeOptions, + type: 'sine', + frequency: 440, + detune: 0, +}; + +export const BaseAudioBufferSourceOptions: TBaseAudioBufferSourceOptions = { + playbackRate: 1, + detune: 0, + pitchCorrection: false, +}; + +export const AudioBufferSourceOptions: TAudioBufferSourceOptions = { + ...BaseAudioBufferSourceOptions, + loop: false, + loopStart: 0, + loopEnd: 0, +}; + +export const AudioBufferOptions: TAudioBufferOptions = { + numberOfChannels: 1, + 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 d06e5173e..2f48416ee 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -1,11 +1,23 @@ import { AudioEventCallback, AudioEventName } from './events/types'; import { - BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, OscillatorType, WindowType, + TGainOptions, + TSteroPannerOptions, + TConvolverOptions, + TConstantSourceOptions, + TAnalyserOptions, + TBiquadFilterOptions, + TOscillatorOptions, + TBaseAudioBufferSourceOptions, + TAudioBufferSourceOptions, + TStreamerOptions, + TAudioBufferOptions, + TDelayOptions, + TIIRFilterOptions, } from './types'; // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer @@ -57,36 +69,34 @@ export interface IBaseAudioContext { shareableWorklet: ShareableWorkletCallback, shouldUseUiRuntime: boolean ): IWorkletProcessingNode; - createOscillator(): IOscillatorNode; - createConstantSource(): IConstantSourceNode; - createGain(): IGainNode; - createDelay(maxDelayTime: number): IDelayNode; - createStereoPanner(): IStereoPannerNode; - createBiquadFilter: () => IBiquadFilterNode; - createIIRFilter: ( - feedforward: number[], - feedback: number[] - ) => IIIRFilterNode; - createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode; + createOscillator(oscillatorOptions: TOscillatorOptions): IOscillatorNode; + createConstantSource( + constantSourceOptions: TConstantSourceOptions + ): IConstantSourceNode; + createGain(gainOptions: TGainOptions): IGainNode; + createStereoPanner( + stereoPannerOptions: TSteroPannerOptions + ): IStereoPannerNode; + createBiquadFilter: ( + biquadFilterOptions: TBiquadFilterOptions + ) => IBiquadFilterNode; + createBufferSource: ( + audioBufferSourceOptions: TAudioBufferSourceOptions + ) => IAudioBufferSourceNode; + createDelay(delayOptions: TDelayOptions): IDelayNode; + createIIRFilter: (IIRFilterOptions: TIIRFilterOptions) => IIIRFilterNode; createBufferQueueSource: ( - pitchCorrection: boolean + audioBufferQueueSourceOptions: TBaseAudioBufferSourceOptions ) => IAudioBufferQueueSourceNode; - createBuffer: ( - channels: number, - length: number, - sampleRate: number - ) => IAudioBuffer; + createBuffer: (audioBufferOptions: TAudioBufferOptions) => IAudioBuffer; createPeriodicWave: ( real: Float32Array, imag: Float32Array, disableNormalization: boolean ) => IPeriodicWave; - createAnalyser: () => IAnalyserNode; - createConvolver: ( - buffer: IAudioBuffer | undefined, - disableNormalization: boolean - ) => IConvolverNode; - createStreamer: () => IStreamerNode | null; // null when FFmpeg is not enabled + createAnalyser: (analyserOptions: TAnalyserOptions) => IAnalyserNode; + createConvolver: (convolverOptions: TConvolverOptions) => IConvolverNode; + createStreamer: (streamerOptions?: TStreamerOptions) => IStreamerNode | null; } export interface IAudioContext extends IBaseAudioContext { @@ -180,6 +190,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/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 740a7ad14..aa371284f 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -1,10 +1,11 @@ import AudioBuffer from './core/AudioBuffer'; +import PeriodicWave from './core/PeriodicWave'; export type ChannelCountMode = 'max' | 'clamped-max' | 'explicit'; export type ChannelInterpretation = 'speakers' | 'discrete'; -export type BiquadFilterType = +type BiquadFilterType = | 'lowpass' | 'highpass' | 'bandpass' @@ -25,10 +26,6 @@ export type OscillatorType = | 'triangle' | 'custom'; -export interface PeriodicWaveConstraints { - disableNormalization: boolean; -} - export interface AudioContextOptions { sampleRate?: number; } @@ -46,18 +43,101 @@ export interface AudioRecorderOptions { export type WindowType = 'blackman' | 'hann'; -export interface AudioBufferBaseSourceNodeOptions { - pitchCorrection: boolean; +export type ProcessorMode = 'processInPlace' | 'processThrough'; + +export interface TAudioNodeOptions { + channelCount?: number; + channelCountMode?: ChannelCountMode; + channelInterpretation?: ChannelInterpretation; } -export type ProcessorMode = 'processInPlace' | 'processThrough'; +export interface TGainOptions extends TAudioNodeOptions { + gain?: number; +} + +export interface TSteroPannerOptions extends TAudioNodeOptions { + pan?: number; +} + +export interface TAnalyserOptions extends TAudioNodeOptions { + fftSize?: number; + minDecibels?: number; + maxDecibels?: number; + smoothingTimeConstant?: number; +} -export interface ConvolverNodeOptions { - buffer?: AudioBuffer | null; +export interface OptionsValidator { + validate(options: T): void; +} + +export interface TBiquadFilterOptions extends TAudioNodeOptions { + type?: BiquadFilterType; + frequency?: number; + detune?: number; + Q?: number; + gain?: number; +} + +export interface TOscillatorOptions { + type?: OscillatorType; + frequency?: number; + detune?: number; + periodicWave?: PeriodicWave; +} + +export interface TBaseAudioBufferSourceOptions { + detune?: number; + playbackRate?: number; + pitchCorrection?: boolean; +} + +export interface TAudioBufferSourceOptions + extends TBaseAudioBufferSourceOptions { + buffer?: AudioBuffer; + loop?: boolean; + loopStart?: number; + loopEnd?: number; +} + +export interface TConvolverOptions extends TAudioNodeOptions { + buffer?: AudioBuffer; + disableNormalization?: boolean; +} + +export interface TWebConvolverOptions { + buffer?: globalThis.AudioBuffer | null; + normalize?: boolean; +} + +export interface TConstantSourceOptions { + offset?: number; +} + +export interface TStreamerOptions { + streamPath?: string; +} + +export interface TPeriodicWaveConstraints { disableNormalization?: boolean; } -export interface IIRFilterNodeOptions { +export interface TPeriodicWaveOptions extends TPeriodicWaveConstraints { + real?: Float32Array; + imag?: Float32Array; +} + +export interface TAudioBufferOptions { + numberOfChannels?: number; + length: number; + sampleRate: number; +} + +export interface TIIRFilterOptions extends TAudioNodeOptions { feedforward: number[]; feedback: number[]; } + +export interface TDelayOptions extends TAudioNodeOptions { + maxDelayTime?: number; + delayTime?: number; +} 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/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/AudioBufferSourceNode.tsx b/packages/react-native-audio-api/src/web-core/AudioBufferSourceNode.tsx index 31ff30221..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,10 +3,12 @@ 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 { 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,28 @@ 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 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; @@ -196,98 +214,218 @@ 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) { + const promise = async () => { + await globalWasmPromise; + return window[globalTag](new window.AudioContext()); + }; + this.context = context; + this.stretcherPromise = promise(); + this.stretcherPromise.then((node) => { + this.node = node; + }); - this.playbackRate = new AudioParam( - new IStretcherNodeAudioParam( - 1, - this.setPlaybackRate.bind(this), - 'a-rate', - 0, - Infinity, - 1 - ), - context - ); + 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 + ); + } + + 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 { - this.detune = new AudioParam((node as DefaultSource).detune, context); - this.playbackRate = new AudioParam( - (node as DefaultSource).playbackRate, - context - ); + action(this.node); } - } - private isStretcherNode() { - return this._pitchCorrection; + return destination; } - private asStretcher(): IStretcherNode { - return this.node as IStretcherNode; + 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); + } } - private asBufferSource(): DefaultSource { - return this.node as DefaultSource; + start(when?: number, offset?: number, duration?: number): void { + if (when && when < 0) { + throw new RangeError( + `when must be a finite non-negative number: ${when}` + ); + } + + if (offset && offset < 0) { + throw new RangeError( + `offset must be a finite non-negative number: ${offset}` + ); + } + + if (duration && duration < 0) { + throw new RangeError( + `duration must be a finite non-negative number: ${duration}` + ); + } + + if (this.hasBeenStarted) { + throw new InvalidStateError('Cannot call start more than once'); + } + + 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); + } + } + + 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, + }); + }; + + if (!this.node) { + this.stretcherPromise!.then((node) => { + action(node); + }); return; } - this.asStretcher().schedule({ - rate: value, - output: when, - }); + 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); + } + + get buffer(): AudioBuffer | null { + return this._buffer; } - public set buffer(buffer: AudioBuffer | null) { - if (this.isStretcherNode()) { - this._buffer = buffer; + set buffer(buffer: AudioBuffer | null) { + this._buffer = buffer; - const stretcher = this.asStretcher(); - stretcher.dropBuffers(); + const action = (node: IStretcherNode) => { + node.dropBuffers(); if (!buffer) { return; @@ -299,70 +437,56 @@ 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 implements IAudioAPIBufferSourceNodeWeb { + private node: DefaultSource; + private hasBeenStarted: boolean = false; + readonly playbackRate: AudioParam; + readonly detune: AudioParam; - this.asBufferSource().loopEnd = value; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { + 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 +510,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 +525,157 @@ 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'); + } + + // 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.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; + } + + 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 (!this.isStretcherNode()) { - this.asBufferSource().stop(when); + if (destination instanceof AudioParam) { + this.node.disconnect(destination.param); return; } + this.node.disconnect(destination.node); + } +} + +export default class AudioBufferSourceNode + implements IAudioAPIBufferSourceNodeWeb +{ + private node: AudioBufferSourceNodeStretcher | AudioBufferSourceNodeWeb; + constructor(context: BaseAudioContext, options?: TAudioBufferSourceOptions) { + const finalOptions: TAudioBufferSourceOptions = { + ...AudioBufferSourceOptions, + ...options, + }; + 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.node 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; + } - this.asStretcher().stop(when); + 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 f2770ef11..3aa8f2aed 100644 --- a/packages/react-native-audio-api/src/web-core/AudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/AudioContext.tsx @@ -1,10 +1,4 @@ -import { - ContextState, - PeriodicWaveConstraints, - AudioContextOptions, - AudioBufferBaseSourceNodeOptions, - IIRFilterNodeOptions, -} from '../types'; +import { ContextState, AudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -19,9 +13,6 @@ import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import ConvolverNode from './ConvolverNode'; import DelayNode from './DelayNode'; -import { ConvolverNodeOptions } from './ConvolverNodeOptions'; - -import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConstantSourceNode from './ConstantSourceNode'; export default class AudioContext implements BaseAudioContext { @@ -56,85 +47,49 @@ export default class AudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createDelay(maxDelayTime?: number): DelayNode { - return new DelayNode(this, this.context.createDelay(maxDelayTime)); + return new DelayNode(this, { maxDelayTime }); } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode { - return new IIRFilterNode( - this, - this.context.createIIRFilter(options.feedforward, options.feedback) - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(), - buffer, - disableNormalization - ); + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { + return new IIRFilterNode(this, { feedforward, feedback }); } - 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(pitchCorrection?: boolean): AudioBufferSourceNode { + return new AudioBufferSourceNode(this, { pitchCorrection }); } 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]` ); } @@ -150,9 +105,7 @@ export default class AudioContext implements BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( @@ -166,13 +119,11 @@ export default class AudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } async decodeAudioDataSource(source: string): Promise { diff --git a/packages/react-native-audio-api/src/web-core/AudioNode.tsx b/packages/react-native-audio-api/src/web-core/AudioNode.tsx index a66113f1f..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; @@ -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 c9341289f..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,8 +1,4 @@ -import { - ContextState, - PeriodicWaveConstraints, - IIRFilterNodeOptions, -} from '../types'; +import { ContextState } from '../types'; import AnalyserNode from './AnalyserNode'; import AudioDestinationNode from './AudioDestinationNode'; import AudioBuffer from './AudioBuffer'; @@ -31,9 +27,9 @@ 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(): Promise; + createBufferSource(): AudioBufferSourceNode; createBuffer( numOfChannels: number, length: number, diff --git a/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx b/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx index 4a8a4df21..dd540369a 100644 --- a/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx +++ b/packages/react-native-audio-api/src/web-core/BiquadFilterNode.tsx @@ -1,8 +1,8 @@ import AudioParam from './AudioParam'; import AudioNode from './AudioNode'; import BaseAudioContext from './BaseAudioContext'; -import { BiquadFilterType } from '../types'; import { InvalidAccessError } from '../errors'; +import { TBiquadFilterOptions } from '../types'; export default class BiquadFilterNode extends AudioNode { readonly frequency: AudioParam; @@ -12,8 +12,12 @@ export default class BiquadFilterNode extends AudioNode { constructor( context: BaseAudioContext, - biquadFilter: globalThis.BiquadFilterNode + biquadFilterOptions?: TBiquadFilterOptions ) { + const biquadFilter = new globalThis.BiquadFilterNode( + context.context, + biquadFilterOptions + ); super(context, biquadFilter); this.frequency = new AudioParam(biquadFilter.frequency, context); this.detune = new AudioParam(biquadFilter.detune, context); diff --git a/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx b/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx index 25560b4a6..9d8e745c1 100644 --- a/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx +++ b/packages/react-native-audio-api/src/web-core/ConstantSourceNode.tsx @@ -1,11 +1,13 @@ import AudioParam from './AudioParam'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; +import { TConstantSourceOptions } from '../types'; export default class ConstantSourceNode extends AudioScheduledSourceNode { readonly offset: AudioParam; - constructor(context: BaseAudioContext, node: globalThis.ConstantSourceNode) { + constructor(context: BaseAudioContext, options?: TConstantSourceOptions) { + const node = new globalThis.ConstantSourceNode(context.context, options); super(context, node); this.offset = new AudioParam(node.offset, context); } diff --git a/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx b/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx index 6bc910514..8a9488ed1 100644 --- a/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx +++ b/packages/react-native-audio-api/src/web-core/ConvolverNode.tsx @@ -1,20 +1,20 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioBuffer from './AudioBuffer'; +import { TWebConvolverOptions } from '../types'; export default class ConvolverNode extends AudioNode { constructor( context: BaseAudioContext, - node: globalThis.ConvolverNode, - buffer: AudioBuffer | null = null, - disableNormalization: boolean = false + convolverOptions?: TWebConvolverOptions ) { - super(context, node); + const convolver = new globalThis.ConvolverNode( + context.context, + convolverOptions + ); - (this.node as globalThis.ConvolverNode).normalize = !disableNormalization; - if (buffer) { - (this.node as globalThis.ConvolverNode).buffer = buffer.buffer; - } + const node = convolver; + super(context, node); } public get buffer(): AudioBuffer | null { diff --git a/packages/react-native-audio-api/src/web-core/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/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/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 54c7f9288..d1939d0b2 100644 --- a/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx +++ b/packages/react-native-audio-api/src/web-core/OfflineAudioContext.tsx @@ -1,10 +1,4 @@ -import { - ContextState, - PeriodicWaveConstraints, - OfflineAudioContextOptions, - AudioBufferBaseSourceNodeOptions, - IIRFilterNodeOptions, -} from '../types'; +import { ContextState, OfflineAudioContextOptions } from '../types'; import { InvalidAccessError, NotSupportedError } from '../errors'; import BaseAudioContext from './BaseAudioContext'; import AnalyserNode from './AnalyserNode'; @@ -19,9 +13,7 @@ import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import ConstantSourceNode from './ConstantSourceNode'; -import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm'; import ConvolverNode from './ConvolverNode'; -import { ConvolverNodeOptions } from './ConvolverNodeOptions'; import DelayNode from './DelayNode'; export default class OfflineAudioContext implements BaseAudioContext { @@ -62,85 +54,49 @@ export default class OfflineAudioContext implements BaseAudioContext { } createOscillator(): OscillatorNode { - return new OscillatorNode(this, this.context.createOscillator()); + return new OscillatorNode(this); } createConstantSource(): ConstantSourceNode { - return new ConstantSourceNode(this, this.context.createConstantSource()); + return new ConstantSourceNode(this); } createGain(): GainNode { - return new GainNode(this, this.context.createGain()); + return new GainNode(this); } createDelay(maxDelayTime?: number): DelayNode { - return new DelayNode(this, this.context.createDelay(maxDelayTime)); + return new DelayNode(this, { maxDelayTime }); } createStereoPanner(): StereoPannerNode { - return new StereoPannerNode(this, this.context.createStereoPanner()); + return new StereoPannerNode(this); } createBiquadFilter(): BiquadFilterNode { - return new BiquadFilterNode(this, this.context.createBiquadFilter()); + return new BiquadFilterNode(this); } - createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode { - return new IIRFilterNode( - this, - this.context.createIIRFilter(options.feedforward, options.feedback) - ); + createConvolver(): ConvolverNode { + return new ConvolverNode(this); } - createConvolver(options?: ConvolverNodeOptions): ConvolverNode { - if (options?.buffer) { - const numberOfChannels = options.buffer.numberOfChannels; - if ( - numberOfChannels !== 1 && - numberOfChannels !== 2 && - numberOfChannels !== 4 - ) { - throw new NotSupportedError( - `The number of channels provided (${numberOfChannels}) in impulse response for ConvolverNode buffer must be 1 or 2 or 4.` - ); - } - } - const buffer = options?.buffer ?? null; - const disableNormalization = options?.disableNormalization ?? false; - return new ConvolverNode( - this, - this.context.createConvolver(), - buffer, - disableNormalization - ); + createIIRFilter(feedforward: number[], feedback: number[]): IIRFilterNode { + return new IIRFilterNode(this, { feedforward, feedback }); } - 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(pitchCorrection?: boolean): AudioBufferSourceNode { + return new AudioBufferSourceNode(this, { pitchCorrection }); } 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]` ); } @@ -156,9 +112,7 @@ export default class OfflineAudioContext implements BaseAudioContext { ); } - return new AudioBuffer( - this.context.createBuffer(numOfChannels, length, sampleRate) - ); + return new AudioBuffer(this, { numberOfChannels, length, sampleRate }); } createPeriodicWave( @@ -172,13 +126,11 @@ export default class OfflineAudioContext implements BaseAudioContext { ); } - return new PeriodicWave( - this.context.createPeriodicWave(real, imag, constraints) - ); + return new PeriodicWave(this, { real, imag, ...constraints }); } createAnalyser(): AnalyserNode { - return new AnalyserNode(this, this.context.createAnalyser()); + return new AnalyserNode(this); } async decodeAudioDataSource(source: string): Promise { diff --git a/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx b/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx index 612cdd9f6..d180aa596 100644 --- a/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx +++ b/packages/react-native-audio-api/src/web-core/OscillatorNode.tsx @@ -1,4 +1,4 @@ -import { OscillatorType } from '../types'; +import { OscillatorType, TOscillatorOptions } from '../types'; import { InvalidStateError } from '../errors'; import AudioScheduledSourceNode from './AudioScheduledSourceNode'; import BaseAudioContext from './BaseAudioContext'; @@ -9,7 +9,8 @@ export default class OscillatorNode extends AudioScheduledSourceNode { readonly frequency: AudioParam; readonly detune: AudioParam; - constructor(context: BaseAudioContext, node: globalThis.OscillatorNode) { + constructor(context: BaseAudioContext, options?: TOscillatorOptions) { + const node = new globalThis.OscillatorNode(context.context, options); super(context, node); this.detune = new AudioParam(node.detune, context); diff --git a/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx b/packages/react-native-audio-api/src/web-core/PeriodicWave.tsx index bdf8979e1..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,8 +1,18 @@ +import BaseAudioContext from './BaseAudioContext'; +import { TPeriodicWaveOptions } from '../types'; +import { generateRealAndImag } from '../core/PeriodicWave'; + export default class PeriodicWave { /** @internal */ readonly periodicWave: globalThis.PeriodicWave; - constructor(periodicWave: globalThis.PeriodicWave) { + constructor(context: BaseAudioContext, options?: TPeriodicWaveOptions) { + const finalOptions = generateRealAndImag(options); + const periodicWave = context.context.createPeriodicWave( + finalOptions.real!, + finalOptions.imag!, + { disableNormalization: finalOptions.disableNormalization } + ); this.periodicWave = periodicWave; } } diff --git a/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx b/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx index 2d468a205..5c968c8ff 100644 --- a/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx +++ b/packages/react-native-audio-api/src/web-core/StereoPannerNode.tsx @@ -1,11 +1,19 @@ import BaseAudioContext from './BaseAudioContext'; import AudioNode from './AudioNode'; import AudioParam from './AudioParam'; +import { TSteroPannerOptions } from '../types'; export default class StereoPannerNode extends AudioNode { readonly pan: AudioParam; - constructor(context: BaseAudioContext, pan: globalThis.StereoPannerNode) { + constructor( + context: BaseAudioContext, + stereoPannerOptions?: TSteroPannerOptions + ) { + const pan = new globalThis.StereoPannerNode( + context.context, + stereoPannerOptions + ); super(context, pan); this.pan = new AudioParam(pan.pan, context); }