@libraz/libsonare 1.5.4 → 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 +873 -63
- package/dist/index.js +1037 -265
- 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 +596 -86
- package/dist/worklet.js +1658 -235
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -11
- package/src/_chain_config.ts +5 -23
- package/src/analysis.ts +137 -0
- package/src/analysis_helpers.ts +29 -0
- package/src/audio.ts +5 -3
- package/src/clip_page_streamer.ts +62 -8
- package/src/codes.ts +51 -64
- package/src/effects_mastering.ts +1 -0
- package/src/effects_transform.ts +12 -6
- package/src/errors.ts +1 -0
- package/src/feature_core.ts +159 -3
- package/src/feature_music.ts +35 -16
- package/src/feature_pitch.ts +75 -6
- package/src/feature_spectral.ts +234 -8
- package/src/feature_spectrogram.ts +186 -5
- package/src/features.ts +16 -1
- package/src/index.ts +104 -7
- package/src/mastering_chain.ts +20 -8
- package/src/mastering_core.ts +7 -0
- package/src/mastering_dynamics.ts +7 -1
- package/src/metering.ts +51 -5
- package/src/mixer.ts +14 -1
- package/src/opfs_clip_pages.ts +26 -1
- package/src/project.ts +7 -0
- package/src/project_class.ts +74 -13
- 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 +28 -0
- package/src/public_types_mixing.ts +11 -2
- package/src/public_types_music.ts +13 -0
- package/src/public_types_spectral.ts +43 -3
- package/src/quick_analysis.ts +48 -12
- package/src/realtime_engine.ts +162 -4
- package/src/realtime_voice_changer.ts +51 -77
- package/src/sonare-analysis.js.d.ts +8 -0
- package/src/sonare.js.d.ts +305 -9
- package/src/streaming_processors.ts +22 -1
- package/src/validation.ts +6 -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 +141 -9
- package/src/worklet/protocol.ts +303 -17
- package/src/worklet/voice-changer-processor.ts +12 -15
- package/src/worklet.ts +20 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
8
8
|
ErrorCode2[ErrorCode2["OutOfMemory"] = 5] = "OutOfMemory";
|
|
9
9
|
ErrorCode2[ErrorCode2["NotSupported"] = 6] = "NotSupported";
|
|
10
10
|
ErrorCode2[ErrorCode2["InvalidState"] = 7] = "InvalidState";
|
|
11
|
+
ErrorCode2[ErrorCode2["Cancelled"] = 8] = "Cancelled";
|
|
11
12
|
ErrorCode2[ErrorCode2["Unknown"] = 99] = "Unknown";
|
|
12
13
|
return ErrorCode2;
|
|
13
14
|
})(ErrorCode || {});
|
|
@@ -200,6 +201,10 @@ function assertSampleRate(fnName, sampleRate) {
|
|
|
200
201
|
);
|
|
201
202
|
}
|
|
202
203
|
}
|
|
204
|
+
function validateAudioBuffer(samples, sampleRate) {
|
|
205
|
+
assertSamples("Audio.fromBuffer", samples, true);
|
|
206
|
+
assertSampleRate("Audio.fromBuffer", sampleRate);
|
|
207
|
+
}
|
|
203
208
|
function assertNonNegativeInteger(fnName, value, argName) {
|
|
204
209
|
if (!Number.isInteger(value) || value < 0) {
|
|
205
210
|
throw new RangeError(`${fnName}: ${argName} must be a non-negative integer`);
|
|
@@ -322,7 +327,7 @@ function noteStretch(samples, sampleRate = 22050, options = {}) {
|
|
|
322
327
|
request.samples,
|
|
323
328
|
request.sampleRate ?? 22050,
|
|
324
329
|
request.onsetSample ?? 0,
|
|
325
|
-
request.offsetSample ??
|
|
330
|
+
request.offsetSample ?? request.samples.length,
|
|
326
331
|
request.stretchRatio ?? 1
|
|
327
332
|
);
|
|
328
333
|
}
|
|
@@ -333,7 +338,7 @@ function noteMove(samples, sampleRate = 22050, options = {}) {
|
|
|
333
338
|
request.samples,
|
|
334
339
|
request.sampleRate ?? 22050,
|
|
335
340
|
request.onsetSample ?? 0,
|
|
336
|
-
request.offsetSample ??
|
|
341
|
+
request.offsetSample ?? request.samples.length,
|
|
337
342
|
request.targetOnsetSample ?? 0
|
|
338
343
|
);
|
|
339
344
|
}
|
|
@@ -401,26 +406,12 @@ function flattenChainConfig(config) {
|
|
|
401
406
|
out[path] = value;
|
|
402
407
|
} else if (value !== null && typeof value === "object") {
|
|
403
408
|
walk(value, path);
|
|
409
|
+
} else if (value !== void 0) {
|
|
410
|
+
throw new TypeError(`Mastering override '${path}' must be a number or boolean.`);
|
|
404
411
|
}
|
|
405
412
|
}
|
|
406
413
|
};
|
|
407
414
|
walk(config, "");
|
|
408
|
-
const aliases = {
|
|
409
|
-
"repair.denoise": "repair.denoise.enabled",
|
|
410
|
-
"repair.nFft": "repair.denoise.nFft",
|
|
411
|
-
"repair.hopLength": "repair.denoise.hopLength",
|
|
412
|
-
"repair.ddAlpha": "repair.denoise.ddAlpha",
|
|
413
|
-
"repair.gainFloor": "repair.denoise.gainFloor",
|
|
414
|
-
"eq.tiltDb": "eq.tilt.tiltDb",
|
|
415
|
-
"eq.pivotHz": "eq.tilt.pivotHz"
|
|
416
|
-
};
|
|
417
|
-
for (const [legacy, canonical] of Object.entries(aliases)) {
|
|
418
|
-
const value = out[legacy];
|
|
419
|
-
if (value !== void 0) {
|
|
420
|
-
out[canonical] = value;
|
|
421
|
-
delete out[legacy];
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
415
|
return out;
|
|
425
416
|
}
|
|
426
417
|
|
|
@@ -458,12 +449,14 @@ function masterAudioStereoRequest(requestOrLeft, right, sampleRate, preset, over
|
|
|
458
449
|
}
|
|
459
450
|
function masteringChain(samples, sampleRate = 22050, config = {}, onProgress) {
|
|
460
451
|
const request = samples instanceof Float32Array ? { samples, sampleRate, config, onProgress } : samples;
|
|
461
|
-
if (request.onProgress) {
|
|
452
|
+
if (request.onProgress || request.cancel) {
|
|
462
453
|
return requireModule3().masteringChainWithProgress(
|
|
463
454
|
request.samples,
|
|
464
455
|
request.sampleRate ?? 22050,
|
|
465
456
|
canonicalChainConfig(request.config ?? {}),
|
|
466
|
-
request.onProgress
|
|
457
|
+
request.onProgress ?? (() => {
|
|
458
|
+
}),
|
|
459
|
+
request.cancel ?? (() => false)
|
|
467
460
|
);
|
|
468
461
|
}
|
|
469
462
|
return requireModule3().masteringChain(
|
|
@@ -477,13 +470,15 @@ function masteringChainStereo(left, right, sampleRate = 22050, config = {}, onPr
|
|
|
477
470
|
if (request.left.length !== request.right.length) {
|
|
478
471
|
throw new Error("Stereo channel lengths must match.");
|
|
479
472
|
}
|
|
480
|
-
if (request.onProgress) {
|
|
473
|
+
if (request.onProgress || request.cancel) {
|
|
481
474
|
return requireModule3().masteringChainStereoWithProgress(
|
|
482
475
|
request.left,
|
|
483
476
|
request.right,
|
|
484
477
|
request.sampleRate ?? 22050,
|
|
485
478
|
canonicalChainConfig(request.config ?? {}),
|
|
486
|
-
request.onProgress
|
|
479
|
+
request.onProgress ?? (() => {
|
|
480
|
+
}),
|
|
481
|
+
request.cancel ?? (() => false)
|
|
487
482
|
);
|
|
488
483
|
}
|
|
489
484
|
return requireModule3().masteringChainStereo(
|
|
@@ -502,7 +497,8 @@ function masteringChainWithProgress(samples, sampleRate = 22050, config = {}, on
|
|
|
502
497
|
request.samples,
|
|
503
498
|
request.sampleRate ?? 22050,
|
|
504
499
|
canonicalChainConfig(request.config ?? {}),
|
|
505
|
-
request.onProgress
|
|
500
|
+
request.onProgress,
|
|
501
|
+
request.cancel ?? (() => false)
|
|
506
502
|
);
|
|
507
503
|
}
|
|
508
504
|
function masteringChainStereoWithProgress(left, right, sampleRate = 22050, config = {}, onProgress) {
|
|
@@ -518,7 +514,8 @@ function masteringChainStereoWithProgress(left, right, sampleRate = 22050, confi
|
|
|
518
514
|
request.right,
|
|
519
515
|
request.sampleRate ?? 22050,
|
|
520
516
|
canonicalChainConfig(request.config ?? {}),
|
|
521
|
-
request.onProgress
|
|
517
|
+
request.onProgress,
|
|
518
|
+
request.cancel ?? (() => false)
|
|
522
519
|
);
|
|
523
520
|
}
|
|
524
521
|
function masteringPresetNames() {
|
|
@@ -527,13 +524,15 @@ function masteringPresetNames() {
|
|
|
527
524
|
function masterAudio(samples, sampleRate = 22050, presetName = "pop", overrides = {}, onProgress) {
|
|
528
525
|
const request = masterAudioRequest(samples, sampleRate, presetName, overrides, onProgress);
|
|
529
526
|
const flat = flattenChainConfig(request.overrides ?? {});
|
|
530
|
-
if (request.onProgress) {
|
|
527
|
+
if (request.onProgress || request.cancel) {
|
|
531
528
|
return requireModule3().masterAudioWithProgress(
|
|
532
529
|
request.preset ?? "pop",
|
|
533
530
|
request.samples,
|
|
534
531
|
request.sampleRate ?? 22050,
|
|
535
532
|
flat,
|
|
536
|
-
request.onProgress
|
|
533
|
+
request.onProgress ?? (() => {
|
|
534
|
+
}),
|
|
535
|
+
request.cancel ?? (() => false)
|
|
537
536
|
);
|
|
538
537
|
}
|
|
539
538
|
return requireModule3().masterAudio(
|
|
@@ -556,14 +555,16 @@ function masterAudioStereo(left, right = void 0, sampleRate = 22050, presetName
|
|
|
556
555
|
if (request.left.length !== request.right.length) {
|
|
557
556
|
throw new Error("Stereo channel lengths must match.");
|
|
558
557
|
}
|
|
559
|
-
if (request.onProgress) {
|
|
558
|
+
if (request.onProgress || request.cancel) {
|
|
560
559
|
return requireModule3().masterAudioStereoWithProgress(
|
|
561
560
|
request.preset ?? "pop",
|
|
562
561
|
request.left,
|
|
563
562
|
request.right,
|
|
564
563
|
request.sampleRate ?? 22050,
|
|
565
564
|
flat,
|
|
566
|
-
request.onProgress
|
|
565
|
+
request.onProgress ?? (() => {
|
|
566
|
+
}),
|
|
567
|
+
request.cancel ?? (() => false)
|
|
567
568
|
);
|
|
568
569
|
}
|
|
569
570
|
return requireModule3().masterAudioStereo(
|
|
@@ -584,7 +585,8 @@ function masterAudioWithProgress(samples, sampleRate = 22050, presetName = "pop"
|
|
|
584
585
|
request.samples,
|
|
585
586
|
request.sampleRate ?? 22050,
|
|
586
587
|
flattenChainConfig(request.overrides ?? {}),
|
|
587
|
-
request.onProgress
|
|
588
|
+
request.onProgress,
|
|
589
|
+
request.cancel ?? (() => false)
|
|
588
590
|
);
|
|
589
591
|
}
|
|
590
592
|
function masterAudioStereoWithProgress(left, right = void 0, sampleRate = 22050, presetName = "pop", overrides = null, onProgress) {
|
|
@@ -608,7 +610,8 @@ function masterAudioStereoWithProgress(left, right = void 0, sampleRate = 22050,
|
|
|
608
610
|
request.right,
|
|
609
611
|
request.sampleRate ?? 22050,
|
|
610
612
|
flattenChainConfig(request.overrides ?? {}),
|
|
611
|
-
request.onProgress
|
|
613
|
+
request.onProgress,
|
|
614
|
+
request.cancel ?? (() => false)
|
|
612
615
|
);
|
|
613
616
|
}
|
|
614
617
|
|
|
@@ -771,7 +774,10 @@ function masteringDynamicsCompressor(samples, sampleRate, options = {}) {
|
|
|
771
774
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
772
775
|
assertSamples("masteringDynamicsCompressor", request.samples, request.validate !== false);
|
|
773
776
|
const detector = typeof request.detector === "string" ? COMPRESSOR_DETECTOR_MAP[request.detector] : request.detector;
|
|
774
|
-
const opts =
|
|
777
|
+
const opts = Object.assign(
|
|
778
|
+
Object.create(Object.getPrototypeOf(request)),
|
|
779
|
+
request
|
|
780
|
+
);
|
|
775
781
|
if (detector !== void 0) {
|
|
776
782
|
opts.detector = detector;
|
|
777
783
|
}
|
|
@@ -860,6 +866,36 @@ function mixStereo(leftChannels, rightChannels, sampleRate = 48e3, options = {})
|
|
|
860
866
|
function requireModule8() {
|
|
861
867
|
return getSonareModule();
|
|
862
868
|
}
|
|
869
|
+
function tone(frequency = 440, sampleRate = 22050, duration = 1, phase = 0, amplitude = 1) {
|
|
870
|
+
const request = typeof frequency === "number" ? { frequency, sampleRate, duration, phase, amplitude } : frequency;
|
|
871
|
+
return requireModule8().tone(
|
|
872
|
+
request.frequency ?? 440,
|
|
873
|
+
request.sampleRate ?? 22050,
|
|
874
|
+
request.duration ?? 1,
|
|
875
|
+
request.phase ?? 0,
|
|
876
|
+
request.amplitude ?? 1
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
function chirp(fmin = 440, fmax = 880, sampleRate = 22050, duration = 1, linear = true) {
|
|
880
|
+
const request = typeof fmin === "number" ? { fmin, fmax, sampleRate, duration, linear } : fmin;
|
|
881
|
+
return requireModule8().chirp(
|
|
882
|
+
request.fmin ?? 440,
|
|
883
|
+
request.fmax ?? 880,
|
|
884
|
+
request.sampleRate ?? 22050,
|
|
885
|
+
request.duration ?? 1,
|
|
886
|
+
request.linear ?? true
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
function clicks(times, sampleRate = 22050, length = 0, frequency = 1e3, clickDuration = 0.1) {
|
|
890
|
+
const request = times instanceof Float32Array ? { times, sampleRate, length, frequency, clickDuration } : times;
|
|
891
|
+
return requireModule8().clicks(
|
|
892
|
+
request.times,
|
|
893
|
+
request.sampleRate ?? 22050,
|
|
894
|
+
request.length ?? 0,
|
|
895
|
+
request.frequency ?? 1e3,
|
|
896
|
+
request.clickDuration ?? 0.1
|
|
897
|
+
);
|
|
898
|
+
}
|
|
863
899
|
function hzToMel(hz) {
|
|
864
900
|
return requireModule8().hzToMel(hz);
|
|
865
901
|
}
|
|
@@ -956,6 +992,12 @@ function fixFrames(frames, xMin = 0, xMax = -1, pad = true) {
|
|
|
956
992
|
}
|
|
957
993
|
return requireModule8().fixFrames(frames, xMin, xMax, pad);
|
|
958
994
|
}
|
|
995
|
+
function onsetBacktrack(events, energy) {
|
|
996
|
+
if (!(events instanceof Int32Array)) {
|
|
997
|
+
return onsetBacktrack(events.events, events.energy);
|
|
998
|
+
}
|
|
999
|
+
return requireModule8().onsetBacktrack(events, energy);
|
|
1000
|
+
}
|
|
959
1001
|
function peakPick(values, preMax, postMax, preAvg, postAvg, delta, wait) {
|
|
960
1002
|
if (!(values instanceof Float32Array)) {
|
|
961
1003
|
const r = values;
|
|
@@ -980,7 +1022,17 @@ function vectorNormalize(values, normType = 0, threshold = 0) {
|
|
|
980
1022
|
function pcen(values, nBins = 0, nFrames = 0, options = {}) {
|
|
981
1023
|
if (!(values instanceof Float32Array)) {
|
|
982
1024
|
const r = values;
|
|
983
|
-
|
|
1025
|
+
const {
|
|
1026
|
+
values: requestValues,
|
|
1027
|
+
nBins: requestBins,
|
|
1028
|
+
nFrames: requestFrames,
|
|
1029
|
+
options: legacyOptions,
|
|
1030
|
+
...flatOptions
|
|
1031
|
+
} = r;
|
|
1032
|
+
return pcen(requestValues, requestBins, requestFrames, {
|
|
1033
|
+
...legacyOptions,
|
|
1034
|
+
...flatOptions
|
|
1035
|
+
});
|
|
984
1036
|
}
|
|
985
1037
|
return requireModule8().pcen(values, nBins, nFrames, options);
|
|
986
1038
|
}
|
|
@@ -990,12 +1042,28 @@ function tonnetz(chromagram, nChroma, nFrames) {
|
|
|
990
1042
|
}
|
|
991
1043
|
return requireModule8().tonnetz(chromagram, nChroma, nFrames);
|
|
992
1044
|
}
|
|
993
|
-
function tempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, mode = "autocorrelation") {
|
|
1045
|
+
function tempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, mode = "autocorrelation", center = true, norm = true) {
|
|
994
1046
|
if (!(onsetEnvelope2 instanceof Float32Array)) {
|
|
995
1047
|
const r = onsetEnvelope2;
|
|
996
|
-
return tempogram(
|
|
1048
|
+
return tempogram(
|
|
1049
|
+
r.onsetEnvelope,
|
|
1050
|
+
r.sampleRate,
|
|
1051
|
+
r.hopLength,
|
|
1052
|
+
r.winLength,
|
|
1053
|
+
r.mode,
|
|
1054
|
+
r.center,
|
|
1055
|
+
r.norm
|
|
1056
|
+
);
|
|
997
1057
|
}
|
|
998
|
-
return requireModule8().tempogram(
|
|
1058
|
+
return requireModule8().tempogram(
|
|
1059
|
+
onsetEnvelope2,
|
|
1060
|
+
sampleRate,
|
|
1061
|
+
hopLength,
|
|
1062
|
+
winLength,
|
|
1063
|
+
mode,
|
|
1064
|
+
center,
|
|
1065
|
+
norm
|
|
1066
|
+
);
|
|
999
1067
|
}
|
|
1000
1068
|
function cyclicTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, bpmMin = 60, nBins = 60) {
|
|
1001
1069
|
if (!(onsetEnvelope2 instanceof Float32Array)) {
|
|
@@ -1056,7 +1124,13 @@ function nnlsChroma(samples, sampleRate = 22050, options = {}) {
|
|
|
1056
1124
|
return nnlsChroma(samples.samples, samples.sampleRate, samples);
|
|
1057
1125
|
}
|
|
1058
1126
|
validateMusicSamples("nnlsChroma", samples, sampleRate, options);
|
|
1059
|
-
return requireModule9().nnlsChroma(
|
|
1127
|
+
return requireModule9().nnlsChroma(
|
|
1128
|
+
samples,
|
|
1129
|
+
sampleRate,
|
|
1130
|
+
options.enableStftBlend ?? true,
|
|
1131
|
+
options.stftBlendWeight ?? 0.55,
|
|
1132
|
+
options.stftBlendNFft ?? 4096
|
|
1133
|
+
);
|
|
1060
1134
|
}
|
|
1061
1135
|
function cqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12, options = {}) {
|
|
1062
1136
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -1112,7 +1186,7 @@ function hybridCqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319
|
|
|
1112
1186
|
validateFrequencyBounds("hybridCqt", fmin);
|
|
1113
1187
|
return requireModule9().hybridCqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave);
|
|
1114
1188
|
}
|
|
1115
|
-
function vqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12, gamma =
|
|
1189
|
+
function vqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12, gamma = -1, options = {}) {
|
|
1116
1190
|
if (!(samples instanceof Float32Array)) {
|
|
1117
1191
|
const request = samples;
|
|
1118
1192
|
return vqt(
|
|
@@ -1130,9 +1204,6 @@ function vqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257
|
|
|
1130
1204
|
validatePositiveIntegers("vqt", { hopLength, nBins, binsPerOctave });
|
|
1131
1205
|
validateFrequencyBounds("vqt", fmin);
|
|
1132
1206
|
assertFiniteScalar("vqt", gamma, "gamma");
|
|
1133
|
-
if (gamma < 0) {
|
|
1134
|
-
throw new RangeError("vqt: gamma must be non-negative");
|
|
1135
|
-
}
|
|
1136
1207
|
return requireModule9().vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);
|
|
1137
1208
|
}
|
|
1138
1209
|
function validateCqtInverse(fnName, magnitude, nBins, nFrames, sampleRate, hopLength, fmin, binsPerOctave, nIter, options) {
|
|
@@ -1188,7 +1259,7 @@ function cqtToAudio(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, hopLe
|
|
|
1188
1259
|
nIter
|
|
1189
1260
|
);
|
|
1190
1261
|
}
|
|
1191
|
-
function vqtToAudio(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, binsPerOctave = 12, gamma =
|
|
1262
|
+
function vqtToAudio(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, binsPerOctave = 12, gamma = -1, nIter = 32, options = {}) {
|
|
1192
1263
|
if (!(magnitude instanceof Float32Array)) {
|
|
1193
1264
|
const request = magnitude;
|
|
1194
1265
|
return vqtToAudio(
|
|
@@ -1217,9 +1288,6 @@ function vqtToAudio(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, hopLe
|
|
|
1217
1288
|
options
|
|
1218
1289
|
);
|
|
1219
1290
|
assertFiniteScalar("vqtToAudio", gamma, "gamma");
|
|
1220
|
-
if (gamma < 0) {
|
|
1221
|
-
throw new RangeError("vqtToAudio: gamma must be non-negative");
|
|
1222
|
-
}
|
|
1223
1291
|
return requireModule9().vqtToAudio(
|
|
1224
1292
|
magnitude,
|
|
1225
1293
|
nBins,
|
|
@@ -1243,8 +1311,8 @@ function analyzeSections(samples, sampleRate = 22050, options = {}) {
|
|
|
1243
1311
|
hopLength: options.hopLength ?? 512
|
|
1244
1312
|
});
|
|
1245
1313
|
assertFiniteScalar("analyzeSections", options.minSectionSec ?? 4, "minSectionSec");
|
|
1246
|
-
if ((options.minSectionSec ?? 4)
|
|
1247
|
-
throw new RangeError("analyzeSections: minSectionSec must be
|
|
1314
|
+
if ((options.minSectionSec ?? 4) < 0) {
|
|
1315
|
+
throw new RangeError("analyzeSections: minSectionSec must be non-negative");
|
|
1248
1316
|
}
|
|
1249
1317
|
const sections = requireModule9().analyzeSections(
|
|
1250
1318
|
samples,
|
|
@@ -1329,7 +1397,7 @@ function onsetStrengthMulti(samples, sampleRate = 22050, nFft = 2048, hopLength
|
|
|
1329
1397
|
validatePositiveIntegers("onsetStrengthMulti", { nFft, hopLength, nMels, nBands });
|
|
1330
1398
|
return requireModule9().onsetStrengthMulti(samples, sampleRate, nFft, hopLength, nMels, nBands);
|
|
1331
1399
|
}
|
|
1332
|
-
function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, options = {}) {
|
|
1400
|
+
function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, center = true, norm = true, options = {}) {
|
|
1333
1401
|
if (!(onsetEnvelope2 instanceof Float32Array)) {
|
|
1334
1402
|
const request = onsetEnvelope2;
|
|
1335
1403
|
return fourierTempogram(
|
|
@@ -1337,13 +1405,22 @@ function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, w
|
|
|
1337
1405
|
request.sampleRate,
|
|
1338
1406
|
request.hopLength,
|
|
1339
1407
|
request.winLength,
|
|
1408
|
+
request.center,
|
|
1409
|
+
request.norm,
|
|
1340
1410
|
request
|
|
1341
1411
|
);
|
|
1342
1412
|
}
|
|
1343
1413
|
assertSampleRate("fourierTempogram", sampleRate);
|
|
1344
1414
|
assertSamples("fourierTempogram", onsetEnvelope2, options.validate !== false, "onsetEnvelope");
|
|
1345
1415
|
validatePositiveIntegers("fourierTempogram", { hopLength, winLength });
|
|
1346
|
-
return requireModule9().fourierTempogram(
|
|
1416
|
+
return requireModule9().fourierTempogram(
|
|
1417
|
+
onsetEnvelope2,
|
|
1418
|
+
sampleRate,
|
|
1419
|
+
hopLength,
|
|
1420
|
+
winLength,
|
|
1421
|
+
center,
|
|
1422
|
+
norm
|
|
1423
|
+
);
|
|
1347
1424
|
}
|
|
1348
1425
|
function tempogramRatio(tempogramData, winLength = 384, sampleRate = 22050, hopLength = 512, factors, options = {}) {
|
|
1349
1426
|
if (!(tempogramData instanceof Float32Array)) {
|
|
@@ -1394,7 +1471,22 @@ function shortTermLufs(samples, sampleRate = 22050, options = {}) {
|
|
|
1394
1471
|
function requireModule10() {
|
|
1395
1472
|
return getSonareModule();
|
|
1396
1473
|
}
|
|
1397
|
-
function
|
|
1474
|
+
function piptrack(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, fmin = 150, fmax = 4e3, threshold = 0.1) {
|
|
1475
|
+
if (!(samples instanceof Float32Array)) {
|
|
1476
|
+
const request = samples;
|
|
1477
|
+
return piptrack(
|
|
1478
|
+
request.samples,
|
|
1479
|
+
request.sampleRate,
|
|
1480
|
+
request.nFft,
|
|
1481
|
+
request.hopLength,
|
|
1482
|
+
request.fmin,
|
|
1483
|
+
request.fmax,
|
|
1484
|
+
request.threshold
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
return requireModule10().piptrack(samples, sampleRate, nFft, hopLength, fmin, fmax, threshold);
|
|
1488
|
+
}
|
|
1489
|
+
function pitchYin(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.1, fillNa = false) {
|
|
1398
1490
|
if (!(samples instanceof Float32Array)) {
|
|
1399
1491
|
const request = samples;
|
|
1400
1492
|
return pitchYin(
|
|
@@ -1419,7 +1511,7 @@ function pitchYin(samples, sampleRate = 22050, frameLength = 2048, hopLength = 5
|
|
|
1419
1511
|
fillNa
|
|
1420
1512
|
);
|
|
1421
1513
|
}
|
|
1422
|
-
function pitchPyin(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.
|
|
1514
|
+
function pitchPyin(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.1, fillNa = false) {
|
|
1423
1515
|
if (!(samples instanceof Float32Array)) {
|
|
1424
1516
|
const request = samples;
|
|
1425
1517
|
return pitchPyin(
|
|
@@ -1444,6 +1536,13 @@ function pitchPyin(samples, sampleRate = 22050, frameLength = 2048, hopLength =
|
|
|
1444
1536
|
fillNa
|
|
1445
1537
|
);
|
|
1446
1538
|
}
|
|
1539
|
+
function noteSegments(request) {
|
|
1540
|
+
return requireModule10().noteSegments(request.f0Hz, request.voicedProb, request.frameRate, {
|
|
1541
|
+
segmentationThresholdCents: request.segmentationThresholdCents,
|
|
1542
|
+
minNoteMs: request.minNoteMs,
|
|
1543
|
+
referenceHz: request.referenceHz
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1447
1546
|
|
|
1448
1547
|
// src/feature_resample.ts
|
|
1449
1548
|
function requireModule11() {
|
|
@@ -1460,6 +1559,15 @@ function resample(samples, srcSr, targetSr) {
|
|
|
1460
1559
|
function requireModule12() {
|
|
1461
1560
|
return getSonareModule();
|
|
1462
1561
|
}
|
|
1562
|
+
function validateSegmentMatrix(fnName, data, rows, cols, dataName) {
|
|
1563
|
+
assertPositiveInteger(fnName, rows, "rows");
|
|
1564
|
+
assertPositiveInteger(fnName, cols, "cols");
|
|
1565
|
+
assertSamples(fnName, data, true, dataName);
|
|
1566
|
+
const expected = rows * cols;
|
|
1567
|
+
if (!Number.isSafeInteger(expected) || data.length !== expected) {
|
|
1568
|
+
throw new RangeError(`${fnName}: ${dataName} length must equal rows * cols`);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1463
1571
|
function spectralCentroid(samples, sampleRate = 22050, nFft = 2048, hopLength = 512) {
|
|
1464
1572
|
if (!(samples instanceof Float32Array)) {
|
|
1465
1573
|
return spectralCentroid(samples.samples, samples.sampleRate, samples.nFft, samples.hopLength);
|
|
@@ -1575,10 +1683,10 @@ function remix(samples, intervals, sampleRate = 22050, alignZeros = false) {
|
|
|
1575
1683
|
const intervalsI32 = intervals instanceof Int32Array ? intervals : Int32Array.from(intervals, (v) => Math.trunc(v));
|
|
1576
1684
|
return requireModule12().remix(samples, intervalsI32, sampleRate, alignZeros);
|
|
1577
1685
|
}
|
|
1578
|
-
function phaseVocoder(samples,
|
|
1686
|
+
function phaseVocoder(samples, sampleRate = 22050, rate = 1, nFft = 2048, hopLength = 512) {
|
|
1579
1687
|
if (!(samples instanceof Float32Array)) {
|
|
1580
1688
|
const r = samples;
|
|
1581
|
-
return phaseVocoder(r.samples, r.
|
|
1689
|
+
return phaseVocoder(r.samples, r.sampleRate ?? 22050, r.rate, r.nFft, r.hopLength);
|
|
1582
1690
|
}
|
|
1583
1691
|
return requireModule12().phaseVocoder(samples, sampleRate, rate, nFft, hopLength);
|
|
1584
1692
|
}
|
|
@@ -1604,11 +1712,17 @@ function ebur128LoudnessRange(samples, sampleRate = 22050) {
|
|
|
1604
1712
|
}
|
|
1605
1713
|
return requireModule12().ebur128LoudnessRange(samples, sampleRate);
|
|
1606
1714
|
}
|
|
1607
|
-
function spectralBandwidth(samples, sampleRate = 22050, nFft = 2048, hopLength = 512) {
|
|
1715
|
+
function spectralBandwidth(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, p = 2) {
|
|
1608
1716
|
if (!(samples instanceof Float32Array)) {
|
|
1609
|
-
return spectralBandwidth(
|
|
1717
|
+
return spectralBandwidth(
|
|
1718
|
+
samples.samples,
|
|
1719
|
+
samples.sampleRate,
|
|
1720
|
+
samples.nFft,
|
|
1721
|
+
samples.hopLength,
|
|
1722
|
+
samples.p
|
|
1723
|
+
);
|
|
1610
1724
|
}
|
|
1611
|
-
return requireModule12().spectralBandwidth(samples, sampleRate, nFft, hopLength);
|
|
1725
|
+
return requireModule12().spectralBandwidth(samples, sampleRate, nFft, hopLength, p);
|
|
1612
1726
|
}
|
|
1613
1727
|
function spectralRolloff(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, rollPercent = 0.85) {
|
|
1614
1728
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -1628,6 +1742,18 @@ function spectralFlatness(samples, sampleRate = 22050, nFft = 2048, hopLength =
|
|
|
1628
1742
|
}
|
|
1629
1743
|
return requireModule12().spectralFlatness(samples, sampleRate, nFft, hopLength);
|
|
1630
1744
|
}
|
|
1745
|
+
function spectralFlux(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, lag = 1) {
|
|
1746
|
+
if (!(samples instanceof Float32Array)) {
|
|
1747
|
+
return spectralFlux(
|
|
1748
|
+
samples.samples,
|
|
1749
|
+
samples.sampleRate,
|
|
1750
|
+
samples.nFft,
|
|
1751
|
+
samples.hopLength,
|
|
1752
|
+
samples.lag
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
return requireModule12().spectralFlux(samples, sampleRate, nFft, hopLength, lag);
|
|
1756
|
+
}
|
|
1631
1757
|
function zeroCrossingRate(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512) {
|
|
1632
1758
|
if (!(samples instanceof Float32Array)) {
|
|
1633
1759
|
return zeroCrossingRate(
|
|
@@ -1645,6 +1771,107 @@ function rmsEnergy(samples, sampleRate = 22050, frameLength = 2048, hopLength =
|
|
|
1645
1771
|
}
|
|
1646
1772
|
return requireModule12().rmsEnergy(samples, sampleRate, frameLength, hopLength);
|
|
1647
1773
|
}
|
|
1774
|
+
function segmentCrossSimilarity(request) {
|
|
1775
|
+
validateSegmentMatrix("segmentCrossSimilarity", request.x, request.xRows, request.xCols, "x");
|
|
1776
|
+
validateSegmentMatrix("segmentCrossSimilarity", request.y, request.yRows, request.yCols, "y");
|
|
1777
|
+
if (request.xRows !== request.yRows) {
|
|
1778
|
+
throw new RangeError("segmentCrossSimilarity: feature dimensions must match");
|
|
1779
|
+
}
|
|
1780
|
+
assertNonNegativeInteger("segmentCrossSimilarity", request.k ?? 0, "k");
|
|
1781
|
+
return requireModule12().segmentCrossSimilarity(
|
|
1782
|
+
request.x,
|
|
1783
|
+
request.xRows,
|
|
1784
|
+
request.xCols,
|
|
1785
|
+
request.y,
|
|
1786
|
+
request.yRows,
|
|
1787
|
+
request.yCols,
|
|
1788
|
+
request.k ?? 0,
|
|
1789
|
+
request.metric ?? "cosine",
|
|
1790
|
+
request.mode ?? "connectivity"
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
function segmentRecurrenceMatrix(request) {
|
|
1794
|
+
validateSegmentMatrix(
|
|
1795
|
+
"segmentRecurrenceMatrix",
|
|
1796
|
+
request.data,
|
|
1797
|
+
request.rows,
|
|
1798
|
+
request.cols,
|
|
1799
|
+
"data"
|
|
1800
|
+
);
|
|
1801
|
+
assertNonNegativeInteger("segmentRecurrenceMatrix", request.k ?? 0, "k");
|
|
1802
|
+
assertNonNegativeInteger("segmentRecurrenceMatrix", request.width ?? 1, "width");
|
|
1803
|
+
return requireModule12().segmentRecurrenceMatrix(
|
|
1804
|
+
request.data,
|
|
1805
|
+
request.rows,
|
|
1806
|
+
request.cols,
|
|
1807
|
+
request.k ?? 0,
|
|
1808
|
+
request.width ?? 1,
|
|
1809
|
+
request.sym ?? false,
|
|
1810
|
+
request.metric ?? "euclidean",
|
|
1811
|
+
request.mode ?? "connectivity"
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
function segmentRecurrenceToLag(request) {
|
|
1815
|
+
validateSegmentMatrix(
|
|
1816
|
+
"segmentRecurrenceToLag",
|
|
1817
|
+
request.recurrence,
|
|
1818
|
+
request.n,
|
|
1819
|
+
request.n,
|
|
1820
|
+
"recurrence"
|
|
1821
|
+
);
|
|
1822
|
+
return requireModule12().segmentRecurrenceToLag(
|
|
1823
|
+
request.recurrence,
|
|
1824
|
+
request.n,
|
|
1825
|
+
request.pad ?? false
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
function segmentLagToRecurrence(request) {
|
|
1829
|
+
validateSegmentMatrix("segmentLagToRecurrence", request.lag, request.rows, request.lags, "lag");
|
|
1830
|
+
return requireModule12().segmentLagToRecurrence(request.lag, request.rows, request.lags);
|
|
1831
|
+
}
|
|
1832
|
+
function segmentSubsegment(request) {
|
|
1833
|
+
validateSegmentMatrix("segmentSubsegment", request.data, request.rows, request.cols, "data");
|
|
1834
|
+
assertPositiveInteger("segmentSubsegment", request.nSegments ?? 4, "nSegments");
|
|
1835
|
+
return requireModule12().segmentSubsegment(
|
|
1836
|
+
request.data,
|
|
1837
|
+
request.rows,
|
|
1838
|
+
request.cols,
|
|
1839
|
+
request.boundaries,
|
|
1840
|
+
request.nSegments ?? 4
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
function segmentAgglomerative(request) {
|
|
1844
|
+
validateSegmentMatrix("segmentAgglomerative", request.data, request.rows, request.cols, "data");
|
|
1845
|
+
assertPositiveInteger("segmentAgglomerative", request.k, "k");
|
|
1846
|
+
return requireModule12().segmentAgglomerative(
|
|
1847
|
+
request.data,
|
|
1848
|
+
request.rows,
|
|
1849
|
+
request.cols,
|
|
1850
|
+
request.k,
|
|
1851
|
+
request.linkage ?? "average"
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
function segmentPathEnhance(request) {
|
|
1855
|
+
validateSegmentMatrix(
|
|
1856
|
+
"segmentPathEnhance",
|
|
1857
|
+
request.recurrence,
|
|
1858
|
+
request.n,
|
|
1859
|
+
request.n,
|
|
1860
|
+
"recurrence"
|
|
1861
|
+
);
|
|
1862
|
+
assertPositiveInteger("segmentPathEnhance", request.win, "win");
|
|
1863
|
+
assertPositiveInteger("segmentPathEnhance", request.maxRatio ?? 2, "maxRatio");
|
|
1864
|
+
assertNonNegativeInteger("segmentPathEnhance", request.minRatio ?? 0, "minRatio");
|
|
1865
|
+
assertPositiveInteger("segmentPathEnhance", request.nFilters ?? 7, "nFilters");
|
|
1866
|
+
return requireModule12().segmentPathEnhance(
|
|
1867
|
+
request.recurrence,
|
|
1868
|
+
request.n,
|
|
1869
|
+
request.win,
|
|
1870
|
+
request.maxRatio ?? 2,
|
|
1871
|
+
request.minRatio ?? 0,
|
|
1872
|
+
request.nFilters ?? 7
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1648
1875
|
|
|
1649
1876
|
// src/feature_spectrogram.ts
|
|
1650
1877
|
function requireModule13() {
|
|
@@ -1708,7 +1935,7 @@ function stftDb(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, optio
|
|
|
1708
1935
|
validatePositiveIntegers2("stftDb", { nFft, hopLength });
|
|
1709
1936
|
return requireModule13().stftDb(samples, sampleRate, nFft, hopLength);
|
|
1710
1937
|
}
|
|
1711
|
-
function chromaCens(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, options = {}) {
|
|
1938
|
+
function chromaCens(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, binsPerOctave = 36, options = {}) {
|
|
1712
1939
|
if (!(samples instanceof Float32Array)) {
|
|
1713
1940
|
const request = samples;
|
|
1714
1941
|
return chromaCens(
|
|
@@ -1716,14 +1943,18 @@ function chromaCens(samples, sampleRate = 22050, hopLength = 512, nChroma = 12,
|
|
|
1716
1943
|
request.sampleRate,
|
|
1717
1944
|
request.hopLength,
|
|
1718
1945
|
request.nChroma,
|
|
1946
|
+
request.binsPerOctave,
|
|
1719
1947
|
request
|
|
1720
1948
|
);
|
|
1721
1949
|
}
|
|
1722
1950
|
validateSpectrogramSamples("chromaCens", samples, sampleRate, options);
|
|
1723
|
-
validatePositiveIntegers2("chromaCens", { hopLength, nChroma });
|
|
1724
|
-
|
|
1951
|
+
validatePositiveIntegers2("chromaCens", { hopLength, nChroma, binsPerOctave });
|
|
1952
|
+
if (binsPerOctave % nChroma !== 0) {
|
|
1953
|
+
throw new RangeError("chromaCens: binsPerOctave must be a multiple of nChroma");
|
|
1954
|
+
}
|
|
1955
|
+
return requireModule13().chromaCens(samples, sampleRate, hopLength, nChroma, binsPerOctave);
|
|
1725
1956
|
}
|
|
1726
|
-
function chromaCqt(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, options = {}) {
|
|
1957
|
+
function chromaCqt(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, binsPerOctave = 36, options = {}) {
|
|
1727
1958
|
if (!(samples instanceof Float32Array)) {
|
|
1728
1959
|
const request = samples;
|
|
1729
1960
|
return chromaCqt(
|
|
@@ -1731,12 +1962,16 @@ function chromaCqt(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, o
|
|
|
1731
1962
|
request.sampleRate,
|
|
1732
1963
|
request.hopLength,
|
|
1733
1964
|
request.nChroma,
|
|
1965
|
+
request.binsPerOctave,
|
|
1734
1966
|
request
|
|
1735
1967
|
);
|
|
1736
1968
|
}
|
|
1737
1969
|
validateSpectrogramSamples("chromaCqt", samples, sampleRate, options);
|
|
1738
|
-
validatePositiveIntegers2("chromaCqt", { hopLength, nChroma });
|
|
1739
|
-
|
|
1970
|
+
validatePositiveIntegers2("chromaCqt", { hopLength, nChroma, binsPerOctave });
|
|
1971
|
+
if (binsPerOctave % nChroma !== 0) {
|
|
1972
|
+
throw new RangeError("chromaCqt: binsPerOctave must be a multiple of nChroma");
|
|
1973
|
+
}
|
|
1974
|
+
return requireModule13().chromaCqt(samples, sampleRate, hopLength, nChroma, binsPerOctave);
|
|
1740
1975
|
}
|
|
1741
1976
|
function bassChroma(samples, sampleRate = 22050, hopLength = 512, nChroma = 12, options = {}) {
|
|
1742
1977
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -1815,6 +2050,53 @@ function mfcc(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, nMels =
|
|
|
1815
2050
|
lifter
|
|
1816
2051
|
);
|
|
1817
2052
|
}
|
|
2053
|
+
function melDelta(features, nFeatures, nFrames, width = 9) {
|
|
2054
|
+
const request = features instanceof Float32Array ? { features, nFeatures: nFeatures ?? 0, nFrames: nFrames ?? 0, width } : features;
|
|
2055
|
+
assertPositiveInteger("melDelta", request.nFeatures, "nFeatures");
|
|
2056
|
+
assertPositiveInteger("melDelta", request.nFrames, "nFrames");
|
|
2057
|
+
assertPositiveInteger("melDelta", request.width ?? 9, "width");
|
|
2058
|
+
if ((request.width ?? 9) < 3 || (request.width ?? 9) % 2 === 0) {
|
|
2059
|
+
throw new RangeError("melDelta: width must be an odd integer of at least 3");
|
|
2060
|
+
}
|
|
2061
|
+
if (request.features.length !== request.nFeatures * request.nFrames) {
|
|
2062
|
+
throw new RangeError("melDelta: feature matrix length must equal nFeatures * nFrames");
|
|
2063
|
+
}
|
|
2064
|
+
return requireModule13().melDelta(
|
|
2065
|
+
request.features,
|
|
2066
|
+
request.nFeatures,
|
|
2067
|
+
request.nFrames,
|
|
2068
|
+
request.width ?? 9
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
function reassignedSpectrogram(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, refPower = 1e-6, fillNan = false) {
|
|
2072
|
+
if (!(samples instanceof Float32Array)) {
|
|
2073
|
+
const request = samples;
|
|
2074
|
+
return reassignedSpectrogram(
|
|
2075
|
+
request.samples,
|
|
2076
|
+
request.sampleRate,
|
|
2077
|
+
request.nFft,
|
|
2078
|
+
request.hopLength,
|
|
2079
|
+
request.refPower,
|
|
2080
|
+
request.fillNan
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
assertSamples("reassignedSpectrogram", samples, true);
|
|
2084
|
+
assertSampleRate("reassignedSpectrogram", sampleRate);
|
|
2085
|
+
assertPositiveInteger("reassignedSpectrogram", nFft, "nFft");
|
|
2086
|
+
assertPositiveInteger("reassignedSpectrogram", hopLength, "hopLength");
|
|
2087
|
+
assertFiniteScalar("reassignedSpectrogram", refPower, "refPower");
|
|
2088
|
+
if (refPower < 0) {
|
|
2089
|
+
throw new RangeError("reassignedSpectrogram: refPower must be non-negative");
|
|
2090
|
+
}
|
|
2091
|
+
return requireModule13().reassignedSpectrogram(
|
|
2092
|
+
samples,
|
|
2093
|
+
sampleRate,
|
|
2094
|
+
nFft,
|
|
2095
|
+
hopLength,
|
|
2096
|
+
refPower,
|
|
2097
|
+
fillNan
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
1818
2100
|
function melToStft(melPower, nMels = 0, nFrames = 0, sampleRate = 22050, nFft = 2048, fmin = 0, fmax = 0, htk = false, options = {}) {
|
|
1819
2101
|
if (!(melPower instanceof Float32Array)) {
|
|
1820
2102
|
const request = melPower;
|
|
@@ -1870,7 +2152,36 @@ function melToAudio(melPower, nMels = 0, nFrames = 0, sampleRate = 22050, nFft =
|
|
|
1870
2152
|
htk
|
|
1871
2153
|
);
|
|
1872
2154
|
}
|
|
1873
|
-
function
|
|
2155
|
+
function griffinLim(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, nFft = 2048, hopLength = 512, nIter = 32, momentum = 0.99, options = {}) {
|
|
2156
|
+
if (!(magnitude instanceof Float32Array)) {
|
|
2157
|
+
const request = magnitude;
|
|
2158
|
+
return griffinLim(
|
|
2159
|
+
request.magnitude,
|
|
2160
|
+
request.nBins,
|
|
2161
|
+
request.nFrames,
|
|
2162
|
+
request.sampleRate,
|
|
2163
|
+
request.nFft,
|
|
2164
|
+
request.hopLength,
|
|
2165
|
+
request.nIter,
|
|
2166
|
+
request.momentum,
|
|
2167
|
+
request
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
assertSampleRate("griffinLim", sampleRate);
|
|
2171
|
+
validateMatrix("griffinLim", magnitude, nBins, nFrames, "magnitude", "nBins", options);
|
|
2172
|
+
validatePositiveIntegers2("griffinLim", { nFft, hopLength, nIter });
|
|
2173
|
+
return requireModule13().griffinLim(
|
|
2174
|
+
magnitude,
|
|
2175
|
+
nBins,
|
|
2176
|
+
nFrames,
|
|
2177
|
+
sampleRate,
|
|
2178
|
+
nFft,
|
|
2179
|
+
hopLength,
|
|
2180
|
+
nIter,
|
|
2181
|
+
momentum
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
function mfccToMel(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, lifter = 0, options = {}) {
|
|
1874
2185
|
if (!(mfccCoefficients instanceof Float32Array)) {
|
|
1875
2186
|
const request = mfccCoefficients;
|
|
1876
2187
|
return mfccToMel(
|
|
@@ -1878,6 +2189,7 @@ function mfccToMel(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, option
|
|
|
1878
2189
|
request.nMfcc,
|
|
1879
2190
|
request.nFrames,
|
|
1880
2191
|
request.nMels,
|
|
2192
|
+
request.lifter,
|
|
1881
2193
|
request
|
|
1882
2194
|
);
|
|
1883
2195
|
}
|
|
@@ -1891,9 +2203,9 @@ function mfccToMel(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, option
|
|
|
1891
2203
|
options
|
|
1892
2204
|
);
|
|
1893
2205
|
validatePositiveIntegers2("mfccToMel", { nMels });
|
|
1894
|
-
return requireModule13().mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels);
|
|
2206
|
+
return requireModule13().mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels, lifter);
|
|
1895
2207
|
}
|
|
1896
|
-
function mfccToAudio(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, sampleRate = 22050, nFft = 2048, hopLength = 512, fmin = 0, fmax = 0, nIter = 32, htk = false, options = {}) {
|
|
2208
|
+
function mfccToAudio(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, sampleRate = 22050, nFft = 2048, hopLength = 512, fmin = 0, fmax = 0, nIter = 32, htk = false, lifter = 0, options = {}) {
|
|
1897
2209
|
if (!(mfccCoefficients instanceof Float32Array)) {
|
|
1898
2210
|
const request = mfccCoefficients;
|
|
1899
2211
|
return mfccToAudio(
|
|
@@ -1908,6 +2220,7 @@ function mfccToAudio(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, samp
|
|
|
1908
2220
|
request.fmax,
|
|
1909
2221
|
request.nIter,
|
|
1910
2222
|
request.htk,
|
|
2223
|
+
request.lifter,
|
|
1911
2224
|
request
|
|
1912
2225
|
);
|
|
1913
2226
|
}
|
|
@@ -1934,7 +2247,8 @@ function mfccToAudio(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, samp
|
|
|
1934
2247
|
fmin,
|
|
1935
2248
|
fmax,
|
|
1936
2249
|
nIter,
|
|
1937
|
-
htk
|
|
2250
|
+
htk,
|
|
2251
|
+
lifter
|
|
1938
2252
|
);
|
|
1939
2253
|
}
|
|
1940
2254
|
function chroma(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, options = {}) {
|
|
@@ -2011,6 +2325,23 @@ var SectionType = {
|
|
|
2011
2325
|
};
|
|
2012
2326
|
|
|
2013
2327
|
// src/analysis_helpers.ts
|
|
2328
|
+
var PITCH_CLASS_NAMES = [
|
|
2329
|
+
"C",
|
|
2330
|
+
"C#",
|
|
2331
|
+
"D",
|
|
2332
|
+
"D#",
|
|
2333
|
+
"E",
|
|
2334
|
+
"F",
|
|
2335
|
+
"F#",
|
|
2336
|
+
"G",
|
|
2337
|
+
"G#",
|
|
2338
|
+
"A",
|
|
2339
|
+
"A#",
|
|
2340
|
+
"B"
|
|
2341
|
+
];
|
|
2342
|
+
function pitchClassName(value) {
|
|
2343
|
+
return PITCH_CLASS_NAMES[value] ?? "C";
|
|
2344
|
+
}
|
|
2014
2345
|
function convertKeyCandidate(wasm) {
|
|
2015
2346
|
return {
|
|
2016
2347
|
key: {
|
|
@@ -2081,6 +2412,8 @@ function convertChordAnalysisResult(wasm) {
|
|
|
2081
2412
|
chords: wasm.chords.map((c) => ({
|
|
2082
2413
|
root: c.root,
|
|
2083
2414
|
bass: c.bass,
|
|
2415
|
+
rootName: pitchClassName(c.root),
|
|
2416
|
+
bassName: pitchClassName(c.bass),
|
|
2084
2417
|
quality: c.quality,
|
|
2085
2418
|
start: c.start,
|
|
2086
2419
|
end: c.end,
|
|
@@ -2106,6 +2439,11 @@ function convertAnalysisResult(wasm) {
|
|
|
2106
2439
|
return {
|
|
2107
2440
|
bpm: wasm.bpm,
|
|
2108
2441
|
bpmConfidence: wasm.bpmConfidence,
|
|
2442
|
+
bpmCandidates: wasm.bpmCandidates.map((candidate) => ({
|
|
2443
|
+
value: candidate.value,
|
|
2444
|
+
confidence: candidate.confidence,
|
|
2445
|
+
relation: candidate.relation
|
|
2446
|
+
})),
|
|
2109
2447
|
key: {
|
|
2110
2448
|
root: wasm.key.root,
|
|
2111
2449
|
mode: wasm.key.mode,
|
|
@@ -2114,11 +2452,14 @@ function convertAnalysisResult(wasm) {
|
|
|
2114
2452
|
shortName: wasm.key.shortName
|
|
2115
2453
|
},
|
|
2116
2454
|
timeSignature: wasm.timeSignature,
|
|
2455
|
+
timeSignatureCandidates: wasm.timeSignatureCandidates,
|
|
2117
2456
|
beatTimes,
|
|
2118
2457
|
beats: wasm.beats,
|
|
2119
2458
|
chords: wasm.chords.map((c) => ({
|
|
2120
2459
|
root: c.root,
|
|
2121
2460
|
bass: c.bass,
|
|
2461
|
+
rootName: pitchClassName(c.root),
|
|
2462
|
+
bassName: pitchClassName(c.bass),
|
|
2122
2463
|
quality: c.quality,
|
|
2123
2464
|
start: c.start,
|
|
2124
2465
|
end: c.end,
|
|
@@ -2202,7 +2543,7 @@ function detectKeyCandidates(samples, sampleRate = 22050, options = {}) {
|
|
|
2202
2543
|
function detectOnsets(samples, sampleRate = 22050, options = {}) {
|
|
2203
2544
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
2204
2545
|
validateAnalysisInput("detectOnsets", request.samples, request.sampleRate ?? 22050, request);
|
|
2205
|
-
return requireModule14().detectOnsets(request.samples, request.sampleRate ?? 22050);
|
|
2546
|
+
return requireModule14().detectOnsets(request.samples, request.sampleRate ?? 22050, request);
|
|
2206
2547
|
}
|
|
2207
2548
|
function detectBeats(samples, sampleRate = 22050, options = {}) {
|
|
2208
2549
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
@@ -2267,7 +2608,7 @@ function chordFunctionalAnalysis(samples, keyRoot, keyMode, sampleRate = 22050,
|
|
|
2267
2608
|
function analyze(samples, sampleRate = 22050, options = {}) {
|
|
2268
2609
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
2269
2610
|
validateAnalysisInput("analyze", request.samples, request.sampleRate ?? 22050, request);
|
|
2270
|
-
const result = requireModule14().analyze(request.samples, request.sampleRate ?? 22050);
|
|
2611
|
+
const result = requireModule14().analyze(request.samples, request.sampleRate ?? 22050, request);
|
|
2271
2612
|
return convertAnalysisResult(result);
|
|
2272
2613
|
}
|
|
2273
2614
|
function analyzeImpulseResponse(samples, sampleRate = 48e3, nOctaveBands = 6) {
|
|
@@ -2334,7 +2675,9 @@ function analyzeWithProgress(samples, sampleRate = 22050, onProgress) {
|
|
|
2334
2675
|
const result = requireModule14().analyzeWithProgress(
|
|
2335
2676
|
request.samples,
|
|
2336
2677
|
request.sampleRate ?? 22050,
|
|
2337
|
-
request.onProgress
|
|
2678
|
+
request.onProgress ?? (() => {
|
|
2679
|
+
}),
|
|
2680
|
+
request.cancel ?? (() => false)
|
|
2338
2681
|
);
|
|
2339
2682
|
return convertAnalysisResult(result);
|
|
2340
2683
|
}
|
|
@@ -2441,7 +2784,8 @@ var Audio = class _Audio {
|
|
|
2441
2784
|
* Node/Python surfaces).
|
|
2442
2785
|
*/
|
|
2443
2786
|
static fromBuffer(samples, sampleRate = 48e3) {
|
|
2444
|
-
|
|
2787
|
+
validateAudioBuffer(samples, sampleRate);
|
|
2788
|
+
return new _Audio(samples.slice(), sampleRate);
|
|
2445
2789
|
}
|
|
2446
2790
|
/**
|
|
2447
2791
|
* Create an Audio instance by decoding audio bytes in memory.
|
|
@@ -2640,7 +2984,7 @@ var Audio = class _Audio {
|
|
|
2640
2984
|
rmsEnergy(frameLength = 2048, hopLength = 512) {
|
|
2641
2985
|
return rmsEnergy(this._samples, this._sampleRate, frameLength, hopLength);
|
|
2642
2986
|
}
|
|
2643
|
-
pitchYin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.
|
|
2987
|
+
pitchYin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.1, fillNa = false) {
|
|
2644
2988
|
return pitchYin(
|
|
2645
2989
|
this._samples,
|
|
2646
2990
|
this._sampleRate,
|
|
@@ -2652,7 +2996,7 @@ var Audio = class _Audio {
|
|
|
2652
2996
|
fillNa
|
|
2653
2997
|
);
|
|
2654
2998
|
}
|
|
2655
|
-
pitchPyin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.
|
|
2999
|
+
pitchPyin(frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.1, fillNa = false) {
|
|
2656
3000
|
return pitchPyin(
|
|
2657
3001
|
this._samples,
|
|
2658
3002
|
this._sampleRate,
|
|
@@ -2757,7 +3101,12 @@ self.onmessage = async (event) => {
|
|
|
2757
3101
|
`;
|
|
2758
3102
|
function createOpfsClipPageWorker() {
|
|
2759
3103
|
const blob = new Blob([opfsClipPageWorkerSource], { type: "text/javascript" });
|
|
2760
|
-
|
|
3104
|
+
const url = URL.createObjectURL(blob);
|
|
3105
|
+
try {
|
|
3106
|
+
return new Worker(url);
|
|
3107
|
+
} finally {
|
|
3108
|
+
URL.revokeObjectURL(url);
|
|
3109
|
+
}
|
|
2761
3110
|
}
|
|
2762
3111
|
function createOpfsClipPageProvider(engine, options) {
|
|
2763
3112
|
if (options.numChannels <= 0 || options.numSamples <= 0 || options.pageFrames <= 0) {
|
|
@@ -2797,6 +3146,7 @@ function createOpfsClipPageProvider(engine, options) {
|
|
|
2797
3146
|
}
|
|
2798
3147
|
try {
|
|
2799
3148
|
provider.supply(response.pageIndex, channels);
|
|
3149
|
+
options.onPageSupplied?.(response.pageIndex, channels);
|
|
2800
3150
|
} catch {
|
|
2801
3151
|
entry.resolve(false);
|
|
2802
3152
|
return;
|
|
@@ -2844,6 +3194,13 @@ function createOpfsClipPageProvider(engine, options) {
|
|
|
2844
3194
|
supplyRequest(request) {
|
|
2845
3195
|
return supplyPage(Math.floor(request.sample / options.pageFrames));
|
|
2846
3196
|
},
|
|
3197
|
+
clearPage(pageIndex) {
|
|
3198
|
+
if (closed) {
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
provider.clear(pageIndex);
|
|
3202
|
+
options.onPageCleared?.(pageIndex);
|
|
3203
|
+
},
|
|
2847
3204
|
close() {
|
|
2848
3205
|
if (closed) {
|
|
2849
3206
|
return;
|
|
@@ -2855,6 +3212,7 @@ function createOpfsClipPageProvider(engine, options) {
|
|
|
2855
3212
|
}
|
|
2856
3213
|
pending.clear();
|
|
2857
3214
|
provider.destroy();
|
|
3215
|
+
options.onClose?.();
|
|
2858
3216
|
if (ownsWorker) {
|
|
2859
3217
|
worker.terminate();
|
|
2860
3218
|
}
|
|
@@ -2932,7 +3290,10 @@ var ClipPageStreamer = class {
|
|
|
2932
3290
|
if (!state) {
|
|
2933
3291
|
continue;
|
|
2934
3292
|
}
|
|
2935
|
-
const page = Math.floor(request.sample / state.source.pageFrames);
|
|
3293
|
+
const page = request.pageIndex !== void 0 ? request.pageIndex : Math.floor((request.sample ?? Number.NaN) / state.source.pageFrames);
|
|
3294
|
+
if (!Number.isInteger(page) || page < 0 || page > state.lastPage) {
|
|
3295
|
+
continue;
|
|
3296
|
+
}
|
|
2936
3297
|
frontiers.set(request.clipId, page);
|
|
2937
3298
|
}
|
|
2938
3299
|
const fetches = [];
|
|
@@ -2967,7 +3328,7 @@ var ClipPageStreamer = class {
|
|
|
2967
3328
|
const high = Math.min(state.lastPage, frontier + this.readAheadPages);
|
|
2968
3329
|
for (const page of state.resident.keys()) {
|
|
2969
3330
|
if (page < low || page > high) {
|
|
2970
|
-
|
|
3331
|
+
this.clearPage(state, page);
|
|
2971
3332
|
state.resident.delete(page);
|
|
2972
3333
|
}
|
|
2973
3334
|
}
|
|
@@ -2982,7 +3343,7 @@ var ClipPageStreamer = class {
|
|
|
2982
3343
|
state.source.binding.supplyPage(pageIndex).then(
|
|
2983
3344
|
(ok) => {
|
|
2984
3345
|
if (state.generation !== generation) {
|
|
2985
|
-
|
|
3346
|
+
this.clearPage(state, pageIndex);
|
|
2986
3347
|
} else if (!ok && state.resident.get(pageIndex) === generation) {
|
|
2987
3348
|
state.resident.delete(pageIndex);
|
|
2988
3349
|
}
|
|
@@ -3003,12 +3364,28 @@ var ClipPageStreamer = class {
|
|
|
3003
3364
|
state.generation += 1;
|
|
3004
3365
|
state.lastFrontier = null;
|
|
3005
3366
|
for (const page of state.resident.keys()) {
|
|
3006
|
-
|
|
3367
|
+
this.clearPage(state, page);
|
|
3007
3368
|
}
|
|
3008
3369
|
state.resident.clear();
|
|
3009
3370
|
}
|
|
3371
|
+
clearPage(state, pageIndex) {
|
|
3372
|
+
if (state.source.binding.clearPage) {
|
|
3373
|
+
state.source.binding.clearPage(pageIndex);
|
|
3374
|
+
} else {
|
|
3375
|
+
state.source.binding.provider.clear(pageIndex);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3010
3378
|
};
|
|
3011
|
-
async function attachOpfsClipStream(
|
|
3379
|
+
async function attachOpfsClipStream(streamerOrEngine, engineOrOptions, maybeOptions) {
|
|
3380
|
+
if (!(streamerOrEngine instanceof ClipPageStreamer)) {
|
|
3381
|
+
return streamerOrEngine.attachOpfsClipStream(engineOrOptions);
|
|
3382
|
+
}
|
|
3383
|
+
const streamer = streamerOrEngine;
|
|
3384
|
+
const engine = engineOrOptions;
|
|
3385
|
+
const options = maybeOptions;
|
|
3386
|
+
if (!options) {
|
|
3387
|
+
throw new Error("attachOpfsClipStream requires options.");
|
|
3388
|
+
}
|
|
3012
3389
|
const { clipId, primePages = 1, ...providerOptions } = options;
|
|
3013
3390
|
const binding = createOpfsClipPageProvider(engine, providerOptions);
|
|
3014
3391
|
const lastPage = Math.ceil(providerOptions.numSamples / providerOptions.pageFrames) - 1;
|
|
@@ -3064,7 +3441,11 @@ async function bindMicrophoneInput(context, engine, options = {}) {
|
|
|
3064
3441
|
function assertOversampleFactor(fnName, factor) {
|
|
3065
3442
|
const normalized = factor === 0 ? 4 : factor;
|
|
3066
3443
|
if (!Number.isInteger(normalized) || normalized < 1 || normalized > 16 || (normalized & normalized - 1) !== 0) {
|
|
3067
|
-
throw new
|
|
3444
|
+
throw new SonareError(
|
|
3445
|
+
4 /* InvalidParameter */,
|
|
3446
|
+
"InvalidParameter",
|
|
3447
|
+
`${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`
|
|
3448
|
+
);
|
|
3068
3449
|
}
|
|
3069
3450
|
}
|
|
3070
3451
|
function requireModule15() {
|
|
@@ -3080,6 +3461,17 @@ function meteringRmsDb(samples, sampleRate = 22050, options = {}) {
|
|
|
3080
3461
|
assertSamples("meteringRmsDb", request.samples, request.validate !== false);
|
|
3081
3462
|
return requireModule15().meteringRmsDb(request.samples, request.sampleRate ?? 22050);
|
|
3082
3463
|
}
|
|
3464
|
+
function meteringSilenceRatio(samples, sampleRate = 22050, thresholdDb = -45, frameLength = 1024, hopLength = 256, options = {}) {
|
|
3465
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, thresholdDb, frameLength, hopLength, ...options } : samples;
|
|
3466
|
+
assertSamples("meteringSilenceRatio", request.samples, request.validate !== false);
|
|
3467
|
+
return requireModule15().meteringSilenceRatio(
|
|
3468
|
+
request.samples,
|
|
3469
|
+
request.sampleRate ?? 22050,
|
|
3470
|
+
request.thresholdDb ?? -45,
|
|
3471
|
+
request.frameLength ?? 1024,
|
|
3472
|
+
request.hopLength ?? 256
|
|
3473
|
+
);
|
|
3474
|
+
}
|
|
3083
3475
|
function meteringCrestFactorDb(samples, sampleRate = 22050, options = {}) {
|
|
3084
3476
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
3085
3477
|
assertSamples("meteringCrestFactorDb", request.samples, request.validate !== false);
|
|
@@ -3100,11 +3492,15 @@ function meteringTruePeakDb(samples, sampleRate = 22050, oversampleFactor = 4, o
|
|
|
3100
3492
|
function meteringDetectClipping(samples, sampleRate = 22050, options = {}) {
|
|
3101
3493
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
3102
3494
|
assertSamples("meteringDetectClipping", request.samples, request.validate !== false);
|
|
3495
|
+
const minRegionSamples = request.minRegionSamples ?? 1;
|
|
3496
|
+
if (!Number.isInteger(minRegionSamples) || minRegionSamples < 0) {
|
|
3497
|
+
throw new RangeError("meteringDetectClipping: minRegionSamples must be a non-negative integer");
|
|
3498
|
+
}
|
|
3103
3499
|
return requireModule15().meteringDetectClipping(
|
|
3104
3500
|
request.samples,
|
|
3105
3501
|
request.sampleRate ?? 22050,
|
|
3106
3502
|
request.threshold ?? 0.999,
|
|
3107
|
-
|
|
3503
|
+
minRegionSamples
|
|
3108
3504
|
);
|
|
3109
3505
|
}
|
|
3110
3506
|
function meteringDynamicRange(samples, sampleRate = 22050, options = {}) {
|
|
@@ -3229,6 +3625,61 @@ function waveformPeakPyramid(samples, channels, options = {}) {
|
|
|
3229
3625
|
return requireModule15().waveformPeakPyramid(request.samples, request.channels, levels);
|
|
3230
3626
|
}
|
|
3231
3627
|
|
|
3628
|
+
// src/codes.ts
|
|
3629
|
+
function resolveEnumOrdinal(value, values, enumName) {
|
|
3630
|
+
if (typeof value === "number") {
|
|
3631
|
+
if (!Number.isSafeInteger(value) || !Object.values(values).includes(value)) {
|
|
3632
|
+
throw new RangeError(`Invalid ${enumName}: ${String(value)}`);
|
|
3633
|
+
}
|
|
3634
|
+
return value;
|
|
3635
|
+
}
|
|
3636
|
+
if (typeof value === "string") {
|
|
3637
|
+
const ordinal = values[value];
|
|
3638
|
+
if (ordinal !== void 0) {
|
|
3639
|
+
return ordinal;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
throw new RangeError(`Invalid ${enumName}: ${String(value)}`);
|
|
3643
|
+
}
|
|
3644
|
+
var AUTOMATION_CURVE_VALUES = {
|
|
3645
|
+
linear: 0,
|
|
3646
|
+
exponential: 1,
|
|
3647
|
+
hold: 2,
|
|
3648
|
+
"s-curve": 3
|
|
3649
|
+
};
|
|
3650
|
+
var PAN_LAW_VALUES = {
|
|
3651
|
+
const3dB: 0,
|
|
3652
|
+
"const4.5dB": 1,
|
|
3653
|
+
const6dB: 2,
|
|
3654
|
+
linear0dB: 3
|
|
3655
|
+
};
|
|
3656
|
+
var PAN_MODE_VALUES = {
|
|
3657
|
+
balance: 0,
|
|
3658
|
+
pan: 0,
|
|
3659
|
+
stereopan: 1,
|
|
3660
|
+
"stereo-pan": 1,
|
|
3661
|
+
dualpan: 2,
|
|
3662
|
+
"dual-pan": 2
|
|
3663
|
+
};
|
|
3664
|
+
var METER_TAP_VALUES = { preFader: 0, postFader: 1 };
|
|
3665
|
+
var SEND_TIMING_VALUES = { postFader: 0, preFader: 1 };
|
|
3666
|
+
function automationCurveCode(curve) {
|
|
3667
|
+
return resolveEnumOrdinal(curve, AUTOMATION_CURVE_VALUES, "automation curve");
|
|
3668
|
+
}
|
|
3669
|
+
function panLawCode(panLaw) {
|
|
3670
|
+
return resolveEnumOrdinal(panLaw, PAN_LAW_VALUES, "pan law");
|
|
3671
|
+
}
|
|
3672
|
+
function panModeCode(panMode) {
|
|
3673
|
+
const normalized = typeof panMode === "string" ? panMode.replace(/_/g, "-").toLowerCase() : panMode;
|
|
3674
|
+
return resolveEnumOrdinal(normalized, PAN_MODE_VALUES, "pan mode");
|
|
3675
|
+
}
|
|
3676
|
+
function meterTapCode(tap) {
|
|
3677
|
+
return resolveEnumOrdinal(tap, METER_TAP_VALUES, "meter tap");
|
|
3678
|
+
}
|
|
3679
|
+
function sendTimingCode(timing) {
|
|
3680
|
+
return resolveEnumOrdinal(timing, SEND_TIMING_VALUES, "send timing");
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3232
3683
|
// src/project_internal.ts
|
|
3233
3684
|
function projectModule() {
|
|
3234
3685
|
const candidate = getSonareModule();
|
|
@@ -3295,37 +3746,17 @@ function assertProjectMidiEvents(fnName, events) {
|
|
|
3295
3746
|
});
|
|
3296
3747
|
}
|
|
3297
3748
|
function projectTrackKindValue(kind) {
|
|
3298
|
-
|
|
3299
|
-
return 0;
|
|
3300
|
-
}
|
|
3301
|
-
if (kind === "midi") {
|
|
3302
|
-
return 1;
|
|
3303
|
-
}
|
|
3304
|
-
if (kind === "aux") {
|
|
3305
|
-
return 2;
|
|
3306
|
-
}
|
|
3307
|
-
return kind;
|
|
3749
|
+
return resolveEnumOrdinal(kind ?? "audio", { audio: 0, midi: 1, aux: 2 }, "project track kind");
|
|
3308
3750
|
}
|
|
3309
3751
|
function projectWarpModeValue(mode) {
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
}
|
|
3316
|
-
if (mode === "tempo-sync") {
|
|
3317
|
-
return 2;
|
|
3318
|
-
}
|
|
3319
|
-
return mode;
|
|
3752
|
+
return resolveEnumOrdinal(
|
|
3753
|
+
mode ?? "off",
|
|
3754
|
+
{ off: 0, repitch: 1, "tempo-sync": 2 },
|
|
3755
|
+
"project warp mode"
|
|
3756
|
+
);
|
|
3320
3757
|
}
|
|
3321
3758
|
function projectLoopModeValue(mode) {
|
|
3322
|
-
|
|
3323
|
-
return 0;
|
|
3324
|
-
}
|
|
3325
|
-
if (mode === "loop") {
|
|
3326
|
-
return 1;
|
|
3327
|
-
}
|
|
3328
|
-
return mode;
|
|
3759
|
+
return resolveEnumOrdinal(mode ?? "off", { off: 0, loop: 1 }, "project loop mode");
|
|
3329
3760
|
}
|
|
3330
3761
|
|
|
3331
3762
|
// src/project_class.ts
|
|
@@ -3498,6 +3929,16 @@ var Project = class _Project {
|
|
|
3498
3929
|
addClip(desc) {
|
|
3499
3930
|
return this.native.addClip(desc);
|
|
3500
3931
|
}
|
|
3932
|
+
/** Import host-separated PCM through the normal audio-track/clip path. */
|
|
3933
|
+
importExternalStems(request) {
|
|
3934
|
+
return this.native.importExternalStems({
|
|
3935
|
+
...request,
|
|
3936
|
+
stems: request.stems.map((stem) => ({
|
|
3937
|
+
...stem,
|
|
3938
|
+
layout: stem.layout === "mono" ? 1 : stem.layout === "stereo" ? 2 : stem.layout
|
|
3939
|
+
}))
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3501
3942
|
/** Split captured loop-recording audio into takes and add one clip. */
|
|
3502
3943
|
addLoopRecordingTakes(desc) {
|
|
3503
3944
|
return this.native.addLoopRecordingTakes(desc);
|
|
@@ -3542,7 +3983,11 @@ var Project = class _Project {
|
|
|
3542
3983
|
* Route a track's MIDI to host-instrument `destinationId` (0 = default). The
|
|
3543
3984
|
* compiler stamps every MIDI clip on the track with this id so the engine
|
|
3544
3985
|
* dispatches its events to the instrument registered for that destination.
|
|
3545
|
-
* Routes through an undoable edit command.
|
|
3986
|
+
* Routes through an undoable edit command. Builtin, NativeSynth, and SF2
|
|
3987
|
+
* instruments retain source-track provenance inside a shared destination
|
|
3988
|
+
* voice pool. With only zero-latency instruments bound, live lanes and
|
|
3989
|
+
* channel-strip bounces remain aligned. Configure one live lane per source
|
|
3990
|
+
* track that needs strip processing.
|
|
3546
3991
|
*/
|
|
3547
3992
|
setTrackMidiDestination(trackId, destinationId) {
|
|
3548
3993
|
this.native.setTrackMidiDestination(trackId, destinationId);
|
|
@@ -3587,7 +4032,11 @@ var Project = class _Project {
|
|
|
3587
4032
|
assertProjectMidiEvents("Project.setMidiEvents", events);
|
|
3588
4033
|
this.native.setMidiEvents(clipId, events);
|
|
3589
4034
|
}
|
|
3590
|
-
/**
|
|
4035
|
+
/**
|
|
4036
|
+
* Import an in-memory SMF buffer; returns the first added clip id.
|
|
4037
|
+
* Malformed or partially truncated tracks are rejected instead of installing
|
|
4038
|
+
* a silently shortened clip.
|
|
4039
|
+
*/
|
|
3591
4040
|
importSmf(data) {
|
|
3592
4041
|
return this.native.importSmf(data);
|
|
3593
4042
|
}
|
|
@@ -3624,7 +4073,10 @@ var Project = class _Project {
|
|
|
3624
4073
|
setProgramOnChannel(clipId, group, channel, program, bank = -1) {
|
|
3625
4074
|
this.native.setProgramOnChannel(clipId, group, channel, program, bank);
|
|
3626
4075
|
}
|
|
3627
|
-
/**
|
|
4076
|
+
/**
|
|
4077
|
+
* Destructively bake a MIDI-FX chain into all stored events. Large clips are
|
|
4078
|
+
* drained without truncation; failure leaves the original clip unchanged.
|
|
4079
|
+
*/
|
|
3628
4080
|
bakeMidiFx(clipId, configJson) {
|
|
3629
4081
|
this.native.bakeMidiFx(clipId, configJson);
|
|
3630
4082
|
}
|
|
@@ -3634,9 +4086,9 @@ var Project = class _Project {
|
|
|
3634
4086
|
}
|
|
3635
4087
|
/**
|
|
3636
4088
|
* Pre-flight check for hanging / unmatched notes in a MIDI clip: reports
|
|
3637
|
-
* whether every note-on
|
|
3638
|
-
*
|
|
3639
|
-
* or not a MIDI clip.
|
|
4089
|
+
* whether every note-on in the exported half-open playback window has a
|
|
4090
|
+
* matching note-off (FIFO per group+channel+note). Useful before bouncing to
|
|
4091
|
+
* catch a stuck note. Throws if `clipId` is unknown or not a MIDI clip.
|
|
3640
4092
|
*/
|
|
3641
4093
|
validateMidiNotes(clipId) {
|
|
3642
4094
|
return this.native.validateMidiNotes(clipId);
|
|
@@ -3772,6 +4224,14 @@ var Project = class _Project {
|
|
|
3772
4224
|
setClipFade(clipId, fadeIn = {}, fadeOut = {}) {
|
|
3773
4225
|
this.native.setClipFade(clipId, fadeIn, fadeOut);
|
|
3774
4226
|
}
|
|
4227
|
+
/** Audio source ids that need decoded PCM after deserialization. */
|
|
4228
|
+
unresolvedAudioSourceIds() {
|
|
4229
|
+
return this.native.unresolvedAudioSourceIds();
|
|
4230
|
+
}
|
|
4231
|
+
/** Register decoded interleaved PCM for an existing audio source (undoable). */
|
|
4232
|
+
setSourceAudio(sourceId, audio, channels, sampleRate) {
|
|
4233
|
+
this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
|
|
4234
|
+
}
|
|
3775
4235
|
/** Replace a clip's take list and active take id (undoable). */
|
|
3776
4236
|
setClipTakes(clipId, takes, activeTakeId = 0) {
|
|
3777
4237
|
this.native.setClipTakes(clipId, takes, activeTakeId);
|
|
@@ -3813,23 +4273,23 @@ var Project = class _Project {
|
|
|
3813
4273
|
setTrackRoute(trackId, channelStripRef, outputTarget) {
|
|
3814
4274
|
this.native.setTrackRoute(trackId, channelStripRef ?? "", outputTarget ?? "");
|
|
3815
4275
|
}
|
|
3816
|
-
/** Append an automation lane
|
|
4276
|
+
/** Append an automation lane; returns its stable target parameter id (undoable). */
|
|
3817
4277
|
addAutomationLane(trackId, desc) {
|
|
3818
4278
|
return this.native.addAutomationLane(trackId, {
|
|
3819
4279
|
targetParamId: desc.targetParamId,
|
|
3820
4280
|
points: desc.points
|
|
3821
4281
|
});
|
|
3822
4282
|
}
|
|
3823
|
-
/** Replace
|
|
3824
|
-
editAutomationLane(trackId,
|
|
3825
|
-
this.native.editAutomationLane(trackId,
|
|
4283
|
+
/** Replace the lane identified by its stable target parameter id (undoable). */
|
|
4284
|
+
editAutomationLane(trackId, targetParamId, desc) {
|
|
4285
|
+
this.native.editAutomationLane(trackId, targetParamId, {
|
|
3826
4286
|
targetParamId: desc.targetParamId,
|
|
3827
4287
|
points: desc.points
|
|
3828
4288
|
});
|
|
3829
4289
|
}
|
|
3830
|
-
/** Remove
|
|
3831
|
-
removeAutomationLane(trackId,
|
|
3832
|
-
this.native.removeAutomationLane(trackId,
|
|
4290
|
+
/** Remove the lane identified by its stable target parameter id (undoable). */
|
|
4291
|
+
removeAutomationLane(trackId, targetParamId) {
|
|
4292
|
+
this.native.removeAutomationLane(trackId, targetParamId);
|
|
3833
4293
|
}
|
|
3834
4294
|
/** Replace the project's key annotation stream (undoable). */
|
|
3835
4295
|
annotateKeys(keys) {
|
|
@@ -3893,6 +4353,18 @@ var Project = class _Project {
|
|
|
3893
4353
|
markerByIndex(index) {
|
|
3894
4354
|
return this.native.markerByIndex(index);
|
|
3895
4355
|
}
|
|
4356
|
+
/** Read a stored project track by 0-based index. */
|
|
4357
|
+
trackByIndex(index) {
|
|
4358
|
+
return this.native.trackByIndex(index);
|
|
4359
|
+
}
|
|
4360
|
+
/** Read a stored project clip by 0-based index. */
|
|
4361
|
+
clipByIndex(index) {
|
|
4362
|
+
return this.native.clipByIndex(index);
|
|
4363
|
+
}
|
|
4364
|
+
/** Read a stored project source by 0-based index. */
|
|
4365
|
+
sourceByIndex(index) {
|
|
4366
|
+
return this.native.sourceByIndex(index);
|
|
4367
|
+
}
|
|
3896
4368
|
/** Number of markers in the project. */
|
|
3897
4369
|
markerCount() {
|
|
3898
4370
|
return this.native.markerCount();
|
|
@@ -3966,6 +4438,7 @@ var MarkerKind = {
|
|
|
3966
4438
|
cuePoint: 3,
|
|
3967
4439
|
keySignature: 4
|
|
3968
4440
|
};
|
|
4441
|
+
var BUILTIN_SYNTH_WAVEFORMS = ["sine", "saw", "sawtooth", "square", "triangle"];
|
|
3969
4442
|
var SYNTH_ENGINE_MODES = [
|
|
3970
4443
|
"default",
|
|
3971
4444
|
"subtractive",
|
|
@@ -4028,82 +4501,6 @@ var SYNTH_MOD_DESTINATIONS = [
|
|
|
4028
4501
|
"pan-units"
|
|
4029
4502
|
];
|
|
4030
4503
|
|
|
4031
|
-
// src/codes.ts
|
|
4032
|
-
function automationCurveCode(curve) {
|
|
4033
|
-
switch (curve) {
|
|
4034
|
-
case "linear":
|
|
4035
|
-
return 0;
|
|
4036
|
-
case "exponential":
|
|
4037
|
-
return 1;
|
|
4038
|
-
case "hold":
|
|
4039
|
-
return 2;
|
|
4040
|
-
case "s-curve":
|
|
4041
|
-
return 3;
|
|
4042
|
-
default:
|
|
4043
|
-
throw new Error(`Invalid automation curve: ${curve}`);
|
|
4044
|
-
}
|
|
4045
|
-
}
|
|
4046
|
-
function panLawCode(panLaw) {
|
|
4047
|
-
if (typeof panLaw === "number") {
|
|
4048
|
-
return panLaw;
|
|
4049
|
-
}
|
|
4050
|
-
switch (panLaw) {
|
|
4051
|
-
case "const3dB":
|
|
4052
|
-
return 0;
|
|
4053
|
-
case "const4.5dB":
|
|
4054
|
-
return 1;
|
|
4055
|
-
case "const6dB":
|
|
4056
|
-
return 2;
|
|
4057
|
-
case "linear0dB":
|
|
4058
|
-
return 3;
|
|
4059
|
-
default:
|
|
4060
|
-
throw new Error(`Invalid pan law: ${panLaw}`);
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
function panModeCode(panMode) {
|
|
4064
|
-
if (typeof panMode === "number") {
|
|
4065
|
-
return panMode;
|
|
4066
|
-
}
|
|
4067
|
-
switch (panMode) {
|
|
4068
|
-
case "balance":
|
|
4069
|
-
return 0;
|
|
4070
|
-
case "stereoPan":
|
|
4071
|
-
case "stereo-pan":
|
|
4072
|
-
return 1;
|
|
4073
|
-
case "dualPan":
|
|
4074
|
-
case "dual-pan":
|
|
4075
|
-
return 2;
|
|
4076
|
-
default:
|
|
4077
|
-
throw new Error(`Invalid pan mode: ${panMode}`);
|
|
4078
|
-
}
|
|
4079
|
-
}
|
|
4080
|
-
function meterTapCode(tap) {
|
|
4081
|
-
if (typeof tap === "number") {
|
|
4082
|
-
return tap;
|
|
4083
|
-
}
|
|
4084
|
-
switch (tap) {
|
|
4085
|
-
case "preFader":
|
|
4086
|
-
return 0;
|
|
4087
|
-
case "postFader":
|
|
4088
|
-
return 1;
|
|
4089
|
-
default:
|
|
4090
|
-
throw new Error(`Invalid meter tap: ${tap}`);
|
|
4091
|
-
}
|
|
4092
|
-
}
|
|
4093
|
-
function sendTimingCode(timing) {
|
|
4094
|
-
if (typeof timing === "number") {
|
|
4095
|
-
return timing;
|
|
4096
|
-
}
|
|
4097
|
-
switch (timing) {
|
|
4098
|
-
case "postFader":
|
|
4099
|
-
return 0;
|
|
4100
|
-
case "preFader":
|
|
4101
|
-
return 1;
|
|
4102
|
-
default:
|
|
4103
|
-
throw new Error(`Invalid send timing: ${timing}`);
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
|
|
4107
4504
|
// src/realtime_engine.ts
|
|
4108
4505
|
var EXPECTED_ENGINE_ABI_VERSION = 3;
|
|
4109
4506
|
function engineCapabilities() {
|
|
@@ -4122,23 +4519,30 @@ function engineCapabilities() {
|
|
|
4122
4519
|
};
|
|
4123
4520
|
}
|
|
4124
4521
|
var RealtimeEngine = class {
|
|
4125
|
-
constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024) {
|
|
4522
|
+
constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024, maxChannels = 64) {
|
|
4126
4523
|
const module2 = getSonareModule();
|
|
4127
|
-
const
|
|
4128
|
-
if (!
|
|
4524
|
+
const capabilities2 = engineCapabilities();
|
|
4525
|
+
if (!capabilities2.abiCompatible) {
|
|
4129
4526
|
throw new Error(
|
|
4130
|
-
`Engine ABI mismatch: wasm=${
|
|
4527
|
+
`Engine ABI mismatch: wasm=${capabilities2.engineAbiVersion}, expected=${capabilities2.expectedEngineAbiVersion}`
|
|
4131
4528
|
);
|
|
4132
4529
|
}
|
|
4133
4530
|
this.native = new module2.RealtimeEngine(
|
|
4134
4531
|
sampleRate,
|
|
4135
4532
|
maxBlockSize,
|
|
4136
4533
|
commandCapacity,
|
|
4137
|
-
telemetryCapacity
|
|
4534
|
+
telemetryCapacity,
|
|
4535
|
+
maxChannels
|
|
4138
4536
|
);
|
|
4139
4537
|
}
|
|
4140
|
-
prepare(sampleRate, maxBlockSize, commandCapacity = 1024, telemetryCapacity = 1024) {
|
|
4141
|
-
this.native.
|
|
4538
|
+
prepare(sampleRate, maxBlockSize, commandCapacity = 1024, telemetryCapacity = 1024, maxChannels = 64) {
|
|
4539
|
+
this.native.prepareWithChannels(
|
|
4540
|
+
sampleRate,
|
|
4541
|
+
maxBlockSize,
|
|
4542
|
+
commandCapacity,
|
|
4543
|
+
telemetryCapacity,
|
|
4544
|
+
maxChannels
|
|
4545
|
+
);
|
|
4142
4546
|
}
|
|
4143
4547
|
/** Queue a sample-accurate parameter change (engine kSetParam). */
|
|
4144
4548
|
setParameter(paramId, value, renderFrame = -1) {
|
|
@@ -4218,6 +4622,10 @@ var RealtimeEngine = class {
|
|
|
4218
4622
|
options.maxValue ?? 1
|
|
4219
4623
|
);
|
|
4220
4624
|
}
|
|
4625
|
+
/** Bind a 7/14-bit CC, RPN, or NRPN descriptor to a live parameter. */
|
|
4626
|
+
bindMidiCcBinding(binding) {
|
|
4627
|
+
this.native.bindMidiCcBinding(binding);
|
|
4628
|
+
}
|
|
4221
4629
|
clearMidiCcBindings() {
|
|
4222
4630
|
this.native.clearMidiCcBindings();
|
|
4223
4631
|
}
|
|
@@ -4260,6 +4668,9 @@ var RealtimeEngine = class {
|
|
|
4260
4668
|
externalMidiDroppedCount() {
|
|
4261
4669
|
return this.native.externalMidiDroppedCount();
|
|
4262
4670
|
}
|
|
4671
|
+
externalMidiPendingCount() {
|
|
4672
|
+
return this.native.externalMidiPendingCount();
|
|
4673
|
+
}
|
|
4263
4674
|
/**
|
|
4264
4675
|
* Drain queued external-MIDI events, already lowered to MIDI 1.0 byte
|
|
4265
4676
|
* messages ready to write to a Web MIDI output port. Call once per audio
|
|
@@ -4270,6 +4681,25 @@ var RealtimeEngine = class {
|
|
|
4270
4681
|
drainExternalMidi(maxRecords = 1024) {
|
|
4271
4682
|
return this.native.drainExternalMidi(maxRecords);
|
|
4272
4683
|
}
|
|
4684
|
+
/** Scalar, allocation-free external-MIDI drain for AudioWorklet SAB output. */
|
|
4685
|
+
popExternalMidiToScratch() {
|
|
4686
|
+
return this.native.popExternalMidiToScratch();
|
|
4687
|
+
}
|
|
4688
|
+
externalMidiScratchDestinationId() {
|
|
4689
|
+
return this.native.externalMidiScratchDestinationId();
|
|
4690
|
+
}
|
|
4691
|
+
externalMidiScratchRenderFrame() {
|
|
4692
|
+
return this.native.externalMidiScratchRenderFrame();
|
|
4693
|
+
}
|
|
4694
|
+
externalMidiScratchByteWord() {
|
|
4695
|
+
return this.native.externalMidiScratchByteWord();
|
|
4696
|
+
}
|
|
4697
|
+
externalMidiScratchByteCount() {
|
|
4698
|
+
return this.native.externalMidiScratchByteCount();
|
|
4699
|
+
}
|
|
4700
|
+
consumeExternalMidiScratch() {
|
|
4701
|
+
this.native.consumeExternalMidiScratch();
|
|
4702
|
+
}
|
|
4273
4703
|
pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples = 0) {
|
|
4274
4704
|
this.native.pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
|
|
4275
4705
|
}
|
|
@@ -4294,6 +4724,10 @@ var RealtimeEngine = class {
|
|
|
4294
4724
|
pushMidiCc(destinationId, group, channel, controller, value, renderFrame = -1) {
|
|
4295
4725
|
this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
|
|
4296
4726
|
}
|
|
4727
|
+
/** Queue one immediate MIDI 1.0 channel-voice UMP word for a destination. */
|
|
4728
|
+
pushMidiUmp(destinationId, word0, renderFrame = -1) {
|
|
4729
|
+
this.native.pushMidiUmp(destinationId, word0, renderFrame);
|
|
4730
|
+
}
|
|
4297
4731
|
/**
|
|
4298
4732
|
* Queue an immediate (live) MIDI SysEx frame to a MIDI destination. `data` is
|
|
4299
4733
|
* the full message including the leading 0xF0 and trailing 0xF7 (1..512
|
|
@@ -4339,9 +4773,14 @@ var RealtimeEngine = class {
|
|
|
4339
4773
|
settleParameters() {
|
|
4340
4774
|
this.native.settleParameters();
|
|
4341
4775
|
}
|
|
4776
|
+
/** Drains queued commands on an offline/control-only engine immediately. */
|
|
4777
|
+
flushControlCommands() {
|
|
4778
|
+
this.native.flushControlCommands();
|
|
4779
|
+
}
|
|
4342
4780
|
seekPpq(ppq, renderFrame = -1) {
|
|
4343
4781
|
this.native.seekPpq(ppq, renderFrame);
|
|
4344
4782
|
}
|
|
4783
|
+
/** Set a finite tempo in the range (0, 100000] BPM. */
|
|
4345
4784
|
setTempo(bpm) {
|
|
4346
4785
|
this.native.setTempo(bpm);
|
|
4347
4786
|
}
|
|
@@ -4396,6 +4835,7 @@ var RealtimeEngine = class {
|
|
|
4396
4835
|
setLoopFromMarkers(startMarkerId, endMarkerId) {
|
|
4397
4836
|
this.native.setLoopFromMarkers(startMarkerId, endMarkerId);
|
|
4398
4837
|
}
|
|
4838
|
+
/** Set a metronome config; click lengths are limited to one second. */
|
|
4399
4839
|
setMetronome(config) {
|
|
4400
4840
|
this.native.setMetronome(config);
|
|
4401
4841
|
}
|
|
@@ -4591,6 +5031,23 @@ var RealtimeEngine = class {
|
|
|
4591
5031
|
popClipPageRequest() {
|
|
4592
5032
|
return this.native.popClipPageRequest();
|
|
4593
5033
|
}
|
|
5034
|
+
/**
|
|
5035
|
+
* Moves one native request into the binding's persistent scalar scratch.
|
|
5036
|
+
* This avoids creating an embind JS object in AudioWorklet process().
|
|
5037
|
+
*/
|
|
5038
|
+
popClipPageRequestToScratch() {
|
|
5039
|
+
return this.native.popClipPageRequestToScratch();
|
|
5040
|
+
}
|
|
5041
|
+
clipPageRequestScratchClipId() {
|
|
5042
|
+
return this.native.clipPageRequestScratchClipId();
|
|
5043
|
+
}
|
|
5044
|
+
clipPageRequestScratchSample() {
|
|
5045
|
+
return this.native.clipPageRequestScratchSample();
|
|
5046
|
+
}
|
|
5047
|
+
/** Cumulative page misses dropped because the native bounded request queue was full. */
|
|
5048
|
+
clipPageRequestOverflowCount() {
|
|
5049
|
+
return this.native.clipPageRequestOverflowCount();
|
|
5050
|
+
}
|
|
4594
5051
|
setCaptureBuffer(numChannels, capacityFrames) {
|
|
4595
5052
|
this.native.setCaptureBuffer(numChannels, capacityFrames);
|
|
4596
5053
|
}
|
|
@@ -4603,6 +5060,7 @@ var RealtimeEngine = class {
|
|
|
4603
5060
|
setCaptureSource(source) {
|
|
4604
5061
|
this.native.setCaptureSource(source);
|
|
4605
5062
|
}
|
|
5063
|
+
/** Positive values delay capture relative to the punch window. */
|
|
4606
5064
|
setRecordOffsetSamples(offsetSamples) {
|
|
4607
5065
|
this.native.setRecordOffsetSamples(offsetSamples);
|
|
4608
5066
|
}
|
|
@@ -4618,6 +5076,10 @@ var RealtimeEngine = class {
|
|
|
4618
5076
|
capturedAudio() {
|
|
4619
5077
|
return this.native.capturedAudio();
|
|
4620
5078
|
}
|
|
5079
|
+
/**
|
|
5080
|
+
* Renders in place, adding engine output to `channels`. Zero each plane first
|
|
5081
|
+
* when it contains no upstream input.
|
|
5082
|
+
*/
|
|
4621
5083
|
process(channels) {
|
|
4622
5084
|
return this.native.process(channels);
|
|
4623
5085
|
}
|
|
@@ -4640,6 +5102,7 @@ var RealtimeEngine = class {
|
|
|
4640
5102
|
}
|
|
4641
5103
|
/**
|
|
4642
5104
|
* Runs the engine in place over the prepared per-channel scratch buffers.
|
|
5105
|
+
* Zero each active span first when it contains no upstream input.
|
|
4643
5106
|
* Allocation-free: safe to call on the AudioWorklet render thread after
|
|
4644
5107
|
* `prepareChannels`.
|
|
4645
5108
|
*/
|
|
@@ -4661,6 +5124,42 @@ var RealtimeEngine = class {
|
|
|
4661
5124
|
drainTelemetry(maxRecords = 1024) {
|
|
4662
5125
|
return this.native.drainTelemetry(maxRecords);
|
|
4663
5126
|
}
|
|
5127
|
+
popTelemetryToScratch() {
|
|
5128
|
+
return this.native.popTelemetryToScratch();
|
|
5129
|
+
}
|
|
5130
|
+
telemetryScratchType() {
|
|
5131
|
+
return this.native.telemetryScratchType();
|
|
5132
|
+
}
|
|
5133
|
+
telemetryScratchError() {
|
|
5134
|
+
return this.native.telemetryScratchError();
|
|
5135
|
+
}
|
|
5136
|
+
telemetryScratchRenderFrame() {
|
|
5137
|
+
return Number(this.native.telemetryScratchRenderFrame());
|
|
5138
|
+
}
|
|
5139
|
+
telemetryScratchTimelineSample() {
|
|
5140
|
+
return Number(this.native.telemetryScratchTimelineSample());
|
|
5141
|
+
}
|
|
5142
|
+
telemetryScratchAudibleTimelineSample() {
|
|
5143
|
+
return Number(this.native.telemetryScratchAudibleTimelineSample());
|
|
5144
|
+
}
|
|
5145
|
+
telemetryScratchGraphLatencySamplesQ8() {
|
|
5146
|
+
return this.native.telemetryScratchGraphLatencySamplesQ8();
|
|
5147
|
+
}
|
|
5148
|
+
telemetryScratchValue() {
|
|
5149
|
+
return this.native.telemetryScratchValue();
|
|
5150
|
+
}
|
|
5151
|
+
popMeterTelemetryToScratch() {
|
|
5152
|
+
return this.native.popMeterTelemetryToScratch();
|
|
5153
|
+
}
|
|
5154
|
+
meterScratchTargetId() {
|
|
5155
|
+
return this.native.meterScratchTargetId();
|
|
5156
|
+
}
|
|
5157
|
+
meterScratchRenderFrame() {
|
|
5158
|
+
return Number(this.native.meterScratchRenderFrame());
|
|
5159
|
+
}
|
|
5160
|
+
meterScratchValue(field) {
|
|
5161
|
+
return this.native.meterScratchValue(field);
|
|
5162
|
+
}
|
|
4664
5163
|
drainMeterTelemetry(maxRecords = 1024) {
|
|
4665
5164
|
return this.native.drainMeterTelemetry(maxRecords);
|
|
4666
5165
|
}
|
|
@@ -4688,6 +5187,30 @@ var RealtimeEngine = class {
|
|
|
4688
5187
|
drainScopeTelemetry(maxRecords = 1024) {
|
|
4689
5188
|
return this.native.drainScopeTelemetry(maxRecords);
|
|
4690
5189
|
}
|
|
5190
|
+
popScopeTelemetryToScratch() {
|
|
5191
|
+
return this.native.popScopeTelemetryToScratch();
|
|
5192
|
+
}
|
|
5193
|
+
scopeScratchTargetId() {
|
|
5194
|
+
return this.native.scopeScratchTargetId();
|
|
5195
|
+
}
|
|
5196
|
+
scopeScratchRenderFrame() {
|
|
5197
|
+
return Number(this.native.scopeScratchRenderFrame());
|
|
5198
|
+
}
|
|
5199
|
+
scopeScratchBandCount() {
|
|
5200
|
+
return this.native.scopeScratchBandCount();
|
|
5201
|
+
}
|
|
5202
|
+
scopeScratchBand(index) {
|
|
5203
|
+
return this.native.scopeScratchBand(index);
|
|
5204
|
+
}
|
|
5205
|
+
scopeScratchPointCount() {
|
|
5206
|
+
return this.native.scopeScratchPointCount();
|
|
5207
|
+
}
|
|
5208
|
+
scopeScratchPointLeft(index) {
|
|
5209
|
+
return this.native.scopeScratchPointLeft(index);
|
|
5210
|
+
}
|
|
5211
|
+
scopeScratchPointRight(index) {
|
|
5212
|
+
return this.native.scopeScratchPointRight(index);
|
|
5213
|
+
}
|
|
4691
5214
|
destroy() {
|
|
4692
5215
|
this.native.delete();
|
|
4693
5216
|
}
|
|
@@ -4980,7 +5503,10 @@ var Mixer = class _Mixer {
|
|
|
4980
5503
|
const module2 = getSonareModule();
|
|
4981
5504
|
return new _Mixer(module2.createMixerFromSceneJson(json, sampleRate, blockSize), blockSize);
|
|
4982
5505
|
}
|
|
4983
|
-
/**
|
|
5506
|
+
/**
|
|
5507
|
+
* Rebuild and compile the routing graph without resetting its absolute
|
|
5508
|
+
* automation sample position or queued strip automation.
|
|
5509
|
+
*/
|
|
4984
5510
|
compile() {
|
|
4985
5511
|
this.mixer.compile();
|
|
4986
5512
|
}
|
|
@@ -5139,6 +5665,10 @@ var Mixer = class _Mixer {
|
|
|
5139
5665
|
setVcaGroupGainDb(id, gainDb) {
|
|
5140
5666
|
this.mixer.setVcaGroupGainDb(id, gainDb);
|
|
5141
5667
|
}
|
|
5668
|
+
/** Replace an existing VCA group's strip membership. */
|
|
5669
|
+
setVcaGroupMembers(id, members) {
|
|
5670
|
+
this.mixer.setVcaGroupMembers(id, members);
|
|
5671
|
+
}
|
|
5142
5672
|
/** Remove a VCA group by id. */
|
|
5143
5673
|
removeVcaGroup(id) {
|
|
5144
5674
|
this.mixer.removeVcaGroup(id);
|
|
@@ -5278,6 +5808,10 @@ var Mixer = class _Mixer {
|
|
|
5278
5808
|
}
|
|
5279
5809
|
return this.mixer.meterTap(stripIndex, meterTapCode(tap));
|
|
5280
5810
|
}
|
|
5811
|
+
/** Read the post-insert meter for a compiled bus, including master. */
|
|
5812
|
+
busMeter(busId) {
|
|
5813
|
+
return this.mixer.busMeter(busId);
|
|
5814
|
+
}
|
|
5281
5815
|
/**
|
|
5282
5816
|
* Schedule sample-accurate fader automation on a strip.
|
|
5283
5817
|
*
|
|
@@ -5378,65 +5912,18 @@ var Mixer = class _Mixer {
|
|
|
5378
5912
|
};
|
|
5379
5913
|
|
|
5380
5914
|
// src/realtime_voice_changer.ts
|
|
5381
|
-
function isFlatVoiceChangerPod(config) {
|
|
5382
|
-
return typeof config === "object" && config !== null && "retuneSemitones" in config;
|
|
5383
|
-
}
|
|
5384
|
-
function flatVoiceChangerPodToNested(pod) {
|
|
5385
|
-
return {
|
|
5386
|
-
inputGainDb: pod.inputGainDb,
|
|
5387
|
-
outputGainDb: pod.outputGainDb,
|
|
5388
|
-
wetMix: pod.wetMix,
|
|
5389
|
-
retune: { semitones: pod.retuneSemitones, mix: pod.retuneMix, grainSize: pod.retuneGrainSize },
|
|
5390
|
-
formant: {
|
|
5391
|
-
factor: pod.formantFactor,
|
|
5392
|
-
amount: pod.formantAmount,
|
|
5393
|
-
body: pod.formantBody,
|
|
5394
|
-
brightness: pod.formantBrightness,
|
|
5395
|
-
nasal: pod.formantNasal
|
|
5396
|
-
},
|
|
5397
|
-
eq: {
|
|
5398
|
-
highpassHz: pod.eqHighpassHz,
|
|
5399
|
-
bodyDb: pod.eqBodyDb,
|
|
5400
|
-
presenceDb: pod.eqPresenceDb,
|
|
5401
|
-
airDb: pod.eqAirDb
|
|
5402
|
-
},
|
|
5403
|
-
gate: {
|
|
5404
|
-
thresholdDb: pod.gateThresholdDb,
|
|
5405
|
-
attackMs: pod.gateAttackMs,
|
|
5406
|
-
releaseMs: pod.gateReleaseMs,
|
|
5407
|
-
rangeDb: pod.gateRangeDb
|
|
5408
|
-
},
|
|
5409
|
-
compressor: {
|
|
5410
|
-
thresholdDb: pod.compressorThresholdDb,
|
|
5411
|
-
ratio: pod.compressorRatio,
|
|
5412
|
-
attackMs: pod.compressorAttackMs,
|
|
5413
|
-
releaseMs: pod.compressorReleaseMs,
|
|
5414
|
-
makeupGainDb: pod.compressorMakeupGainDb
|
|
5415
|
-
},
|
|
5416
|
-
deesser: {
|
|
5417
|
-
frequencyHz: pod.deesserFrequencyHz,
|
|
5418
|
-
thresholdDb: pod.deesserThresholdDb,
|
|
5419
|
-
ratio: pod.deesserRatio,
|
|
5420
|
-
rangeDb: pod.deesserRangeDb
|
|
5421
|
-
},
|
|
5422
|
-
reverb: {
|
|
5423
|
-
mix: pod.reverbMix,
|
|
5424
|
-
timeMs: pod.reverbTimeMs,
|
|
5425
|
-
damping: pod.reverbDamping,
|
|
5426
|
-
seed: pod.reverbSeed
|
|
5427
|
-
},
|
|
5428
|
-
limiter: {
|
|
5429
|
-
ceilingDb: pod.limiterCeilingDb,
|
|
5430
|
-
releaseMs: pod.limiterReleaseMs,
|
|
5431
|
-
enableIspLimiter: pod.limiterEnableIspLimiter,
|
|
5432
|
-
ispCeilingDbtp: pod.limiterIspCeilingDbtp
|
|
5433
|
-
}
|
|
5434
|
-
};
|
|
5435
|
-
}
|
|
5436
5915
|
var RealtimeVoiceChanger = class {
|
|
5437
|
-
|
|
5916
|
+
/**
|
|
5917
|
+
* Creates a voice changer. Supplying `sampleRate` prepares it immediately,
|
|
5918
|
+
* matching the Node and Python constructors; omitting it preserves the
|
|
5919
|
+
* explicit {@link prepare} lifecycle for callers that configure later.
|
|
5920
|
+
*/
|
|
5921
|
+
constructor(config = "neutral-monitor", sampleRate, maxBlockSize = 128, channels = 1) {
|
|
5438
5922
|
const module2 = getSonareModule();
|
|
5439
5923
|
this.changer = module2.createRealtimeVoiceChanger(config);
|
|
5924
|
+
if (sampleRate !== void 0) {
|
|
5925
|
+
this.changer.prepare(sampleRate, maxBlockSize, channels);
|
|
5926
|
+
}
|
|
5440
5927
|
}
|
|
5441
5928
|
prepare(sampleRate, maxBlockSize = 128, channels = 1) {
|
|
5442
5929
|
this.changer.prepare(sampleRate, maxBlockSize, channels);
|
|
@@ -5445,8 +5932,15 @@ var RealtimeVoiceChanger = class {
|
|
|
5445
5932
|
this.changer.reset();
|
|
5446
5933
|
}
|
|
5447
5934
|
setConfig(config) {
|
|
5448
|
-
|
|
5449
|
-
|
|
5935
|
+
this.changer.setConfig(config);
|
|
5936
|
+
}
|
|
5937
|
+
/**
|
|
5938
|
+
* Apply a flat, pre-normalized config without JSON serialization. Intended
|
|
5939
|
+
* for AudioWorklet control messages whose sender prepared the POD on the
|
|
5940
|
+
* main thread.
|
|
5941
|
+
*/
|
|
5942
|
+
setPodConfig(config) {
|
|
5943
|
+
this.changer.setPodConfig(config);
|
|
5450
5944
|
}
|
|
5451
5945
|
configJson() {
|
|
5452
5946
|
return this.changer.configJson();
|
|
@@ -5454,6 +5948,13 @@ var RealtimeVoiceChanger = class {
|
|
|
5454
5948
|
latencySamples() {
|
|
5455
5949
|
return this.changer.latencySamples();
|
|
5456
5950
|
}
|
|
5951
|
+
/**
|
|
5952
|
+
* Monotonically increases whenever {@link prepare} can replace the native
|
|
5953
|
+
* scratch buffers. Cached WASM heap views must be reacquired after it changes.
|
|
5954
|
+
*/
|
|
5955
|
+
bufferGeneration() {
|
|
5956
|
+
return this.changer.bufferGeneration();
|
|
5957
|
+
}
|
|
5457
5958
|
processMono(samples) {
|
|
5458
5959
|
return this.changer.processMono(samples);
|
|
5459
5960
|
}
|
|
@@ -5530,10 +6031,12 @@ var RealtimeVoiceChanger = class {
|
|
|
5530
6031
|
createRealtimeMonoBuffer(numSamples) {
|
|
5531
6032
|
let input = this.getMonoInputBuffer(numSamples);
|
|
5532
6033
|
let output = this.getMonoOutputBuffer(numSamples);
|
|
6034
|
+
let generation = this.bufferGeneration();
|
|
5533
6035
|
const reacquireIfDetached = () => {
|
|
5534
|
-
if (input.byteLength === 0 || output.byteLength === 0) {
|
|
6036
|
+
if (generation !== this.bufferGeneration() || input.byteLength === 0 || output.byteLength === 0) {
|
|
5535
6037
|
input = this.getMonoInputBuffer(numSamples);
|
|
5536
6038
|
output = this.getMonoOutputBuffer(numSamples);
|
|
6039
|
+
generation = this.bufferGeneration();
|
|
5537
6040
|
}
|
|
5538
6041
|
};
|
|
5539
6042
|
return {
|
|
@@ -5555,10 +6058,12 @@ var RealtimeVoiceChanger = class {
|
|
|
5555
6058
|
createRealtimeInterleavedBuffer(numFrames, numChannels) {
|
|
5556
6059
|
let input = this.getInterleavedInputBuffer(numFrames, numChannels);
|
|
5557
6060
|
let output = this.getInterleavedOutputBuffer(numFrames, numChannels);
|
|
6061
|
+
let generation = this.bufferGeneration();
|
|
5558
6062
|
const reacquireIfDetached = () => {
|
|
5559
|
-
if (input.byteLength === 0 || output.byteLength === 0) {
|
|
6063
|
+
if (generation !== this.bufferGeneration() || input.byteLength === 0 || output.byteLength === 0) {
|
|
5560
6064
|
input = this.getInterleavedInputBuffer(numFrames, numChannels);
|
|
5561
6065
|
output = this.getInterleavedOutputBuffer(numFrames, numChannels);
|
|
6066
|
+
generation = this.bufferGeneration();
|
|
5562
6067
|
}
|
|
5563
6068
|
};
|
|
5564
6069
|
return {
|
|
@@ -5585,15 +6090,17 @@ var RealtimeVoiceChanger = class {
|
|
|
5585
6090
|
*/
|
|
5586
6091
|
createRealtimePlanarBuffer(numFrames, numChannels) {
|
|
5587
6092
|
let channels = [];
|
|
6093
|
+
let generation = this.bufferGeneration();
|
|
5588
6094
|
const acquire = () => {
|
|
5589
6095
|
channels = [];
|
|
5590
6096
|
for (let ch = 0; ch < numChannels; ch++) {
|
|
5591
6097
|
channels.push(this.getPlanarChannelBuffer(ch, numFrames));
|
|
5592
6098
|
}
|
|
6099
|
+
generation = this.bufferGeneration();
|
|
5593
6100
|
};
|
|
5594
6101
|
acquire();
|
|
5595
6102
|
const reacquireIfDetached = () => {
|
|
5596
|
-
if ((channels[0]?.byteLength ?? 0) === 0) {
|
|
6103
|
+
if (generation !== this.bufferGeneration() || (channels[0]?.byteLength ?? 0) === 0) {
|
|
5597
6104
|
acquire();
|
|
5598
6105
|
}
|
|
5599
6106
|
};
|
|
@@ -5637,7 +6144,12 @@ var EQ_PHASE_MODES = {
|
|
|
5637
6144
|
var StreamingMasteringChain = class {
|
|
5638
6145
|
constructor(config) {
|
|
5639
6146
|
const module2 = getSonareModule();
|
|
5640
|
-
|
|
6147
|
+
const { loudnessStaticGainDb, loudnessStaticGainPeakDb, ...chainConfig } = config;
|
|
6148
|
+
this.chain = module2.createStreamingMasteringChain({
|
|
6149
|
+
__flatParams: flattenChainConfig(chainConfig),
|
|
6150
|
+
loudnessStaticGainDb,
|
|
6151
|
+
loudnessStaticGainPeakDb
|
|
6152
|
+
});
|
|
5641
6153
|
}
|
|
5642
6154
|
/**
|
|
5643
6155
|
* Initialize processors for the given sample rate and block layout.
|
|
@@ -5664,6 +6176,19 @@ var StreamingMasteringChain = class {
|
|
|
5664
6176
|
}
|
|
5665
6177
|
return this.chain.processStereo(left, right);
|
|
5666
6178
|
}
|
|
6179
|
+
/**
|
|
6180
|
+
* Emit delayed audio and finite processor tails after the final mono block.
|
|
6181
|
+
* Call until this returns an empty array. The initial `latencySamples()`
|
|
6182
|
+
* samples of the concatenated stream are delayed and should be discarded for
|
|
6183
|
+
* time-aligned output.
|
|
6184
|
+
*/
|
|
6185
|
+
flushMono() {
|
|
6186
|
+
return this.chain.flushMono();
|
|
6187
|
+
}
|
|
6188
|
+
/** Stereo counterpart of {@link flushMono}. */
|
|
6189
|
+
flushStereo() {
|
|
6190
|
+
return this.chain.flushStereo();
|
|
6191
|
+
}
|
|
5667
6192
|
/** Reset all processor state without rebuilding. */
|
|
5668
6193
|
reset() {
|
|
5669
6194
|
this.chain.reset();
|
|
@@ -6055,6 +6580,214 @@ function iterInputs(access) {
|
|
|
6055
6580
|
return access.inputs instanceof Map ? access.inputs.entries() : access.inputs;
|
|
6056
6581
|
}
|
|
6057
6582
|
|
|
6583
|
+
// src/worker_client.ts
|
|
6584
|
+
var OfflineWorkerTask = class {
|
|
6585
|
+
constructor(result, cancelRequest) {
|
|
6586
|
+
this.result = result;
|
|
6587
|
+
this.cancelRequest = cancelRequest;
|
|
6588
|
+
}
|
|
6589
|
+
cancel() {
|
|
6590
|
+
this.cancelRequest();
|
|
6591
|
+
}
|
|
6592
|
+
// biome-ignore lint/suspicious/noThenProperty: this intentionally implements PromiseLike so callers can await a task and cancel it.
|
|
6593
|
+
then(onfulfilled, onrejected) {
|
|
6594
|
+
return this.result.then(onfulfilled, onrejected);
|
|
6595
|
+
}
|
|
6596
|
+
catch(onrejected) {
|
|
6597
|
+
return this.result.catch(onrejected);
|
|
6598
|
+
}
|
|
6599
|
+
finally(onfinally) {
|
|
6600
|
+
return this.result.finally(onfinally);
|
|
6601
|
+
}
|
|
6602
|
+
};
|
|
6603
|
+
function cloneForWorker(value, copy, transfers, transferred = /* @__PURE__ */ new Set()) {
|
|
6604
|
+
if (value instanceof Float32Array) {
|
|
6605
|
+
const samples = copy ? value.slice() : value;
|
|
6606
|
+
const buffer = samples.buffer;
|
|
6607
|
+
if (!(buffer instanceof ArrayBuffer)) {
|
|
6608
|
+
throw new TypeError(
|
|
6609
|
+
"OfflineWorkerClient only transfers Float32Array values backed by ArrayBuffer"
|
|
6610
|
+
);
|
|
6611
|
+
}
|
|
6612
|
+
if (!transferred.has(buffer)) {
|
|
6613
|
+
transferred.add(buffer);
|
|
6614
|
+
transfers.push(buffer);
|
|
6615
|
+
}
|
|
6616
|
+
return samples;
|
|
6617
|
+
}
|
|
6618
|
+
if (Array.isArray(value)) {
|
|
6619
|
+
return value.map((item) => cloneForWorker(item, copy, transfers, transferred));
|
|
6620
|
+
}
|
|
6621
|
+
if (value !== null && typeof value === "object") {
|
|
6622
|
+
return Object.fromEntries(
|
|
6623
|
+
Object.entries(value).map(([key, item]) => [
|
|
6624
|
+
key,
|
|
6625
|
+
cloneForWorker(item, copy, transfers, transferred)
|
|
6626
|
+
])
|
|
6627
|
+
);
|
|
6628
|
+
}
|
|
6629
|
+
return value;
|
|
6630
|
+
}
|
|
6631
|
+
function cancellationFlag() {
|
|
6632
|
+
if (typeof SharedArrayBuffer === "undefined") {
|
|
6633
|
+
return void 0;
|
|
6634
|
+
}
|
|
6635
|
+
return new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
6636
|
+
}
|
|
6637
|
+
function workerError(message) {
|
|
6638
|
+
if (message.error.code !== void 0) {
|
|
6639
|
+
return new SonareError(
|
|
6640
|
+
message.error.code,
|
|
6641
|
+
message.error.codeName ?? "Unknown",
|
|
6642
|
+
message.error.message
|
|
6643
|
+
);
|
|
6644
|
+
}
|
|
6645
|
+
const error = new Error(message.error.message);
|
|
6646
|
+
error.name = message.error.name;
|
|
6647
|
+
return error;
|
|
6648
|
+
}
|
|
6649
|
+
var OfflineWorkerClient = class {
|
|
6650
|
+
constructor(options = {}) {
|
|
6651
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
6652
|
+
this.nextId = 1;
|
|
6653
|
+
this.closed = false;
|
|
6654
|
+
this.usesEventTarget = false;
|
|
6655
|
+
this.onMessage = (event) => {
|
|
6656
|
+
const message = event.data;
|
|
6657
|
+
const pending = this.pending.get(message.id);
|
|
6658
|
+
if (!pending) {
|
|
6659
|
+
return;
|
|
6660
|
+
}
|
|
6661
|
+
if (message.type === "sonare:offline-progress") {
|
|
6662
|
+
pending.onProgress?.({ progress: message.progress, stage: message.stage });
|
|
6663
|
+
return;
|
|
6664
|
+
}
|
|
6665
|
+
this.pending.delete(message.id);
|
|
6666
|
+
if (message.type === "sonare:offline-result") {
|
|
6667
|
+
pending.resolve(message.result);
|
|
6668
|
+
return;
|
|
6669
|
+
}
|
|
6670
|
+
pending.reject(workerError(message));
|
|
6671
|
+
};
|
|
6672
|
+
this.onError = (event) => {
|
|
6673
|
+
const error = new Error(event.message || "Offline Worker failed");
|
|
6674
|
+
for (const { reject } of this.pending.values()) {
|
|
6675
|
+
reject(error);
|
|
6676
|
+
}
|
|
6677
|
+
this.pending.clear();
|
|
6678
|
+
};
|
|
6679
|
+
this.onNodeMessage = (data) => {
|
|
6680
|
+
this.onMessage({ data });
|
|
6681
|
+
};
|
|
6682
|
+
this.onNodeError = (error) => {
|
|
6683
|
+
this.onError(
|
|
6684
|
+
error instanceof Error ? { message: error.message } : { message: String(error) }
|
|
6685
|
+
);
|
|
6686
|
+
};
|
|
6687
|
+
this.ownsWorker = options.worker === void 0 || options.terminateWorkerOnDispose === true;
|
|
6688
|
+
if (options.worker) {
|
|
6689
|
+
this.worker = options.worker;
|
|
6690
|
+
} else {
|
|
6691
|
+
if (!options.workerFactory && typeof Worker === "undefined") {
|
|
6692
|
+
throw new Error("OfflineWorkerClient requires a browser Worker implementation");
|
|
6693
|
+
}
|
|
6694
|
+
const url = options.workerUrl === void 0 ? new URL("./worker.js", import.meta.url) : new URL(options.workerUrl, import.meta.url);
|
|
6695
|
+
this.worker = options.workerFactory?.(url) ?? new Worker(url, { type: "module", name: "sonare-offline" });
|
|
6696
|
+
}
|
|
6697
|
+
if (this.worker.addEventListener) {
|
|
6698
|
+
this.usesEventTarget = true;
|
|
6699
|
+
this.worker.addEventListener("message", this.onMessage);
|
|
6700
|
+
this.worker.addEventListener("error", this.onError);
|
|
6701
|
+
} else if (this.worker.on) {
|
|
6702
|
+
this.worker.on("message", this.onNodeMessage);
|
|
6703
|
+
this.worker.on("error", this.onNodeError);
|
|
6704
|
+
} else {
|
|
6705
|
+
throw new TypeError("OfflineWorkerClient requires Worker event listeners");
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
/** Dispatch full music analysis to the Worker. */
|
|
6709
|
+
analyze(request, options) {
|
|
6710
|
+
return this.call("analyze", request, options);
|
|
6711
|
+
}
|
|
6712
|
+
/** Dispatch BPM detection to the Worker. */
|
|
6713
|
+
detectBpm(request, options) {
|
|
6714
|
+
return this.call("detectBpm", request, options);
|
|
6715
|
+
}
|
|
6716
|
+
/** Dispatch key detection to the Worker. */
|
|
6717
|
+
detectKey(request, options) {
|
|
6718
|
+
return this.call("detectKey", request, options);
|
|
6719
|
+
}
|
|
6720
|
+
/** Dispatch chord detection to the Worker. */
|
|
6721
|
+
detectChords(request, options) {
|
|
6722
|
+
return this.call("detectChords", request, options);
|
|
6723
|
+
}
|
|
6724
|
+
/** Dispatch mono preset mastering to the Worker. */
|
|
6725
|
+
masterAudio(request, options) {
|
|
6726
|
+
return this.call("masterAudio", request, options);
|
|
6727
|
+
}
|
|
6728
|
+
/** Dispatch stereo preset mastering to the Worker. */
|
|
6729
|
+
masterAudioStereo(request, options) {
|
|
6730
|
+
return this.call("masterAudioStereo", request, options);
|
|
6731
|
+
}
|
|
6732
|
+
/** Stop accepting calls, reject outstanding work, and release the Worker if owned. */
|
|
6733
|
+
dispose() {
|
|
6734
|
+
if (this.closed) {
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6737
|
+
this.closed = true;
|
|
6738
|
+
if (this.usesEventTarget) {
|
|
6739
|
+
this.worker.removeEventListener?.("message", this.onMessage);
|
|
6740
|
+
this.worker.removeEventListener?.("error", this.onError);
|
|
6741
|
+
} else {
|
|
6742
|
+
this.worker.off?.("message", this.onNodeMessage);
|
|
6743
|
+
this.worker.off?.("error", this.onNodeError);
|
|
6744
|
+
}
|
|
6745
|
+
for (const { reject } of this.pending.values()) {
|
|
6746
|
+
reject(new Error("OfflineWorkerClient was disposed"));
|
|
6747
|
+
}
|
|
6748
|
+
this.pending.clear();
|
|
6749
|
+
if (this.ownsWorker) {
|
|
6750
|
+
this.worker.terminate();
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
call(operation, request, options = {}) {
|
|
6754
|
+
if (this.closed) {
|
|
6755
|
+
throw new Error("OfflineWorkerClient was disposed");
|
|
6756
|
+
}
|
|
6757
|
+
const id = this.nextId++;
|
|
6758
|
+
const transfers = [];
|
|
6759
|
+
const preparedRequest = cloneForWorker(request, options.copy === true, transfers);
|
|
6760
|
+
const cancelFlag = cancellationFlag();
|
|
6761
|
+
const result = new Promise((resolve, reject) => {
|
|
6762
|
+
this.pending.set(id, {
|
|
6763
|
+
resolve: (value) => resolve(value),
|
|
6764
|
+
reject,
|
|
6765
|
+
onProgress: options.onProgress,
|
|
6766
|
+
cancelFlag
|
|
6767
|
+
});
|
|
6768
|
+
});
|
|
6769
|
+
this.worker.postMessage(
|
|
6770
|
+
{
|
|
6771
|
+
type: "sonare:offline-run",
|
|
6772
|
+
id,
|
|
6773
|
+
operation,
|
|
6774
|
+
request: preparedRequest,
|
|
6775
|
+
...cancelFlag ? { cancelBuffer: cancelFlag.buffer } : {}
|
|
6776
|
+
},
|
|
6777
|
+
transfers
|
|
6778
|
+
);
|
|
6779
|
+
return new OfflineWorkerTask(result, () => {
|
|
6780
|
+
if (!this.pending.has(id)) {
|
|
6781
|
+
return;
|
|
6782
|
+
}
|
|
6783
|
+
if (cancelFlag) {
|
|
6784
|
+
Atomics.store(cancelFlag, 0, 1);
|
|
6785
|
+
}
|
|
6786
|
+
this.worker.postMessage({ type: "sonare:offline-cancel", id });
|
|
6787
|
+
});
|
|
6788
|
+
}
|
|
6789
|
+
};
|
|
6790
|
+
|
|
6058
6791
|
// src/index.ts
|
|
6059
6792
|
var module = null;
|
|
6060
6793
|
var initPromise = null;
|
|
@@ -6086,6 +6819,18 @@ function version() {
|
|
|
6086
6819
|
}
|
|
6087
6820
|
return module.version();
|
|
6088
6821
|
}
|
|
6822
|
+
function capabilities() {
|
|
6823
|
+
if (!module) {
|
|
6824
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
6825
|
+
}
|
|
6826
|
+
return module.capabilities();
|
|
6827
|
+
}
|
|
6828
|
+
function capabilityCatalog() {
|
|
6829
|
+
if (!module) {
|
|
6830
|
+
throw new Error("Module not initialized. Call init() first.");
|
|
6831
|
+
}
|
|
6832
|
+
return JSON.parse(module.capabilityCatalog());
|
|
6833
|
+
}
|
|
6089
6834
|
function abiVersion() {
|
|
6090
6835
|
if (!module) {
|
|
6091
6836
|
throw new Error("Module not initialized. Call init() first.");
|
|
@@ -6114,6 +6859,9 @@ var VOICE_PRESET_ORDINALS = [
|
|
|
6114
6859
|
];
|
|
6115
6860
|
function resolveVoicePresetOrdinal(preset) {
|
|
6116
6861
|
if (typeof preset === "number") {
|
|
6862
|
+
if (!Number.isSafeInteger(preset) || preset < 0 || preset >= VOICE_PRESET_ORDINALS.length) {
|
|
6863
|
+
throw new RangeError(`Unknown voice-character preset ordinal: ${String(preset)}`);
|
|
6864
|
+
}
|
|
6117
6865
|
return preset;
|
|
6118
6866
|
}
|
|
6119
6867
|
const ordinal = VOICE_PRESET_ORDINALS.indexOf(preset);
|
|
@@ -6136,7 +6884,9 @@ function realtimeVoiceChangerPresetConfig(preset) {
|
|
|
6136
6884
|
}
|
|
6137
6885
|
export {
|
|
6138
6886
|
Audio,
|
|
6887
|
+
BUILTIN_SYNTH_WAVEFORMS,
|
|
6139
6888
|
ChordQuality,
|
|
6889
|
+
ClipPageProvider,
|
|
6140
6890
|
ClipPageStreamer,
|
|
6141
6891
|
EXPECTED_ENGINE_ABI_VERSION,
|
|
6142
6892
|
EXPECTED_PROJECT_ABI_VERSION,
|
|
@@ -6145,6 +6895,8 @@ export {
|
|
|
6145
6895
|
MarkerKind,
|
|
6146
6896
|
Mixer,
|
|
6147
6897
|
Mode,
|
|
6898
|
+
OfflineWorkerClient,
|
|
6899
|
+
OfflineWorkerTask,
|
|
6148
6900
|
PitchClass as Pitch,
|
|
6149
6901
|
PitchClass,
|
|
6150
6902
|
Project,
|
|
@@ -6178,10 +6930,14 @@ export {
|
|
|
6178
6930
|
bassChroma,
|
|
6179
6931
|
bindMicrophoneInput,
|
|
6180
6932
|
bindWebMidi,
|
|
6933
|
+
capabilities,
|
|
6934
|
+
capabilityCatalog,
|
|
6935
|
+
chirp,
|
|
6181
6936
|
chordFunctionalAnalysis,
|
|
6182
6937
|
chroma,
|
|
6183
6938
|
chromaCens,
|
|
6184
6939
|
chromaCqt,
|
|
6940
|
+
clicks,
|
|
6185
6941
|
cqt,
|
|
6186
6942
|
cqtToAudio,
|
|
6187
6943
|
createOpfsClipPageProvider,
|
|
@@ -6211,6 +6967,7 @@ export {
|
|
|
6211
6967
|
frameSignal,
|
|
6212
6968
|
framesToSamples,
|
|
6213
6969
|
framesToTime,
|
|
6970
|
+
griffinLim,
|
|
6214
6971
|
harmonic,
|
|
6215
6972
|
hasFfmpegSupport,
|
|
6216
6973
|
hpss,
|
|
@@ -6261,6 +7018,7 @@ export {
|
|
|
6261
7018
|
masteringStereoAnalysisNames,
|
|
6262
7019
|
masteringStereoAnalyze,
|
|
6263
7020
|
masteringStreamingPreview,
|
|
7021
|
+
melDelta,
|
|
6264
7022
|
melSpectrogram,
|
|
6265
7023
|
melToAudio,
|
|
6266
7024
|
melToHz,
|
|
@@ -6273,6 +7031,7 @@ export {
|
|
|
6273
7031
|
meteringPhaseScope,
|
|
6274
7032
|
meteringPhaseScopeDecimated,
|
|
6275
7033
|
meteringRmsDb,
|
|
7034
|
+
meteringSilenceRatio,
|
|
6276
7035
|
meteringSpectrum,
|
|
6277
7036
|
meteringSpectrumFrame,
|
|
6278
7037
|
meteringStereoCorrelation,
|
|
@@ -6292,8 +7051,10 @@ export {
|
|
|
6292
7051
|
nnlsChroma,
|
|
6293
7052
|
normalize,
|
|
6294
7053
|
noteMove,
|
|
7054
|
+
noteSegments,
|
|
6295
7055
|
noteStretch,
|
|
6296
7056
|
noteToHz,
|
|
7057
|
+
onsetBacktrack,
|
|
6297
7058
|
onsetEnvelope,
|
|
6298
7059
|
onsetStrengthMulti,
|
|
6299
7060
|
opfsClipPageWorkerSource,
|
|
@@ -6302,6 +7063,7 @@ export {
|
|
|
6302
7063
|
peakPick,
|
|
6303
7064
|
percussive,
|
|
6304
7065
|
phaseVocoder,
|
|
7066
|
+
piptrack,
|
|
6305
7067
|
pitchCorrectTimevarying,
|
|
6306
7068
|
pitchCorrectToMidi,
|
|
6307
7069
|
pitchCorrectToMidiTimevarying,
|
|
@@ -6318,6 +7080,7 @@ export {
|
|
|
6318
7080
|
realtimeVoiceChangerPresetConfig,
|
|
6319
7081
|
realtimeVoiceChangerPresetJson,
|
|
6320
7082
|
realtimeVoiceChangerPresetNames,
|
|
7083
|
+
reassignedSpectrogram,
|
|
6321
7084
|
remix,
|
|
6322
7085
|
resample,
|
|
6323
7086
|
rmsEnergy,
|
|
@@ -6326,12 +7089,20 @@ export {
|
|
|
6326
7089
|
scaleCorrectionSemitones,
|
|
6327
7090
|
scalePitchClassEnabled,
|
|
6328
7091
|
scaleQuantizeMidi,
|
|
7092
|
+
segmentAgglomerative,
|
|
7093
|
+
segmentCrossSimilarity,
|
|
7094
|
+
segmentLagToRecurrence,
|
|
7095
|
+
segmentPathEnhance,
|
|
7096
|
+
segmentRecurrenceMatrix,
|
|
7097
|
+
segmentRecurrenceToLag,
|
|
7098
|
+
segmentSubsegment,
|
|
6329
7099
|
shortTermLufs,
|
|
6330
7100
|
spectralBandwidth,
|
|
6331
7101
|
spectralCentroid,
|
|
6332
7102
|
spectralContrast,
|
|
6333
7103
|
spectralEdit,
|
|
6334
7104
|
spectralFlatness,
|
|
7105
|
+
spectralFlux,
|
|
6335
7106
|
spectralRolloff,
|
|
6336
7107
|
splitSilence,
|
|
6337
7108
|
stft,
|
|
@@ -6345,6 +7116,7 @@ export {
|
|
|
6345
7116
|
tempogramRatio,
|
|
6346
7117
|
timeStretch,
|
|
6347
7118
|
timeToFrames,
|
|
7119
|
+
tone,
|
|
6348
7120
|
tonnetz,
|
|
6349
7121
|
trim,
|
|
6350
7122
|
trimSilence,
|