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.
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +7 -6
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +25 -24
- package/common/cpp/audioapi/HostObjects/AnalyserNodeHostObject.h +17 -41
- package/common/cpp/audioapi/HostObjects/AudioBufferHostObject.h +22 -32
- package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +2 -17
- package/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +14 -4
- package/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +5 -8
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +13 -9
- package/common/cpp/audioapi/HostObjects/BiquadFilterNodeHostObject.h +8 -19
- package/common/cpp/audioapi/core/AudioContext.cpp +14 -4
- package/common/cpp/audioapi/core/AudioContext.h +2 -2
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +4 -2
- package/common/cpp/audioapi/core/BaseAudioContext.h +1 -1
- package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +5 -6
- package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +4 -3
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +5 -11
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +3 -31
- package/lib/module/api.js +1 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +1 -2
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AnalyserNode.js.map +1 -1
- package/lib/module/core/AudioBuffer.js.map +1 -1
- package/lib/module/core/AudioBufferSourceNode.js +0 -6
- package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/core/AudioParam.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +3 -2
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/BiquadFilterNode.js.map +1 -1
- package/lib/module/utils/index.js +6 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/module/web-core/AnalyserNode.js +4 -20
- package/lib/module/web-core/AnalyserNode.js.map +1 -1
- package/lib/module/web-core/AudioBuffer.js +2 -6
- package/lib/module/web-core/AudioBuffer.js.map +1 -1
- package/lib/module/web-core/AudioBufferSourceNode.js +161 -21
- package/lib/module/web-core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/web-core/AudioContext.js +7 -8
- package/lib/module/web-core/AudioContext.js.map +1 -1
- package/lib/module/web-core/AudioParam.js +1 -1
- package/lib/module/web-core/AudioParam.js.map +1 -1
- package/lib/module/web-core/BiquadFilterNode.js +1 -9
- package/lib/module/web-core/BiquadFilterNode.js.map +1 -1
- package/lib/module/web-core/custom/signalsmithStretch/README.md +1 -1
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +2 -2
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs.map +1 -1
- package/lib/typescript/api.d.ts +1 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +1 -2
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/core/AnalyserNode.d.ts +4 -4
- package/lib/typescript/core/AnalyserNode.d.ts.map +1 -1
- package/lib/typescript/core/AudioBuffer.d.ts +3 -3
- package/lib/typescript/core/AudioBuffer.d.ts.map +1 -1
- package/lib/typescript/core/AudioBufferSourceNode.d.ts +0 -3
- package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/AudioParam.d.ts +1 -1
- package/lib/typescript/core/AudioParam.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +3 -3
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/BiquadFilterNode.d.ts +1 -1
- package/lib/typescript/core/BiquadFilterNode.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +12 -13
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +3 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +2 -0
- package/lib/typescript/utils/index.d.ts.map +1 -0
- package/lib/typescript/web-core/AnalyserNode.d.ts +4 -4
- package/lib/typescript/web-core/AnalyserNode.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioBuffer.d.ts +2 -2
- package/lib/typescript/web-core/AudioBuffer.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioBufferSourceNode.d.ts +58 -6
- package/lib/typescript/web-core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioContext.d.ts +3 -5
- package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioParam.d.ts +1 -1
- package/lib/typescript/web-core/AudioParam.d.ts.map +1 -1
- package/lib/typescript/web-core/BaseAudioContext.d.ts +2 -2
- package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/BiquadFilterNode.d.ts +1 -1
- package/lib/typescript/web-core/BiquadFilterNode.d.ts.map +1 -1
- package/package.json +8 -8
- package/scripts/setup-rn-audio-api-web.js +58 -0
- package/src/api.ts +0 -1
- package/src/api.web.ts +0 -2
- package/src/core/AnalyserNode.ts +4 -4
- package/src/core/AudioBuffer.ts +3 -3
- package/src/core/AudioBufferSourceNode.ts +0 -9
- package/src/core/AudioParam.ts +1 -1
- package/src/core/BaseAudioContext.ts +16 -5
- package/src/core/BiquadFilterNode.ts +3 -3
- package/src/interfaces.ts +14 -16
- package/src/types.ts +3 -1
- package/src/utils/index.ts +3 -0
- package/src/web-core/AnalyserNode.tsx +8 -30
- package/src/web-core/AudioBuffer.tsx +4 -14
- package/src/web-core/AudioBufferSourceNode.tsx +357 -31
- package/src/web-core/AudioContext.tsx +19 -13
- package/src/web-core/AudioParam.tsx +2 -6
- package/src/web-core/BaseAudioContext.tsx +3 -3
- package/src/web-core/BiquadFilterNode.tsx +6 -16
- package/src/web-core/custom/signalsmithStretch/README.md +1 -1
- package/src/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +4 -3
- package/common/cpp/audioapi/core/types/TimeStretchType.h +0 -7
- package/lib/module/web-core/StretcherNode.js +0 -64
- package/lib/module/web-core/StretcherNode.js.map +0 -1
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js +0 -822
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js.map +0 -1
- package/lib/typescript/web-core/StretcherNode.d.ts +0 -45
- package/lib/typescript/web-core/StretcherNode.d.ts.map +0 -1
- package/scripts/setup-custom-wasm.js +0 -104
- package/src/web-core/StretcherNode.tsx +0 -125
- 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:
|
|
33
|
-
|
|
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:
|
|
43
|
-
|
|
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:
|
|
53
|
-
|
|
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:
|
|
62
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
6
|
+
import AudioScheduledSourceNode from './AudioScheduledSourceNode';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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.
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
302
|
+
this.asBufferSource().buffer = null;
|
|
35
303
|
return;
|
|
36
304
|
}
|
|
37
305
|
|
|
38
|
-
|
|
306
|
+
this.asBufferSource().buffer = buffer.buffer;
|
|
39
307
|
}
|
|
40
308
|
|
|
41
309
|
public get loop(): boolean {
|
|
42
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
59
|
-
|
|
344
|
+
if (this.isStretcherNode()) {
|
|
345
|
+
return this._loopEnd;
|
|
346
|
+
}
|
|
60
347
|
|
|
61
|
-
|
|
62
|
-
(this.node as globalThis.AudioBufferSourceNode).loopEnd = value;
|
|
348
|
+
return this.asBufferSource().loopEnd;
|
|
63
349
|
}
|
|
64
350
|
|
|
65
|
-
public
|
|
66
|
-
|
|
67
|
-
|
|
351
|
+
public set loopEnd(value: number) {
|
|
352
|
+
if (this.isStretcherNode()) {
|
|
353
|
+
this._loopEnd = value;
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
68
356
|
|
|
69
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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(
|
|
65
|
-
|
|
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:
|
|
98
|
-
imag:
|
|
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:
|
|
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:
|
|
32
|
-
imag:
|
|
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:
|
|
34
|
-
magResponseOutput:
|
|
35
|
-
phaseResponseOutput:
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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
|