@libraz/libsonare 1.6.0 → 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 (47) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis.d.ts +88 -9
  3. package/dist/analysis.js +152 -42
  4. package/dist/analysis.js.map +1 -1
  5. package/dist/index.d.ts +129 -24
  6. package/dist/index.js +455 -140
  7. package/dist/index.js.map +1 -1
  8. package/dist/schemas/realtime-voice-changer-preset.schema.json +2 -2
  9. package/dist/sonare-analysis.js +1 -1
  10. package/dist/sonare-analysis.wasm +0 -0
  11. package/dist/sonare.js +1 -1
  12. package/dist/sonare.wasm +0 -0
  13. package/dist/worker.js +56 -29
  14. package/dist/worker.js.map +1 -1
  15. package/dist/worklet.d.ts +62 -10
  16. package/dist/worklet.js +193 -42
  17. package/dist/worklet.js.map +1 -1
  18. package/package.json +3 -3
  19. package/src/analysis_helpers.ts +34 -29
  20. package/src/audio.ts +7 -2
  21. package/src/codes.ts +47 -11
  22. package/src/effects_transform.ts +179 -10
  23. package/src/feature_music.ts +8 -1
  24. package/src/feature_spectral.ts +64 -2
  25. package/src/feature_spectrogram.ts +44 -3
  26. package/src/index.ts +21 -3
  27. package/src/mixer.ts +3 -3
  28. package/src/project.ts +6 -0
  29. package/src/project_class.ts +143 -16
  30. package/src/project_internal.ts +14 -5
  31. package/src/project_types.ts +45 -1
  32. package/src/public_types_mixing.ts +16 -0
  33. package/src/public_types_realtime.ts +23 -5
  34. package/src/quick_analysis.ts +21 -3
  35. package/src/realtime_engine.ts +14 -3
  36. package/src/sonare.js.d.ts +64 -0
  37. package/src/stream_analyzer.ts +6 -1
  38. package/src/web_midi.ts +10 -7
  39. package/src/worklet/engine-node.ts +54 -19
  40. package/src/worklet/engine-parameter-facade.ts +30 -1
  41. package/src/worklet/engine-processor.ts +52 -20
  42. package/src/worklet/engine-register.ts +1 -1
  43. package/src/worklet/engine-strips.ts +2 -2
  44. package/src/worklet/engine.ts +10 -0
  45. package/src/worklet/guards.ts +99 -7
  46. package/src/worklet/messages.ts +41 -1
  47. package/src/worklet/protocol.ts +4 -0
@@ -113,6 +113,8 @@ export interface TrimRequest extends GuardedOptions {
113
113
  samples: Float32Array;
114
114
  sampleRate: number;
115
115
  thresholdDb?: number;
116
+ frameLength?: number;
117
+ hopLength?: number;
116
118
  }
117
119
 
