@libraz/libsonare 1.5.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +109 -20
  2. package/dist/analysis.d.ts +5730 -0
  3. package/dist/analysis.js +2464 -0
  4. package/dist/analysis.js.map +1 -0
  5. package/dist/index.d.ts +873 -63
  6. package/dist/index.js +1037 -265
  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 +707 -0
  16. package/dist/worker.js.map +1 -0
  17. package/dist/worklet.d.ts +596 -86
  18. package/dist/worklet.js +1658 -235
  19. package/dist/worklet.js.map +1 -1
  20. package/package.json +23 -11
  21. package/src/_chain_config.ts +5 -23
  22. package/src/analysis.ts +137 -0
  23. package/src/analysis_helpers.ts +29 -0
  24. package/src/audio.ts +5 -3
  25. package/src/clip_page_streamer.ts +62 -8
  26. package/src/codes.ts +51 -64
  27. package/src/effects_mastering.ts +1 -0
  28. package/src/effects_transform.ts +12 -6
  29. package/src/errors.ts +1 -0
  30. package/src/feature_core.ts +159 -3
  31. package/src/feature_music.ts +35 -16
  32. package/src/feature_pitch.ts +75 -6
  33. package/src/feature_spectral.ts +234 -8
  34. package/src/feature_spectrogram.ts +186 -5
  35. package/src/features.ts +16 -1
  36. package/src/index.ts +104 -7
  37. package/src/mastering_chain.ts +20 -8
  38. package/src/mastering_core.ts +7 -0
  39. package/src/mastering_dynamics.ts +7 -1
  40. package/src/metering.ts +51 -5
  41. package/src/mixer.ts +14 -1
  42. package/src/opfs_clip_pages.ts +26 -1
  43. package/src/project.ts +7 -0
  44. package/src/project_class.ts +74 -13
  45. package/src/project_internal.ts +24 -29
  46. package/src/project_types.ts +69 -10
  47. package/src/public_types.ts +68 -0
  48. package/src/public_types_mastering.ts +28 -0
  49. package/src/public_types_mixing.ts +11 -2
  50. package/src/public_types_music.ts +13 -0
  51. package/src/public_types_spectral.ts +43 -3
  52. package/src/quick_analysis.ts +48 -12
  53. package/src/realtime_engine.ts +162 -4
  54. package/src/realtime_voice_changer.ts +51 -77
  55. package/src/sonare-analysis.js.d.ts +8 -0
  56. package/src/sonare.js.d.ts +305 -9
  57. package/src/streaming_processors.ts +22 -1
  58. package/src/validation.ts +6 -0
  59. package/src/web_midi.ts +36 -4
  60. package/src/worker.ts +196 -0
  61. package/src/worker_client.ts +352 -0
  62. package/src/worker_protocol.ts +59 -0
  63. package/src/worklet/engine-automation.ts +2 -1
  64. package/src/worklet/engine-capture-facade.ts +13 -4
  65. package/src/worklet/engine-clips.ts +12 -3
  66. package/src/worklet/engine-node.ts +212 -4
  67. package/src/worklet/engine-offline.ts +7 -2
  68. package/src/worklet/engine-parameter-facade.ts +26 -5
  69. package/src/worklet/engine-processor.ts +383 -42
  70. package/src/worklet/engine-register.ts +8 -0
  71. package/src/worklet/engine-strips.ts +17 -1
  72. package/src/worklet/engine-sync.ts +2 -6
  73. package/src/worklet/engine-tempo-facade.ts +8 -6
  74. package/src/worklet/engine.ts +380 -13
  75. package/src/worklet/guards.ts +22 -1
  76. package/src/worklet/messages.ts +141 -9
  77. package/src/worklet/protocol.ts +303 -17
  78. package/src/worklet/voice-changer-processor.ts +12 -15
  79. package/src/worklet.ts +20 -1
