react-native-audio-api 0.4.12 → 0.5.0-rc.1

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 (114) hide show
  1. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +7 -6
  2. package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +25 -24
  3. package/common/cpp/audioapi/HostObjects/AnalyserNodeHostObject.h +17 -41
  4. package/common/cpp/audioapi/HostObjects/AudioBufferHostObject.h +22 -32
  5. package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +2 -17
  6. package/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +14 -4
  7. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +5 -8
  8. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +13 -9
  9. package/common/cpp/audioapi/HostObjects/BiquadFilterNodeHostObject.h +8 -19
  10. package/common/cpp/audioapi/core/AudioContext.cpp +14 -4
  11. package/common/cpp/audioapi/core/AudioContext.h +2 -2
  12. package/common/cpp/audioapi/core/BaseAudioContext.cpp +4 -2
  13. package/common/cpp/audioapi/core/BaseAudioContext.h +1 -1
  14. package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +5 -6
  15. package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +4 -3
  16. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +5 -11
  17. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +3 -31
  18. package/lib/module/api.js +1 -1
  19. package/lib/module/api.js.map +1 -1
  20. package/lib/module/api.web.js +1 -2
  21. package/lib/module/api.web.js.map +1 -1
  22. package/lib/module/core/AnalyserNode.js.map +1 -1
  23. package/lib/module/core/AudioBuffer.js.map +1 -1
  24. package/lib/module/core/AudioBufferSourceNode.js +0 -6
  25. package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
  26. package/lib/module/core/AudioParam.js.map +1 -1
  27. package/lib/module/core/BaseAudioContext.js +3 -2
  28. package/lib/module/core/BaseAudioContext.js.map +1 -1
  29. package/lib/module/core/BiquadFilterNode.js.map +1 -1
  30. package/lib/module/utils/index.js +6 -0
  31. package/lib/module/utils/index.js.map +1 -0
  32. package/lib/module/web-core/AnalyserNode.js +4 -20
  33. package/lib/module/web-core/AnalyserNode.js.map +1 -1
  34. package/lib/module/web-core/AudioBuffer.js +2 -6
  35. package/lib/module/web-core/AudioBuffer.js.map +1 -1
  36. package/lib/module/web-core/AudioBufferSourceNode.js +161 -21
  37. package/lib/module/web-core/AudioBufferSourceNode.js.map +1 -1
  38. package/lib/module/web-core/AudioContext.js +7 -8
  39. package/lib/module/web-core/AudioContext.js.map +1 -1
  40. package/lib/module/web-core/AudioParam.js +1 -1
  41. package/lib/module/web-core/AudioParam.js.map +1 -1
  42. package/lib/module/web-core/BiquadFilterNode.js +1 -9
  43. package/lib/module/web-core/BiquadFilterNode.js.map +1 -1
  44. package/lib/module/web-core/custom/signalsmithStretch/README.md +1 -1
  45. package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +2 -2
  46. package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs.map +1 -1
  47. package/lib/typescript/api.d.ts +1 -1
  48. package/lib/typescript/api.d.ts.map +1 -1
  49. package/lib/typescript/api.web.d.ts +1 -2
  50. package/lib/typescript/api.web.d.ts.map +1 -1
  51. package/lib/typescript/core/AnalyserNode.d.ts +4 -4
  52. package/lib/typescript/core/AnalyserNode.d.ts.map +1 -1
  53. package/lib/typescript/core/AudioBuffer.d.ts +3 -3
  54. package/lib/typescript/core/AudioBuffer.d.ts.map +1 -1
  55. package/lib/typescript/core/AudioBufferSourceNode.d.ts +0 -3
  56. package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
  57. package/lib/typescript/core/AudioParam.d.ts +1 -1
  58. package/lib/typescript/core/AudioParam.d.ts.map +1 -1
  59. package/lib/typescript/core/BaseAudioContext.d.ts +3 -3
  60. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  61. package/lib/typescript/core/BiquadFilterNode.d.ts +1 -1
  62. package/lib/typescript/core/BiquadFilterNode.d.ts.map +1 -1
  63. package/lib/typescript/interfaces.d.ts +12 -13
  64. package/lib/typescript/interfaces.d.ts.map +1 -1
  65. package/lib/typescript/types.d.ts +3 -1
  66. package/lib/typescript/types.d.ts.map +1 -1
  67. package/lib/typescript/utils/index.d.ts +2 -0
  68. package/lib/typescript/utils/index.d.ts.map +1 -0
  69. package/lib/typescript/web-core/AnalyserNode.d.ts +4 -4
  70. package/lib/typescript/web-core/AnalyserNode.d.ts.map +1 -1
  71. package/lib/typescript/web-core/AudioBuffer.d.ts +2 -2
  72. package/lib/typescript/web-core/AudioBuffer.d.ts.map +1 -1
  73. package/lib/typescript/web-core/AudioBufferSourceNode.d.ts +58 -6
  74. package/lib/typescript/web-core/AudioBufferSourceNode.d.ts.map +1 -1
  75. package/lib/typescript/web-core/AudioContext.d.ts +3 -5
  76. package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
  77. package/lib/typescript/web-core/AudioParam.d.ts +1 -1
  78. package/lib/typescript/web-core/AudioParam.d.ts.map +1 -1
  79. package/lib/typescript/web-core/BaseAudioContext.d.ts +2 -2
  80. package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
  81. package/lib/typescript/web-core/BiquadFilterNode.d.ts +1 -1
  82. package/lib/typescript/web-core/BiquadFilterNode.d.ts.map +1 -1
  83. package/package.json +8 -8
  84. package/scripts/setup-rn-audio-api-web.js +58 -0
  85. package/src/api.ts +0 -1
  86. package/src/api.web.ts +0 -2
  87. package/src/core/AnalyserNode.ts +4 -4
  88. package/src/core/AudioBuffer.ts +3 -3
  89. package/src/core/AudioBufferSourceNode.ts +0 -9
  90. package/src/core/AudioParam.ts +1 -1
  91. package/src/core/BaseAudioContext.ts +16 -5
  92. package/src/core/BiquadFilterNode.ts +3 -3
  93. package/src/interfaces.ts +14 -16
  94. package/src/types.ts +3 -1
  95. package/src/utils/index.ts +3 -0
  96. package/src/web-core/AnalyserNode.tsx +8 -30
  97. package/src/web-core/AudioBuffer.tsx +4 -14
  98. package/src/web-core/AudioBufferSourceNode.tsx +357 -31
  99. package/src/web-core/AudioContext.tsx +19 -13
  100. package/src/web-core/AudioParam.tsx +2 -6
  101. package/src/web-core/BaseAudioContext.tsx +3 -3
  102. package/src/web-core/BiquadFilterNode.tsx +6 -16
  103. package/src/web-core/custom/signalsmithStretch/README.md +1 -1
  104. package/src/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +4 -3
  105. package/common/cpp/audioapi/core/types/TimeStretchType.h +0 -7
  106. package/lib/module/web-core/StretcherNode.js +0 -64
  107. package/lib/module/web-core/StretcherNode.js.map +0 -1
  108. package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js +0 -822
  109. package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js.map +0 -1
  110. package/lib/typescript/web-core/StretcherNode.d.ts +0 -45
  111. package/lib/typescript/web-core/StretcherNode.d.ts.map +0 -1
  112. package/scripts/setup-custom-wasm.js +0 -104
  113. package/src/web-core/StretcherNode.tsx +0 -125
  114. package/src/web-core/custom/signalsmithStretch/SignalsmithStretch.js +0 -945
