@libraz/libsonare 1.8.0 → 1.8.2

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 (138) hide show
  1. package/README.md +5 -5
  2. package/dist/_effects_common.d.ts +2 -1
  3. package/dist/_effects_common.d.ts.map +1 -1
  4. package/dist/analysis.js +3 -0
  5. package/dist/analysis.js.map +1 -1
  6. package/dist/effects_mastering.d.ts +2 -2
  7. package/dist/effects_mastering.d.ts.map +1 -1
  8. package/dist/effects_note_ops.d.ts +55 -30
  9. package/dist/effects_note_ops.d.ts.map +1 -1
  10. package/dist/effects_timepitch.d.ts +17 -12
  11. package/dist/effects_timepitch.d.ts.map +1 -1
  12. package/dist/feature_decompose.d.ts +1 -1
  13. package/dist/feature_decompose.d.ts.map +1 -1
  14. package/dist/index.d.ts +11 -5
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2098 -155
  17. package/dist/index.js.map +1 -1
  18. package/dist/instrument_types.d.ts +27 -3
  19. package/dist/instrument_types.d.ts.map +1 -1
  20. package/dist/mastering_core.d.ts +30 -3
  21. package/dist/mastering_core.d.ts.map +1 -1
  22. package/dist/mixer.d.ts +2 -2
  23. package/dist/mixer.d.ts.map +1 -1
  24. package/dist/project.d.ts +5 -4
  25. package/dist/project.d.ts.map +1 -1
  26. package/dist/project_class.d.ts +57 -3
  27. package/dist/project_class.d.ts.map +1 -1
  28. package/dist/project_internal.d.ts +35 -1
  29. package/dist/project_internal.d.ts.map +1 -1
  30. package/dist/project_types.d.ts +17 -6
  31. package/dist/project_types.d.ts.map +1 -1
  32. package/dist/public_types.d.ts +1 -0
  33. package/dist/public_types.d.ts.map +1 -1
  34. package/dist/public_types_mastering.d.ts +19 -1
  35. package/dist/public_types_mastering.d.ts.map +1 -1
  36. package/dist/public_types_playback.d.ts +1 -0
  37. package/dist/public_types_playback.d.ts.map +1 -1
  38. package/dist/public_types_spectral.d.ts +5 -5
  39. package/dist/public_types_spectral.d.ts.map +1 -1
  40. package/dist/public_types_vocal_edit.d.ts +250 -0
  41. package/dist/public_types_vocal_edit.d.ts.map +1 -0
  42. package/dist/public_types_vocal_project.d.ts +63 -0
  43. package/dist/public_types_vocal_project.d.ts.map +1 -0
  44. package/dist/realtime_engine.d.ts +106 -16
  45. package/dist/realtime_engine.d.ts.map +1 -1
  46. package/dist/repair_dereverb.d.ts +1 -0
  47. package/dist/repair_dereverb.d.ts.map +1 -1
  48. package/dist/repair_noise.d.ts +1 -0
  49. package/dist/repair_noise.d.ts.map +1 -1
  50. package/dist/sonare-analysis.wasm +0 -0
  51. package/dist/sonare.d.ts +89 -0
  52. package/dist/sonare.js +1 -1
  53. package/dist/sonare.wasm +0 -0
  54. package/dist/streaming_processors.d.ts +5 -0
  55. package/dist/streaming_processors.d.ts.map +1 -1
  56. package/dist/vocal_edit.d.ts +131 -0
  57. package/dist/vocal_edit.d.ts.map +1 -0
  58. package/dist/vocal_edit_worker.d.ts +51 -0
  59. package/dist/vocal_edit_worker.d.ts.map +1 -0
  60. package/dist/vocal_edit_worker.js +1462 -0
  61. package/dist/vocal_edit_worker.js.map +1 -0
  62. package/dist/vocal_edit_worker_client.d.ts +118 -0
  63. package/dist/vocal_edit_worker_client.d.ts.map +1 -0
  64. package/dist/vocal_edit_worker_protocol.d.ts +139 -0
  65. package/dist/vocal_edit_worker_protocol.d.ts.map +1 -0
  66. package/dist/vocal_project.d.ts +17 -0
  67. package/dist/vocal_project.d.ts.map +1 -0
  68. package/dist/web_midi.d.ts +4 -0
  69. package/dist/web_midi.d.ts.map +1 -1
  70. package/dist/worker.js.map +1 -1
  71. package/dist/worklet/engine-mixer-facade.d.ts +42 -5
  72. package/dist/worklet/engine-mixer-facade.d.ts.map +1 -1
  73. package/dist/worklet/engine-node.d.ts +10 -0
  74. package/dist/worklet/engine-node.d.ts.map +1 -1
  75. package/dist/worklet/engine-processor.d.ts +6 -1
  76. package/dist/worklet/engine-processor.d.ts.map +1 -1
  77. package/dist/worklet/engine-register.d.ts.map +1 -1
  78. package/dist/worklet/engine-strips.d.ts +8 -4
  79. package/dist/worklet/engine-strips.d.ts.map +1 -1
  80. package/dist/worklet/engine-sync.d.ts +4 -2
  81. package/dist/worklet/engine-sync.d.ts.map +1 -1
  82. package/dist/worklet/engine.d.ts +42 -9
  83. package/dist/worklet/engine.d.ts.map +1 -1
  84. package/dist/worklet/guards.d.ts +10 -2
  85. package/dist/worklet/guards.d.ts.map +1 -1
  86. package/dist/worklet/messages.d.ts +54 -9
  87. package/dist/worklet/messages.d.ts.map +1 -1
  88. package/dist/worklet/mixer-processor.d.ts +2 -2
  89. package/dist/worklet/mixer-processor.d.ts.map +1 -1
  90. package/dist/worklet/protocol.d.ts +20 -2
  91. package/dist/worklet/protocol.d.ts.map +1 -1
  92. package/dist/worklet.d.ts +1 -1
  93. package/dist/worklet.d.ts.map +1 -1
  94. package/dist/worklet.js +808 -204
  95. package/dist/worklet.js.map +1 -1
  96. package/package.json +6 -1
  97. package/src/_effects_common.ts +36 -6
  98. package/src/effects_mastering.ts +4 -0
  99. package/src/effects_note_ops.ts +92 -44
  100. package/src/effects_timepitch.ts +42 -31
  101. package/src/feature_decompose.ts +2 -2
  102. package/src/index.ts +39 -0
  103. package/src/instrument_types.ts +29 -3
  104. package/src/mastering_core.ts +98 -2
  105. package/src/mixer.ts +41 -21
  106. package/src/project.ts +8 -1
  107. package/src/project_class.ts +136 -1
  108. package/src/project_internal.ts +90 -0
  109. package/src/project_types.ts +18 -6
  110. package/src/public_types.ts +1 -0
  111. package/src/public_types_mastering.ts +23 -0
  112. package/src/public_types_playback.ts +1 -0
  113. package/src/public_types_spectral.ts +5 -5
  114. package/src/public_types_vocal_edit.ts +289 -0
  115. package/src/public_types_vocal_project.ts +69 -0
  116. package/src/realtime_engine.ts +157 -24
  117. package/src/repair_dereverb.ts +1 -0
  118. package/src/repair_noise.ts +1 -0
  119. package/src/sonare.js.d.ts +89 -0
  120. package/src/streaming_processors.ts +8 -0
  121. package/src/vocal_edit.ts +885 -0
  122. package/src/vocal_edit_worker.ts +832 -0
  123. package/src/vocal_edit_worker_client.ts +812 -0
  124. package/src/vocal_edit_worker_protocol.ts +206 -0
  125. package/src/vocal_project.ts +327 -0
  126. package/src/web_midi.ts +100 -5
  127. package/src/worklet/engine-mixer-facade.ts +422 -87
  128. package/src/worklet/engine-node.ts +69 -4
  129. package/src/worklet/engine-processor.ts +129 -34
  130. package/src/worklet/engine-register.ts +3 -0
  131. package/src/worklet/engine-strips.ts +36 -14
  132. package/src/worklet/engine-sync.ts +6 -1
  133. package/src/worklet/engine.ts +76 -40
  134. package/src/worklet/guards.ts +81 -0
  135. package/src/worklet/messages.ts +63 -4
  136. package/src/worklet/mixer-processor.ts +10 -3
  137. package/src/worklet/protocol.ts +39 -8
  138. package/src/worklet.ts +2 -0
