@libraz/libsonare 1.4.0 → 1.4.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libraz/libsonare",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Audio analysis library for music information retrieval",
@@ -385,11 +385,16 @@ export function mastering(
385
385
  options.targetLufs ?? -14.0,
386
386
  options.ceilingDb ?? -1.0,
387
387
  options.truePeakOversample ?? 4,
388
+ options.releaseMs ?? 0, // 0 => library default (50 ms)
389
+ options.applyGainAtInputRate ?? false,
388
390
  );
389
391
  }
390
392
 
391
393
  export function masteringProcessorNames(): SoloProcessor[] {
392
- return requireModule().masteringProcessorNames() as SoloProcessor[];
394
+ // embind hands back a vector whose constructor is not this realm's Array, so the
395
+ // result is not structured-cloneable (breaks postMessage to a Worker).
396
+ // Array.from() re-roots it as a plain Array. Same for the sibling *Names() below.
397
+ return Array.from(requireModule().masteringProcessorNames()) as SoloProcessor[];
393
398
  }
394
399
 
395
400
  /**
@@ -414,9 +419,11 @@ export function masteringInsertNames(): string[] {
414
419
  * @param name - Insert processor name (see {@link masteringInsertNames}).
415
420
  */
416
421
  export function masteringInsertParamNames(name: string): string[] {
417
- return (
418
- requireModule() as unknown as { masteringInsertParamNames: (name: string) => string[] }
419
- ).masteringInsertParamNames(name);
422
+ return Array.from(
423
+ (
424
+ requireModule() as unknown as { masteringInsertParamNames: (name: string) => string[] }
425
+ ).masteringInsertParamNames(name),
426
+ );
420
427
  }
421
428
 
422
429
  /** One realtime-automatable parameter of an insert processor. */
@@ -495,15 +502,15 @@ export function masteringProcessorCatalog(): MasteringProcessorCatalogEntry[] {
495
502
  }
496
503
 
497
504
  export function masteringPairProcessorNames(): PairProcessor[] {
498
- return requireModule().masteringPairProcessorNames() as PairProcessor[];
505
+ return Array.from(requireModule().masteringPairProcessorNames()) as PairProcessor[];
499
506
  }
500
507
 
501
508
  export function masteringPairAnalysisNames(): PairAnalysis[] {
502
- return requireModule().masteringPairAnalysisNames() as PairAnalysis[];
509
+ return Array.from(requireModule().masteringPairAnalysisNames()) as PairAnalysis[];
503
510
  }
504
511
 
505
512
  export function masteringStereoAnalysisNames(): StereoAnalysis[] {
506
- return requireModule().masteringStereoAnalysisNames() as StereoAnalysis[];
513
+ return Array.from(requireModule().masteringStereoAnalysisNames()) as StereoAnalysis[];
507
514
  }
508
515
 