@@ -29,41 +29,19 @@ export default class AnalyserNode extends AudioNode {
29
29
  );
30
30
  }
31
31
 
32
- public getByteFrequencyData(array: number[]): void {
33
- const data = new Uint8Array(array);
34
-
35
- (this.node as globalThis.AnalyserNode).getByteFrequencyData(data);
36
-
37
- for (let i = 0; i < array.length; i++) {
38
- array[i] = data[i];
39
- }
32
+ public getByteFrequencyData(array: Uint8Array): void {
33
+ (this.node as globalThis.AnalyserNode).getByteFrequencyData(array);
40
34
  }
41
35
 
42
- public getByteTimeDomainData(array: number[]): void {
43
- const data = new Uint8Array(array);
44
-
45
- (this.node as globalThis.AnalyserNode).getByteTimeDomainData(data);
46
-
47
- for (let i = 0; i < array.length; i++) {
48
- array[i] = data[i];
49
- }
36
+ public getByteTimeDomainData(array: Uint8Array): void {
37
+ (this.node as globalThis.AnalyserNode).getByteTimeDomainData(array);
50
38
  }
51
39
 
52
- public getFloatFrequencyData(array: number[]): void {
53
- const data = new Float32Array(array);
54
- (this.node as globalThis.AnalyserNode).getFloatFrequencyData(data);
55
-
56
- for (let i = 0; i < array.length; i++) {
57
- array[i] = data[i];
58
- }
40
+ public getFloatFrequencyData(array: Float32Array): void {
41
+ (this.node as globalThis.AnalyserNode).getFloatFrequencyData(array);
59
42
  }
60
43
 
61
- public getFloatTimeDomainData(array: number[]): void {
62
- const data = new Float32Array(array);
63
- (this.node as globalThis.AnalyserNode).getFloatTimeDomainData(data);
64
-
65
- for (let i = 0; i < array.length; i++) {
66
- array[i] = data[i];
67
- }
44
+ public getFloatTimeDomainData(array: Float32Array): void {
45
+ (this.node as globalThis.AnalyserNode).getFloatTimeDomainData(array);
68
46
  }
69
47
  }
@@ -28,7 +28,7 @@ export default class AudioBuffer {
28
28
  }
29
29
 
30
30
  public copyFromChannel(
31
- destination: number[],
31
+ destination: Float32Array,
32
32
  channelNumber: number,
33
33
  startInChannel: number = 0
34
34
  ): void {
@@ -44,17 +44,11 @@ export default class AudioBuffer {
44
44
  );
45
45
  }
46
46
 
47
- const array = new Float32Array(destination);
48
-
49
- this.buffer.copyFromChannel(array, channelNumber, startInChannel);
50
-
51
- for (let i = 0; i < destination.length; i++) {
52
- destination[i] = array[i];
53
- }
47
+ this.buffer.copyFromChannel(destination, channelNumber, startInChannel);
54
48
  }
55
49
 
56
50
  public copyToChannel(
57
- source: number[],
51
+ source: Float32Array,
58
52
  channelNumber: number,
59
53
  startInChannel: number = 0
60
54
  ): void {
@@ -70,10 +64,6 @@ export default class AudioBuffer {
70
64
  );
71
65
  }
72
66
 
73
- this.buffer.copyToChannel(
74
- new Float32Array(source),
75
- channelNumber,
76
- startInChannel
77
- );
67
+ this.buffer.copyToChannel(source, channelNumber, startInChannel);
78
68
  }
79
69
  }
