@k13engineering/rubberband 0.0.2 → 0.0.3
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/dist/lib/index.d.ts +27 -0
- package/dist/lib/index.js +125 -0
- package/dist/lib/rubberband.d.ts +2 -0
- package/dist/lib/rubberband.js +14 -0
- package/package.json +5 -2
package/dist/lib/index.d.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type TRubberbandAudioData = {
|
|
2
|
+
planes: Float32Array[];
|
|
3
|
+
};
|
|
4
|
+
declare const createRubberbandWrapper: ({ sampleRate, channelCount, maxBufferSizeInFrames }: {
|
|
5
|
+
sampleRate: number;
|
|
6
|
+
channelCount: number;
|
|
7
|
+
maxBufferSizeInFrames: number;
|
|
8
|
+
}) => {
|
|
9
|
+
requestPitchScale: ({ pitchScale }: {
|
|
10
|
+
pitchScale: number;
|
|
11
|
+
}) => void;
|
|
12
|
+
requestTimeRatio: ({ timeRatio }: {
|
|
13
|
+
timeRatio: number;
|
|
14
|
+
}) => void;
|
|
15
|
+
samplesRequired: () => any;
|
|
16
|
+
process: ({ audioData }: {
|
|
17
|
+
audioData: TRubberbandAudioData;
|
|
18
|
+
}) => void;
|
|
19
|
+
available: () => number;
|
|
20
|
+
latencyInSamples: () => number;
|
|
21
|
+
retrieve: ({ sampleCount }: {
|
|
22
|
+
sampleCount: number;
|
|
23
|
+
}) => TRubberbandAudioData;
|
|
24
|
+
};
|
|
25
|
+
type TRubberbandWrapper = ReturnType<typeof createRubberbandWrapper>;
|
|
26
|
+
export { createRubberbandWrapper };
|
|
27
|
+
export type { TRubberbandWrapper };
|
package/dist/lib/index.js
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { RubberBandInterface } from "rubberband-wasm";
|
|
2
|
+
import { loadRubberbandWasm } from "./rubberband.js";
|
|
3
|
+
|
|
4
|
+
/*type TRubberbandAudioData = {
|
|
5
|
+
planes: Float32Array[];
|
|
6
|
+
};*/
|
|
7
|
+
|
|
8
|
+
const rubberbandWasm = await loadRubberbandWasm();
|
|
9
|
+
|
|
10
|
+
const wasm = await WebAssembly.compile(rubberbandWasm);
|
|
11
|
+
const rubberbandApi = await RubberBandInterface.initialize(wasm);
|
|
12
|
+
|
|
13
|
+
const OptionProcessRealTime = 1;
|
|
14
|
+
|
|
15
|
+
const createRubberbandWrapper = ({
|
|
16
|
+
sampleRate,
|
|
17
|
+
channelCount,
|
|
18
|
+
maxBufferSizeInFrames
|
|
19
|
+
}/*: {
|
|
20
|
+
sampleRate: number;
|
|
21
|
+
channelCount: number;
|
|
22
|
+
maxBufferSizeInFrames: number;
|
|
23
|
+
}*/) => {
|
|
24
|
+
|
|
25
|
+
const channelArrayPtr = rubberbandApi.malloc(channelCount * 4);
|
|
26
|
+
const channelDataPtrs/*: number[]*/ = [];
|
|
27
|
+
for (let channel = 0; channel < channelCount; channel += 1) {
|
|
28
|
+
const bufferPtr = rubberbandApi.malloc(maxBufferSizeInFrames * 4);
|
|
29
|
+
channelDataPtrs.push(bufferPtr);
|
|
30
|
+
rubberbandApi.memWritePtr(channelArrayPtr + channel * 4, bufferPtr);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rb = rubberbandApi.rubberband_new(sampleRate, channelCount, OptionProcessRealTime, 1, 1);
|
|
34
|
+
|
|
35
|
+
const requestPitchScale = ({ pitchScale }/*: { pitchScale: number }*/) => {
|
|
36
|
+
rubberbandApi.rubberband_set_pitch_scale(rb, pitchScale);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const requestTimeRatio = ({ timeRatio }/*: { timeRatio: number }*/) => {
|
|
40
|
+
rubberbandApi.rubberband_set_time_ratio(rb, timeRatio);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const samplesRequired = () => {
|
|
44
|
+
return rubberbandApi.rubberband_get_samples_required(rb);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const process = ({ audioData }/*: { audioData: TRubberbandAudioData }*/) => {
|
|
48
|
+
|
|
49
|
+
const firstPlane = audioData.planes[0];
|
|
50
|
+
const sampleCount = firstPlane.length;
|
|
51
|
+
|
|
52
|
+
audioData.planes.forEach((plane, channelIndex) => {
|
|
53
|
+
|
|
54
|
+
if (plane.length > maxBufferSizeInFrames) {
|
|
55
|
+
throw new Error("plane length exceeds max buffer size");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const channelDataPtr = channelDataPtrs[channelIndex];
|
|
59
|
+
rubberbandApi.memWrite(channelDataPtr, plane);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
rubberbandApi.rubberband_process(rb, channelArrayPtr, sampleCount, 0);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const available = ()/*: number*/ => {
|
|
66
|
+
return rubberbandApi.rubberband_available(rb);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const latencyInSamples = ()/*: number*/ => {
|
|
70
|
+
return rubberbandApi.rubberband_get_latency(rb);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// eslint-disable-next-line complexity
|
|
74
|
+
const retrieve = ({ sampleCount }/*: { sampleCount: number }*/)/*: TRubberbandAudioData*/ => {
|
|
75
|
+
|
|
76
|
+
if (sampleCount > maxBufferSizeInFrames) {
|
|
77
|
+
throw Error(`requested sample count ${sampleCount} exceeds max buffer size ${maxBufferSizeInFrames}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (sampleCount > available()) {
|
|
81
|
+
throw Error(`look before you leap: requested ${sampleCount} samples but only ${available()} available`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const samplesReceived = rubberbandApi.rubberband_retrieve(rb, channelArrayPtr, sampleCount);
|
|
85
|
+
|
|
86
|
+
if (samplesReceived > sampleCount) {
|
|
87
|
+
throw Error("BUG: received more samples than requested");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (samplesReceived < sampleCount) {
|
|
91
|
+
throw Error("BUG: received fewer samples than requested");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let planes/*: Float32Array[]*/ = [];
|
|
95
|
+
|
|
96
|
+
channelDataPtrs.forEach((channelDataPtr) => {
|
|
97
|
+
const plane = rubberbandApi.memReadF32(channelDataPtr, samplesReceived);
|
|
98
|
+
planes = [...planes, plane];
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
planes
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
requestPitchScale,
|
|
108
|
+
requestTimeRatio,
|
|
109
|
+
samplesRequired,
|
|
110
|
+
process,
|
|
111
|
+
available,
|
|
112
|
+
latencyInSamples,
|
|
113
|
+
retrieve
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/*type TRubberbandWrapper = ReturnType<typeof createRubberbandWrapper>;*/
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
createRubberbandWrapper
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/*export type {
|
|
124
|
+
TRubberbandWrapper
|
|
125
|
+
};*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* c8 ignore start */
|
|
2
|
+
import { loadAsBlob } from "esm-resource";
|
|
3
|
+
|
|
4
|
+
const loadRubberbandWasm = async () => {
|
|
5
|
+
const rubberbandBlob = await loadAsBlob({ importMeta: import.meta, filepath: "../../node_modules/rubberband-wasm/dist/rubberband.wasm" });
|
|
6
|
+
const rubberbandWasmArrayBuffer = await rubberbandBlob.arrayBuffer();
|
|
7
|
+
const rubberbandWasm = new Uint8Array(rubberbandWasmArrayBuffer);
|
|
8
|
+
return rubberbandWasm;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
loadRubberbandWasm
|
|
13
|
+
};
|
|
14
|
+
/* c8 ignore end */
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.3",
|
|
3
3
|
"name": "@k13engineering/rubberband",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rubberband wrapper for Node.js",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"npm-check-updates": "^19.3.1",
|
|
18
18
|
"typescript-eslint": "^8.53.0"
|
|
19
19
|
},
|
|
20
|
-
"dependencies": {
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"esm-resource": "^0.0.3",
|
|
22
|
+
"rubberband-wasm": "^3.3.0"
|
|
23
|
+
},
|
|
21
24
|
"publishConfig": {
|
|
22
25
|
"access": "public"
|
|
23
26
|
},
|