@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/src/index.ts CHANGED
@@ -19,7 +19,12 @@
19
19
  */
20
20
 
21
21
  import { setSonareModule } from './module_state';
22
- import type { RealtimeVoiceChangerPodConfig, VoicePresetId } from './public_types';
22
+ import type {
23
+ CapabilityCatalog,
24
+ RealtimeVoiceChangerPodConfig,
25
+ SonareCapabilities,
26
+ VoicePresetId,
27
+ } from './public_types';
23
28
  import type {
24
29
  SonareModule,
25
30
  WasmDecomposeResult,
@@ -32,9 +37,11 @@ export { Audio } from './audio';
32
37
  export type {
33
38
  ClipPageStreamerEngine,
34
39
  ClipPageStreamerOptions,
40
+ ClipPageStreamerRequest,
35
41
  ClipPageStreamSource,
36
42
  OpfsClipStream,
37
43
  OpfsClipStreamOptions,
44
+ WorkletOpfsClipStreamHost,
38
45
  } from './clip_page_streamer';
39
46
  export { attachOpfsClipStream, ClipPageStreamer } from './clip_page_streamer';
40
47
  export type {
@@ -61,6 +68,7 @@ export type {
61
68
  MasteringProcessorCatalogEntry,
62
69
  MasteringProcessRequest,
63
70
  MasteringProcessStereoRequest,
71
+ MasteringRealtimeCost,
64
72
  MasteringRepairDeclickRequest,
65
73
  MasteringRepairDeclipRequest,
66
74
  MasteringRepairDecrackleRequest,
@@ -138,6 +146,7 @@ export {
138
146
  export type {
139
147
  HarmonicRequest,
140
148
  HpssRequest,
149
+ NormalizeMode,
141
150
  NormalizeRequest,
142
151
  NoteMoveRequest,
143
152
  NoteStretchRequest,
@@ -151,18 +160,22 @@ export type {
151
160
  } from './effects_transform';
152
161
  export { ErrorCode, isSonareError, SonareError } from './errors';
153
162
  export type {
163
+ ChirpRequest,
164
+ ClicksRequest,
154
165
  CyclicTempogramRequest,
155
166
  DbConversionRequest,
156
167
  EmphasisRequest,
157
168
  FixFramesRequest,
158
169
  FixLengthRequest,
159
170
  FrameSignalRequest,
171
+ OnsetBacktrackRequest,
160
172
  PadCenterRequest,
161
173
  PcenRequest,
162
174
  PeakPickRequest,
163
175
  PlpRequest,
164
176
  SilenceRequest,
165
177
  TempogramRequest,
178
+ ToneRequest,
166
179
  TonnetzRequest,
167
180
  VectorNormalizeRequest,
168
181
  } from './feature_core';
@@ -181,7 +194,12 @@ export type {
181
194
  VqtRequest,
182
195
  VqtToAudioRequest,
183
196
  } from './feature_music';
184
- export type { PitchPyinRequest, PitchYinRequest } from './feature_pitch';
197
+ export type {
198
+ NoteSegmentsRequest,
199
+ PiptrackRequest,
200
+ PitchPyinRequest,
201
+ PitchYinRequest,
202
+ } from './feature_pitch';
185
203
  export type { ResampleRequest } from './feature_resample';
186
204
  export type {
187
205
  DecomposeRequest,
@@ -195,6 +213,13 @@ export type {
195
213
  PitchTuningRequest,
196
214
  PolyFeaturesRequest,
197
215
  RemixRequest,
216
+ SegmentAgglomerativeRequest,
217
+ SegmentCrossSimilarityRequest,
218
+ SegmentLagToRecurrenceRequest,
219
+ SegmentPathEnhanceRequest,
220
+ SegmentRecurrenceMatrixRequest,
221
+ SegmentRecurrenceToLagRequest,
222
+ SegmentSubsegmentRequest,
198
223
  SpectralContrastRequest,
199
224
  SpectralFrameRequest,
200
225
  SpectralRolloffRequest,
@@ -203,12 +228,15 @@ export type {
203
228
  } from './feature_spectral';
204
229
  export type {
205
230
  ChromaSpectrogramRequest,
231
+ GriffinLimRequest,
232
+ MelDeltaRequest,
206
233
  MelSpectrogramRequest,
207
234
  MelToAudioRequest,
208
235
  MelToStftRequest,
209
236
  MfccRequest,
210
237
  MfccToAudioRequest,
211
238
  MfccToMelRequest,
239
+ ReassignedSpectrogramRequest,
212
240
  SpectrogramRequest,
213
241
  TrimRequest,
214
242
  } from './feature_spectrogram';
@@ -217,9 +245,11 @@ export {
217
245
  analyzeMelody,
218
246
  analyzeSections,
219
247
  bassChroma,
248
+ chirp,
220
249
  chroma,
221
250
  chromaCens,
222
251
  chromaCqt,
252
+ clicks,
223
253
  cqt,
224
254
  cqtToAudio,
225
255
  cyclicTempogram,
@@ -236,6 +266,7 @@ export {
236
266
  frameSignal,
237
267
  framesToSamples,
238
268
  framesToTime,
269
+ griffinLim,
239
270
  hpssWithResidual,
240
271
  hybridCqt,
241
272
  hzToMel,
@@ -243,6 +274,7 @@ export {
243
274
  hzToNote,
244
275
  lufs,
245
276
  lufsInterleaved,
277
+ melDelta,
246
278
  melSpectrogram,
247
279
  melToAudio,
248
280
  melToHz,
@@ -254,13 +286,16 @@ export {
254
286
  momentaryLufs,
255
287
  nnFilter,
256
288
  nnlsChroma,
289
+ noteSegments,
257
290
  noteToHz,
291
+ onsetBacktrack,
258
292
  onsetEnvelope,
259
293
  onsetStrengthMulti,
260
294
  padCenter,
261
295
  pcen,
262
296
  peakPick,
263
297
  phaseVocoder,
298
+ piptrack,
264
299
  pitchPyin,
265
300
  pitchTuning,
266
301
  pitchYin,
@@ -269,15 +304,24 @@ export {
269
304
  powerToDb,
270
305
  preemphasis,
271
306
  pseudoCqt,
307
+ reassignedSpectrogram,
272
308
  remix,
273
309
  resample,
274
310
  rmsEnergy,
275
311
  samplesToFrames,
312
+ segmentAgglomerative,
313
+ segmentCrossSimilarity,
314
+ segmentLagToRecurrence,
315
+ segmentPathEnhance,
316
+ segmentRecurrenceMatrix,
317
+ segmentRecurrenceToLag,
318
+ segmentSubsegment,
276
319
  shortTermLufs,
277
320
  spectralBandwidth,
278
321
  spectralCentroid,
279
322
  spectralContrast,
280
323
  spectralFlatness,
324
+ spectralFlux,
281
325
  spectralRolloff,
282
326
  splitSilence,
283
327
  stft,
@@ -285,6 +329,7 @@ export {
285
329
  tempogram,
286
330
  tempogramRatio,
287
331
  timeToFrames,
332
+ tone,
288
333
  tonnetz,
289
334
  trim,
290
335
  trimSilence,
@@ -361,13 +406,19 @@ export type {
361
406
  BuiltinSynthBinding,
362
407
  BuiltinSynthConfig,
363
408
  BuiltinSynthWaveform,
409
+ ExternalSeparatedStem,
410
+ ExternalSeparatedStemImportRequest,
411
+ ExternalSeparatedStemImportResult,
364
412
  MidiCcLearnOptions,
365
413
  ProjectAssistSidecar,
414
+ ProjectAssistSidecarInput,
366
415
  ProjectAutomationCurve,
367
416
  ProjectAutomationLaneDesc,
368
417
  ProjectAutomationPoint,
418
+ ProjectAutomationTargetKind,
369
419
  ProjectBounceOptions,
370
420
  ProjectChordSymbol,
421
+ ProjectClip,
371
422
  ProjectClipCompSegment,
372
423
  ProjectClipDesc,
373
424
  ProjectClipFade,
@@ -382,6 +433,8 @@ export type {
382
433
  ProjectMidiClipResult,
383
434
  ProjectMidiEvent,
384
435
  ProjectNotePairValidation,
436
+ ProjectSource,
437
+ ProjectTrack,
385
438
  ProjectTrackDesc,
386
439
  ProjectTrackKind,
387
440
  ProjectWarpAnchor,
@@ -401,8 +454,13 @@ export type {
401
454
  SynthPatch,
402
455
  } from './project';
403
456
  export {
457
+ AutomationTargetKind,
458
+ BUILTIN_SYNTH_WAVEFORMS,
404
459
  EXPECTED_PROJECT_ABI_VERSION,
405
460
  MarkerKind,
461
+ PROJECT_AUTOMATION_TARGET_OPAQUE,
462
+ PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
463
+ PROJECT_AUTOMATION_TARGET_TRACK_PAN,
406
464
  Project,
407
465
  projectAbiVersion,
408
466
  SYNTH_BODY_TYPES,
@@ -427,6 +485,7 @@ export type {
427
485
  AnalyzeTimbreOptions,
428
486
  AutomationCurve,
429
487
  Beat,
488
+ BpmHypothesis,
430
489
  Chord,
431
490
  ChordAnalysisResult,
432
491
  ChordDetectionOptions,
@@ -450,9 +509,11 @@ export type {
450
509
  MasteringChainConfig,
451
510
  MasteringChainResult,
452
511
  MasteringChainStereoResult,
512
+ MasteringLoudnessSummary,
453
513
  MasteringOptions,
454
514
  MasteringPreset,
455
515
  MasteringProcessorParams,
516
+ MasteringReport,
456
517
  MasteringResult,
457
518
  MasteringStereoChainResult,
458
519
  MasteringStereoResult,
@@ -466,10 +527,13 @@ export type {
466
527
  MixMeterSnapshot,
467
528
  MixOptions,
468
529
  MixResult,
530
+ NoteSegment,
469
531
  NoteStretchOptions,
470
532
  PairAnalysis,
471
533
  PairProcessor,
472
534
  PanLaw,
535
+ PanLawInput,
536
+ PanLawName,
473
537
  PanMode,
474
538
  PitchCorrectOptions,
475
539
  PitchResult,
@@ -483,8 +547,10 @@ export type {
483
547
  RoomGeometryOptions,
484
548
  RoomMorphOptions,
485
549
  Section,
550
+ SegmentMatrix,
486
551
  SendTiming,
487
552
  SoloProcessor,
553
+ SonareCapabilities,
488
554
  SpectralEditMode,
489
555
  SpectralEditOptions,
490
556
  SpectralEditWindow,
@@ -522,6 +588,7 @@ export type {
522
588
  DetectAcousticRequest,
523
589
  DetectChordsRequest,
524
590
  DetectKeyRequest,
591
+ DetectOnsetsRequest,
525
592
  DynamicsAnalysisResult,
526
593
  EstimateRoomRequest,
527
594
  MusicAnalyzeRequest,
@@ -560,6 +627,7 @@ export type {
560
627
  EngineBounceResult,
561
628
  EngineBus,
562
629
  EngineCapabilities,
630
+ EngineCaptureSource,
563
631
  EngineCaptureStatus,
564
632
  EngineClip,
565
633
  EngineFreezeOptions,
@@ -577,12 +645,19 @@ export type {
577
645
  EngineTempoSegment,
578
646
  EngineTimeSignatureSegment,
579
647
  EngineTrackLane,
648
+ EngineTrackMonitorMode,
580
649
  EngineTrackSend,
581
650
  EngineTransportState,
582
651
  ExternalMidiEvent,
583
652
  MidiCcBindOptions,
653
+ TrackMonitorMode,
654
+ } from './realtime_engine';
655
+ export {
656
+ ClipPageProvider,
657
+ EXPECTED_ENGINE_ABI_VERSION,
658
+ engineCapabilities,
659
+ RealtimeEngine,
584
660
  } from './realtime_engine';
585
- export { EXPECTED_ENGINE_ABI_VERSION, engineCapabilities, RealtimeEngine } from './realtime_engine';
586
661
  export { scaleCorrectionSemitones, scalePitchClassEnabled, scaleQuantizeMidi } from './scale';
587
662
  export type { ProgressCallback } from './sonare.js';
588
663
  export { StreamAnalyzer, streamAnalyzerConfigDefaults } from './stream_analyzer';
@@ -623,6 +698,13 @@ export type {
623
698
  WebMidiInputInfo,
624
699
  } from './web_midi';
625
700
  export { bindWebMidi, isWebMidiAvailable } from './web_midi';
701
+ export type {
702
+ OfflineWorker,
703
+ OfflineWorkerCallOptions,
704
+ OfflineWorkerClientOptions,
705
+ OfflineWorkerProgress,
706
+ } from './worker_client';
707
+ export { OfflineWorkerClient, OfflineWorkerTask } from './worker_client';
626
708
 
627
709
  /** Row-major 2-D matrix as a flat buffer plus its dimensions. */
628
710
  export type Matrix2dResult = WasmMatrix2dResult;
@@ -696,6 +778,26 @@ export function version(): string {
696
778
  return module.version();
697
779
  }
698
780
 
781
+ /**
782
+ * Return the capabilities of the loaded WASM build.
783
+ *
784
+ * This is synchronous and only describes the already-initialized module.
785
+ */
786
+ export function capabilities(): SonareCapabilities {
787
+ if (!module) {
788
+ throw new Error('Module not initialized. Call init() first.');
789
+ }
790
+ return module.capabilities();
791
+ }
792
+
793
+ /** Return the initialized module's processors, parameters, and presets. */
794
+ export function capabilityCatalog(): CapabilityCatalog {
795
+ if (!module) {
796
+ throw new Error('Module not initialized. Call init() first.');
797
+ }
798
+ return JSON.parse(module.capabilityCatalog()) as CapabilityCatalog;
799
+ }
800
+
699
801
  /**
700
802
  * Aggregate native ABI version: the per-subsystem ABI macros folded into one
701
803
  * 32-bit value. It bumps whenever any flat C POD layout changes, so callers can
@@ -735,6 +837,9 @@ const VOICE_PRESET_ORDINALS: readonly VoicePresetId[] = [
735
837
 
736
838
  function resolveVoicePresetOrdinal(preset: VoicePresetId | number): number {
737
839
  if (typeof preset === 'number') {
840
+ if (!Number.isSafeInteger(preset) || preset < 0 || preset >= VOICE_PRESET_ORDINALS.length) {
841
+ throw new RangeError(`Unknown voice-character preset ordinal: ${String(preset)}`);
842
+ }
738
843
  return preset;
739
844
  }
740
845
  const ordinal = VOICE_PRESET_ORDINALS.indexOf(preset);
@@ -746,23 +851,30 @@ function resolveVoicePresetOrdinal(preset: VoicePresetId | number): number {
746
851
 
747
852
  /**
748
853
  * Map a voice-character preset ordinal (or canonical id) to its canonical id
749
- * string (e.g. `'bright-idol'`). Returns `null` for an out-of-range ordinal.
854
+ * string (e.g. `'bright-idol'`). Unknown numeric ordinals return `null`;
855
+ * unknown preset ids throw.
750
856
  */
751
- export function voiceCharacterPresetId(preset: VoicePresetId | number): string | null {
857
+ export function voiceCharacterPresetId(preset: VoicePresetId | number): VoicePresetId | null {
752
858
  if (!module) {
753
859
  throw new Error('Module not initialized. Call init() first.');
754
860
  }
755
- return module.voiceCharacterPresetId(resolveVoicePresetOrdinal(preset));
861
+ if (
862
+ typeof preset === 'number' &&
863
+ (!Number.isSafeInteger(preset) || preset < 0 || preset >= VOICE_PRESET_ORDINALS.length)
864
+ ) {
865
+ return null;
866
+ }
867
+ return module.voiceCharacterPresetId(resolveVoicePresetOrdinal(preset)) as VoicePresetId;
756
868
  }
757
869
 
758
870
  /**
759
871
  * Return the canonical (normalized) flat POD config for a built-in voice
760
872
  * preset, skipping the JSON round-trip. Accepts a canonical preset id or its
761
- * integer ordinal. Returns `null` for an out-of-range ordinal.
873
+ * integer ordinal. Invalid ordinals throw.
762
874
  */
763
875
  export function realtimeVoiceChangerPresetConfig(
764
876
  preset: VoicePresetId | number,
765
- ): RealtimeVoiceChangerPodConfig | null {
877
+ ): RealtimeVoiceChangerPodConfig {
766
878
  if (!module) {
767
879
  throw new Error('Module not initialized. Call init() first.');
768
880
  }
@@ -22,6 +22,7 @@ export interface MasteringChainRequest {
22
22
  sampleRate?: number;
23
23
  config?: MasteringChainConfig;
24
24
  onProgress?: ProgressCallback;
25
+ cancel?: () => boolean;
25
26
  }
26
27
 
27
28
  export interface MasteringChainStereoRequest {
@@ -30,6 +31,7 @@ export interface MasteringChainStereoRequest {
30
31
  sampleRate?: number;
31
32
  config?: MasteringChainConfig;
32
33
  onProgress?: ProgressCallback;
34
+ cancel?: () => boolean;
33
35
  }
34
36
 
35
37
  /** Canonical request form for one-shot preset mastering. */
@@ -39,6 +41,7 @@ export interface MasterAudioRequest {
39
41
  preset?: MasteringPreset;
40
42
  overrides?: MasteringChainConfig;
41
43
  onProgress?: ProgressCallback;
44
+ cancel?: () => boolean;
42
45
  }
43
46
 
44
47
  /** Canonical request form for one-shot stereo preset mastering. */
@@ -49,6 +52,7 @@ export interface MasterAudioStereoRequest {
49
52
  preset?: MasteringPreset;
50
53
  overrides?: MasteringChainConfig;
51
54
  onProgress?: ProgressCallback;
55
+ cancel?: () => boolean;
52
56
  }
53
57
 
54
58
  function masterAudioRequest(
@@ -114,12 +118,13 @@ export function masteringChain(
114
118
  ): MasteringChainResult {
115
119
  const request =
116
120
  samples instanceof Float32Array ? { samples, sampleRate, config, onProgress } : samples;
117
- if (request.onProgress) {
121
+ if (request.onProgress || request.cancel) {
118
122
  return requireModule().masteringChainWithProgress(
119
123
  request.samples,
120
124
  request.sampleRate ?? 22050,
121
125
  canonicalChainConfig(request.config ?? {}),
122
- request.onProgress,
126
+ request.onProgress ?? (() => {}),
127
+ request.cancel ?? (() => false),
123
128
  );
124
129
  }
125
130
  return requireModule().masteringChain(
@@ -162,13 +167,14 @@ export function masteringChainStereo(
162
167
  if (request.left.length !== request.right.length) {
163
168
  throw new Error('Stereo channel lengths must match.');
164
169
  }
165
- if (request.onProgress) {
170
+ if (request.onProgress || request.cancel) {
166
171
  return requireModule().masteringChainStereoWithProgress(
167
172
  request.left,
168
173
  request.right,
169
174
  request.sampleRate ?? 22050,
170
175
  canonicalChainConfig(request.config ?? {}),
171
- request.onProgress,
176
+ request.onProgress ?? (() => {}),
177
+ request.cancel ?? (() => false),
172
178
  );
173
179
  }
174
180
  return requireModule().masteringChainStereo(
@@ -213,6 +219,7 @@ export function masteringChainWithProgress(
213
219
  request.sampleRate ?? 22050,
214
220
  canonicalChainConfig(request.config ?? {}),
215
221
  request.onProgress,
222
+ request.cancel ?? (() => false),
216
223
  );
217
224
  }
218
225
 
@@ -259,6 +266,7 @@ export function masteringChainStereoWithProgress(
259
266
  request.sampleRate ?? 22050,
260
267
  canonicalChainConfig(request.config ?? {}),
261
268
  request.onProgress,
269
+ request.cancel ?? (() => false),
262
270
  );
263
271
  }
264
272
 
@@ -298,13 +306,14 @@ export function masterAudio(
298
306
  ): MasteringChainResult {
299
307
  const request = masterAudioRequest(samples, sampleRate, presetName, overrides, onProgress);
300
308
  const flat = flattenChainConfig(request.overrides ?? {});
301
- if (request.onProgress) {
309
+ if (request.onProgress || request.cancel) {
302
310
  return requireModule().masterAudioWithProgress(
303
311
  request.preset ?? 'pop',
304
312
  request.samples,
305
313
  request.sampleRate ?? 22050,
306
314
  flat,
307
- request.onProgress,
315
+ request.onProgress ?? (() => {}),
316
+ request.cancel ?? (() => false),
308
317
  );
309
318
  }
310
319
  return requireModule().masterAudio(
@@ -355,14 +364,15 @@ export function masterAudioStereo(
355
364
  if (request.left.length !== request.right.length) {
356
365
  throw new Error('Stereo channel lengths must match.');
357
366
  }
358
- if (request.onProgress) {
367
+ if (request.onProgress || request.cancel) {
359
368
  return requireModule().masterAudioStereoWithProgress(
360
369
  request.preset ?? 'pop',
361
370
  request.left,
362
371
  request.right,
363
372
  request.sampleRate ?? 22050,
364
373
  flat,
365
- request.onProgress,
374
+ request.onProgress ?? (() => {}),
375
+ request.cancel ?? (() => false),
366
376
  );
367
377
  }
368
378
  return requireModule().masterAudioStereo(
@@ -405,6 +415,7 @@ export function masterAudioWithProgress(
405
415
  request.sampleRate ?? 22050,
406
416
  flattenChainConfig(request.overrides ?? {}),
407
417
  request.onProgress,
418
+ request.cancel ?? (() => false),
408
419
  );
409
420
  }
410
421
 
@@ -451,5 +462,6 @@ export function masterAudioStereoWithProgress(
451
462
  request.sampleRate ?? 22050,
452
463
  flattenChainConfig(request.overrides ?? {}),
453
464
  request.onProgress,
465
+ request.cancel ?? (() => false),
454
466
  );
455
467
  }
@@ -186,6 +186,9 @@ export type MasteringChannelPolicy =
186
186
  | 'perChannel'
187
187
  | 'passthrough';
188
188
 
189
+ /** Coarse algorithmic work estimate for a realtime insert; not a benchmark. */
190
+ export type MasteringRealtimeCost = 'low' | 'moderate' | 'high';
191
+
189
192
  /** One processor's realtime/offline/pair classification in the catalog. */
190
193
  export interface MasteringProcessorCatalogEntry {
191
194
  /** Processor id (the name used for scene inserts / named processors). */
@@ -210,6 +213,8 @@ export interface MasteringProcessorCatalogEntry {
210
213
  * offline, dry-only, and no-tail processors.
211
214
  */
212
215
  tailSamples: number;
216
+ /** Coarse realtime work estimate, or null when the processor is not an insert. */
217
+ realtimeCost: MasteringRealtimeCost | null;
213
218
  /**
214
219
  * How the mixer wraps the processor on a >2-channel (surround) bus insert:
215
220
  * "multichannel" (one full-buffer call) or "stereoPairOnly" (front L/R pair,
@@ -107,7 +107,13 @@ export function masteringDynamicsCompressor(
107
107
  typeof request.detector === 'string'
108
108
  ? COMPRESSOR_DETECTOR_MAP[request.detector]
109
109
  : request.detector;
110
- const opts: Record<string, unknown> = { ...request };
110
+ // Preserve inherited option values as well as own properties. The embind
111
+ // boundary's hasProperty() intentionally follows the prototype chain, so a
112
+ // spread copy here would otherwise erase values before C++ can read them.
113
+ const opts: Record<string, unknown> = Object.assign(
114
+ Object.create(Object.getPrototypeOf(request)) as Record<string, unknown>,
115
+ request,
116
+ );
111
117
  if (detector !== undefined) {
112
118
  opts.detector = detector;
113
119
  }
package/src/metering.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ErrorCode, SonareError } from './errors';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type { ValidateOptions } from './validation';
3
4
  import { assertSamples } from './validation';
@@ -17,7 +18,11 @@ function assertOversampleFactor(fnName: string, factor: number): void {
17
18
  normalized > 16 ||
18
19
  (normalized & (normalized - 1)) !== 0
19
20
  ) {
20
- throw new RangeError(`${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`);
21
+ throw new SonareError(
22
+ ErrorCode.InvalidParameter,
23
+ 'InvalidParameter',
24
+ `${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`,
25
+ );
21
26
  }
22
27
  }
23
28
 
package/src/mixer.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  MeterTap,
13
13
  MixerProcessResult,
14
14
  MixMeterSnapshot,
15
- PanLaw,
15
+ PanLawInput,
16
16
  PanMode,
17
17
  SendTiming,
18
18
  SurroundPan,
@@ -336,8 +336,8 @@ export class Mixer {
336
336
  this.mixer.setPolarityInvert(stripIndex, invertLeft, invertRight);
337
337
  }
338
338
 
339
- /** Set the strip's pan law. */
340
- setPanLaw(stripIndex: number, panLaw: PanLaw | number): void {
339
+ /** Set the strip's pan law (a {@link PanLawName} alias or raw C ABI ordinal). */
340
+ setPanLaw(stripIndex: number, panLaw: PanLawInput): void {
341
341
  this.mixer.setPanLaw(stripIndex, panLawCode(panLaw));
342
342
  }
343
343
 
@@ -8,12 +8,20 @@ export interface OpfsClipPageProviderOptions {
8
8
  dataOffsetBytes?: number;
9
9
  worker?: Worker;
10
10
  terminateWorkerOnClose?: boolean;
11
+ /** Internal bridge hook used to mirror a supplied page into an AudioWorklet. */
12
+ onPageSupplied?: (pageIndex: number, channels: Float32Array[]) => void;
13
+ /** Internal bridge hook used to mirror an eviction into an AudioWorklet. */
14
+ onPageCleared?: (pageIndex: number) => void;
15
+ /** Internal bridge hook called after the local provider is destroyed. */
16
+ onClose?: () => void;
11
17
  }
12
18
 
13
19
  export interface OpfsClipPageProviderBinding {
14
20
  provider: ClipPageProvider;
15
21
  supplyPage(pageIndex: number): Promise<boolean>;
16
22
  supplyRequest(request: ClipPageRequest): Promise<boolean>;
23
+ /** Evict one resident page from every configured consumer. */
24
+ clearPage?(pageIndex: number): void;
17
25
  close(): void;
18
26
  }
19
27
 
@@ -116,7 +124,15 @@ self.onmessage = async (event) => {
116
124
 
117
125
  export function createOpfsClipPageWorker(): Worker {
118
126
  const blob = new Blob([opfsClipPageWorkerSource], { type: 'text/javascript' });
119
- return new Worker(URL.createObjectURL(blob));
127
+ const url = URL.createObjectURL(blob);
128
+ try {
129
+ return new Worker(url);
130
+ } finally {
131
+ // Worker construction retains the script URL internally; keeping this
132
+ // object URL alive after construction leaks one browser registration per
133
+ // attach/close cycle.
134
+ URL.revokeObjectURL(url);
135
+ }
120
136
  }
121
137
 
122
138
  export function createOpfsClipPageProvider(
@@ -166,6 +182,7 @@ export function createOpfsClipPageProvider(
166
182
  }
167
183
  try {
168
184
  provider.supply(response.pageIndex, channels);
185
+ options.onPageSupplied?.(response.pageIndex, channels);
169
186
  } catch {
170
187
  entry.resolve(false);
171
188
  return;
@@ -218,6 +235,13 @@ export function createOpfsClipPageProvider(
218
235
  supplyRequest(request: ClipPageRequest) {
219
236
  return supplyPage(Math.floor(request.sample / options.pageFrames));
220
237
  },
238
+ clearPage(pageIndex: number) {
239
+ if (closed) {
240
+ return;
241
+ }
242
+ provider.clear(pageIndex);
243
+ options.onPageCleared?.(pageIndex);
244
+ },
221
245
  close() {
222
246
  if (closed) {
223
247
  return;
@@ -229,6 +253,7 @@ export function createOpfsClipPageProvider(
229
253
  }
230
254
  pending.clear();
231
255
  provider.destroy();
256
+ options.onClose?.();
232
257
  if (ownsWorker) {
233
258
  worker.terminate();
234
259
  }
package/src/project.ts CHANGED
@@ -10,13 +10,19 @@ export type {
10
10
  BuiltinSynthBinding,
11
11
  BuiltinSynthConfig,
12
12
  BuiltinSynthWaveform,
13
+ ExternalSeparatedStem,
14
+ ExternalSeparatedStemImportRequest,
15
+ ExternalSeparatedStemImportResult,
13
16
  MidiCcLearnOptions,
14
17
  ProjectAssistSidecar,
18
+ ProjectAssistSidecarInput,
15
19
  ProjectAutomationCurve,
16
20
  ProjectAutomationLaneDesc,
17
21
  ProjectAutomationPoint,
22
+ ProjectAutomationTargetKind,
18
23
  ProjectBounceOptions,
19
24
  ProjectChordSymbol,
25
+ ProjectClip,
20
26
  ProjectClipCompSegment,
21
27
  ProjectClipDesc,
22
28
  ProjectClipFade,
@@ -37,8 +43,10 @@ export type {
37
43
  ProjectMidiRouteConfig,
38
44
  ProjectMidiRouteResult,
39
45
  ProjectNotePairValidation,
46
+ ProjectSource,
40
47
  ProjectTempoSegment,
41
48
  ProjectTimeSignatureSegment,
49
+ ProjectTrack,
42
50
  ProjectTrackDesc,
43
51
  ProjectTrackKind,
44
52
  ProjectWarpAnchor,
@@ -59,8 +67,13 @@ export type {
59
67
  SynthPatch,
60
68
  } from './project_types';
61
69
  export {
70
+ AutomationTargetKind,
71
+ BUILTIN_SYNTH_WAVEFORMS,
62
72
  EXPECTED_PROJECT_ABI_VERSION,
63
73
  MarkerKind,
74
+ PROJECT_AUTOMATION_TARGET_OPAQUE,
75
+ PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
76
+ PROJECT_AUTOMATION_TARGET_TRACK_PAN,
64
77
  SYNTH_BODY_TYPES,
65
78
  SYNTH_ENGINE_MODES,
66
79
  SYNTH_FILTER_MODELS,