@@ -1,26 +1,274 @@
1
- import AudioScheduledSourceNode from './AudioScheduledSourceNode';
1
+ import { InvalidStateError, RangeError } from '../errors';
2
+
2
3
  import AudioParam from './AudioParam';
3
4
  import AudioBuffer from './AudioBuffer';
4
- import { InvalidStateError, RangeError } from '../errors';
5
5
  import BaseAudioContext from './BaseAudioContext';
6
- import { TimeStretchType } from '../types';
6
+ import AudioScheduledSourceNode from './AudioScheduledSourceNode';
7
7
 
8
- export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
9
- readonly playbackRate: AudioParam;
10
- readonly detune: AudioParam;
8
+ import { clamp } from '../utils';
9
+ import { globalTag } from './custom/LoadCustomWasm';
10
+
11
+ interface ScheduleOptions {
12
+ rate?: number;
13
+ active?: boolean;
14
+ output?: number;
15
+ input?: number;
16
+ semitones?: number;
17
+ loopStart?: number;
18
+ loopEnd?: number;
19
+ }
20
+
21
+ interface IStretcherNode extends globalThis.AudioNode {
22
+ channelCount: number;
23
+ channelCountMode: globalThis.ChannelCountMode;
24
+ channelInterpretation: globalThis.ChannelInterpretation;
25
+ context: globalThis.BaseAudioContext;
26
+ numberOfInputs: number;
27
+ numberOfOutputs: number;
28
+
29
+ onended:
30
+ | ((this: globalThis.AudioScheduledSourceNode, ev: Event) => unknown)
31
+ | null;
32
+ addEventListener: (
33
+ type: string,
34
+ listener: EventListenerOrEventListenerObject | null,
35
+ options?: boolean | AddEventListenerOptions | undefined
36
+ ) => void;
37
+ dispatchEvent: (event: Event) => boolean;
38
+ removeEventListener: (
39
+ type: string,
40
+ callback: EventListenerOrEventListenerObject | null,
41
+ options?: boolean | EventListenerOptions | undefined
42
+ ) => void;
43
+
44
+ addBuffers(channels: Float32Array[]): void;
45
+ dropBuffers(): void;
46
+
47
+ schedule(options: ScheduleOptions): void;
48
+
49
+ start(
50
+ when?: number,
51
+ offset?: number,
52
+ duration?: number,
53
+ rate?: number,
54
+ semitones?: number
55
+ ): void;
56
+
57
+ stop(when?: number): void;
58
+
59
+ connect(
60
+ destination: globalThis.AudioNode,
61
+ output?: number,
62
+ input?: number
63
+ ): globalThis.AudioNode;
64
+ connect(destination: globalThis.AudioParam, output?: number): void;
65
+
66
+ disconnect(): void;
67
+ disconnect(output: number): void;
68
+
69
+ disconnect(destination: globalThis.AudioNode): globalThis.AudioNode;
70
+ disconnect(destination: globalThis.AudioNode, output: number): void;
71
+ disconnect(
72
+ destination: globalThis.AudioNode,
73
+ output: number,
74
+ input: number
75
+ ): void;
76
+
77
+ disconnect(destination: globalThis.AudioParam): void;
78
+ disconnect(destination: globalThis.AudioParam, output: number): void;
79
+ }
80
+
81
+ class IStretcherNodeAudioParam implements globalThis.AudioParam {
82
+ private _value: number;
83
+ private _setter: (value: number, when?: number) => void;
84
+
85
+ public automationRate: AutomationRate;
86
+ public defaultValue: number;
87
+ public maxValue: number;
88
+ public minValue: number;
11
89
 
12
90
  constructor(
13
- context: BaseAudioContext,
14
- node: globalThis.AudioBufferSourceNode
91
+ value: number,
92
+ setter: (value: number, when?: number) => void,
93
+ automationRate: AutomationRate,
94
+ minValue: number,
95
+ maxValue: number,
96
+ defaultValue: number
15
97
  ) {
98
+ this._value = value;
99
+ this.automationRate = automationRate;
100
+ this.minValue = minValue;
101
+ this.maxValue = maxValue;
102
+ this.defaultValue = defaultValue;
103
+ this._setter = setter;
104
+ }
105
+
106
+ public get value(): number {
107
+ return this._value;
108
+ }
109
+
110
+ public set value(value: number) {
111
+ this._value = value;
112
+
113
+ this._setter(value);
114
+ }
115
+
116
+ cancelAndHoldAtTime(cancelTime: number): globalThis.AudioParam {
117
+ this._setter(this.defaultValue, cancelTime);
118
+ return this;
119
+ }
120
+
121
+ cancelScheduledValues(cancelTime: number): globalThis.AudioParam {
122
+ this._setter(this.defaultValue, cancelTime);
123
+ return this;
124
+ }
125
+
126
+ exponentialRampToValueAtTime(
127
+ _value: number,
128
+ _endTime: number
129
+ ): globalThis.AudioParam {
130
+ console.warn(
131
+ 'exponentialRampToValueAtTime is not implemented for pitch correction mode'
132
+ );
133
+ return this;
134
+ }
135
+
136
+ linearRampToValueAtTime(
137
+ _value: number,
138
+ _endTime: number
139
+ ): globalThis.AudioParam {
140
+ console.warn(
141
+ 'linearRampToValueAtTime is not implemented for pitch correction mode'
142
+ );
143
+ return this;
144
+ }
145
+
146
+ setTargetAtTime(
147
+ _target: number,
148
+ _startTime: number,
149
+ _timeConstant: number
150
+ ): globalThis.AudioParam {
151
+ console.warn(
152
+ 'setTargetAtTime is not implemented for pitch correction mode'
153
+ );
154
+ return this;
155
+ }
156
+
157
+ setValueAtTime(value: number, startTime: number): globalThis.AudioParam {
158
+ this._setter(value, startTime);
159
+ return this;
160
+ }
161
+
162
+ setValueCurveAtTime(
163
+ _values: Float32Array,
164
+ _startTime: number,
165
+ _duration: number
166
+ ): globalThis.AudioParam {
167
+ console.warn(
168
+ 'setValueCurveAtTime is not implemented for pitch correction mode'
169
+ );
170
+ return this;
171
+ }
172
+ }
173
+
174
+ type DefaultSource = globalThis.AudioBufferSourceNode;
175
+
176
+ type IAudioBufferSourceNode = DefaultSource | IStretcherNode;
177
+
178
+ declare global {
179
+ interface Window {
180
+ [globalTag]: (
181
+ audioContext: globalThis.BaseAudioContext
182
+ ) => Promise<IStretcherNode>;
183
+ }
184
+ }
185
+
186
+ export default class AudioBufferSourceNode<
187
+ T extends IAudioBufferSourceNode = DefaultSource,
188
+ > extends AudioScheduledSourceNode {
189
+ private _pitchCorrection: boolean;
190
+ readonly playbackRate: AudioParam;
191
+ readonly detune: AudioParam;
192
+
193
+ private _loop: boolean = false;
194
+ private _loopStart: number = -1;
195
+ private _loopEnd: number = -1;
196
+
197
+ private _buffer: AudioBuffer | null = null;
198
+
199
+ constructor(context: BaseAudioContext, node: T, pitchCorrection: boolean) {
16
200
  super(context, node);
17
201
 
18
- this.detune = new AudioParam(node.detune);
19
- this.playbackRate = new AudioParam(node.playbackRate);
202
+ this._pitchCorrection = pitchCorrection;
203
+
204
+ if (pitchCorrection) {
205
+ this.detune = new AudioParam(
206
+ new IStretcherNodeAudioParam(
207
+ 0,
208
+ this.setDetune.bind(this),
209
+ 'a-rate',
210
+ -1200,
211
+ 1200,
212
+ 0
213
+ )
214
+ );
215
+
216
+ this.playbackRate = new AudioParam(
217
+ new IStretcherNodeAudioParam(
218
+ 1,
219
+ this.setPlaybackRate.bind(this),
220
+ 'a-rate',
221
+ 0,
222
+ Infinity,
223
+ 1
224
+ )
225
+ );
226
+ } else {
227
+ this.detune = new AudioParam((node as DefaultSource).detune);
228
+ this.playbackRate = new AudioParam((node as DefaultSource).playbackRate);
229
+ }
230
+ }
231
+
232
+ private isStretcherNode() {
233
+ return this._pitchCorrection;
234
+ }
235
+
236
+ private asStretcher(): IStretcherNode {
237
+ return this.node as IStretcherNode;
238
+ }
239
+
240
+ private asBufferSource(): DefaultSource {
241
+ return this.node as DefaultSource;
242
+ }
243
+
244
+ public setDetune(value: number, when: number = 0): void {
245
+ if (!this.isStretcherNode() || !this.hasBeenStarted) {
246
+ return;
247
+ }
248
+
249
+ this.asStretcher().schedule({
250
+ semitones: Math.floor(clamp(value / 100, -12, 12)),
251
+ output: when,
252
+ });
253
+ }
254
+
255
+ public setPlaybackRate(value: number, when: number = 0): void {
256
+ if (!this.isStretcherNode() || !this.hasBeenStarted) {
257
+ return;
258
+ }
259
+
260
+ this.asStretcher().schedule({
261
+ rate: value,
262
+ output: when,
263
+ });
20
264
  }
21
265
 
22
266
  public get buffer(): AudioBuffer | null {
23
- const buffer = (this.node as globalThis.AudioBufferSourceNode).buffer;
267
+ if (this.isStretcherNode()) {
268
+ return this._buffer;
269
+ }
270
+
271
+ const buffer = this.asBufferSource().buffer;
24
272
 
25
273
  if (!buffer) {
26
274
  return null;
@@ -30,46 +278,83 @@ export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
30
278
  }
31
279
 
32
280
  public set buffer(buffer: AudioBuffer | null) {
281
+ if (this.isStretcherNode()) {
282
+ this._buffer = buffer;
283
+
284
+ const stretcher = this.asStretcher();
285
+ stretcher.dropBuffers();
286
+
287
+ if (!buffer) {
288
+ return;
289
+ }
290
+
291
+ const channelArrays: Float32Array[] = [];
292
+
293
+ for (let i = 0; i < buffer.numberOfChannels; i++) {
294
+ channelArrays.push(buffer.getChannelData(i));
295
+ }
296
+
297
+ stretcher.addBuffers(channelArrays);
298
+ return;
299
+ }
300
+
33
301
  if (!buffer) {
34
- (this.node as globalThis.AudioBufferSourceNode | null) = null;
302
+ this.asBufferSource().buffer = null;
35
303
  return;
36
304
  }
37
305
 
38
- (this.node as globalThis.AudioBufferSourceNode).buffer = buffer.buffer;
306
+ this.asBufferSource().buffer = buffer.buffer;
39
307
  }
40
308
 
41
309
  public get loop(): boolean {
42
- return (this.node as globalThis.AudioBufferSourceNode).loop;
310
+ if (this.isStretcherNode()) {
311
+ return this._loop;
312
+ }
313
+
314
+ return this.asBufferSource().loop;
43
315
  }
44
316
 
45
317
  public set loop(value: boolean) {
46
- (this.node as globalThis.AudioBufferSourceNode).loop = value;
318
+ if (this.isStretcherNode()) {
319
+ this._loop = value;
320
+ return;
321
+ }
322
+
323
+ this.asBufferSource().loop = value;
47
324
  }
48
325
 
49
326
  public get loopStart(): number {
50
- return (this.node as globalThis.AudioBufferSourceNode).loopStart;
327
+ if (this.isStretcherNode()) {
328
+ return this._loopStart;
329
+ }
330
+
331
+ return this.asBufferSource().loopStart;
51
332
  }
52
333
 
53
334
  public set loopStart(value: number) {
54
- (this.node as globalThis.AudioBufferSourceNode).loopStart = value;
335
+ if (this.isStretcherNode()) {
336
+ this._loopStart = value;
337
+ return;
338
+ }
339
+
340
+ this.asBufferSource().loopStart = value;
55
341
  }
56
342
 
57
343
  public get loopEnd(): number {
58
- return (this.node as globalThis.AudioBufferSourceNode).loopEnd;
59
- }
344
+ if (this.isStretcherNode()) {
345
+ return this._loopEnd;
346
+ }
60
347
 
61
- public set loopEnd(value: number) {
62
- (this.node as globalThis.AudioBufferSourceNode).loopEnd = value;
348
+ return this.asBufferSource().loopEnd;
63
349
  }
64
350
 
65
- public get timeStretch(): TimeStretchType {
66
- return 'linear';
67
- }
351
+ public set loopEnd(value: number) {
352
+ if (this.isStretcherNode()) {
353
+ this._loopEnd = value;
354
+ return;
355
+ }
68
356
 
69
- public set timeStretch(value: TimeStretchType) {
70
- console.log(
71
- 'React Native Audio API: setting timeStretch is not supported on web'
72
- );
357
+ this.asBufferSource().loopEnd = value;
73
358
  }
74
359
 
75
360
  public start(when?: number, offset?: number, duration?: number): void {
@@ -96,10 +381,51 @@ export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
96
381
  }
97
382
 
98
383
  this.hasBeenStarted = true;
99
- (this.node as globalThis.AudioBufferSourceNode).start(
100
- when,
384
+
385
+ if (!this.isStretcherNode()) {
386
+ this.asBufferSource().start(when, offset, duration);
387
+ return;
388
+ }
389
+
390
+ const startAt =
391
+ !when || when < this.context.currentTime
392
+ ? this.context.currentTime
393
+ : when;
394
+
395
+ if (this.loop && this._loopStart !== -1 && this._loopEnd !== -1) {
396
+ this.asStretcher().schedule({
397
+ loopStart: this._loopStart,
398
+ loopEnd: this._loopEnd,
399
+ });
400
+ }
401
+
402
+ this.asStretcher().start(
403
+ startAt,
101
404
  offset,
102
- duration
405
+ duration,
406
+ this.playbackRate.value,
407
+ Math.floor(clamp(this.detune.value / 100, -12, 12))
103
408
  );
104
409
  }
410
+
411
+ public stop(when: number = 0): void {
412
+ if (when < 0) {
413
+ throw new RangeError(
414
+ `when must be a finite non-negative number: ${when}`
415
+ );
416
+ }
417
+
418
+ if (!this.hasBeenStarted) {
419
+ throw new InvalidStateError(
420
+ 'Cannot call stop without calling start first'
421
+ );
422
+ }
423
+
424
+ if (!this.isStretcherNode()) {
425
+ this.asBufferSource().stop(when);
426
+ return;
427
+ }
428
+
429
+ this.asStretcher().stop(when);
430
+ }
105
431
  }
@@ -2,6 +2,7 @@ import {
2
2
  ContextState,
3
3
  PeriodicWaveConstraints,
4
4
  AudioContextOptions,
5
+ AudioBufferSourceNodeOptions,
5
6
  } from '../types';
6
7
  import { InvalidAccessError, NotSupportedError } from '../errors';
7
8
  import BaseAudioContext from './BaseAudioContext';
@@ -14,7 +15,6 @@ import GainNode from './GainNode';
14
15
  import OscillatorNode from './OscillatorNode';
15
16
  import PeriodicWave from './PeriodicWave';
16
17
  import StereoPannerNode from './StereoPannerNode';
17
- import StretcherNode from './StretcherNode';
18
18
 
19
19
  import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm';
20
20
 
@@ -61,8 +61,22 @@ export default class AudioContext implements BaseAudioContext {
61
61
  return new BiquadFilterNode(this, this.context.createBiquadFilter());
62
62
  }
63
63
 
64
- createBufferSource(): AudioBufferSourceNode {
65
- return new AudioBufferSourceNode(this, this.context.createBufferSource());
64
+ async createBufferSource(
65
+ options?: AudioBufferSourceNodeOptions
66
+ ): Promise<AudioBufferSourceNode> {
67
+ if (!options || !options.pitchCorrection) {
68
+ return new AudioBufferSourceNode(
69
+ this,
70
+ this.context.createBufferSource(),
71
+ false
72
+ );
73
+ }
74
+
75
+ await globalWasmPromise;
76
+
77
+ const wasmStretch = await window[globalTag](this.context);
78
+
79
+ return new AudioBufferSourceNode(this, wasmStretch, true);
66
80
  }
67
81
 
68
82
  createBuffer(
@@ -94,8 +108,8 @@ export default class AudioContext implements BaseAudioContext {
94
108
  }
95
109
 
96
110
  createPeriodicWave(
97
- real: number[],
98
- imag: number[],
111
+ real: Float32Array,
112
+ imag: Float32Array,
99
113
  constraints?: PeriodicWaveConstraints
100
114
  ): PeriodicWave {
101
115
  if (real.length !== imag.length) {
@@ -113,14 +127,6 @@ export default class AudioContext implements BaseAudioContext {
113
127
  return new AnalyserNode(this, this.context.createAnalyser());
114
128
  }
115
129
 
116
- async createStretcher(): Promise<StretcherNode> {
117
- await globalWasmPromise;
118
-
119
- const wasmStretch = await window[globalTag](this.context);
120
-
121
- return new StretcherNode(this, wasmStretch);
122
- }
123
-
124
130
  async decodeAudioDataSource(source: string): Promise<AudioBuffer> {
125
131
  const arrayBuffer = await fetch(source).then((response) =>
126
132
  response.arrayBuffer()
@@ -84,7 +84,7 @@ export default class AudioParam {
84
84
  }
85
85
 
86
86
  public setValueCurveAtTime(
87
- values: number[],
87
+ values: Float32Array,
88
88
  startTime: number,
89
89
  duration: number
90
90
  ): AudioParam {
@@ -104,11 +104,7 @@ export default class AudioParam {
104
104
  throw new InvalidStateError(`values must contain at least two values`);
105
105
  }
106
106
 
107
- this.param.setValueCurveAtTime(
108
- new Float32Array(values),
109
- startTime,
110
- duration
111
- );
107
+ this.param.setValueCurveAtTime(values, startTime, duration);
112
108
 
113
109
  return this;
114
110
  }
@@ -21,15 +21,15 @@ export default interface BaseAudioContext {
21
21
  createGain(): GainNode;
22
22
  createStereoPanner(): StereoPannerNode;
23
23
  createBiquadFilter(): BiquadFilterNode;
24
- createBufferSource(): AudioBufferSourceNode;
24
+ createBufferSource(): Promise<AudioBufferSourceNode>;
25
25
  createBuffer(
26
26
  numOfChannels: number,
27
27
  length: number,
28
28
  sampleRate: number
29
29
  ): AudioBuffer;
30
30
  createPeriodicWave(
31
- real: number[],
32
- imag: number[],
31
+ real: Float32Array,
32
+ imag: Float32Array,
33
33
  constraints?: PeriodicWaveConstraints
34
34
  ): PeriodicWave;
35
35
  createAnalyser(): AnalyserNode;
@@ -30,9 +30,9 @@ export default class BiquadFilterNode extends AudioNode {
30
30
  }
31
31
 
32
32
  public getFrequencyResponse(
33
- frequencyArray: number[],
34
- magResponseOutput: number[],
35
- phaseResponseOutput: number[]
33
+ frequencyArray: Float32Array,
34
+ magResponseOutput: Float32Array,
35
+ phaseResponseOutput: Float32Array
36
36
  ) {
37
37
  if (
38
38
  frequencyArray.length !== magResponseOutput.length ||
@@ -42,21 +42,11 @@ export default class BiquadFilterNode extends AudioNode {
42
42
  `The lengths of the arrays are not the same frequencyArray: ${frequencyArray.length}, magResponseOutput: ${magResponseOutput.length}, phaseResponseOutput: ${phaseResponseOutput.length}`
43
43
  );
44
44
  }
45
- const magData = new Float32Array(magResponseOutput);
46
- const phaseData = new Float32Array(phaseResponseOutput);
47
45
 
48
46
  (this.node as globalThis.BiquadFilterNode).getFrequencyResponse(
49
- new Float32Array(frequencyArray),
50
- magData,
51
- phaseData
47
+ frequencyArray,
48
+ magResponseOutput,
49
+ phaseResponseOutput
52
50
  );
53
-
54
- for (let i = 0; i < magData.length; i++) {
55
- magResponseOutput[i] = magData[i];
56
- }
57
-
58
- for (let i = 0; i < phaseData.length; i++) {
59
- phaseResponseOutput[i] = phaseData[i];
60
- }
61
51
  }
62
52
  }
@@ -14,7 +14,7 @@ The current input time, within the sample buffer. You can change how often this
14
14
 
15
15
  ### `stretch.schedule({...})`
16
16
 
17
- This adds a scheduled change, removing any scheduled changes occuring after this one. The object properties are:
17
+ This adds a scheduled change, removing any scheduled changes occurring after this one. The object properties are:
18
18
 
19
19
  - `output` (seconds): audio context time for this change. The node compensates for its own latency, but this means you might want to schedule some things ahead of time, otherwise you'll have a softer transition as it catches up.
20
20
  - `active` (bool): processing audio