@libraz/libsonare 1.5.5 → 1.7.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.
Files changed (80) hide show
  1. package/README.md +109 -20
  2. package/dist/analysis.d.ts +5809 -0
  3. package/dist/analysis.js +2574 -0
  4. package/dist/analysis.js.map +1 -0
  5. package/dist/index.d.ts +884 -55
  6. package/dist/index.js +1302 -292
  7. package/dist/index.js.map +1 -1
  8. package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
  9. package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
  10. package/dist/sonare-analysis.js +2 -0
  11. package/dist/sonare-analysis.wasm +0 -0
  12. package/dist/sonare.js +1 -1
  13. package/dist/sonare.wasm +0 -0
  14. package/dist/worker.d.ts +65 -0
  15. package/dist/worker.js +734 -0
  16. package/dist/worker.js.map +1 -0
  17. package/dist/worklet.d.ts +628 -88
  18. package/dist/worklet.js +1816 -266
  19. package/dist/worklet.js.map +1 -1
  20. package/package.json +23 -11
  21. package/src/_chain_config.ts +5 -3
  22. package/src/analysis.ts +137 -0
  23. package/src/analysis_helpers.ts +40 -29
  24. package/src/audio.ts +7 -2
  25. package/src/clip_page_streamer.ts +62 -8
  26. package/src/codes.ts +87 -64
  27. package/src/effects_mastering.ts +1 -0
  28. package/src/effects_transform.ts +179 -10
  29. package/src/errors.ts +1 -0
  30. package/src/feature_core.ts +140 -2
  31. package/src/feature_music.ts +25 -3
  32. package/src/feature_pitch.ts +70 -1
  33. package/src/feature_spectral.ts +295 -7
  34. package/src/feature_spectrogram.ts +203 -3
  35. package/src/features.ts +16 -1
  36. package/src/index.ts +120 -8
  37. package/src/mastering_chain.ts +20 -8
  38. package/src/mastering_core.ts +5 -0
  39. package/src/mastering_dynamics.ts +7 -1
  40. package/src/metering.ts +6 -1
  41. package/src/mixer.ts +3 -3
  42. package/src/opfs_clip_pages.ts +26 -1
  43. package/src/project.ts +13 -0
  44. package/src/project_class.ts +204 -23
  45. package/src/project_internal.ts +38 -34
  46. package/src/project_types.ts +114 -11
  47. package/src/public_types.ts +68 -0
  48. package/src/public_types_mastering.ts +22 -0
  49. package/src/public_types_mixing.ts +27 -2
  50. package/src/public_types_music.ts +9 -0
  51. package/src/public_types_realtime.ts +23 -5
  52. package/src/public_types_spectral.ts +33 -0
  53. package/src/quick_analysis.ts +46 -10
  54. package/src/realtime_engine.ts +167 -6
  55. package/src/realtime_voice_changer.ts +51 -77
  56. package/src/sonare-analysis.js.d.ts +8 -0
  57. package/src/sonare.js.d.ts +340 -6
  58. package/src/stream_analyzer.ts +6 -1
  59. package/src/streaming_processors.ts +15 -0
  60. package/src/web_midi.ts +46 -11
  61. package/src/worker.ts +196 -0
  62. package/src/worker_client.ts +352 -0
  63. package/src/worker_protocol.ts +59 -0
  64. package/src/worklet/engine-automation.ts +2 -1
  65. package/src/worklet/engine-capture-facade.ts +13 -4
  66. package/src/worklet/engine-clips.ts +12 -3
  67. package/src/worklet/engine-node.ts +264 -21
  68. package/src/worklet/engine-offline.ts +7 -2
  69. package/src/worklet/engine-parameter-facade.ts +55 -5
  70. package/src/worklet/engine-processor.ts +435 -62
  71. package/src/worklet/engine-register.ts +9 -1
  72. package/src/worklet/engine-strips.ts +19 -3
  73. package/src/worklet/engine-sync.ts +2 -6
  74. package/src/worklet/engine-tempo-facade.ts +8 -6
  75. package/src/worklet/engine.ts +390 -13
  76. package/src/worklet/guards.ts +121 -8
  77. package/src/worklet/messages.ts +166 -8
  78. package/src/worklet/protocol.ts +305 -17
  79. package/src/worklet/voice-changer-processor.ts +12 -15
  80. 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 || {});
