@libraz/libsonare 1.5.5 → 1.6.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 +109 -20
- package/dist/analysis.d.ts +5730 -0
- package/dist/analysis.js +2464 -0
- package/dist/analysis.js.map +1 -0
- package/dist/index.d.ts +759 -35
- package/dist/index.js +904 -209
- package/dist/index.js.map +1 -1
- package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
- package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
- package/dist/sonare-analysis.js +2 -0
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worker.d.ts +65 -0
- package/dist/worker.js +707 -0
- package/dist/worker.js.map +1 -0
- package/dist/worklet.d.ts +574 -86
- package/dist/worklet.js +1634 -235
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -11
- package/src/_chain_config.ts +5 -3
- package/src/analysis.ts +137 -0
- package/src/analysis_helpers.ts +6 -0
- package/src/clip_page_streamer.ts +62 -8
- package/src/codes.ts +51 -64
- package/src/effects_mastering.ts +1 -0
- package/src/errors.ts +1 -0
- package/src/feature_core.ts +140 -2
- package/src/feature_music.ts +17 -2
- package/src/feature_pitch.ts +70 -1
- package/src/feature_spectral.ts +231 -5
- package/src/feature_spectrogram.ts +159 -0
- package/src/features.ts +16 -1
- package/src/index.ts +101 -7
- package/src/mastering_chain.ts +20 -8
- package/src/mastering_core.ts +5 -0
- package/src/mastering_dynamics.ts +7 -1
- package/src/metering.ts +6 -1
- package/src/opfs_clip_pages.ts +26 -1
- package/src/project.ts +7 -0
- package/src/project_class.ts +62 -8
- package/src/project_internal.ts +24 -29
- package/src/project_types.ts +69 -10
- package/src/public_types.ts +68 -0
- package/src/public_types_mastering.ts +22 -0
- package/src/public_types_mixing.ts +11 -2
- package/src/public_types_music.ts +9 -0
- package/src/public_types_spectral.ts +33 -0
- package/src/quick_analysis.ts +25 -7
- package/src/realtime_engine.ts +153 -3
- package/src/realtime_voice_changer.ts +51 -77
- package/src/sonare-analysis.js.d.ts +8 -0
- package/src/sonare.js.d.ts +276 -6
- package/src/streaming_processors.ts +15 -0
- package/src/web_midi.ts +36 -4
- package/src/worker.ts +196 -0
- package/src/worker_client.ts +352 -0
- package/src/worker_protocol.ts +59 -0
- package/src/worklet/engine-automation.ts +2 -1
- package/src/worklet/engine-capture-facade.ts +13 -4
- package/src/worklet/engine-clips.ts +12 -3
- package/src/worklet/engine-node.ts +212 -4
- package/src/worklet/engine-offline.ts +7 -2
- package/src/worklet/engine-parameter-facade.ts +26 -5
- package/src/worklet/engine-processor.ts +383 -42
- package/src/worklet/engine-register.ts +8 -0
- package/src/worklet/engine-strips.ts +17 -1
- package/src/worklet/engine-sync.ts +2 -6
- package/src/worklet/engine-tempo-facade.ts +8 -6
- package/src/worklet/engine.ts +380 -13
- package/src/worklet/guards.ts +22 -1
- package/src/worklet/messages.ts +125 -7
- package/src/worklet/protocol.ts +301 -17
- package/src/worklet/voice-changer-processor.ts +12 -15
- package/src/worklet.ts +20 -1
package/dist/worker.js
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var SonareError = class extends Error {
|
|
3
|
+
constructor(code, codeName, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "SonareError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.codeName = codeName;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/module_state.ts
|
|
12
|
+
var wrappedModule = null;
|
|
13
|
+
function nativeExceptionPtr(error) {
|
|
14
|
+
if (typeof error === "number") {
|
|
15
|
+
return error;
|
|
16
|
+
}
|
|
17
|
+
if (error !== null && typeof error === "object") {
|
|
18
|
+
const ptr = error.excPtr;
|
|
19
|
+
if (typeof ptr === "number") {
|
|
20
|
+
return ptr;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function makeSonareError(raw, thrown) {
|
|
26
|
+
let code = 99 /* Unknown */;
|
|
27
|
+
let codeName = "Unknown";
|
|
28
|
+
let message = `libsonare native exception (${thrown})`;
|
|
29
|
+
try {
|
|
30
|
+
const info = raw.sonareExceptionInfo?.(thrown);
|
|
31
|
+
if (info) {
|
|
32
|
+
code = info.code ?? code;
|
|
33
|
+
codeName = info.codeName ?? codeName;
|
|
34
|
+
message = info.message || message;
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
return new SonareError(code, codeName, message);
|
|
39
|
+
}
|
|
40
|
+
function wrapModuleErrors(raw) {
|
|
41
|
+
const cache = /* @__PURE__ */ new Map();
|
|
42
|
+
const objectCache = /* @__PURE__ */ new WeakMap();
|
|
43
|
+
const convert = (error) => {
|
|
44
|
+
const ptr = nativeExceptionPtr(error);
|
|
45
|
+
if (ptr !== null) {
|
|
46
|
+
throw makeSonareError(raw, ptr);
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
};
|
|
50
|
+
const wrapNativeObject = (value) => {
|
|
51
|
+
if (value === null || typeof value !== "object") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof Promise) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
const objectValue = value;
|
|
58
|
+
const cached = objectCache.get(objectValue);
|
|
59
|
+
if (cached) {
|
|
60
|
+
return cached;
|
|
61
|
+
}
|
|
62
|
+
const methodCache = /* @__PURE__ */ new Map();
|
|
63
|
+
const wrapped = new Proxy(objectValue, {
|
|
64
|
+
get(target, prop, receiver) {
|
|
65
|
+
const member = Reflect.get(target, prop, receiver);
|
|
66
|
+
if (typeof member !== "function") {
|
|
67
|
+
return member;
|
|
68
|
+
}
|
|
69
|
+
const cachedMethod = methodCache.get(prop);
|
|
70
|
+
if (cachedMethod) {
|
|
71
|
+
return cachedMethod;
|
|
72
|
+
}
|
|
73
|
+
const method = member;
|
|
74
|
+
const wrappedMethod = (...args) => {
|
|
75
|
+
try {
|
|
76
|
+
return wrapNativeObject(Reflect.apply(method, target, args));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return convert(error);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
methodCache.set(prop, wrappedMethod);
|
|
82
|
+
return wrappedMethod;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
objectCache.set(objectValue, wrapped);
|
|
86
|
+
return wrapped;
|
|
87
|
+
};
|
|
88
|
+
const wrapFunction = (value) => {
|
|
89
|
+
const fnCache = /* @__PURE__ */ new Map();
|
|
90
|
+
return new Proxy(value, {
|
|
91
|
+
get(target, prop, receiver) {
|
|
92
|
+
const member = Reflect.get(target, prop, receiver);
|
|
93
|
+
if (typeof member !== "function") {
|
|
94
|
+
return member;
|
|
95
|
+
}
|
|
96
|
+
const cachedMember = fnCache.get(prop);
|
|
97
|
+
if (cachedMember) {
|
|
98
|
+
return cachedMember;
|
|
99
|
+
}
|
|
100
|
+
const fn = member;
|
|
101
|
+
const wrappedMember = (...args) => {
|
|
102
|
+
try {
|
|
103
|
+
return wrapNativeObject(Reflect.apply(fn, target, args));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return convert(error);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
fnCache.set(prop, wrappedMember);
|
|
109
|
+
return wrappedMember;
|
|
110
|
+
},
|
|
111
|
+
apply(t, thisArg, args) {
|
|
112
|
+
try {
|
|
113
|
+
return wrapNativeObject(Reflect.apply(t, thisArg, args));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return convert(error);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
construct(t, args, newTarget) {
|
|
119
|
+
try {
|
|
120
|
+
return wrapNativeObject(Reflect.construct(t, args, newTarget));
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return convert(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
return new Proxy(raw, {
|
|
128
|
+
get(target, prop, receiver) {
|
|
129
|
+
const value = Reflect.get(target, prop, receiver);
|
|
130
|
+
if (typeof value !== "function") {
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
const cached = cache.get(prop);
|
|
134
|
+
if (cached) {
|
|
135
|
+
return cached;
|
|
136
|
+
}
|
|
137
|
+
const wrapped = wrapFunction(value);
|
|
138
|
+
cache.set(prop, wrapped);
|
|
139
|
+
return wrapped;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function setSonareModule(module2) {
|
|
144
|
+
wrappedModule = wrapModuleErrors(module2);
|
|
145
|
+
}
|
|
146
|
+
function getSonareModule() {
|
|
147
|
+
if (!wrappedModule) {
|
|
148
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
149
|
+
}
|
|
150
|
+
return wrappedModule;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/validation.ts
|
|
154
|
+
var MIN_AUDIO_SAMPLE_RATE = 8e3;
|
|
155
|
+
var MAX_AUDIO_SAMPLE_RATE = 384e3;
|
|
156
|
+
function assertNonEmptySamples(fnName, samples, argName = "samples") {
|
|
157
|
+
if (samples.length === 0) {
|
|
158
|
+
throw new RangeError(`${fnName}: ${argName} must not be empty`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function assertFiniteSamples(fnName, samples, validate, argName = "samples") {
|
|
162
|
+
if (!validate) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
for (let i = 0; i < samples.length; i++) {
|
|
166
|
+
const v = samples[i];
|
|
167
|
+
if (!Number.isFinite(v)) {
|
|
168
|
+
throw new RangeError(`${fnName}: ${argName} contains NaN or Inf at index ${i}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function assertSamples(fnName, samples, validate, argName = "samples") {
|
|
173
|
+
assertNonEmptySamples(fnName, samples, argName);
|
|
174
|
+
assertFiniteSamples(fnName, samples, validate, argName);
|
|
175
|
+
}
|
|
176
|
+
function assertSampleRate(fnName, sampleRate) {
|
|
177
|
+
if (!Number.isInteger(sampleRate) || sampleRate < MIN_AUDIO_SAMPLE_RATE || sampleRate > MAX_AUDIO_SAMPLE_RATE) {
|
|
178
|
+
throw new RangeError(
|
|
179
|
+
`${fnName}: sampleRate out of supported range [${MIN_AUDIO_SAMPLE_RATE}, ${MAX_AUDIO_SAMPLE_RATE}]`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/_chain_config.ts
|
|
185
|
+
function flattenChainConfig(config) {
|
|
186
|
+
const out = {};
|
|
187
|
+
const walk = (node, prefix) => {
|
|
188
|
+
for (const [key, value] of Object.entries(node)) {
|
|
189
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
190
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
191
|
+
out[path] = value;
|
|
192
|
+
} else if (value !== null && typeof value === "object") {
|
|
193
|
+
walk(value, path);
|
|
194
|
+
} else if (value !== void 0) {
|
|
195
|
+
throw new TypeError(`Mastering override '${path}' must be a number or boolean.`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
walk(config, "");
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/mastering_chain.ts
|
|
204
|
+
function requireModule() {
|
|
205
|
+
return getSonareModule();
|
|
206
|
+
}
|
|
207
|
+
function masterAudioRequest(requestOrSamples, sampleRate, preset, overrides, onProgress) {
|
|
208
|
+
if (requestOrSamples instanceof Float32Array) {
|
|
209
|
+
return {
|
|
210
|
+
samples: requestOrSamples,
|
|
211
|
+
sampleRate,
|
|
212
|
+
preset,
|
|
213
|
+
overrides: overrides ?? {},
|
|
214
|
+
onProgress
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return requestOrSamples;
|
|
218
|
+
}
|
|
219
|
+
function masterAudioStereoRequest(requestOrLeft, right, sampleRate, preset, overrides, onProgress) {
|
|
220
|
+
if (requestOrLeft instanceof Float32Array) {
|
|
221
|
+
return {
|
|
222
|
+
left: requestOrLeft,
|
|
223
|
+
right,
|
|
224
|
+
sampleRate,
|
|
225
|
+
preset,
|
|
226
|
+
overrides: overrides ?? {},
|
|
227
|
+
onProgress
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return requestOrLeft;
|
|
231
|
+
}
|
|
232
|
+
function masterAudio(samples, sampleRate = 22050, presetName = "pop", overrides = {}, onProgress) {
|
|
233
|
+
const request = masterAudioRequest(samples, sampleRate, presetName, overrides, onProgress);
|
|
234
|
+
const flat = flattenChainConfig(request.overrides ?? {});
|
|
235
|
+
if (request.onProgress || request.cancel) {
|
|
236
|
+
return requireModule().masterAudioWithProgress(
|
|
237
|
+
request.preset ?? "pop",
|
|
238
|
+
request.samples,
|
|
239
|
+
request.sampleRate ?? 22050,
|
|
240
|
+
flat,
|
|
241
|
+
request.onProgress ?? (() => {
|
|
242
|
+
}),
|
|
243
|
+
request.cancel ?? (() => false)
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return requireModule().masterAudio(
|
|
247
|
+
request.preset ?? "pop",
|
|
248
|
+
request.samples,
|
|
249
|
+
request.sampleRate ?? 22050,
|
|
250
|
+
flat
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
function masterAudioStereo(left, right = void 0, sampleRate = 22050, presetName = "pop", overrides = {}, onProgress) {
|
|
254
|
+
const request = masterAudioStereoRequest(
|
|
255
|
+
left,
|
|
256
|
+
right,
|
|
257
|
+
sampleRate,
|
|
258
|
+
presetName,
|
|
259
|
+
overrides,
|
|
260
|
+
onProgress
|
|
261
|
+
);
|
|
262
|
+
const flat = flattenChainConfig(request.overrides ?? {});
|
|
263
|
+
if (request.left.length !== request.right.length) {
|
|
264
|
+
throw new Error("Stereo channel lengths must match.");
|
|
265
|
+
}
|
|
266
|
+
if (request.onProgress || request.cancel) {
|
|
267
|
+
return requireModule().masterAudioStereoWithProgress(
|
|
268
|
+
request.preset ?? "pop",
|
|
269
|
+
request.left,
|
|
270
|
+
request.right,
|
|
271
|
+
request.sampleRate ?? 22050,
|
|
272
|
+
flat,
|
|
273
|
+
request.onProgress ?? (() => {
|
|
274
|
+
}),
|
|
275
|
+
request.cancel ?? (() => false)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return requireModule().masterAudioStereo(
|
|
279
|
+
request.preset ?? "pop",
|
|
280
|
+
request.left,
|
|
281
|
+
request.right,
|
|
282
|
+
request.sampleRate ?? 22050,
|
|
283
|
+
flat
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/public_types_music.ts
|
|
288
|
+
var PitchClass = {
|
|
289
|
+
C: 0,
|
|
290
|
+
Cs: 1,
|
|
291
|
+
D: 2,
|
|
292
|
+
Ds: 3,
|
|
293
|
+
E: 4,
|
|
294
|
+
F: 5,
|
|
295
|
+
Fs: 6,
|
|
296
|
+
G: 7,
|
|
297
|
+
Gs: 8,
|
|
298
|
+
A: 9,
|
|
299
|
+
As: 10,
|
|
300
|
+
B: 11
|
|
301
|
+
};
|
|
302
|
+
var Mode = {
|
|
303
|
+
Major: 0,
|
|
304
|
+
Minor: 1,
|
|
305
|
+
Dorian: 2,
|
|
306
|
+
Phrygian: 3,
|
|
307
|
+
Lydian: 4,
|
|
308
|
+
Mixolydian: 5,
|
|
309
|
+
Locrian: 6
|
|
310
|
+
};
|
|
311
|
+
var KeyProfile = {
|
|
312
|
+
KrumhanslSchmuckler: 0,
|
|
313
|
+
Temperley: 1,
|
|
314
|
+
Shaath: 2,
|
|
315
|
+
FaraldoEDMT: 3,
|
|
316
|
+
FaraldoEDMA: 4,
|
|
317
|
+
FaraldoEDMM: 5,
|
|
318
|
+
BellmanBudge: 6
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// src/analysis_helpers.ts
|
|
322
|
+
var PITCH_CLASS_NAMES = [
|
|
323
|
+
"C",
|
|
324
|
+
"C#",
|
|
325
|
+
"D",
|
|
326
|
+
"D#",
|
|
327
|
+
"E",
|
|
328
|
+
"F",
|
|
329
|
+
"F#",
|
|
330
|
+
"G",
|
|
331
|
+
"G#",
|
|
332
|
+
"A",
|
|
333
|
+
"A#",
|
|
334
|
+
"B"
|
|
335
|
+
];
|
|
336
|
+
function pitchClassName(value) {
|
|
337
|
+
return PITCH_CLASS_NAMES[value] ?? "C";
|
|
338
|
+
}
|
|
339
|
+
function keyModeValues(modes) {
|
|
340
|
+
if (!modes) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
if (modes === "major-minor") {
|
|
344
|
+
return [Mode.Major, Mode.Minor];
|
|
345
|
+
}
|
|
346
|
+
if (modes === "all" || modes === "modal") {
|
|
347
|
+
return [
|
|
348
|
+
Mode.Major,
|
|
349
|
+
Mode.Minor,
|
|
350
|
+
Mode.Dorian,
|
|
351
|
+
Mode.Phrygian,
|
|
352
|
+
Mode.Lydian,
|
|
353
|
+
Mode.Mixolydian,
|
|
354
|
+
Mode.Locrian
|
|
355
|
+
];
|
|
356
|
+
}
|
|
357
|
+
const names = {
|
|
358
|
+
major: Mode.Major,
|
|
359
|
+
minor: Mode.Minor,
|
|
360
|
+
dorian: Mode.Dorian,
|
|
361
|
+
phrygian: Mode.Phrygian,
|
|
362
|
+
lydian: Mode.Lydian,
|
|
363
|
+
mixolydian: Mode.Mixolydian,
|
|
364
|
+
locrian: Mode.Locrian
|
|
365
|
+
};
|
|
366
|
+
return modes.map((mode) => typeof mode === "number" ? mode : names[mode]);
|
|
367
|
+
}
|
|
368
|
+
function keyProfileValue(profile) {
|
|
369
|
+
if (profile === void 0) {
|
|
370
|
+
return -1;
|
|
371
|
+
}
|
|
372
|
+
if (typeof profile === "number") {
|
|
373
|
+
return profile;
|
|
374
|
+
}
|
|
375
|
+
const names = {
|
|
376
|
+
ks: KeyProfile.KrumhanslSchmuckler,
|
|
377
|
+
krumhansl: KeyProfile.KrumhanslSchmuckler,
|
|
378
|
+
temperley: KeyProfile.Temperley,
|
|
379
|
+
shaath: KeyProfile.Shaath,
|
|
380
|
+
keyfinder: KeyProfile.Shaath,
|
|
381
|
+
"faraldo-edmt": KeyProfile.FaraldoEDMT,
|
|
382
|
+
edmt: KeyProfile.FaraldoEDMT,
|
|
383
|
+
"faraldo-edma": KeyProfile.FaraldoEDMA,
|
|
384
|
+
edma: KeyProfile.FaraldoEDMA,
|
|
385
|
+
"faraldo-edmm": KeyProfile.FaraldoEDMM,
|
|
386
|
+
edmm: KeyProfile.FaraldoEDMM,
|
|
387
|
+
"bellman-budge": KeyProfile.BellmanBudge,
|
|
388
|
+
bellman: KeyProfile.BellmanBudge
|
|
389
|
+
};
|
|
390
|
+
return names[profile];
|
|
391
|
+
}
|
|
392
|
+
function convertChordAnalysisResult(wasm) {
|
|
393
|
+
return {
|
|
394
|
+
chords: wasm.chords.map((c) => ({
|
|
395
|
+
root: c.root,
|
|
396
|
+
bass: c.bass,
|
|
397
|
+
rootName: pitchClassName(c.root),
|
|
398
|
+
bassName: pitchClassName(c.bass),
|
|
399
|
+
quality: c.quality,
|
|
400
|
+
start: c.start,
|
|
401
|
+
end: c.end,
|
|
402
|
+
confidence: c.confidence,
|
|
403
|
+
name: c.name
|
|
404
|
+
}))
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function chordChromaMethodValue(method) {
|
|
408
|
+
if (method === "stft") {
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
if (method === "nnls") {
|
|
412
|
+
return 1;
|
|
413
|
+
}
|
|
414
|
+
throw new Error(`Invalid chord chroma method: ${method}`);
|
|
415
|
+
}
|
|
416
|
+
function convertAnalysisResult(wasm) {
|
|
417
|
+
const beatTimes = new Float32Array(wasm.beats.length);
|
|
418
|
+
for (let i = 0; i < wasm.beats.length; i++) {
|
|
419
|
+
beatTimes[i] = wasm.beats[i].time;
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
bpm: wasm.bpm,
|
|
423
|
+
bpmConfidence: wasm.bpmConfidence,
|
|
424
|
+
bpmCandidates: wasm.bpmCandidates.map((candidate) => ({
|
|
425
|
+
value: candidate.value,
|
|
426
|
+
confidence: candidate.confidence,
|
|
427
|
+
relation: candidate.relation
|
|
428
|
+
})),
|
|
429
|
+
key: {
|
|
430
|
+
root: wasm.key.root,
|
|
431
|
+
mode: wasm.key.mode,
|
|
432
|
+
confidence: wasm.key.confidence,
|
|
433
|
+
name: wasm.key.name,
|
|
434
|
+
shortName: wasm.key.shortName
|
|
435
|
+
},
|
|
436
|
+
timeSignature: wasm.timeSignature,
|
|
437
|
+
timeSignatureCandidates: wasm.timeSignatureCandidates,
|
|
438
|
+
beatTimes,
|
|
439
|
+
beats: wasm.beats,
|
|
440
|
+
chords: wasm.chords.map((c) => ({
|
|
441
|
+
root: c.root,
|
|
442
|
+
bass: c.bass,
|
|
443
|
+
rootName: pitchClassName(c.root),
|
|
444
|
+
bassName: pitchClassName(c.bass),
|
|
445
|
+
quality: c.quality,
|
|
446
|
+
start: c.start,
|
|
447
|
+
end: c.end,
|
|
448
|
+
confidence: c.confidence,
|
|
449
|
+
name: c.name
|
|
450
|
+
})),
|
|
451
|
+
sections: wasm.sections.map((s) => ({
|
|
452
|
+
type: s.type,
|
|
453
|
+
start: s.start,
|
|
454
|
+
end: s.end,
|
|
455
|
+
energyLevel: s.energyLevel,
|
|
456
|
+
confidence: s.confidence,
|
|
457
|
+
name: s.name
|
|
458
|
+
})),
|
|
459
|
+
timbre: wasm.timbre,
|
|
460
|
+
dynamics: wasm.dynamics,
|
|
461
|
+
rhythm: wasm.rhythm,
|
|
462
|
+
melody: wasm.melody,
|
|
463
|
+
form: wasm.form
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/quick_analysis.ts
|
|
468
|
+
function requireModule2() {
|
|
469
|
+
return getSonareModule();
|
|
470
|
+
}
|
|
471
|
+
function validateAnalysisInput(fnName, samples, sampleRate, options = {}) {
|
|
472
|
+
assertSampleRate(fnName, sampleRate);
|
|
473
|
+
assertSamples(fnName, samples, options.validate !== false);
|
|
474
|
+
}
|
|
475
|
+
function detectBpm(samples, sampleRate = 22050, options = {}) {
|
|
476
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
477
|
+
validateAnalysisInput("detectBpm", request.samples, request.sampleRate ?? 22050, request);
|
|
478
|
+
return requireModule2().detectBpm(request.samples, request.sampleRate ?? 22050);
|
|
479
|
+
}
|
|
480
|
+
function detectKey(samples, sampleRate = 22050, options = {}) {
|
|
481
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
482
|
+
validateAnalysisInput("detectKey", request.samples, request.sampleRate ?? 22050, request);
|
|
483
|
+
const result = requireModule2()._detectKeyWithOptions(
|
|
484
|
+
request.samples,
|
|
485
|
+
request.sampleRate ?? 22050,
|
|
486
|
+
request.nFft ?? 4096,
|
|
487
|
+
request.hopLength ?? 512,
|
|
488
|
+
request.useHpss ?? false,
|
|
489
|
+
request.loudnessWeighted ?? false,
|
|
490
|
+
request.highPassHz ?? 0,
|
|
491
|
+
keyModeValues(request.modes),
|
|
492
|
+
keyProfileValue(request.profile),
|
|
493
|
+
request.genreHint ?? ""
|
|
494
|
+
);
|
|
495
|
+
return {
|
|
496
|
+
root: result.root,
|
|
497
|
+
mode: result.mode,
|
|
498
|
+
confidence: result.confidence,
|
|
499
|
+
name: result.name,
|
|
500
|
+
shortName: result.shortName
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function detectChords(samples, sampleRate = 22050, options = {}) {
|
|
504
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
505
|
+
validateAnalysisInput("detectChords", request.samples, request.sampleRate ?? 22050, request);
|
|
506
|
+
const result = requireModule2().detectChords(
|
|
507
|
+
request.samples,
|
|
508
|
+
request.sampleRate ?? 22050,
|
|
509
|
+
request.minDuration ?? 0.3,
|
|
510
|
+
request.smoothingWindow ?? 2,
|
|
511
|
+
request.threshold ?? 0.5,
|
|
512
|
+
request.useTriadsOnly ?? false,
|
|
513
|
+
request.nFft ?? 2048,
|
|
514
|
+
request.hopLength ?? 512,
|
|
515
|
+
request.useBeatSync ?? true,
|
|
516
|
+
request.useHmm ?? false,
|
|
517
|
+
request.hmmBeamWidth ?? 24,
|
|
518
|
+
request.useKeyContext ?? false,
|
|
519
|
+
request.keyRoot ?? PitchClass.C,
|
|
520
|
+
request.keyMode ?? Mode.Major,
|
|
521
|
+
request.detectInversions ?? false,
|
|
522
|
+
chordChromaMethodValue(request.chromaMethod ?? "stft")
|
|
523
|
+
);
|
|
524
|
+
return convertChordAnalysisResult(result);
|
|
525
|
+
}
|
|
526
|
+
function analyzeWithProgress(samples, sampleRate = 22050, onProgress) {
|
|
527
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, onProgress } : samples;
|
|
528
|
+
validateAnalysisInput(
|
|
529
|
+
"analyzeWithProgress",
|
|
530
|
+
request.samples,
|
|
531
|
+
request.sampleRate ?? 22050,
|
|
532
|
+
request
|
|
533
|
+
);
|
|
534
|
+
const result = requireModule2().analyzeWithProgress(
|
|
535
|
+
request.samples,
|
|
536
|
+
request.sampleRate ?? 22050,
|
|
537
|
+
request.onProgress ?? (() => {
|
|
538
|
+
}),
|
|
539
|
+
request.cancel ?? (() => false)
|
|
540
|
+
);
|
|
541
|
+
return convertAnalysisResult(result);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/index.ts
|
|
545
|
+
var module = null;
|
|
546
|
+
var initPromise = null;
|
|
547
|
+
async function init(options) {
|
|
548
|
+
if (module) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (initPromise) {
|
|
552
|
+
return initPromise;
|
|
553
|
+
}
|
|
554
|
+
initPromise = (async () => {
|
|
555
|
+
try {
|
|
556
|
+
const createModule = options?.moduleFactory ?? (await import("./sonare.js")).default;
|
|
557
|
+
module = await createModule(options);
|
|
558
|
+
setSonareModule(module);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
initPromise = null;
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
})();
|
|
564
|
+
return initPromise;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/worker.ts
|
|
568
|
+
function transferableBuffers(value, seen = /* @__PURE__ */ new Set()) {
|
|
569
|
+
const buffers = [];
|
|
570
|
+
const seenBuffers = /* @__PURE__ */ new Set();
|
|
571
|
+
const visit = (item) => {
|
|
572
|
+
if (item === null || item === void 0 || typeof item !== "object") {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (ArrayBuffer.isView(item)) {
|
|
576
|
+
const buffer = item.buffer;
|
|
577
|
+
if (buffer instanceof ArrayBuffer && !seenBuffers.has(buffer)) {
|
|
578
|
+
seenBuffers.add(buffer);
|
|
579
|
+
buffers.push(buffer);
|
|
580
|
+
}
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (item instanceof ArrayBuffer) {
|
|
584
|
+
if (!seenBuffers.has(item)) {
|
|
585
|
+
seenBuffers.add(item);
|
|
586
|
+
buffers.push(item);
|
|
587
|
+
}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (seen.has(item)) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
seen.add(item);
|
|
594
|
+
if (Array.isArray(item)) {
|
|
595
|
+
item.forEach(visit);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
Object.values(item).forEach(visit);
|
|
599
|
+
};
|
|
600
|
+
visit(value);
|
|
601
|
+
return buffers;
|
|
602
|
+
}
|
|
603
|
+
function errorMessage(error) {
|
|
604
|
+
if (error instanceof SonareError) {
|
|
605
|
+
return {
|
|
606
|
+
name: error.name,
|
|
607
|
+
message: error.message,
|
|
608
|
+
code: error.code,
|
|
609
|
+
codeName: error.codeName
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (error instanceof Error) {
|
|
613
|
+
return { name: error.name, message: error.message };
|
|
614
|
+
}
|
|
615
|
+
return { name: "Error", message: String(error) };
|
|
616
|
+
}
|
|
617
|
+
function cancelledError() {
|
|
618
|
+
return new SonareError(8 /* Cancelled */, "Cancelled", "Operation cancelled");
|
|
619
|
+
}
|
|
620
|
+
function installOfflineWorkerEndpoint(endpoint2) {
|
|
621
|
+
const cancelled = /* @__PURE__ */ new Set();
|
|
622
|
+
const run = async (message) => {
|
|
623
|
+
const cancelFlag = message.cancelBuffer ? new Int32Array(message.cancelBuffer) : void 0;
|
|
624
|
+
const isCancelled = () => cancelled.has(message.id) || cancelFlag !== void 0 && Atomics.load(cancelFlag, 0) !== 0;
|
|
625
|
+
const onProgress = (progress, stage) => {
|
|
626
|
+
endpoint2.postMessage({ type: "sonare:offline-progress", id: message.id, progress, stage });
|
|
627
|
+
return isCancelled() ? false : void 0;
|
|
628
|
+
};
|
|
629
|
+
try {
|
|
630
|
+
await init();
|
|
631
|
+
if (isCancelled()) {
|
|
632
|
+
throw cancelledError();
|
|
633
|
+
}
|
|
634
|
+
let result;
|
|
635
|
+
switch (message.operation) {
|
|
636
|
+
case "analyze":
|
|
637
|
+
result = analyzeWithProgress({
|
|
638
|
+
...message.request,
|
|
639
|
+
onProgress
|
|
640
|
+
});
|
|
641
|
+
break;
|
|
642
|
+
case "detectBpm":
|
|
643
|
+
result = detectBpm(message.request);
|
|
644
|
+
break;
|
|
645
|
+
case "detectKey":
|
|
646
|
+
result = detectKey(message.request);
|
|
647
|
+
break;
|
|
648
|
+
case "detectChords":
|
|
649
|
+
result = detectChords(message.request);
|
|
650
|
+
break;
|
|
651
|
+
case "masterAudio":
|
|
652
|
+
result = masterAudio({
|
|
653
|
+
...message.request,
|
|
654
|
+
onProgress
|
|
655
|
+
});
|
|
656
|
+
break;
|
|
657
|
+
case "masterAudioStereo":
|
|
658
|
+
result = masterAudioStereo({
|
|
659
|
+
...message.request,
|
|
660
|
+
onProgress
|
|
661
|
+
});
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
if (isCancelled()) {
|
|
665
|
+
throw cancelledError();
|
|
666
|
+
}
|
|
667
|
+
endpoint2.postMessage(
|
|
668
|
+
{ type: "sonare:offline-result", id: message.id, result },
|
|
669
|
+
transferableBuffers(result)
|
|
670
|
+
);
|
|
671
|
+
} catch (error) {
|
|
672
|
+
endpoint2.postMessage({
|
|
673
|
+
type: "sonare:offline-error",
|
|
674
|
+
id: message.id,
|
|
675
|
+
error: errorMessage(error)
|
|
676
|
+
});
|
|
677
|
+
} finally {
|
|
678
|
+
cancelled.delete(message.id);
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
endpoint2.addEventListener("message", (event) => {
|
|
682
|
+
const message = event.data;
|
|
683
|
+
if (message.type === "sonare:offline-cancel") {
|
|
684
|
+
cancelled.add(message.id);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
void run(message);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
function browserWorkerEndpoint() {
|
|
691
|
+
const scope = globalThis;
|
|
692
|
+
if (scope.document !== void 0 || !scope.postMessage || !scope.addEventListener) {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
postMessage: (message, transfer) => scope.postMessage?.(message, transfer),
|
|
697
|
+
addEventListener: (type, listener) => scope.addEventListener?.(type, listener)
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
var endpoint = browserWorkerEndpoint();
|
|
701
|
+
if (endpoint) {
|
|
702
|
+
installOfflineWorkerEndpoint(endpoint);
|
|
703
|
+
}
|
|
704
|
+
export {
|
|
705
|
+
installOfflineWorkerEndpoint
|
|
706
|
+
};
|
|
707
|
+
//# sourceMappingURL=worker.js.map
|