@@ -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,6 +10,9 @@ export type {
10
10
  BuiltinSynthBinding,
11
11
  BuiltinSynthConfig,
12
12
  BuiltinSynthWaveform,
13
+ ExternalSeparatedStem,
14
+ ExternalSeparatedStemImportRequest,
15
+ ExternalSeparatedStemImportResult,
13
16
  MidiCcLearnOptions,
14
17
  ProjectAssistSidecar,
15
18
  ProjectAutomationCurve,
@@ -17,6 +20,7 @@ export type {
17
20
  ProjectAutomationPoint,
18
21
  ProjectBounceOptions,
19
22
  ProjectChordSymbol,
23
+ ProjectClip,
20
24
  ProjectClipCompSegment,
21
25
  ProjectClipDesc,
22
26
  ProjectClipFade,
@@ -37,8 +41,10 @@ export type {
37
41
  ProjectMidiRouteConfig,
38
42
  ProjectMidiRouteResult,
39
43
  ProjectNotePairValidation,
44
+ ProjectSource,
40
45
  ProjectTempoSegment,
41
46
  ProjectTimeSignatureSegment,
47
+ ProjectTrack,
42
48
  ProjectTrackDesc,
43
49
  ProjectTrackKind,
44
50
  ProjectWarpAnchor,
@@ -59,6 +65,7 @@ export type {
59
65
  SynthPatch,
60
66
  } from './project_types';
61
67
  export {
68
+ BUILTIN_SYNTH_WAVEFORMS,
62
69
  EXPECTED_PROJECT_ABI_VERSION,
63
70
  MarkerKind,
64
71
  SYNTH_BODY_TYPES,
@@ -9,12 +9,15 @@ import {
9
9
  } from './project_internal';
10
10
  import type {
11
11
  BuiltinSynthBinding,
12
+ ExternalSeparatedStemImportRequest,
13
+ ExternalSeparatedStemImportResult,
12
14
  MidiCcLearnOptions,
13
15
  ProjectAssistSidecar,
14
16
  ProjectAutomationLaneDesc,
15
17
  ProjectAutomationPoint,
16
18
  ProjectBounceOptions,
17
19
  ProjectChordSymbol,
20
+ ProjectClip,
18
21
  ProjectClipCompSegment,
19
22
  ProjectClipDesc,
20
23
  ProjectClipFade,
@@ -32,9 +35,11 @@ import type {
32
35
  ProjectMidiRouteConfig,
33
36
  ProjectMidiRouteResult,
34
37
  ProjectNotePairValidation,
38
+ ProjectSource,
35
39
  ProjectTempoCandidate,
36
40
  ProjectTempoSegment,
37
41
  ProjectTimeSignatureSegment,
42
+ ProjectTrack,
38
43
  ProjectTrackDesc,
39
44
  ProjectTrackKind,
40
45
  ProjectWarpMapDesc,
@@ -337,6 +342,19 @@ export class Project {
337
342
  return this.native.addClip(desc);
338
343
  }
339
344
 
345
+ /** Import host-separated PCM through the normal audio-track/clip path. */
346
+ importExternalStems(
347
+ request: ExternalSeparatedStemImportRequest,
348
+ ): ExternalSeparatedStemImportResult {
349
+ return this.native.importExternalStems({
350
+ ...request,
351
+ stems: request.stems.map((stem) => ({
352
+ ...stem,
353
+ layout: stem.layout === 'mono' ? 1 : stem.layout === 'stereo' ? 2 : stem.layout,
354
+ })),
355
+ });
356
+ }
357
+
340
358
  /** Split captured loop-recording audio into takes and add one clip. */
341
359
  addLoopRecordingTakes(desc: ProjectLoopRecordingDesc): ProjectLoopRecordingResult {
342
360
  return this.native.addLoopRecordingTakes(desc);
@@ -391,7 +409,11 @@ export class Project {
391
409
  * Route a track's MIDI to host-instrument `destinationId` (0 = default). The
392
410
  * compiler stamps every MIDI clip on the track with this id so the engine
393
411
  * dispatches its events to the instrument registered for that destination.
394
- * Routes through an undoable edit command.
412
+ * Routes through an undoable edit command. Builtin, NativeSynth, and SF2
413
+ * instruments retain source-track provenance inside a shared destination
414
+ * voice pool. With only zero-latency instruments bound, live lanes and
415
+ * channel-strip bounces remain aligned. Configure one live lane per source
416
+ * track that needs strip processing.
395
417
  */
396
418
  setTrackMidiDestination(trackId: number, destinationId: number): void {
397
419
  this.native.setTrackMidiDestination(trackId, destinationId);
@@ -449,7 +471,11 @@ export class Project {
449
471
  this.native.setMidiEvents(clipId, events);
450
472
  }
451
473
 
452
- /** Import an in-memory SMF buffer; returns the first added clip id. */
474
+ /**
475
+ * Import an in-memory SMF buffer; returns the first added clip id.
476
+ * Malformed or partially truncated tracks are rejected instead of installing
477
+ * a silently shortened clip.
478
+ */
453
479
  importSmf(data: Uint8Array): number {
454
480
  return this.native.importSmf(data);
455
481
  }
@@ -498,7 +524,10 @@ export class Project {
498
524
  this.native.setProgramOnChannel(clipId, group, channel, program, bank);
499
525
  }
500
526
 
501
- /** Destructively bake a MIDI-FX chain into a clip's stored MIDI events. */
527
+ /**
528
+ * Destructively bake a MIDI-FX chain into all stored events. Large clips are
529
+ * drained without truncation; failure leaves the original clip unchanged.
530
+ */
502
531
  bakeMidiFx(clipId: number, configJson: string): void {
503
532
  this.native.bakeMidiFx(clipId, configJson);
504
533
  }
@@ -510,9 +539,9 @@ export class Project {
510
539
 
511
540
  /**
512
541
  * Pre-flight check for hanging / unmatched notes in a MIDI clip: reports
513
- * whether every note-on has a matching note-off (FIFO per channel+note).
514
- * Useful before bouncing to catch a stuck note. Throws if `clipId` is unknown
515
- * or not a MIDI clip.
542
+ * whether every note-on in the exported half-open playback window has a
543
+ * matching note-off (FIFO per group+channel+note). Useful before bouncing to
544
+ * catch a stuck note. Throws if `clipId` is unknown or not a MIDI clip.
516
545
  */
517
546
  validateMidiNotes(clipId: number): ProjectNotePairValidation {
518
547
  return this.native.validateMidiNotes(clipId);
@@ -678,6 +707,21 @@ export class Project {
678
707
  this.native.setClipFade(clipId, fadeIn, fadeOut);
679
708
  }
680
709
 
710
+ /** Audio source ids that need decoded PCM after deserialization. */
711
+ unresolvedAudioSourceIds(): number[] {
712
+ return this.native.unresolvedAudioSourceIds();
713
+ }
714
+
715
+ /** Register decoded interleaved PCM for an existing audio source (undoable). */
716
+ setSourceAudio(
717
+ sourceId: number,
718
+ audio: Float32Array,
719
+ channels: number,
720
+ sampleRate: number,
721
+ ): void {
722
+ this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
723
+ }
724
+
681
725
  /** Replace a clip's take list and active take id (undoable). */
682
726
  setClipTakes(clipId: number, takes: ReadonlyArray<ProjectClipTake>, activeTakeId = 0): void {
683
727
  this.native.setClipTakes(clipId, takes, activeTakeId);
@@ -732,7 +776,7 @@ export class Project {
732
776
  this.native.setTrackRoute(trackId, channelStripRef ?? '', outputTarget ?? '');
733
777
  }
734
778
 
735
- /** Append an automation lane to a track; returns the lane index (undoable). */
779
+ /** Append an automation lane; returns its stable target parameter id (undoable). */
736
780
  addAutomationLane(trackId: number, desc: ProjectAutomationLaneDesc): number {
737
781
  return this.native.addAutomationLane(trackId, {
738
782
  targetParamId: desc.targetParamId,
@@ -740,17 +784,21 @@ export class Project {
740
784
  });
741
785
  }
742
786
 
743
- /** Replace an existing automation lane in place (undoable). */
744
- editAutomationLane(trackId: number, laneIndex: number, desc: ProjectAutomationLaneDesc): void {
745
- this.native.editAutomationLane(trackId, laneIndex, {
787
+ /** Replace the lane identified by its stable target parameter id (undoable). */
788
+ editAutomationLane(
789
+ trackId: number,
790
+ targetParamId: number,
791
+ desc: ProjectAutomationLaneDesc,
792
+ ): void {
793
+ this.native.editAutomationLane(trackId, targetParamId, {
746
794
  targetParamId: desc.targetParamId,
747
795
  points: desc.points,
748
796
  });
749
797
  }
750
798
 
751
- /** Remove an automation lane from a track (undoable). */
752
- removeAutomationLane(trackId: number, laneIndex: number): void {
753
- this.native.removeAutomationLane(trackId, laneIndex);
799
+ /** Remove the lane identified by its stable target parameter id (undoable). */
800
+ removeAutomationLane(trackId: number, targetParamId: number): void {
801
+ this.native.removeAutomationLane(trackId, targetParamId);
754
802
  }
755
803
 
756
804
  /** Replace the project's key annotation stream (undoable). */
@@ -834,6 +882,19 @@ export class Project {
834
882
  return this.native.markerByIndex(index);
835
883
  }
836
884
 
885
+ /** Read a stored project track by 0-based index. */
886
+ trackByIndex(index: number): ProjectTrack {
887
+ return this.native.trackByIndex(index);
888
+ }
889
+ /** Read a stored project clip by 0-based index. */
890
+ clipByIndex(index: number): ProjectClip {
891
+ return this.native.clipByIndex(index);
892
+ }
893
+ /** Read a stored project source by 0-based index. */
894
+ sourceByIndex(index: number): ProjectSource {
895
+ return this.native.sourceByIndex(index);
896
+ }
897
+
837
898
  /** Number of markers in the project. */
838
899
  markerCount(): number {
839
900
  return this.native.markerCount();
@@ -1,3 +1,4 @@
1
+ import { resolveEnumOrdinal } from './codes';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type {
3
4
  BuiltinSynthBinding,
@@ -5,6 +6,7 @@ import type {
5
6
  ProjectAutomationPoint,
6
7
  ProjectBounceOptions,
7
8
  ProjectChordSymbol,
9
+ ProjectClip,
8
10
  ProjectClipCompSegment,
9
11
  ProjectClipDesc,
10
12
  ProjectClipFade,
@@ -21,9 +23,11 @@ import type {
21
23
  ProjectMidiRouteConfig,
22
24
  ProjectMidiRouteResult,
23
25
  ProjectNotePairValidation,
26
+ ProjectSource,
24
27
  ProjectTempoCandidate,
25
28
  ProjectTempoSegment,
26
29
  ProjectTimeSignatureSegment,
30
+ ProjectTrack,
27
31
  ProjectTrackKind,
28
32
  ProjectWarpMapDesc,
29
33
  ProjectWarpMode,
@@ -41,6 +45,7 @@ export interface WasmProject {
41
45
  setSampleRate: (sampleRate: number) => void;
42
46
  addTrack: (desc: { kind?: number | string; name?: string }) => number;
43
47
  addClip: (desc: ProjectClipDesc) => number;
48
+ importExternalStems: (request: unknown) => { trackIds: number[]; clipIds: number[] };
44
49
  addLoopRecordingTakes: (desc: ProjectLoopRecordingDesc) => ProjectLoopRecordingResult;
45
50
  addMidiClip: (startPpq: number, lengthPpq: number) => ProjectMidiClipResult;
46
51
  splitClip: (clipId: number, splitPpq: number) => number;
@@ -108,6 +113,13 @@ export interface WasmProject {
108
113
  removeClip: (clipId: number) => void;
109
114
  setClipGain: (clipId: number, gain: number) => void;
110
115
  setClipFade: (clipId: number, fadeIn: ProjectClipFade, fadeOut: ProjectClipFade) => void;
116
+ unresolvedAudioSourceIds: () => number[];
117
+ setSourceAudio: (
118
+ sourceId: number,
119
+ audio: Float32Array,
120
+ channels: number,
121
+ sampleRate: number,
122
+ ) => void;
111
123
  setClipTakes: (
112
124
  clipId: number,
113
125
  takes: ReadonlyArray<ProjectClipTake>,
@@ -131,10 +143,10 @@ export interface WasmProject {
131
143
  ) => number;
132
144
  editAutomationLane: (
133
145
  trackId: number,
134
- laneIndex: number,
146
+ targetParamId: number,
135
147
  desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
136
148
  ) => void;
137
- removeAutomationLane: (trackId: number, laneIndex: number) => void;
149
+ removeAutomationLane: (trackId: number, targetParamId: number) => void;
138
150
  annotateKeys: (keys: ReadonlyArray<ProjectKeySegment>) => void;
139
151
  annotateChords: (chords: ReadonlyArray<ProjectChordSymbol>) => void;
140
152
  setAssistSidecar: (
@@ -154,6 +166,9 @@ export interface WasmProject {
154
166
  setMarker: (markerId: number, ppq: number, name: string) => number;
155
167
  setMarkerEx: (marker: ProjectMarker) => number;
156
168
  markerByIndex: (index: number) => ProjectMarker;
169
+ trackByIndex: (index: number) => ProjectTrack;
170
+ clipByIndex: (index: number) => ProjectClip;
171
+ sourceByIndex: (index: number) => ProjectSource;
157
172
  markerCount: () => number;
158
173
  trackCount: () => number;
159
174
  clipCount: () => number;
@@ -307,37 +322,17 @@ export function assertProjectMidiEvents(
307
322
  }
308
323
 
309
324
  export function projectTrackKindValue(kind: ProjectTrackKind | undefined): number {
310
- if (kind === undefined || kind === 'audio') {
311
- return 0;
312
- }
313
- if (kind === 'midi') {
314
- return 1;
315
- }
316
- if (kind === 'aux') {
317
- return 2;
318
- }
319
- return kind;
325
+ return resolveEnumOrdinal(kind ?? 'audio', { audio: 0, midi: 1, aux: 2 }, 'project track kind');
320
326
  }
321
327
 
322
328
  export function projectWarpModeValue(mode: ProjectWarpMode | undefined): number {
323
- if (mode === undefined || mode === 'off') {
324
- return 0;
325
- }
326
- if (mode === 'repitch') {
327
- return 1;
328
- }
329
- if (mode === 'tempo-sync') {
330
- return 2;
331
- }
332
- return mode;
329
+ return resolveEnumOrdinal(
330
+ mode ?? 'off',
331
+ { off: 0, repitch: 1, 'tempo-sync': 2 },
332
+ 'project warp mode',
333
+ );
333
334
  }
334
335
 
335
336
  export function projectLoopModeValue(mode: ProjectLoopMode | undefined): number {
336
- if (mode === undefined || mode === 'off') {
337
- return 0;
338
- }
339
- if (mode === 'loop') {
340
- return 1;
341
- }
342
- return mode;
337
+ return resolveEnumOrdinal(mode ?? 'off', { off: 0, loop: 1 }, 'project loop mode');
343
338
  }
@@ -25,6 +25,29 @@ export interface ProjectBounceOptions {
25
25
  instrumentLatencySamples?: number;
26
26
  }
27
27
 
28
+ /** One decoded output from a host-owned source-separation model. */
29
+ export interface ExternalSeparatedStem {
30
+ name: string;
31
+ /** Optional host metadata; it does not change DSP. */
32
+ role?: string;
33
+ layout: 'mono' | 'stereo' | 1 | 2;
34
+ planarSamples: Float32Array[];
35
+ /** Absolute project-rate frame at which this stem begins. */
36
+ startFrame?: number;
37
+ }
38
+
39
+ /** Request for {@link Project.importExternalStems}. */
40
+ export interface ExternalSeparatedStemImportRequest {
41
+ sampleRate: number;
42
+ stems: ExternalSeparatedStem[];
43
+ }
44
+
45
+ /** Ids of the normal tracks and clips created by the import. */
46
+ export interface ExternalSeparatedStemImportResult {
47
+ trackIds: number[];
48
+ clipIds: number[];
49
+ }
50
+
28
51
  /**
29
52
  * Marker kind ordinals. Mirrors `SonareMarkerKind` in `src/sonare_c_types.h`;
30
53
  * the values are part of the ABI and must not be renumbered.
@@ -53,17 +76,47 @@ export interface ProjectMarker {
53
76
  keyMinor?: boolean;
54
77
  }
55
78
 
79
+ /** Read-only stored project track returned by {@link Project.trackByIndex}. */
80
+ export interface ProjectTrack {
81
+ id: number;
82
+ kind: number;
83
+ midiDestinationId: number;
84
+ gain: number;
85
+ pan: number;
86
+ mute: boolean;
87
+ solo: boolean;
88
+ name: string;
89
+ }
90
+
91
+ /** Read-only stored project clip returned by {@link Project.clipByIndex}. */
92
+ export interface ProjectClip {
93
+ id: number;
94
+ trackId: number;
95
+ sourceId: number;
96
+ sourceKind: number;
97
+ startPpq: number;
98
+ lengthPpq: number;
99
+ sourceOffsetPpq: number;
100
+ gain: number;
101
+ loopMode: number;
102
+ loopLengthPpq: number;
103
+ }
104
+
105
+ /** Read-only stored project source returned by {@link Project.sourceByIndex}. */
106
+ export interface ProjectSource {
107
+ id: number;
108
+ kind: number;
109
+ channelCount: number;
110
+ storageHandleId: number;
111
+ sampleRateHint: number;
112
+ nameOrUri: string;
113
+ }
114
+
115
+ /** Names accepted by the minimal built-in oscillator synth. */
116
+ export const BUILTIN_SYNTH_WAVEFORMS = ['sine', 'saw', 'sawtooth', 'square', 'triangle'] as const;
117
+
56
118
  /** Oscillator waveform for the built-in synth. */
57
- export type BuiltinSynthWaveform =
58
- | 'sine'
59
- | 'saw'
60
- | 'sawtooth'
61
- | 'square'
62
- | 'triangle'
63
- | 0
64
- | 1
65
- | 2
66
- | 3;
119
+ export type BuiltinSynthWaveform = (typeof BUILTIN_SYNTH_WAVEFORMS)[number] | 0 | 1 | 2 | 3;
67
120
 
68
121
  /**
69
122
  * Built-in synth patch + MIDI routing for
@@ -111,6 +164,8 @@ export interface Sf2InstrumentConfig {
111
164
  gain?: number;
112
165
  /** Max simultaneous voices (0 => 48, clamped to [1, 64]). */
113
166
  polyphony?: number;
167
+ /** Prefer dedicated physical models for covered melodic GM programs. Defaults to false; drums stay SF2-first. */
168
+ preferModelForModeledFamilies?: boolean;
114
169
  }
115
170
 
116
171
  /** Source backend a resolved MIDI program renders through. */
@@ -198,6 +253,7 @@ export const SYNTH_MOD_DESTINATIONS = [
198
253
  export interface SynthEnumTables {
199
254
  engineModes: string[];
200
255
  waveforms: string[];
256
+ builtinWaveforms: string[];
201
257
  filterModels: string[];
202
258
  filterOutputs: string[];
203
259
  bodyTypes: string[];
@@ -312,8 +368,11 @@ export type ProjectFadeCurve =
312
368
  | 'equal-power'
313
369
  | 'equal_power'
314
370
  | 'equalPower'
371
+ | 'equalpower'
315
372
  | 'exponential'
373
+ | 'exp'
316
374
  | 'logarithmic'
375
+ | 'log'
317
376
  | 0
318
377
  | 1
319
378
  | 2
@@ -4,3 +4,71 @@ export * from './public_types_mixing';
4
4
  export * from './public_types_music';
5
5
  export * from './public_types_realtime';
6
6
  export * from './public_types_spectral';
7
+
8
+ /** Runtime capabilities reported by the loaded libsonare build. */
9
+ export interface SonareCapabilities {
10
+ version: string;
11
+ abi: {
12
+ project: number;
13
+ engine: number;
14
+ };
15
+ platform: string;
16
+ features: {
17
+ mastering: boolean;
18
+ mixing: boolean;
19
+ fx: boolean;
20
+ ffmpeg: boolean;
21
+ };
22
+ decode: {
23
+ builtin: string[];
24
+ ffmpeg: string[];
25
+ };
26
+ simd: string;
27
+ hardwareConcurrency: number;
28
+ }
29
+
30
+ /** One parameter descriptor in the cross-surface capability catalog. */
31
+ export interface CapabilityCatalogParameter {
32
+ name: string;
33
+ id: number;
34
+ rtSafe: boolean;
35
+ type: 'boolean' | 'number';
36
+ min: number | null;
37
+ max: number | null;
38
+ default: boolean | number | null;
39
+ unit: string | null;
40
+ }
41
+
42
+ /** One named mastering processor and its host-facing capabilities. */
43
+ export interface CapabilityCatalogProcessor {
44
+ id: string;
45
+ kind: 'realtime' | 'offline' | 'pair';
46
+ realtimeInsertable: boolean;
47
+ stereoOnly: boolean;
48
+ latencySamples: number;
49
+ tailSamples: number;
50
+ /** Coarse realtime work estimate, or null when the processor is not an insert. */
51
+ realtimeCost: 'low' | 'moderate' | 'high' | null;
52
+ channelPolicy: 'multichannel' | 'stereoPairOnly' | 'perChannel' | 'passthrough';
53
+ category: string;
54
+ params: CapabilityCatalogParameter[];
55
+ }
56
+
57
+ /** Built-in preset names grouped by feature family. */
58
+ export interface CapabilityCatalogPresets {
59
+ mastering: string[];
60
+ synth: string[];
61
+ mixingScene: string[];
62
+ voiceChanger: string[];
63
+ }
64
+
65
+ /** Complete runtime catalog exposed by {@link capabilityCatalog}. */
66
+ export interface CapabilityCatalog {
67
+ version: string;
68
+ abi: SonareCapabilities['abi'];
69
+ processors: CapabilityCatalogProcessor[];
70
+ presets: CapabilityCatalogPresets;
71
+ }
72
+
73
+ /** Synchronous progress callback for offline operations. Its return value is ignored. */
74
+ export type ProgressCallback = (progress: number, stage: string) => void;
@@ -138,6 +138,8 @@ export interface MasteringResult {
138
138
  inputLufs: number;
139
139
  outputLufs: number;
140
140
  appliedGainDb: number;
141
+ /** True when peak headroom prevented the requested LUFS target. */
142
+ loudnessTargetLimited?: boolean;
141
143
  latencySamples?: number;
142
144
  }
143
145
 
@@ -329,6 +331,7 @@ export interface MasteringChainConfig {
329
331
  monoMaker?: {
330
332
  enabled?: boolean;
331
333
  amount?: number;
334
+ frequencyHz?: number;
332
335
  };
333
336
  };
334
337
  maximizer?: {
@@ -391,6 +394,26 @@ export interface StageGainReduction {
391
394
  gainReductionDb: number;
392
395
  }
393
396
 
397
+ /** Existing EBU R128 measurements captured before or after mastering. */
398
+ export interface MasteringLoudnessSummary {
399
+ integratedLufs: number;
400
+ maxMomentaryLufs: number;
401
+ maxShortTermLufs: number;
402
+ truePeakDbtp: number;
403
+ loudnessRange: number;
404
+ }
405
+
406
+ /** Compact explanation of how an offline mastering chain changed a program. */
407
+ export interface MasteringReport {
408
+ before: MasteringLoudnessSummary;
409
+ after: MasteringLoudnessSummary;
410
+ appliedGainDb: number;
411
+ maxGainReductionDb: number;
412
+ loudnessTargetLimited: boolean;
413
+ /** 32 logarithmically-spaced after-minus-before spectral energy deltas (dB). */
414
+ bandEnergyDeltaDb: Float32Array;
415
+ }
416
+
394
417
  export interface MasteringChainResult {
395
418
  /** Latency-compensated offline output; no separate latency field is reported. */
396
419
  samples: Float32Array;
@@ -407,8 +430,11 @@ export interface MasteringChainResult {
407
430
  outputTruePeakDbtp: number;
408
431
  /** EBU Tech 3342 Loudness Range of the output (LU). */
409
432
  outputLra: number;
433
+ /** True when peak headroom prevented the requested LUFS target. */
434
+ loudnessTargetLimited: boolean;
410
435
  /** Per-stage gain reductions for the dynamics/maximizer stages (a subset of `stages`). */
411
436
  stageGainReductions: StageGainReduction[];
437
+ report: MasteringReport;
412
438
  }
413
439
 
414
440
  export interface MasteringChainStereoResult {
@@ -422,7 +448,9 @@ export interface MasteringChainStereoResult {
422
448
  /** See {@link MasteringChainResult} for field semantics. */
423
449
  outputTruePeakDbtp: number;
424
450
  outputLra: number;
451
+ loudnessTargetLimited: boolean;
425
452
  stageGainReductions: StageGainReduction[];
453
+ report: MasteringReport;
426
454
  }
427
455
 
428
456
  /**
@@ -1,4 +1,11 @@
1
- export type PanMode = 'balance' | 'stereoPan' | 'stereo-pan' | 'dualPan' | 'dual-pan' | number;
1
+ export type PanMode =
2
+ | 'balance'
3
+ | 'pan'
4
+ | 'stereoPan'
5
+ | 'stereo-pan'
6
+ | 'dualPan'
7
+ | 'dual-pan'
8
+ | number;
2
9
 
3
10
  /**
4
11
  * Surround pan position for a strip feeding a >2-channel bus. Phase 1 honors
@@ -77,7 +84,9 @@ export type AutomationCurve = 'linear' | 'exponential' | 'hold' | 's-curve';
77
84
 
78
85
  /**
79
86
  * Pan law applied when computing left/right gains from a pan position
80
- * (see {@link Mixer.setPanLaw}). Maps to the underlying integer code.
87
+ * (see {@link Mixer.setPanLaw}). On mono strips it changes the centre gain;
88
+ * on stereo Balance strips it changes only the far-channel taper, while centre
89
+ * remains unity. Maps to the underlying integer code.
81
90
  */
82
91
  export type PanLaw = 'const3dB' | 'const4.5dB' | 'const6dB' | 'linear0dB';
83
92