@@ -226,13 +227,59 @@ function assertInterleavedSamples(fnName, samples, channels, validate) {
226
227
  function requireModule() {
227
228
  return getSonareModule();
228
229
  }
229
- function hpss(samples, sampleRate = 22050, kernelHarmonic = 31, kernelPercussive = 31) {
230
- const request = samples instanceof Float32Array ? { samples, sampleRate, kernelHarmonic, kernelPercussive } : samples;
231
- return requireModule().hpss(
230
+ function resolveEffectFftOptions(fnName, nFft, hopLength) {
231
+ const resolvedNFft = nFft === void 0 ? 2048 : nFft;
232
+ const resolvedHopLength = hopLength === void 0 ? 512 : hopLength;
233
+ if (typeof resolvedNFft !== "number" || !Number.isInteger(resolvedNFft)) {
234
+ throw new TypeError(`${fnName}: nFft must be an integer`);
235
+ }
236
+ if (resolvedNFft < 2 || resolvedNFft > 2 ** 30) {
237
+ throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
238
+ }
239
+ if ((resolvedNFft & resolvedNFft - 1) !== 0) {
240
+ throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
241
+ }
242
+ if (typeof resolvedHopLength !== "number" || !Number.isInteger(resolvedHopLength)) {
243
+ throw new TypeError(`${fnName}: hopLength must be an integer`);
244
+ }
245
+ if (resolvedHopLength <= 0 || resolvedHopLength > 2 ** 31 - 1) {
246
+ throw new RangeError(`${fnName}: hopLength must be a positive integer`);
247
+ }
248
+ return { nFft: resolvedNFft, hopLength: resolvedHopLength };
249
+ }
250
+ function resolveNormalizeMode(value) {
251
+ if (value === void 0) {
252
+ return "peak";
253
+ }
254
+ if (typeof value !== "string") {
255
+ throw new TypeError("normalize: mode must be the string 'peak' or 'rms'");
256
+ }
257
+ if (value !== "peak" && value !== "rms") {
258
+ throw new RangeError("normalize: mode must be the string 'peak' or 'rms'");
259
+ }
260
+ return value;
261
+ }
262
+ function resolveHardMask(value, fnName) {
263
+ if (value === void 0) {
264
+ return false;
265
+ }
266
+ if (typeof value !== "boolean") {
267
+ throw new TypeError(`${fnName}: hardMask must be a boolean`);
268
+ }
269
+ return value;
270
+ }
271
+ function hpss(samples, sampleRate = 22050, kernelHarmonic = 31, kernelPercussive = 31, nFft, hopLength, hardMask) {
272
+ const request = samples instanceof Float32Array ? { samples, sampleRate, kernelHarmonic, kernelPercussive, nFft, hopLength, hardMask } : samples;
273
+ const fftOptions = resolveEffectFftOptions("hpss", request.nFft, request.hopLength);
274
+ const resolvedHardMask = resolveHardMask(request.hardMask, "hpss");
275
+ return requireModule().hpssEx(
232
276
  request.samples,
233
277
  request.sampleRate ?? 22050,
234
278
  request.kernelHarmonic ?? 31,
235
- request.kernelPercussive ?? 31
279
+ request.kernelPercussive ?? 31,
280
+ fftOptions.nFft,
281
+ fftOptions.hopLength,
282
+ resolvedHardMask
236
283
  );
237
284
  }
238
285
  function harmonic(samples, sampleRate = 22050, options = {}) {
@@ -245,18 +292,58 @@ function percussive(samples, sampleRate = 22050, options = {}) {
245
292
  assertSamples("percussive", request.samples, request.validate !== false);
246
293
  return requireModule().percussive(request.samples, request.sampleRate ?? 22050);
247
294
  }
248
- function timeStretch(samples, sampleRate, rate, options = {}) {
249
- const request = samples instanceof Float32Array ? { samples, sampleRate, rate, ...options } : samples;
295
+ function timeStretch(samples, sampleRate, rate, nFftOrOptions, hopLength, options = {}) {
296
+ if (nFftOrOptions !== void 0 && nFftOrOptions !== null && typeof nFftOrOptions !== "number" && typeof nFftOrOptions !== "object") {
297
+ throw new TypeError("timeStretch: nFft must be an integer or options object");
298
+ }
299
+ if (nFftOrOptions === null) {
300
+ throw new TypeError("timeStretch: nFft must be an integer or options object");
301
+ }
302
+ const positionalOptions = typeof nFftOrOptions === "object" && nFftOrOptions !== null ? nFftOrOptions : options;
303
+ const positionalNFft = typeof nFftOrOptions === "number" ? nFftOrOptions : void 0;
304
+ const request = samples instanceof Float32Array ? {
305
+ samples,
306
+ sampleRate,
307
+ rate,
308
+ nFft: positionalNFft,
309
+ hopLength,
310
+ ...positionalOptions
311
+ } : samples;
250
312
  assertSamples("timeStretch", request.samples, request.validate !== false);
251
- return requireModule().timeStretch(request.samples, request.sampleRate ?? 22050, request.rate);
313
+ const fftOptions = resolveEffectFftOptions("timeStretch", request.nFft, request.hopLength);
314
+ return requireModule().timeStretchEx(
315
+ request.samples,
316
+ request.sampleRate ?? 22050,
317
+ request.rate,
318
+ fftOptions.nFft,
319
+ fftOptions.hopLength
320
+ );
252
321
  }
253
- function pitchShift(samples, sampleRate, semitones, options = {}) {
254
- const request = samples instanceof Float32Array ? { samples, sampleRate, semitones, ...options } : samples;
322
+ function pitchShift(samples, sampleRate, semitones, nFftOrOptions, hopLength, options = {}) {
323
+ if (nFftOrOptions !== void 0 && nFftOrOptions !== null && typeof nFftOrOptions !== "number" && typeof nFftOrOptions !== "object") {
324
+ throw new TypeError("pitchShift: nFft must be an integer or options object");
325
+ }
326
+ if (nFftOrOptions === null) {
327
+ throw new TypeError("pitchShift: nFft must be an integer or options object");
328
+ }
329
+ const positionalOptions = typeof nFftOrOptions === "object" && nFftOrOptions !== null ? nFftOrOptions : options;
330
+ const positionalNFft = typeof nFftOrOptions === "number" ? nFftOrOptions : void 0;
331
+ const request = samples instanceof Float32Array ? {
332
+ samples,
333
+ sampleRate,
334
+ semitones,
335
+ nFft: positionalNFft,
336
+ hopLength,
337
+ ...positionalOptions
338
+ } : samples;
255
339
  assertSamples("pitchShift", request.samples, request.validate !== false);
256
- return requireModule().pitchShift(
340
+ const fftOptions = resolveEffectFftOptions("pitchShift", request.nFft, request.hopLength);
341
+ return requireModule().pitchShiftEx(
257
342
  request.samples,
258
343
  request.sampleRate ?? 22050,
259
- request.semitones
344
+ request.semitones,
345
+ fftOptions.nFft,
346
+ fftOptions.hopLength
260
347
  );
261
348
  }
262
349
  function pitchCorrectToMidi(samples, sampleRate = 22050, currentMidi = 69, targetMidi = 69, options = {}) {
@@ -341,13 +428,23 @@ function noteMove(samples, sampleRate = 22050, options = {}) {
341
428
  request.targetOnsetSample ?? 0
342
429
  );
343
430
  }
344
- function normalize(samples, sampleRate, targetDb = 0, options = {}) {
345
- const request = samples instanceof Float32Array ? { samples, sampleRate, targetDb, ...options } : samples;
431
+ function normalize(samples, sampleRate, targetDb = 0, modeOrOptions = "peak", options = {}) {
432
+ if (modeOrOptions !== void 0 && modeOrOptions !== null && typeof modeOrOptions !== "string" && typeof modeOrOptions !== "object") {
433
+ throw new TypeError("normalize: mode must be the string 'peak' or 'rms'");
434
+ }
435
+ if (modeOrOptions === null) {
436
+ throw new TypeError("normalize: mode must be the string 'peak' or 'rms'");
437
+ }
438
+ const positionalOptions = typeof modeOrOptions === "object" && modeOrOptions !== null ? modeOrOptions : options;
439
+ const positionalMode = typeof modeOrOptions === "string" ? modeOrOptions : void 0;
440
+ const request = samples instanceof Float32Array ? { samples, sampleRate, targetDb, mode: positionalMode, ...positionalOptions } : samples;
346
441
  assertSamples("normalize", request.samples, request.validate !== false);
347
- return requireModule().normalize(
442
+ const mode = resolveNormalizeMode(request.mode);
443
+ return requireModule().normalizeEx(
348
444
  request.samples,
349
445
  request.sampleRate ?? 22050,
350
- request.targetDb ?? 0
446
+ request.targetDb ?? 0,
447
+ mode
351
448
  );
352
449
  }
353
450
  function spectralEdit(samples, sampleRate, ops = [], options = {}) {
@@ -405,6 +502,8 @@ function flattenChainConfig(config) {
405
502
  out[path] = value;
406
503
  } else if (value !== null && typeof value === "object") {
407
504
  walk(value, path);
505
+ } else if (value !== void 0) {
506
+ throw new TypeError(`Mastering override '${path}' must be a number or boolean.`);
408
507
  }
409
508
  }
410
509
  };
@@ -446,12 +545,14 @@ function masterAudioStereoRequest(requestOrLeft, right, sampleRate, preset, over
446
545
  }
447
546
  function masteringChain(samples, sampleRate = 22050, config = {}, onProgress) {
448
547
  const request = samples instanceof Float32Array ? { samples, sampleRate, config, onProgress } : samples;
449
- if (request.onProgress) {
548
+ if (request.onProgress || request.cancel) {
450
549
  return requireModule3().masteringChainWithProgress(
451
550
  request.samples,
452
551
  request.sampleRate ?? 22050,
453
552
  canonicalChainConfig(request.config ?? {}),
454
- request.onProgress
553
+ request.onProgress ?? (() => {
554
+ }),
555
+ request.cancel ?? (() => false)
455
556
  );
456
557
  }
457
558
  return requireModule3().masteringChain(
@@ -465,13 +566,15 @@ function masteringChainStereo(left, right, sampleRate = 22050, config = {}, onPr
465
566
  if (request.left.length !== request.right.length) {
466
567
  throw new Error("Stereo channel lengths must match.");
467
568
  }
468
- if (request.onProgress) {
569
+ if (request.onProgress || request.cancel) {
469
570
  return requireModule3().masteringChainStereoWithProgress(
470
571
  request.left,
471
572
  request.right,
472
573
  request.sampleRate ?? 22050,
473
574
  canonicalChainConfig(request.config ?? {}),
474
- request.onProgress
575
+ request.onProgress ?? (() => {
576
+ }),
577
+ request.cancel ?? (() => false)
475
578
  );
476
579
  }
477
580
  return requireModule3().masteringChainStereo(
@@ -490,7 +593,8 @@ function masteringChainWithProgress(samples, sampleRate = 22050, config = {}, on
490
593
  request.samples,
491
594
  request.sampleRate ?? 22050,
492
595
  canonicalChainConfig(request.config ?? {}),
493
- request.onProgress
596
+ request.onProgress,
597
+ request.cancel ?? (() => false)
494
598
  );
495
599
  }
496
600
  function masteringChainStereoWithProgress(left, right, sampleRate = 22050, config = {}, onProgress) {
@@ -506,7 +610,8 @@ function masteringChainStereoWithProgress(left, right, sampleRate = 22050, confi
506
610
  request.right,
507
611
  request.sampleRate ?? 22050,
508
612
  canonicalChainConfig(request.config ?? {}),
509
- request.onProgress
613
+ request.onProgress,
614
+ request.cancel ?? (() => false)
510
615
  );
511
616
  }
512
617
  function masteringPresetNames() {
@@ -515,13 +620,15 @@ function masteringPresetNames() {
515
620
  function masterAudio(samples, sampleRate = 22050, presetName = "pop", overrides = {}, onProgress) {
516
621
  const request = masterAudioRequest(samples, sampleRate, presetName, overrides, onProgress);
517
622
  const flat = flattenChainConfig(request.overrides ?? {});
518
- if (request.onProgress) {
623
+ if (request.onProgress || request.cancel) {
519
624
  return requireModule3().masterAudioWithProgress(
520
625
  request.preset ?? "pop",
521
626
  request.samples,
522
627
  request.sampleRate ?? 22050,
523
628
  flat,
524
- request.onProgress
629
+ request.onProgress ?? (() => {
630
+ }),
631
+ request.cancel ?? (() => false)
525
632
  );
526
633
  }
527
634
  return requireModule3().masterAudio(
@@ -544,14 +651,16 @@ function masterAudioStereo(left, right = void 0, sampleRate = 22050, presetName
544
651
  if (request.left.length !== request.right.length) {
545
652
  throw new Error("Stereo channel lengths must match.");
546
653
  }
547
- if (request.onProgress) {
654
+ if (request.onProgress || request.cancel) {
548
655
  return requireModule3().masterAudioStereoWithProgress(
549
656
  request.preset ?? "pop",
550
657
  request.left,
551
658
  request.right,
552
659
  request.sampleRate ?? 22050,
553
660
  flat,
554
- request.onProgress
661
+ request.onProgress ?? (() => {
662
+ }),
663
+ request.cancel ?? (() => false)
555
664
  );
556
665
  }
557
666
  return requireModule3().masterAudioStereo(
@@ -572,7 +681,8 @@ function masterAudioWithProgress(samples, sampleRate = 22050, presetName = "pop"
572
681
  request.samples,
573
682
  request.sampleRate ?? 22050,
574
683
  flattenChainConfig(request.overrides ?? {}),
575
- request.onProgress
684
+ request.onProgress,
685
+ request.cancel ?? (() => false)
576
686
  );
577
687
  }
578
688
  function masterAudioStereoWithProgress(left, right = void 0, sampleRate = 22050, presetName = "pop", overrides = null, onProgress) {
@@ -596,7 +706,8 @@ function masterAudioStereoWithProgress(left, right = void 0, sampleRate = 22050,
596
706
  request.right,
597
707
  request.sampleRate ?? 22050,
598
708
  flattenChainConfig(request.overrides ?? {}),
599
- request.onProgress
709
+ request.onProgress,
710
+ request.cancel ?? (() => false)
600
711
  );
601
712
  }
602
713
 
@@ -759,7 +870,10 @@ function masteringDynamicsCompressor(samples, sampleRate, options = {}) {
759
870
  const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
760
871
  assertSamples("masteringDynamicsCompressor", request.samples, request.validate !== false);
761
872
  const detector = typeof request.detector === "string" ? COMPRESSOR_DETECTOR_MAP[request.detector] : request.detector;
762
- const opts = { ...request };
873
+ const opts = Object.assign(
874
+ Object.create(Object.getPrototypeOf(request)),
875
+ request
876
+ );
763
877
  if (detector !== void 0) {
764
878
  opts.detector = detector;
765
879
  }
@@ -848,6 +962,36 @@ function mixStereo(leftChannels, rightChannels, sampleRate = 48e3, options = {})
848
962
  function requireModule8() {
849
963
  return getSonareModule();
850
964
  }
965
+ function tone(frequency = 440, sampleRate = 22050, duration = 1, phase = 0, amplitude = 1) {
966
+ const request = typeof frequency === "number" ? { frequency, sampleRate, duration, phase, amplitude } : frequency;
967
+ return requireModule8().tone(
968
+ request.frequency ?? 440,
969
+ request.sampleRate ?? 22050,
970
+ request.duration ?? 1,
971
+ request.phase ?? 0,
972
+ request.amplitude ?? 1
973
+ );
974
+ }
975
+ function chirp(fmin = 440, fmax = 880, sampleRate = 22050, duration = 1, linear = true) {
976
+ const request = typeof fmin === "number" ? { fmin, fmax, sampleRate, duration, linear } : fmin;
977
+ return requireModule8().chirp(
978
+ request.fmin ?? 440,
979
+ request.fmax ?? 880,
980
+ request.sampleRate ?? 22050,
981
+ request.duration ?? 1,
982
+ request.linear ?? true
983
+ );
984
+ }
985
+ function clicks(times, sampleRate = 22050, length = 0, frequency = 1e3, clickDuration = 0.1) {
986
+ const request = times instanceof Float32Array ? { times, sampleRate, length, frequency, clickDuration } : times;
987
+ return requireModule8().clicks(
988
+ request.times,
989
+ request.sampleRate ?? 22050,
990
+ request.length ?? 0,
991
+ request.frequency ?? 1e3,
992
+ request.clickDuration ?? 0.1
993
+ );
994
+ }
851
995
  function hzToMel(hz) {
852
996
  return requireModule8().hzToMel(hz);
853
997
  }
@@ -944,6 +1088,12 @@ function fixFrames(frames, xMin = 0, xMax = -1, pad = true) {
944
1088
  }
945
1089
  return requireModule8().fixFrames(frames, xMin, xMax, pad);
946
1090
  }
1091
+ function onsetBacktrack(events, energy) {
1092
+ if (!(events instanceof Int32Array)) {
1093
+ return onsetBacktrack(events.events, events.energy);
1094
+ }
1095
+ return requireModule8().onsetBacktrack(events, energy);
1096
+ }
947
1097
  function peakPick(values, preMax, postMax, preAvg, postAvg, delta, wait) {
948
1098
  if (!(values instanceof Float32Array)) {
949
1099
  const r = values;
@@ -988,12 +1138,28 @@ function tonnetz(chromagram, nChroma, nFrames) {
988
1138
  }
989
1139
  return requireModule8().tonnetz(chromagram, nChroma, nFrames);
990
1140
  }
991
- function tempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, mode = "autocorrelation") {
1141
+ function tempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, mode = "autocorrelation", center = true, norm = true) {
992
1142
  if (!(onsetEnvelope2 instanceof Float32Array)) {
993
1143
  const r = onsetEnvelope2;
994
- return tempogram(r.onsetEnvelope, r.sampleRate, r.hopLength, r.winLength, r.mode);
1144
+ return tempogram(
1145
+ r.onsetEnvelope,
1146
+ r.sampleRate,
1147
+ r.hopLength,
1148
+ r.winLength,
1149
+ r.mode,
1150
+ r.center,
1151
+ r.norm
1152
+ );
995
1153
  }
996
- return requireModule8().tempogram(onsetEnvelope2, sampleRate, hopLength, winLength, mode);
1154
+ return requireModule8().tempogram(
1155
+ onsetEnvelope2,
1156
+ sampleRate,
1157
+ hopLength,
1158
+ winLength,
1159
+ mode,
1160
+ center,
1161
+ norm
1162
+ );
997
1163
  }
998
1164
  function cyclicTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, bpmMin = 60, nBins = 60) {
999
1165
  if (!(onsetEnvelope2 instanceof Float32Array)) {
@@ -1054,12 +1220,18 @@ function nnlsChroma(samples, sampleRate = 22050, options = {}) {
1054
1220
  return nnlsChroma(samples.samples, samples.sampleRate, samples);
1055
1221
  }
1056
1222
  validateMusicSamples("nnlsChroma", samples, sampleRate, options);
1057
- return requireModule9().nnlsChroma(
1223
+ const hopLength = options.hopLength === void 0 ? 512 : options.hopLength;
1224
+ assertPositiveInteger("nnlsChroma", hopLength, "hopLength");
1225
+ if (hopLength > 2 ** 31 - 1) {
1226
+ throw new RangeError("nnlsChroma: hopLength must fit in a signed 32-bit integer");
1227
+ }
1228
+ return requireModule9().nnlsChromaEx(
1058
1229
  samples,
1059
1230
  sampleRate,
1060
1231
  options.enableStftBlend ?? true,
1061
1232
  options.stftBlendWeight ?? 0.55,
1062
- options.stftBlendNFft ?? 4096
1233
+ options.stftBlendNFft ?? 4096,
1234
+ hopLength
1063
1235
  );
1064
1236
  }
1065
1237
  function cqt(samples, sampleRate = 22050, hopLength = 512, fmin = 32.70319566257483, nBins = 84, binsPerOctave = 12, options = {}) {
@@ -1327,7 +1499,7 @@ function onsetStrengthMulti(samples, sampleRate = 22050, nFft = 2048, hopLength
1327
1499
  validatePositiveIntegers("onsetStrengthMulti", { nFft, hopLength, nMels, nBands });
1328
1500
  return requireModule9().onsetStrengthMulti(samples, sampleRate, nFft, hopLength, nMels, nBands);
1329
1501
  }
1330
- function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, options = {}) {
1502
+ function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, winLength = 384, center = true, norm = true, options = {}) {
1331
1503
  if (!(onsetEnvelope2 instanceof Float32Array)) {
1332
1504
  const request = onsetEnvelope2;
1333
1505
  return fourierTempogram(
@@ -1335,13 +1507,22 @@ function fourierTempogram(onsetEnvelope2, sampleRate = 22050, hopLength = 512, w
1335
1507
  request.sampleRate,
1336
1508
  request.hopLength,
1337
1509
  request.winLength,
1510
+ request.center,
1511
+ request.norm,
1338
1512
  request
1339
1513
  );
1340
1514
  }
1341
1515
  assertSampleRate("fourierTempogram", sampleRate);
1342
1516
  assertSamples("fourierTempogram", onsetEnvelope2, options.validate !== false, "onsetEnvelope");
1343
1517
  validatePositiveIntegers("fourierTempogram", { hopLength, winLength });
1344
- return requireModule9().fourierTempogram(onsetEnvelope2, sampleRate, hopLength, winLength);
1518
+ return requireModule9().fourierTempogram(
1519
+ onsetEnvelope2,
1520
+ sampleRate,
1521
+ hopLength,
1522
+ winLength,
1523
+ center,
1524
+ norm
1525
+ );
1345
1526
  }
1346
1527
  function tempogramRatio(tempogramData, winLength = 384, sampleRate = 22050, hopLength = 512, factors, options = {}) {
1347
1528
  if (!(tempogramData instanceof Float32Array)) {
@@ -1392,6 +1573,21 @@ function shortTermLufs(samples, sampleRate = 22050, options = {}) {
1392
1573
  function requireModule10() {
1393
1574
  return getSonareModule();
1394
1575
  }
1576
+ function piptrack(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, fmin = 150, fmax = 4e3, threshold = 0.1) {
1577
+ if (!(samples instanceof Float32Array)) {
1578
+ const request = samples;
1579
+ return piptrack(
1580
+ request.samples,
1581
+ request.sampleRate,
1582
+ request.nFft,
1583
+ request.hopLength,
1584
+ request.fmin,
1585
+ request.fmax,
1586
+ request.threshold
1587
+ );
1588
+ }
1589
+ return requireModule10().piptrack(samples, sampleRate, nFft, hopLength, fmin, fmax, threshold);
1590
+ }
1395
1591
  function pitchYin(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512, fmin = 65, fmax = 2093, threshold = 0.1, fillNa = false) {
1396
1592
  if (!(samples instanceof Float32Array)) {
1397
1593
  const request = samples;
@@ -1442,6 +1638,13 @@ function pitchPyin(samples, sampleRate = 22050, frameLength = 2048, hopLength =
1442
1638
  fillNa
1443
1639
  );
1444
1640
  }
1641
+ function noteSegments(request) {
1642
+ return requireModule10().noteSegments(request.f0Hz, request.voicedProb, request.frameRate, {
1643
+ segmentationThresholdCents: request.segmentationThresholdCents,
1644
+ minNoteMs: request.minNoteMs,
1645
+ referenceHz: request.referenceHz
1646
+ });
1647
+ }
1445
1648
 
1446
1649
  // src/feature_resample.ts
1447
1650
  function requireModule11() {
@@ -1458,6 +1661,44 @@ function resample(samples, srcSr, targetSr) {
1458
1661
  function requireModule12() {
1459
1662
  return getSonareModule();
1460
1663
  }
1664
+ function resolveEffectFftOptions2(fnName, nFft, hopLength) {
1665
+ const resolvedNFft = nFft === void 0 ? 2048 : nFft;
1666
+ const resolvedHopLength = hopLength === void 0 ? 512 : hopLength;
1667
+ if (typeof resolvedNFft !== "number" || !Number.isInteger(resolvedNFft)) {
1668
+ throw new TypeError(`${fnName}: nFft must be an integer`);
1669
+ }
1670
+ if (resolvedNFft < 2 || resolvedNFft > 2 ** 30) {
1671
+ throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
1672
+ }
1673
+ if ((resolvedNFft & resolvedNFft - 1) !== 0) {
1674
+ throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
1675
+ }
1676
+ if (typeof resolvedHopLength !== "number" || !Number.isInteger(resolvedHopLength)) {
1677
+ throw new TypeError(`${fnName}: hopLength must be an integer`);
1678
+ }
1679
+ if (resolvedHopLength <= 0 || resolvedHopLength > 2 ** 31 - 1) {
1680
+ throw new RangeError(`${fnName}: hopLength must be a positive integer`);
1681
+ }
1682
+ return { nFft: resolvedNFft, hopLength: resolvedHopLength };
1683
+ }
1684
+ function resolveHardMask2(fnName, value) {
1685
+ if (value === void 0) {
1686
+ return false;
1687
+ }
1688
+ if (typeof value !== "boolean") {
1689
+ throw new TypeError(`${fnName}: hardMask must be a boolean`);
1690
+ }
1691
+ return value;
1692
+ }
1693
+ function validateSegmentMatrix(fnName, data, rows, cols, dataName) {
1694
+ assertPositiveInteger(fnName, rows, "rows");
1695
+ assertPositiveInteger(fnName, cols, "cols");
1696
+ assertSamples(fnName, data, true, dataName);
1697
+ const expected = rows * cols;
1698
+ if (!Number.isSafeInteger(expected) || data.length !== expected) {
1699
+ throw new RangeError(`${fnName}: ${dataName} length must equal rows * cols`);
1700
+ }
1701
+ }
1461
1702
  function spectralCentroid(samples, sampleRate = 22050, nFft = 2048, hopLength = 512) {
1462
1703
  if (!(samples instanceof Float32Array)) {
1463
1704
  return spectralCentroid(samples.samples, samples.sampleRate, samples.nFft, samples.hopLength);
@@ -1580,12 +1821,30 @@ function phaseVocoder(samples, sampleRate = 22050, rate = 1, nFft = 2048, hopLen
1580
1821
  }
1581
1822
  return requireModule12().phaseVocoder(samples, sampleRate, rate, nFft, hopLength);
1582
1823
  }
1583
- function hpssWithResidual(samples, sampleRate = 22050, kernelHarmonic = 31, kernelPercussive = 31) {
1824
+ function hpssWithResidual(samples, sampleRate = 22050, kernelHarmonic = 31, kernelPercussive = 31, nFft, hopLength, hardMask) {
1584
1825
  if (!(samples instanceof Float32Array)) {
1585
1826
  const r = samples;
1586
- return hpssWithResidual(r.samples, r.sampleRate, r.kernelHarmonic, r.kernelPercussive);
1827
+ return hpssWithResidual(
1828
+ r.samples,
1829
+ r.sampleRate,
1830
+ r.kernelHarmonic,
1831
+ r.kernelPercussive,
1832
+ r.nFft,
1833
+ r.hopLength,
1834
+ r.hardMask
1835
+ );
1587
1836
  }
1588
- return requireModule12().hpssWithResidual(samples, sampleRate, kernelHarmonic, kernelPercussive);
1837
+ const fftOptions = resolveEffectFftOptions2("hpssWithResidual", nFft, hopLength);
1838
+ const resolvedHardMask = resolveHardMask2("hpssWithResidual", hardMask);
1839
+ return requireModule12().hpssWithResidualEx(
1840
+ samples,
1841
+ sampleRate,
1842
+ kernelHarmonic,
1843
+ kernelPercussive,
1844
+ fftOptions.nFft,
1845
+ fftOptions.hopLength,
1846
+ resolvedHardMask
1847
+ );
1589
1848
  }
1590
1849
  function lufsInterleaved(samples, channels = 0, sampleRate = 22050, options = {}) {
1591
1850
  if (!(samples instanceof Float32Array)) {
@@ -1602,11 +1861,17 @@ function ebur128LoudnessRange(samples, sampleRate = 22050) {
1602
1861
  }
1603
1862
  return requireModule12().ebur128LoudnessRange(samples, sampleRate);
1604
1863
  }
1605
- function spectralBandwidth(samples, sampleRate = 22050, nFft = 2048, hopLength = 512) {
1864
+ function spectralBandwidth(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, p = 2) {
1606
1865
  if (!(samples instanceof Float32Array)) {
1607
- return spectralBandwidth(samples.samples, samples.sampleRate, samples.nFft, samples.hopLength);
1866
+ return spectralBandwidth(
1867
+ samples.samples,
1868
+ samples.sampleRate,
1869
+ samples.nFft,
1870
+ samples.hopLength,
1871
+ samples.p
1872
+ );
1608
1873
  }
1609
- return requireModule12().spectralBandwidth(samples, sampleRate, nFft, hopLength);
1874
+ return requireModule12().spectralBandwidth(samples, sampleRate, nFft, hopLength, p);
1610
1875
  }
1611
1876
  function spectralRolloff(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, rollPercent = 0.85) {
1612
1877
  if (!(samples instanceof Float32Array)) {
@@ -1626,6 +1891,18 @@ function spectralFlatness(samples, sampleRate = 22050, nFft = 2048, hopLength =
1626
1891
  }
1627
1892
  return requireModule12().spectralFlatness(samples, sampleRate, nFft, hopLength);
1628
1893
  }
1894
+ function spectralFlux(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, lag = 1) {
1895
+ if (!(samples instanceof Float32Array)) {
1896
+ return spectralFlux(
1897
+ samples.samples,
1898
+ samples.sampleRate,
1899
+ samples.nFft,
1900
+ samples.hopLength,
1901
+ samples.lag
1902
+ );
1903
+ }
1904
+ return requireModule12().spectralFlux(samples, sampleRate, nFft, hopLength, lag);
1905
+ }
1629
1906
  function zeroCrossingRate(samples, sampleRate = 22050, frameLength = 2048, hopLength = 512) {
1630
1907
  if (!(samples instanceof Float32Array)) {
1631
1908
  return zeroCrossingRate(
@@ -1643,6 +1920,107 @@ function rmsEnergy(samples, sampleRate = 22050, frameLength = 2048, hopLength =
1643
1920
  }
1644
1921
  return requireModule12().rmsEnergy(samples, sampleRate, frameLength, hopLength);
1645
1922
  }
1923
+ function segmentCrossSimilarity(request) {
1924
+ validateSegmentMatrix("segmentCrossSimilarity", request.x, request.xRows, request.xCols, "x");
1925
+ validateSegmentMatrix("segmentCrossSimilarity", request.y, request.yRows, request.yCols, "y");
1926
+ if (request.xRows !== request.yRows) {
1927
+ throw new RangeError("segmentCrossSimilarity: feature dimensions must match");
1928
+ }
1929
+ assertNonNegativeInteger("segmentCrossSimilarity", request.k ?? 0, "k");
1930
+ return requireModule12().segmentCrossSimilarity(
1931
+ request.x,
1932
+ request.xRows,
1933
+ request.xCols,
1934
+ request.y,
1935
+ request.yRows,
1936
+ request.yCols,
1937
+ request.k ?? 0,
1938
+ request.metric ?? "cosine",
1939
+ request.mode ?? "connectivity"
1940
+ );
1941
+ }
1942
+ function segmentRecurrenceMatrix(request) {
1943
+ validateSegmentMatrix(
1944
+ "segmentRecurrenceMatrix",
1945
+ request.data,
1946
+ request.rows,
1947
+ request.cols,
1948
+ "data"
1949
+ );
1950
+ assertNonNegativeInteger("segmentRecurrenceMatrix", request.k ?? 0, "k");
1951
+ assertNonNegativeInteger("segmentRecurrenceMatrix", request.width ?? 1, "width");
1952
+ return requireModule12().segmentRecurrenceMatrix(
1953
+ request.data,
1954
+ request.rows,
1955
+ request.cols,
1956
+ request.k ?? 0,
1957
+ request.width ?? 1,
1958
+ request.sym ?? false,
1959
+ request.metric ?? "euclidean",
1960
+ request.mode ?? "connectivity"
1961
+ );
1962
+ }
1963
+ function segmentRecurrenceToLag(request) {
1964
+ validateSegmentMatrix(
1965
+ "segmentRecurrenceToLag",
1966
+ request.recurrence,
1967
+ request.n,
1968
+ request.n,
1969
+ "recurrence"
1970
+ );
1971
+ return requireModule12().segmentRecurrenceToLag(
1972
+ request.recurrence,
1973
+ request.n,
1974
+ request.pad ?? false
1975
+ );
1976
+ }
1977
+ function segmentLagToRecurrence(request) {
1978
+ validateSegmentMatrix("segmentLagToRecurrence", request.lag, request.rows, request.lags, "lag");
1979
+ return requireModule12().segmentLagToRecurrence(request.lag, request.rows, request.lags);
1980
+ }
1981
+ function segmentSubsegment(request) {
1982
+ validateSegmentMatrix("segmentSubsegment", request.data, request.rows, request.cols, "data");
1983
+ assertPositiveInteger("segmentSubsegment", request.nSegments ?? 4, "nSegments");
1984
+ return requireModule12().segmentSubsegment(
1985
+ request.data,
1986
+ request.rows,
1987
+ request.cols,
1988
+ request.boundaries,
1989
+ request.nSegments ?? 4
1990
+ );
1991
+ }
1992
+ function segmentAgglomerative(request) {
1993
+ validateSegmentMatrix("segmentAgglomerative", request.data, request.rows, request.cols, "data");
1994
+ assertPositiveInteger("segmentAgglomerative", request.k, "k");
1995
+ return requireModule12().segmentAgglomerative(
1996
+ request.data,
1997
+ request.rows,
1998
+ request.cols,
1999
+ request.k,
2000
+ request.linkage ?? "average"
2001
+ );
2002
+ }
2003
+ function segmentPathEnhance(request) {
2004
+ validateSegmentMatrix(
2005
+ "segmentPathEnhance",
2006
+ request.recurrence,
2007
+ request.n,
2008
+ request.n,
2009
+ "recurrence"
2010
+ );
2011
+ assertPositiveInteger("segmentPathEnhance", request.win, "win");
2012
+ assertPositiveInteger("segmentPathEnhance", request.maxRatio ?? 2, "maxRatio");
2013
+ assertNonNegativeInteger("segmentPathEnhance", request.minRatio ?? 0, "minRatio");
2014
+ assertPositiveInteger("segmentPathEnhance", request.nFilters ?? 7, "nFilters");
2015
+ return requireModule12().segmentPathEnhance(
2016
+ request.recurrence,
2017
+ request.n,
2018
+ request.win,
2019
+ request.maxRatio ?? 2,
2020
+ request.minRatio ?? 0,
2021
+ request.nFilters ?? 7
2022
+ );
2023
+ }
1646
2024
 
1647
2025
  // src/feature_spectrogram.ts
1648
2026
  function requireModule13() {
@@ -1679,14 +2057,35 @@ function validateMatrix(fnName, data, rows, frames, dataName, rowName, options =
1679
2057
  throw new RangeError(`${fnName}: ${dataName} length must equal ${rowName} * nFrames`);
1680
2058
  }
1681
2059
  }
1682
- function trim(samples, sampleRate = 22050, thresholdDb = -60, options = {}) {
2060
+ function trim(samples, sampleRate = 22050, thresholdDb = -60, frameLengthOrOptions, hopLength, options = {}) {
1683
2061
  if (!(samples instanceof Float32Array)) {
1684
2062
  const r = samples;
1685
- return trim(r.samples, r.sampleRate, r.thresholdDb, r);
2063
+ return trim(r.samples, r.sampleRate, r.thresholdDb, r.frameLength, r.hopLength, r);
2064
+ }
2065
+ if (frameLengthOrOptions === null) {
2066
+ throw new TypeError("trim: frameLength must be an integer or options object");
2067
+ }
2068
+ if (frameLengthOrOptions !== void 0 && typeof frameLengthOrOptions !== "number" && typeof frameLengthOrOptions !== "object") {
2069
+ throw new TypeError("trim: frameLength must be an integer or options object");
1686
2070
  }
1687
- validateSpectrogramSamples("trim", samples, sampleRate, options);
2071
+ const positionalOptions = typeof frameLengthOrOptions === "object" && frameLengthOrOptions !== null ? frameLengthOrOptions : options;
2072
+ const positionalFrameLength = typeof frameLengthOrOptions === "number" ? frameLengthOrOptions : void 0;
2073
+ const resolvedFrameLength = positionalFrameLength ?? 2048;
2074
+ const resolvedHopLength = hopLength === void 0 ? 512 : hopLength;
2075
+ validateSpectrogramSamples("trim", samples, sampleRate, positionalOptions);
1688
2076
  assertFiniteScalar("trim", thresholdDb, "thresholdDb");
1689
- return requireModule13().trim(samples, sampleRate, thresholdDb);
2077
+ assertPositiveInteger("trim", resolvedFrameLength, "frameLength");
2078
+ assertPositiveInteger("trim", resolvedHopLength, "hopLength");
2079
+ if (resolvedFrameLength > 2 ** 31 - 1 || resolvedHopLength > 2 ** 31 - 1) {
2080
+ throw new RangeError("trim: frameLength and hopLength must fit in a signed 32-bit integer");
2081
+ }
2082
+ return requireModule13().trimEx(
2083
+ samples,
2084
+ sampleRate,
2085
+ thresholdDb,
2086
+ resolvedFrameLength,
2087
+ resolvedHopLength
2088
+ );
1690
2089
  }
1691
2090
  function stft(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, options = {}) {
1692
2091
  if (!(samples instanceof Float32Array)) {
@@ -1821,6 +2220,53 @@ function mfcc(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, nMels =
1821
2220
  lifter
1822
2221
  );
1823
2222
  }
2223
+ function melDelta(features, nFeatures, nFrames, width = 9) {
2224
+ const request = features instanceof Float32Array ? { features, nFeatures: nFeatures ?? 0, nFrames: nFrames ?? 0, width } : features;
2225
+ assertPositiveInteger("melDelta", request.nFeatures, "nFeatures");
2226
+ assertPositiveInteger("melDelta", request.nFrames, "nFrames");
2227
+ assertPositiveInteger("melDelta", request.width ?? 9, "width");
2228
+ if ((request.width ?? 9) < 3 || (request.width ?? 9) % 2 === 0) {
2229
+ throw new RangeError("melDelta: width must be an odd integer of at least 3");
2230
+ }
2231
+ if (request.features.length !== request.nFeatures * request.nFrames) {
2232
+ throw new RangeError("melDelta: feature matrix length must equal nFeatures * nFrames");
2233
+ }
2234
+ return requireModule13().melDelta(
2235
+ request.features,
2236
+ request.nFeatures,
2237
+ request.nFrames,
2238
+ request.width ?? 9
2239
+ );
2240
+ }
2241
+ function reassignedSpectrogram(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, refPower = 1e-6, fillNan = false) {
2242
+ if (!(samples instanceof Float32Array)) {
2243
+ const request = samples;
2244
+ return reassignedSpectrogram(
2245
+ request.samples,
2246
+ request.sampleRate,
2247
+ request.nFft,
2248
+ request.hopLength,
2249
+ request.refPower,
2250
+ request.fillNan
2251
+ );
2252
+ }
2253
+ assertSamples("reassignedSpectrogram", samples, true);
2254
+ assertSampleRate("reassignedSpectrogram", sampleRate);
2255
+ assertPositiveInteger("reassignedSpectrogram", nFft, "nFft");
2256
+ assertPositiveInteger("reassignedSpectrogram", hopLength, "hopLength");
2257
+ assertFiniteScalar("reassignedSpectrogram", refPower, "refPower");
2258
+ if (refPower < 0) {
2259
+ throw new RangeError("reassignedSpectrogram: refPower must be non-negative");
2260
+ }
2261
+ return requireModule13().reassignedSpectrogram(
2262
+ samples,
2263
+ sampleRate,
2264
+ nFft,
2265
+ hopLength,
2266
+ refPower,
2267
+ fillNan
2268
+ );
2269
+ }
1824
2270
  function melToStft(melPower, nMels = 0, nFrames = 0, sampleRate = 22050, nFft = 2048, fmin = 0, fmax = 0, htk = false, options = {}) {
1825
2271
  if (!(melPower instanceof Float32Array)) {
1826
2272
  const request = melPower;
@@ -1876,6 +2322,35 @@ function melToAudio(melPower, nMels = 0, nFrames = 0, sampleRate = 22050, nFft =
1876
2322
  htk
1877
2323
  );
1878
2324
  }
2325
+ function griffinLim(magnitude, nBins = 0, nFrames = 0, sampleRate = 22050, nFft = 2048, hopLength = 512, nIter = 32, momentum = 0.99, options = {}) {
2326
+ if (!(magnitude instanceof Float32Array)) {
2327
+ const request = magnitude;
2328
+ return griffinLim(
2329
+ request.magnitude,
2330
+ request.nBins,
2331
+ request.nFrames,
2332
+ request.sampleRate,
2333
+ request.nFft,
2334
+ request.hopLength,
2335
+ request.nIter,
2336
+ request.momentum,
2337
+ request
2338
+ );
2339
+ }
2340
+ assertSampleRate("griffinLim", sampleRate);
2341
+ validateMatrix("griffinLim", magnitude, nBins, nFrames, "magnitude", "nBins", options);
2342
+ validatePositiveIntegers2("griffinLim", { nFft, hopLength, nIter });
2343
+ return requireModule13().griffinLim(
2344
+ magnitude,
2345
+ nBins,
2346
+ nFrames,
2347
+ sampleRate,
2348
+ nFft,
2349
+ hopLength,
2350
+ nIter,
2351
+ momentum
2352
+ );
2353
+ }
1879
2354
  function mfccToMel(mfccCoefficients, nMfcc = 0, nFrames = 0, nMels = 128, lifter = 0, options = {}) {
1880
2355
  if (!(mfccCoefficients instanceof Float32Array)) {
1881
2356
  const request = mfccCoefficients;
@@ -1956,6 +2431,88 @@ function chroma(samples, sampleRate = 22050, nFft = 2048, hopLength = 512, optio
1956
2431
  return requireModule13().chroma(samples, sampleRate, nFft, hopLength);
1957
2432
  }
1958
2433
 
2434
+ // src/codes.ts
2435
+ function resolveOrdinalInRange(value, min, max, enumName) {
2436
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) {
2437
+ throw new RangeError(`Invalid ${enumName}: ${String(value)}`);
2438
+ }
2439
+ return value;
2440
+ }
2441
+ function resolveEnumOrdinal(value, values, enumName) {
2442
+ if (typeof value === "number") {
2443
+ const ordinals = Object.values(values);
2444
+ const ordinal = resolveOrdinalInRange(
2445
+ value,
2446
+ Math.min(...ordinals),
2447
+ Math.max(...ordinals),
2448
+ enumName
2449
+ );
2450
+ if (!ordinals.includes(ordinal)) {
2451
+ throw new RangeError(`Invalid ${enumName}: ${String(value)}`);
2452
+ }
2453
+ return ordinal;
2454
+ }
2455
+ if (typeof value === "string") {
2456
+ const ordinal = values[value];
2457
+ if (ordinal !== void 0) {
2458
+ return ordinal;
2459
+ }
2460
+ }
2461
+ throw new RangeError(`Invalid ${enumName}: ${String(value)}`);
2462
+ }
2463
+ var AUTOMATION_CURVE_VALUES = {
2464
+ linear: 0,
2465
+ exponential: 1,
2466
+ hold: 2,
2467
+ "s-curve": 3
2468
+ };
2469
+ var PAN_LAW_VALUES = {
2470
+ const3db: 0,
2471
+ "const-3db": 0,
2472
+ "-3db": 0,
2473
+ "const4.5db": 1,
2474
+ "const-4.5db": 1,
2475
+ "-4.5db": 1,
2476
+ const6db: 2,
2477
+ "const-6db": 2,
2478
+ "-6db": 2,
2479
+ linear0db: 3,
2480
+ "linear-0db": 3,
2481
+ linear: 3,
2482
+ "0db": 3
2483
+ };
2484
+ var PAN_MODE_VALUES = {
2485
+ balance: 0,
2486
+ pan: 1,
2487
+ stereopan: 1,
2488
+ "stereo-pan": 1,
2489
+ dualpan: 2,
2490
+ "dual-pan": 2
2491
+ };
2492
+ var METER_TAP_VALUES = { preFader: 0, postFader: 1 };
2493
+ var SEND_TIMING_VALUES = { postFader: 0, preFader: 1 };
2494
+ var TRACK_MONITOR_MODE_VALUES = { off: 0, pfl: 1, afl: 2 };
2495
+ function automationCurveCode(curve) {
2496
+ return resolveEnumOrdinal(curve, AUTOMATION_CURVE_VALUES, "automation curve");
2497
+ }
2498
+ function panLawCode(panLaw) {
2499
+ const normalized = typeof panLaw === "string" ? panLaw.toLowerCase().replace(/_/g, "-") : panLaw;
2500
+ return resolveEnumOrdinal(normalized, PAN_LAW_VALUES, "pan law");
2501
+ }
2502
+ function panModeCode(panMode) {
2503
+ const normalized = typeof panMode === "string" ? panMode.replace(/_/g, "-").toLowerCase() : panMode;
2504
+ return resolveEnumOrdinal(normalized, PAN_MODE_VALUES, "pan mode");
2505
+ }
2506
+ function meterTapCode(tap) {
2507
+ return resolveEnumOrdinal(tap, METER_TAP_VALUES, "meter tap");
2508
+ }
2509
+ function sendTimingCode(timing) {
2510
+ return resolveEnumOrdinal(timing, SEND_TIMING_VALUES, "send timing");
2511
+ }
2512
+ function trackMonitorModeCode(mode) {
2513
+ return resolveEnumOrdinal(mode, TRACK_MONITOR_MODE_VALUES, "track monitor mode");
2514
+ }
2515
+
1959
2516
  // src/public_types_music.ts
1960
2517
  var PitchClass = {
1961
2518
  C: 0,
@@ -2049,6 +2606,30 @@ function convertKeyCandidate(wasm) {
2049
2606
  correlation: wasm.correlation
2050
2607
  };
2051
2608
  }
2609
+ var KEY_MODE_VALUES = {
2610
+ major: Mode.Major,
2611
+ minor: Mode.Minor,
2612
+ dorian: Mode.Dorian,
2613
+ phrygian: Mode.Phrygian,
2614
+ lydian: Mode.Lydian,
2615
+ mixolydian: Mode.Mixolydian,
2616
+ locrian: Mode.Locrian
2617
+ };
2618
+ var KEY_PROFILE_VALUES = {
2619
+ ks: KeyProfile.KrumhanslSchmuckler,
2620
+ krumhansl: KeyProfile.KrumhanslSchmuckler,
2621
+ temperley: KeyProfile.Temperley,
2622
+ shaath: KeyProfile.Shaath,
2623
+ keyfinder: KeyProfile.Shaath,
2624
+ "faraldo-edmt": KeyProfile.FaraldoEDMT,
2625
+ edmt: KeyProfile.FaraldoEDMT,
2626
+ "faraldo-edma": KeyProfile.FaraldoEDMA,
2627
+ edma: KeyProfile.FaraldoEDMA,
2628
+ "faraldo-edmm": KeyProfile.FaraldoEDMM,
2629
+ edmm: KeyProfile.FaraldoEDMM,
2630
+ "bellman-budge": KeyProfile.BellmanBudge,
2631
+ bellman: KeyProfile.BellmanBudge
2632
+ };
2052
2633
  function keyModeValues(modes) {
2053
2634
  if (!modes) {
2054
2635
  return [];
@@ -2067,40 +2648,13 @@ function keyModeValues(modes) {
2067
2648
  Mode.Locrian
2068
2649
  ];
2069
2650
  }
2070
- const names = {
2071
- major: Mode.Major,
2072
- minor: Mode.Minor,
2073
- dorian: Mode.Dorian,
2074
- phrygian: Mode.Phrygian,
2075
- lydian: Mode.Lydian,
2076
- mixolydian: Mode.Mixolydian,
2077
- locrian: Mode.Locrian
2078
- };
2079
- return modes.map((mode) => typeof mode === "number" ? mode : names[mode]);
2651
+ return modes.map((mode) => resolveEnumOrdinal(mode, KEY_MODE_VALUES, "key mode"));
2080
2652
  }
2081
2653
  function keyProfileValue(profile) {
2082
2654
  if (profile === void 0) {
2083
2655
  return -1;
2084
2656
  }
2085
- if (typeof profile === "number") {
2086
- return profile;
2087
- }
2088
- const names = {
2089
- ks: KeyProfile.KrumhanslSchmuckler,
2090
- krumhansl: KeyProfile.KrumhanslSchmuckler,
2091
- temperley: KeyProfile.Temperley,
2092
- shaath: KeyProfile.Shaath,
2093
- keyfinder: KeyProfile.Shaath,
2094
- "faraldo-edmt": KeyProfile.FaraldoEDMT,
2095
- edmt: KeyProfile.FaraldoEDMT,
2096
- "faraldo-edma": KeyProfile.FaraldoEDMA,
2097
- edma: KeyProfile.FaraldoEDMA,
2098
- "faraldo-edmm": KeyProfile.FaraldoEDMM,
2099
- edmm: KeyProfile.FaraldoEDMM,
2100
- "bellman-budge": KeyProfile.BellmanBudge,
2101
- bellman: KeyProfile.BellmanBudge
2102
- };
2103
- return names[profile];
2657
+ return resolveEnumOrdinal(profile, KEY_PROFILE_VALUES, "key profile");
2104
2658
  }
2105
2659
  function convertChordAnalysisResult(wasm) {
2106
2660
  return {
@@ -2134,6 +2688,11 @@ function convertAnalysisResult(wasm) {
2134
2688
  return {
2135
2689
  bpm: wasm.bpm,
2136
2690
  bpmConfidence: wasm.bpmConfidence,
2691
+ bpmCandidates: wasm.bpmCandidates.map((candidate) => ({
2692
+ value: candidate.value,
2693
+ confidence: candidate.confidence,
2694
+ relation: candidate.relation
2695
+ })),
2137
2696
  key: {
2138
2697
  root: wasm.key.root,
2139
2698
  mode: wasm.key.mode,
@@ -2142,6 +2701,7 @@ function convertAnalysisResult(wasm) {
2142
2701
  shortName: wasm.key.shortName
2143
2702
  },
2144
2703
  timeSignature: wasm.timeSignature,
2704
+ timeSignatureCandidates: wasm.timeSignatureCandidates,
2145
2705
  beatTimes,
2146
2706
  beats: wasm.beats,
2147
2707
  chords: wasm.chords.map((c) => ({
@@ -2232,7 +2792,7 @@ function detectKeyCandidates(samples, sampleRate = 22050, options = {}) {
2232
2792
  function detectOnsets(samples, sampleRate = 22050, options = {}) {
2233
2793
  const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
2234
2794
  validateAnalysisInput("detectOnsets", request.samples, request.sampleRate ?? 22050, request);
2235
- return requireModule14().detectOnsets(request.samples, request.sampleRate ?? 22050);
2795
+ return requireModule14().detectOnsets(request.samples, request.sampleRate ?? 22050, request);
2236
2796
  }
2237
2797
  function detectBeats(samples, sampleRate = 22050, options = {}) {
2238
2798
  const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
@@ -2300,18 +2860,27 @@ function analyze(samples, sampleRate = 22050, options = {}) {
2300
2860
  const result = requireModule14().analyze(request.samples, request.sampleRate ?? 22050, request);
2301
2861
  return convertAnalysisResult(result);
2302
2862
  }
2303
- function analyzeImpulseResponse(samples, sampleRate = 48e3, nOctaveBands = 6) {
2304
- const request = samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands } : samples;
2863
+ function analyzeImpulseResponse(samples, sampleRate = 48e3, nOctaveBands = 6, minDecayDb) {
2864
+ const request = samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands, minDecayDb } : samples;
2865
+ if (request.minDecayDb === null) {
2866
+ throw new TypeError("analyzeImpulseResponse: minDecayDb must be a finite number");
2867
+ }
2868
+ const resolvedMinDecayDb = request.minDecayDb === void 0 ? 30 : request.minDecayDb;
2869
+ assertFiniteScalar("analyzeImpulseResponse", resolvedMinDecayDb, "minDecayDb");
2870
+ if (resolvedMinDecayDb <= 0) {
2871
+ throw new RangeError("analyzeImpulseResponse: minDecayDb must be greater than zero");
2872
+ }
2305
2873
  validateAnalysisInput(
2306
2874
  "analyzeImpulseResponse",
2307
2875
  request.samples,
2308
2876
  request.sampleRate ?? 48e3,
2309
2877
  request
2310
2878
  );
2311
- const result = requireModule14().analyzeImpulseResponse(
2879
+ const result = requireModule14().analyzeImpulseResponseEx(
2312
2880
  request.samples,
2313
2881
  request.sampleRate ?? 48e3,
2314
- request.nOctaveBands ?? 6
2882
+ request.nOctaveBands ?? 6,
2883
+ resolvedMinDecayDb
2315
2884
  );
2316
2885
  return result;
2317
2886
  }
@@ -2364,7 +2933,9 @@ function analyzeWithProgress(samples, sampleRate = 22050, onProgress) {
2364
2933
  const result = requireModule14().analyzeWithProgress(
2365
2934
  request.samples,
2366
2935
  request.sampleRate ?? 22050,
2367
- request.onProgress
2936
+ request.onProgress ?? (() => {
2937
+ }),
2938
+ request.cancel ?? (() => false)
2368
2939
  );
2369
2940
  return convertAnalysisResult(result);
2370
2941
  }
@@ -2518,9 +3089,14 @@ var Audio = class _Audio {
2518
3089
  }
2519
3090
  }
2520
3091
  }
2521
- /** The raw audio samples. */
3092
+ /**
3093
+ * A copy of the raw audio samples. Mirrors Node's `getData()` contract: the
3094
+ * returned array is independent of the Audio's internal buffer, so mutating
3095
+ * it (or transferring it to a Worker) does not affect subsequent facade
3096
+ * calls, which all read the internal snapshot directly.
3097
+ */
2522
3098
  get data() {
2523
- return this._samples;
3099
+ return this._samples.slice();
2524
3100
  }
2525
3101
  /** Number of samples. */
2526
3102
  get length() {
@@ -2788,7 +3364,12 @@ self.onmessage = async (event) => {
2788
3364
  `;
2789
3365
  function createOpfsClipPageWorker() {
2790
3366
  const blob = new Blob([opfsClipPageWorkerSource], { type: "text/javascript" });
2791
- return new Worker(URL.createObjectURL(blob));
3367
+ const url = URL.createObjectURL(blob);
3368
+ try {
3369
+ return new Worker(url);
3370
+ } finally {
3371
+ URL.revokeObjectURL(url);
3372
+ }
2792
3373
  }
2793
3374
  function createOpfsClipPageProvider(engine, options) {
2794
3375
  if (options.numChannels <= 0 || options.numSamples <= 0 || options.pageFrames <= 0) {
@@ -2828,6 +3409,7 @@ function createOpfsClipPageProvider(engine, options) {
2828
3409
  }
2829
3410
  try {
2830
3411
  provider.supply(response.pageIndex, channels);
3412
+ options.onPageSupplied?.(response.pageIndex, channels);
2831
3413
  } catch {
2832
3414
  entry.resolve(false);
2833
3415
  return;
@@ -2875,6 +3457,13 @@ function createOpfsClipPageProvider(engine, options) {
2875
3457
  supplyRequest(request) {
2876
3458
  return supplyPage(Math.floor(request.sample / options.pageFrames));
2877
3459
  },
3460
+ clearPage(pageIndex) {
3461
+ if (closed) {
3462
+ return;
3463
+ }
3464
+ provider.clear(pageIndex);
3465
+ options.onPageCleared?.(pageIndex);
3466
+ },
2878
3467
  close() {
2879
3468
  if (closed) {
2880
3469
  return;
@@ -2886,6 +3475,7 @@ function createOpfsClipPageProvider(engine, options) {
2886
3475
  }
2887
3476
  pending.clear();
2888
3477
  provider.destroy();
3478
+ options.onClose?.();
2889
3479
  if (ownsWorker) {
2890
3480
  worker.terminate();
2891
3481
  }
@@ -2963,7 +3553,10 @@ var ClipPageStreamer = class {
2963
3553
  if (!state) {
2964
3554
  continue;
2965
3555
  }
2966
- const page = Math.floor(request.sample / state.source.pageFrames);
3556
+ const page = request.pageIndex !== void 0 ? request.pageIndex : Math.floor((request.sample ?? Number.NaN) / state.source.pageFrames);
3557
+ if (!Number.isInteger(page) || page < 0 || page > state.lastPage) {
3558
+ continue;
3559
+ }
2967
3560
  frontiers.set(request.clipId, page);
2968
3561
  }
2969
3562
  const fetches = [];
@@ -2998,7 +3591,7 @@ var ClipPageStreamer = class {
2998
3591
  const high = Math.min(state.lastPage, frontier + this.readAheadPages);
2999
3592
  for (const page of state.resident.keys()) {
3000
3593
  if (page < low || page > high) {
3001
- state.source.binding.provider.clear(page);
3594
+ this.clearPage(state, page);
3002
3595
  state.resident.delete(page);
3003
3596
  }
3004
3597
  }
@@ -3013,7 +3606,7 @@ var ClipPageStreamer = class {
3013
3606
  state.source.binding.supplyPage(pageIndex).then(
3014
3607
  (ok) => {
3015
3608
  if (state.generation !== generation) {
3016
- state.source.binding.provider.clear(pageIndex);
3609
+ this.clearPage(state, pageIndex);
3017
3610
  } else if (!ok && state.resident.get(pageIndex) === generation) {
3018
3611
  state.resident.delete(pageIndex);
3019
3612
  }
@@ -3034,12 +3627,28 @@ var ClipPageStreamer = class {
3034
3627
  state.generation += 1;
3035
3628
  state.lastFrontier = null;
3036
3629
  for (const page of state.resident.keys()) {
3037
- state.source.binding.provider.clear(page);
3630
+ this.clearPage(state, page);
3038
3631
  }
3039
3632
  state.resident.clear();
3040
3633
  }
3634
+ clearPage(state, pageIndex) {
3635
+ if (state.source.binding.clearPage) {
3636
+ state.source.binding.clearPage(pageIndex);
3637
+ } else {
3638
+ state.source.binding.provider.clear(pageIndex);
3639
+ }
3640
+ }
3041
3641
  };
3042
- async function attachOpfsClipStream(streamer, engine, options) {
3642
+ async function attachOpfsClipStream(streamerOrEngine, engineOrOptions, maybeOptions) {
3643
+ if (!(streamerOrEngine instanceof ClipPageStreamer)) {
3644
+ return streamerOrEngine.attachOpfsClipStream(engineOrOptions);
3645
+ }
3646
+ const streamer = streamerOrEngine;
3647
+ const engine = engineOrOptions;
3648
+ const options = maybeOptions;
3649
+ if (!options) {
3650
+ throw new Error("attachOpfsClipStream requires options.");
3651
+ }
3043
3652
  const { clipId, primePages = 1, ...providerOptions } = options;
3044
3653
  const binding = createOpfsClipPageProvider(engine, providerOptions);
3045
3654
  const lastPage = Math.ceil(providerOptions.numSamples / providerOptions.pageFrames) - 1;
@@ -3095,7 +3704,11 @@ async function bindMicrophoneInput(context, engine, options = {}) {
3095
3704
  function assertOversampleFactor(fnName, factor) {
3096
3705
  const normalized = factor === 0 ? 4 : factor;
3097
3706
  if (!Number.isInteger(normalized) || normalized < 1 || normalized > 16 || (normalized & normalized - 1) !== 0) {
3098
- throw new RangeError(`${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`);
3707
+ throw new SonareError(
3708
+ 4 /* InvalidParameter */,
3709
+ "InvalidParameter",
3710
+ `${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`
3711
+ );
3099
3712
  }
3100
3713
  }
3101
3714
  function requireModule15() {
@@ -3341,44 +3954,61 @@ function assertProjectMidiEvents(fnName, events) {
3341
3954
  });
3342
3955
  }
3343
3956
  function projectTrackKindValue(kind) {
3344
- if (kind === void 0 || kind === "audio") {
3345
- return 0;
3346
- }
3347
- if (kind === "midi") {
3348
- return 1;
3349
- }
3350
- if (kind === "aux") {
3351
- return 2;
3352
- }
3353
- return kind;
3957
+ return resolveEnumOrdinal(kind ?? "audio", { audio: 0, midi: 1, aux: 2 }, "project track kind");
3958
+ }
3959
+ function projectAutomationTargetKindValue(kind) {
3960
+ return resolveEnumOrdinal(
3961
+ kind,
3962
+ { opaque: 0, "track-fader-db": 1, "track-pan": 2 },
3963
+ "project automation target kind"
3964
+ );
3354
3965
  }
3355
3966
  function projectWarpModeValue(mode) {
3356
- if (mode === void 0 || mode === "off") {
3357
- return 0;
3358
- }
3359
- if (mode === "repitch") {
3360
- return 1;
3967
+ return resolveEnumOrdinal(
3968
+ mode ?? "off",
3969
+ { off: 0, repitch: 1, "tempo-sync": 2 },
3970
+ "project warp mode"
3971
+ );
3972
+ }
3973
+ function projectLoopModeValue(mode) {
3974
+ return resolveEnumOrdinal(mode ?? "off", { off: 0, loop: 1 }, "project loop mode");
3975
+ }
3976
+
3977
+ // src/project_class.ts
3978
+ function validateAssistSidecarUint32(value, field) {
3979
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 4294967295) {
3980
+ throw new RangeError(`Project.setAssistSidecar: ${field} must be a uint32`);
3361
3981
  }
3362
- if (mode === "tempo-sync") {
3363
- return 2;
3982
+ return value;
3983
+ }
3984
+ function validateAssistSidecarPpq(value, field) {
3985
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
3986
+ throw new RangeError(
3987
+ `Project.setAssistSidecar: ${field} must be a finite, non-negative number`
3988
+ );
3364
3989
  }
3365
- return mode;
3990
+ return value;
3366
3991
  }
3367
- function projectLoopModeValue(mode) {
3368
- if (mode === void 0 || mode === "off") {
3369
- return 0;
3992
+ function validateAssistSidecarPayload(value) {
3993
+ if (!(value instanceof Uint8Array)) {
3994
+ throw new TypeError("Project.setAssistSidecar: payload must be a Uint8Array");
3370
3995
  }
3371
- if (mode === "loop") {
3372
- return 1;
3996
+ return value;
3997
+ }
3998
+ function validateAssistSidecarModuleId(value) {
3999
+ if (typeof value !== "string" || value.length === 0) {
4000
+ throw new TypeError("Project.setAssistSidecar: moduleId must be a non-empty string");
3373
4001
  }
3374
- return mode;
4002
+ return value;
3375
4003
  }
3376
-
3377
- // src/project_class.ts
3378
4004
  var Project = class _Project {
3379
4005
  constructor() {
3380
4006
  this.native = new (projectModule()).Project();
3381
4007
  }
4008
+ /** Create a new empty project. */
4009
+ static create() {
4010
+ return new _Project();
4011
+ }
3382
4012
  /** Pack a MIDI 1.0 note-on event accepted by {@link setMidiEvents}. */
3383
4013
  static midiNoteOn(ppq, group, channel, note, velocity) {
3384
4014
  return projectMidi1Event("Project.midiNoteOn", ppq, group, 9, channel, note, velocity);
@@ -3544,6 +4174,16 @@ var Project = class _Project {
3544
4174
  addClip(desc) {
3545
4175
  return this.native.addClip(desc);
3546
4176
  }
4177
+ /** Import host-separated PCM through the normal audio-track/clip path. */
4178
+ importExternalStems(request) {
4179
+ return this.native.importExternalStems({
4180
+ ...request,
4181
+ stems: request.stems.map((stem) => ({
4182
+ ...stem,
4183
+ layout: stem.layout === "mono" ? 1 : stem.layout === "stereo" ? 2 : stem.layout
4184
+ }))
4185
+ });
4186
+ }
3547
4187
  /** Split captured loop-recording audio into takes and add one clip. */
3548
4188
  addLoopRecordingTakes(desc) {
3549
4189
  return this.native.addLoopRecordingTakes(desc);
@@ -3588,7 +4228,11 @@ var Project = class _Project {
3588
4228
  * Route a track's MIDI to host-instrument `destinationId` (0 = default). The
3589
4229
  * compiler stamps every MIDI clip on the track with this id so the engine
3590
4230
  * dispatches its events to the instrument registered for that destination.
3591
- * Routes through an undoable edit command.
4231
+ * Routes through an undoable edit command. Builtin, NativeSynth, and SF2
4232
+ * instruments retain source-track provenance inside a shared destination
4233
+ * voice pool. With only zero-latency instruments bound, live lanes and
4234
+ * channel-strip bounces remain aligned. Configure one live lane per source
4235
+ * track that needs strip processing.
3592
4236
  */
3593
4237
  setTrackMidiDestination(trackId, destinationId) {
3594
4238
  this.native.setTrackMidiDestination(trackId, destinationId);
@@ -3628,6 +4272,18 @@ var Project = class _Project {
3628
4272
  }
3629
4273
  this.native.setMaxUndoDepth(depth);
3630
4274
  }
4275
+ /** Set the combined undo/redo history byte cap. Zero disables retention. */
4276
+ setMaxHistoryBytes(bytes) {
4277
+ if (typeof bytes !== "number") {
4278
+ throw new TypeError("Project.setMaxHistoryBytes: bytes must be a number");
4279
+ }
4280
+ if (!Number.isFinite(bytes) || !Number.isInteger(bytes) || bytes < 0 || bytes > 4294967295) {
4281
+ throw new RangeError(
4282
+ "Project.setMaxHistoryBytes: bytes must be a finite integer in the uint32 range"
4283
+ );
4284
+ }
4285
+ this.native.setMaxHistoryBytes(bytes);
4286
+ }
3631
4287
  /** Replace a MIDI clip's entire event list. */
3632
4288
  setMidiEvents(clipId, events) {
3633
4289
  assertProjectMidiEvents("Project.setMidiEvents", events);
@@ -3758,8 +4414,11 @@ var Project = class _Project {
3758
4414
  * Karplus-Strong / modal / additive / percussion / extended-waveguide-piano
3759
4415
  * engines plus the realism layer). Pass a {@link SynthPatch}, a preset-name
3760
4416
  * string (`'saw-lead'` / `'va:saw-lead'`; see {@link synthPresetNames}), or
3761
- * an array of either; each object entry may carry a `destinationId` binding
3762
- * convenience (default 0), which is not part of the NativeSynth patch itself.
4417
+ * an array of either; each object entry may carry `destinationId` (default
4418
+ * 0) and `useGmPrograms` (default `false`) binding conveniences, neither of
4419
+ * which is part of the NativeSynth patch itself. When enabled, MIDI program
4420
+ * changes select the corresponding General MIDI voice while the patch remains
4421
+ * the fallback.
3763
4422
  * Because the parameter defaults to `{}`, omission and explicit `undefined`
3764
4423
  * both create one default binding. Use an explicitly empty array `[]` (or
3765
4424
  * runtime `null`) for zero bindings. Unknown preset names throw.
@@ -3825,6 +4484,18 @@ var Project = class _Project {
3825
4484
  setClipFade(clipId, fadeIn = {}, fadeOut = {}) {
3826
4485
  this.native.setClipFade(clipId, fadeIn, fadeOut);
3827
4486
  }
4487
+ /** Audio source ids that need decoded PCM after deserialization. */
4488
+ unresolvedAudioSourceIds() {
4489
+ return this.native.unresolvedAudioSourceIds();
4490
+ }
4491
+ /** Register decoded interleaved PCM for an existing audio source (undoable). */
4492
+ setSourceAudio(sourceId, audio, channels, sampleRate) {
4493
+ this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
4494
+ }
4495
+ /** Replace an audio source's metadata strings as one undoable edit. */
4496
+ setAudioSourceMetadata(sourceId, contentHash, externalStemRole) {
4497
+ this.native.setAudioSourceMetadata(sourceId, contentHash, externalStemRole);
4498
+ }
3828
4499
  /** Replace a clip's take list and active take id (undoable). */
3829
4500
  setClipTakes(clipId, takes, activeTakeId = 0) {
3830
4501
  this.native.setClipTakes(clipId, takes, activeTakeId);
@@ -3866,23 +4537,35 @@ var Project = class _Project {
3866
4537
  setTrackRoute(trackId, channelStripRef, outputTarget) {
3867
4538
  this.native.setTrackRoute(trackId, channelStripRef ?? "", outputTarget ?? "");
3868
4539
  }
3869
- /** Append an automation lane to a track; returns the lane index (undoable). */
4540
+ /** Append an automation lane; returns its stable target parameter id (undoable). */
3870
4541
  addAutomationLane(trackId, desc) {
3871
- return this.native.addAutomationLane(trackId, {
3872
- targetParamId: desc.targetParamId,
3873
- points: desc.points
3874
- });
4542
+ if (desc.targetParamId === 0) {
4543
+ throw new RangeError("project automation lane targetParamId must be non-zero");
4544
+ }
4545
+ const nativeDesc = { ...desc };
4546
+ if (Object.keys(desc).includes("targetKind")) {
4547
+ nativeDesc.targetKind = projectAutomationTargetKindValue(
4548
+ desc.targetKind
4549
+ );
4550
+ }
4551
+ return this.native.addAutomationLane(trackId, nativeDesc);
3875
4552
  }
3876
- /** Replace an existing automation lane in place (undoable). */
3877
- editAutomationLane(trackId, laneIndex, desc) {
3878
- this.native.editAutomationLane(trackId, laneIndex, {
3879
- targetParamId: desc.targetParamId,
3880
- points: desc.points
3881
- });
4553
+ /** Replace the lane identified by its stable target parameter id (undoable). */
4554
+ editAutomationLane(trackId, targetParamId, desc) {
4555
+ if (desc.targetParamId === 0) {
4556
+ throw new RangeError("project automation lane targetParamId must be non-zero");
4557
+ }
4558
+ const nativeDesc = { ...desc };
4559
+ if (Object.keys(desc).includes("targetKind")) {
4560
+ nativeDesc.targetKind = projectAutomationTargetKindValue(
4561
+ desc.targetKind
4562
+ );
4563
+ }
4564
+ this.native.editAutomationLane(trackId, targetParamId, nativeDesc);
3882
4565
  }
3883
- /** Remove an automation lane from a track (undoable). */
3884
- removeAutomationLane(trackId, laneIndex) {
3885
- this.native.removeAutomationLane(trackId, laneIndex);
4566
+ /** Remove the lane identified by its stable target parameter id (undoable). */
4567
+ removeAutomationLane(trackId, targetParamId) {
4568
+ this.native.removeAutomationLane(trackId, targetParamId);
3886
4569
  }
3887
4570
  /** Replace the project's key annotation stream (undoable). */
3888
4571
  annotateKeys(keys) {
@@ -3892,15 +4575,33 @@ var Project = class _Project {
3892
4575
  annotateChords(chords) {
3893
4576
  this.native.annotateChords(chords);
3894
4577
  }
3895
- /** Add or update an opaque assist sidecar by module id + target scope (undoable). */
3896
- setAssistSidecar(moduleId, schemaVersion, targetTrackId, regionStartPpq, regionEndPpq, payload) {
4578
+ setAssistSidecar(sidecarOrModuleId, schemaVersion, targetTrackId, regionStartPpq, regionEndPpq, payload) {
4579
+ if (typeof sidecarOrModuleId === "string") {
4580
+ if (schemaVersion === void 0 || targetTrackId === void 0 || regionStartPpq === void 0 || regionEndPpq === void 0 || payload === void 0) {
4581
+ throw new TypeError("Project.setAssistSidecar: positional form requires six arguments");
4582
+ }
4583
+ this.native.setAssistSidecar(
4584
+ validateAssistSidecarModuleId(sidecarOrModuleId),
4585
+ validateAssistSidecarUint32(schemaVersion, "schemaVersion"),
4586
+ validateAssistSidecarUint32(targetTrackId, "targetTrackId"),
4587
+ validateAssistSidecarPpq(regionStartPpq, "regionStartPpq"),
4588
+ validateAssistSidecarPpq(regionEndPpq, "regionEndPpq"),
4589
+ validateAssistSidecarPayload(payload)
4590
+ );
4591
+ return;
4592
+ }
4593
+ if (sidecarOrModuleId === null || typeof sidecarOrModuleId !== "object" || Array.isArray(sidecarOrModuleId)) {
4594
+ throw new TypeError("Project.setAssistSidecar: expected a sidecar descriptor object");
4595
+ }
4596
+ const sidecar = sidecarOrModuleId;
4597
+ const moduleId = validateAssistSidecarModuleId(sidecar.moduleId);
3897
4598
  this.native.setAssistSidecar(
3898
4599
  moduleId,
3899
- schemaVersion,
3900
- targetTrackId,
3901
- regionStartPpq,
3902
- regionEndPpq,
3903
- payload
4600
+ validateAssistSidecarUint32(sidecar.schemaVersion ?? 0, "schemaVersion"),
4601
+ validateAssistSidecarUint32(sidecar.targetTrackId ?? 0, "targetTrackId"),
4602
+ validateAssistSidecarPpq(sidecar.regionStartPpq ?? 0, "regionStartPpq"),
4603
+ validateAssistSidecarPpq(sidecar.regionEndPpq ?? 0, "regionEndPpq"),
4604
+ validateAssistSidecarPayload(sidecar.payload ?? new Uint8Array())
3904
4605
  );
3905
4606
  }
3906
4607
  /** Number of assist sidecars currently stored on the project. */
@@ -3911,6 +4612,11 @@ var Project = class _Project {
3911
4612
  getAssistSidecar(index) {
3912
4613
  return this.native.getAssistSidecar(index);
3913
4614
  }
4615
+ /** Read every stored assist sidecar in the same order as the index getter. */
4616
+ assistSidecars() {
4617
+ const count = this.assistSidecarCount();
4618
+ return Array.from({ length: count }, (_, index) => this.getAssistSidecar(index));
4619
+ }
3914
4620
  /** Set the project's clip-overlap policy (SonareProjectOverlapPolicy ordinal). */
3915
4621
  setOverlapPolicy(policy) {
3916
4622
  this.native.setOverlapPolicy(policy);
@@ -3946,6 +4652,18 @@ var Project = class _Project {
3946
4652
  markerByIndex(index) {
3947
4653
  return this.native.markerByIndex(index);
3948
4654
  }
4655
+ /** Read a stored project track by 0-based index. */
4656
+ trackByIndex(index) {
4657
+ return this.native.trackByIndex(index);
4658
+ }
4659
+ /** Read a stored project clip by 0-based index. */
4660
+ clipByIndex(index) {
4661
+ return this.native.clipByIndex(index);
4662
+ }
4663
+ /** Read a stored project source by 0-based index. */
4664
+ sourceByIndex(index) {
4665
+ return this.native.sourceByIndex(index);
4666
+ }
3949
4667
  /** Number of markers in the project. */
3950
4668
  markerCount() {
3951
4669
  return this.native.markerCount();
@@ -4019,6 +4737,7 @@ var MarkerKind = {
4019
4737
  cuePoint: 3,
4020
4738
  keySignature: 4
4021
4739
  };
4740
+ var BUILTIN_SYNTH_WAVEFORMS = ["sine", "saw", "sawtooth", "square", "triangle"];
4022
4741
  var SYNTH_ENGINE_MODES = [
4023
4742
  "default",
4024
4743
  "subtractive",
@@ -4080,82 +4799,14 @@ var SYNTH_MOD_DESTINATIONS = [
4080
4799
  "amp-gain",
4081
4800
  "pan-units"
4082
4801
  ];
4083
-
4084
- // src/codes.ts
4085
- function automationCurveCode(curve) {
4086
- switch (curve) {
4087
- case "linear":
4088
- return 0;
4089
- case "exponential":
4090
- return 1;
4091
- case "hold":
4092
- return 2;
4093
- case "s-curve":
4094
- return 3;
4095
- default:
4096
- throw new Error(`Invalid automation curve: ${curve}`);
4097
- }
4098
- }
4099
- function panLawCode(panLaw) {
4100
- if (typeof panLaw === "number") {
4101
- return panLaw;
4102
- }
4103
- switch (panLaw) {
4104
- case "const3dB":
4105
- return 0;
4106
- case "const4.5dB":
4107
- return 1;
4108
- case "const6dB":
4109
- return 2;
4110
- case "linear0dB":
4111
- return 3;
4112
- default:
4113
- throw new Error(`Invalid pan law: ${panLaw}`);
4114
- }
4115
- }
4116
- function panModeCode(panMode) {
4117
- if (typeof panMode === "number") {
4118
- return panMode;
4119
- }
4120
- switch (panMode) {
4121
- case "balance":
4122
- return 0;
4123
- case "stereoPan":
4124
- case "stereo-pan":
4125
- return 1;
4126
- case "dualPan":
4127
- case "dual-pan":
4128
- return 2;
4129
- default:
4130
- throw new Error(`Invalid pan mode: ${panMode}`);
4131
- }
4132
- }
4133
- function meterTapCode(tap) {
4134
- if (typeof tap === "number") {
4135
- return tap;
4136
- }
4137
- switch (tap) {
4138
- case "preFader":
4139
- return 0;
4140
- case "postFader":
4141
- return 1;
4142
- default:
4143
- throw new Error(`Invalid meter tap: ${tap}`);
4144
- }
4145
- }
4146
- function sendTimingCode(timing) {
4147
- if (typeof timing === "number") {
4148
- return timing;
4149
- }
4150
- switch (timing) {
4151
- case "postFader":
4152
- return 0;
4153
- case "preFader":
4154
- return 1;
4155
- default:
4156
- throw new Error(`Invalid send timing: ${timing}`);
4157
- }
4158
- }
4802
+ var AutomationTargetKind = {
4803
+ opaque: 0,
4804
+ trackFaderDb: 1,
4805
+ trackPan: 2
4806
+ };
4807
+ var PROJECT_AUTOMATION_TARGET_OPAQUE = AutomationTargetKind.opaque;
4808
+ var PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB = AutomationTargetKind.trackFaderDb;
4809
+ var PROJECT_AUTOMATION_TARGET_TRACK_PAN = AutomationTargetKind.trackPan;
4159
4810
 
4160
4811
  // src/realtime_engine.ts
4161
4812
  var EXPECTED_ENGINE_ABI_VERSION = 3;
@@ -4175,23 +4826,30 @@ function engineCapabilities() {
4175
4826
  };
4176
4827
  }
4177
4828
  var RealtimeEngine = class {
4178
- constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024) {
4829
+ constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024, maxChannels = 64) {
4179
4830
  const module2 = getSonareModule();
4180
- const capabilities = engineCapabilities();
4181
- if (!capabilities.abiCompatible) {
4831
+ const capabilities2 = engineCapabilities();
4832
+ if (!capabilities2.abiCompatible) {
4182
4833
  throw new Error(
4183
- `Engine ABI mismatch: wasm=${capabilities.engineAbiVersion}, expected=${capabilities.expectedEngineAbiVersion}`
4834
+ `Engine ABI mismatch: wasm=${capabilities2.engineAbiVersion}, expected=${capabilities2.expectedEngineAbiVersion}`
4184
4835
  );
4185
4836
  }
4186
4837
  this.native = new module2.RealtimeEngine(
4187
4838
  sampleRate,
4188
4839
  maxBlockSize,
4189
4840
  commandCapacity,
4190
- telemetryCapacity
4841
+ telemetryCapacity,
4842
+ maxChannels
4191
4843
  );
4192
4844
  }
4193
- prepare(sampleRate, maxBlockSize, commandCapacity = 1024, telemetryCapacity = 1024) {
4194
- this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);
4845
+ prepare(sampleRate, maxBlockSize, commandCapacity = 1024, telemetryCapacity = 1024, maxChannels = 64) {
4846
+ this.native.prepareWithChannels(
4847
+ sampleRate,
4848
+ maxBlockSize,
4849
+ commandCapacity,
4850
+ telemetryCapacity,
4851
+ maxChannels
4852
+ );
4195
4853
  }
4196
4854
  /** Queue a sample-accurate parameter change (engine kSetParam). */
4197
4855
  setParameter(paramId, value, renderFrame = -1) {
@@ -4212,6 +4870,10 @@ var RealtimeEngine = class {
4212
4870
  setSoloMute(laneIndex, solo, mute, renderFrame = -1) {
4213
4871
  this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
4214
4872
  }
4873
+ /** Queue a per-track PFL/AFL monitor tap mode change. */
4874
+ setTrackMonitorMode(laneIndex, mode, renderFrame = -1) {
4875
+ this.native.setTrackMonitorMode(laneIndex, trackMonitorModeCode(mode), renderFrame);
4876
+ }
4215
4877
  setMidiClips(clips) {
4216
4878
  this.native.setMidiClips(clips);
4217
4879
  }
@@ -4317,6 +4979,9 @@ var RealtimeEngine = class {
4317
4979
  externalMidiDroppedCount() {
4318
4980
  return this.native.externalMidiDroppedCount();
4319
4981
  }
4982
+ externalMidiPendingCount() {
4983
+ return this.native.externalMidiPendingCount();
4984
+ }
4320
4985
  /**
4321
4986
  * Drain queued external-MIDI events, already lowered to MIDI 1.0 byte
4322
4987
  * messages ready to write to a Web MIDI output port. Call once per audio
@@ -4327,6 +4992,25 @@ var RealtimeEngine = class {
4327
4992
  drainExternalMidi(maxRecords = 1024) {
4328
4993
  return this.native.drainExternalMidi(maxRecords);
4329
4994
  }
4995
+ /** Scalar, allocation-free external-MIDI drain for AudioWorklet SAB output. */
4996
+ popExternalMidiToScratch() {
4997
+ return this.native.popExternalMidiToScratch();
4998
+ }
4999
+ externalMidiScratchDestinationId() {
5000
+ return this.native.externalMidiScratchDestinationId();
5001
+ }
5002
+ externalMidiScratchRenderFrame() {
5003
+ return this.native.externalMidiScratchRenderFrame();
5004
+ }
5005
+ externalMidiScratchByteWord() {
5006
+ return this.native.externalMidiScratchByteWord();
5007
+ }
5008
+ externalMidiScratchByteCount() {
5009
+ return this.native.externalMidiScratchByteCount();
5010
+ }
5011
+ consumeExternalMidiScratch() {
5012
+ this.native.consumeExternalMidiScratch();
5013
+ }
4330
5014
  pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples = 0) {
4331
5015
  this.native.pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
4332
5016
  }
@@ -4351,6 +5035,10 @@ var RealtimeEngine = class {
4351
5035
  pushMidiCc(destinationId, group, channel, controller, value, renderFrame = -1) {
4352
5036
  this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
4353
5037
  }
5038
+ /** Queue one immediate MIDI 1.0 channel-voice UMP word for a destination. */
5039
+ pushMidiUmp(destinationId, word0, renderFrame = -1) {
5040
+ this.native.pushMidiUmp(destinationId, word0, renderFrame);
5041
+ }
4354
5042
  /**
4355
5043
  * Queue an immediate (live) MIDI SysEx frame to a MIDI destination. `data` is
4356
5044
  * the full message including the leading 0xF0 and trailing 0xF7 (1..512
@@ -4396,6 +5084,10 @@ var RealtimeEngine = class {
4396
5084
  settleParameters() {
4397
5085
  this.native.settleParameters();
4398
5086
  }
5087
+ /** Drains queued commands on an offline/control-only engine immediately. */
5088
+ flushControlCommands() {
5089
+ this.native.flushControlCommands();
5090
+ }
4399
5091
  seekPpq(ppq, renderFrame = -1) {
4400
5092
  this.native.seekPpq(ppq, renderFrame);
4401
5093
  }
@@ -4650,6 +5342,23 @@ var RealtimeEngine = class {
4650
5342
  popClipPageRequest() {
4651
5343
  return this.native.popClipPageRequest();
4652
5344
  }
5345
+ /**
5346
+ * Moves one native request into the binding's persistent scalar scratch.
5347
+ * This avoids creating an embind JS object in AudioWorklet process().
5348
+ */
5349
+ popClipPageRequestToScratch() {
5350
+ return this.native.popClipPageRequestToScratch();
5351
+ }
5352
+ clipPageRequestScratchClipId() {
5353
+ return this.native.clipPageRequestScratchClipId();
5354
+ }
5355
+ clipPageRequestScratchSample() {
5356
+ return this.native.clipPageRequestScratchSample();
5357
+ }
5358
+ /** Cumulative page misses dropped because the native bounded request queue was full. */
5359
+ clipPageRequestOverflowCount() {
5360
+ return this.native.clipPageRequestOverflowCount();
5361
+ }
4653
5362
  setCaptureBuffer(numChannels, capacityFrames) {
4654
5363
  this.native.setCaptureBuffer(numChannels, capacityFrames);
4655
5364
  }
@@ -4678,6 +5387,10 @@ var RealtimeEngine = class {
4678
5387
  capturedAudio() {
4679
5388
  return this.native.capturedAudio();
4680
5389
  }
5390
+ /**
5391
+ * Renders in place, adding engine output to `channels`. Zero each plane first
5392
+ * when it contains no upstream input.
5393
+ */
4681
5394
  process(channels) {
4682
5395
  return this.native.process(channels);
4683
5396
  }
@@ -4700,6 +5413,7 @@ var RealtimeEngine = class {
4700
5413
  }
4701
5414
  /**
4702
5415
  * Runs the engine in place over the prepared per-channel scratch buffers.
5416
+ * Zero each active span first when it contains no upstream input.
4703
5417
  * Allocation-free: safe to call on the AudioWorklet render thread after
4704
5418
  * `prepareChannels`.
4705
5419
  */
@@ -4721,6 +5435,42 @@ var RealtimeEngine = class {
4721
5435
  drainTelemetry(maxRecords = 1024) {
4722
5436
  return this.native.drainTelemetry(maxRecords);
4723
5437
  }
5438
+ popTelemetryToScratch() {
5439
+ return this.native.popTelemetryToScratch();
5440
+ }
5441
+ telemetryScratchType() {
5442
+ return this.native.telemetryScratchType();
5443
+ }
5444
+ telemetryScratchError() {
5445
+ return this.native.telemetryScratchError();
5446
+ }
5447
+ telemetryScratchRenderFrame() {
5448
+ return Number(this.native.telemetryScratchRenderFrame());
5449
+ }
5450
+ telemetryScratchTimelineSample() {
5451
+ return Number(this.native.telemetryScratchTimelineSample());
5452
+ }
5453
+ telemetryScratchAudibleTimelineSample() {
5454
+ return Number(this.native.telemetryScratchAudibleTimelineSample());
5455
+ }
5456
+ telemetryScratchGraphLatencySamplesQ8() {
5457
+ return this.native.telemetryScratchGraphLatencySamplesQ8();
5458
+ }
5459
+ telemetryScratchValue() {
5460
+ return this.native.telemetryScratchValue();
5461
+ }
5462
+ popMeterTelemetryToScratch() {
5463
+ return this.native.popMeterTelemetryToScratch();
5464
+ }
5465
+ meterScratchTargetId() {
5466
+ return this.native.meterScratchTargetId();
5467
+ }
5468
+ meterScratchRenderFrame() {
5469
+ return Number(this.native.meterScratchRenderFrame());
5470
+ }
5471
+ meterScratchValue(field) {
5472
+ return this.native.meterScratchValue(field);
5473
+ }
4724
5474
  drainMeterTelemetry(maxRecords = 1024) {
4725
5475
  return this.native.drainMeterTelemetry(maxRecords);
4726
5476
  }
@@ -4748,6 +5498,30 @@ var RealtimeEngine = class {
4748
5498
  drainScopeTelemetry(maxRecords = 1024) {
4749
5499
  return this.native.drainScopeTelemetry(maxRecords);
4750
5500
  }
5501
+ popScopeTelemetryToScratch() {
5502
+ return this.native.popScopeTelemetryToScratch();
5503
+ }
5504
+ scopeScratchTargetId() {
5505
+ return this.native.scopeScratchTargetId();
5506
+ }
5507
+ scopeScratchRenderFrame() {
5508
+ return Number(this.native.scopeScratchRenderFrame());
5509
+ }
5510
+ scopeScratchBandCount() {
5511
+ return this.native.scopeScratchBandCount();
5512
+ }
5513
+ scopeScratchBand(index) {
5514
+ return this.native.scopeScratchBand(index);
5515
+ }
5516
+ scopeScratchPointCount() {
5517
+ return this.native.scopeScratchPointCount();
5518
+ }
5519
+ scopeScratchPointLeft(index) {
5520
+ return this.native.scopeScratchPointLeft(index);
5521
+ }
5522
+ scopeScratchPointRight(index) {
5523
+ return this.native.scopeScratchPointRight(index);
5524
+ }
4751
5525
  destroy() {
4752
5526
  this.native.delete();
4753
5527
  }
@@ -4813,6 +5587,7 @@ var StreamAnalyzer = class {
4813
5587
  if (config.outputFormat !== void 0 && (typeof config.outputFormat !== "number" || !Number.isFinite(config.outputFormat) || !Number.isInteger(config.outputFormat) || config.outputFormat !== 0)) {
4814
5588
  throw new TypeError("outputFormat must be the integer 0 (Float32)");
4815
5589
  }
5590
+ const window = config.window === void 0 ? void 0 : resolveOrdinalInRange(config.window, 0, 3, "stream analyzer window");
4816
5591
  const module2 = getSonareModule();
4817
5592
  const defaults = streamAnalyzerConfigDefaults();
4818
5593
  this.analyzer = new module2.StreamAnalyzer(
@@ -4834,7 +5609,7 @@ var StreamAnalyzer = class {
4834
5609
  config.maxProgressionEntries ?? defaults.maxProgressionEntries,
4835
5610
  config.keyUpdateIntervalSec ?? defaults.keyUpdateIntervalSec,
4836
5611
  config.bpmUpdateIntervalSec ?? defaults.bpmUpdateIntervalSec,
4837
- config.window ?? defaults.window,
5612
+ window ?? defaults.window,
4838
5613
  config.outputFormat ?? defaults.outputFormat
4839
5614
  );
4840
5615
  }
@@ -5262,7 +6037,7 @@ var Mixer = class _Mixer {
5262
6037
  setPolarityInvert(stripIndex, invertLeft, invertRight) {
5263
6038
  this.mixer.setPolarityInvert(stripIndex, invertLeft, invertRight);
5264
6039
  }
5265
- /** Set the strip's pan law. */
6040
+ /** Set the strip's pan law (a {@link PanLawName} alias or raw C ABI ordinal). */
5266
6041
  setPanLaw(stripIndex, panLaw) {
5267
6042
  this.mixer.setPanLaw(stripIndex, panLawCode(panLaw));
5268
6043
  }
@@ -5449,65 +6224,18 @@ var Mixer = class _Mixer {
5449
6224
  };
5450
6225
 
5451
6226
  // src/realtime_voice_changer.ts
5452
- function isFlatVoiceChangerPod(config) {
5453
- return typeof config === "object" && config !== null && "retuneSemitones" in config;
5454
- }
5455
- function flatVoiceChangerPodToNested(pod) {
5456
- return {
5457
- inputGainDb: pod.inputGainDb,
5458
- outputGainDb: pod.outputGainDb,
5459
- wetMix: pod.wetMix,
5460
- retune: { semitones: pod.retuneSemitones, mix: pod.retuneMix, grainSize: pod.retuneGrainSize },
5461
- formant: {
5462
- factor: pod.formantFactor,
5463
- amount: pod.formantAmount,
5464
- body: pod.formantBody,
5465
- brightness: pod.formantBrightness,
5466
- nasal: pod.formantNasal
5467
- },
5468
- eq: {
5469
- highpassHz: pod.eqHighpassHz,
5470
- bodyDb: pod.eqBodyDb,
5471
- presenceDb: pod.eqPresenceDb,
5472
- airDb: pod.eqAirDb
5473
- },
5474
- gate: {
5475
- thresholdDb: pod.gateThresholdDb,
5476
- attackMs: pod.gateAttackMs,
5477
- releaseMs: pod.gateReleaseMs,
5478
- rangeDb: pod.gateRangeDb
5479
- },
5480
- compressor: {
5481
- thresholdDb: pod.compressorThresholdDb,
5482
- ratio: pod.compressorRatio,
5483
- attackMs: pod.compressorAttackMs,
5484
- releaseMs: pod.compressorReleaseMs,
5485
- makeupGainDb: pod.compressorMakeupGainDb
5486
- },
5487
- deesser: {
5488
- frequencyHz: pod.deesserFrequencyHz,
5489
- thresholdDb: pod.deesserThresholdDb,
5490
- ratio: pod.deesserRatio,
5491
- rangeDb: pod.deesserRangeDb
5492
- },
5493
- reverb: {
5494
- mix: pod.reverbMix,
5495
- timeMs: pod.reverbTimeMs,
5496
- damping: pod.reverbDamping,
5497
- seed: pod.reverbSeed
5498
- },
5499
- limiter: {
5500
- ceilingDb: pod.limiterCeilingDb,
5501
- releaseMs: pod.limiterReleaseMs,
5502
- enableIspLimiter: pod.limiterEnableIspLimiter,
5503
- ispCeilingDbtp: pod.limiterIspCeilingDbtp
5504
- }
5505
- };
5506
- }
5507
6227
  var RealtimeVoiceChanger = class {
5508
- constructor(config = "neutral-monitor") {
6228
+ /**
6229
+ * Creates a voice changer. Supplying `sampleRate` prepares it immediately,
6230
+ * matching the Node and Python constructors; omitting it preserves the
6231
+ * explicit {@link prepare} lifecycle for callers that configure later.
6232
+ */
6233
+ constructor(config = "neutral-monitor", sampleRate, maxBlockSize = 128, channels = 1) {
5509
6234
  const module2 = getSonareModule();
5510
6235
  this.changer = module2.createRealtimeVoiceChanger(config);
6236
+ if (sampleRate !== void 0) {
6237
+ this.changer.prepare(sampleRate, maxBlockSize, channels);
6238
+ }
5511
6239
  }
5512
6240
  prepare(sampleRate, maxBlockSize = 128, channels = 1) {
5513
6241
  this.changer.prepare(sampleRate, maxBlockSize, channels);
@@ -5516,8 +6244,15 @@ var RealtimeVoiceChanger = class {
5516
6244
  this.changer.reset();
5517
6245
  }
5518
6246
  setConfig(config) {
5519
- const resolved = isFlatVoiceChangerPod(config) ? flatVoiceChangerPodToNested(config) : config;
5520
- this.changer.setConfig(resolved);
6247
+ this.changer.setConfig(config);
6248
+ }
6249
+ /**
6250
+ * Apply a flat, pre-normalized config without JSON serialization. Intended
6251
+ * for AudioWorklet control messages whose sender prepared the POD on the
6252
+ * main thread.
6253
+ */
6254
+ setPodConfig(config) {
6255
+ this.changer.setPodConfig(config);
5521
6256
  }
5522
6257
  configJson() {
5523
6258
  return this.changer.configJson();
@@ -5525,6 +6260,13 @@ var RealtimeVoiceChanger = class {
5525
6260
  latencySamples() {
5526
6261
  return this.changer.latencySamples();
5527
6262
  }
6263
+ /**
6264
+ * Monotonically increases whenever {@link prepare} can replace the native
6265
+ * scratch buffers. Cached WASM heap views must be reacquired after it changes.
6266
+ */
6267
+ bufferGeneration() {
6268
+ return this.changer.bufferGeneration();
6269
+ }
5528
6270
  processMono(samples) {
5529
6271
  return this.changer.processMono(samples);
5530
6272
  }
@@ -5601,10 +6343,12 @@ var RealtimeVoiceChanger = class {
5601
6343
  createRealtimeMonoBuffer(numSamples) {
5602
6344
  let input = this.getMonoInputBuffer(numSamples);
5603
6345
  let output = this.getMonoOutputBuffer(numSamples);
6346
+ let generation = this.bufferGeneration();
5604
6347
  const reacquireIfDetached = () => {
5605
- if (input.byteLength === 0 || output.byteLength === 0) {
6348
+ if (generation !== this.bufferGeneration() || input.byteLength === 0 || output.byteLength === 0) {
5606
6349
  input = this.getMonoInputBuffer(numSamples);
5607
6350
  output = this.getMonoOutputBuffer(numSamples);
6351
+ generation = this.bufferGeneration();
5608
6352
  }
5609
6353
  };
5610
6354
  return {
@@ -5626,10 +6370,12 @@ var RealtimeVoiceChanger = class {
5626
6370
  createRealtimeInterleavedBuffer(numFrames, numChannels) {
5627
6371
  let input = this.getInterleavedInputBuffer(numFrames, numChannels);
5628
6372
  let output = this.getInterleavedOutputBuffer(numFrames, numChannels);
6373
+ let generation = this.bufferGeneration();
5629
6374
  const reacquireIfDetached = () => {
5630
- if (input.byteLength === 0 || output.byteLength === 0) {
6375
+ if (generation !== this.bufferGeneration() || input.byteLength === 0 || output.byteLength === 0) {
5631
6376
  input = this.getInterleavedInputBuffer(numFrames, numChannels);
5632
6377
  output = this.getInterleavedOutputBuffer(numFrames, numChannels);
6378
+ generation = this.bufferGeneration();
5633
6379
  }
5634
6380
  };
5635
6381
  return {
@@ -5656,15 +6402,17 @@ var RealtimeVoiceChanger = class {
5656
6402
  */
5657
6403
  createRealtimePlanarBuffer(numFrames, numChannels) {
5658
6404
  let channels = [];
6405
+ let generation = this.bufferGeneration();
5659
6406
  const acquire = () => {
5660
6407
  channels = [];
5661
6408
  for (let ch = 0; ch < numChannels; ch++) {
5662
6409
  channels.push(this.getPlanarChannelBuffer(ch, numFrames));
5663
6410
  }
6411
+ generation = this.bufferGeneration();
5664
6412
  };
5665
6413
  acquire();
5666
6414
  const reacquireIfDetached = () => {
5667
- if ((channels[0]?.byteLength ?? 0) === 0) {
6415
+ if (generation !== this.bufferGeneration() || (channels[0]?.byteLength ?? 0) === 0) {
5668
6416
  acquire();
5669
6417
  }
5670
6418
  };
@@ -5740,6 +6488,19 @@ var StreamingMasteringChain = class {
5740
6488
  }
5741
6489
  return this.chain.processStereo(left, right);
5742
6490
  }
6491
+ /**
6492
+ * Emit delayed audio and finite processor tails after the final mono block.
6493
+ * Call until this returns an empty array. The initial `latencySamples()`
6494
+ * samples of the concatenated stream are delayed and should be discarded for
6495
+ * time-aligned output.
6496
+ */
6497
+ flushMono() {
6498
+ return this.chain.flushMono();
6499
+ }
6500
+ /** Stereo counterpart of {@link flushMono}. */
6501
+ flushStereo() {
6502
+ return this.chain.flushStereo();
6503
+ }
5743
6504
  /** Reset all processor state without rebuilding. */
5744
6505
  reset() {
5745
6506
  this.chain.reset();
@@ -5981,7 +6742,6 @@ async function bindWebMidi(engine, options = {}) {
5981
6742
  for (const [, input] of iterInputs(access)) {
5982
6743
  bindInput(input);
5983
6744
  }
5984
- notify();
5985
6745
  };
5986
6746
  const stateListener = (event) => {
5987
6747
  if (closed) {
@@ -6000,6 +6760,7 @@ async function bindWebMidi(engine, options = {}) {
6000
6760
  notify();
6001
6761
  };
6002
6762
  refreshInputs();
6763
+ notify();
6003
6764
  if (access.addEventListener) {
6004
6765
  access.addEventListener("statechange", stateListener);
6005
6766
  } else {
@@ -6101,12 +6862,8 @@ function dispatchUmpMessage(engine, words, portTimeSamples) {
6101
6862
  if (status === 8) {
6102
6863
  engine.pushMidiInputNoteOff(group, channel, data1, word1 >>> 25 & 127, portTimeSamples);
6103
6864
  } else if (status === 9) {
6104
- const velocity = word1 >>> 25 & 127;
6105
- if (velocity === 0) {
6106
- engine.pushMidiInputNoteOff(group, channel, data1, 0, portTimeSamples);
6107
- } else {
6108
- engine.pushMidiInputNoteOn(group, channel, data1, velocity, portTimeSamples);
6109
- }
6865
+ const velocity = Math.max(1, word1 >>> 25 & 127);
6866
+ engine.pushMidiInputNoteOn(group, channel, data1, velocity, portTimeSamples);
6110
6867
  } else if (status === 11) {
6111
6868
  engine.pushMidiInputCc(group, channel, data1, word1 >>> 25 & 127, portTimeSamples);
6112
6869
  }
@@ -6131,6 +6888,214 @@ function iterInputs(access) {
6131
6888
  return access.inputs instanceof Map ? access.inputs.entries() : access.inputs;
6132
6889
  }
6133
6890
 
6891
+ // src/worker_client.ts
6892
+ var OfflineWorkerTask = class {
6893
+ constructor(result, cancelRequest) {
6894
+ this.result = result;
6895
+ this.cancelRequest = cancelRequest;
6896
+ }
6897
+ cancel() {
6898
+ this.cancelRequest();
6899
+ }
6900
+ // biome-ignore lint/suspicious/noThenProperty: this intentionally implements PromiseLike so callers can await a task and cancel it.
6901
+ then(onfulfilled, onrejected) {
6902
+ return this.result.then(onfulfilled, onrejected);
6903
+ }
6904
+ catch(onrejected) {
6905
+ return this.result.catch(onrejected);
6906
+ }
6907
+ finally(onfinally) {
6908
+ return this.result.finally(onfinally);
6909
+ }
6910
+ };
6911
+ function cloneForWorker(value, copy, transfers, transferred = /* @__PURE__ */ new Set()) {
6912
+ if (value instanceof Float32Array) {
6913
+ const samples = copy ? value.slice() : value;
6914
+ const buffer = samples.buffer;
6915
+ if (!(buffer instanceof ArrayBuffer)) {
6916
+ throw new TypeError(
6917
+ "OfflineWorkerClient only transfers Float32Array values backed by ArrayBuffer"
6918
+ );
6919
+ }
6920
+ if (!transferred.has(buffer)) {
6921
+ transferred.add(buffer);
6922
+ transfers.push(buffer);
6923
+ }
6924
+ return samples;
6925
+ }
6926
+ if (Array.isArray(value)) {
6927
+ return value.map((item) => cloneForWorker(item, copy, transfers, transferred));
6928
+ }
6929
+ if (value !== null && typeof value === "object") {
6930
+ return Object.fromEntries(
6931
+ Object.entries(value).map(([key, item]) => [
6932
+ key,
6933
+ cloneForWorker(item, copy, transfers, transferred)
6934
+ ])
6935
+ );
6936
+ }
6937
+ return value;
6938
+ }
6939
+ function cancellationFlag() {
6940
+ if (typeof SharedArrayBuffer === "undefined") {
6941
+ return void 0;
6942
+ }
6943
+ return new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
6944
+ }
6945
+ function workerError(message) {
6946
+ if (message.error.code !== void 0) {
6947
+ return new SonareError(
6948
+ message.error.code,
6949
+ message.error.codeName ?? "Unknown",
6950
+ message.error.message
6951
+ );
6952
+ }
6953
+ const error = new Error(message.error.message);
6954
+ error.name = message.error.name;
6955
+ return error;
6956
+ }
6957
+ var OfflineWorkerClient = class {
6958
+ constructor(options = {}) {
6959
+ this.pending = /* @__PURE__ */ new Map();
6960
+ this.nextId = 1;
6961
+ this.closed = false;
6962
+ this.usesEventTarget = false;
6963
+ this.onMessage = (event) => {
6964
+ const message = event.data;
6965
+ const pending = this.pending.get(message.id);
6966
+ if (!pending) {
6967
+ return;
6968
+ }
6969
+ if (message.type === "sonare:offline-progress") {
6970
+ pending.onProgress?.({ progress: message.progress, stage: message.stage });
6971
+ return;
6972
+ }
6973
+ this.pending.delete(message.id);
6974
+ if (message.type === "sonare:offline-result") {
6975
+ pending.resolve(message.result);
6976
+ return;
6977
+ }
6978
+ pending.reject(workerError(message));
6979
+ };
6980
+ this.onError = (event) => {
6981
+ const error = new Error(event.message || "Offline Worker failed");
6982
+ for (const { reject } of this.pending.values()) {
6983
+ reject(error);
6984
+ }
6985
+ this.pending.clear();
6986
+ };
6987
+ this.onNodeMessage = (data) => {
6988
+ this.onMessage({ data });
6989
+ };
6990
+ this.onNodeError = (error) => {
6991
+ this.onError(
6992
+ error instanceof Error ? { message: error.message } : { message: String(error) }
6993
+ );
6994
+ };
6995
+ this.ownsWorker = options.worker === void 0 || options.terminateWorkerOnDispose === true;
6996
+ if (options.worker) {
6997
+ this.worker = options.worker;
6998
+ } else {
6999
+ if (!options.workerFactory && typeof Worker === "undefined") {
7000
+ throw new Error("OfflineWorkerClient requires a browser Worker implementation");
7001
+ }
7002
+ const url = options.workerUrl === void 0 ? new URL("./worker.js", import.meta.url) : new URL(options.workerUrl, import.meta.url);
7003
+ this.worker = options.workerFactory?.(url) ?? new Worker(url, { type: "module", name: "sonare-offline" });
7004
+ }
7005
+ if (this.worker.addEventListener) {
7006
+ this.usesEventTarget = true;
7007
+ this.worker.addEventListener("message", this.onMessage);
7008
+ this.worker.addEventListener("error", this.onError);
7009
+ } else if (this.worker.on) {
7010
+ this.worker.on("message", this.onNodeMessage);
7011
+ this.worker.on("error", this.onNodeError);
7012
+ } else {
7013
+ throw new TypeError("OfflineWorkerClient requires Worker event listeners");
7014
+ }
7015
+ }
7016
+ /** Dispatch full music analysis to the Worker. */
7017
+ analyze(request, options) {
7018
+ return this.call("analyze", request, options);
7019
+ }
7020
+ /** Dispatch BPM detection to the Worker. */
7021
+ detectBpm(request, options) {
7022
+ return this.call("detectBpm", request, options);
7023
+ }
7024
+ /** Dispatch key detection to the Worker. */
7025
+ detectKey(request, options) {
7026
+ return this.call("detectKey", request, options);
7027
+ }
7028
+ /** Dispatch chord detection to the Worker. */
7029
+ detectChords(request, options) {
7030
+ return this.call("detectChords", request, options);
7031
+ }
7032
+ /** Dispatch mono preset mastering to the Worker. */
7033
+ masterAudio(request, options) {
7034
+ return this.call("masterAudio", request, options);
7035
+ }
7036
+ /** Dispatch stereo preset mastering to the Worker. */
7037
+ masterAudioStereo(request, options) {
7038
+ return this.call("masterAudioStereo", request, options);
7039
+ }
7040
+ /** Stop accepting calls, reject outstanding work, and release the Worker if owned. */
7041
+ dispose() {
7042
+ if (this.closed) {
7043
+ return;
7044
+ }
7045
+ this.closed = true;
7046
+ if (this.usesEventTarget) {
7047
+ this.worker.removeEventListener?.("message", this.onMessage);
7048
+ this.worker.removeEventListener?.("error", this.onError);
7049
+ } else {
7050
+ this.worker.off?.("message", this.onNodeMessage);
7051
+ this.worker.off?.("error", this.onNodeError);
7052
+ }
7053
+ for (const { reject } of this.pending.values()) {
7054
+ reject(new Error("OfflineWorkerClient was disposed"));
7055
+ }
7056
+ this.pending.clear();
7057
+ if (this.ownsWorker) {
7058
+ this.worker.terminate();
7059
+ }
7060
+ }
7061
+ call(operation, request, options = {}) {
7062
+ if (this.closed) {
7063
+ throw new Error("OfflineWorkerClient was disposed");
7064
+ }
7065
+ const id = this.nextId++;
7066
+ const transfers = [];
7067
+ const preparedRequest = cloneForWorker(request, options.copy === true, transfers);
7068
+ const cancelFlag = cancellationFlag();
7069
+ const result = new Promise((resolve, reject) => {
7070
+ this.pending.set(id, {
7071
+ resolve: (value) => resolve(value),
7072
+ reject,
7073
+ onProgress: options.onProgress,
7074
+ cancelFlag
7075
+ });
7076
+ });
7077
+ this.worker.postMessage(
7078
+ {
7079
+ type: "sonare:offline-run",
7080
+ id,
7081
+ operation,
7082
+ request: preparedRequest,
7083
+ ...cancelFlag ? { cancelBuffer: cancelFlag.buffer } : {}
7084
+ },
7085
+ transfers
7086
+ );
7087
+ return new OfflineWorkerTask(result, () => {
7088
+ if (!this.pending.has(id)) {
7089
+ return;
7090
+ }
7091
+ if (cancelFlag) {
7092
+ Atomics.store(cancelFlag, 0, 1);
7093
+ }
7094
+ this.worker.postMessage({ type: "sonare:offline-cancel", id });
7095
+ });
7096
+ }
7097
+ };
7098
+
6134
7099
  // src/index.ts
6135
7100
  var module = null;
6136
7101
  var initPromise = null;
@@ -6162,6 +7127,18 @@ function version() {
6162
7127
  }
6163
7128
  return module.version();
6164
7129
  }
7130
+ function capabilities() {
7131
+ if (!module) {
7132
+ throw new Error("Module not initialized. Call init() first.");
7133
+ }
7134
+ return module.capabilities();
7135
+ }
7136
+ function capabilityCatalog() {
7137
+ if (!module) {
7138
+ throw new Error("Module not initialized. Call init() first.");
7139
+ }
7140
+ return JSON.parse(module.capabilityCatalog());
7141
+ }
6165
7142
  function abiVersion() {
6166
7143
  if (!module) {
6167
7144
  throw new Error("Module not initialized. Call init() first.");
@@ -6190,6 +7167,9 @@ var VOICE_PRESET_ORDINALS = [
6190
7167
  ];
6191
7168
  function resolveVoicePresetOrdinal(preset) {
6192
7169
  if (typeof preset === "number") {
7170
+ if (!Number.isSafeInteger(preset) || preset < 0 || preset >= VOICE_PRESET_ORDINALS.length) {
7171
+ throw new RangeError(`Unknown voice-character preset ordinal: ${String(preset)}`);
7172
+ }
6193
7173
  return preset;
6194
7174
  }
6195
7175
  const ordinal = VOICE_PRESET_ORDINALS.indexOf(preset);
@@ -6202,6 +7182,9 @@ function voiceCharacterPresetId(preset) {
6202
7182
  if (!module) {
6203
7183
  throw new Error("Module not initialized. Call init() first.");
6204
7184
  }
7185
+ if (typeof preset === "number" && (!Number.isSafeInteger(preset) || preset < 0 || preset >= VOICE_PRESET_ORDINALS.length)) {
7186
+ return null;
7187
+ }
6205
7188
  return module.voiceCharacterPresetId(resolveVoicePresetOrdinal(preset));
6206
7189
  }
6207
7190
  function realtimeVoiceChangerPresetConfig(preset) {
@@ -6212,7 +7195,10 @@ function realtimeVoiceChangerPresetConfig(preset) {
6212
7195
  }
6213
7196
  export {
6214
7197
  Audio,
7198
+ AutomationTargetKind,
7199
+ BUILTIN_SYNTH_WAVEFORMS,
6215
7200
  ChordQuality,
7201
+ ClipPageProvider,
6216
7202
  ClipPageStreamer,
6217
7203
  EXPECTED_ENGINE_ABI_VERSION,
6218
7204
  EXPECTED_PROJECT_ABI_VERSION,
@@ -6221,6 +7207,11 @@ export {
6221
7207
  MarkerKind,
6222
7208
  Mixer,
6223
7209
  Mode,
7210
+ OfflineWorkerClient,
7211
+ OfflineWorkerTask,
7212
+ PROJECT_AUTOMATION_TARGET_OPAQUE,
7213
+ PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
7214
+ PROJECT_AUTOMATION_TARGET_TRACK_PAN,
6224
7215
  PitchClass as Pitch,
6225
7216
  PitchClass,
6226
7217
  Project,
@@ -6254,10 +7245,14 @@ export {
6254
7245
  bassChroma,
6255
7246
  bindMicrophoneInput,
6256
7247
  bindWebMidi,
7248
+ capabilities,
7249
+ capabilityCatalog,
7250
+ chirp,
6257
7251
  chordFunctionalAnalysis,
6258
7252
  chroma,
6259
7253
  chromaCens,
6260
7254
  chromaCqt,
7255
+ clicks,
6261
7256
  cqt,
6262
7257
  cqtToAudio,
6263
7258
  createOpfsClipPageProvider,
@@ -6287,6 +7282,7 @@ export {
6287
7282
  frameSignal,
6288
7283
  framesToSamples,
6289
7284
  framesToTime,
7285
+ griffinLim,
6290
7286
  harmonic,
6291
7287
  hasFfmpegSupport,
6292
7288
  hpss,
@@ -6337,6 +7333,7 @@ export {
6337
7333
  masteringStereoAnalysisNames,
6338
7334
  masteringStereoAnalyze,
6339
7335
  masteringStreamingPreview,
7336
+ melDelta,
6340
7337
  melSpectrogram,
6341
7338
  melToAudio,
6342
7339
  melToHz,
@@ -6369,8 +7366,10 @@ export {
6369
7366
  nnlsChroma,
6370
7367
  normalize,
6371
7368
  noteMove,
7369
+ noteSegments,
6372
7370
  noteStretch,
6373
7371
  noteToHz,
7372
+ onsetBacktrack,
6374
7373
  onsetEnvelope,
6375
7374
  onsetStrengthMulti,
6376
7375
  opfsClipPageWorkerSource,
@@ -6379,6 +7378,7 @@ export {
6379
7378
  peakPick,
6380
7379
  percussive,
6381
7380
  phaseVocoder,
7381
+ piptrack,
6382
7382
  pitchCorrectTimevarying,
6383
7383
  pitchCorrectToMidi,
6384
7384
  pitchCorrectToMidiTimevarying,
@@ -6395,6 +7395,7 @@ export {
6395
7395
  realtimeVoiceChangerPresetConfig,
6396
7396
  realtimeVoiceChangerPresetJson,
6397
7397
  realtimeVoiceChangerPresetNames,
7398
+ reassignedSpectrogram,
6398
7399
  remix,
6399
7400
  resample,
6400
7401
  rmsEnergy,
@@ -6403,12 +7404,20 @@ export {
6403
7404
  scaleCorrectionSemitones,
6404
7405
  scalePitchClassEnabled,
6405
7406
  scaleQuantizeMidi,
7407
+ segmentAgglomerative,
7408
+ segmentCrossSimilarity,
7409
+ segmentLagToRecurrence,
7410
+ segmentPathEnhance,
7411
+ segmentRecurrenceMatrix,
7412
+ segmentRecurrenceToLag,
7413
+ segmentSubsegment,
6406
7414
  shortTermLufs,
6407
7415
  spectralBandwidth,
6408
7416
  spectralCentroid,
6409
7417
  spectralContrast,
6410
7418
  spectralEdit,
6411
7419
  spectralFlatness,
7420
+ spectralFlux,
6412
7421
  spectralRolloff,
6413
7422
  splitSilence,
6414
7423
  stft,
@@ -6422,6 +7431,7 @@ export {
6422
7431
  tempogramRatio,
6423
7432
  timeStretch,
6424
7433
  timeToFrames,
7434
+ tone,
6425
7435
  tonnetz,
6426
7436
  trim,
6427
7437
  trimSilence,