@libraz/libsonare 1.0.4 → 1.2.0
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/README.md +299 -2
- package/dist/index.d.ts +1649 -202
- package/dist/index.js +2101 -994
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +2 -0
- package/dist/sonare-rt.js +2 -0
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +447 -0
- package/dist/worklet.js +2078 -0
- package/dist/worklet.js.map +1 -0
- package/package.json +24 -27
- package/src/index.ts +3670 -0
- package/src/public_types.ts +852 -0
- package/src/sonare-rt.d.ts +93 -0
- package/src/sonare.js.d.ts +1332 -0
- package/src/stream_types.ts +133 -0
- package/src/wasm_types.ts +1248 -0
- package/src/worklet.ts +2140 -0
- package/README.npm.md +0 -114
- package/dist/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1013 +1,2120 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* // Full analysis
|
|
17
|
-
* const result = analyze(samples, sampleRate);
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Public Types
|
|
22
|
-
// ============================================================================
|
|
23
|
-
/**
|
|
24
|
-
* Pitch class enum (C=0, C#=1, ..., B=11)
|
|
25
|
-
*/
|
|
26
|
-
export const PitchClass = {
|
|
27
|
-
C: 0,
|
|
28
|
-
Cs: 1,
|
|
29
|
-
D: 2,
|
|
30
|
-
Ds: 3,
|
|
31
|
-
E: 4,
|
|
32
|
-
F: 5,
|
|
33
|
-
Fs: 6,
|
|
34
|
-
G: 7,
|
|
35
|
-
Gs: 8,
|
|
36
|
-
A: 9,
|
|
37
|
-
As: 10,
|
|
38
|
-
B: 11,
|
|
1
|
+
// src/public_types.ts
|
|
2
|
+
var PitchClass = {
|
|
3
|
+
C: 0,
|
|
4
|
+
Cs: 1,
|
|
5
|
+
D: 2,
|
|
6
|
+
Ds: 3,
|
|
7
|
+
E: 4,
|
|
8
|
+
F: 5,
|
|
9
|
+
Fs: 6,
|
|
10
|
+
G: 7,
|
|
11
|
+
Gs: 8,
|
|
12
|
+
A: 9,
|
|
13
|
+
As: 10,
|
|
14
|
+
B: 11
|
|
39
15
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
16
|
+
var Mode = {
|
|
17
|
+
Major: 0,
|
|
18
|
+
Minor: 1,
|
|
19
|
+
Dorian: 2,
|
|
20
|
+
Phrygian: 3,
|
|
21
|
+
Lydian: 4,
|
|
22
|
+
Mixolydian: 5,
|
|
23
|
+
Locrian: 6
|
|
46
24
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Dominant7: 4,
|
|
56
|
-
Major7: 5,
|
|
57
|
-
Minor7: 6,
|
|
58
|
-
Sus2: 7,
|
|
59
|
-
Sus4: 8,
|
|
25
|
+
var KeyProfile = {
|
|
26
|
+
KrumhanslSchmuckler: 0,
|
|
27
|
+
Temperley: 1,
|
|
28
|
+
Shaath: 2,
|
|
29
|
+
FaraldoEDMT: 3,
|
|
30
|
+
FaraldoEDMA: 4,
|
|
31
|
+
FaraldoEDMM: 5,
|
|
32
|
+
BellmanBudge: 6
|
|
60
33
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
34
|
+
var ChordQuality = {
|
|
35
|
+
Major: 0,
|
|
36
|
+
Minor: 1,
|
|
37
|
+
Diminished: 2,
|
|
38
|
+
Augmented: 3,
|
|
39
|
+
Dominant7: 4,
|
|
40
|
+
Major7: 5,
|
|
41
|
+
Minor7: 6,
|
|
42
|
+
Sus2: 7,
|
|
43
|
+
Sus4: 8,
|
|
44
|
+
Unknown: 9,
|
|
45
|
+
Add9: 10,
|
|
46
|
+
MinorAdd9: 11,
|
|
47
|
+
Dim7: 12,
|
|
48
|
+
HalfDim7: 13,
|
|
49
|
+
Major9: 14,
|
|
50
|
+
Dominant9: 15,
|
|
51
|
+
Sus2Add4: 16
|
|
72
52
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
53
|
+
var SectionType = {
|
|
54
|
+
Intro: 0,
|
|
55
|
+
Verse: 1,
|
|
56
|
+
PreChorus: 2,
|
|
57
|
+
Chorus: 3,
|
|
58
|
+
Bridge: 4,
|
|
59
|
+
Instrumental: 5,
|
|
60
|
+
Outro: 6,
|
|
61
|
+
Unknown: 7
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var EXPECTED_ENGINE_ABI_VERSION = 2;
|
|
66
|
+
function automationCurveCode(curve) {
|
|
67
|
+
switch (curve) {
|
|
68
|
+
case "linear":
|
|
69
|
+
return 0;
|
|
70
|
+
case "exponential":
|
|
71
|
+
return 1;
|
|
72
|
+
case "hold":
|
|
73
|
+
return 2;
|
|
74
|
+
case "s-curve":
|
|
75
|
+
return 3;
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Invalid automation curve: ${curve}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function panLawCode(panLaw) {
|
|
81
|
+
if (typeof panLaw === "number") {
|
|
82
|
+
return panLaw;
|
|
83
|
+
}
|
|
84
|
+
switch (panLaw) {
|
|
85
|
+
case "const4.5dB":
|
|
86
|
+
return 1;
|
|
87
|
+
case "const6dB":
|
|
88
|
+
return 2;
|
|
89
|
+
case "linear0dB":
|
|
90
|
+
return 3;
|
|
91
|
+
default:
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function meterTapCode(tap) {
|
|
96
|
+
return tap === "preFader" || tap === 0 ? 0 : 1;
|
|
97
|
+
}
|
|
98
|
+
function sendTimingCode(timing) {
|
|
99
|
+
return timing === "preFader" || timing === 0 ? 0 : 1;
|
|
100
|
+
}
|
|
101
|
+
var module = null;
|
|
102
|
+
var initPromise = null;
|
|
103
|
+
async function init(options) {
|
|
104
|
+
if (module) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (initPromise) {
|
|
105
108
|
return initPromise;
|
|
109
|
+
}
|
|
110
|
+
initPromise = (async () => {
|
|
111
|
+
try {
|
|
112
|
+
const createModule = (await import("./sonare.js")).default;
|
|
113
|
+
module = await createModule(options);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
initPromise = null;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
return initPromise;
|
|
106
120
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
*/
|
|
110
|
-
export function isInitialized() {
|
|
111
|
-
return module !== null;
|
|
121
|
+
function isInitialized() {
|
|
122
|
+
return module !== null;
|
|
112
123
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
function version() {
|
|
125
|
+
if (!module) {
|
|
126
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
127
|
+
}
|
|
128
|
+
return module.version();
|
|
129
|
+
}
|
|
130
|
+
function engineAbiVersion() {
|
|
131
|
+
if (!module) {
|
|
132
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
133
|
+
}
|
|
134
|
+
return module.engineAbiVersion();
|
|
135
|
+
}
|
|
136
|
+
function engineCapabilities() {
|
|
137
|
+
const abiVersion = engineAbiVersion();
|
|
138
|
+
const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === "function";
|
|
139
|
+
const atomics = typeof globalThis.Atomics === "object";
|
|
140
|
+
const audioWorklet = typeof AudioWorkletNode !== "undefined" || typeof globalThis.AudioWorkletProcessor !== "undefined";
|
|
141
|
+
return {
|
|
142
|
+
engineAbiVersion: abiVersion,
|
|
143
|
+
expectedEngineAbiVersion: EXPECTED_ENGINE_ABI_VERSION,
|
|
144
|
+
abiCompatible: abiVersion === EXPECTED_ENGINE_ABI_VERSION,
|
|
145
|
+
sharedArrayBuffer,
|
|
146
|
+
atomics,
|
|
147
|
+
audioWorklet,
|
|
148
|
+
mode: sharedArrayBuffer && atomics ? "sab" : "postMessage"
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
var RealtimeEngine = class {
|
|
152
|
+
constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024) {
|
|
117
153
|
if (!module) {
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
154
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
155
|
+
}
|
|
156
|
+
const capabilities = engineCapabilities();
|
|
157
|
+
if (!capabilities.abiCompatible) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Engine ABI mismatch: wasm=${capabilities.engineAbiVersion}, expected=${capabilities.expectedEngineAbiVersion}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
this.native = new module.RealtimeEngine(
|
|
163
|
+
sampleRate,
|
|
164
|
+
maxBlockSize,
|
|
165
|
+
commandCapacity,
|
|
166
|
+
telemetryCapacity
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
prepare(sampleRate, maxBlockSize, commandCapacity = 1024, telemetryCapacity = 1024) {
|
|
170
|
+
this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);
|
|
171
|
+
}
|
|
172
|
+
/** Queue a sample-accurate parameter change (engine kSetParam). */
|
|
173
|
+
setParameter(paramId, value, renderFrame = -1) {
|
|
174
|
+
this.native.setParameter(paramId, value, renderFrame);
|
|
175
|
+
}
|
|
176
|
+
/** Queue a smoothed parameter change (engine kSetParamSmoothed). */
|
|
177
|
+
setParameterSmoothed(paramId, value, renderFrame = -1) {
|
|
178
|
+
this.native.setParameterSmoothed(paramId, value, renderFrame);
|
|
179
|
+
}
|
|
180
|
+
/** Read back the current transport state snapshot. */
|
|
181
|
+
getTransportState() {
|
|
182
|
+
return this.native.getTransportState();
|
|
183
|
+
}
|
|
184
|
+
play(renderFrame = -1) {
|
|
185
|
+
this.native.play(renderFrame);
|
|
186
|
+
}
|
|
187
|
+
stop(renderFrame = -1) {
|
|
188
|
+
this.native.stop(renderFrame);
|
|
189
|
+
}
|
|
190
|
+
seekSample(timelineSample, renderFrame = -1) {
|
|
191
|
+
this.native.seekSample(timelineSample, renderFrame);
|
|
192
|
+
}
|
|
193
|
+
seekPpq(ppq, renderFrame = -1) {
|
|
194
|
+
this.native.seekPpq(ppq, renderFrame);
|
|
195
|
+
}
|
|
196
|
+
setTempo(bpm) {
|
|
197
|
+
this.native.setTempo(bpm);
|
|
198
|
+
}
|
|
199
|
+
setTimeSignature(numerator, denominator) {
|
|
200
|
+
this.native.setTimeSignature(numerator, denominator);
|
|
201
|
+
}
|
|
202
|
+
setLoop(startPpq, endPpq, enabled = true) {
|
|
203
|
+
this.native.setLoop(startPpq, endPpq, enabled);
|
|
204
|
+
}
|
|
205
|
+
addParameter(info) {
|
|
206
|
+
this.native.addParameter(info);
|
|
207
|
+
}
|
|
208
|
+
parameterCount() {
|
|
209
|
+
return this.native.parameterCount();
|
|
210
|
+
}
|
|
211
|
+
parameterInfoByIndex(index) {
|
|
212
|
+
return this.native.parameterInfoByIndex(index);
|
|
213
|
+
}
|
|
214
|
+
parameterInfo(id) {
|
|
215
|
+
return this.native.parameterInfo(id);
|
|
216
|
+
}
|
|
217
|
+
setAutomationLane(paramId, points) {
|
|
218
|
+
this.native.setAutomationLane(paramId, points);
|
|
219
|
+
}
|
|
220
|
+
automationLaneCount() {
|
|
221
|
+
return this.native.automationLaneCount();
|
|
222
|
+
}
|
|
223
|
+
setMarkers(markers) {
|
|
224
|
+
this.native.setMarkers(markers);
|
|
225
|
+
}
|
|
226
|
+
markerCount() {
|
|
227
|
+
return this.native.markerCount();
|
|
228
|
+
}
|
|
229
|
+
markerByIndex(index) {
|
|
230
|
+
return this.native.markerByIndex(index);
|
|
231
|
+
}
|
|
232
|
+
marker(id) {
|
|
233
|
+
return this.native.marker(id);
|
|
234
|
+
}
|
|
235
|
+
seekMarker(markerId, renderFrame = -1) {
|
|
236
|
+
this.native.seekMarker(markerId, renderFrame);
|
|
237
|
+
}
|
|
238
|
+
setLoopFromMarkers(startMarkerId, endMarkerId) {
|
|
239
|
+
this.native.setLoopFromMarkers(startMarkerId, endMarkerId);
|
|
240
|
+
}
|
|
241
|
+
setMetronome(config) {
|
|
242
|
+
this.native.setMetronome(config);
|
|
243
|
+
}
|
|
244
|
+
metronome() {
|
|
245
|
+
return this.native.metronome();
|
|
246
|
+
}
|
|
247
|
+
countInEndSample(startSample, bars) {
|
|
248
|
+
return Number(this.native.countInEndSample(startSample, bars));
|
|
249
|
+
}
|
|
250
|
+
setGraph(spec) {
|
|
251
|
+
this.native.setGraph(spec);
|
|
252
|
+
}
|
|
253
|
+
graphNodeCount() {
|
|
254
|
+
return this.native.graphNodeCount();
|
|
255
|
+
}
|
|
256
|
+
graphConnectionCount() {
|
|
257
|
+
return this.native.graphConnectionCount();
|
|
258
|
+
}
|
|
259
|
+
setClips(clips) {
|
|
260
|
+
this.native.setClips(clips);
|
|
261
|
+
}
|
|
262
|
+
clipCount() {
|
|
263
|
+
return this.native.clipCount();
|
|
264
|
+
}
|
|
265
|
+
setCaptureBuffer(numChannels, capacityFrames) {
|
|
266
|
+
this.native.setCaptureBuffer(numChannels, capacityFrames);
|
|
267
|
+
}
|
|
268
|
+
armCapture(armed = true) {
|
|
269
|
+
this.native.armCapture(armed);
|
|
270
|
+
}
|
|
271
|
+
setCapturePunch(startSample, endSample, enabled = true) {
|
|
272
|
+
this.native.setCapturePunch(startSample, endSample, enabled);
|
|
273
|
+
}
|
|
274
|
+
resetCapture() {
|
|
275
|
+
this.native.resetCapture();
|
|
276
|
+
}
|
|
277
|
+
captureStatus() {
|
|
278
|
+
return this.native.captureStatus();
|
|
279
|
+
}
|
|
280
|
+
capturedAudio() {
|
|
281
|
+
return this.native.capturedAudio();
|
|
282
|
+
}
|
|
283
|
+
process(channels) {
|
|
284
|
+
return this.native.process(channels);
|
|
285
|
+
}
|
|
286
|
+
processWithMonitor(channels) {
|
|
287
|
+
return this.native.processWithMonitor(channels);
|
|
288
|
+
}
|
|
289
|
+
renderOffline(channels, blockSize = 128) {
|
|
290
|
+
return this.native.renderOffline(channels, blockSize);
|
|
291
|
+
}
|
|
292
|
+
bounceOffline(options) {
|
|
293
|
+
return this.native.bounceOffline(options);
|
|
294
|
+
}
|
|
295
|
+
freezeOffline(options) {
|
|
296
|
+
return this.native.freezeOffline(options);
|
|
297
|
+
}
|
|
298
|
+
drainTelemetry(maxRecords = 1024) {
|
|
299
|
+
return this.native.drainTelemetry(maxRecords);
|
|
300
|
+
}
|
|
301
|
+
drainMeterTelemetry(maxRecords = 1024) {
|
|
302
|
+
return this.native.drainMeterTelemetry(maxRecords);
|
|
303
|
+
}
|
|
304
|
+
destroy() {
|
|
305
|
+
this.native.delete();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
function detectBpm(samples, sampleRate) {
|
|
309
|
+
if (!module) {
|
|
310
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
311
|
+
}
|
|
312
|
+
return module.detectBpm(samples, sampleRate);
|
|
313
|
+
}
|
|
314
|
+
function detectKey(samples, sampleRate, options = {}) {
|
|
315
|
+
if (!module) {
|
|
316
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
317
|
+
}
|
|
318
|
+
const result = module._detectKeyWithOptions(
|
|
319
|
+
samples,
|
|
320
|
+
sampleRate,
|
|
321
|
+
options.nFft ?? 4096,
|
|
322
|
+
options.hopLength ?? 512,
|
|
323
|
+
options.useHpss ?? false,
|
|
324
|
+
options.loudnessWeighted ?? false,
|
|
325
|
+
options.highPassHz ?? 0,
|
|
326
|
+
keyModeValues(options.modes),
|
|
327
|
+
keyProfileValue(options.profile),
|
|
328
|
+
options.genreHint ?? ""
|
|
329
|
+
);
|
|
330
|
+
return {
|
|
331
|
+
root: result.root,
|
|
332
|
+
mode: result.mode,
|
|
333
|
+
confidence: result.confidence,
|
|
334
|
+
name: result.name,
|
|
335
|
+
shortName: result.shortName
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function convertKeyCandidate(wasm) {
|
|
339
|
+
return {
|
|
340
|
+
key: {
|
|
341
|
+
root: wasm.key.root,
|
|
342
|
+
mode: wasm.key.mode,
|
|
343
|
+
confidence: wasm.key.confidence,
|
|
344
|
+
name: wasm.key.name,
|
|
345
|
+
shortName: wasm.key.shortName
|
|
346
|
+
},
|
|
347
|
+
correlation: wasm.correlation
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function keyModeValues(modes) {
|
|
351
|
+
if (!modes) {
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
354
|
+
if (modes === "major-minor") {
|
|
355
|
+
return [Mode.Major, Mode.Minor];
|
|
356
|
+
}
|
|
357
|
+
if (modes === "all" || modes === "modal") {
|
|
358
|
+
return [
|
|
359
|
+
Mode.Major,
|
|
360
|
+
Mode.Minor,
|
|
361
|
+
Mode.Dorian,
|
|
362
|
+
Mode.Phrygian,
|
|
363
|
+
Mode.Lydian,
|
|
364
|
+
Mode.Mixolydian,
|
|
365
|
+
Mode.Locrian
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
const names = {
|
|
369
|
+
major: Mode.Major,
|
|
370
|
+
minor: Mode.Minor,
|
|
371
|
+
dorian: Mode.Dorian,
|
|
372
|
+
phrygian: Mode.Phrygian,
|
|
373
|
+
lydian: Mode.Lydian,
|
|
374
|
+
mixolydian: Mode.Mixolydian,
|
|
375
|
+
locrian: Mode.Locrian
|
|
376
|
+
};
|
|
377
|
+
return modes.map((mode) => typeof mode === "number" ? mode : names[mode]);
|
|
378
|
+
}
|
|
379
|
+
function keyProfileValue(profile) {
|
|
380
|
+
if (profile === void 0) {
|
|
381
|
+
return -1;
|
|
382
|
+
}
|
|
383
|
+
if (typeof profile === "number") {
|
|
384
|
+
return profile;
|
|
385
|
+
}
|
|
386
|
+
const names = {
|
|
387
|
+
ks: KeyProfile.KrumhanslSchmuckler,
|
|
388
|
+
krumhansl: KeyProfile.KrumhanslSchmuckler,
|
|
389
|
+
temperley: KeyProfile.Temperley,
|
|
390
|
+
shaath: KeyProfile.Shaath,
|
|
391
|
+
keyfinder: KeyProfile.Shaath,
|
|
392
|
+
"faraldo-edmt": KeyProfile.FaraldoEDMT,
|
|
393
|
+
edmt: KeyProfile.FaraldoEDMT,
|
|
394
|
+
"faraldo-edma": KeyProfile.FaraldoEDMA,
|
|
395
|
+
edma: KeyProfile.FaraldoEDMA,
|
|
396
|
+
"faraldo-edmm": KeyProfile.FaraldoEDMM,
|
|
397
|
+
edmm: KeyProfile.FaraldoEDMM,
|
|
398
|
+
"bellman-budge": KeyProfile.BellmanBudge,
|
|
399
|
+
bellman: KeyProfile.BellmanBudge
|
|
400
|
+
};
|
|
401
|
+
return names[profile];
|
|
402
|
+
}
|
|
403
|
+
function detectKeyCandidates(samples, sampleRate, options = {}) {
|
|
404
|
+
if (!module) {
|
|
405
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
406
|
+
}
|
|
407
|
+
return module._detectKeyCandidates(
|
|
408
|
+
samples,
|
|
409
|
+
sampleRate,
|
|
410
|
+
options.nFft ?? 4096,
|
|
411
|
+
options.hopLength ?? 512,
|
|
412
|
+
options.useHpss ?? false,
|
|
413
|
+
options.loudnessWeighted ?? false,
|
|
414
|
+
options.highPassHz ?? 0,
|
|
415
|
+
keyModeValues(options.modes),
|
|
416
|
+
keyProfileValue(options.profile),
|
|
417
|
+
options.genreHint ?? ""
|
|
418
|
+
).map(convertKeyCandidate);
|
|
419
|
+
}
|
|
420
|
+
function detectOnsets(samples, sampleRate) {
|
|
421
|
+
if (!module) {
|
|
422
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
423
|
+
}
|
|
424
|
+
return module.detectOnsets(samples, sampleRate);
|
|
425
|
+
}
|
|
426
|
+
function detectBeats(samples, sampleRate) {
|
|
427
|
+
if (!module) {
|
|
428
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
429
|
+
}
|
|
430
|
+
return module.detectBeats(samples, sampleRate);
|
|
431
|
+
}
|
|
432
|
+
function detectDownbeats(samples, sampleRate) {
|
|
433
|
+
if (!module) {
|
|
434
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
435
|
+
}
|
|
436
|
+
return module.detectDownbeats(samples, sampleRate);
|
|
437
|
+
}
|
|
438
|
+
function convertChordAnalysisResult(wasm) {
|
|
439
|
+
return {
|
|
440
|
+
chords: wasm.chords.map((c) => ({
|
|
441
|
+
root: c.root,
|
|
442
|
+
bass: c.bass,
|
|
443
|
+
quality: c.quality,
|
|
444
|
+
start: c.start,
|
|
445
|
+
end: c.end,
|
|
446
|
+
confidence: c.confidence,
|
|
447
|
+
name: c.name
|
|
448
|
+
}))
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function detectChords(samples, sampleRate, options = {}) {
|
|
452
|
+
if (!module) {
|
|
453
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
454
|
+
}
|
|
455
|
+
const result = module.detectChords(
|
|
456
|
+
samples,
|
|
457
|
+
sampleRate,
|
|
458
|
+
options.minDuration ?? 0.3,
|
|
459
|
+
options.smoothingWindow ?? 2,
|
|
460
|
+
options.threshold ?? 0.5,
|
|
461
|
+
options.useTriadsOnly ?? false,
|
|
462
|
+
options.nFft ?? 2048,
|
|
463
|
+
options.hopLength ?? 512,
|
|
464
|
+
options.useBeatSync ?? true,
|
|
465
|
+
options.useHmm ?? false,
|
|
466
|
+
options.hmmBeamWidth ?? 24,
|
|
467
|
+
options.useKeyContext ?? false,
|
|
468
|
+
options.keyRoot ?? PitchClass.C,
|
|
469
|
+
options.keyMode ?? Mode.Major,
|
|
470
|
+
options.detectInversions ?? false,
|
|
471
|
+
chordChromaMethodValue(options.chromaMethod ?? "stft")
|
|
472
|
+
);
|
|
473
|
+
return convertChordAnalysisResult(result);
|
|
474
|
+
}
|
|
475
|
+
function chordChromaMethodValue(method) {
|
|
476
|
+
if (method === "stft") {
|
|
477
|
+
return 0;
|
|
478
|
+
}
|
|
479
|
+
if (method === "nnls") {
|
|
480
|
+
return 1;
|
|
481
|
+
}
|
|
482
|
+
throw new Error(`Invalid chord chroma method: ${method}`);
|
|
483
|
+
}
|
|
484
|
+
function convertAnalysisResult(wasm) {
|
|
485
|
+
const beatTimes = new Float32Array(wasm.beats.length);
|
|
486
|
+
for (let i = 0; i < wasm.beats.length; i++) {
|
|
487
|
+
beatTimes[i] = wasm.beats[i].time;
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
bpm: wasm.bpm,
|
|
491
|
+
bpmConfidence: wasm.bpmConfidence,
|
|
492
|
+
key: {
|
|
493
|
+
root: wasm.key.root,
|
|
494
|
+
mode: wasm.key.mode,
|
|
495
|
+
confidence: wasm.key.confidence,
|
|
496
|
+
name: wasm.key.name,
|
|
497
|
+
shortName: wasm.key.shortName
|
|
498
|
+
},
|
|
499
|
+
timeSignature: wasm.timeSignature,
|
|
500
|
+
beatTimes,
|
|
501
|
+
beats: wasm.beats,
|
|
502
|
+
chords: wasm.chords.map((c) => ({
|
|
503
|
+
root: c.root,
|
|
504
|
+
bass: c.bass,
|
|
505
|
+
quality: c.quality,
|
|
506
|
+
start: c.start,
|
|
507
|
+
end: c.end,
|
|
508
|
+
confidence: c.confidence,
|
|
509
|
+
name: c.name
|
|
510
|
+
})),
|
|
511
|
+
sections: wasm.sections.map((s) => ({
|
|
512
|
+
type: s.type,
|
|
513
|
+
start: s.start,
|
|
514
|
+
end: s.end,
|
|
515
|
+
energyLevel: s.energyLevel,
|
|
516
|
+
confidence: s.confidence,
|
|
517
|
+
name: s.name
|
|
518
|
+
})),
|
|
519
|
+
timbre: wasm.timbre,
|
|
520
|
+
dynamics: wasm.dynamics,
|
|
521
|
+
rhythm: wasm.rhythm,
|
|
522
|
+
form: wasm.form
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function analyze(samples, sampleRate) {
|
|
526
|
+
if (!module) {
|
|
527
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
528
|
+
}
|
|
529
|
+
const result = module.analyze(samples, sampleRate);
|
|
530
|
+
return convertAnalysisResult(result);
|
|
531
|
+
}
|
|
532
|
+
function analyzeImpulseResponse(samples, sampleRate, nOctaveBands = 6) {
|
|
533
|
+
if (!module) {
|
|
534
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
535
|
+
}
|
|
536
|
+
const result = module.analyzeImpulseResponse(
|
|
537
|
+
samples,
|
|
538
|
+
sampleRate,
|
|
539
|
+
nOctaveBands
|
|
540
|
+
);
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
function detectAcoustic(samples, sampleRate, nOctaveBands = 6, nThirdOctaveSubbands = 24, minDecayDb = 30, noiseFloorMarginDb = 10) {
|
|
544
|
+
if (!module) {
|
|
545
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
546
|
+
}
|
|
547
|
+
const result = module.detectAcoustic(
|
|
548
|
+
samples,
|
|
549
|
+
sampleRate,
|
|
550
|
+
nOctaveBands,
|
|
551
|
+
nThirdOctaveSubbands,
|
|
552
|
+
minDecayDb,
|
|
553
|
+
noiseFloorMarginDb
|
|
554
|
+
);
|
|
555
|
+
return result;
|
|
556
|
+
}
|
|
557
|
+
function analyzeWithProgress(samples, sampleRate, onProgress) {
|
|
558
|
+
if (!module) {
|
|
559
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
560
|
+
}
|
|
561
|
+
const result = module.analyzeWithProgress(samples, sampleRate, onProgress);
|
|
562
|
+
return convertAnalysisResult(result);
|
|
563
|
+
}
|
|
564
|
+
function hpss(samples, sampleRate, kernelHarmonic = 31, kernelPercussive = 31) {
|
|
565
|
+
if (!module) {
|
|
566
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
567
|
+
}
|
|
568
|
+
return module.hpss(samples, sampleRate, kernelHarmonic, kernelPercussive);
|
|
569
|
+
}
|
|
570
|
+
function harmonic(samples, sampleRate) {
|
|
571
|
+
if (!module) {
|
|
572
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
573
|
+
}
|
|
574
|
+
return module.harmonic(samples, sampleRate);
|
|
575
|
+
}
|
|
576
|
+
function percussive(samples, sampleRate) {
|
|
577
|
+
if (!module) {
|
|
578
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
579
|
+
}
|
|
580
|
+
return module.percussive(samples, sampleRate);
|
|
581
|
+
}
|
|
582
|
+
function timeStretch(samples, sampleRate, rate) {
|
|
583
|
+
if (!module) {
|
|
584
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
585
|
+
}
|
|
586
|
+
return module.timeStretch(samples, sampleRate, rate);
|
|
587
|
+
}
|
|
588
|
+
function pitchShift(samples, sampleRate, semitones) {
|
|
589
|
+
if (!module) {
|
|
590
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
591
|
+
}
|
|
592
|
+
return module.pitchShift(samples, sampleRate, semitones);
|
|
593
|
+
}
|
|
594
|
+
function pitchCorrectToMidi(samples, sampleRate, currentMidi, targetMidi) {
|
|
595
|
+
if (!module) {
|
|
596
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
597
|
+
}
|
|
598
|
+
return module.pitchCorrectToMidi(samples, sampleRate, currentMidi, targetMidi);
|
|
599
|
+
}
|
|
600
|
+
function noteStretch(samples, sampleRate, onsetSample, offsetSample, stretchRatio) {
|
|
601
|
+
if (!module) {
|
|
602
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
603
|
+
}
|
|
604
|
+
return module.noteStretch(samples, sampleRate, onsetSample, offsetSample, stretchRatio);
|
|
605
|
+
}
|
|
606
|
+
function voiceChange(samples, sampleRate, pitchSemitones, formantFactor) {
|
|
607
|
+
if (!module) {
|
|
608
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
609
|
+
}
|
|
610
|
+
return module.voiceChange(samples, sampleRate, pitchSemitones, formantFactor);
|
|
611
|
+
}
|
|
612
|
+
function normalize(samples, sampleRate, targetDb = 0) {
|
|
613
|
+
if (!module) {
|
|
614
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
615
|
+
}
|
|
616
|
+
return module.normalize(samples, sampleRate, targetDb);
|
|
617
|
+
}
|
|
618
|
+
function mastering(samples, sampleRate, targetLufs = -14, ceilingDb = -1, truePeakOversample = 4) {
|
|
619
|
+
if (!module) {
|
|
620
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
621
|
+
}
|
|
622
|
+
return module.mastering(samples, sampleRate, targetLufs, ceilingDb, truePeakOversample);
|
|
623
|
+
}
|
|
624
|
+
function masteringProcessorNames() {
|
|
625
|
+
if (!module) {
|
|
626
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
627
|
+
}
|
|
628
|
+
return module.masteringProcessorNames();
|
|
629
|
+
}
|
|
630
|
+
function masteringPairProcessorNames() {
|
|
631
|
+
if (!module) {
|
|
632
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
633
|
+
}
|
|
634
|
+
return module.masteringPairProcessorNames();
|
|
635
|
+
}
|
|
636
|
+
function masteringPairAnalysisNames() {
|
|
637
|
+
if (!module) {
|
|
638
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
639
|
+
}
|
|
640
|
+
return module.masteringPairAnalysisNames();
|
|
641
|
+
}
|
|
642
|
+
function masteringStereoAnalysisNames() {
|
|
643
|
+
if (!module) {
|
|
644
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
645
|
+
}
|
|
646
|
+
return module.masteringStereoAnalysisNames();
|
|
647
|
+
}
|
|
648
|
+
function masteringProcess(processorName, samples, sampleRate, params = {}) {
|
|
649
|
+
if (!module) {
|
|
650
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
651
|
+
}
|
|
652
|
+
return module.masteringProcess(processorName, samples, sampleRate, params);
|
|
653
|
+
}
|
|
654
|
+
function masteringProcessStereo(processorName, left, right, sampleRate, params = {}) {
|
|
655
|
+
if (!module) {
|
|
656
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
657
|
+
}
|
|
658
|
+
if (left.length !== right.length) {
|
|
659
|
+
throw new Error("Stereo channel lengths must match.");
|
|
660
|
+
}
|
|
661
|
+
return module.masteringProcessStereo(processorName, left, right, sampleRate, params);
|
|
662
|
+
}
|
|
663
|
+
function masteringPairProcess(processorName, source, reference, sampleRate, params = {}) {
|
|
664
|
+
if (!module) {
|
|
665
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
666
|
+
}
|
|
667
|
+
return module.masteringPairProcess(processorName, source, reference, sampleRate, params);
|
|
668
|
+
}
|
|
669
|
+
function masteringPairAnalyze(analysisName, source, reference, sampleRate, params = {}) {
|
|
670
|
+
if (!module) {
|
|
671
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
672
|
+
}
|
|
673
|
+
return module.masteringPairAnalyze(analysisName, source, reference, sampleRate, params);
|
|
674
|
+
}
|
|
675
|
+
function masteringStereoAnalyze(analysisName, left, right, sampleRate, params = {}) {
|
|
676
|
+
if (!module) {
|
|
677
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
678
|
+
}
|
|
679
|
+
return module.masteringStereoAnalyze(analysisName, left, right, sampleRate, params);
|
|
680
|
+
}
|
|
681
|
+
function masteringAssistantSuggest(samples, sampleRate, params = {}) {
|
|
682
|
+
if (!module) {
|
|
683
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
684
|
+
}
|
|
685
|
+
return module.masteringAssistantSuggest(samples, sampleRate, params);
|
|
686
|
+
}
|
|
687
|
+
function masteringAudioProfile(samples, sampleRate, params = {}) {
|
|
688
|
+
if (!module) {
|
|
689
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
690
|
+
}
|
|
691
|
+
return module.masteringAudioProfile(samples, sampleRate, params);
|
|
692
|
+
}
|
|
693
|
+
function masteringStreamingPreview(samples, sampleRate, platforms = []) {
|
|
694
|
+
if (!module) {
|
|
695
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
696
|
+
}
|
|
697
|
+
return module.masteringStreamingPreview(samples, sampleRate, platforms);
|
|
698
|
+
}
|
|
699
|
+
function masteringChain(samples, sampleRate, config) {
|
|
700
|
+
if (!module) {
|
|
701
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
702
|
+
}
|
|
703
|
+
return module.masteringChain(samples, sampleRate, config);
|
|
704
|
+
}
|
|
705
|
+
function masteringChainStereo(left, right, sampleRate, config) {
|
|
706
|
+
if (!module) {
|
|
707
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
708
|
+
}
|
|
709
|
+
if (left.length !== right.length) {
|
|
710
|
+
throw new Error("Stereo channel lengths must match.");
|
|
711
|
+
}
|
|
712
|
+
return module.masteringChainStereo(left, right, sampleRate, config);
|
|
713
|
+
}
|
|
714
|
+
function masteringChainWithProgress(samples, sampleRate, config, onProgress) {
|
|
715
|
+
if (!module) {
|
|
716
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
717
|
+
}
|
|
718
|
+
return module.masteringChainWithProgress(
|
|
719
|
+
samples,
|
|
720
|
+
sampleRate,
|
|
721
|
+
config,
|
|
722
|
+
onProgress
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
function masteringChainStereoWithProgress(left, right, sampleRate, config, onProgress) {
|
|
726
|
+
if (!module) {
|
|
727
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
728
|
+
}
|
|
729
|
+
if (left.length !== right.length) {
|
|
730
|
+
throw new Error("Stereo channel lengths must match.");
|
|
731
|
+
}
|
|
732
|
+
return module.masteringChainStereoWithProgress(
|
|
733
|
+
left,
|
|
734
|
+
right,
|
|
735
|
+
sampleRate,
|
|
736
|
+
config,
|
|
737
|
+
onProgress
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
function masteringPresetNames() {
|
|
741
|
+
if (!module) {
|
|
742
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
743
|
+
}
|
|
744
|
+
return module.masteringPresetNames();
|
|
745
|
+
}
|
|
746
|
+
function masterAudio(samples, sampleRate, presetName, overrides = null) {
|
|
747
|
+
if (!module) {
|
|
748
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
749
|
+
}
|
|
750
|
+
return module.masterAudio(presetName, samples, sampleRate, overrides);
|
|
751
|
+
}
|
|
752
|
+
function masterAudioStereo(left, right, sampleRate, presetName, overrides = null) {
|
|
753
|
+
if (!module) {
|
|
754
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
755
|
+
}
|
|
756
|
+
if (left.length !== right.length) {
|
|
757
|
+
throw new Error("Stereo channel lengths must match.");
|
|
758
|
+
}
|
|
759
|
+
return module.masterAudioStereo(presetName, left, right, sampleRate, overrides);
|
|
760
|
+
}
|
|
761
|
+
function mixingScenePresetNames() {
|
|
762
|
+
if (!module) {
|
|
763
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
764
|
+
}
|
|
765
|
+
return module.mixingScenePresetNames();
|
|
766
|
+
}
|
|
767
|
+
function mixingScenePresetJson(preset) {
|
|
768
|
+
if (!module) {
|
|
769
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
770
|
+
}
|
|
771
|
+
return module.mixingScenePresetJson(preset);
|
|
772
|
+
}
|
|
773
|
+
function mixStereo(leftChannels, rightChannels, sampleRate = 48e3, options = {}) {
|
|
774
|
+
if (!module) {
|
|
775
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
776
|
+
}
|
|
777
|
+
if (leftChannels.length === 0 || leftChannels.length !== rightChannels.length) {
|
|
778
|
+
throw new Error("leftChannels and rightChannels must have the same non-zero length.");
|
|
779
|
+
}
|
|
780
|
+
return module.mixStereo(
|
|
781
|
+
leftChannels,
|
|
782
|
+
rightChannels,
|
|
783
|
+
sampleRate,
|
|
784
|
+
options
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
var StreamingMasteringChain = class {
|
|
788
|
+
constructor(config) {
|
|
133
789
|
if (!module) {
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
790
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
791
|
+
}
|
|
792
|
+
this.chain = module.createStreamingMasteringChain(config);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Initialize processors for the given sample rate and block layout.
|
|
796
|
+
*
|
|
797
|
+
* @param sampleRate - Sample rate in Hz
|
|
798
|
+
* @param maxBlockSize - Maximum block size per process call
|
|
799
|
+
* @param numChannels - 1 (mono) or 2 (stereo)
|
|
800
|
+
*/
|
|
801
|
+
prepare(sampleRate, maxBlockSize, numChannels) {
|
|
802
|
+
this.chain.prepare(sampleRate, maxBlockSize, numChannels);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Process one mono block, returning the processed samples (same length).
|
|
806
|
+
*/
|
|
807
|
+
processMono(samples) {
|
|
808
|
+
return this.chain.processMono(samples);
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Process one stereo block, returning the processed channels.
|
|
812
|
+
*/
|
|
813
|
+
processStereo(left, right) {
|
|
814
|
+
if (left.length !== right.length) {
|
|
815
|
+
throw new Error("Stereo channel lengths must match.");
|
|
816
|
+
}
|
|
817
|
+
return this.chain.processStereo(left, right);
|
|
818
|
+
}
|
|
819
|
+
/** Reset all processor state without rebuilding. */
|
|
820
|
+
reset() {
|
|
821
|
+
this.chain.reset();
|
|
822
|
+
}
|
|
823
|
+
/** Total reported latency in samples across all active processors. */
|
|
824
|
+
latencySamples() {
|
|
825
|
+
return this.chain.latencySamples();
|
|
826
|
+
}
|
|
827
|
+
/** Ordered stage names that will run (e.g. `"eq.tilt"`). */
|
|
828
|
+
stageNames() {
|
|
829
|
+
return this.chain.stageNames();
|
|
830
|
+
}
|
|
831
|
+
/** Release the underlying WASM object. Safe to call only once. */
|
|
832
|
+
delete() {
|
|
833
|
+
this.chain.delete();
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
var StreamingEqualizer = class {
|
|
837
|
+
constructor(config = {}) {
|
|
146
838
|
if (!module) {
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
839
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
840
|
+
}
|
|
841
|
+
this.eq = module.createEqualizer(config);
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Configure the band at `index` (0..23). Omitted fields use C++ defaults.
|
|
845
|
+
*/
|
|
846
|
+
setBand(index, band) {
|
|
847
|
+
this.eq.setBand(index, band);
|
|
848
|
+
}
|
|
849
|
+
/** Disable and reset every band. */
|
|
850
|
+
clear() {
|
|
851
|
+
this.eq.clear();
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Set the global phase mode: 1=ZeroLatency, 2=NaturalPhase, 3=LinearPhase.
|
|
855
|
+
*/
|
|
856
|
+
setPhaseMode(mode) {
|
|
857
|
+
this.eq.setPhaseMode(mode);
|
|
858
|
+
}
|
|
859
|
+
/** Enable or disable output auto-gain compensation. */
|
|
860
|
+
setAutoGain(enabled) {
|
|
861
|
+
this.eq.setAutoGain(enabled);
|
|
862
|
+
}
|
|
863
|
+
/** Set all-band EQ gain scale as a 0.0..2.0 multiplier. */
|
|
864
|
+
setGainScale(scale) {
|
|
865
|
+
this.eq.setGainScale(scale);
|
|
866
|
+
}
|
|
867
|
+
/** Set post-EQ output gain in dB. */
|
|
868
|
+
setOutputGainDb(gainDb) {
|
|
869
|
+
this.eq.setOutputGainDb(gainDb);
|
|
870
|
+
}
|
|
871
|
+
/** Set post-EQ stereo balance in -1.0..1.0; mono input ignores pan. */
|
|
872
|
+
setOutputPan(pan) {
|
|
873
|
+
this.eq.setOutputPan(pan);
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Provide a mono external sidechain key for dynamic bands that opt into
|
|
877
|
+
* `external_sidechain`. The samples are copied into an owned buffer.
|
|
878
|
+
*/
|
|
879
|
+
setSidechainMono(samples) {
|
|
880
|
+
this.eq.setSidechainMono(samples);
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Provide a stereo external sidechain key. Both channels must match length.
|
|
884
|
+
*/
|
|
885
|
+
setSidechainStereo(left, right) {
|
|
886
|
+
if (left.length !== right.length) {
|
|
887
|
+
throw new Error("Sidechain channel lengths must match.");
|
|
888
|
+
}
|
|
889
|
+
this.eq.setSidechainStereo(left, right);
|
|
890
|
+
}
|
|
891
|
+
/** Release any borrowed external sidechain buffers. */
|
|
892
|
+
clearSidechain() {
|
|
893
|
+
this.eq.clearSidechain();
|
|
894
|
+
}
|
|
895
|
+
/** Auto-gain applied on the most recent block, in dB. */
|
|
896
|
+
lastAutoGainDb() {
|
|
897
|
+
return this.eq.lastAutoGainDb();
|
|
898
|
+
}
|
|
899
|
+
/** Reported processing latency in samples (non-zero for linear-phase bands). */
|
|
900
|
+
latencySamples() {
|
|
901
|
+
return this.eq.latencySamples();
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Process one mono block, returning the equalized samples (same length).
|
|
905
|
+
*/
|
|
906
|
+
processMono(samples) {
|
|
907
|
+
return this.eq.processMono(samples);
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Process one stereo block, returning the equalized channels.
|
|
911
|
+
*/
|
|
912
|
+
processStereo(left, right) {
|
|
913
|
+
if (left.length !== right.length) {
|
|
914
|
+
throw new Error("Stereo channel lengths must match.");
|
|
915
|
+
}
|
|
916
|
+
return this.eq.processStereo(left, right);
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Read the latest pre/post spectrum snapshot for metering. `seq` increments
|
|
920
|
+
* each time a new snapshot is published.
|
|
921
|
+
*/
|
|
922
|
+
spectrum() {
|
|
923
|
+
return this.eq.spectrum();
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Configure bands so the source spectrum matches the reference spectrum.
|
|
927
|
+
*
|
|
928
|
+
* @param source - Source audio (mono samples)
|
|
929
|
+
* @param reference - Reference audio (mono samples)
|
|
930
|
+
* @param options - `sampleRate` (default 48000) and `maxBands` (default 8)
|
|
931
|
+
*/
|
|
932
|
+
match(source, reference, options = {}) {
|
|
933
|
+
this.eq.match(source, reference, options);
|
|
934
|
+
}
|
|
935
|
+
/** Release the underlying WASM object. Safe to call only once. */
|
|
936
|
+
delete() {
|
|
937
|
+
this.eq.delete();
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
function mixerScenePresetJson(preset) {
|
|
941
|
+
if (!module) {
|
|
942
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
943
|
+
}
|
|
944
|
+
return module.mixerPresetJson(preset);
|
|
945
|
+
}
|
|
946
|
+
var Mixer = class _Mixer {
|
|
947
|
+
constructor(mixer) {
|
|
948
|
+
this.mixer = mixer;
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Build a mixer from a scene JSON string.
|
|
952
|
+
*
|
|
953
|
+
* @param json - Scene JSON (strips, buses, sends, connections, inserts)
|
|
954
|
+
* @param sampleRate - Sample rate in Hz (default: 48000)
|
|
955
|
+
* @param blockSize - Maximum block size per {@link processStereo} call (default: 512)
|
|
956
|
+
*/
|
|
957
|
+
static fromSceneJson(json, sampleRate = 48e3, blockSize = 512) {
|
|
958
|
+
if (!module) {
|
|
959
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
960
|
+
}
|
|
961
|
+
return new _Mixer(module.createMixerFromSceneJson(json, sampleRate, blockSize));
|
|
962
|
+
}
|
|
963
|
+
/** Rebuild and compile the routing graph from the current scene topology. */
|
|
964
|
+
compile() {
|
|
965
|
+
this.mixer.compile();
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Mix one block of per-strip stereo audio into the stereo master.
|
|
969
|
+
*
|
|
970
|
+
* @param leftChannels - `leftChannels[i]` is the left channel of strip `i`
|
|
971
|
+
* @param rightChannels - `rightChannels[i]` is the right channel of strip `i`
|
|
972
|
+
* @returns Mixed stereo master (`left`, `right`, `sampleRate`)
|
|
973
|
+
*/
|
|
974
|
+
processStereo(leftChannels, rightChannels) {
|
|
975
|
+
if (leftChannels.length !== rightChannels.length) {
|
|
976
|
+
throw new Error("leftChannels and rightChannels must have the same length.");
|
|
977
|
+
}
|
|
978
|
+
return this.mixer.processStereo(leftChannels, rightChannels);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Mix one block into caller-owned output arrays.
|
|
982
|
+
*
|
|
983
|
+
* This avoids allocating the result object and result `Float32Array`s. It is
|
|
984
|
+
* intended for realtime bridges such as AudioWorklet; the input channel count
|
|
985
|
+
* must match the scene strip count and all arrays must have the same length.
|
|
986
|
+
*/
|
|
987
|
+
processStereoInto(leftChannels, rightChannels, outLeft, outRight) {
|
|
988
|
+
if (leftChannels.length !== rightChannels.length) {
|
|
989
|
+
throw new Error("leftChannels and rightChannels must have the same length.");
|
|
990
|
+
}
|
|
991
|
+
if (outLeft.length !== outRight.length) {
|
|
992
|
+
throw new Error("outLeft and outRight must have the same length.");
|
|
993
|
+
}
|
|
994
|
+
this.mixer.processStereoInto(leftChannels, rightChannels, outLeft, outRight);
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Create reusable WASM-heap input/output views for realtime-style processing.
|
|
998
|
+
*
|
|
999
|
+
* Fill `leftInputs[i]` / `rightInputs[i]`, call `process()`, then read
|
|
1000
|
+
* `outLeft` / `outRight`. The views are owned by this mixer and become invalid
|
|
1001
|
+
* after {@link delete}.
|
|
1002
|
+
*/
|
|
1003
|
+
createRealtimeBuffer() {
|
|
1004
|
+
const stripCount = this.stripCount();
|
|
1005
|
+
const leftInputs = [];
|
|
1006
|
+
const rightInputs = [];
|
|
1007
|
+
for (let index = 0; index < stripCount; index++) {
|
|
1008
|
+
leftInputs.push(this.mixer.inputLeftView(index));
|
|
1009
|
+
rightInputs.push(this.mixer.inputRightView(index));
|
|
1010
|
+
}
|
|
1011
|
+
const outLeft = this.mixer.outputLeftView();
|
|
1012
|
+
const outRight = this.mixer.outputRightView();
|
|
150
1013
|
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
1014
|
+
leftInputs,
|
|
1015
|
+
rightInputs,
|
|
1016
|
+
outLeft,
|
|
1017
|
+
outRight,
|
|
1018
|
+
process: (numSamples = outLeft.length) => this.mixer.processPreparedStereo(numSamples)
|
|
156
1019
|
};
|
|
1020
|
+
}
|
|
1021
|
+
/** Number of strips in the mixer (e.g. strips loaded from the scene). */
|
|
1022
|
+
stripCount() {
|
|
1023
|
+
return this.mixer.stripCount();
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Schedule sample-accurate insert-parameter automation on a strip's insert.
|
|
1027
|
+
*
|
|
1028
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1029
|
+
* @param insertIndex - Index into the strip's combined insert sequence
|
|
1030
|
+
* (`[pre-inserts... post-inserts...]`)
|
|
1031
|
+
* @param paramId - Processor-specific parameter id
|
|
1032
|
+
* @param samplePos - Absolute samples from the start of processing (the mixer
|
|
1033
|
+
* advances an internal position from 0 on the first {@link processStereo}
|
|
1034
|
+
* call; recompiling resets it to 0)
|
|
1035
|
+
* @param value - Target parameter value
|
|
1036
|
+
* @param curve - Interpolation curve (default: `'linear'`)
|
|
1037
|
+
* @throws If the strip index is out of range or the schedule call fails
|
|
1038
|
+
* (unknown curve, out-of-range insert index, or full event lane)
|
|
1039
|
+
*/
|
|
1040
|
+
scheduleInsertAutomation(stripIndex, insertIndex, paramId, samplePos, value, curve = "linear") {
|
|
1041
|
+
this.mixer.scheduleInsertAutomation(
|
|
1042
|
+
stripIndex,
|
|
1043
|
+
insertIndex,
|
|
1044
|
+
paramId,
|
|
1045
|
+
samplePos,
|
|
1046
|
+
value,
|
|
1047
|
+
automationCurveCode(curve)
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Resolve a strip's index in `[0, stripCount())` from its scene id, or `null`
|
|
1052
|
+
* when no strip with that id exists (matches the Node binding's `number | null`).
|
|
1053
|
+
*/
|
|
1054
|
+
stripById(id) {
|
|
1055
|
+
const index = this.mixer.stripById(id);
|
|
1056
|
+
return index < 0 ? null : index;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Add a bus to the mixer topology. `role` is one of `'master'`, `'aux'`, or
|
|
1060
|
+
* `'submix'` (defaults to `'aux'`). Marks the routing graph dirty; call
|
|
1061
|
+
* {@link compile} (or {@link processStereo}) to rebuild.
|
|
1062
|
+
*/
|
|
1063
|
+
addBus(id, role = "aux") {
|
|
1064
|
+
this.mixer.addBus(id, role);
|
|
1065
|
+
}
|
|
1066
|
+
/** Remove a bus by id. Marks the routing graph dirty. */
|
|
1067
|
+
removeBus(id) {
|
|
1068
|
+
this.mixer.removeBus(id);
|
|
1069
|
+
}
|
|
1070
|
+
/** Number of buses in the mixer topology. */
|
|
1071
|
+
busCount() {
|
|
1072
|
+
return this.mixer.busCount();
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Add a VCA group with the given gain offset (dB). `members` is a list of
|
|
1076
|
+
* strip ids governed by the group (may be empty).
|
|
1077
|
+
*/
|
|
1078
|
+
addVcaGroup(id, gainDb = 0, members = []) {
|
|
1079
|
+
this.mixer.addVcaGroup(id, gainDb, members);
|
|
1080
|
+
}
|
|
1081
|
+
/** Remove a VCA group by id. */
|
|
1082
|
+
removeVcaGroup(id) {
|
|
1083
|
+
this.mixer.removeVcaGroup(id);
|
|
1084
|
+
}
|
|
1085
|
+
/** Number of VCA groups in the mixer topology. */
|
|
1086
|
+
vcaGroupCount() {
|
|
1087
|
+
return this.mixer.vcaGroupCount();
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Set a strip's solo state. Takes effect on the next process without a
|
|
1091
|
+
* graph recompile.
|
|
1092
|
+
*/
|
|
1093
|
+
setSoloed(stripIndex, soloed) {
|
|
1094
|
+
this.mixer.setSoloed(stripIndex, soloed);
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Mark a strip solo-safe so it is never implied-muted by another strip's
|
|
1098
|
+
* solo. Takes effect on the next process without a graph recompile.
|
|
1099
|
+
*/
|
|
1100
|
+
setSoloSafe(stripIndex, soloSafe) {
|
|
1101
|
+
this.mixer.setSoloSafe(stripIndex, soloSafe);
|
|
1102
|
+
}
|
|
1103
|
+
/** Invert the polarity of the left and/or right channel of a strip. */
|
|
1104
|
+
setPolarityInvert(stripIndex, invertLeft, invertRight) {
|
|
1105
|
+
this.mixer.setPolarityInvert(stripIndex, invertLeft, invertRight);
|
|
1106
|
+
}
|
|
1107
|
+
/** Set the strip's pan law. */
|
|
1108
|
+
setPanLaw(stripIndex, panLaw) {
|
|
1109
|
+
this.mixer.setPanLaw(stripIndex, panLawCode(panLaw));
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Set a per-strip channel delay in samples. This changes the strip's reported
|
|
1113
|
+
* latency; recompile to re-run latency compensation.
|
|
1114
|
+
*/
|
|
1115
|
+
setChannelDelaySamples(stripIndex, delaySamples) {
|
|
1116
|
+
this.mixer.setChannelDelaySamples(stripIndex, delaySamples);
|
|
1117
|
+
}
|
|
1118
|
+
/** Set the strip's live VCA gain offset in dB (not persisted to the scene). */
|
|
1119
|
+
setVcaOffsetDb(stripIndex, offsetDb) {
|
|
1120
|
+
this.mixer.setVcaOffsetDb(stripIndex, offsetDb);
|
|
1121
|
+
}
|
|
1122
|
+
/** Set independent left/right pan positions (dual-pan mode). */
|
|
1123
|
+
setDualPan(stripIndex, leftPan, rightPan) {
|
|
1124
|
+
this.mixer.setDualPan(stripIndex, leftPan, rightPan);
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Add a send to a strip after construction.
|
|
1128
|
+
*
|
|
1129
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1130
|
+
* @param id - Send id
|
|
1131
|
+
* @param destinationBusId - Destination bus id
|
|
1132
|
+
* @param sendDb - Initial send level in dB
|
|
1133
|
+
* @param timing - `'preFader'` or `'postFader'` (default: `'postFader'`)
|
|
1134
|
+
* @returns The new send's index
|
|
1135
|
+
*/
|
|
1136
|
+
addSend(stripIndex, id, destinationBusId, sendDb, timing = "postFader") {
|
|
1137
|
+
return this.mixer.addSend(stripIndex, id, destinationBusId, sendDb, sendTimingCode(timing));
|
|
1138
|
+
}
|
|
1139
|
+
/** Set the send level (in dB) for an existing send by index. */
|
|
1140
|
+
setSendDb(stripIndex, sendIndex, sendDb) {
|
|
1141
|
+
this.mixer.setSendDb(stripIndex, sendIndex, sendDb);
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Read a strip's meter snapshot at the given tap point.
|
|
1145
|
+
*
|
|
1146
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1147
|
+
* @param tap - `'preFader'` or `'postFader'` (default: `'postFader'`)
|
|
1148
|
+
*/
|
|
1149
|
+
meterTap(stripIndex, tap = "postFader") {
|
|
1150
|
+
return this.mixer.meterTap(stripIndex, meterTapCode(tap));
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Read a strip's meter snapshot. Alias of {@link meterTap}, provided for
|
|
1154
|
+
* cross-binding (Node/Python) parity.
|
|
1155
|
+
*
|
|
1156
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1157
|
+
* @param tap - `'preFader'` or `'postFader'` (default: `'postFader'`)
|
|
1158
|
+
*/
|
|
1159
|
+
stripMeter(stripIndex, tap = "postFader") {
|
|
1160
|
+
return this.mixer.stripMeter(stripIndex, meterTapCode(tap));
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Schedule sample-accurate fader automation on a strip.
|
|
1164
|
+
*
|
|
1165
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1166
|
+
* @param samplePos - Absolute samples from the start of processing
|
|
1167
|
+
* @param faderDb - Target fader level in dB
|
|
1168
|
+
* @param curve - Interpolation curve (default: `'linear'`)
|
|
1169
|
+
*/
|
|
1170
|
+
scheduleFaderAutomation(stripIndex, samplePos, faderDb, curve = "linear") {
|
|
1171
|
+
this.mixer.scheduleFaderAutomation(stripIndex, samplePos, faderDb, automationCurveCode(curve));
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Schedule sample-accurate pan automation on a strip.
|
|
1175
|
+
*
|
|
1176
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1177
|
+
* @param samplePos - Absolute samples from the start of processing
|
|
1178
|
+
* @param pan - Target pan position
|
|
1179
|
+
* @param curve - Interpolation curve (default: `'linear'`)
|
|
1180
|
+
*/
|
|
1181
|
+
schedulePanAutomation(stripIndex, samplePos, pan, curve = "linear") {
|
|
1182
|
+
this.mixer.schedulePanAutomation(stripIndex, samplePos, pan, automationCurveCode(curve));
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Schedule sample-accurate width automation on a strip.
|
|
1186
|
+
*
|
|
1187
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1188
|
+
* @param samplePos - Absolute samples from the start of processing
|
|
1189
|
+
* @param width - Target stereo width
|
|
1190
|
+
* @param curve - Interpolation curve (default: `'linear'`)
|
|
1191
|
+
*/
|
|
1192
|
+
scheduleWidthAutomation(stripIndex, samplePos, width, curve = "linear") {
|
|
1193
|
+
this.mixer.scheduleWidthAutomation(stripIndex, samplePos, width, automationCurveCode(curve));
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Schedule sample-accurate send-level automation on a strip's send.
|
|
1197
|
+
*
|
|
1198
|
+
* @param stripIndex - Strip index in `[0, stripCount())`
|
|
1199
|
+
* @param sendIndex - Send index in the strip's add order
|
|
1200
|
+
* @param samplePos - Absolute samples from the start of processing
|
|
1201
|
+
* @param db - Target send level in dB
|
|
1202
|
+
* @param curve - Interpolation curve (default: `'linear'`)
|
|
1203
|
+
*/
|
|
1204
|
+
scheduleSendAutomation(stripIndex, sendIndex, samplePos, db, curve = "linear") {
|
|
1205
|
+
this.mixer.scheduleSendAutomation(
|
|
1206
|
+
stripIndex,
|
|
1207
|
+
sendIndex,
|
|
1208
|
+
samplePos,
|
|
1209
|
+
db,
|
|
1210
|
+
automationCurveCode(curve)
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Read up to `maxPoints` of a strip's most recent goniometer samples
|
|
1215
|
+
* (oldest to newest).
|
|
1216
|
+
*/
|
|
1217
|
+
readGoniometerLatest(stripIndex, maxPoints) {
|
|
1218
|
+
return this.mixer.readGoniometerLatest(stripIndex, maxPoints);
|
|
1219
|
+
}
|
|
1220
|
+
/** Serialize the current scene (strips, buses, sends, connections) to JSON. */
|
|
1221
|
+
toSceneJson() {
|
|
1222
|
+
return this.mixer.toSceneJson();
|
|
1223
|
+
}
|
|
1224
|
+
/** Release the underlying WASM object. Safe to call only once. */
|
|
1225
|
+
delete() {
|
|
1226
|
+
this.mixer.delete();
|
|
1227
|
+
}
|
|
1228
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
1229
|
+
destroy() {
|
|
1230
|
+
this.delete();
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
function trim(samples, sampleRate, thresholdDb = -60) {
|
|
1234
|
+
if (!module) {
|
|
1235
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1236
|
+
}
|
|
1237
|
+
return module.trim(samples, sampleRate, thresholdDb);
|
|
157
1238
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
* @returns Array of onset times in seconds
|
|
164
|
-
*/
|
|
165
|
-
export function detectOnsets(samples, sampleRate) {
|
|
166
|
-
if (!module) {
|
|
167
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
168
|
-
}
|
|
169
|
-
return module.detectOnsets(samples, sampleRate);
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Detect beat times from audio samples.
|
|
173
|
-
*
|
|
174
|
-
* @param samples - Audio samples (mono, float32)
|
|
175
|
-
* @param sampleRate - Sample rate in Hz
|
|
176
|
-
* @returns Array of beat times in seconds
|
|
177
|
-
*/
|
|
178
|
-
export function detectBeats(samples, sampleRate) {
|
|
179
|
-
if (!module) {
|
|
180
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
181
|
-
}
|
|
182
|
-
return module.detectBeats(samples, sampleRate);
|
|
1239
|
+
function stft(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1240
|
+
if (!module) {
|
|
1241
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1242
|
+
}
|
|
1243
|
+
return module.stft(samples, sampleRate, nFft, hopLength);
|
|
183
1244
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1245
|
+
function stftDb(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1246
|
+
if (!module) {
|
|
1247
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1248
|
+
}
|
|
1249
|
+
return module.stftDb(samples, sampleRate, nFft, hopLength);
|
|
1250
|
+
}
|
|
1251
|
+
function melSpectrogram(samples, sampleRate, nFft = 2048, hopLength = 512, nMels = 128) {
|
|
1252
|
+
if (!module) {
|
|
1253
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1254
|
+
}
|
|
1255
|
+
return module.melSpectrogram(samples, sampleRate, nFft, hopLength, nMels);
|
|
1256
|
+
}
|
|
1257
|
+
function mfcc(samples, sampleRate, nFft = 2048, hopLength = 512, nMels = 128, nMfcc = 13) {
|
|
1258
|
+
if (!module) {
|
|
1259
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1260
|
+
}
|
|
1261
|
+
return module.mfcc(samples, sampleRate, nFft, hopLength, nMels, nMfcc);
|
|
1262
|
+
}
|
|
1263
|
+
function melToStft(melPower, nMels, nFrames, sampleRate, nFft = 2048, hopLength = 512, fmin = 0, fmax = 0) {
|
|
1264
|
+
if (!module) {
|
|
1265
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1266
|
+
}
|
|
1267
|
+
return module.melToStft(melPower, nMels, nFrames, sampleRate, nFft, hopLength, fmin, fmax);
|
|
1268
|
+
}
|
|
1269
|
+
function melToAudio(melPower, nMels, nFrames, sampleRate, nFft = 2048, hopLength = 512, nIter = 32, fmin = 0, fmax = 0) {
|
|
1270
|
+
if (!module) {
|
|
1271
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1272
|
+
}
|
|
1273
|
+
return module.melToAudio(
|
|
1274
|
+
melPower,
|
|
1275
|
+
nMels,
|
|
1276
|
+
nFrames,
|
|
1277
|
+
sampleRate,
|
|
1278
|
+
nFft,
|
|
1279
|
+
hopLength,
|
|
1280
|
+
nIter,
|
|
1281
|
+
fmin,
|
|
1282
|
+
fmax
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
function mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels = 128) {
|
|
1286
|
+
if (!module) {
|
|
1287
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1288
|
+
}
|
|
1289
|
+
return module.mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels);
|
|
1290
|
+
}
|
|
1291
|
+
function mfccToAudio(mfccCoefficients, nMfcc, nFrames, nMels, sampleRate, nFft = 2048, hopLength = 512, nIter = 32, fmin = 0, fmax = 0) {
|
|
1292
|
+
if (!module) {
|
|
1293
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1294
|
+
}
|
|
1295
|
+
return module.mfccToAudio(
|
|
1296
|
+
mfccCoefficients,
|
|
1297
|
+
nMfcc,
|
|
1298
|
+
nFrames,
|
|
1299
|
+
nMels,
|
|
1300
|
+
sampleRate,
|
|
1301
|
+
nFft,
|
|
1302
|
+
hopLength,
|
|
1303
|
+
nIter,
|
|
1304
|
+
fmin,
|
|
1305
|
+
fmax
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
function chroma(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1309
|
+
if (!module) {
|
|
1310
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1311
|
+
}
|
|
1312
|
+
return module.chroma(samples, sampleRate, nFft, hopLength);
|
|
1313
|
+
}
|
|
1314
|
+
function spectralCentroid(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1315
|
+
if (!module) {
|
|
1316
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1317
|
+
}
|
|
1318
|
+
return module.spectralCentroid(samples, sampleRate, nFft, hopLength);
|
|
1319
|
+
}
|
|
1320
|
+
function spectralBandwidth(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1321
|
+
if (!module) {
|
|
1322
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1323
|
+
}
|
|
1324
|
+
return module.spectralBandwidth(samples, sampleRate, nFft, hopLength);
|
|
1325
|
+
}
|
|
1326
|
+
function spectralRolloff(samples, sampleRate, nFft = 2048, hopLength = 512, rollPercent = 0.85) {
|
|
1327
|
+
if (!module) {
|
|
1328
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1329
|
+
}
|
|
1330
|
+
return module.spectralRolloff(samples, sampleRate, nFft, hopLength, rollPercent);
|
|
1331
|
+
}
|
|
1332
|
+
function spectralFlatness(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
1333
|
+
if (!module) {
|
|
1334
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1335
|
+
}
|
|
1336
|
+
return module.spectralFlatness(samples, sampleRate, nFft, hopLength);
|
|
1337
|
+
}
|
|
1338
|
+
function zeroCrossingRate(samples, sampleRate, frameLength = 2048, hopLength = 512) {
|
|
1339
|
+
if (!module) {
|
|
1340
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1341
|
+
}
|
|
1342
|
+
return module.zeroCrossingRate(samples, sampleRate, frameLength, hopLength);
|
|
1343
|
+
}
|
|
1344
|
+
function rmsEnergy(samples, sampleRate, frameLength = 2048, hopLength = 512) {
|
|
1345
|
+
if (!module) {
|
|
1346
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1347
|
+
}
|
|
1348
|
+
return module.rmsEnergy(samples, sampleRate, frameLength, hopLength);
|
|
1349
|
+
}
|
|
1350
|
+
function pitchYin(samples, sampleRate, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.3) {
|
|
1351
|
+
if (!module) {
|
|
1352
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1353
|
+
}
|
|
1354
|
+
return module.pitchYin(samples, sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
1355
|
+
}
|
|
1356
|
+
function pitchPyin(samples, sampleRate, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.3) {
|
|
1357
|
+
if (!module) {
|
|
1358
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1359
|
+
}
|
|
1360
|
+
return module.pitchPyin(samples, sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
1361
|
+
}
|
|
1362
|
+
function hzToMel(hz) {
|
|
1363
|
+
if (!module) {
|
|
1364
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1365
|
+
}
|
|
1366
|
+
return module.hzToMel(hz);
|
|
1367
|
+
}
|
|
1368
|
+
function melToHz(mel) {
|
|
1369
|
+
if (!module) {
|
|
1370
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1371
|
+
}
|
|
1372
|
+
return module.melToHz(mel);
|
|
1373
|
+
}
|
|
1374
|
+
function hzToMidi(hz) {
|
|
1375
|
+
if (!module) {
|
|
1376
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1377
|
+
}
|
|
1378
|
+
return module.hzToMidi(hz);
|
|
1379
|
+
}
|
|
1380
|
+
function midiToHz(midi) {
|
|
1381
|
+
if (!module) {
|
|
1382
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1383
|
+
}
|
|
1384
|
+
return module.midiToHz(midi);
|
|
1385
|
+
}
|
|
1386
|
+
function hzToNote(hz) {
|
|
1387
|
+
if (!module) {
|
|
1388
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1389
|
+
}
|
|
1390
|
+
return module.hzToNote(hz);
|
|
1391
|
+
}
|
|
1392
|
+
function noteToHz(note) {
|
|
1393
|
+
if (!module) {
|
|
1394
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1395
|
+
}
|
|
1396
|
+
return module.noteToHz(note);
|
|
1397
|
+
}
|
|
1398
|
+
function framesToTime(frames, sr, hopLength) {
|
|
1399
|
+
if (!module) {
|
|
1400
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1401
|
+
}
|
|
1402
|
+
return module.framesToTime(frames, sr, hopLength);
|
|
1403
|
+
}
|
|
1404
|
+
function timeToFrames(time, sr, hopLength) {
|
|
1405
|
+
if (!module) {
|
|
1406
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1407
|
+
}
|
|
1408
|
+
return module.timeToFrames(time, sr, hopLength);
|
|
1409
|
+
}
|
|
1410
|
+
function framesToSamples(frames, hopLength = 512, nFft = 0) {
|
|
1411
|
+
if (!module) {
|
|
1412
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1413
|
+
}
|
|
1414
|
+
return module.framesToSamples(frames, hopLength, nFft);
|
|
1415
|
+
}
|
|
1416
|
+
function samplesToFrames(samples, hopLength = 512, nFft = 0) {
|
|
1417
|
+
if (!module) {
|
|
1418
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1419
|
+
}
|
|
1420
|
+
return module.samplesToFrames(samples, hopLength, nFft);
|
|
1421
|
+
}
|
|
1422
|
+
function powerToDb(values, ref = 1, amin = 1e-10, topDb = 80) {
|
|
1423
|
+
if (!module) {
|
|
1424
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1425
|
+
}
|
|
1426
|
+
return module.powerToDb(values, ref, amin, topDb);
|
|
1427
|
+
}
|
|
1428
|
+
function amplitudeToDb(values, ref = 1, amin = 1e-5, topDb = 80) {
|
|
1429
|
+
if (!module) {
|
|
1430
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1431
|
+
}
|
|
1432
|
+
return module.amplitudeToDb(values, ref, amin, topDb);
|
|
1433
|
+
}
|
|
1434
|
+
function dbToPower(values, ref = 1) {
|
|
1435
|
+
if (!module) {
|
|
1436
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1437
|
+
}
|
|
1438
|
+
return module.dbToPower(values, ref);
|
|
1439
|
+
}
|
|
1440
|
+
function dbToAmplitude(values, ref = 1) {
|
|
1441
|
+
if (!module) {
|
|
1442
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1443
|
+
}
|
|
1444
|
+
return module.dbToAmplitude(values, ref);
|
|
1445
|
+
}
|
|
1446
|
+
function preemphasis(samples, coef = 0.97, zi) {
|
|
1447
|
+
if (!module) {
|
|
1448
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1449
|
+
}
|
|
1450
|
+
return module.preemphasis(samples, coef, zi ?? null);
|
|
1451
|
+
}
|
|
1452
|
+
function deemphasis(samples, coef = 0.97, zi) {
|
|
1453
|
+
if (!module) {
|
|
1454
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1455
|
+
}
|
|
1456
|
+
return module.deemphasis(samples, coef, zi ?? null);
|
|
1457
|
+
}
|
|
1458
|
+
function trimSilence(samples, topDb = 60, frameLength = 2048, hopLength = 512) {
|
|
1459
|
+
if (!module) {
|
|
1460
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1461
|
+
}
|
|
1462
|
+
return module.trimSilence(samples, topDb, frameLength, hopLength);
|
|
1463
|
+
}
|
|
1464
|
+
function splitSilence(samples, topDb = 60, frameLength = 2048, hopLength = 512) {
|
|
1465
|
+
if (!module) {
|
|
1466
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1467
|
+
}
|
|
1468
|
+
return module.splitSilence(samples, topDb, frameLength, hopLength);
|
|
1469
|
+
}
|
|
1470
|
+
function frameSignal(samples, frameLength, hopLength) {
|
|
1471
|
+
if (!module) {
|
|
1472
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1473
|
+
}
|
|
1474
|
+
return module.frameSignal(samples, frameLength, hopLength);
|
|
1475
|
+
}
|
|
1476
|
+
function padCenter(values, size, padValue = 0) {
|
|
1477
|
+
if (!module) {
|
|
1478
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1479
|
+
}
|
|
1480
|
+
return module.padCenter(values, size, padValue);
|
|
1481
|
+
}
|
|
1482
|
+
function fixLength(values, size, padValue = 0) {
|
|
1483
|
+
if (!module) {
|
|
1484
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1485
|
+
}
|
|
1486
|
+
return module.fixLength(values, size, padValue);
|
|
1487
|
+
}
|
|
1488
|
+
function fixFrames(frames, xMin = 0, xMax = -1, pad = true) {
|
|
1489
|
+
if (!module) {
|
|
1490
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1491
|
+
}
|
|
1492
|
+
return module.fixFrames(frames, xMin, xMax, pad);
|
|
1493
|
+
}
|
|
1494
|
+
function peakPick(values, preMax, postMax, preAvg, postAvg, delta, wait) {
|
|
1495
|
+
if (!module) {
|
|
1496
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1497
|
+
}
|
|
1498
|
+
return module.peakPick(values, preMax, postMax, preAvg, postAvg, delta, wait);
|
|
1499
|
+
}
|
|
1500
|
+
function vectorNormalize(values, normType = 0, threshold = 1e-12) {
|
|
1501
|
+
if (!module) {
|
|
1502
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1503
|
+
}
|
|
1504
|
+
return module.vectorNormalize(values, normType, threshold);
|
|
1505
|
+
}
|
|
1506
|
+
function pcen(values, nBins, nFrames, options = {}) {
|
|
1507
|
+
if (!module) {
|
|
1508
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1509
|
+
}
|
|
1510
|
+
return module.pcen(values, nBins, nFrames, options);
|
|
1511
|
+
}
|
|
1512
|
+
function tonnetz(chromagram, nChroma, nFrames) {
|
|
1513
|
+
if (!module) {
|
|
1514
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1515
|
+
}
|
|
1516
|
+
return module.tonnetz(chromagram, nChroma, nFrames);
|
|
1517
|
+
}
|
|
1518
|
+
function tempogram(onsetEnvelope2, sampleRate, hopLength = 512, winLength = 384, mode = "autocorrelation") {
|
|
1519
|
+
if (!module) {
|
|
1520
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1521
|
+
}
|
|
1522
|
+
return module.tempogram(onsetEnvelope2, sampleRate, hopLength, winLength, mode);
|
|
1523
|
+
}
|
|
1524
|
+
function cyclicTempogram(onsetEnvelope2, sampleRate, hopLength = 512, winLength = 384, bpmMin = 60, nBins = 60) {
|
|
1525
|
+
if (!module) {
|
|
1526
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1527
|
+
}
|
|
1528
|
+
return module.cyclicTempogram(onsetEnvelope2, sampleRate, hopLength, winLength, bpmMin, nBins);
|
|
1529
|
+
}
|
|
1530
|
+
function plp(onsetEnvelope2, sampleRate, hopLength = 512, tempoMin = 30, tempoMax = 300, winLength = 384) {
|
|
1531
|
+
if (!module) {
|
|
1532
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1533
|
+
}
|
|
1534
|
+
return module.plp(onsetEnvelope2, sampleRate, hopLength, tempoMin, tempoMax, winLength);
|
|
1535
|
+
}
|
|
1536
|
+
function nnlsChroma(samples, sampleRate = 22050) {
|
|
1537
|
+
if (!module) {
|
|
1538
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1539
|
+
}
|
|
1540
|
+
return module.nnlsChroma(samples, sampleRate);
|
|
1541
|
+
}
|
|
1542
|
+
function cqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12) {
|
|
1543
|
+
if (!module) {
|
|
1544
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1545
|
+
}
|
|
1546
|
+
return module.cqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave);
|
|
1547
|
+
}
|
|
1548
|
+
function vqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12, gamma = 0) {
|
|
1549
|
+
if (!module) {
|
|
1550
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1551
|
+
}
|
|
1552
|
+
return module.vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);
|
|
1553
|
+
}
|
|
1554
|
+
function analyzeSections(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, minSectionSec = 8) {
|
|
1555
|
+
if (!module) {
|
|
1556
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1557
|
+
}
|
|
1558
|
+
return module.analyzeSections(samples, sampleRate, nFft, hopLength, minSectionSec).map((s) => ({ ...s, type: s.type }));
|
|
1559
|
+
}
|
|
1560
|
+
function analyzeMelody(samples, sampleRate = 22050, fmin = 65, fmax = 2093, frameLength = 2048, hopLength = 512, threshold = 0.1) {
|
|
1561
|
+
if (!module) {
|
|
1562
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1563
|
+
}
|
|
1564
|
+
return module.analyzeMelody(samples, sampleRate, fmin, fmax, frameLength, hopLength, threshold);
|
|
1565
|
+
}
|
|
1566
|
+
function onsetEnvelope(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, nMels = 128) {
|
|
1567
|
+
if (!module) {
|
|
1568
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1569
|
+
}
|
|
1570
|
+
return module.onsetEnvelope(samples, sampleRate, nFft, hopLength, nMels);
|
|
1571
|
+
}
|
|
1572
|
+
function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384) {
|
|
1573
|
+
if (!module) {
|
|
1574
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1575
|
+
}
|
|
1576
|
+
return module.fourierTempogram(onsetEnvelope2, sampleRate, hopLength, winLength);
|
|
1577
|
+
}
|
|
1578
|
+
function tempogramRatio(tempogramData, winLength = 384, sampleRate = 22050, hopLength = 512) {
|
|
1579
|
+
if (!module) {
|
|
1580
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1581
|
+
}
|
|
1582
|
+
return module.tempogramRatio(tempogramData, winLength, sampleRate, hopLength);
|
|
1583
|
+
}
|
|
1584
|
+
function lufs(samples, sampleRate = 22050) {
|
|
1585
|
+
if (!module) {
|
|
1586
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1587
|
+
}
|
|
1588
|
+
return module.lufs(samples, sampleRate);
|
|
1589
|
+
}
|
|
1590
|
+
function momentaryLufs(samples, sampleRate = 22050) {
|
|
1591
|
+
if (!module) {
|
|
1592
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1593
|
+
}
|
|
1594
|
+
return module.momentaryLufs(samples, sampleRate);
|
|
1595
|
+
}
|
|
1596
|
+
function shortTermLufs(samples, sampleRate = 22050) {
|
|
1597
|
+
if (!module) {
|
|
1598
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1599
|
+
}
|
|
1600
|
+
return module.shortTermLufs(samples, sampleRate);
|
|
1601
|
+
}
|
|
1602
|
+
function resample(samples, srcSr, targetSr) {
|
|
1603
|
+
if (!module) {
|
|
1604
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1605
|
+
}
|
|
1606
|
+
return module.resample(samples, srcSr, targetSr);
|
|
1607
|
+
}
|
|
1608
|
+
var Audio = class _Audio {
|
|
1609
|
+
constructor(samples, sampleRate) {
|
|
1610
|
+
this._samples = samples;
|
|
1611
|
+
this._sampleRate = sampleRate;
|
|
1612
|
+
}
|
|
1613
|
+
/** Create an Audio instance from raw sample data. */
|
|
1614
|
+
static fromBuffer(samples, sampleRate) {
|
|
1615
|
+
return new _Audio(samples, sampleRate);
|
|
1616
|
+
}
|
|
1617
|
+
/** The raw audio samples. */
|
|
1618
|
+
get data() {
|
|
1619
|
+
return this._samples;
|
|
1620
|
+
}
|
|
1621
|
+
/** Number of samples. */
|
|
1622
|
+
get length() {
|
|
1623
|
+
return this._samples.length;
|
|
1624
|
+
}
|
|
1625
|
+
/** Sample rate in Hz. */
|
|
1626
|
+
get sampleRate() {
|
|
1627
|
+
return this._sampleRate;
|
|
1628
|
+
}
|
|
1629
|
+
/** Duration in seconds. */
|
|
1630
|
+
get duration() {
|
|
1631
|
+
return this._samples.length / this._sampleRate;
|
|
1632
|
+
}
|
|
1633
|
+
// -- Analysis --
|
|
1634
|
+
detectBpm() {
|
|
1635
|
+
return detectBpm(this._samples, this._sampleRate);
|
|
1636
|
+
}
|
|
1637
|
+
detectKey(options = {}) {
|
|
1638
|
+
return detectKey(this._samples, this._sampleRate, options);
|
|
1639
|
+
}
|
|
1640
|
+
detectKeyCandidates(options = {}) {
|
|
1641
|
+
return detectKeyCandidates(this._samples, this._sampleRate, options);
|
|
1642
|
+
}
|
|
1643
|
+
detectOnsets() {
|
|
1644
|
+
return detectOnsets(this._samples, this._sampleRate);
|
|
1645
|
+
}
|
|
1646
|
+
detectBeats() {
|
|
1647
|
+
return detectBeats(this._samples, this._sampleRate);
|
|
1648
|
+
}
|
|
1649
|
+
detectDownbeats() {
|
|
1650
|
+
return detectDownbeats(this._samples, this._sampleRate);
|
|
1651
|
+
}
|
|
1652
|
+
detectChords(options = {}) {
|
|
1653
|
+
return detectChords(this._samples, this._sampleRate, options);
|
|
1654
|
+
}
|
|
1655
|
+
analyze() {
|
|
1656
|
+
return analyze(this._samples, this._sampleRate);
|
|
1657
|
+
}
|
|
1658
|
+
analyzeWithProgress(onProgress) {
|
|
1659
|
+
return analyzeWithProgress(this._samples, this._sampleRate, onProgress);
|
|
1660
|
+
}
|
|
1661
|
+
// -- Effects --
|
|
1662
|
+
hpss(kernelHarmonic = 31, kernelPercussive = 31) {
|
|
1663
|
+
return hpss(this._samples, this._sampleRate, kernelHarmonic, kernelPercussive);
|
|
1664
|
+
}
|
|
1665
|
+
harmonic() {
|
|
1666
|
+
return harmonic(this._samples, this._sampleRate);
|
|
1667
|
+
}
|
|
1668
|
+
percussive() {
|
|
1669
|
+
return percussive(this._samples, this._sampleRate);
|
|
1670
|
+
}
|
|
1671
|
+
timeStretch(rate) {
|
|
1672
|
+
return timeStretch(this._samples, this._sampleRate, rate);
|
|
1673
|
+
}
|
|
1674
|
+
pitchShift(semitones) {
|
|
1675
|
+
return pitchShift(this._samples, this._sampleRate, semitones);
|
|
1676
|
+
}
|
|
1677
|
+
pitchCorrectToMidi(currentMidi, targetMidi) {
|
|
1678
|
+
return pitchCorrectToMidi(this._samples, this._sampleRate, currentMidi, targetMidi);
|
|
1679
|
+
}
|
|
1680
|
+
noteStretch(onsetSample, offsetSample, stretchRatio) {
|
|
1681
|
+
return noteStretch(this._samples, this._sampleRate, onsetSample, offsetSample, stretchRatio);
|
|
1682
|
+
}
|
|
1683
|
+
voiceChange(pitchSemitones, formantFactor) {
|
|
1684
|
+
return voiceChange(this._samples, this._sampleRate, pitchSemitones, formantFactor);
|
|
1685
|
+
}
|
|
1686
|
+
normalize(targetDb = 0) {
|
|
1687
|
+
return normalize(this._samples, this._sampleRate, targetDb);
|
|
1688
|
+
}
|
|
1689
|
+
mastering(targetLufs = -14, ceilingDb = -1, truePeakOversample = 4) {
|
|
1690
|
+
return mastering(this._samples, this._sampleRate, targetLufs, ceilingDb, truePeakOversample);
|
|
1691
|
+
}
|
|
1692
|
+
masteringChain(config) {
|
|
1693
|
+
return masteringChain(this._samples, this._sampleRate, config);
|
|
1694
|
+
}
|
|
1695
|
+
masterAudio(presetName, overrides = null) {
|
|
1696
|
+
return masterAudio(this._samples, this._sampleRate, presetName, overrides);
|
|
1697
|
+
}
|
|
1698
|
+
masteringProcess(processorName, params = {}) {
|
|
1699
|
+
return masteringProcess(processorName, this._samples, this._sampleRate, params);
|
|
1700
|
+
}
|
|
1701
|
+
trim(thresholdDb = -60) {
|
|
1702
|
+
return trim(this._samples, this._sampleRate, thresholdDb);
|
|
1703
|
+
}
|
|
1704
|
+
// -- Features --
|
|
1705
|
+
stft(nFft = 2048, hopLength = 512) {
|
|
1706
|
+
return stft(this._samples, this._sampleRate, nFft, hopLength);
|
|
1707
|
+
}
|
|
1708
|
+
stftDb(nFft = 2048, hopLength = 512) {
|
|
1709
|
+
return stftDb(this._samples, this._sampleRate, nFft, hopLength);
|
|
1710
|
+
}
|
|
1711
|
+
melSpectrogram(nFft = 2048, hopLength = 512, nMels = 128) {
|
|
1712
|
+
return melSpectrogram(this._samples, this._sampleRate, nFft, hopLength, nMels);
|
|
1713
|
+
}
|
|
1714
|
+
mfcc(nFft = 2048, hopLength = 512, nMels = 128, nMfcc = 13) {
|
|
1715
|
+
return mfcc(this._samples, this._sampleRate, nFft, hopLength, nMels, nMfcc);
|
|
1716
|
+
}
|
|
1717
|
+
chroma(nFft = 2048, hopLength = 512) {
|
|
1718
|
+
return chroma(this._samples, this._sampleRate, nFft, hopLength);
|
|
1719
|
+
}
|
|
1720
|
+
nnlsChroma() {
|
|
1721
|
+
return nnlsChroma(this._samples, this._sampleRate);
|
|
1722
|
+
}
|
|
1723
|
+
onsetEnvelope(nFft = 2048, hopLength = 512, nMels = 128) {
|
|
1724
|
+
return onsetEnvelope(this._samples, this._sampleRate, nFft, hopLength, nMels);
|
|
1725
|
+
}
|
|
1726
|
+
lufs() {
|
|
1727
|
+
return lufs(this._samples, this._sampleRate);
|
|
1728
|
+
}
|
|
1729
|
+
momentaryLufs() {
|
|
1730
|
+
return momentaryLufs(this._samples, this._sampleRate);
|
|
1731
|
+
}
|
|
1732
|
+
shortTermLufs() {
|
|
1733
|
+
return shortTermLufs(this._samples, this._sampleRate);
|
|
1734
|
+
}
|
|
1735
|
+
spectralCentroid(nFft = 2048, hopLength = 512) {
|
|
1736
|
+
return spectralCentroid(this._samples, this._sampleRate, nFft, hopLength);
|
|
1737
|
+
}
|
|
1738
|
+
spectralBandwidth(nFft = 2048, hopLength = 512) {
|
|
1739
|
+
return spectralBandwidth(this._samples, this._sampleRate, nFft, hopLength);
|
|
1740
|
+
}
|
|
1741
|
+
spectralRolloff(nFft = 2048, hopLength = 512, rollPercent = 0.85) {
|
|
1742
|
+
return spectralRolloff(this._samples, this._sampleRate, nFft, hopLength, rollPercent);
|
|
1743
|
+
}
|
|
1744
|
+
spectralFlatness(nFft = 2048, hopLength = 512) {
|
|
1745
|
+
return spectralFlatness(this._samples, this._sampleRate, nFft, hopLength);
|
|
1746
|
+
}
|
|
1747
|
+
zeroCrossingRate(frameLength = 2048, hopLength = 512) {
|
|
1748
|
+
return zeroCrossingRate(this._samples, this._sampleRate, frameLength, hopLength);
|
|
1749
|
+
}
|
|
1750
|
+
rmsEnergy(frameLength = 2048, hopLength = 512) {
|
|
1751
|
+
return rmsEnergy(this._samples, this._sampleRate, frameLength, hopLength);
|
|
1752
|
+
}
|
|
1753
|
+
pitchYin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.3) {
|
|
1754
|
+
return pitchYin(this._samples, this._sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
1755
|
+
}
|
|
1756
|
+
pitchPyin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.3) {
|
|
1757
|
+
return pitchPyin(
|
|
1758
|
+
this._samples,
|
|
1759
|
+
this._sampleRate,
|
|
1760
|
+
frameLength,
|
|
1761
|
+
hopLength,
|
|
1762
|
+
fmin,
|
|
1763
|
+
fmax,
|
|
1764
|
+
threshold
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
resample(targetSr) {
|
|
1768
|
+
return resample(this._samples, this._sampleRate, targetSr);
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
var StreamAnalyzer = class {
|
|
1772
|
+
/**
|
|
1773
|
+
* Create a new StreamAnalyzer.
|
|
1774
|
+
*
|
|
1775
|
+
* @param config - Configuration options
|
|
1776
|
+
*/
|
|
1777
|
+
constructor(config) {
|
|
1778
|
+
if (!module) {
|
|
1779
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
1780
|
+
}
|
|
1781
|
+
const wasmModule = module;
|
|
1782
|
+
const args = [
|
|
1783
|
+
config.sampleRate,
|
|
1784
|
+
config.nFft ?? 2048,
|
|
1785
|
+
config.hopLength ?? 512,
|
|
1786
|
+
config.nMels ?? 128,
|
|
1787
|
+
config.fmin ?? 0,
|
|
1788
|
+
config.fmax ?? 0,
|
|
1789
|
+
config.tuningRefHz ?? 440,
|
|
1790
|
+
config.computeMagnitude ?? true,
|
|
1791
|
+
config.computeMel ?? true,
|
|
1792
|
+
config.computeChroma ?? true,
|
|
1793
|
+
config.computeOnset ?? true,
|
|
1794
|
+
config.computeSpectral ?? true,
|
|
1795
|
+
config.emitEveryNFrames ?? 1,
|
|
1796
|
+
config.magnitudeDownsample ?? 1,
|
|
1797
|
+
config.keyUpdateIntervalSec ?? 5,
|
|
1798
|
+
config.bpmUpdateIntervalSec ?? 10,
|
|
1799
|
+
config.window ?? 0,
|
|
1800
|
+
config.outputFormat ?? 0
|
|
1801
|
+
];
|
|
1802
|
+
const isArityError = (error) => {
|
|
1803
|
+
const message = String(error?.message ?? error);
|
|
1804
|
+
return message.includes("invalid number of parameters");
|
|
1805
|
+
};
|
|
1806
|
+
const createLegacy = () => {
|
|
1807
|
+
const LegacyStreamAnalyzer = wasmModule.StreamAnalyzer;
|
|
1808
|
+
return new LegacyStreamAnalyzer(
|
|
1809
|
+
args[0],
|
|
1810
|
+
args[1],
|
|
1811
|
+
args[2],
|
|
1812
|
+
args[3],
|
|
1813
|
+
args[8],
|
|
1814
|
+
args[9],
|
|
1815
|
+
args[10],
|
|
1816
|
+
args[12]
|
|
1817
|
+
);
|
|
1818
|
+
};
|
|
1819
|
+
const hasExtendedConfig = config.fmin !== void 0 || config.fmax !== void 0 || config.tuningRefHz !== void 0 || config.computeMagnitude !== void 0 || config.computeSpectral !== void 0 || config.magnitudeDownsample !== void 0 || config.keyUpdateIntervalSec !== void 0 || config.bpmUpdateIntervalSec !== void 0 || config.window !== void 0 || config.outputFormat !== void 0;
|
|
1820
|
+
if (hasExtendedConfig) {
|
|
1821
|
+
try {
|
|
1822
|
+
this.analyzer = new wasmModule.StreamAnalyzer(...args);
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
if (!isArityError(error)) {
|
|
1825
|
+
throw error;
|
|
1826
|
+
}
|
|
1827
|
+
this.analyzer = createLegacy();
|
|
1828
|
+
}
|
|
1829
|
+
} else {
|
|
1830
|
+
try {
|
|
1831
|
+
this.analyzer = createLegacy();
|
|
1832
|
+
} catch (error) {
|
|
1833
|
+
if (!isArityError(error)) {
|
|
1834
|
+
throw error;
|
|
1835
|
+
}
|
|
1836
|
+
this.analyzer = new wasmModule.StreamAnalyzer(...args);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Process audio samples.
|
|
1842
|
+
*
|
|
1843
|
+
* @param samples - Audio samples (mono, float32)
|
|
1844
|
+
*/
|
|
1845
|
+
process(samples) {
|
|
1846
|
+
this.analyzer.process(samples);
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Process audio samples with explicit sample offset.
|
|
1850
|
+
*
|
|
1851
|
+
* @param samples - Audio samples (mono, float32)
|
|
1852
|
+
* @param sampleOffset - Cumulative sample count at start of this chunk
|
|
1853
|
+
*/
|
|
1854
|
+
processWithOffset(samples, sampleOffset) {
|
|
1855
|
+
this.analyzer.processWithOffset(samples, sampleOffset);
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Get the number of frames available to read.
|
|
1859
|
+
*/
|
|
1860
|
+
availableFrames() {
|
|
1861
|
+
return this.analyzer.availableFrames();
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Read processed frames as Structure of Arrays.
|
|
1865
|
+
*
|
|
1866
|
+
* @param maxFrames - Maximum number of frames to read
|
|
1867
|
+
* @returns Frame buffer with analysis results
|
|
1868
|
+
*/
|
|
1869
|
+
readFrames(maxFrames) {
|
|
1870
|
+
return this.analyzer.readFramesSoa(maxFrames);
|
|
1871
|
+
}
|
|
1872
|
+
readFramesU8(maxFrames) {
|
|
1873
|
+
return this.analyzer.readFramesU8(maxFrames);
|
|
1874
|
+
}
|
|
1875
|
+
readFramesI16(maxFrames) {
|
|
1876
|
+
return this.analyzer.readFramesI16(maxFrames);
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Reset the analyzer state.
|
|
1880
|
+
*
|
|
1881
|
+
* @param baseSampleOffset - Starting sample offset (default 0)
|
|
1882
|
+
*/
|
|
1883
|
+
reset(baseSampleOffset = 0) {
|
|
1884
|
+
this.analyzer.reset(baseSampleOffset);
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Get current statistics and progressive estimates.
|
|
1888
|
+
*
|
|
1889
|
+
* @returns Analyzer statistics including BPM, key, and chord progression
|
|
1890
|
+
*/
|
|
1891
|
+
stats() {
|
|
1892
|
+
const s = this.analyzer.stats();
|
|
190
1893
|
return {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
1894
|
+
totalFrames: s.totalFrames,
|
|
1895
|
+
totalSamples: s.totalSamples,
|
|
1896
|
+
durationSeconds: s.durationSeconds,
|
|
1897
|
+
estimate: {
|
|
1898
|
+
bpm: s.estimate.bpm,
|
|
1899
|
+
bpmConfidence: s.estimate.bpmConfidence,
|
|
1900
|
+
bpmCandidateCount: s.estimate.bpmCandidateCount,
|
|
1901
|
+
key: s.estimate.key,
|
|
1902
|
+
keyMinor: s.estimate.keyMinor,
|
|
1903
|
+
keyConfidence: s.estimate.keyConfidence,
|
|
1904
|
+
chordRoot: s.estimate.chordRoot,
|
|
1905
|
+
chordQuality: s.estimate.chordQuality,
|
|
1906
|
+
chordConfidence: s.estimate.chordConfidence,
|
|
1907
|
+
chordStartTime: s.estimate.chordStartTime,
|
|
1908
|
+
chordProgression: s.estimate.chordProgression.map((c) => ({
|
|
1909
|
+
root: c.root,
|
|
1910
|
+
quality: c.quality,
|
|
1911
|
+
startTime: c.startTime,
|
|
1912
|
+
confidence: c.confidence
|
|
1913
|
+
})),
|
|
1914
|
+
barChordProgression: s.estimate.barChordProgression.map((c) => ({
|
|
1915
|
+
barIndex: c.barIndex,
|
|
1916
|
+
root: c.root,
|
|
1917
|
+
quality: c.quality,
|
|
1918
|
+
startTime: c.startTime,
|
|
1919
|
+
confidence: c.confidence
|
|
210
1920
|
})),
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
1921
|
+
currentBar: s.estimate.currentBar,
|
|
1922
|
+
barDuration: s.estimate.barDuration,
|
|
1923
|
+
votedPattern: (s.estimate.votedPattern || []).map((c) => ({
|
|
1924
|
+
barIndex: c.barIndex,
|
|
1925
|
+
root: c.root,
|
|
1926
|
+
quality: c.quality,
|
|
1927
|
+
startTime: c.startTime,
|
|
1928
|
+
confidence: c.confidence
|
|
218
1929
|
})),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
1930
|
+
patternLength: s.estimate.patternLength,
|
|
1931
|
+
detectedPatternName: s.estimate.detectedPatternName || "",
|
|
1932
|
+
detectedPatternScore: s.estimate.detectedPatternScore || 0,
|
|
1933
|
+
allPatternScores: (s.estimate.allPatternScores || []).map((p) => ({
|
|
1934
|
+
name: p.name,
|
|
1935
|
+
score: p.score
|
|
1936
|
+
})),
|
|
1937
|
+
accumulatedSeconds: s.estimate.accumulatedSeconds,
|
|
1938
|
+
usedFrames: s.estimate.usedFrames,
|
|
1939
|
+
updated: s.estimate.updated
|
|
1940
|
+
}
|
|
223
1941
|
};
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
export
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
403
|
-
}
|
|
404
|
-
return module.melSpectrogram(samples, sampleRate, nFft, hopLength, nMels);
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Compute MFCC (Mel-Frequency Cepstral Coefficients).
|
|
408
|
-
*
|
|
409
|
-
* @param samples - Audio samples (mono, float32)
|
|
410
|
-
* @param sampleRate - Sample rate in Hz
|
|
411
|
-
* @param nFft - FFT size (default: 2048)
|
|
412
|
-
* @param hopLength - Hop length (default: 512)
|
|
413
|
-
* @param nMels - Number of Mel bands (default: 128)
|
|
414
|
-
* @param nMfcc - Number of MFCC coefficients (default: 13)
|
|
415
|
-
* @returns MFCC result
|
|
416
|
-
*/
|
|
417
|
-
export function mfcc(samples, sampleRate, nFft = 2048, hopLength = 512, nMels = 128, nMfcc = 13) {
|
|
418
|
-
if (!module) {
|
|
419
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
420
|
-
}
|
|
421
|
-
return module.mfcc(samples, sampleRate, nFft, hopLength, nMels, nMfcc);
|
|
422
|
-
}
|
|
423
|
-
// ============================================================================
|
|
424
|
-
// Features - Chroma
|
|
425
|
-
// ============================================================================
|
|
426
|
-
/**
|
|
427
|
-
* Compute chromagram (pitch class distribution).
|
|
428
|
-
*
|
|
429
|
-
* @param samples - Audio samples (mono, float32)
|
|
430
|
-
* @param sampleRate - Sample rate in Hz
|
|
431
|
-
* @param nFft - FFT size (default: 2048)
|
|
432
|
-
* @param hopLength - Hop length (default: 512)
|
|
433
|
-
* @returns Chroma features result
|
|
434
|
-
*/
|
|
435
|
-
export function chroma(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
436
|
-
if (!module) {
|
|
437
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
438
|
-
}
|
|
439
|
-
return module.chroma(samples, sampleRate, nFft, hopLength);
|
|
440
|
-
}
|
|
441
|
-
// ============================================================================
|
|
442
|
-
// Features - Spectral
|
|
443
|
-
// ============================================================================
|
|
444
|
-
/**
|
|
445
|
-
* Compute spectral centroid (center of mass of spectrum).
|
|
446
|
-
*
|
|
447
|
-
* @param samples - Audio samples (mono, float32)
|
|
448
|
-
* @param sampleRate - Sample rate in Hz
|
|
449
|
-
* @param nFft - FFT size (default: 2048)
|
|
450
|
-
* @param hopLength - Hop length (default: 512)
|
|
451
|
-
* @returns Spectral centroid in Hz for each frame
|
|
452
|
-
*/
|
|
453
|
-
export function spectralCentroid(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
454
|
-
if (!module) {
|
|
455
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
456
|
-
}
|
|
457
|
-
return module.spectralCentroid(samples, sampleRate, nFft, hopLength);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Compute spectral bandwidth.
|
|
461
|
-
*
|
|
462
|
-
* @param samples - Audio samples (mono, float32)
|
|
463
|
-
* @param sampleRate - Sample rate in Hz
|
|
464
|
-
* @param nFft - FFT size (default: 2048)
|
|
465
|
-
* @param hopLength - Hop length (default: 512)
|
|
466
|
-
* @returns Spectral bandwidth in Hz for each frame
|
|
467
|
-
*/
|
|
468
|
-
export function spectralBandwidth(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
469
|
-
if (!module) {
|
|
470
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
471
|
-
}
|
|
472
|
-
return module.spectralBandwidth(samples, sampleRate, nFft, hopLength);
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* Compute spectral rolloff frequency.
|
|
476
|
-
*
|
|
477
|
-
* @param samples - Audio samples (mono, float32)
|
|
478
|
-
* @param sampleRate - Sample rate in Hz
|
|
479
|
-
* @param nFft - FFT size (default: 2048)
|
|
480
|
-
* @param hopLength - Hop length (default: 512)
|
|
481
|
-
* @param rollPercent - Percentage threshold (default: 0.85)
|
|
482
|
-
* @returns Rolloff frequency in Hz for each frame
|
|
483
|
-
*/
|
|
484
|
-
export function spectralRolloff(samples, sampleRate, nFft = 2048, hopLength = 512, rollPercent = 0.85) {
|
|
485
|
-
if (!module) {
|
|
486
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
487
|
-
}
|
|
488
|
-
return module.spectralRolloff(samples, sampleRate, nFft, hopLength, rollPercent);
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Compute spectral flatness.
|
|
492
|
-
*
|
|
493
|
-
* @param samples - Audio samples (mono, float32)
|
|
494
|
-
* @param sampleRate - Sample rate in Hz
|
|
495
|
-
* @param nFft - FFT size (default: 2048)
|
|
496
|
-
* @param hopLength - Hop length (default: 512)
|
|
497
|
-
* @returns Spectral flatness for each frame (0 = tonal, 1 = noise-like)
|
|
498
|
-
*/
|
|
499
|
-
export function spectralFlatness(samples, sampleRate, nFft = 2048, hopLength = 512) {
|
|
500
|
-
if (!module) {
|
|
501
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
502
|
-
}
|
|
503
|
-
return module.spectralFlatness(samples, sampleRate, nFft, hopLength);
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Compute zero crossing rate.
|
|
507
|
-
*
|
|
508
|
-
* @param samples - Audio samples (mono, float32)
|
|
509
|
-
* @param sampleRate - Sample rate in Hz
|
|
510
|
-
* @param frameLength - Frame length (default: 2048)
|
|
511
|
-
* @param hopLength - Hop length (default: 512)
|
|
512
|
-
* @returns Zero crossing rate for each frame
|
|
513
|
-
*/
|
|
514
|
-
export function zeroCrossingRate(samples, sampleRate, frameLength = 2048, hopLength = 512) {
|
|
515
|
-
if (!module) {
|
|
516
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
517
|
-
}
|
|
518
|
-
return module.zeroCrossingRate(samples, sampleRate, frameLength, hopLength);
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Compute RMS energy.
|
|
522
|
-
*
|
|
523
|
-
* @param samples - Audio samples (mono, float32)
|
|
524
|
-
* @param sampleRate - Sample rate in Hz
|
|
525
|
-
* @param frameLength - Frame length (default: 2048)
|
|
526
|
-
* @param hopLength - Hop length (default: 512)
|
|
527
|
-
* @returns RMS energy for each frame
|
|
528
|
-
*/
|
|
529
|
-
export function rmsEnergy(samples, sampleRate, frameLength = 2048, hopLength = 512) {
|
|
530
|
-
if (!module) {
|
|
531
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
532
|
-
}
|
|
533
|
-
return module.rmsEnergy(samples, sampleRate, frameLength, hopLength);
|
|
534
|
-
}
|
|
535
|
-
// ============================================================================
|
|
536
|
-
// Features - Pitch
|
|
537
|
-
// ============================================================================
|
|
538
|
-
/**
|
|
539
|
-
* Detect pitch using YIN algorithm.
|
|
540
|
-
*
|
|
541
|
-
* @param samples - Audio samples (mono, float32)
|
|
542
|
-
* @param sampleRate - Sample rate in Hz
|
|
543
|
-
* @param frameLength - Frame length (default: 2048)
|
|
544
|
-
* @param hopLength - Hop length (default: 512)
|
|
545
|
-
* @param fmin - Minimum frequency in Hz (default: 65)
|
|
546
|
-
* @param fmax - Maximum frequency in Hz (default: 2093)
|
|
547
|
-
* @param threshold - YIN threshold (default: 0.3)
|
|
548
|
-
* @returns Pitch detection result
|
|
549
|
-
*/
|
|
550
|
-
export function pitchYin(samples, sampleRate, frameLength = 2048, hopLength = 512, fmin = 65.0, fmax = 2093.0, threshold = 0.3) {
|
|
551
|
-
if (!module) {
|
|
552
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
553
|
-
}
|
|
554
|
-
return module.pitchYin(samples, sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* Detect pitch using pYIN algorithm (probabilistic YIN with HMM smoothing).
|
|
558
|
-
*
|
|
559
|
-
* @param samples - Audio samples (mono, float32)
|
|
560
|
-
* @param sampleRate - Sample rate in Hz
|
|
561
|
-
* @param frameLength - Frame length (default: 2048)
|
|
562
|
-
* @param hopLength - Hop length (default: 512)
|
|
563
|
-
* @param fmin - Minimum frequency in Hz (default: 65)
|
|
564
|
-
* @param fmax - Maximum frequency in Hz (default: 2093)
|
|
565
|
-
* @param threshold - YIN threshold (default: 0.3)
|
|
566
|
-
* @returns Pitch detection result
|
|
567
|
-
*/
|
|
568
|
-
export function pitchPyin(samples, sampleRate, frameLength = 2048, hopLength = 512, fmin = 65.0, fmax = 2093.0, threshold = 0.3) {
|
|
569
|
-
if (!module) {
|
|
570
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
571
|
-
}
|
|
572
|
-
return module.pitchPyin(samples, sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
573
|
-
}
|
|
574
|
-
// ============================================================================
|
|
575
|
-
// Core - Unit Conversion
|
|
576
|
-
// ============================================================================
|
|
577
|
-
/**
|
|
578
|
-
* Convert frequency in Hz to Mel scale.
|
|
579
|
-
*
|
|
580
|
-
* @param hz - Frequency in Hz
|
|
581
|
-
* @returns Mel frequency
|
|
582
|
-
*/
|
|
583
|
-
export function hzToMel(hz) {
|
|
584
|
-
if (!module) {
|
|
585
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
586
|
-
}
|
|
587
|
-
return module.hzToMel(hz);
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Convert Mel scale to frequency in Hz.
|
|
591
|
-
*
|
|
592
|
-
* @param mel - Mel frequency
|
|
593
|
-
* @returns Frequency in Hz
|
|
594
|
-
*/
|
|
595
|
-
export function melToHz(mel) {
|
|
596
|
-
if (!module) {
|
|
597
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
598
|
-
}
|
|
599
|
-
return module.melToHz(mel);
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Convert frequency in Hz to MIDI note number.
|
|
603
|
-
*
|
|
604
|
-
* @param hz - Frequency in Hz
|
|
605
|
-
* @returns MIDI note number (A4 = 440 Hz = 69)
|
|
606
|
-
*/
|
|
607
|
-
export function hzToMidi(hz) {
|
|
608
|
-
if (!module) {
|
|
609
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
610
|
-
}
|
|
611
|
-
return module.hzToMidi(hz);
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Convert MIDI note number to frequency in Hz.
|
|
615
|
-
*
|
|
616
|
-
* @param midi - MIDI note number
|
|
617
|
-
* @returns Frequency in Hz
|
|
618
|
-
*/
|
|
619
|
-
export function midiToHz(midi) {
|
|
620
|
-
if (!module) {
|
|
621
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
622
|
-
}
|
|
623
|
-
return module.midiToHz(midi);
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Convert frequency in Hz to note name.
|
|
627
|
-
*
|
|
628
|
-
* @param hz - Frequency in Hz
|
|
629
|
-
* @returns Note name (e.g., "A4", "C#5")
|
|
630
|
-
*/
|
|
631
|
-
export function hzToNote(hz) {
|
|
632
|
-
if (!module) {
|
|
633
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
634
|
-
}
|
|
635
|
-
return module.hzToNote(hz);
|
|
636
|
-
}
|
|
637
|
-
/**
|
|
638
|
-
* Convert note name to frequency in Hz.
|
|
639
|
-
*
|
|
640
|
-
* @param note - Note name (e.g., "A4", "C#5")
|
|
641
|
-
* @returns Frequency in Hz
|
|
642
|
-
*/
|
|
643
|
-
export function noteToHz(note) {
|
|
644
|
-
if (!module) {
|
|
645
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
646
|
-
}
|
|
647
|
-
return module.noteToHz(note);
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* Convert frame index to time in seconds.
|
|
651
|
-
*
|
|
652
|
-
* @param frames - Frame index
|
|
653
|
-
* @param sr - Sample rate in Hz
|
|
654
|
-
* @param hopLength - Hop length in samples
|
|
655
|
-
* @returns Time in seconds
|
|
656
|
-
*/
|
|
657
|
-
export function framesToTime(frames, sr, hopLength) {
|
|
658
|
-
if (!module) {
|
|
659
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
660
|
-
}
|
|
661
|
-
return module.framesToTime(frames, sr, hopLength);
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Convert time in seconds to frame index.
|
|
665
|
-
*
|
|
666
|
-
* @param time - Time in seconds
|
|
667
|
-
* @param sr - Sample rate in Hz
|
|
668
|
-
* @param hopLength - Hop length in samples
|
|
669
|
-
* @returns Frame index
|
|
670
|
-
*/
|
|
671
|
-
export function timeToFrames(time, sr, hopLength) {
|
|
672
|
-
if (!module) {
|
|
673
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
674
|
-
}
|
|
675
|
-
return module.timeToFrames(time, sr, hopLength);
|
|
676
|
-
}
|
|
677
|
-
// ============================================================================
|
|
678
|
-
// Core - Resample
|
|
679
|
-
// ============================================================================
|
|
680
|
-
/**
|
|
681
|
-
* Resample audio to a different sample rate.
|
|
682
|
-
*
|
|
683
|
-
* @param samples - Audio samples (mono, float32)
|
|
684
|
-
* @param srcSr - Source sample rate in Hz
|
|
685
|
-
* @param targetSr - Target sample rate in Hz
|
|
686
|
-
* @returns Resampled audio
|
|
687
|
-
*/
|
|
688
|
-
export function resample(samples, srcSr, targetSr) {
|
|
689
|
-
if (!module) {
|
|
690
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
691
|
-
}
|
|
692
|
-
return module.resample(samples, srcSr, targetSr);
|
|
693
|
-
}
|
|
694
|
-
// ============================================================================
|
|
695
|
-
// Audio Class
|
|
696
|
-
// ============================================================================
|
|
697
|
-
/**
|
|
698
|
-
* Wrapper around audio data that exposes all analysis and feature functions as instance methods.
|
|
699
|
-
*
|
|
700
|
-
* @example
|
|
701
|
-
* ```typescript
|
|
702
|
-
* import { init, Audio } from '@libraz/sonare';
|
|
703
|
-
*
|
|
704
|
-
* await init();
|
|
705
|
-
*
|
|
706
|
-
* const audio = Audio.fromBuffer(samples, 44100);
|
|
707
|
-
* console.log('BPM:', audio.detectBpm());
|
|
708
|
-
* console.log('Key:', audio.detectKey().name);
|
|
709
|
-
*
|
|
710
|
-
* const mel = audio.melSpectrogram();
|
|
711
|
-
* ```
|
|
712
|
-
*/
|
|
713
|
-
export class Audio {
|
|
714
|
-
constructor(samples, sampleRate) {
|
|
715
|
-
this._samples = samples;
|
|
716
|
-
this._sampleRate = sampleRate;
|
|
717
|
-
}
|
|
718
|
-
/** Create an Audio instance from raw sample data. */
|
|
719
|
-
static fromBuffer(samples, sampleRate) {
|
|
720
|
-
return new Audio(samples, sampleRate);
|
|
721
|
-
}
|
|
722
|
-
/** The raw audio samples. */
|
|
723
|
-
get data() {
|
|
724
|
-
return this._samples;
|
|
725
|
-
}
|
|
726
|
-
/** Number of samples. */
|
|
727
|
-
get length() {
|
|
728
|
-
return this._samples.length;
|
|
729
|
-
}
|
|
730
|
-
/** Sample rate in Hz. */
|
|
731
|
-
get sampleRate() {
|
|
732
|
-
return this._sampleRate;
|
|
733
|
-
}
|
|
734
|
-
/** Duration in seconds. */
|
|
735
|
-
get duration() {
|
|
736
|
-
return this._samples.length / this._sampleRate;
|
|
737
|
-
}
|
|
738
|
-
// -- Analysis --
|
|
739
|
-
detectBpm() {
|
|
740
|
-
return detectBpm(this._samples, this._sampleRate);
|
|
741
|
-
}
|
|
742
|
-
detectKey() {
|
|
743
|
-
return detectKey(this._samples, this._sampleRate);
|
|
744
|
-
}
|
|
745
|
-
detectOnsets() {
|
|
746
|
-
return detectOnsets(this._samples, this._sampleRate);
|
|
747
|
-
}
|
|
748
|
-
detectBeats() {
|
|
749
|
-
return detectBeats(this._samples, this._sampleRate);
|
|
750
|
-
}
|
|
751
|
-
analyze() {
|
|
752
|
-
return analyze(this._samples, this._sampleRate);
|
|
753
|
-
}
|
|
754
|
-
analyzeWithProgress(onProgress) {
|
|
755
|
-
return analyzeWithProgress(this._samples, this._sampleRate, onProgress);
|
|
756
|
-
}
|
|
757
|
-
// -- Effects --
|
|
758
|
-
hpss(kernelHarmonic = 31, kernelPercussive = 31) {
|
|
759
|
-
return hpss(this._samples, this._sampleRate, kernelHarmonic, kernelPercussive);
|
|
760
|
-
}
|
|
761
|
-
harmonic() {
|
|
762
|
-
return harmonic(this._samples, this._sampleRate);
|
|
763
|
-
}
|
|
764
|
-
percussive() {
|
|
765
|
-
return percussive(this._samples, this._sampleRate);
|
|
766
|
-
}
|
|
767
|
-
timeStretch(rate) {
|
|
768
|
-
return timeStretch(this._samples, this._sampleRate, rate);
|
|
769
|
-
}
|
|
770
|
-
pitchShift(semitones) {
|
|
771
|
-
return pitchShift(this._samples, this._sampleRate, semitones);
|
|
772
|
-
}
|
|
773
|
-
normalize(targetDb = 0.0) {
|
|
774
|
-
return normalize(this._samples, this._sampleRate, targetDb);
|
|
775
|
-
}
|
|
776
|
-
trim(thresholdDb = -60.0) {
|
|
777
|
-
return trim(this._samples, this._sampleRate, thresholdDb);
|
|
778
|
-
}
|
|
779
|
-
// -- Features --
|
|
780
|
-
stft(nFft = 2048, hopLength = 512) {
|
|
781
|
-
return stft(this._samples, this._sampleRate, nFft, hopLength);
|
|
782
|
-
}
|
|
783
|
-
stftDb(nFft = 2048, hopLength = 512) {
|
|
784
|
-
return stftDb(this._samples, this._sampleRate, nFft, hopLength);
|
|
785
|
-
}
|
|
786
|
-
melSpectrogram(nFft = 2048, hopLength = 512, nMels = 128) {
|
|
787
|
-
return melSpectrogram(this._samples, this._sampleRate, nFft, hopLength, nMels);
|
|
788
|
-
}
|
|
789
|
-
mfcc(nFft = 2048, hopLength = 512, nMels = 128, nMfcc = 13) {
|
|
790
|
-
return mfcc(this._samples, this._sampleRate, nFft, hopLength, nMels, nMfcc);
|
|
791
|
-
}
|
|
792
|
-
chroma(nFft = 2048, hopLength = 512) {
|
|
793
|
-
return chroma(this._samples, this._sampleRate, nFft, hopLength);
|
|
794
|
-
}
|
|
795
|
-
spectralCentroid(nFft = 2048, hopLength = 512) {
|
|
796
|
-
return spectralCentroid(this._samples, this._sampleRate, nFft, hopLength);
|
|
797
|
-
}
|
|
798
|
-
spectralBandwidth(nFft = 2048, hopLength = 512) {
|
|
799
|
-
return spectralBandwidth(this._samples, this._sampleRate, nFft, hopLength);
|
|
800
|
-
}
|
|
801
|
-
spectralRolloff(nFft = 2048, hopLength = 512, rollPercent = 0.85) {
|
|
802
|
-
return spectralRolloff(this._samples, this._sampleRate, nFft, hopLength, rollPercent);
|
|
803
|
-
}
|
|
804
|
-
spectralFlatness(nFft = 2048, hopLength = 512) {
|
|
805
|
-
return spectralFlatness(this._samples, this._sampleRate, nFft, hopLength);
|
|
806
|
-
}
|
|
807
|
-
zeroCrossingRate(frameLength = 2048, hopLength = 512) {
|
|
808
|
-
return zeroCrossingRate(this._samples, this._sampleRate, frameLength, hopLength);
|
|
809
|
-
}
|
|
810
|
-
rmsEnergy(frameLength = 2048, hopLength = 512) {
|
|
811
|
-
return rmsEnergy(this._samples, this._sampleRate, frameLength, hopLength);
|
|
812
|
-
}
|
|
813
|
-
pitchYin(frameLength = 2048, hopLength = 512, fmin = 65.0, fmax = 2093.0, threshold = 0.3) {
|
|
814
|
-
return pitchYin(this._samples, this._sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
815
|
-
}
|
|
816
|
-
pitchPyin(frameLength = 2048, hopLength = 512, fmin = 65.0, fmax = 2093.0, threshold = 0.3) {
|
|
817
|
-
return pitchPyin(this._samples, this._sampleRate, frameLength, hopLength, fmin, fmax, threshold);
|
|
818
|
-
}
|
|
819
|
-
resample(targetSr) {
|
|
820
|
-
return resample(this._samples, this._sampleRate, targetSr);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
// ============================================================================
|
|
824
|
-
// StreamAnalyzer Class
|
|
825
|
-
// ============================================================================
|
|
826
|
-
/**
|
|
827
|
-
* Real-time streaming audio analyzer.
|
|
828
|
-
*
|
|
829
|
-
* @example
|
|
830
|
-
* ```typescript
|
|
831
|
-
* import { init, StreamAnalyzer } from '@libraz/sonare';
|
|
832
|
-
*
|
|
833
|
-
* await init();
|
|
834
|
-
*
|
|
835
|
-
* const analyzer = new StreamAnalyzer({ sampleRate: 44100 });
|
|
836
|
-
*
|
|
837
|
-
* // In audio processing callback
|
|
838
|
-
* analyzer.process(samples);
|
|
839
|
-
*
|
|
840
|
-
* // Get current analysis state
|
|
841
|
-
* const stats = analyzer.stats();
|
|
842
|
-
* console.log('BPM:', stats.estimate.bpm);
|
|
843
|
-
* console.log('Key:', stats.estimate.key);
|
|
844
|
-
* console.log('Chord progression:', stats.estimate.chordProgression);
|
|
845
|
-
* ```
|
|
846
|
-
*/
|
|
847
|
-
export class StreamAnalyzer {
|
|
848
|
-
/**
|
|
849
|
-
* Create a new StreamAnalyzer.
|
|
850
|
-
*
|
|
851
|
-
* @param config - Configuration options
|
|
852
|
-
*/
|
|
853
|
-
constructor(config) {
|
|
854
|
-
if (!module) {
|
|
855
|
-
throw new Error('Module not initialized. Call init() first.');
|
|
856
|
-
}
|
|
857
|
-
this.analyzer = new module.StreamAnalyzer(config.sampleRate, config.nFft ?? 2048, config.hopLength ?? 512, config.nMels ?? 128, config.computeMel ?? true, config.computeChroma ?? true, config.computeOnset ?? true, config.emitEveryNFrames ?? 1);
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Process audio samples.
|
|
861
|
-
*
|
|
862
|
-
* @param samples - Audio samples (mono, float32)
|
|
863
|
-
*/
|
|
864
|
-
process(samples) {
|
|
865
|
-
this.analyzer.process(samples);
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* Process audio samples with explicit sample offset.
|
|
869
|
-
*
|
|
870
|
-
* @param samples - Audio samples (mono, float32)
|
|
871
|
-
* @param sampleOffset - Cumulative sample count at start of this chunk
|
|
872
|
-
*/
|
|
873
|
-
processWithOffset(samples, sampleOffset) {
|
|
874
|
-
this.analyzer.processWithOffset(samples, sampleOffset);
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Get the number of frames available to read.
|
|
878
|
-
*/
|
|
879
|
-
availableFrames() {
|
|
880
|
-
return this.analyzer.availableFrames();
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Read processed frames as Structure of Arrays.
|
|
884
|
-
*
|
|
885
|
-
* @param maxFrames - Maximum number of frames to read
|
|
886
|
-
* @returns Frame buffer with analysis results
|
|
887
|
-
*/
|
|
888
|
-
readFrames(maxFrames) {
|
|
889
|
-
return this.analyzer.readFramesSoa(maxFrames);
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* Reset the analyzer state.
|
|
893
|
-
*
|
|
894
|
-
* @param baseSampleOffset - Starting sample offset (default 0)
|
|
895
|
-
*/
|
|
896
|
-
reset(baseSampleOffset = 0) {
|
|
897
|
-
this.analyzer.reset(baseSampleOffset);
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* Get current statistics and progressive estimates.
|
|
901
|
-
*
|
|
902
|
-
* @returns Analyzer statistics including BPM, key, and chord progression
|
|
903
|
-
*/
|
|
904
|
-
stats() {
|
|
905
|
-
const s = this.analyzer.stats();
|
|
906
|
-
return {
|
|
907
|
-
totalFrames: s.totalFrames,
|
|
908
|
-
totalSamples: s.totalSamples,
|
|
909
|
-
durationSeconds: s.durationSeconds,
|
|
910
|
-
estimate: {
|
|
911
|
-
bpm: s.estimate.bpm,
|
|
912
|
-
bpmConfidence: s.estimate.bpmConfidence,
|
|
913
|
-
bpmCandidateCount: s.estimate.bpmCandidateCount,
|
|
914
|
-
key: s.estimate.key,
|
|
915
|
-
keyMinor: s.estimate.keyMinor,
|
|
916
|
-
keyConfidence: s.estimate.keyConfidence,
|
|
917
|
-
chordRoot: s.estimate.chordRoot,
|
|
918
|
-
chordQuality: s.estimate.chordQuality,
|
|
919
|
-
chordConfidence: s.estimate.chordConfidence,
|
|
920
|
-
chordProgression: s.estimate.chordProgression.map((c) => ({
|
|
921
|
-
root: c.root,
|
|
922
|
-
quality: c.quality,
|
|
923
|
-
startTime: c.startTime,
|
|
924
|
-
confidence: c.confidence,
|
|
925
|
-
})),
|
|
926
|
-
barChordProgression: s.estimate.barChordProgression.map((c) => ({
|
|
927
|
-
barIndex: c.barIndex,
|
|
928
|
-
root: c.root,
|
|
929
|
-
quality: c.quality,
|
|
930
|
-
startTime: c.startTime,
|
|
931
|
-
confidence: c.confidence,
|
|
932
|
-
})),
|
|
933
|
-
currentBar: s.estimate.currentBar,
|
|
934
|
-
barDuration: s.estimate.barDuration,
|
|
935
|
-
votedPattern: (s.estimate.votedPattern || []).map((c) => ({
|
|
936
|
-
barIndex: c.barIndex,
|
|
937
|
-
root: c.root,
|
|
938
|
-
quality: c.quality,
|
|
939
|
-
startTime: c.startTime,
|
|
940
|
-
confidence: c.confidence,
|
|
941
|
-
})),
|
|
942
|
-
patternLength: s.estimate.patternLength,
|
|
943
|
-
detectedPatternName: s.estimate.detectedPatternName || '',
|
|
944
|
-
detectedPatternScore: s.estimate.detectedPatternScore || 0,
|
|
945
|
-
allPatternScores: (s.estimate.allPatternScores || []).map((p) => ({
|
|
946
|
-
name: p.name,
|
|
947
|
-
score: p.score,
|
|
948
|
-
})),
|
|
949
|
-
accumulatedSeconds: s.estimate.accumulatedSeconds,
|
|
950
|
-
usedFrames: s.estimate.usedFrames,
|
|
951
|
-
updated: s.estimate.updated,
|
|
952
|
-
},
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Get total frames processed.
|
|
957
|
-
*/
|
|
958
|
-
frameCount() {
|
|
959
|
-
return this.analyzer.frameCount();
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Get current time position in seconds.
|
|
963
|
-
*/
|
|
964
|
-
currentTime() {
|
|
965
|
-
return this.analyzer.currentTime();
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Get the sample rate.
|
|
969
|
-
*/
|
|
970
|
-
sampleRate() {
|
|
971
|
-
return this.analyzer.sampleRate();
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Set the expected total duration for pattern lock timing.
|
|
975
|
-
*
|
|
976
|
-
* @param durationSeconds - Total duration in seconds
|
|
977
|
-
*/
|
|
978
|
-
setExpectedDuration(durationSeconds) {
|
|
979
|
-
this.analyzer.setExpectedDuration(durationSeconds);
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Set normalization gain for loud/compressed audio.
|
|
983
|
-
*
|
|
984
|
-
* @param gain - Gain factor to apply (e.g., 0.5 for -6dB reduction)
|
|
985
|
-
*/
|
|
986
|
-
setNormalizationGain(gain) {
|
|
987
|
-
this.analyzer.setNormalizationGain(gain);
|
|
988
|
-
}
|
|
989
|
-
/**
|
|
990
|
-
* Set tuning reference frequency for non-standard tuning.
|
|
991
|
-
*
|
|
992
|
-
* @param refHz - Reference frequency for A4 (default 440 Hz)
|
|
993
|
-
* @example
|
|
994
|
-
* // If audio is 1 semitone sharp (A4 = 466.16 Hz)
|
|
995
|
-
* analyzer.setTuningRefHz(466.16);
|
|
996
|
-
* // If audio is 1 semitone flat (A4 = 415.30 Hz)
|
|
997
|
-
* analyzer.setTuningRefHz(415.30);
|
|
998
|
-
*/
|
|
999
|
-
setTuningRefHz(refHz) {
|
|
1000
|
-
this.analyzer.setTuningRefHz(refHz);
|
|
1001
|
-
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Release resources. Call when done using the analyzer.
|
|
1004
|
-
*/
|
|
1005
|
-
dispose() {
|
|
1006
|
-
this.analyzer.delete();
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
// ============================================================================
|
|
1010
|
-
// Re-exports
|
|
1011
|
-
// ============================================================================
|
|
1012
|
-
export { PitchClass as Pitch };
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Get total frames processed.
|
|
1945
|
+
*/
|
|
1946
|
+
frameCount() {
|
|
1947
|
+
return this.analyzer.frameCount();
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Get current time position in seconds.
|
|
1951
|
+
*/
|
|
1952
|
+
currentTime() {
|
|
1953
|
+
return this.analyzer.currentTime();
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Get the sample rate.
|
|
1957
|
+
*/
|
|
1958
|
+
sampleRate() {
|
|
1959
|
+
return this.analyzer.sampleRate();
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Set the expected total duration for pattern lock timing.
|
|
1963
|
+
*
|
|
1964
|
+
* @param durationSeconds - Total duration in seconds
|
|
1965
|
+
*/
|
|
1966
|
+
setExpectedDuration(durationSeconds) {
|
|
1967
|
+
this.analyzer.setExpectedDuration(durationSeconds);
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Set normalization gain for loud/compressed audio.
|
|
1971
|
+
*
|
|
1972
|
+
* @param gain - Gain factor to apply (e.g., 0.5 for -6dB reduction)
|
|
1973
|
+
*/
|
|
1974
|
+
setNormalizationGain(gain) {
|
|
1975
|
+
this.analyzer.setNormalizationGain(gain);
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Set tuning reference frequency for non-standard tuning.
|
|
1979
|
+
*
|
|
1980
|
+
* @param refHz - Reference frequency for A4 (default 440 Hz)
|
|
1981
|
+
* @example
|
|
1982
|
+
* // If audio is 1 semitone sharp (A4 = 466.16 Hz)
|
|
1983
|
+
* analyzer.setTuningRefHz(466.16);
|
|
1984
|
+
* // If audio is 1 semitone flat (A4 = 415.30 Hz)
|
|
1985
|
+
* analyzer.setTuningRefHz(415.30);
|
|
1986
|
+
*/
|
|
1987
|
+
setTuningRefHz(refHz) {
|
|
1988
|
+
this.analyzer.setTuningRefHz(refHz);
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Release resources. Call when done using the analyzer.
|
|
1992
|
+
*/
|
|
1993
|
+
dispose() {
|
|
1994
|
+
this.analyzer.delete();
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
export {
|
|
1998
|
+
Audio,
|
|
1999
|
+
ChordQuality,
|
|
2000
|
+
EXPECTED_ENGINE_ABI_VERSION,
|
|
2001
|
+
KeyProfile,
|
|
2002
|
+
Mixer,
|
|
2003
|
+
Mode,
|
|
2004
|
+
PitchClass as Pitch,
|
|
2005
|
+
PitchClass,
|
|
2006
|
+
RealtimeEngine,
|
|
2007
|
+
SectionType,
|
|
2008
|
+
StreamAnalyzer,
|
|
2009
|
+
StreamingEqualizer,
|
|
2010
|
+
StreamingMasteringChain,
|
|
2011
|
+
amplitudeToDb,
|
|
2012
|
+
analyze,
|
|
2013
|
+
analyzeImpulseResponse,
|
|
2014
|
+
analyzeMelody,
|
|
2015
|
+
analyzeSections,
|
|
2016
|
+
analyzeWithProgress,
|
|
2017
|
+
chroma,
|
|
2018
|
+
cqt,
|
|
2019
|
+
cyclicTempogram,
|
|
2020
|
+
dbToAmplitude,
|
|
2021
|
+
dbToPower,
|
|
2022
|
+
deemphasis,
|
|
2023
|
+
detectAcoustic,
|
|
2024
|
+
detectBeats,
|
|
2025
|
+
detectBpm,
|
|
2026
|
+
detectChords,
|
|
2027
|
+
detectDownbeats,
|
|
2028
|
+
detectKey,
|
|
2029
|
+
detectKeyCandidates,
|
|
2030
|
+
detectOnsets,
|
|
2031
|
+
engineAbiVersion,
|
|
2032
|
+
engineCapabilities,
|
|
2033
|
+
fixFrames,
|
|
2034
|
+
fixLength,
|
|
2035
|
+
fourierTempogram,
|
|
2036
|
+
frameSignal,
|
|
2037
|
+
framesToSamples,
|
|
2038
|
+
framesToTime,
|
|
2039
|
+
harmonic,
|
|
2040
|
+
hpss,
|
|
2041
|
+
hzToMel,
|
|
2042
|
+
hzToMidi,
|
|
2043
|
+
hzToNote,
|
|
2044
|
+
init,
|
|
2045
|
+
isInitialized,
|
|
2046
|
+
lufs,
|
|
2047
|
+
masterAudio,
|
|
2048
|
+
masterAudioStereo,
|
|
2049
|
+
mastering,
|
|
2050
|
+
masteringAssistantSuggest,
|
|
2051
|
+
masteringAudioProfile,
|
|
2052
|
+
masteringChain,
|
|
2053
|
+
masteringChainStereo,
|
|
2054
|
+
masteringChainStereoWithProgress,
|
|
2055
|
+
masteringChainWithProgress,
|
|
2056
|
+
masteringPairAnalysisNames,
|
|
2057
|
+
masteringPairAnalyze,
|
|
2058
|
+
masteringPairProcess,
|
|
2059
|
+
masteringPairProcessorNames,
|
|
2060
|
+
masteringPresetNames,
|
|
2061
|
+
masteringProcess,
|
|
2062
|
+
masteringProcessStereo,
|
|
2063
|
+
masteringProcessorNames,
|
|
2064
|
+
masteringStereoAnalysisNames,
|
|
2065
|
+
masteringStereoAnalyze,
|
|
2066
|
+
masteringStreamingPreview,
|
|
2067
|
+
melSpectrogram,
|
|
2068
|
+
melToAudio,
|
|
2069
|
+
melToHz,
|
|
2070
|
+
melToStft,
|
|
2071
|
+
mfcc,
|
|
2072
|
+
mfccToAudio,
|
|
2073
|
+
mfccToMel,
|
|
2074
|
+
midiToHz,
|
|
2075
|
+
mixStereo,
|
|
2076
|
+
mixerScenePresetJson,
|
|
2077
|
+
mixingScenePresetJson,
|
|
2078
|
+
mixingScenePresetNames,
|
|
2079
|
+
momentaryLufs,
|
|
2080
|
+
nnlsChroma,
|
|
2081
|
+
normalize,
|
|
2082
|
+
noteStretch,
|
|
2083
|
+
noteToHz,
|
|
2084
|
+
onsetEnvelope,
|
|
2085
|
+
padCenter,
|
|
2086
|
+
pcen,
|
|
2087
|
+
peakPick,
|
|
2088
|
+
percussive,
|
|
2089
|
+
pitchCorrectToMidi,
|
|
2090
|
+
pitchPyin,
|
|
2091
|
+
pitchShift,
|
|
2092
|
+
pitchYin,
|
|
2093
|
+
plp,
|
|
2094
|
+
powerToDb,
|
|
2095
|
+
preemphasis,
|
|
2096
|
+
resample,
|
|
2097
|
+
rmsEnergy,
|
|
2098
|
+
samplesToFrames,
|
|
2099
|
+
shortTermLufs,
|
|
2100
|
+
spectralBandwidth,
|
|
2101
|
+
spectralCentroid,
|
|
2102
|
+
spectralFlatness,
|
|
2103
|
+
spectralRolloff,
|
|
2104
|
+
splitSilence,
|
|
2105
|
+
stft,
|
|
2106
|
+
stftDb,
|
|
2107
|
+
tempogram,
|
|
2108
|
+
tempogramRatio,
|
|
2109
|
+
timeStretch,
|
|
2110
|
+
timeToFrames,
|
|
2111
|
+
tonnetz,
|
|
2112
|
+
trim,
|
|
2113
|
+
trimSilence,
|
|
2114
|
+
vectorNormalize,
|
|
2115
|
+
version,
|
|
2116
|
+
voiceChange,
|
|
2117
|
+
vqt,
|
|
2118
|
+
zeroCrossingRate
|
|
2119
|
+
};
|
|
1013
2120
|
//# sourceMappingURL=index.js.map
|