509
516
  export function masteringProcess(
@@ -945,7 +952,7 @@ export function masteringChainStereoWithProgress(
945
952
  * @returns Preset names in display order (e.g. "pop", "edm", "aiMusic")
946
953
  */
947
954
  export function masteringPresetNames(): MasteringPreset[] {
948
- return requireModule().masteringPresetNames() as MasteringPreset[];
955
+ return Array.from(requireModule().masteringPresetNames()) as MasteringPreset[];
949
956
  }
950
957
 
951
958
  /**
@@ -1034,7 +1041,7 @@ export function masterAudioStereoWithProgress(
1034
1041
  }
1035
1042
 
1036
1043
  export function mixingScenePresetNames(): string[] {
1037
- return requireModule().mixingScenePresetNames();
1044
+ return Array.from(requireModule().mixingScenePresetNames());
1038
1045
  }
1039
1046
 
1040
1047
  /**
@@ -204,15 +204,19 @@ export function analyzeSections(
204
204
  if ((options.minSectionSec ?? 4.0) <= 0) {
205
205
  throw new RangeError('analyzeSections: minSectionSec must be positive');
206
206
  }
207
- return requireModule()
208
- .analyzeSections(
209
- samples,
210
- sampleRate,
211
- options.nFft ?? 2048,
212
- options.hopLength ?? 512,
213
- options.minSectionSec ?? 4.0,
214
- )
215
- .map((s) => ({ ...s, type: s.type as SectionType }));
207
+ // The embind value marshalling returns an array whose constructor is not this
208
+ // realm's Array; chaining .map() onto it propagates that constructor via
209
+ // Symbol.species, leaving a result that structuredClone (and so postMessage to
210
+ // a Worker) rejects with "could not be cloned". Array.from() re-roots it as a
211
+ // plain Array before mapping.
212
+ const sections = requireModule().analyzeSections(
213
+ samples,
214
+ sampleRate,
215
+ options.nFft ?? 2048,
216
+ options.hopLength ?? 512,
217
+ options.minSectionSec ?? 4.0,
218
+ );
219
+ return Array.from(sections, (s) => ({ ...s, type: s.type as SectionType }));
216
220
  }
217
221
 
218
222
  /** Options for {@link analyzeMelody}. All fields are optional. */
package/src/project.ts CHANGED
@@ -626,7 +626,12 @@ interface WasmProject {
626
626
  activeTakeId: number,
627
627
  ) => void;
628
628
  setClipCompSegments: (clipId: number, segments: ReadonlyArray<ProjectClipCompSegment>) => void;
629
- setClipLoop: (clipId: number, loopMode: number, loopLengthPpq: number) => void;
629
+ setClipLoop: (
630
+ clipId: number,
631
+ loopMode: number,
632
+ loopLengthPpq: number,
633
+ loopCrossfadePpq: number,
634
+ ) => void;
630
635
  setClipSource: (clipId: number, sourceId: number) => void;
631
636
  duplicateClip: (clipId: number, newStartPpq: number) => number;
632
637
  removeTrack: (trackId: number) => void;
@@ -827,7 +832,8 @@ export function projectAbiVersion(): number {
827
832
  * names instead of hardcoding magic strings.
828
833
  */
829
834
  export function synthPresetNames(): string[] {
830
- return projectModule().synthPresetNames();
835
+ // Array.from re-roots embind's vector as a plain, structured-cloneable Array.
836
+ return Array.from(projectModule().synthPresetNames());
831
837
  }
832
838
 
833
839
  /**
@@ -837,7 +843,11 @@ export function synthPresetNames(): string[] {
837
843
  * throw.
838
844
  */
839
845
  export function synthPresetPatch(name: string): SynthPatch {
840
- return projectModule().synthPresetPatch(name);
846
+ // embind returns a val::object whose constructor is not this realm's Object, so a
847
+ // direct return is not structured-cloneable (breaks postMessage to a Worker).
848
+ // Spreading into a fresh literal re-roots it as a plain Object; modRoutings is
849
+ // already a plain member array.
850
+ return { ...projectModule().synthPresetPatch(name) };
841
851
  }
842
852
 
843
853
  export function synthEnumTables(): SynthEnumTables {
@@ -1464,9 +1474,23 @@ export class Project {
1464
1474
  this.native.setClipCompSegments(clipId, segments);
1465
1475
  }
1466
1476
 
1467
- /** Set a clip's loop mode + loop length in PPQ (undoable). */
1468
- setClipLoop(clipId: number, loopMode: ProjectLoopMode, loopLengthPpq = 0): void {
1469
- this.native.setClipLoop(clipId, projectLoopModeValue(loopMode), loopLengthPpq);
1477
+ /**
1478
+ * Set a clip's loop mode + loop length in PPQ (undoable). `loopCrossfadePpq`
1479
+ * is an optional equal-power crossfade at the loop seam (PPQ, finite and >= 0;
1480
+ * 0 = hard loop); the engine clamps it to the clip's pre-roll and half the loop.
1481
+ */
1482
+ setClipLoop(
1483
+ clipId: number,
1484
+ loopMode: ProjectLoopMode,
1485
+ loopLengthPpq = 0,
1486
+ loopCrossfadePpq = 0,
1487
+ ): void {
1488
+ this.native.setClipLoop(
1489
+ clipId,
1490
+ projectLoopModeValue(loopMode),
1491
+ loopLengthPpq,
1492
+ loopCrossfadePpq,
1493
+ );
1470
1494
  }
1471
1495
 
1472
1496
  /** Rebind a clip to a different (already-registered) source (undoable). */
@@ -355,6 +355,10 @@ export interface MasteringOptions {
355
355
  ceilingDb?: number;
356
356
  /** Oversampling factor used for peak estimation. Default 4. */
357
357
  truePeakOversample?: number;
358
+ /** Post true-peak limiter release in ms. Default 0 => library default (50 ms). */
359
+ releaseMs?: number;
360
+ /** Apply the static loudness gain at the input (pre-oversample) rate. Default false. */
361
+ applyGainAtInputRate?: boolean;
358
362
  }
359
363
 
360
364
  /** Options for `noteStretch`. All fields are optional. */
@@ -107,20 +107,24 @@ export function detectKeyCandidates(
107
107
  options: KeyDetectionOptions = {},
108
108
  ): KeyCandidate[] {
109
109
  validateAnalysisInput('detectKeyCandidates', samples, sampleRate, options);
110
- return requireModule()
111
- ._detectKeyCandidates(
112
- samples,
113
- sampleRate,
114
- options.nFft ?? 4096,
115
- options.hopLength ?? 512,
116
- options.useHpss ?? false,
117
- options.loudnessWeighted ?? false,
118
- options.highPassHz ?? 0,
119
- keyModeValues(options.modes),
120
- keyProfileValue(options.profile),
121
- options.genreHint ?? '',
122
- )
123
- .map(convertKeyCandidate);
110
+ // The embind value marshalling returns an array whose constructor is not this
111
+ // realm's Array; chaining .map() onto it propagates that constructor via
112
+ // Symbol.species, leaving a result that structuredClone (and so postMessage to
113
+ // a Worker) rejects with "could not be cloned". Array.from() re-roots it as a
114
+ // plain Array before mapping.
115
+ const candidates = requireModule()._detectKeyCandidates(
116
+ samples,
117
+ sampleRate,
118
+ options.nFft ?? 4096,
119
+ options.hopLength ?? 512,
120
+ options.useHpss ?? false,
121
+ options.loudnessWeighted ?? false,
122
+ options.highPassHz ?? 0,
123
+ keyModeValues(options.modes),
124
+ keyProfileValue(options.profile),
125
+ options.genreHint ?? '',
126
+ );
127
+ return Array.from(candidates, convertKeyCandidate);
124
128
  }
125
129
 
126
130
  /**
@@ -259,7 +259,8 @@ export class RealtimeVoiceChanger {
259
259
  }
260
260
 
261
261
  export function realtimeVoiceChangerPresetNames(): VoicePresetId[] {
262
- return getSonareModule().realtimeVoiceChangerPresetNames() as VoicePresetId[];
262
+ // Array.from re-roots embind's vector as a plain, structured-cloneable Array.
263
+ return Array.from(getSonareModule().realtimeVoiceChangerPresetNames()) as VoicePresetId[];
263
264
  }
264
265
 
265
266
  export function realtimeVoiceChangerPresetJson(name: VoicePresetId): string {
@@ -1357,6 +1357,8 @@ export interface SonareModule {
1357
1357
  targetLufs: number,
1358
1358
  ceilingDb: number,
1359
1359
  truePeakOversample: number,
1360
+ releaseMs: number,
1361
+ applyGainAtInputRate: boolean,
1360
1362
  ) => WasmMasteringResult;
1361
1363
  masteringProcessorNames: () => string[];
1362
1364
  masteringPairProcessorNames: () => string[];