react-native-audio-api 0.11.0-nightly-0a987bc-20251120 → 0.11.0-nightly-6dcac64-20251122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/RNAudioAPI.podspec +7 -5
  2. package/android/build.gradle +8 -2
  3. package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +18 -1
  4. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +32 -0
  5. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +1 -0
  6. package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.cpp +33 -0
  7. package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.h +20 -0
  8. package/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp +4 -0
  9. package/common/cpp/audioapi/core/BaseAudioContext.cpp +15 -2
  10. package/common/cpp/audioapi/core/BaseAudioContext.h +4 -0
  11. package/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +166 -0
  12. package/common/cpp/audioapi/core/effects/IIRFilterNode.h +75 -0
  13. package/common/cpp/audioapi/core/sources/StreamerNode.h +5 -5
  14. package/common/cpp/audioapi/core/utils/Constants.h +1 -0
  15. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +2 -0
  16. package/common/cpp/test/CMakeLists.txt +1 -0
  17. package/common/cpp/test/src/IIRFilterTest.cpp +153 -0
  18. package/ios/audioapi/ios/core/utils/AudioDecoder.mm +12 -0
  19. package/lib/commonjs/core/BaseAudioContext.js +23 -1
  20. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  21. package/lib/commonjs/core/IIRFilterNode.js +19 -0
  22. package/lib/commonjs/core/IIRFilterNode.js.map +1 -0
  23. package/lib/commonjs/plugin/withAudioAPI.js +46 -0
  24. package/lib/commonjs/plugin/withAudioAPI.js.map +1 -1
  25. package/lib/commonjs/web-core/AudioContext.js +4 -0
  26. package/lib/commonjs/web-core/AudioContext.js.map +1 -1
  27. package/lib/commonjs/web-core/IIRFilterNode.js +19 -0
  28. package/lib/commonjs/web-core/IIRFilterNode.js.map +1 -0
  29. package/lib/commonjs/web-core/OfflineAudioContext.js +4 -0
  30. package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -1
  31. package/lib/module/core/BaseAudioContext.js +24 -2
  32. package/lib/module/core/BaseAudioContext.js.map +1 -1
  33. package/lib/module/core/IIRFilterNode.js +13 -0
  34. package/lib/module/core/IIRFilterNode.js.map +1 -0
  35. package/lib/module/plugin/withAudioAPI.js +47 -1
  36. package/lib/module/plugin/withAudioAPI.js.map +1 -1
  37. package/lib/module/web-core/AudioContext.js +4 -0
  38. package/lib/module/web-core/AudioContext.js.map +1 -1
  39. package/lib/module/web-core/IIRFilterNode.js +13 -0
  40. package/lib/module/web-core/IIRFilterNode.js.map +1 -0
  41. package/lib/module/web-core/OfflineAudioContext.js +4 -0
  42. package/lib/module/web-core/OfflineAudioContext.js.map +1 -1
  43. package/lib/typescript/core/BaseAudioContext.d.ts +3 -1
  44. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  45. package/lib/typescript/core/IIRFilterNode.d.ts +5 -0
  46. package/lib/typescript/core/IIRFilterNode.d.ts.map +1 -0
  47. package/lib/typescript/interfaces.d.ts +5 -1
  48. package/lib/typescript/interfaces.d.ts.map +1 -1
  49. package/lib/typescript/plugin/withAudioAPI.d.ts +1 -0
  50. package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -1
  51. package/lib/typescript/types.d.ts +4 -0
  52. package/lib/typescript/types.d.ts.map +1 -1
  53. package/lib/typescript/web-core/AudioContext.d.ts +3 -1
  54. package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
  55. package/lib/typescript/web-core/BaseAudioContext.d.ts +3 -1
  56. package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
  57. package/lib/typescript/web-core/IIRFilterNode.d.ts +5 -0
  58. package/lib/typescript/web-core/IIRFilterNode.d.ts.map +1 -0
  59. package/lib/typescript/web-core/OfflineAudioContext.d.ts +3 -1
  60. package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/src/core/BaseAudioContext.ts +44 -2
  63. package/src/core/IIRFilterNode.ts +25 -0
  64. package/src/interfaces.ts +13 -1
  65. package/src/plugin/withAudioAPI.ts +61 -0
  66. package/src/types.ts +5 -0
  67. package/src/web-core/AudioContext.tsx +9 -0
  68. package/src/web-core/BaseAudioContext.tsx +7 -1
  69. package/src/web-core/IIRFilterNode.tsx +25 -0
  70. package/src/web-core/OfflineAudioContext.tsx +9 -0
