@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
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  assertProjectMidiEvents,
3
+ projectAutomationTargetKindValue,
3
4
  projectLoopModeValue,
4
5
  projectMidi1Event,
5
6
  projectModule,
@@ -9,12 +10,17 @@ import {
9
10
  } from './project_internal';
10
11
  import type {
11
12
  BuiltinSynthBinding,
13
+ ExternalSeparatedStemImportRequest,
14
+ ExternalSeparatedStemImportResult,
12
15
  MidiCcLearnOptions,
13
16
  ProjectAssistSidecar,
17
+ ProjectAssistSidecarInput,
14
18
  ProjectAutomationLaneDesc,
15
19
  ProjectAutomationPoint,
20
+ ProjectAutomationTargetKind,
16
21
  ProjectBounceOptions,
17
22
  ProjectChordSymbol,
23
+ ProjectClip,
18
24
  ProjectClipCompSegment,
19
25
  ProjectClipDesc,
20
26
  ProjectClipFade,
@@ -32,9 +38,11 @@ import type {
32
38
  ProjectMidiRouteConfig,
33
39
  ProjectMidiRouteResult,
34
40
  ProjectNotePairValidation,
41
+ ProjectSource,
35
42
  ProjectTempoCandidate,
36
43
  ProjectTempoSegment,
37
44
  ProjectTimeSignatureSegment,
45
+ ProjectTrack,
38
46
  ProjectTrackDesc,
39
47
  ProjectTrackKind,
40
48
  ProjectWarpMapDesc,
@@ -44,6 +52,41 @@ import type {
44
52
  SynthPatch,
45
53
  } from './project_types';
46
54
 
55
+ function validateAssistSidecarUint32(value: unknown, field: string): number {
56
+ if (
57
+ typeof value !== 'number' ||
58
+ !Number.isSafeInteger(value) ||
59
+ value < 0 ||
60
+ value > 0xffff_ffff
61
+ ) {
62
+ throw new RangeError(`Project.setAssistSidecar: ${field} must be a uint32`);
63
+ }
64
+ return value;
65
+ }
66
+
67
+ function validateAssistSidecarPpq(value: unknown, field: string): number {
68
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
69
+ throw new RangeError(
70
+ `Project.setAssistSidecar: ${field} must be a finite, non-negative number`,
71
+ );
72
+ }
73
+ return value;
74
+ }
75
+
76
+ function validateAssistSidecarPayload(value: unknown): Uint8Array {
77
+ if (!(value instanceof Uint8Array)) {
78
+ throw new TypeError('Project.setAssistSidecar: payload must be a Uint8Array');
79
+ }
80
+ return value;
81
+ }
82
+
83
+ function validateAssistSidecarModuleId(value: unknown): string {
84
+ if (typeof value !== 'string' || value.length === 0) {
85
+ throw new TypeError('Project.setAssistSidecar: moduleId must be a non-empty string');
86
+ }
87
+ return value;
88
+ }
89
+
47
90
  /**
48
91
  * Headless DAW project (control-thread-only arrangement model).
49
92
  *
@@ -77,6 +120,11 @@ export class Project {
77
120
  this.native = new (projectModule().Project)();
78
121
  }
79
122
 
123
+ /** Create a new empty project. */
124
+ static create(): Project {
125
+ return new Project();
126
+ }
127
+
80
128
  /** Pack a MIDI 1.0 note-on event accepted by {@link setMidiEvents}. */
81
129
  static midiNoteOn(
82
130
  ppq: number,
@@ -337,6 +385,19 @@ export class Project {
337
385
  return this.native.addClip(desc);
338
386
  }
339
387
 
388
+ /** Import host-separated PCM through the normal audio-track/clip path. */
389
+ importExternalStems(
390
+ request: ExternalSeparatedStemImportRequest,
391
+ ): ExternalSeparatedStemImportResult {
392
+ return this.native.importExternalStems({
393
+ ...request,
394
+ stems: request.stems.map((stem) => ({
395
+ ...stem,
396
+ layout: stem.layout === 'mono' ? 1 : stem.layout === 'stereo' ? 2 : stem.layout,
397
+ })),
398
+ });
399
+ }
400
+
340
401
  /** Split captured loop-recording audio into takes and add one clip. */
341
402
  addLoopRecordingTakes(desc: ProjectLoopRecordingDesc): ProjectLoopRecordingResult {
342
403
  return this.native.addLoopRecordingTakes(desc);
@@ -391,7 +452,11 @@ export class Project {
391
452
  * Route a track's MIDI to host-instrument `destinationId` (0 = default). The
392
453
  * compiler stamps every MIDI clip on the track with this id so the engine
393
454
  * dispatches its events to the instrument registered for that destination.
394
- * Routes through an undoable edit command.
455
+ * Routes through an undoable edit command. Builtin, NativeSynth, and SF2
456
+ * instruments retain source-track provenance inside a shared destination
457
+ * voice pool. With only zero-latency instruments bound, live lanes and
458
+ * channel-strip bounces remain aligned. Configure one live lane per source
459
+ * track that needs strip processing.
395
460
  */
396
461
  setTrackMidiDestination(trackId: number, destinationId: number): void {
397
462
  this.native.setTrackMidiDestination(trackId, destinationId);
@@ -440,6 +505,19 @@ export class Project {
440
505
  this.native.setMaxUndoDepth(depth);
441
506
  }
442
507
 
508
+ /** Set the combined undo/redo history byte cap. Zero disables retention. */
509
+ setMaxHistoryBytes(bytes: number): void {
510
+ if (typeof bytes !== 'number') {
511
+ throw new TypeError('Project.setMaxHistoryBytes: bytes must be a number');
512
+ }
513
+ if (!Number.isFinite(bytes) || !Number.isInteger(bytes) || bytes < 0 || bytes > 0xffff_ffff) {
514
+ throw new RangeError(
515
+ 'Project.setMaxHistoryBytes: bytes must be a finite integer in the uint32 range',
516
+ );
517
+ }
518
+ this.native.setMaxHistoryBytes(bytes);
519
+ }
520
+
443
521
  /** Replace a MIDI clip's entire event list. */
444
522
  setMidiEvents(
445
523
  clipId: number,
@@ -603,8 +681,11 @@ export class Project {
603
681
  * Karplus-Strong / modal / additive / percussion / extended-waveguide-piano
604
682
  * engines plus the realism layer). Pass a {@link SynthPatch}, a preset-name
605
683
  * string (`'saw-lead'` / `'va:saw-lead'`; see {@link synthPresetNames}), or
606
- * an array of either; each object entry may carry a `destinationId` binding
607
- * convenience (default 0), which is not part of the NativeSynth patch itself.
684
+ * an array of either; each object entry may carry `destinationId` (default
685
+ * 0) and `useGmPrograms` (default `false`) binding conveniences, neither of
686
+ * which is part of the NativeSynth patch itself. When enabled, MIDI program
687
+ * changes select the corresponding General MIDI voice while the patch remains
688
+ * the fallback.
608
689
  * Because the parameter defaults to `{}`, omission and explicit `undefined`
609
690
  * both create one default binding. Use an explicitly empty array `[]` (or
610
691
  * runtime `null`) for zero bindings. Unknown preset names throw.
@@ -685,6 +766,26 @@ export class Project {
685
766
  this.native.setClipFade(clipId, fadeIn, fadeOut);
686
767
  }
687
768
 
769
+ /** Audio source ids that need decoded PCM after deserialization. */
770
+ unresolvedAudioSourceIds(): number[] {
771
+ return this.native.unresolvedAudioSourceIds();
772
+ }
773
+
774
+ /** Register decoded interleaved PCM for an existing audio source (undoable). */
775
+ setSourceAudio(
776
+ sourceId: number,
777
+ audio: Float32Array,
778
+ channels: number,
779
+ sampleRate: number,
780
+ ): void {
781
+ this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
782
+ }
783
+
784
+ /** Replace an audio source's metadata strings as one undoable edit. */
785
+ setAudioSourceMetadata(sourceId: number, contentHash: string, externalStemRole: string): void {
786
+ this.native.setAudioSourceMetadata(sourceId, contentHash, externalStemRole);
787
+ }
788
+
688
789
  /** Replace a clip's take list and active take id (undoable). */
689
790
  setClipTakes(clipId: number, takes: ReadonlyArray<ProjectClipTake>, activeTakeId = 0): void {
690
791
  this.native.setClipTakes(clipId, takes, activeTakeId);
@@ -739,25 +840,41 @@ export class Project {
739
840
  this.native.setTrackRoute(trackId, channelStripRef ?? '', outputTarget ?? '');
740
841
  }
741
842
 
742
- /** Append an automation lane to a track; returns the lane index (undoable). */
843
+ /** Append an automation lane; returns its stable target parameter id (undoable). */
743
844
  addAutomationLane(trackId: number, desc: ProjectAutomationLaneDesc): number {
744
- return this.native.addAutomationLane(trackId, {
745
- targetParamId: desc.targetParamId,
746
- points: desc.points,
747
- });
845
+ if (desc.targetParamId === 0) {
846
+ throw new RangeError('project automation lane targetParamId must be non-zero');
847
+ }
848
+ const nativeDesc: ProjectAutomationLaneDesc = { ...desc };
849
+ if (Object.keys(desc).includes('targetKind')) {
850
+ nativeDesc.targetKind = projectAutomationTargetKindValue(
851
+ desc.targetKind as ProjectAutomationTargetKind,
852
+ ) as ProjectAutomationTargetKind;
853
+ }
854
+ return this.native.addAutomationLane(trackId, nativeDesc);
748
855
  }
749
856
 
750
- /** Replace an existing automation lane in place (undoable). */
751
- editAutomationLane(trackId: number, laneIndex: number, desc: ProjectAutomationLaneDesc): void {
752
- this.native.editAutomationLane(trackId, laneIndex, {
753
- targetParamId: desc.targetParamId,
754
- points: desc.points,
755
- });
857
+ /** Replace the lane identified by its stable target parameter id (undoable). */
858
+ editAutomationLane(
859
+ trackId: number,
860
+ targetParamId: number,
861
+ desc: ProjectAutomationLaneDesc,
862
+ ): void {
863
+ if (desc.targetParamId === 0) {
864
+ throw new RangeError('project automation lane targetParamId must be non-zero');
865
+ }
866
+ const nativeDesc: ProjectAutomationLaneDesc = { ...desc };
867
+ if (Object.keys(desc).includes('targetKind')) {
868
+ nativeDesc.targetKind = projectAutomationTargetKindValue(
869
+ desc.targetKind as ProjectAutomationTargetKind,
870
+ ) as ProjectAutomationTargetKind;
871
+ }
872
+ this.native.editAutomationLane(trackId, targetParamId, nativeDesc);
756
873
  }
757
874
 
758
- /** Remove an automation lane from a track (undoable). */
759
- removeAutomationLane(trackId: number, laneIndex: number): void {
760
- this.native.removeAutomationLane(trackId, laneIndex);
875
+ /** Remove the lane identified by its stable target parameter id (undoable). */
876
+ removeAutomationLane(trackId: number, targetParamId: number): void {
877
+ this.native.removeAutomationLane(trackId, targetParamId);
761
878
  }
762
879
 
763
880
  /** Replace the project's key annotation stream (undoable). */
@@ -770,7 +887,14 @@ export class Project {
770
887
  this.native.annotateChords(chords);
771
888
  }
772
889
 
773
- /** Add or update an opaque assist sidecar by module id + target scope (undoable). */
890
+ /**
891
+ * Add or update an opaque assist sidecar via an undoable edit.
892
+ *
893
+ * The descriptor form is the canonical API and matches the Node binding;
894
+ * the positional form remains available for compatibility with the original
895
+ * WASM facade.
896
+ */
897
+ setAssistSidecar(sidecar: ProjectAssistSidecarInput): void;
774
898
  setAssistSidecar(
775
899
  moduleId: string,
776
900
  schemaVersion: number,
@@ -778,14 +902,52 @@ export class Project {
778
902
  regionStartPpq: number,
779
903
  regionEndPpq: number,
780
904
  payload: Uint8Array,
905
+ ): void;
906
+ setAssistSidecar(
907
+ sidecarOrModuleId: ProjectAssistSidecarInput | string,
908
+ schemaVersion?: number,
909
+ targetTrackId?: number,
910
+ regionStartPpq?: number,
911
+ regionEndPpq?: number,
912
+ payload?: Uint8Array,
781
913
  ): void {
914
+ if (typeof sidecarOrModuleId === 'string') {
915
+ if (
916
+ schemaVersion === undefined ||
917
+ targetTrackId === undefined ||
918
+ regionStartPpq === undefined ||
919
+ regionEndPpq === undefined ||
920
+ payload === undefined
921
+ ) {
922
+ throw new TypeError('Project.setAssistSidecar: positional form requires six arguments');
923
+ }
924
+ this.native.setAssistSidecar(
925
+ validateAssistSidecarModuleId(sidecarOrModuleId),
926
+ validateAssistSidecarUint32(schemaVersion, 'schemaVersion'),
927
+ validateAssistSidecarUint32(targetTrackId, 'targetTrackId'),
928
+ validateAssistSidecarPpq(regionStartPpq, 'regionStartPpq'),
929
+ validateAssistSidecarPpq(regionEndPpq, 'regionEndPpq'),
930
+ validateAssistSidecarPayload(payload),
931
+ );
932
+ return;
933
+ }
934
+
935
+ if (
936
+ sidecarOrModuleId === null ||
937
+ typeof sidecarOrModuleId !== 'object' ||
938
+ Array.isArray(sidecarOrModuleId)
939
+ ) {
940
+ throw new TypeError('Project.setAssistSidecar: expected a sidecar descriptor object');
941
+ }
942
+ const sidecar = sidecarOrModuleId;
943
+ const moduleId = validateAssistSidecarModuleId(sidecar.moduleId);
782
944
  this.native.setAssistSidecar(
783
945
  moduleId,
784
- schemaVersion,
785
- targetTrackId,
786
- regionStartPpq,
787
- regionEndPpq,
788
- payload,
946
+ validateAssistSidecarUint32(sidecar.schemaVersion ?? 0, 'schemaVersion'),
947
+ validateAssistSidecarUint32(sidecar.targetTrackId ?? 0, 'targetTrackId'),
948
+ validateAssistSidecarPpq(sidecar.regionStartPpq ?? 0, 'regionStartPpq'),
949
+ validateAssistSidecarPpq(sidecar.regionEndPpq ?? 0, 'regionEndPpq'),
950
+ validateAssistSidecarPayload(sidecar.payload ?? new Uint8Array()),
789
951
  );
790
952
  }
791
953
 
@@ -799,6 +961,12 @@ export class Project {
799
961
  return this.native.getAssistSidecar(index);
800
962
  }
801
963
 
964
+ /** Read every stored assist sidecar in the same order as the index getter. */
965
+ assistSidecars(): ProjectAssistSidecar[] {
966
+ const count = this.assistSidecarCount();
967
+ return Array.from({ length: count }, (_, index) => this.getAssistSidecar(index));
968
+ }
969
+
802
970
  /** Set the project's clip-overlap policy (SonareProjectOverlapPolicy ordinal). */
803
971
  setOverlapPolicy(policy: number): void {
804
972
  this.native.setOverlapPolicy(policy);
@@ -841,6 +1009,19 @@ export class Project {
841
1009
  return this.native.markerByIndex(index);
842
1010
  }
843
1011
 
1012
+ /** Read a stored project track by 0-based index. */
1013
+ trackByIndex(index: number): ProjectTrack {
1014
+ return this.native.trackByIndex(index);
1015
+ }
1016
+ /** Read a stored project clip by 0-based index. */
1017
+ clipByIndex(index: number): ProjectClip {
1018
+ return this.native.clipByIndex(index);
1019
+ }
1020
+ /** Read a stored project source by 0-based index. */
1021
+ sourceByIndex(index: number): ProjectSource {
1022
+ return this.native.sourceByIndex(index);
1023
+ }
1024
+
844
1025
  /** Number of markers in the project. */
845
1026
  markerCount(): number {
846
1027
  return this.native.markerCount();
@@ -1,10 +1,14 @@
1
+ import { resolveEnumOrdinal } from './codes';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type {
3
4
  BuiltinSynthBinding,
4
5
  ProjectAssistSidecar,
6
+ ProjectAutomationLaneDesc,
5
7
  ProjectAutomationPoint,
8
+ ProjectAutomationTargetKind,
6
9
  ProjectBounceOptions,
7
10
  ProjectChordSymbol,
11
+ ProjectClip,
8
12
  ProjectClipCompSegment,
9
13
  ProjectClipDesc,
10
14
  ProjectClipFade,
@@ -21,9 +25,11 @@ import type {
21
25
  ProjectMidiRouteConfig,
22
26
  ProjectMidiRouteResult,
23
27
  ProjectNotePairValidation,
28
+ ProjectSource,
24
29
  ProjectTempoCandidate,
25
30
  ProjectTempoSegment,
26
31
  ProjectTimeSignatureSegment,
32
+ ProjectTrack,
27
33
  ProjectTrackKind,
28
34
  ProjectWarpMapDesc,
29
35
  ProjectWarpMode,
@@ -41,6 +47,7 @@ export interface WasmProject {
41
47
  setSampleRate: (sampleRate: number) => void;
42
48
  addTrack: (desc: { kind?: number | string; name?: string }) => number;
43
49
  addClip: (desc: ProjectClipDesc) => number;
50
+ importExternalStems: (request: unknown) => { trackIds: number[]; clipIds: number[] };
44
51
  addLoopRecordingTakes: (desc: ProjectLoopRecordingDesc) => ProjectLoopRecordingResult;
45
52
  addMidiClip: (startPpq: number, lengthPpq: number) => ProjectMidiClipResult;
46
53
  splitClip: (clipId: number, splitPpq: number) => number;
@@ -60,6 +67,7 @@ export interface WasmProject {
60
67
  redo: () => void;
61
68
  clearHistory: () => void;
62
69
  setMaxUndoDepth: (depth: number) => void;
70
+ setMaxHistoryBytes: (bytes: number) => void;
63
71
  setMidiEvents: (
64
72
  clipId: number,
65
73
  events: ReadonlyArray<ProjectMidiEvent | readonly [number, number, number]>,
@@ -108,6 +116,14 @@ export interface WasmProject {
108
116
  removeClip: (clipId: number) => void;
109
117
  setClipGain: (clipId: number, gain: number) => void;
110
118
  setClipFade: (clipId: number, fadeIn: ProjectClipFade, fadeOut: ProjectClipFade) => void;
119
+ unresolvedAudioSourceIds: () => number[];
120
+ setSourceAudio: (
121
+ sourceId: number,
122
+ audio: Float32Array,
123
+ channels: number,
124
+ sampleRate: number,
125
+ ) => void;
126
+ setAudioSourceMetadata: (sourceId: number, contentHash: string, externalStemRole: string) => void;
111
127
  setClipTakes: (
112
128
  clipId: number,
113
129
  takes: ReadonlyArray<ProjectClipTake>,
@@ -125,16 +141,13 @@ export interface WasmProject {
125
141
  removeTrack: (trackId: number) => void;
126
142
  renameTrack: (trackId: number, name: string) => void;
127
143
  setTrackRoute: (trackId: number, channelStripRef: string, outputTarget: string) => void;
128
- addAutomationLane: (
129
- trackId: number,
130
- desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
131
- ) => number;
144
+ addAutomationLane: (trackId: number, desc: ProjectAutomationLaneDesc) => number;
132
145
  editAutomationLane: (
133
146
  trackId: number,
134
- laneIndex: number,
135
- desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
147
+ targetParamId: number,
148
+ desc: ProjectAutomationLaneDesc,
136
149
  ) => void;
137
- removeAutomationLane: (trackId: number, laneIndex: number) => void;
150
+ removeAutomationLane: (trackId: number, targetParamId: number) => void;
138
151
  annotateKeys: (keys: ReadonlyArray<ProjectKeySegment>) => void;
139
152
  annotateChords: (chords: ReadonlyArray<ProjectChordSymbol>) => void;
140
153
  setAssistSidecar: (
@@ -154,6 +167,9 @@ export interface WasmProject {
154
167
  setMarker: (markerId: number, ppq: number, name: string) => number;
155
168
  setMarkerEx: (marker: ProjectMarker) => number;
156
169
  markerByIndex: (index: number) => ProjectMarker;
170
+ trackByIndex: (index: number) => ProjectTrack;
171
+ clipByIndex: (index: number) => ProjectClip;
172
+ sourceByIndex: (index: number) => ProjectSource;
157
173
  markerCount: () => number;
158
174
  trackCount: () => number;
159
175
  clipCount: () => number;
@@ -307,37 +323,25 @@ export function assertProjectMidiEvents(
307
323
  }
308
324
 
309
325
  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;
326
+ return resolveEnumOrdinal(kind ?? 'audio', { audio: 0, midi: 1, aux: 2 }, 'project track kind');
327
+ }
328
+
329
+ export function projectAutomationTargetKindValue(kind: ProjectAutomationTargetKind): number {
330
+ return resolveEnumOrdinal(
331
+ kind,
332
+ { opaque: 0, 'track-fader-db': 1, 'track-pan': 2 },
333
+ 'project automation target kind',
334
+ );
320
335
  }
321
336
 
322
337
  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;
338
+ return resolveEnumOrdinal(
339
+ mode ?? 'off',
340
+ { off: 0, repitch: 1, 'tempo-sync': 2 },
341
+ 'project warp mode',
342
+ );
333
343
  }
334
344
 
335
345
  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;
346
+ return resolveEnumOrdinal(mode ?? 'off', { off: 0, loop: 1 }, 'project loop mode');
343
347
  }
@@ -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,51 @@ 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
+ /** Owning content hash for audio sources; empty for MIDI sources. */
114
+ contentHash: string;
115
+ /** External source-separation role for audio sources; empty for MIDI sources. */
116
+ externalStemRole: string;
117
+ }
118
+
119
+ /** Names accepted by the minimal built-in oscillator synth. */
120
+ export const BUILTIN_SYNTH_WAVEFORMS = ['sine', 'saw', 'sawtooth', 'square', 'triangle'] as const;
121
+
56
122
  /** 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;
123
+ export type BuiltinSynthWaveform = (typeof BUILTIN_SYNTH_WAVEFORMS)[number] | 0 | 1 | 2 | 3;
67
124
 
68
125
  /**
69
126
  * Built-in synth patch + MIDI routing for
@@ -111,6 +168,8 @@ export interface Sf2InstrumentConfig {
111
168
  gain?: number;
112
169
  /** Max simultaneous voices (0 => 48, clamped to [1, 64]). */
113
170
  polyphony?: number;
171
+ /** Prefer dedicated physical models for covered melodic GM programs. Defaults to false; drums stay SF2-first. */
172
+ preferModelForModeledFamilies?: boolean;
114
173
  }
115
174
 
116
175
  /** Source backend a resolved MIDI program renders through. */
@@ -198,6 +257,7 @@ export const SYNTH_MOD_DESTINATIONS = [
198
257
  export interface SynthEnumTables {
199
258
  engineModes: string[];
200
259
  waveforms: string[];
260
+ builtinWaveforms: string[];
201
261
  filterModels: string[];
202
262
  filterOutputs: string[];
203
263
  bodyTypes: string[];
@@ -258,6 +318,8 @@ export interface SynthPatch {
258
318
  * `(destination_id, patch)` bindings instead. Defaults to `0`.
259
319
  */
260
320
  destinationId?: number;
321
+ /** Resolve MIDI channels from incoming GM bank/program changes; defaults to false. */
322
+ useGmPrograms?: boolean;
261
323
  /** Base preset name (see {@link synthPresetNames}); omit for the init patch. */
262
324
  preset?: string;
263
325
  engineMode?: SynthEngineMode | number;
@@ -312,8 +374,11 @@ export type ProjectFadeCurve =
312
374
  | 'equal-power'
313
375
  | 'equal_power'
314
376
  | 'equalPower'
377
+ | 'equalpower'
315
378
  | 'exponential'
379
+ | 'exp'
316
380
  | 'logarithmic'
381
+ | 'log'
317
382
  | 0
318
383
  | 1
319
384
  | 2
@@ -389,12 +454,34 @@ export interface ProjectAutomationPoint {
389
454
  curve?: ProjectAutomationCurve;
390
455
  }
391
456
 
457
+ /**
458
+ * Persistent target classification for an automation lane.
459
+ *
460
+ * The numeric ordinals mirror `SonareAutomationTargetKind`; the string names
461
+ * are the public WASM spellings accepted by the project facade.
462
+ */
463
+ export const AutomationTargetKind = {
464
+ opaque: 0,
465
+ trackFaderDb: 1,
466
+ trackPan: 2,
467
+ } as const;
468
+
469
+ /** Accepted automation target kind name or ABI ordinal. */
470
+ export type ProjectAutomationTargetKind = 'opaque' | 'track-fader-db' | 'track-pan' | 0 | 1 | 2;
471
+
472
+ /** Numeric aliases matching the C-ABI enum names. */
473
+ export const PROJECT_AUTOMATION_TARGET_OPAQUE = AutomationTargetKind.opaque;
474
+ export const PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB = AutomationTargetKind.trackFaderDb;
475
+ export const PROJECT_AUTOMATION_TARGET_TRACK_PAN = AutomationTargetKind.trackPan;
476
+
392
477
  /** Automation-lane descriptor for {@link Project.addAutomationLane}. */
393
478
  export interface ProjectAutomationLaneDesc {
394
- /** Host-defined id of the parameter the lane drives. */
479
+ /** Host-defined, non-zero id of the parameter the lane drives (zero is reserved). */
395
480
  targetParamId: number;
396
481
  /** Breakpoints (stored verbatim). */
397
482
  points: ReadonlyArray<ProjectAutomationPoint>;
483
+ /** Optional persistent target classification; omission retains the legacy opaque route. */
484
+ targetKind?: ProjectAutomationTargetKind;
398
485
  }
399
486
 
400
487
  /** One tempo segment for {@link Project.setTempoSegments}. */
@@ -456,6 +543,22 @@ export interface ProjectChordSymbol {
456
543
  modulationBoundary?: boolean;
457
544
  }
458
545
 
546
+ /** Descriptor accepted by {@link Project.setAssistSidecar}. */
547
+ export interface ProjectAssistSidecarInput {
548
+ /** Non-empty module id key. */
549
+ moduleId: string;
550
+ /** Module-defined schema version. Defaults to `0`. */
551
+ schemaVersion?: number;
552
+ /** Target track id (`0` = project scope). Defaults to `0`. */
553
+ targetTrackId?: number;
554
+ /** Region start in PPQ. Defaults to `0`. */
555
+ regionStartPpq?: number;
556
+ /** Region end in PPQ. Defaults to `0`. */
557
+ regionEndPpq?: number;
558
+ /** Opaque module-owned payload bytes. Defaults to an empty payload. */
559
+ payload?: Uint8Array;
560
+ }
561
+
459
562
  /** Assist sidecar snapshot returned by {@link Project.getAssistSidecar}. */
460
563
  export interface ProjectAssistSidecar {
461
564
  moduleId: string;