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.
- package/RNAudioAPI.podspec +7 -5
- package/android/build.gradle +8 -2
- package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +18 -1
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +32 -0
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +1 -0
- package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.cpp +33 -0
- package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.h +20 -0
- package/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp +4 -0
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +15 -2
- package/common/cpp/audioapi/core/BaseAudioContext.h +4 -0
- package/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +166 -0
- package/common/cpp/audioapi/core/effects/IIRFilterNode.h +75 -0
- package/common/cpp/audioapi/core/sources/StreamerNode.h +5 -5
- package/common/cpp/audioapi/core/utils/Constants.h +1 -0
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +2 -0
- package/common/cpp/test/CMakeLists.txt +1 -0
- package/common/cpp/test/src/IIRFilterTest.cpp +153 -0
- package/ios/audioapi/ios/core/utils/AudioDecoder.mm +12 -0
- package/lib/commonjs/core/BaseAudioContext.js +23 -1
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/commonjs/core/IIRFilterNode.js +19 -0
- package/lib/commonjs/core/IIRFilterNode.js.map +1 -0
- package/lib/commonjs/plugin/withAudioAPI.js +46 -0
- package/lib/commonjs/plugin/withAudioAPI.js.map +1 -1
- package/lib/commonjs/web-core/AudioContext.js +4 -0
- package/lib/commonjs/web-core/AudioContext.js.map +1 -1
- package/lib/commonjs/web-core/IIRFilterNode.js +19 -0
- package/lib/commonjs/web-core/IIRFilterNode.js.map +1 -0
- package/lib/commonjs/web-core/OfflineAudioContext.js +4 -0
- package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +24 -2
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/IIRFilterNode.js +13 -0
- package/lib/module/core/IIRFilterNode.js.map +1 -0
- package/lib/module/plugin/withAudioAPI.js +47 -1
- package/lib/module/plugin/withAudioAPI.js.map +1 -1
- package/lib/module/web-core/AudioContext.js +4 -0
- package/lib/module/web-core/AudioContext.js.map +1 -1
- package/lib/module/web-core/IIRFilterNode.js +13 -0
- package/lib/module/web-core/IIRFilterNode.js.map +1 -0
- package/lib/module/web-core/OfflineAudioContext.js +4 -0
- package/lib/module/web-core/OfflineAudioContext.js.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +3 -1
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/IIRFilterNode.d.ts +5 -0
- package/lib/typescript/core/IIRFilterNode.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +5 -1
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/plugin/withAudioAPI.d.ts +1 -0
- package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +4 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioContext.d.ts +3 -1
- package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/BaseAudioContext.d.ts +3 -1
- package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/IIRFilterNode.d.ts +5 -0
- package/lib/typescript/web-core/IIRFilterNode.d.ts.map +1 -0
- package/lib/typescript/web-core/OfflineAudioContext.d.ts +3 -1
- package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/BaseAudioContext.ts +44 -2
- package/src/core/IIRFilterNode.ts +25 -0
- package/src/interfaces.ts +13 -1
- package/src/plugin/withAudioAPI.ts +61 -0
- package/src/types.ts +5 -0
- package/src/web-core/AudioContext.tsx +9 -0
- package/src/web-core/BaseAudioContext.tsx +7 -1
- package/src/web-core/IIRFilterNode.tsx +25 -0
- package/src/web-core/OfflineAudioContext.tsx +9 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
@@ -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 {
|
|
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;
|