@@ -1,4 +1,8 @@
1
- import { InvalidAccessError, NotSupportedError } from '../errors';
1
+ import {
2
+ InvalidAccessError,
3
+ InvalidStateError,
4
+ NotSupportedError,
5
+ } from '../errors';
2
6
  import { IBaseAudioContext } from '../interfaces';
3
7
  import {
4
8
  AudioBufferBaseSourceNodeOptions,
@@ -6,6 +10,7 @@ import {
6
10
  PeriodicWaveConstraints,
7
11
  AudioWorkletRuntime,
8
12
  ConvolverNodeOptions,
13
+ IIRFilterNodeOptions,
9
14
  } from '../types';
10
15
  import { assertWorkletsEnabled, workletsModule } from '../utils';
11
16
  import WorkletSourceNode from './WorkletSourceNode';
@@ -25,6 +30,7 @@ import RecorderAdapterNode from './RecorderAdapterNode';
25
30
  import StereoPannerNode from './StereoPannerNode';
26
31
  import StreamerNode from './StreamerNode';
27
32
  import WorkletNode from './WorkletNode';
33
+ import IIRFilterNode from './IIRFilterNode';
28
34
  import { decodeAudioData, decodePCMInBase64 } from './AudioDecoder';
29
35
 
30
36
  export default class BaseAudioContext {
@@ -185,7 +191,11 @@ export default class BaseAudioContext {
185
191
  }
186
192
 
187
193
  createStreamer(): StreamerNode {
188
- return new StreamerNode(this, this.context.createStreamer());
194
+ const streamer = this.context.createStreamer();
195
+ if (!streamer) {
196
+ throw new NotSupportedError('StreamerNode requires FFmpeg build');
197
+ }
198
+ return new StreamerNode(this, streamer);
189
199
  }
190
200
 
191
201
  createConstantSource(): ConstantSourceNode {
@@ -204,6 +214,38 @@ export default class BaseAudioContext {
204
214
  return new BiquadFilterNode(this, this.context.createBiquadFilter());
205
215
  }
206
216
 
217
+ createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode {
218
+ const feedforward = options.feedforward;
219
+ const feedback = options.feedback;
220
+ if (feedforward.length < 1 || feedforward.length > 20) {
221
+ throw new NotSupportedError(
222
+ `The provided feedforward array has length (${feedforward.length}) outside the range [1, 20]`
223
+ );
224
+ }
225
+ if (feedback.length < 1 || feedback.length > 20) {
226
+ throw new NotSupportedError(
227
+ `The provided feedback array has length (${feedback.length}) outside the range [1, 20]`
228
+ );
229
+ }
230
+
231
+ if (feedforward.every((value) => value === 0)) {
232
+ throw new InvalidStateError(
233
+ `Feedforward array must contain at least one non-zero value`
234
+ );
235
+ }
236
+
237
+ if (feedback[0] === 0) {
238
+ throw new InvalidStateError(
239
+ `First value of feedback array cannot be zero`
240
+ );
241
+ }
242
+
243
+ return new IIRFilterNode(
244
+ this,
245
+ this.context.createIIRFilter(feedforward, feedback)
246
+ );
247
+ }
248
+
207
249
  createBufferSource(
208
250
  options?: AudioBufferBaseSourceNodeOptions
209
251
  ): AudioBufferSourceNode {
@@ -0,0 +1,25 @@
1
+ import { NotSupportedError } from '../errors';
2
+ import { IIIRFilterNode } from '../interfaces';
3
+ import AudioNode from './AudioNode';
4
+
5
+ export default class IIRFilterNode extends AudioNode {
6
+ public getFrequencyResponse(
7
+ frequencyArray: Float32Array,
8
+ magResponseOutput: Float32Array,
9
+ phaseResponseOutput: Float32Array
10
+ ) {
11
+ if (
12
+ frequencyArray.length !== magResponseOutput.length ||
13
+ frequencyArray.length !== phaseResponseOutput.length
14
+ ) {
15
+ throw new NotSupportedError(
16
+ `The lengths of the arrays are not the same frequencyArray: ${frequencyArray.length}, magResponseOutput: ${magResponseOutput.length}, phaseResponseOutput: ${phaseResponseOutput.length}`
17
+ );
18
+ }
19
+ (this.node as IIIRFilterNode).getFrequencyResponse(
20
+ frequencyArray,
21
+ magResponseOutput,
22
+ phaseResponseOutput
23
+ );
24
+ }
25
+ }
package/src/interfaces.ts CHANGED
@@ -62,6 +62,10 @@ export interface IBaseAudioContext {
62
62
  createGain(): IGainNode;
63
63
  createStereoPanner(): IStereoPannerNode;
64
64
  createBiquadFilter: () => IBiquadFilterNode;
65
+ createIIRFilter: (
66
+ feedforward: number[],
67
+ feedback: number[]
68
+ ) => IIIRFilterNode;
65
69
  createBufferSource: (pitchCorrection: boolean) => IAudioBufferSourceNode;
66
70
  createBufferQueueSource: (
67
71
  pitchCorrection: boolean
@@ -81,7 +85,7 @@ export interface IBaseAudioContext {
81
85
  buffer: IAudioBuffer | undefined,
82
86
  disableNormalization: boolean
83
87
  ) => IConvolverNode;
84
- createStreamer: () => IStreamerNode;
88
+ createStreamer: () => IStreamerNode | null; // null when FFmpeg is not enabled
85
89
  }
86
90
 
87
91
  export interface IAudioContext extends IBaseAudioContext {
@@ -130,6 +134,14 @@ export interface IBiquadFilterNode extends IAudioNode {
130
134
  ): void;
131
135
  }
132
136
 
137
+ export interface IIIRFilterNode extends IAudioNode {
138
+ getFrequencyResponse(
139
+ frequencyArray: Float32Array,
140
+ magResponseOutput: Float32Array,
141
+ phaseResponseOutput: Float32Array
142
+ ): void;
143
+ }
144
+
133
145
  export interface IAudioDestinationNode extends IAudioNode {}
134
146
 
135
147
  export interface IAudioScheduledSourceNode extends IAudioNode {
@@ -4,6 +4,8 @@ import {
4
4
  ConfigPlugin,
5
5
  withInfoPlist,
6
6
  withAndroidManifest,
7
+ withGradleProperties,
8
+ withPodfile,
7
9
  } from '@expo/config-plugins';
8
10
  const pkg = require('react-native-audio-api/package.json');
9
11
 
@@ -13,6 +15,7 @@ interface Options {
13
15
  androidPermissions: string[];
14
16
  androidForegroundService: boolean;
15
17
  androidFSTypes: string[];
18
+ disableFFmpeg: boolean;
16
19
  }
17
20
 
18
21
  const withDefaultOptions = (options: Partial<Options>): Options => {
@@ -24,6 +27,7 @@ const withDefaultOptions = (options: Partial<Options>): Options => {
24
27
  ],
25
28
  androidForegroundService: true,
26
29
  androidFSTypes: ['mediaPlayback'],
30
+ disableFFmpeg: false,
27
31
  ...options,
28
32
  };
29
33
  };
@@ -88,6 +92,59 @@ const withForegroundService: ConfigPlugin<Options> = (
88
92
  });
89
93
  };
90
94
 
95
+ const withFFmpegConfig: ConfigPlugin<Options> = (config, options) => {
96
+ const iosConf = withPodfile(config, (mod) => {
97
+ let contents = mod.modResults.contents;
98
+ const ffmpegRegex = /^.*ENV\['DISABLE_AUDIOAPI_FFMPEG'\].*$/gm;
99
+ const podfileString = options.disableFFmpeg
100
+ ? `ENV['DISABLE_AUDIOAPI_FFMPEG'] = '1'`
101
+ : '';
102
+ // No existing setting
103
+ if (contents.search(ffmpegRegex) === -1) {
104
+ if (options.disableFFmpeg) {
105
+ if (contents.endsWith('\n')) {
106
+ contents = `${contents}${podfileString}`;
107
+ } else {
108
+ contents = `${contents}\n${podfileString}`;
109
+ }
110
+ mod.modResults.contents = contents;
111
+ }
112
+ } else {
113
+ // Existing setting found, will replace
114
+ contents = contents.replace(ffmpegRegex, podfileString);
115
+ }
116
+
117
+ mod.modResults.contents = contents;
118
+ return mod;
119
+ });
120
+
121
+ const finalConf = withGradleProperties(iosConf, (mod) => {
122
+ const gradleProperties = mod.modResults;
123
+
124
+ const existingIndex = gradleProperties.findIndex(
125
+ (prop) => prop.type === 'property' && prop.key === 'disableAudioapiFFmpeg'
126
+ );
127
+ if (existingIndex !== -1) {
128
+ gradleProperties.splice(existingIndex, 1);
129
+ } else if (!options.disableFFmpeg) {
130
+ // No existing setting and FFmpeg is enabled, do nothing.
131
+ return mod;
132
+ }
133
+
134
+ if (options.disableFFmpeg) {
135
+ gradleProperties.push({
136
+ type: 'property',
137
+ key: 'disableAudioapiFFmpeg',
138
+ value: options.disableFFmpeg ? 'true' : 'false',
139
+ });
140
+ }
141
+
142
+ return mod;
143
+ });
144
+
145
+ return finalConf;
146
+ };
147
+
91
148
  const withAudioAPI: ConfigPlugin<Options> = (config, optionsIn) => {
92
149
  const options = withDefaultOptions(optionsIn ?? {});
93
150
 
@@ -105,6 +162,10 @@ const withAudioAPI: ConfigPlugin<Options> = (config, optionsIn) => {
105
162
  config = withIosMicrophonePermission(config, options);
106
163
  }
107
164
 
165
+ if (options.disableFFmpeg !== undefined) {
166
+ config = withFFmpegConfig(config, options);
167
+ }
168
+
108
169
  return config;
109
170
  };
110
171
 
package/src/types.ts CHANGED
@@ -56,3 +56,8 @@ export interface ConvolverNodeOptions {
56
56
  buffer?: AudioBuffer | null;
57
57
  disableNormalization?: boolean;
58
58
  }
59
+
60
+ export interface IIRFilterNodeOptions {
61
+ feedforward: number[];
62
+ feedback: number[];
63
+ }
@@ -3,6 +3,7 @@ import {
3
3
  PeriodicWaveConstraints,
4
4
  AudioContextOptions,
5
5
  AudioBufferBaseSourceNodeOptions,
6
+ IIRFilterNodeOptions,
6
7
  } from '../types';
7
8
  import { InvalidAccessError, NotSupportedError } from '../errors';
8
9
  import BaseAudioContext from './BaseAudioContext';
@@ -11,6 +12,7 @@ import AudioDestinationNode from './AudioDestinationNode';
11
12
  import AudioBuffer from './AudioBuffer';
12
13
  import AudioBufferSourceNode from './AudioBufferSourceNode';
13
14
  import BiquadFilterNode from './BiquadFilterNode';
15
+ import IIRFilterNode from './IIRFilterNode';
14
16
  import GainNode from './GainNode';
15
17
  import OscillatorNode from './OscillatorNode';
16
18
  import PeriodicWave from './PeriodicWave';
@@ -72,6 +74,13 @@ export default class AudioContext implements BaseAudioContext {
72
74
  return new BiquadFilterNode(this, this.context.createBiquadFilter());
73
75
  }
74
76
 
77
+ createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode {
78
+ return new IIRFilterNode(
79
+ this,
80
+ this.context.createIIRFilter(options.feedforward, options.feedback)
81
+ );
82
+ }
83
+
75
84
  createConvolver(options?: ConvolverNodeOptions): ConvolverNode {
76
85
  if (options?.buffer) {
77
86
  const numberOfChannels = options.buffer.numberOfChannels;
@@ -1,9 +1,14 @@
1
- import { ContextState, PeriodicWaveConstraints } from '../types';
1
+ import {
2
+ ContextState,
3
+ PeriodicWaveConstraints,
4
+ IIRFilterNodeOptions,
5
+ } from '../types';
2
6
  import AnalyserNode from './AnalyserNode';
3
7
  import AudioDestinationNode from './AudioDestinationNode';
4
8
  import AudioBuffer from './AudioBuffer';
5
9
  import AudioBufferSourceNode from './AudioBufferSourceNode';
6
10
  import BiquadFilterNode from './BiquadFilterNode';
11
+ import IIRFilterNode from './IIRFilterNode';
7
12
  import GainNode from './GainNode';
8
13
  import OscillatorNode from './OscillatorNode';
9
14
  import PeriodicWave from './PeriodicWave';
@@ -24,6 +29,7 @@ export default interface BaseAudioContext {
24
29
  createGain(): GainNode;
25
30
  createStereoPanner(): StereoPannerNode;
26
31
  createBiquadFilter(): BiquadFilterNode;
32
+ createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode;
27
33
  createConvolver(): ConvolverNode;
28
34
  createBufferSource(): Promise<AudioBufferSourceNode>;
29
35
  createBuffer(
@@ -0,0 +1,25 @@
1
+ import { NotSupportedError } from '../errors';
2
+ import AudioNode from './AudioNode';
3
+
4
+ export default class IIRFilterNode extends AudioNode {
5
+ public getFrequencyResponse(
6
+ frequencyArray: Float32Array,
7
+ magResponseOutput: Float32Array,
8
+ phaseResponseOutput: Float32Array
9
+ ) {
10
+ if (
11
+ frequencyArray.length !== magResponseOutput.length ||
12
+ frequencyArray.length !== phaseResponseOutput.length
13
+ ) {
14
+ throw new NotSupportedError(
15
+ `The lengths of the arrays are not the same frequencyArray: ${frequencyArray.length}, magResponseOutput: ${magResponseOutput.length}, phaseResponseOutput: ${phaseResponseOutput.length}`
16
+ );
17
+ }
18
+
19
+ (this.node as globalThis.IIRFilterNode).getFrequencyResponse(
20
+ frequencyArray,
21
+ magResponseOutput,
22
+ phaseResponseOutput
23
+ );
24
+ }
25
+ }
@@ -3,6 +3,7 @@ import {
3
3
  PeriodicWaveConstraints,
4
4
  OfflineAudioContextOptions,
5
5
  AudioBufferBaseSourceNodeOptions,
6
+ IIRFilterNodeOptions,
6
7
  } from '../types';
7
8
  import { InvalidAccessError, NotSupportedError } from '../errors';
8
9
  import BaseAudioContext from './BaseAudioContext';
@@ -11,6 +12,7 @@ import AudioDestinationNode from './AudioDestinationNode';
11
12
  import AudioBuffer from './AudioBuffer';
12
13
  import AudioBufferSourceNode from './AudioBufferSourceNode';
13
14
  import BiquadFilterNode from './BiquadFilterNode';
15
+ import IIRFilterNode from './IIRFilterNode';
14
16
  import GainNode from './GainNode';
15
17
  import OscillatorNode from './OscillatorNode';
16
18
  import PeriodicWave from './PeriodicWave';
@@ -78,6 +80,13 @@ export default class OfflineAudioContext implements BaseAudioContext {
78
80
  return new BiquadFilterNode(this, this.context.createBiquadFilter());
79
81
  }
80
82
 
83
+ createIIRFilter(options: IIRFilterNodeOptions): IIRFilterNode {
84
+ return new IIRFilterNode(
85
+ this,
86
+ this.context.createIIRFilter(options.feedforward, options.feedback)
87
+ );
88
+ }
89
+
81
90
  createConvolver(options?: ConvolverNodeOptions): ConvolverNode {
82
91
  if (options?.buffer) {
83
92
  const numberOfChannels = options.buffer.numberOfChannels;