118
120
  function validateSpectrogramSamples(
@@ -183,19 +185,58 @@ export function trim(
183
185
  thresholdDb?: number,
184
186
  options?: GuardedOptions,
185
187
  ): Float32Array;
188
+ export function trim(
189
+ samples: Float32Array,
190
+ sampleRate: number,
191
+ thresholdDb?: number,
192
+ frameLength?: number,
193
+ hopLength?: number,
194
+ options?: GuardedOptions,
195
+ ): Float32Array;
186
196
  export function trim(
187
197
  samples: Float32Array | TrimRequest,
188
198
  sampleRate = 22050,
189
199
  thresholdDb = -60.0,
200
+ frameLengthOrOptions?: number | GuardedOptions,
201
+ hopLength?: number,
190
202
  options: GuardedOptions = {},
191
203
  ): Float32Array {
192
204
  if (!(samples instanceof Float32Array)) {
193
205
  const r = samples;
194
- return trim(r.samples, r.sampleRate, r.thresholdDb, r);
206
+ return trim(r.samples, r.sampleRate, r.thresholdDb, r.frameLength, r.hopLength, r);
207
+ }
208
+ if (frameLengthOrOptions === null) {
209
+ throw new TypeError('trim: frameLength must be an integer or options object');
195
210
  }
196
- validateSpectrogramSamples('trim', samples, sampleRate, options);
211
+ if (
212
+ frameLengthOrOptions !== undefined &&
213
+ typeof frameLengthOrOptions !== 'number' &&
214
+ typeof frameLengthOrOptions !== 'object'
215
+ ) {
216
+ throw new TypeError('trim: frameLength must be an integer or options object');
217
+ }
218
+ const positionalOptions =
219
+ typeof frameLengthOrOptions === 'object' && frameLengthOrOptions !== null
220
+ ? frameLengthOrOptions
221
+ : options;
222
+ const positionalFrameLength =
223
+ typeof frameLengthOrOptions === 'number' ? frameLengthOrOptions : undefined;
224
+ const resolvedFrameLength = positionalFrameLength ?? 2048;
225
+ const resolvedHopLength = hopLength === undefined ? 512 : hopLength;
226
+ validateSpectrogramSamples('trim', samples, sampleRate, positionalOptions);
197
227
  assertFiniteScalar('trim', thresholdDb, 'thresholdDb');
198
- return requireModule().trim(samples, sampleRate, thresholdDb);
228
+ assertPositiveInteger('trim', resolvedFrameLength, 'frameLength');
229
+ assertPositiveInteger('trim', resolvedHopLength, 'hopLength');
230
+ if (resolvedFrameLength > 2 ** 31 - 1 || resolvedHopLength > 2 ** 31 - 1) {
231
+ throw new RangeError('trim: frameLength and hopLength must fit in a signed 32-bit integer');
232
+ }
233
+ return requireModule().trimEx(
234
+ samples,
235
+ sampleRate,
236
+ thresholdDb,
237
+ resolvedFrameLength,
238
+ resolvedHopLength,
239
+ );
199
240
  }
200
241
 
201
242
  // ============================================================================
package/src/index.ts CHANGED
@@ -146,6 +146,7 @@ export {
146
146
  export type {
147
147
  HarmonicRequest,
148
148
  HpssRequest,
149
+ NormalizeMode,
149
150
  NormalizeRequest,
150
151
  NoteMoveRequest,
151
152
  NoteStretchRequest,
@@ -410,9 +411,11 @@ export type {
410
411
  ExternalSeparatedStemImportResult,
411
412
  MidiCcLearnOptions,
412
413
  ProjectAssistSidecar,
414
+ ProjectAssistSidecarInput,
413
415
  ProjectAutomationCurve,
414
416
  ProjectAutomationLaneDesc,
415
417
  ProjectAutomationPoint,
418
+ ProjectAutomationTargetKind,
416
419
  ProjectBounceOptions,
417
420
  ProjectChordSymbol,
418
421
  ProjectClip,
@@ -451,9 +454,13 @@ export type {
451
454
  SynthPatch,
452
455
  } from './project';
453
456
  export {
457
+ AutomationTargetKind,
454
458
  BUILTIN_SYNTH_WAVEFORMS,
455
459
  EXPECTED_PROJECT_ABI_VERSION,
456
460
  MarkerKind,
461
+ PROJECT_AUTOMATION_TARGET_OPAQUE,
462
+ PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
463
+ PROJECT_AUTOMATION_TARGET_TRACK_PAN,
457
464
  Project,
458
465
  projectAbiVersion,
459
466
  SYNTH_BODY_TYPES,
@@ -525,6 +532,8 @@ export type {
525
532
  PairAnalysis,
526
533
  PairProcessor,
527
534
  PanLaw,
535
+ PanLawInput,
536
+ PanLawName,
528
537
  PanMode,
529
538
  PitchCorrectOptions,
530
539
  PitchResult,
@@ -636,10 +645,12 @@ export type {
636
645
  EngineTempoSegment,
637
646
  EngineTimeSignatureSegment,
638
647
  EngineTrackLane,
648
+ EngineTrackMonitorMode,
639
649
  EngineTrackSend,
640
650
  EngineTransportState,
641
651
  ExternalMidiEvent,
642
652
  MidiCcBindOptions,
653
+ TrackMonitorMode,
643
654
  } from './realtime_engine';
644
655
  export {
645
656
  ClipPageProvider,
@@ -840,13 +851,20 @@ function resolveVoicePresetOrdinal(preset: VoicePresetId | number): number {
840
851
 
841
852
  /**
842
853
  * Map a voice-character preset ordinal (or canonical id) to its canonical id
843
- * string (e.g. `'bright-idol'`). Invalid ordinals throw.
854
+ * string (e.g. `'bright-idol'`). Unknown numeric ordinals return `null`;
855
+ * unknown preset ids throw.
844
856
  */
845
- export function voiceCharacterPresetId(preset: VoicePresetId | number): string {
857
+ export function voiceCharacterPresetId(preset: VoicePresetId | number): VoicePresetId | null {
846
858
  if (!module) {
847
859
  throw new Error('Module not initialized. Call init() first.');
848
860
  }
849
- 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;
850
868
  }
851
869
 
852
870
  /**
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
 
package/src/project.ts CHANGED
@@ -15,9 +15,11 @@ export type {
15
15
  ExternalSeparatedStemImportResult,
16
16
  MidiCcLearnOptions,
17
17
  ProjectAssistSidecar,
18
+ ProjectAssistSidecarInput,
18
19
  ProjectAutomationCurve,
19
20
  ProjectAutomationLaneDesc,
20
21
  ProjectAutomationPoint,
22
+ ProjectAutomationTargetKind,
21
23
  ProjectBounceOptions,
22
24
  ProjectChordSymbol,
23
25
  ProjectClip,
@@ -65,9 +67,13 @@ export type {
65
67
  SynthPatch,
66
68
  } from './project_types';
67
69
  export {
70
+ AutomationTargetKind,
68
71
  BUILTIN_SYNTH_WAVEFORMS,
69
72
  EXPECTED_PROJECT_ABI_VERSION,
70
73
  MarkerKind,
74
+ PROJECT_AUTOMATION_TARGET_OPAQUE,
75
+ PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
76
+ PROJECT_AUTOMATION_TARGET_TRACK_PAN,
71
77
  SYNTH_BODY_TYPES,
72
78
  SYNTH_ENGINE_MODES,
73
79
  SYNTH_FILTER_MODELS,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  assertProjectMidiEvents,
3
+ projectAutomationTargetKindValue,
3
4
  projectLoopModeValue,
4
5
  projectMidi1Event,
5
6
  projectModule,
@@ -13,8 +14,10 @@ import type {
13
14
  ExternalSeparatedStemImportResult,
14
15
  MidiCcLearnOptions,
15
16
  ProjectAssistSidecar,
17
+ ProjectAssistSidecarInput,
16
18
  ProjectAutomationLaneDesc,
17
19
  ProjectAutomationPoint,
20
+ ProjectAutomationTargetKind,
18
21
  ProjectBounceOptions,
19
22
  ProjectChordSymbol,
20
23
  ProjectClip,
@@ -49,6 +52,41 @@ import type {
49
52
  SynthPatch,
50
53
  } from './project_types';
51
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
+
52
90
  /**
53
91
  * Headless DAW project (control-thread-only arrangement model).
54
92
  *
@@ -82,6 +120,11 @@ export class Project {
82
120
  this.native = new (projectModule().Project)();
83
121
  }
84
122
 
123
+ /** Create a new empty project. */
124
+ static create(): Project {
125
+ return new Project();
126
+ }
127
+
85
128
  /** Pack a MIDI 1.0 note-on event accepted by {@link setMidiEvents}. */
86
129
  static midiNoteOn(
87
130
  ppq: number,
@@ -462,6 +505,19 @@ export class Project {
462
505
  this.native.setMaxUndoDepth(depth);
463
506
  }
464
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
+
465
521
  /** Replace a MIDI clip's entire event list. */
466
522
  setMidiEvents(
467
523
  clipId: number,
@@ -625,8 +681,11 @@ export class Project {
625
681
  * Karplus-Strong / modal / additive / percussion / extended-waveguide-piano
626
682
  * engines plus the realism layer). Pass a {@link SynthPatch}, a preset-name
627
683
  * string (`'saw-lead'` / `'va:saw-lead'`; see {@link synthPresetNames}), or
628
- * an array of either; each object entry may carry a `destinationId` binding
629
- * 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.
630
689
  * Because the parameter defaults to `{}`, omission and explicit `undefined`
631
690
  * both create one default binding. Use an explicitly empty array `[]` (or
632
691
  * runtime `null`) for zero bindings. Unknown preset names throw.
@@ -722,6 +781,11 @@ export class Project {
722
781
  this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
723
782
  }
724
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
+
725
789
  /** Replace a clip's take list and active take id (undoable). */
726
790
  setClipTakes(clipId: number, takes: ReadonlyArray<ProjectClipTake>, activeTakeId = 0): void {
727
791
  this.native.setClipTakes(clipId, takes, activeTakeId);
@@ -778,10 +842,16 @@ export class Project {
778
842
 
779
843
  /** Append an automation lane; returns its stable target parameter id (undoable). */
780
844
  addAutomationLane(trackId: number, desc: ProjectAutomationLaneDesc): number {
781
- return this.native.addAutomationLane(trackId, {
782
- targetParamId: desc.targetParamId,
783
- points: desc.points,
784
- });
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);
785
855
  }
786
856
 
787
857
  /** Replace the lane identified by its stable target parameter id (undoable). */
@@ -790,10 +860,16 @@ export class Project {
790
860
  targetParamId: number,
791
861
  desc: ProjectAutomationLaneDesc,
792
862
  ): void {
793
- this.native.editAutomationLane(trackId, targetParamId, {
794
- targetParamId: desc.targetParamId,
795
- points: desc.points,
796
- });
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);
797
873
  }
798
874
 
799
875
  /** Remove the lane identified by its stable target parameter id (undoable). */
@@ -811,7 +887,14 @@ export class Project {
811
887
  this.native.annotateChords(chords);
812
888
  }
813
889
 
814
- /** 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;
815
898
  setAssistSidecar(
816
899
  moduleId: string,
817
900
  schemaVersion: number,
@@ -819,14 +902,52 @@ export class Project {
819
902
  regionStartPpq: number,
820
903
  regionEndPpq: number,
821
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,
822
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);
823
944
  this.native.setAssistSidecar(
824
945
  moduleId,
825
- schemaVersion,
826
- targetTrackId,
827
- regionStartPpq,
828
- regionEndPpq,
829
- 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()),
830
951
  );
831
952
  }
832
953
 
@@ -840,6 +961,12 @@ export class Project {
840
961
  return this.native.getAssistSidecar(index);
841
962
  }
842
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
+
843
970
  /** Set the project's clip-overlap policy (SonareProjectOverlapPolicy ordinal). */
844
971
  setOverlapPolicy(policy: number): void {
845
972
  this.native.setOverlapPolicy(policy);
@@ -3,7 +3,9 @@ import { getSonareModule } from './module_state';
3
3
  import type {
4
4
  BuiltinSynthBinding,
5
5
  ProjectAssistSidecar,
6
+ ProjectAutomationLaneDesc,
6
7
  ProjectAutomationPoint,
8
+ ProjectAutomationTargetKind,
7
9
  ProjectBounceOptions,
8
10
  ProjectChordSymbol,
9
11
  ProjectClip,
@@ -65,6 +67,7 @@ export interface WasmProject {
65
67
  redo: () => void;
66
68
  clearHistory: () => void;
67
69
  setMaxUndoDepth: (depth: number) => void;
70
+ setMaxHistoryBytes: (bytes: number) => void;
68
71
  setMidiEvents: (
69
72
  clipId: number,
70
73
  events: ReadonlyArray<ProjectMidiEvent | readonly [number, number, number]>,
@@ -120,6 +123,7 @@ export interface WasmProject {
120
123
  channels: number,
121
124
  sampleRate: number,
122
125
  ) => void;
126
+ setAudioSourceMetadata: (sourceId: number, contentHash: string, externalStemRole: string) => void;
123
127
  setClipTakes: (
124
128
  clipId: number,
125
129
  takes: ReadonlyArray<ProjectClipTake>,
@@ -137,14 +141,11 @@ export interface WasmProject {
137
141
  removeTrack: (trackId: number) => void;
138
142
  renameTrack: (trackId: number, name: string) => void;
139
143
  setTrackRoute: (trackId: number, channelStripRef: string, outputTarget: string) => void;
140
- addAutomationLane: (
141
- trackId: number,
142
- desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
143
- ) => number;
144
+ addAutomationLane: (trackId: number, desc: ProjectAutomationLaneDesc) => number;
144
145
  editAutomationLane: (
145
146
  trackId: number,
146
147
  targetParamId: number,
147
- desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
148
+ desc: ProjectAutomationLaneDesc,
148
149
  ) => void;
149
150
  removeAutomationLane: (trackId: number, targetParamId: number) => void;
150
151
  annotateKeys: (keys: ReadonlyArray<ProjectKeySegment>) => void;
@@ -325,6 +326,14 @@ export function projectTrackKindValue(kind: ProjectTrackKind | undefined): numbe
325
326
  return resolveEnumOrdinal(kind ?? 'audio', { audio: 0, midi: 1, aux: 2 }, 'project track kind');
326
327
  }
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
+ );
335
+ }
336
+
328
337
  export function projectWarpModeValue(mode: ProjectWarpMode | undefined): number {
329
338
  return resolveEnumOrdinal(
330
339
  mode ?? 'off',
@@ -110,6 +110,10 @@ export interface ProjectSource {
110
110
  storageHandleId: number;
111
111
  sampleRateHint: number;
112
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;
113
117
  }
114
118
 
115
119
  /** Names accepted by the minimal built-in oscillator synth. */
@@ -314,6 +318,8 @@ export interface SynthPatch {
314
318
  * `(destination_id, patch)` bindings instead. Defaults to `0`.
315
319
  */
316
320
  destinationId?: number;
321
+ /** Resolve MIDI channels from incoming GM bank/program changes; defaults to false. */
322
+ useGmPrograms?: boolean;
317
323
  /** Base preset name (see {@link synthPresetNames}); omit for the init patch. */
318
324
  preset?: string;
319
325
  engineMode?: SynthEngineMode | number;
@@ -448,12 +454,34 @@ export interface ProjectAutomationPoint {
448
454
  curve?: ProjectAutomationCurve;
449
455
  }
450
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
+
451
477
  /** Automation-lane descriptor for {@link Project.addAutomationLane}. */
452
478
  export interface ProjectAutomationLaneDesc {
453
- /** Host-defined id of the parameter the lane drives. */
479
+ /** Host-defined, non-zero id of the parameter the lane drives (zero is reserved). */
454
480
  targetParamId: number;
455
481
  /** Breakpoints (stored verbatim). */
456
482
  points: ReadonlyArray<ProjectAutomationPoint>;
483
+ /** Optional persistent target classification; omission retains the legacy opaque route. */
484
+ targetKind?: ProjectAutomationTargetKind;
457
485
  }
458
486
 
459
487
  /** One tempo segment for {@link Project.setTempoSegments}. */
@@ -515,6 +543,22 @@ export interface ProjectChordSymbol {
515
543
  modulationBoundary?: boolean;
516
544
  }
517
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
+
518
562
  /** Assist sidecar snapshot returned by {@link Project.getAssistSidecar}. */
519
563
  export interface ProjectAssistSidecar {
520
564
  moduleId: string;
@@ -90,6 +90,22 @@ export type AutomationCurve = 'linear' | 'exponential' | 'hold' | 's-curve';
90
90
  */
91
91
  export type PanLaw = 'const3dB' | 'const4.5dB' | 'const6dB' | 'linear0dB';
92
92
 
93
+ /** Accepted pan-law name aliases for mixer and realtime-engine inputs. */
94
+ export type PanLawName =
95
+ | PanLaw
96
+ | 'const-3db'
97
+ | '-3db'
98
+ | 'const-4.5db'
99
+ | '-4.5db'
100
+ | 'const-6db'
101
+ | '-6db'
102
+ | 'linear-0db'
103
+ | 'linear'
104
+ | '0db';
105
+
106
+ /** Pan-law name or raw C ABI ordinal. */
107
+ export type PanLawInput = PanLawName | number;
108
+
93
109
  /**
94
110
  * Meter tap point for reading a strip's meter snapshot
95
111
  * (see {@link Mixer.meterTap} and {@link Mixer.stripMeter}).
@@ -105,15 +105,33 @@ export type VoicePresetId =
105
105
  | 'robot-mascot'
106
106
  | 'dark-villain';
107
107
 
108
- export interface RealtimeVoiceChangerPreset {
108
+ export type VoicePresetCategory =
109
+ | 'monitor'
110
+ | 'bright'
111
+ | 'soft'
112
+ | 'deep'
113
+ | 'robot'
114
+ | 'dark'
115
+ | 'custom';
116
+
117
+ export interface RealtimeVoiceChangerPresetMetadata {
109
118
  schemaVersion: 1;
110
- id?: string;
111
- name?: string;
119
+ id: string;
120
+ name: string;
112
121
  description?: string;
113
- macros?: Record<string, number>;
114
- dsp?: Record<string, unknown>;
122
+ category: VoicePresetCategory;
115
123
  }
116
124
 
125
+ export type RealtimeVoiceChangerPreset =
126
+ | (RealtimeVoiceChangerPresetMetadata & {
127
+ dsp: Record<string, unknown>;
128
+ macros?: never;
129
+ })
130
+ | (RealtimeVoiceChangerPresetMetadata & {
131
+ macros: Record<string, number>;
132
+ dsp?: never;
133
+ });
134
+
117
135
  export type RealtimeVoiceChangerConfigInput = VoicePresetId | RealtimeVoiceChangerPreset;
118
136
 
119
137
  /**