react-native-audio-api 0.4.12-beta.4 → 0.4.12
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/cpp/audioapi/CMakeLists.txt +2 -3
- package/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp +3 -3
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +10 -11
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +1 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +0 -1
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +1 -3
- package/common/cpp/audioapi/HostObjects/AnalyserNodeHostObject.h +24 -16
- package/common/cpp/audioapi/HostObjects/AudioBufferHostObject.h +4 -0
- package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +20 -4
- package/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +3 -2
- package/common/cpp/audioapi/HostObjects/AudioScheduledSourceNodeHostObject.h +32 -2
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +14 -21
- package/common/cpp/audioapi/HostObjects/OscillatorNodeHostObject.h +4 -2
- package/common/cpp/audioapi/core/AudioNode.cpp +2 -2
- package/common/cpp/audioapi/core/AudioParam.cpp +1 -1
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +4 -12
- package/common/cpp/audioapi/core/BaseAudioContext.h +2 -4
- package/common/cpp/audioapi/core/Constants.h +8 -33
- package/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +42 -45
- package/common/cpp/audioapi/core/analysis/AnalyserNode.h +8 -6
- package/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +1 -1
- package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +12 -8
- package/common/cpp/audioapi/core/effects/GainNode.cpp +4 -3
- package/common/cpp/audioapi/core/effects/PeriodicWave.cpp +32 -49
- package/common/cpp/audioapi/core/effects/PeriodicWave.h +8 -3
- package/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +3 -3
- package/common/cpp/audioapi/core/sources/AudioBuffer.cpp +9 -2
- package/common/cpp/audioapi/core/sources/AudioBuffer.h +5 -2
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +72 -35
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +41 -8
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +18 -6
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +7 -0
- package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +12 -3
- package/common/cpp/audioapi/core/sources/OscillatorNode.h +1 -0
- package/common/cpp/audioapi/core/types/TimeStretchType.h +7 -0
- package/common/cpp/audioapi/dsp/AudioUtils.cpp +2 -2
- package/common/cpp/audioapi/dsp/AudioUtils.h +2 -2
- package/common/cpp/audioapi/dsp/FFT.cpp +41 -0
- package/common/cpp/audioapi/dsp/FFT.h +29 -0
- package/common/cpp/audioapi/dsp/VectorMath.cpp +3 -3
- package/common/cpp/audioapi/dsp/VectorMath.h +2 -2
- package/common/cpp/audioapi/dsp/Windows.cpp +80 -0
- package/common/cpp/audioapi/dsp/Windows.h +95 -0
- package/{android/src/main/cpp/audioapi/android/libs → common/cpp/audioapi/libs/pffft}/pffft.c +1 -1
- package/common/cpp/audioapi/libs/{dsp → signalsmith-stretch}/delay.h +9 -11
- package/common/cpp/audioapi/libs/{dsp → signalsmith-stretch}/fft.h +6 -7
- package/common/cpp/audioapi/libs/{dsp → signalsmith-stretch}/perf.h +0 -2
- package/common/cpp/audioapi/libs/{signalsmith-stretch.h → signalsmith-stretch/signalsmith-stretch.h} +3 -4
- package/common/cpp/audioapi/libs/{dsp → signalsmith-stretch}/spectral.h +10 -13
- package/common/cpp/audioapi/{core/utils → utils}/AudioArray.cpp +5 -5
- package/common/cpp/audioapi/{core/utils → utils}/AudioBus.cpp +29 -29
- package/ios/audioapi/ios/core/AudioDecoder.mm +3 -3
- package/ios/audioapi/ios/core/AudioPlayer.h +5 -2
- package/ios/audioapi/ios/core/AudioPlayer.m +9 -5
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -0
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +12 -10
- package/lib/module/api.js +30 -0
- package/lib/module/api.js.map +1 -0
- package/lib/module/{index.web.js → api.web.js} +4 -2
- package/lib/module/api.web.js.map +1 -0
- package/lib/module/core/AudioBufferSourceNode.js +6 -0
- package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/core/AudioScheduledSourceNode.js +5 -0
- package/lib/module/core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +0 -4
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/index.js +1 -28
- package/lib/module/index.js.map +1 -1
- package/lib/module/web-core/AudioBuffer.js +1 -1
- package/lib/module/web-core/AudioBuffer.js.map +1 -1
- package/lib/module/web-core/AudioBufferSourceNode.js +6 -0
- package/lib/module/web-core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/web-core/AudioContext.js +7 -0
- package/lib/module/web-core/AudioContext.js.map +1 -1
- package/lib/module/web-core/AudioScheduledSourceNode.js +8 -0
- package/lib/module/web-core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/module/web-core/StretcherNode.js +64 -0
- package/lib/module/web-core/StretcherNode.js.map +1 -0
- package/lib/module/web-core/custom/LoadCustomWasm.js +33 -0
- package/lib/module/web-core/custom/LoadCustomWasm.js.map +1 -0
- package/lib/module/web-core/custom/index.js +4 -0
- package/lib/module/web-core/custom/index.js.map +1 -0
- package/lib/module/web-core/custom/signalsmithStretch/LICENSE.txt +21 -0
- package/lib/module/web-core/custom/signalsmithStretch/README.md +46 -0
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js +822 -0
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.js.map +1 -0
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +826 -0
- package/lib/module/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs.map +1 -0
- package/lib/typescript/api.d.ts +20 -0
- package/lib/typescript/api.d.ts.map +1 -0
- package/lib/typescript/{index.web.d.ts → api.web.d.ts} +4 -2
- package/lib/typescript/api.web.d.ts.map +1 -0
- package/lib/typescript/core/AudioBufferSourceNode.d.ts +3 -0
- package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/AudioScheduledSourceNode.d.ts +1 -0
- package/lib/typescript/core/AudioScheduledSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +0 -2
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -20
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +3 -6
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +1 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioBuffer.d.ts +1 -1
- package/lib/typescript/web-core/AudioBuffer.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioBufferSourceNode.d.ts +3 -0
- package/lib/typescript/web-core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioContext.d.ts +2 -0
- package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts +1 -0
- package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts.map +1 -1
- package/lib/typescript/web-core/StretcherNode.d.ts +45 -0
- package/lib/typescript/web-core/StretcherNode.d.ts.map +1 -0
- package/lib/typescript/web-core/custom/LoadCustomWasm.d.ts +5 -0
- package/lib/typescript/web-core/custom/LoadCustomWasm.d.ts.map +1 -0
- package/lib/typescript/web-core/custom/index.d.ts +2 -0
- package/lib/typescript/web-core/custom/index.d.ts.map +1 -0
- package/package.json +8 -5
- package/scripts/setup-custom-wasm.js +104 -0
- package/src/api.ts +51 -0
- package/src/{index.web.ts → api.web.ts} +4 -0
- package/src/core/AudioBufferSourceNode.ts +9 -0
- package/src/core/AudioScheduledSourceNode.ts +5 -0
- package/src/core/BaseAudioContext.ts +0 -5
- package/src/index.ts +1 -51
- package/src/interfaces.ts +3 -6
- package/src/types.ts +2 -0
- package/src/web-core/AudioBuffer.tsx +2 -2
- package/src/web-core/AudioBufferSourceNode.tsx +11 -0
- package/src/web-core/AudioContext.tsx +11 -0
- package/src/web-core/AudioScheduledSourceNode.tsx +9 -0
- package/src/web-core/StretcherNode.tsx +125 -0
- package/src/web-core/custom/LoadCustomWasm.ts +39 -0
- package/src/web-core/custom/index.ts +1 -0
- package/src/web-core/custom/signalsmithStretch/LICENSE.txt +21 -0
- package/src/web-core/custom/signalsmithStretch/README.md +46 -0
- package/src/web-core/custom/signalsmithStretch/SignalsmithStretch.js +945 -0
- package/src/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +949 -0
- package/common/cpp/audioapi/HostObjects/StretcherNodeHostObject.h +0 -35
- package/common/cpp/audioapi/core/effects/StretcherNode.cpp +0 -94
- package/common/cpp/audioapi/core/effects/StretcherNode.h +0 -35
- package/common/cpp/audioapi/dsp/FFTFrame.cpp +0 -100
- package/common/cpp/audioapi/dsp/FFTFrame.h +0 -74
- package/common/cpp/audioapi/libs/dsp/common.h +0 -47
- package/common/cpp/audioapi/libs/dsp/windows.h +0 -219
- package/lib/module/core/StretcherNode.js +0 -12
- package/lib/module/core/StretcherNode.js.map +0 -1
- package/lib/module/index.web.js.map +0 -1
- package/lib/typescript/core/StretcherNode.d.ts +0 -10
- package/lib/typescript/core/StretcherNode.d.ts.map +0 -1
- package/lib/typescript/index.web.d.ts.map +0 -1
- package/src/core/StretcherNode.ts +0 -15
- /package/common/cpp/audioapi/libs/{miniaudio.h → miniaudio/miniaudio.h} +0 -0
- /package/{android/src/main/cpp/audioapi/android/libs → common/cpp/audioapi/libs/pffft}/pffft.h +0 -0
- /package/common/cpp/audioapi/{core/utils → utils}/AudioArray.h +0 -0
- /package/common/cpp/audioapi/{core/utils → utils}/AudioBus.h +0 -0
package/src/api.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import NativeAudioAPIModule from './specs/NativeAudioAPIModule';
|
|
2
|
+
import type { IAudioContext } from './interfaces';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable no-var */
|
|
5
|
+
declare global {
|
|
6
|
+
var createAudioContext: (sampleRate?: number) => IAudioContext;
|
|
7
|
+
}
|
|
8
|
+
/* eslint-disable no-var */
|
|
9
|
+
|
|
10
|
+
if (global.createAudioContext == null) {
|
|
11
|
+
if (!NativeAudioAPIModule) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Failed to install react-native-audio-api: The native module could not be found.`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
NativeAudioAPIModule.install();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { default as AudioBuffer } from './core/AudioBuffer';
|
|
21
|
+
export { default as AudioBufferSourceNode } from './core/AudioBufferSourceNode';
|
|
22
|
+
export { default as AudioContext } from './core/AudioContext';
|
|
23
|
+
export { default as AudioDestinationNode } from './core/AudioDestinationNode';
|
|
24
|
+
export { default as AudioNode } from './core/AudioNode';
|
|
25
|
+
export { default as AnalyserNode } from './core/AnalyserNode';
|
|
26
|
+
export { default as AudioParam } from './core/AudioParam';
|
|
27
|
+
export { default as AudioScheduledSourceNode } from './core/AudioScheduledSourceNode';
|
|
28
|
+
export { default as BaseAudioContext } from './core/BaseAudioContext';
|
|
29
|
+
export { default as BiquadFilterNode } from './core/BiquadFilterNode';
|
|
30
|
+
export { default as GainNode } from './core/GainNode';
|
|
31
|
+
export { default as OscillatorNode } from './core/OscillatorNode';
|
|
32
|
+
export { default as StereoPannerNode } from './core/StereoPannerNode';
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
OscillatorType,
|
|
36
|
+
BiquadFilterType,
|
|
37
|
+
ChannelCountMode,
|
|
38
|
+
ChannelInterpretation,
|
|
39
|
+
ContextState,
|
|
40
|
+
WindowType,
|
|
41
|
+
PeriodicWaveConstraints,
|
|
42
|
+
TimeStretchType,
|
|
43
|
+
} from './types';
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
IndexSizeError,
|
|
47
|
+
InvalidAccessError,
|
|
48
|
+
InvalidStateError,
|
|
49
|
+
RangeError,
|
|
50
|
+
NotSupportedError,
|
|
51
|
+
} from './errors';
|
|
@@ -11,6 +11,9 @@ export { default as BiquadFilterNode } from './web-core/BiquadFilterNode';
|
|
|
11
11
|
export { default as GainNode } from './web-core/GainNode';
|
|
12
12
|
export { default as OscillatorNode } from './web-core/OscillatorNode';
|
|
13
13
|
export { default as StereoPannerNode } from './web-core/StereoPannerNode';
|
|
14
|
+
export { default as StretcherNode } from './web-core/StretcherNode';
|
|
15
|
+
|
|
16
|
+
export * from './web-core/custom';
|
|
14
17
|
|
|
15
18
|
export {
|
|
16
19
|
OscillatorType,
|
|
@@ -20,6 +23,7 @@ export {
|
|
|
20
23
|
ContextState,
|
|
21
24
|
WindowType,
|
|
22
25
|
PeriodicWaveConstraints,
|
|
26
|
+
TimeStretchType,
|
|
23
27
|
} from './types';
|
|
24
28
|
|
|
25
29
|
export {
|
|
@@ -4,6 +4,7 @@ import BaseAudioContext from './BaseAudioContext';
|
|
|
4
4
|
import AudioBuffer from './AudioBuffer';
|
|
5
5
|
import AudioParam from './AudioParam';
|
|
6
6
|
import { InvalidStateError, RangeError } from '../errors';
|
|
7
|
+
import { TimeStretchType } from '../types';
|
|
7
8
|
|
|
8
9
|
export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
|
|
9
10
|
readonly playbackRate: AudioParam;
|
|
@@ -57,6 +58,14 @@ export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
|
|
|
57
58
|
(this.node as IAudioBufferSourceNode).loopEnd = value;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
public get timeStretch(): TimeStretchType {
|
|
62
|
+
return (this.node as IAudioBufferSourceNode).timeStretch;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public set timeStretch(value: TimeStretchType) {
|
|
66
|
+
(this.node as IAudioBufferSourceNode).timeStretch = value;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
public start(when: number = 0, offset: number = 0, duration?: number): void {
|
|
61
70
|
if (when < 0) {
|
|
62
71
|
throw new RangeError(
|
|
@@ -35,4 +35,9 @@ export default class AudioScheduledSourceNode extends AudioNode {
|
|
|
35
35
|
|
|
36
36
|
(this.node as IAudioScheduledSourceNode).stop(when);
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line accessor-pairs
|
|
40
|
+
public set onended(callback: (arg?: number) => void) {
|
|
41
|
+
(this.node as IAudioScheduledSourceNode).onended = callback;
|
|
42
|
+
}
|
|
38
43
|
}
|
|
@@ -9,7 +9,6 @@ import AudioBufferSourceNode from './AudioBufferSourceNode';
|
|
|
9
9
|
import AudioBuffer from './AudioBuffer';
|
|
10
10
|
import PeriodicWave from './PeriodicWave';
|
|
11
11
|
import AnalyserNode from './AnalyserNode';
|
|
12
|
-
import StretcherNode from './StretcherNode';
|
|
13
12
|
import { InvalidAccessError, NotSupportedError } from '../errors';
|
|
14
13
|
|
|
15
14
|
export default class BaseAudioContext {
|
|
@@ -101,10 +100,6 @@ export default class BaseAudioContext {
|
|
|
101
100
|
return new AnalyserNode(this, this.context.createAnalyser());
|
|
102
101
|
}
|
|
103
102
|
|
|
104
|
-
createStretcher(): StretcherNode {
|
|
105
|
-
return new StretcherNode(this, this.context.createStretcher());
|
|
106
|
-
}
|
|
107
|
-
|
|
108
103
|
async decodeAudioDataSource(sourcePath: string): Promise<AudioBuffer> {
|
|
109
104
|
// Remove the file:// prefix if it exists
|
|
110
105
|
if (sourcePath.startsWith('file://')) {
|
package/src/index.ts
CHANGED
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { IAudioContext } from './interfaces';
|
|
3
|
-
|
|
4
|
-
/* eslint-disable no-var */
|
|
5
|
-
declare global {
|
|
6
|
-
var createAudioContext: (sampleRate?: number) => IAudioContext;
|
|
7
|
-
}
|
|
8
|
-
/* eslint-disable no-var */
|
|
9
|
-
|
|
10
|
-
if (global.createAudioContext == null) {
|
|
11
|
-
if (!NativeAudioAPIModule) {
|
|
12
|
-
throw new Error(
|
|
13
|
-
`Failed to install react-native-audio-api: The native module could not be found.`
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
NativeAudioAPIModule.install();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { default as AudioBuffer } from './core/AudioBuffer';
|
|
21
|
-
export { default as AudioBufferSourceNode } from './core/AudioBufferSourceNode';
|
|
22
|
-
export { default as AudioContext } from './core/AudioContext';
|
|
23
|
-
export { default as AudioDestinationNode } from './core/AudioDestinationNode';
|
|
24
|
-
export { default as AudioNode } from './core/AudioNode';
|
|
25
|
-
export { default as AnalyserNode } from './core/AnalyserNode';
|
|
26
|
-
export { default as AudioParam } from './core/AudioParam';
|
|
27
|
-
export { default as AudioScheduledSourceNode } from './core/AudioScheduledSourceNode';
|
|
28
|
-
export { default as BaseAudioContext } from './core/BaseAudioContext';
|
|
29
|
-
export { default as BiquadFilterNode } from './core/BiquadFilterNode';
|
|
30
|
-
export { default as GainNode } from './core/GainNode';
|
|
31
|
-
export { default as OscillatorNode } from './core/OscillatorNode';
|
|
32
|
-
export { default as StereoPannerNode } from './core/StereoPannerNode';
|
|
33
|
-
export { default as StretcherNode } from './core/StretcherNode';
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
OscillatorType,
|
|
37
|
-
BiquadFilterType,
|
|
38
|
-
ChannelCountMode,
|
|
39
|
-
ChannelInterpretation,
|
|
40
|
-
ContextState,
|
|
41
|
-
WindowType,
|
|
42
|
-
PeriodicWaveConstraints,
|
|
43
|
-
} from './types';
|
|
44
|
-
|
|
45
|
-
export {
|
|
46
|
-
IndexSizeError,
|
|
47
|
-
InvalidAccessError,
|
|
48
|
-
InvalidStateError,
|
|
49
|
-
RangeError,
|
|
50
|
-
NotSupportedError,
|
|
51
|
-
} from './errors';
|
|
1
|
+
export * from './api';
|
package/src/interfaces.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ChannelCountMode,
|
|
6
6
|
ChannelInterpretation,
|
|
7
7
|
WindowType,
|
|
8
|
+
TimeStretchType,
|
|
8
9
|
} from './types';
|
|
9
10
|
|
|
10
11
|
export interface AudioAPIInstaller {
|
|
@@ -33,7 +34,6 @@ export interface IBaseAudioContext {
|
|
|
33
34
|
disableNormalization: boolean
|
|
34
35
|
) => IPeriodicWave;
|
|
35
36
|
createAnalyser: () => IAnalyserNode;
|
|
36
|
-
createStretcher: () => IStretcherNode;
|
|
37
37
|
decodeAudioDataSource: (sourcePath: string) => Promise<IAudioBuffer>;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -82,6 +82,7 @@ export interface IAudioDestinationNode extends IAudioNode {}
|
|
|
82
82
|
export interface IAudioScheduledSourceNode extends IAudioNode {
|
|
83
83
|
start(when?: number): void;
|
|
84
84
|
stop: (when: number) => void;
|
|
85
|
+
onended: (arg?: number) => void | null;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
export interface IOscillatorNode extends IAudioScheduledSourceNode {
|
|
@@ -99,6 +100,7 @@ export interface IAudioBufferSourceNode extends IAudioScheduledSourceNode {
|
|
|
99
100
|
loopEnd: number;
|
|
100
101
|
detune: IAudioParam;
|
|
101
102
|
playbackRate: IAudioParam;
|
|
103
|
+
timeStretch: TimeStretchType;
|
|
102
104
|
|
|
103
105
|
start: (when?: number, offset?: number, duration?: number) => void;
|
|
104
106
|
}
|
|
@@ -160,8 +162,3 @@ export interface IAnalyserNode extends IAudioNode {
|
|
|
160
162
|
getFloatTimeDomainData: (array: number[]) => void;
|
|
161
163
|
getByteTimeDomainData: (array: number[]) => void;
|
|
162
164
|
}
|
|
163
|
-
|
|
164
|
-
export interface IStretcherNode extends IAudioNode {
|
|
165
|
-
readonly rate: IAudioParam;
|
|
166
|
-
readonly semitones: IAudioParam;
|
|
167
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -17,14 +17,14 @@ export default class AudioBuffer {
|
|
|
17
17
|
this.numberOfChannels = buffer.numberOfChannels;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
public getChannelData(channel: number):
|
|
20
|
+
public getChannelData(channel: number): Float32Array {
|
|
21
21
|
if (channel < 0 || channel >= this.numberOfChannels) {
|
|
22
22
|
throw new IndexSizeError(
|
|
23
23
|
`The channel number provided (${channel}) is outside the range [0, ${this.numberOfChannels - 1}]`
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
return
|
|
27
|
+
return this.buffer.getChannelData(channel);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
public copyFromChannel(
|
|
@@ -3,6 +3,7 @@ import AudioParam from './AudioParam';
|
|
|
3
3
|
import AudioBuffer from './AudioBuffer';
|
|
4
4
|
import { InvalidStateError, RangeError } from '../errors';
|
|
5
5
|
import BaseAudioContext from './BaseAudioContext';
|
|
6
|
+
import { TimeStretchType } from '../types';
|
|
6
7
|
|
|
7
8
|
export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
|
|
8
9
|
readonly playbackRate: AudioParam;
|
|
@@ -61,6 +62,16 @@ export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
|
|
|
61
62
|
(this.node as globalThis.AudioBufferSourceNode).loopEnd = value;
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
public get timeStretch(): TimeStretchType {
|
|
66
|
+
return 'linear';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public set timeStretch(value: TimeStretchType) {
|
|
70
|
+
console.log(
|
|
71
|
+
'React Native Audio API: setting timeStretch is not supported on web'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
public start(when?: number, offset?: number, duration?: number): void {
|
|
65
76
|
if (when && when < 0) {
|
|
66
77
|
throw new RangeError(
|
|
@@ -14,6 +14,9 @@ import GainNode from './GainNode';
|
|
|
14
14
|
import OscillatorNode from './OscillatorNode';
|
|
15
15
|
import PeriodicWave from './PeriodicWave';
|
|
16
16
|
import StereoPannerNode from './StereoPannerNode';
|
|
17
|
+
import StretcherNode from './StretcherNode';
|
|
18
|
+
|
|
19
|
+
import { globalWasmPromise, globalTag } from './custom/LoadCustomWasm';
|
|
17
20
|
|
|
18
21
|
export default class AudioContext implements BaseAudioContext {
|
|
19
22
|
readonly context: globalThis.AudioContext;
|
|
@@ -110,6 +113,14 @@ export default class AudioContext implements BaseAudioContext {
|
|
|
110
113
|
return new AnalyserNode(this, this.context.createAnalyser());
|
|
111
114
|
}
|
|
112
115
|
|
|
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
|
+
|
|
113
124
|
async decodeAudioDataSource(source: string): Promise<AudioBuffer> {
|
|
114
125
|
const arrayBuffer = await fetch(source).then((response) =>
|
|
115
126
|
response.arrayBuffer()
|
|
@@ -34,4 +34,13 @@ export default class AudioScheduledSourceNode extends AudioNode {
|
|
|
34
34
|
|
|
35
35
|
(this.node as globalThis.AudioScheduledSourceNode).stop(when);
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line accessor-pairs
|
|
39
|
+
public set onended(callback: (arg?: number) => void) {
|
|
40
|
+
const eventCallback = (_event: Event) => {
|
|
41
|
+
callback();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
(this.node as globalThis.AudioScheduledSourceNode).onended = eventCallback;
|
|
45
|
+
}
|
|
37
46
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import BaseAudioContext from './BaseAudioContext';
|
|
2
|
+
import AudioBuffer from './AudioBuffer';
|
|
3
|
+
import AudioNode from './AudioNode';
|
|
4
|
+
|
|
5
|
+
import { globalTag } from './custom/LoadCustomWasm';
|
|
6
|
+
|
|
7
|
+
interface ScheduleOptions {
|
|
8
|
+
rate?: number;
|
|
9
|
+
active?: boolean;
|
|
10
|
+
outputTime?: number;
|
|
11
|
+
semitones?: number;
|
|
12
|
+
loopStart?: number;
|
|
13
|
+
loopEnd?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare class IStretcherNode {
|
|
17
|
+
addBuffers(channels: Float32Array[]): void;
|
|
18
|
+
|
|
19
|
+
schedule(options: ScheduleOptions): void;
|
|
20
|
+
|
|
21
|
+
start(
|
|
22
|
+
when?: number,
|
|
23
|
+
offset?: number,
|
|
24
|
+
duration?: number,
|
|
25
|
+
rate?: number,
|
|
26
|
+
semitones?: number
|
|
27
|
+
): void;
|
|
28
|
+
|
|
29
|
+
stop(when?: number): void;
|
|
30
|
+
|
|
31
|
+
connect(destination: globalThis.AudioNode): void;
|
|
32
|
+
disconnect(destination: globalThis.AudioNode): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare global {
|
|
36
|
+
interface Window {
|
|
37
|
+
[globalTag]: (
|
|
38
|
+
audioContext: globalThis.BaseAudioContext
|
|
39
|
+
) => Promise<IStretcherNode>;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default class StretcherNode extends AudioNode {
|
|
44
|
+
_buffer: AudioBuffer | null = null;
|
|
45
|
+
|
|
46
|
+
_playbackRate: number = 1;
|
|
47
|
+
_loopStart: number = -1;
|
|
48
|
+
_loopEnd: number = -1;
|
|
49
|
+
_isPlaying = false;
|
|
50
|
+
|
|
51
|
+
constructor(context: BaseAudioContext, node: IStretcherNode) {
|
|
52
|
+
super(context, node as unknown as globalThis.AudioNode);
|
|
53
|
+
this._buffer = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public get buffer(): AudioBuffer | null {
|
|
57
|
+
return this._buffer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public set buffer(buffer: AudioBuffer) {
|
|
61
|
+
this._buffer = buffer;
|
|
62
|
+
const channelArrays = [];
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < buffer.numberOfChannels; i += 1) {
|
|
65
|
+
channelArrays.push(buffer.getChannelData(i));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
(this.node as unknown as IStretcherNode).addBuffers(channelArrays);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public get playbackRate(): number {
|
|
72
|
+
return this._playbackRate;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public set playbackRate(value: number) {
|
|
76
|
+
this._playbackRate = value;
|
|
77
|
+
|
|
78
|
+
if (this._isPlaying) {
|
|
79
|
+
(this.node as unknown as IStretcherNode).schedule({ rate: value });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public get loopStart(): number {
|
|
84
|
+
return this._loopStart;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public set loopStart(value: number) {
|
|
88
|
+
this._loopStart = value;
|
|
89
|
+
|
|
90
|
+
(this.node as unknown as IStretcherNode).schedule({ loopStart: value });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public get loopEnd(): number {
|
|
94
|
+
return this._loopEnd;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public set loopEnd(value: number) {
|
|
98
|
+
this._loopEnd = value;
|
|
99
|
+
|
|
100
|
+
(this.node as unknown as IStretcherNode).schedule({ loopEnd: value });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public start(
|
|
104
|
+
when?: number,
|
|
105
|
+
offset?: number,
|
|
106
|
+
duration?: number,
|
|
107
|
+
rate?: number,
|
|
108
|
+
semitones?: number
|
|
109
|
+
): void {
|
|
110
|
+
this._isPlaying = true;
|
|
111
|
+
|
|
112
|
+
(this.node as unknown as IStretcherNode).start(
|
|
113
|
+
when,
|
|
114
|
+
offset,
|
|
115
|
+
duration,
|
|
116
|
+
rate,
|
|
117
|
+
semitones
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public stop(when?: number): void {
|
|
122
|
+
this._isPlaying = false;
|
|
123
|
+
(this.node as unknown as IStretcherNode).stop(when);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const globalTag = '__rnaaCstStretch';
|
|
2
|
+
const eventTitle = 'rnaaCstStretchLoaded';
|
|
3
|
+
|
|
4
|
+
export let globalWasmPromise: Promise<void> | null = null;
|
|
5
|
+
|
|
6
|
+
const LoadCustomWasm = async () => {
|
|
7
|
+
if (typeof window === 'undefined') {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (globalWasmPromise) {
|
|
12
|
+
return globalWasmPromise;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
globalWasmPromise = new Promise<void>((resolve) => {
|
|
16
|
+
const loadScript = document.createElement('script');
|
|
17
|
+
document.head.appendChild(loadScript);
|
|
18
|
+
loadScript.type = 'module';
|
|
19
|
+
|
|
20
|
+
loadScript.textContent = `
|
|
21
|
+
import SignalsmithStretch from '/signalsmithStretch.mjs';
|
|
22
|
+
window.${globalTag} = SignalsmithStretch;
|
|
23
|
+
window.postMessage('${eventTitle}');
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
function onScriptLoaded(event: MessageEvent<string>) {
|
|
27
|
+
if (event.data !== eventTitle) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resolve();
|
|
32
|
+
window.removeEventListener('message', onScriptLoaded);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
window.addEventListener('message', onScriptLoaded);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default LoadCustomWasm;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as LoadCustomWasm } from './LoadCustomWasm';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Geraint Luff / Signalsmith Audio Ltd.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Signalsmith Stretch Web
|
|
2
|
+
|
|
3
|
+
This is an official release of the Signalsmith Stretch library for Web Audio, using WASM/AudioWorklet. It includes both plain `.js` (UMD), and ES6 `.mjs` versions.
|
|
4
|
+
|
|
5
|
+
## How to use it
|
|
6
|
+
|
|
7
|
+
Call `SignalsmithStretch(audioContext, ?channelOptions)` from the main thread. This returns a Promise which resolves to an `AudioNode`, with extra methods attached to it.
|
|
8
|
+
|
|
9
|
+
It can operate either on live/streaming input, or on a sample buffer you load into it.
|
|
10
|
+
|
|
11
|
+
### `stretch.inputTime`
|
|
12
|
+
|
|
13
|
+
The current input time, within the sample buffer. You can change how often this is updated, with an optional callback function, using `stretch.setUpdateInterval(seconds, ?callback)`.
|
|
14
|
+
|
|
15
|
+
### `stretch.schedule({...})`
|
|
16
|
+
|
|
17
|
+
This adds a scheduled change, removing any scheduled changes occuring after this one. The object properties are:
|
|
18
|
+
|
|
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
|
+
- `active` (bool): processing audio
|
|
21
|
+
- `input` (seconds): position in input buffer
|
|
22
|
+
- `rate` (number): playback rate, e.g. 0.5 == half speed
|
|
23
|
+
- `semitones` (number): pitch shift
|
|
24
|
+
- `loopStart` / `loopEnd`: sets a section of the input to auto-loop. Disabled if both are set to the same value.
|
|
25
|
+
|
|
26
|
+
### `stretch.start(?when)` / `stretch.stop(?when)`
|
|
27
|
+
|
|
28
|
+
Starts/stops playback or processing, immediately or at some future time. These are convenience methods which call `.schedule(...)` under the hood.
|
|
29
|
+
|
|
30
|
+
`.start()` actually has more parameters, presenting a similar interface to [AudioBufferSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start).
|
|
31
|
+
|
|
32
|
+
### `stretch.addBuffers([...])`
|
|
33
|
+
|
|
34
|
+
This adds buffers to the end of the current input. Buffers should be typed arrays of equal length, one per channel.
|
|
35
|
+
|
|
36
|
+
It can be called multiple times, and the new buffers are inserted after the existing ones, which lets you start playback before the entire audio is loaded. It returns (as a Promise) the new end time for the stored input, in seconds.
|
|
37
|
+
|
|
38
|
+
### `stretch.dropBuffers()`
|
|
39
|
+
|
|
40
|
+
This drops all input buffers, and resets the input buffer start time to 0.
|
|
41
|
+
|
|
42
|
+
### `stretch.dropBuffers(toSeconds)`
|
|
43
|
+
|
|
44
|
+
This drops all input buffers before the given time. It returns (as a Promise) the an object with the current input buffer extent: `{start: ..., end: ...}`.
|
|
45
|
+
|
|
46
|
+
This can be useful when processing streams or very long audio files, letting the Stretch node release old buffers once that section of the input will no longer be played back.
|