package/src/index.ts CHANGED
@@ -61,6 +61,7 @@ export type {
61
61
  DynamicsProcessorResult,
62
62
  GateOptions,
63
63
  MasteringAbMatchLoudnessRequest,
64
+ MasteringAbMatchLoudnessStereoRequest,
64
65
  MasteringAmpPresetCatalogEntry,
65
66
  MasteringAssistantParamsRequest,
66
67
  MasteringAssistantStereoParamsRequest,
@@ -74,6 +75,7 @@ export type {
74
75
  MasteringInsertTiming,
75
76
  MasteringPairAnalyzeRequest,
76
77
  MasteringPairProcessRequest,
78
+ MasteringPairProcessStereoRequest,
77
79
  MasteringProcessorCatalogEntry,
78
80
  MasteringProcessorCategory,
79
81
  MasteringProcessRequest,
@@ -132,6 +134,7 @@ export {
132
134
  masterAudioWithProgress,
133
135
  mastering,
134
136
  masteringAbMatchLoudness,
137
+ masteringAbMatchLoudnessStereo,
135
138
  masteringAmpPresetCatalog,
136
139
  masteringAssistantSuggest,
137
140
  masteringAssistantSuggestChain,
@@ -154,6 +157,7 @@ export {
154
157
  masteringPairAnalyze,
155
158
  masteringPairProcess,
156
159
  masteringPairProcessorNames,
160
+ masteringPairProcessStereo,
157
161
  masteringPlatformNames,
158
162
  masteringPresetNames,
159
163
  masteringPresetParams,
@@ -542,6 +546,9 @@ export type {
542
546
  MidiCcLearnOptions,
543
547
  MpeDimension,
544
548
  NoteTracking,
549
+ PartRig,
550
+ PartRigInsert,
551
+ PartRigMode,
545
552
  ProjectAssistSidecar,
546
553
  ProjectAssistSidecarInput,
547
554
  ProjectAutomationCurve,
@@ -556,6 +563,7 @@ export type {
556
563
  ProjectClipFade,
557
564
  ProjectClipTake,
558
565
  ProjectCompileResult,
566
+ ProjectCompileTimelineResult,
559
567
  ProjectFadeCurve,
560
568
  ProjectKeySegment,
561
569
  ProjectLoopMode,
@@ -613,10 +621,13 @@ export {
613
621
  MarkerKind,
614
622
  MPE_DIMENSIONS,
615
623
  NOTE_TRACKINGS,
624
+ PART_RIG_ALL_PARTS,
625
+ PART_RIG_MODES,
616
626
  PROJECT_AUTOMATION_TARGET_OPAQUE,
617
627
  PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
618
628
  PROJECT_AUTOMATION_TARGET_TRACK_PAN,
619
629
  Project,
630
+ ProjectTimeline,
620
631
  projectAbiVersion,
621
632
  SAMPLE_KEY_TRACKS,
622
633
  SAMPLE_LOOP_MODES,
@@ -687,6 +698,7 @@ export type {
687
698
  KeyDetectionOptions,
688
699
  KeyProfileName,
689
700
  LoudnessMatchResult,
701
+ LoudnessMatchStereoResult,
690
702
  LufsResult,
691
703
  LufsSeriesResult,
692
704
  MasteringAssistantParams,
@@ -807,6 +819,7 @@ export type {
807
819
  SpectralRegionOp,
808
820
  StageGainReduction,
809
821
  StereoAnalysis,
822
+ StereoPairProcessor,
810
823
  StftPowerResult,
811
824
  StftResult,
812
825
  StreamingEqualizerConfig,
@@ -829,6 +842,7 @@ export {
829
842
  PitchClass,
830
843
  SectionType,
831
844
  } from './public_types';
845
+ export type * from './public_types_vocal_edit';
832
846
  export type {
833
847
  AnalyzeBpmRequest,
834
848
  AnalyzeDynamicsRequest,
@@ -953,6 +967,29 @@ export {
953
967
  export type { TranscribeRequest } from './transcribe';
954
968
  export { transcribe } from './transcribe';
955
969
  export type { ValidateOptions } from './validation';
970
+ export {
971
+ createVocalEditSession,
972
+ restoreVocalEditSession,
973
+ VocalEditDraft,
974
+ VocalEditSession,
975
+ VocalRenderJob,
976
+ VocalRenderSnapshot,
977
+ vocalEditApiVersion,
978
+ vocalEditAvailable,
979
+ } from './vocal_edit';
980
+ export type {
981
+ VocalEditWorker,
982
+ VocalEditWorkerCallOptions,
983
+ VocalEditWorkerClientOptions,
984
+ VocalEditWorkerTransferOptions,
985
+ } from './vocal_edit_worker_client';
986
+ export {
987
+ VocalEditWorkerClient,
988
+ VocalEditWorkerSession,
989
+ VocalEditWorkerStaleResultError,
990
+ VocalEditWorkerTask,
991
+ } from './vocal_edit_worker_client';
992
+ export type * from './vocal_edit_worker_protocol';
956
993
  export type {
957
994
  BindWebMidiOptions,
958
995
  WebMidiBinding,
@@ -1148,3 +1185,5 @@ export function realtimeVoiceChangerPresetConfig(
1148
1185
  // ============================================================================
1149
1186
 
1150
1187
  export { PitchClass as Pitch } from './public_types';
1188
+
1189
+ export type * from './public_types_vocal_project';
@@ -228,6 +228,16 @@ export const CONTROLLER_AXES = [
228
228
  */
229
229
  export const ARTICULATIONS = ['poly', 'mono-retrigger', 'mono-legato'] as const;
230
230
 
231
+ /**
232
+ * How a part's signal path is built. `'bank'` keeps whatever the instrument's
233
+ * own bank/program selection resolves to, `'none'` bypasses the rig stage, and
234
+ * `'chain'` replaces it with an explicit insert chain.
235
+ */
236
+ export const PART_RIG_MODES = ['bank', 'none', 'chain'] as const;
237
+
238
+ /** The `part` value that addresses a destination's default instead of one part (0-15). */
239
+ export const PART_RIG_ALL_PARTS = 0xff;
240
+
231
241
  /** The three dimensions MPE carries per note ({@link RealtimeEngine.setControllerNoteTracking}). */
232
242
  export const MPE_DIMENSIONS = ['bend', 'pressure', 'timbre'] as const;
233
243
 
@@ -380,6 +390,23 @@ export type ControllerAxis = (typeof CONTROLLER_AXES)[number];
380
390
  /** Per-channel note-overlap rule ({@link RealtimeEngine.setArticulation}). */
381
391
  export type Articulation = (typeof ARTICULATIONS)[number];
382
392
 
393
+ /** How a part's rig is built ({@link PART_RIG_MODES}). */
394
+ export type PartRigMode = (typeof PART_RIG_MODES)[number];
395
+
396
+ /** One stage of a `'chain'` part rig: a processor name and its parameters. */
397
+ export interface PartRigInsert {
398
+ processor: string;
399
+ /** Processor parameters, as an object or an already-serialized JSON object string. */
400
+ params?: Record<string, unknown> | string;
401
+ }
402
+
403
+ /** A part rig as stored on a project ({@link Project.getPartRig}). */
404
+ export interface PartRig {
405
+ mode: PartRigMode;
406
+ /** Present for `'chain'` only; `params` is the parsed parameter object. */
407
+ inserts?: { processor: string; params: Record<string, unknown> }[];
408
+ }
409
+
383
410
  /** One per-note MPE dimension ({@link MPE_DIMENSIONS}). */
384
411
  export type MpeDimension = (typeof MPE_DIMENSIONS)[number];
385
412
 
@@ -582,9 +609,8 @@ export interface SynthPatch {
582
609
  * whose per-note kit patch replaces this whole section and never reads it.
583
610
  *
584
611
  * Also automatable under this same name through
585
- * {@link RealtimeEngine.resolveInstrumentAutomationId}; it is one of the names
586
- * that reaches a voice that is already sounding, rather than waiting for the
587
- * next note.
612
+ * {@link RealtimeEngine.resolveInstrumentAutomationId}; it reaches
613
+ * already-sounding voices from the next processed block.
588
614
  */
589
615
  pitchOffsetCents?: number;
590
616
  ampAttackMs?: number;
@@ -2,6 +2,7 @@ import { ErrorCode, SonareError } from './errors';
2
2
  import { getSonareModule } from './module_state';
3
3
  import type {
4
4
  LoudnessMatchResult,
5
+ LoudnessMatchStereoResult,
5
6
  MasteringAssistantParams,
6
7
  MasteringInsertParamChoice,
7
8
  MasteringInsertSlot,
@@ -13,6 +14,7 @@ import type {
13
14
  PairProcessor,
14
15
  SoloProcessor,
15
16
  StereoAnalysis,
17
+ StereoPairProcessor,
16
18
  StreamingPlatform,
17
19
  } from './public_types';
18
20
 
@@ -52,6 +54,17 @@ export interface MasteringPairProcessRequest {
52
54
  params?: MasteringProcessorParams;
53
55
  }
54
56
 
57
+ /** Canonical request form for a stereo two-input match processor. */
58
+ export interface MasteringPairProcessStereoRequest {
59
+ processorName: StereoPairProcessor;
60
+ sourceLeft: Float32Array;
61
+ sourceRight: Float32Array;
62
+ referenceLeft: Float32Array;
63
+ referenceRight: Float32Array;
64
+ sampleRate?: number;
65
+ params?: MasteringProcessorParams;
66
+ }
67
+
55
68
  /** Canonical request form for {@link masteringAbMatchLoudness}. */
56
69
  export interface MasteringAbMatchLoudnessRequest {
57
70
  /** The take to gain-match. */
@@ -61,6 +74,15 @@ export interface MasteringAbMatchLoudnessRequest {
61
74
  sampleRate?: number;
62
75
  }
63
76
 
77
+ /** Canonical request form for stereo AB loudness matching. */
78
+ export interface MasteringAbMatchLoudnessStereoRequest {
79
+ sourceLeft: Float32Array;
80
+ sourceRight: Float32Array;
81
+ referenceLeft: Float32Array;
82
+ referenceRight: Float32Array;
83
+ sampleRate?: number;
84
+ }
85
+
64
86
  /** Canonical request form for a two-input match analysis. */
65
87
  export interface MasteringPairAnalyzeRequest {
66
88
  analysisName: PairAnalysis;
@@ -548,6 +570,60 @@ export function masteringPairProcess(
548
570
  );
549
571
  }
550
572
 
573
+ /**
574
+ * Apply the stereo `match.abCrossfade` processor. Source and reference stereo
575
+ * pairs may have independent lengths, but each pair must have equal channels.
576
+ */
577
+ export function masteringPairProcessStereo(
578
+ request: MasteringPairProcessStereoRequest,
579
+ ): MasteringStereoResult;
580
+ export function masteringPairProcessStereo(
581
+ processorName: StereoPairProcessor,
582
+ sourceLeft: Float32Array,
583
+ sourceRight: Float32Array,
584
+ referenceLeft: Float32Array,
585
+ referenceRight: Float32Array,
586
+ sampleRate?: number,
587
+ params?: MasteringProcessorParams,
588
+ ): MasteringStereoResult;
589
+ export function masteringPairProcessStereo(
590
+ processorName: StereoPairProcessor | MasteringPairProcessStereoRequest,
591
+ sourceLeft?: Float32Array,
592
+ sourceRight?: Float32Array,
593
+ referenceLeft?: Float32Array,
594
+ referenceRight?: Float32Array,
595
+ sampleRate = 22050,
596
+ params: MasteringProcessorParams = {},
597
+ ): MasteringStereoResult {
598
+ const request =
599
+ typeof processorName === 'string'
600
+ ? {
601
+ processorName,
602
+ sourceLeft: sourceLeft as Float32Array,
603
+ sourceRight: sourceRight as Float32Array,
604
+ referenceLeft: referenceLeft as Float32Array,
605
+ referenceRight: referenceRight as Float32Array,
606
+ sampleRate,
607
+ params,
608
+ }
609
+ : processorName;
610
+ if (request.sourceLeft.length !== request.sourceRight.length) {
611
+ throw new Error('Source left and right channel lengths must match.');
612
+ }
613
+ if (request.referenceLeft.length !== request.referenceRight.length) {
614
+ throw new Error('Reference left and right channel lengths must match.');
615
+ }
616
+ return requireModule().masteringPairProcessStereo(
617
+ request.processorName,
618
+ request.sourceLeft,
619
+ request.sourceRight,
620
+ request.referenceLeft,
621
+ request.referenceRight,
622
+ request.sampleRate ?? 22050,
623
+ request.params ?? {},
624
+ );
625
+ }
626
+
551
627
  /**
552
628
  * Analyze a `source` against a `reference` with a two-input analysis. The two
553
629
  * buffers may have independent lengths.
@@ -616,6 +692,25 @@ export function masteringAbMatchLoudness(
616
692
  );
617
693
  }
618
694
 
695
+ /** Gain-match a stereo source to a stereo reference with one shared gain. */
696
+ export function masteringAbMatchLoudnessStereo(
697
+ request: MasteringAbMatchLoudnessStereoRequest,
698
+ ): LoudnessMatchStereoResult {
699
+ if (request.sourceLeft.length !== request.sourceRight.length) {
700
+ throw new Error('Source left and right channel lengths must match.');
701
+ }
702
+ if (request.referenceLeft.length !== request.referenceRight.length) {
703
+ throw new Error('Reference left and right channel lengths must match.');
704
+ }
705
+ return requireModule().masteringAbMatchLoudnessStereo(
706
+ request.sourceLeft,
707
+ request.sourceRight,
708
+ request.referenceLeft,
709
+ request.referenceRight,
710
+ request.sampleRate ?? 22050,
711
+ );
712
+ }
713
+
619
714
  export function masteringStereoAnalyze(request: MasteringStereoAnalyzeRequest): string;
620
715
  export function masteringStereoAnalyze(
621
716
  analysisName: StereoAnalysis,
@@ -829,11 +924,12 @@ export function masteringAssistantSuggestChainStereo(
829
924
  /**
830
925
  * Mastering assistant profile of a stereo pair, as shared JSON.
831
926
  *
832
- * Only the `loudness` block is measured from the two channels: integrated LUFS
927
+ * The `loudness` block is measured from the two channels: integrated LUFS
833
928
  * and LRA come from the channel-summed program and the true peak is the larger
834
929
  * of the two. The spectral, dynamics and tempo fields describe shape and timing
835
930
  * rather than absolute level and are measured on the downmix, which keeps them
836
- * comparable with {@link masteringAudioProfile}.
931
+ * comparable with {@link masteringAudioProfile}. Defect detectors run on each
932
+ * channel and their results are aggregated.
837
933
  */
838
934
  export function masteringAudioProfileStereo(request: MasteringStereoParamsRequest): string {
839
935
  return requireModule().masteringAudioProfileStereo(
package/src/mixer.ts CHANGED
@@ -188,50 +188,70 @@ export class Mixer {
188
188
  * after {@link delete}.
189
189
  */
190
190
  createRealtimeBuffer(): MixerRealtimeBuffer {
191
- const stripCount = this.stripCount();
192
- let leftInputs: Float32Array[] = [];
193
- let rightInputs: Float32Array[] = [];
191
+ const leftInputs: Float32Array[] = [];
192
+ const rightInputs: Float32Array[] = [];
194
193
  let outLeft = this.mixer.outputLeftView();
195
194
  let outRight = this.mixer.outputRightView();
195
+ let acquiredStripCount = -1;
196
+
197
+ // Every view shares one heap buffer, so a growth detaches the output views too.
198
+ const viewsDetached = (): boolean => outLeft.byteLength === 0 || outRight.byteLength === 0;
199
+
196
200
  const acquire = (): void => {
197
- leftInputs = [];
198
- rightInputs = [];
199
- for (let index = 0; index < stripCount; index++) {
201
+ const stripCount = this.stripCount();
202
+ const detached = viewsDetached();
203
+ if (detached) {
204
+ // A heap growth detached every view: reacquire all planes.
205
+ leftInputs.length = 0;
206
+ rightInputs.length = 0;
207
+ } else {
208
+ // Topology growth keeps existing planes in place: append views for new strips only.
209
+ leftInputs.length = Math.min(leftInputs.length, stripCount);
210
+ rightInputs.length = Math.min(rightInputs.length, stripCount);
211
+ }
212
+ for (let index = leftInputs.length; index < stripCount; index++) {
200
213
  leftInputs.push(this.mixer.inputLeftView(index));
214
+ }
215
+ for (let index = rightInputs.length; index < stripCount; index++) {
201
216
  rightInputs.push(this.mixer.inputRightView(index));
202
217
  }
203
- outLeft = this.mixer.outputLeftView();
204
- outRight = this.mixer.outputRightView();
218
+ if (detached) {
219
+ outLeft = this.mixer.outputLeftView();
220
+ outRight = this.mixer.outputRightView();
221
+ }
222
+ acquiredStripCount = stripCount;
205
223
  };
206
224
  acquire();
225
+
207
226
  // The cached heap views can detach if WASM linear memory grows (the embind
208
- // module is built ALLOW_MEMORY_GROWTH). Re-acquire them if detached
209
- // (byteLength === 0) before use, mirroring the worklet RT path.
210
- const reacquireIfDetached = (): void => {
211
- if (outLeft.byteLength === 0 || (leftInputs[0]?.byteLength ?? 1) === 0) {
227
+ // module is built ALLOW_MEMORY_GROWTH). Also refresh the view list when a
228
+ // caller adds a strip and recompiles the graph after this buffer was made.
229
+ const acquireIfNeeded = (): void => {
230
+ if (acquiredStripCount !== this.stripCount() || viewsDetached()) {
212
231
  acquire();
213
232
  }
214
233
  };
215
234
  return {
216
235
  get leftInputs(): Float32Array[] {
217
- reacquireIfDetached();
236
+ acquireIfNeeded();
218
237
  return leftInputs;
219
238
  },
220
239
  get rightInputs(): Float32Array[] {
221
- reacquireIfDetached();
240
+ acquireIfNeeded();
222
241
  return rightInputs;
223
242
  },
224
243
  get outLeft(): Float32Array {
225
- reacquireIfDetached();
244
+ acquireIfNeeded();
226
245
  return outLeft;
227
246
  },
228
247
  get outRight(): Float32Array {
229
- reacquireIfDetached();
248
+ acquireIfNeeded();
230
249
  return outRight;
231
250
  },
232
- process: (numSamples = outLeft.length) => {
233
- reacquireIfDetached();
234
- this.mixer.processPreparedStereo(numSamples);
251
+ process: (numSamples?: number) => {
252
+ acquireIfNeeded();
253
+ // Resolve the default only after reacquiring: a detached view reports length 0.
254
+ this.mixer.processPreparedStereo(numSamples ?? outLeft.length);
235
255
  },
236
256
  };
237
257
  }
@@ -484,8 +504,8 @@ export class Mixer {
484
504
  }
485
505
 
486
506
  /**
487
- * Set a per-strip channel delay in samples. This changes the strip's reported
488
- * latency; recompile to re-run latency compensation.
507
+ * Set a per-strip channel delay in samples, moving the strip later relative to
508
+ * the others. It is not latency: latency compensation does not undo it.
489
509
  */
490
510
  setChannelDelaySamples(stripIndex: number, delaySamples: number): void {
491
511
  this.mixer.setChannelDelaySamples(stripIndex, delaySamples);
package/src/project.ts CHANGED
@@ -8,6 +8,9 @@ export type {
8
8
  ControllerInput,
9
9
  MpeDimension,
10
10
  NoteTracking,
11
+ PartRig,
12
+ PartRigInsert,
13
+ PartRigMode,
11
14
  SampleDesc,
12
15
  SampleDescLoopMode,
13
16
  SampleKeyTrack,
@@ -35,6 +38,8 @@ export {
35
38
  CONTROLLER_INPUTS,
36
39
  MPE_DIMENSIONS,
37
40
  NOTE_TRACKINGS,
41
+ PART_RIG_ALL_PARTS,
42
+ PART_RIG_MODES,
38
43
  SAMPLE_KEY_TRACKS,
39
44
  SAMPLE_LOOP_MODES,
40
45
  SYNTH_BODY_TYPES,
@@ -46,7 +51,7 @@ export {
46
51
  SYNTH_OSC_WAVEFORMS,
47
52
  SYNTH_RETRIGGERS,
48
53
  } from './instrument_types';
49
- export { Project } from './project_class';
54
+ export { Project, ProjectTimeline } from './project_class';
50
55
  export {
51
56
  controllerProfileNames,
52
57
  projectAbiVersion,
@@ -79,6 +84,7 @@ export type {
79
84
  ProjectClipFade,
80
85
  ProjectClipTake,
81
86
  ProjectCompileResult,
87
+ ProjectCompileTimelineResult,
82
88
  ProjectDeserializeResult,
83
89
  ProjectDiagnostic,
84
90
  ProjectFadeCurve,
@@ -121,4 +127,5 @@ export {
121
127
  PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
122
128
  PROJECT_AUTOMATION_TARGET_TRACK_PAN,
123
129
  } from './project_types';
130
+ export type * from './public_types_vocal_project';
124
131
  export { SampleBank } from './sample_bank';
@@ -1,5 +1,8 @@
1
1
  import type {
2
2
  BuiltinSynthBinding,
3
+ PartRig,
4
+ PartRigInsert,
5
+ PartRigMode,
3
6
  Sf2InstrumentConfig,
4
7
  Sf2ProgramStatus,
5
8
  SynthPatch,
@@ -7,6 +10,8 @@ import type {
7
10
  import {
8
11
  assertProjectMidiEvents,
9
12
  normalizeSynthInstrument,
13
+ partRigFromNative,
14
+ partRigInsertsJson,
10
15
  projectAutomationPointValue,
11
16
  projectAutomationTargetKindValue,
12
17
  projectLoopModeValue,
@@ -14,7 +19,10 @@ import {
14
19
  projectModule,
15
20
  projectTrackKindValue,
16
21
  projectWarpModeValue,
22
+ registerProjectTimeline,
23
+ unregisterProjectTimeline,
17
24
  type WasmProject,
25
+ type WasmProjectTimeline,
18
26
  } from './project_internal';
19
27
  import type {
20
28
  ExternalSeparatedStemImportRequest,
@@ -33,6 +41,7 @@ import type {
33
41
  ProjectClipFade,
34
42
  ProjectClipTake,
35
43
  ProjectCompileResult,
44
+ ProjectCompileTimelineResult,
36
45
  ProjectDeserializeResult,
37
46
  ProjectKeySegment,
38
47
  ProjectLoopMode,
@@ -60,6 +69,13 @@ import type {
60
69
  ProjectWarpMapDesc,
61
70
  ProjectWarpMode,
62
71
  } from './project_types';
72
+ import type {
73
+ ProjectVocalEditApplyRequest,
74
+ ProjectVocalEditApplyResult,
75
+ ProjectVocalEditDependency,
76
+ ProjectVocalOriginalSource,
77
+ ProjectVocalRehydrateItem,
78
+ } from './public_types_vocal_project';
63
79
  import {
64
80
  assertBoundedInteger,
65
81
  assertNibble,
@@ -67,6 +83,11 @@ import {
67
83
  assertSamples,
68
84
  assertU7,
69
85
  } from './validation';
86
+ import {
87
+ projectApplyVocalEdit,
88
+ projectGetVocalEditDependencies,
89
+ projectRehydrateVocalEdits,
90
+ } from './vocal_project';
70
91
 
71
92
  /**
72
93
  * Folds the positional and request call forms of `bakeMidiFx` into one shape,
@@ -171,6 +192,47 @@ function midi2Byte(fnName: string, value: number, argName: string): number {
171
192
  return value;
172
193
  }
173
194
 
195
+ /**
196
+ * An immutable compiled snapshot of a {@link Project}, produced by
197
+ * {@link Project.compileTimeline} and installed into a stopped realtime engine
198
+ * with `RealtimeEngine.applyProjectTimeline`.
199
+ *
200
+ * An engine keeps its own reference after an apply, so {@link dispose} may be
201
+ * called right afterwards. The embind handle is not garbage-collected; release
202
+ * it when no further apply needs it.
203
+ */
204
+ export class ProjectTimeline {
205
+ private native: WasmProjectTimeline | null;
206
+
207
+ /** @internal */
208
+ constructor(native: WasmProjectTimeline) {
209
+ this.native = native;
210
+ registerProjectTimeline(this, native);
211
+ }
212
+
213
+ /**
214
+ * Identity an engine names this timeline by, so no raw pointer crosses into JS.
215
+ *
216
+ * @internal
217
+ */
218
+ get nativeId(): number {
219
+ if (this.native === null) {
220
+ throw new TypeError('ProjectTimeline is disposed');
221
+ }
222
+ return this.native.id;
223
+ }
224
+
225
+ /** Release the underlying WASM object. Idempotent. */
226
+ dispose(): void {
227
+ if (this.native === null) {
228
+ return;
229
+ }
230
+ unregisterProjectTimeline(this);
231
+ this.native.delete();
232
+ this.native = null;
233
+ }
234
+ }
235
+
174
236
  /**
175
237
  * Headless DAW project (control-thread-only arrangement model).
176
238
  *
@@ -199,7 +261,33 @@ function midi2Byte(fnName: string, value: number, argName: string): number {
199
261
  */
200
262
  export class Project {
201
263
  private native: WasmProject;
264
+
265
+ applyVocalEdit(request: ProjectVocalEditApplyRequest): ProjectVocalEditApplyResult {
266
+ return projectApplyVocalEdit(this.native, request);
267
+ }
268
+
269
+ getVocalEditDependencies(): ProjectVocalEditDependency[] {
270
+ return projectGetVocalEditDependencies(this.native);
271
+ }
272
+
273
+ rehydrateVocalEdits(
274
+ originals: readonly ProjectVocalOriginalSource[],
275
+ cancel?: () => boolean,
276
+ ): ProjectVocalRehydrateItem[] {
277
+ this.activeVocalRehydrateCalls += 1;
278
+ try {
279
+ return projectRehydrateVocalEdits(this.native, originals, cancel);
280
+ } finally {
281
+ this.activeVocalRehydrateCalls -= 1;
282
+ if (this.activeVocalRehydrateCalls === 0 && this.deleteRequested) {
283
+ this.native.delete();
284
+ }
285
+ }
286
+ }
287
+
202
288
  private released = false;
289
+ private deleteRequested = false;
290
+ private activeVocalRehydrateCalls = 0;
203
291
 
204
292
  constructor() {
205
293
  this.native = new (projectModule().Project)();
@@ -798,6 +886,34 @@ export class Project {
798
886
  this.native.setTrackGain(trackId, gain);
799
887
  }
800
888
 
889
+ /**
890
+ * Set the rig of one part (0-15), or of the destination's default when `part`
891
+ * is {@link PART_RIG_ALL_PARTS}, on MIDI destination `destinationId`, via an
892
+ * undoable edit. `'chain'` takes 1-8 `inserts` and is the only mode that does;
893
+ * a destination no track references is stored and read back all the same.
894
+ * Refused: a malformed insert document (invalid format), an out-of-range part,
895
+ * an unknown mode or processor, or inserts on a non-chain mode (invalid
896
+ * parameter), and an insert chain in a build without mastering.
897
+ */
898
+ setPartRig(
899
+ destinationId: number,
900
+ part: number,
901
+ mode: PartRigMode | number,
902
+ inserts?: PartRigInsert[],
903
+ ): void {
904
+ this.native.setPartRig(destinationId, part, mode, partRigInsertsJson(inserts));
905
+ }
906
+
907
+ /** The stored rig for `(destinationId, part)`, or null when none is stored. */
908
+ getPartRig(destinationId: number, part: number): PartRig | null {
909
+ return partRigFromNative(this.native.getPartRig(destinationId, part));
910
+ }
911
+
912
+ /** Remove the stored rig for `(destinationId, part)` via an undoable edit (no-op when absent). */
913
+ clearPartRig(destinationId: number, part: number): void {
914
+ this.native.clearPartRig(destinationId, part);
915
+ }
916
+
801
917
  /** Set a track's mute flag via an undoable edit (a muted track is silent). */
802
918
  setTrackMute(trackId: number, mute: boolean): void {
803
919
  this.native.setTrackMute(trackId, mute);
@@ -1059,6 +1175,17 @@ export class Project {
1059
1175
  return this.native.compile();
1060
1176
  }
1061
1177
 
1178
+ /**
1179
+ * Compile the project and keep the resulting timeline. The result is
1180
+ * {@link compile}'s plus `timeline`: a {@link ProjectTimeline} to install with
1181
+ * `RealtimeEngine.applyProjectTimeline`, or `null` when an error diagnostic
1182
+ * means nothing compiled.
1183
+ */
1184
+ compileTimeline(): ProjectCompileTimelineResult {
1185
+ const { timeline, ...result } = this.native.compileTimeline();
1186
+ return { ...result, timeline: timeline === null ? null : new ProjectTimeline(timeline) };
1187
+ }
1188
+
1062
1189
  /**
1063
1190
  * Compile + render the project offline to interleaved float audio. MIDI
1064
1191
  * tracks render silently here (no instrument is bound) — use
@@ -1540,12 +1667,20 @@ export class Project {
1540
1667
  return this.native.lastBounceCompileResult();
1541
1668
  }
1542
1669
 
1543
- /** Release the underlying WASM object. Idempotent, as the Node facade is. */
1670
+ /**
1671
+ * Release the underlying WASM object. Idempotent, as the Node facade is.
1672
+ * Called from a rehydrate cancel callback, the release happens when the
1673
+ * outermost {@link rehydrateVocalEdits} call returns.
1674
+ */
1544
1675
  delete(): void {
1545
1676
  if (this.released) {
1546
1677
  return;
1547
1678
  }
1548
1679
  this.released = true;
1680
+ if (this.activeVocalRehydrateCalls !== 0) {
1681
+ this.deleteRequested = true;
1682
+ return;
1683
+ }
1549
1684
  this.native.delete();
1550
1685
  }
1551
1686