@libraz/libsonare 1.6.0 → 1.7.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/README.md +2 -2
- package/dist/analysis.d.ts +139 -11
- package/dist/analysis.js +162 -42
- package/dist/analysis.js.map +1 -1
- package/dist/index.d.ts +211 -39
- package/dist/index.js +501 -142
- package/dist/index.js.map +1 -1
- package/dist/schemas/realtime-voice-changer-preset.schema.json +2 -2
- package/dist/sonare-analysis.js +1 -1
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worker.js +56 -29
- package/dist/worker.js.map +1 -1
- package/dist/worklet.d.ts +67 -18
- package/dist/worklet.js +193 -42
- package/dist/worklet.js.map +1 -1
- package/package.json +3 -3
- package/src/_chain_config.ts +6 -1
- package/src/analysis_helpers.ts +34 -29
- package/src/audio.ts +7 -2
- package/src/codes.ts +47 -11
- package/src/effects_mastering.ts +5 -0
- package/src/effects_transform.ts +202 -22
- package/src/feature_music.ts +8 -1
- package/src/feature_spectral.ts +64 -2
- package/src/feature_spectrogram.ts +44 -3
- package/src/index.ts +28 -3
- package/src/mastering_core.ts +70 -0
- package/src/metering.ts +18 -0
- package/src/mixer.ts +3 -3
- package/src/project.ts +6 -0
- package/src/project_class.ts +143 -16
- package/src/project_internal.ts +14 -5
- package/src/project_types.ts +50 -9
- package/src/public_types_mastering.ts +9 -0
- package/src/public_types_mixing.ts +16 -0
- package/src/public_types_realtime.ts +23 -5
- package/src/public_types_spectral.ts +16 -2
- package/src/quick_analysis.ts +21 -3
- package/src/realtime_engine.ts +14 -3
- package/src/sonare.js.d.ts +87 -0
- package/src/stream_analyzer.ts +6 -1
- package/src/web_midi.ts +10 -7
- package/src/worklet/engine-node.ts +54 -19
- package/src/worklet/engine-parameter-facade.ts +30 -1
- package/src/worklet/engine-processor.ts +52 -20
- package/src/worklet/engine-register.ts +1 -1
- package/src/worklet/engine-strips.ts +2 -2
- package/src/worklet/engine.ts +10 -0
- package/src/worklet/guards.ts +99 -7
- package/src/worklet/messages.ts +41 -1
- package/src/worklet/protocol.ts +4 -0
package/src/project_class.ts
CHANGED
|
@@ -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
|
|
629
|
-
*
|
|
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
|
-
|
|
782
|
-
targetParamId
|
|
783
|
-
|
|
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
|
-
|
|
794
|
-
targetParamId
|
|
795
|
-
|
|
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
|
-
/**
|
|
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);
|
package/src/project_internal.ts
CHANGED
|
@@ -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:
|
|
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',
|
package/src/project_types.ts
CHANGED
|
@@ -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. */
|
|
@@ -296,12 +300,11 @@ export interface SynthModRouting {
|
|
|
296
300
|
*
|
|
297
301
|
* The patch starts from a BASE — the named `preset` (see
|
|
298
302
|
* {@link synthPresetNames}; a `"va:"` routing prefix is accepted) or, when
|
|
299
|
-
* `preset` is omitted, the default subtractive patch.
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* base value. A non-empty `modRoutings` REPLACES the base mod matrix.
|
|
303
|
+
* `preset` is omitted, the default subtractive patch. Omitting a numeric field
|
|
304
|
+
* keeps the base value; supplying one overrides it (clamped to its audible
|
|
305
|
+
* range), including an explicit `0` such as `stereoSpread: 0`. The enum fields
|
|
306
|
+
* reserve `'default'` as keep. A `modRoutings` array REPLACES the base mod
|
|
307
|
+
* matrix, and an empty array clears it, while omitting the key keeps it.
|
|
305
308
|
*
|
|
306
309
|
* Mode-specific deep parameters (FM operator stacks, modal mode tables,
|
|
307
310
|
* drawbar registrations, kit pieces, piano strings) travel inside the named
|
|
@@ -314,6 +317,8 @@ export interface SynthPatch {
|
|
|
314
317
|
* `(destination_id, patch)` bindings instead. Defaults to `0`.
|
|
315
318
|
*/
|
|
316
319
|
destinationId?: number;
|
|
320
|
+
/** Resolve MIDI channels from incoming GM bank/program changes; defaults to false. */
|
|
321
|
+
useGmPrograms?: boolean;
|
|
317
322
|
/** Base preset name (see {@link synthPresetNames}); omit for the init patch. */
|
|
318
323
|
preset?: string;
|
|
319
324
|
engineMode?: SynthEngineMode | number;
|
|
@@ -335,12 +340,10 @@ export interface SynthPatch {
|
|
|
335
340
|
velToCutoffCents?: number;
|
|
336
341
|
ampAttackMs?: number;
|
|
337
342
|
ampDecayMs?: number;
|
|
338
|
-
/** 0 / omit keeps the base value; explicit zero sustain is not representable. */
|
|
339
343
|
ampSustain?: number;
|
|
340
344
|
ampReleaseMs?: number;
|
|
341
345
|
filterAttackMs?: number;
|
|
342
346
|
filterDecayMs?: number;
|
|
343
|
-
/** 0 / omit keeps the base value; explicit zero sustain is not representable. */
|
|
344
347
|
filterSustain?: number;
|
|
345
348
|
filterReleaseMs?: number;
|
|
346
349
|
lfoRateHz?: number;
|
|
@@ -448,12 +451,34 @@ export interface ProjectAutomationPoint {
|
|
|
448
451
|
curve?: ProjectAutomationCurve;
|
|
449
452
|
}
|
|
450
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Persistent target classification for an automation lane.
|
|
456
|
+
*
|
|
457
|
+
* The numeric ordinals mirror `SonareAutomationTargetKind`; the string names
|
|
458
|
+
* are the public WASM spellings accepted by the project facade.
|
|
459
|
+
*/
|
|
460
|
+
export const AutomationTargetKind = {
|
|
461
|
+
opaque: 0,
|
|
462
|
+
trackFaderDb: 1,
|
|
463
|
+
trackPan: 2,
|
|
464
|
+
} as const;
|
|
465
|
+
|
|
466
|
+
/** Accepted automation target kind name or ABI ordinal. */
|
|
467
|
+
export type ProjectAutomationTargetKind = 'opaque' | 'track-fader-db' | 'track-pan' | 0 | 1 | 2;
|
|
468
|
+
|
|
469
|
+
/** Numeric aliases matching the C-ABI enum names. */
|
|
470
|
+
export const PROJECT_AUTOMATION_TARGET_OPAQUE = AutomationTargetKind.opaque;
|
|
471
|
+
export const PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB = AutomationTargetKind.trackFaderDb;
|
|
472
|
+
export const PROJECT_AUTOMATION_TARGET_TRACK_PAN = AutomationTargetKind.trackPan;
|
|
473
|
+
|
|
451
474
|
/** Automation-lane descriptor for {@link Project.addAutomationLane}. */
|
|
452
475
|
export interface ProjectAutomationLaneDesc {
|
|
453
|
-
/** Host-defined id of the parameter the lane drives. */
|
|
476
|
+
/** Host-defined, non-zero id of the parameter the lane drives (zero is reserved). */
|
|
454
477
|
targetParamId: number;
|
|
455
478
|
/** Breakpoints (stored verbatim). */
|
|
456
479
|
points: ReadonlyArray<ProjectAutomationPoint>;
|
|
480
|
+
/** Optional persistent target classification; omission retains the legacy opaque route. */
|
|
481
|
+
targetKind?: ProjectAutomationTargetKind;
|
|
457
482
|
}
|
|
458
483
|
|
|
459
484
|
/** One tempo segment for {@link Project.setTempoSegments}. */
|
|
@@ -515,6 +540,22 @@ export interface ProjectChordSymbol {
|
|
|
515
540
|
modulationBoundary?: boolean;
|
|
516
541
|
}
|
|
517
542
|
|
|
543
|
+
/** Descriptor accepted by {@link Project.setAssistSidecar}. */
|
|
544
|
+
export interface ProjectAssistSidecarInput {
|
|
545
|
+
/** Non-empty module id key. */
|
|
546
|
+
moduleId: string;
|
|
547
|
+
/** Module-defined schema version. Defaults to `0`. */
|
|
548
|
+
schemaVersion?: number;
|
|
549
|
+
/** Target track id (`0` = project scope). Defaults to `0`. */
|
|
550
|
+
targetTrackId?: number;
|
|
551
|
+
/** Region start in PPQ. Defaults to `0`. */
|
|
552
|
+
regionStartPpq?: number;
|
|
553
|
+
/** Region end in PPQ. Defaults to `0`. */
|
|
554
|
+
regionEndPpq?: number;
|
|
555
|
+
/** Opaque module-owned payload bytes. Defaults to an empty payload. */
|
|
556
|
+
payload?: Uint8Array;
|
|
557
|
+
}
|
|
558
|
+
|
|
518
559
|
/** Assist sidecar snapshot returned by {@link Project.getAssistSidecar}. */
|
|
519
560
|
export interface ProjectAssistSidecar {
|
|
520
561
|
moduleId: string;
|
|
@@ -352,6 +352,15 @@ export interface MasteringChainConfig {
|
|
|
352
352
|
releaseMs?: number;
|
|
353
353
|
applyGainAtInputRate?: boolean;
|
|
354
354
|
};
|
|
355
|
+
/**
|
|
356
|
+
* Dot-notation spelling of any leaf above, e.g. `'loudness.targetLufs': -20`
|
|
357
|
+
* beside or instead of `loudness: { targetLufs: -20 }`. It is the form the C
|
|
358
|
+
* ABI carries parameters in, so a caller assembling overrides dynamically can
|
|
359
|
+
* emit it directly; the core validates the key and rejects an unknown one.
|
|
360
|
+
* The nested spelling is canonical — prefer it in hand-written code, where it
|
|
361
|
+
* is checked field by field while a dotted key is only checked at run time.
|
|
362
|
+
*/
|
|
363
|
+
[flatKey: `${string}.${string}`]: number | boolean | undefined;
|
|
355
364
|
}
|
|
356
365
|
|
|
357
366
|
/**
|
|
@@ -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
|
|
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
|
|
111
|
-
name
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
112
121
|
description?: string;
|
|
113
|
-
|
|
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
|
/**
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import type { ValidateOptions } from './validation';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Per-frame voicing decision, one entry per `f0Hz` frame. A truthy or non-zero
|
|
5
|
+
* entry marks the frame voiced. The union covers what the analysis side hands
|
|
6
|
+
* back — `PitchResult.voicedFlag` is a `boolean[]` — as well as the typed and
|
|
7
|
+
* plain numeric arrays a caller may build directly, so a pitch track can be fed
|
|
8
|
+
* straight into pitch correction without a conversion step.
|
|
9
|
+
*/
|
|
10
|
+
export type VoicedFlags =
|
|
11
|
+
| Int32Array
|
|
12
|
+
| Uint8Array
|
|
13
|
+
| Float32Array
|
|
14
|
+
| readonly number[]
|
|
15
|
+
| readonly boolean[];
|
|
16
|
+
|
|
3
17
|
/** Options for `pitchCorrectTimevarying`. All fields are optional. */
|
|
4
18
|
export interface PitchCorrectOptions extends ValidateOptions {
|
|
5
19
|
/** `'midi'` retunes toward `targetMidi`; `'scale'` snaps to the key. Default `'midi'`. */
|
|
@@ -20,8 +34,8 @@ export interface PitchCorrectOptions extends ValidateOptions {
|
|
|
20
34
|
retuneSpeedMs?: number;
|
|
21
35
|
/** Corrections below this are bypassed to preserve vibrato (cents). Default 20. */
|
|
22
36
|
vibratoThresholdCents?: number;
|
|
23
|
-
/** Per-frame voiced flags (
|
|
24
|
-
voiced?:
|
|
37
|
+
/** Per-frame voiced flags (truthy = voiced); omit to treat all frames as voiced. */
|
|
38
|
+
voiced?: VoicedFlags;
|
|
25
39
|
/** Per-frame voicing probability in `[0, 1]`; omit to derive from `voiced`. */
|
|
26
40
|
voicedProb?: Float32Array;
|
|
27
41
|
}
|
package/src/quick_analysis.ts
CHANGED
|
@@ -29,7 +29,12 @@ import type {
|
|
|
29
29
|
import { Mode, PitchClass } from './public_types';
|
|
30
30
|
import type { ProgressCallback, WasmAcousticResult } from './sonare.js';
|
|
31
31
|
import type { ValidateOptions } from './validation';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
assertFiniteScalar,
|
|
34
|
+
assertNonNegativeInteger,
|
|
35
|
+
assertSampleRate,
|
|
36
|
+
assertSamples,
|
|
37
|
+
} from './validation';
|
|
33
38
|
|
|
34
39
|
function requireModule() {
|
|
35
40
|
return getSonareModule();
|
|
@@ -81,6 +86,7 @@ export interface ChordFunctionalAnalysisRequest extends DetectChordsRequest {
|
|
|
81
86
|
/** Canonical request form for impulse-response analysis. */
|
|
82
87
|
export interface AnalyzeImpulseResponseRequest extends SamplesRequest {
|
|
83
88
|
nOctaveBands?: number;
|
|
89
|
+
minDecayDb?: number;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
/** Canonical request form for acoustic analysis. */
|
|
@@ -435,23 +441,35 @@ export function analyzeImpulseResponse(
|
|
|
435
441
|
samples: Float32Array,
|
|
436
442
|
sampleRate?: number,
|
|
437
443
|
nOctaveBands?: number,
|
|
444
|
+
minDecayDb?: number,
|
|
438
445
|
): AcousticResult;
|
|
439
446
|
export function analyzeImpulseResponse(
|
|
440
447
|
samples: Float32Array | AnalyzeImpulseResponseRequest,
|
|
441
448
|
sampleRate = 48000,
|
|
442
449
|
nOctaveBands = 6,
|
|
450
|
+
minDecayDb?: number,
|
|
443
451
|
): AcousticResult {
|
|
444
|
-
const request =
|
|
452
|
+
const request =
|
|
453
|
+
samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands, minDecayDb } : samples;
|
|
454
|
+
if (request.minDecayDb === null) {
|
|
455
|
+
throw new TypeError('analyzeImpulseResponse: minDecayDb must be a finite number');
|
|
456
|
+
}
|
|
457
|
+
const resolvedMinDecayDb = request.minDecayDb === undefined ? 30.0 : request.minDecayDb;
|
|
458
|
+
assertFiniteScalar('analyzeImpulseResponse', resolvedMinDecayDb, 'minDecayDb');
|
|
459
|
+
if (resolvedMinDecayDb <= 0) {
|
|
460
|
+
throw new RangeError('analyzeImpulseResponse: minDecayDb must be greater than zero');
|
|
461
|
+
}
|
|
445
462
|
validateAnalysisInput(
|
|
446
463
|
'analyzeImpulseResponse',
|
|
447
464
|
request.samples,
|
|
448
465
|
request.sampleRate ?? 48000,
|
|
449
466
|
request,
|
|
450
467
|
);
|
|
451
|
-
const result: WasmAcousticResult = requireModule().
|
|
468
|
+
const result: WasmAcousticResult = requireModule().analyzeImpulseResponseEx(
|
|
452
469
|
request.samples,
|
|
453
470
|
request.sampleRate ?? 48000,
|
|
454
471
|
request.nOctaveBands ?? 6,
|
|
472
|
+
resolvedMinDecayDb,
|
|
455
473
|
);
|
|
456
474
|
return result;
|
|
457
475
|
}
|
package/src/realtime_engine.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { panLawCode, panModeCode, sendTimingCode } from './codes';
|
|
1
|
+
import { panLawCode, panModeCode, sendTimingCode, trackMonitorModeCode } from './codes';
|
|
2
2
|
import { ErrorCode, SonareError } from './errors';
|
|
3
3
|
import { getSonareModule } from './module_state';
|
|
4
4
|
import type { ProjectMidiCcBinding, SynthPatch } from './project';
|
|
5
|
-
import type { EqBand,
|
|
5
|
+
import type { EqBand, PanLawInput, PanMode, SendTiming } from './public_types';
|
|
6
6
|
import type {
|
|
7
7
|
WasmClipPageRequest,
|
|
8
8
|
WasmEngineAutomationPoint,
|
|
@@ -78,6 +78,12 @@ export interface EngineTrackLane {
|
|
|
78
78
|
sourceChannelLayout?: number;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** Per-track cue/monitor tap mode: off, pre-fader listen, or after-fader listen. */
|
|
82
|
+
export type EngineTrackMonitorMode = 'off' | 'pfl' | 'afl' | 0 | 1 | 2;
|
|
83
|
+
|
|
84
|
+
/** Short alias for {@link EngineTrackMonitorMode}. */
|
|
85
|
+
export type TrackMonitorMode = EngineTrackMonitorMode;
|
|
86
|
+
|
|
81
87
|
export interface EngineBus {
|
|
82
88
|
busId: number;
|
|
83
89
|
gainDb?: number;
|
|
@@ -219,6 +225,11 @@ export class RealtimeEngine {
|
|
|
219
225
|
this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
|
|
220
226
|
}
|
|
221
227
|
|
|
228
|
+
/** Queue a per-track PFL/AFL monitor tap mode change. */
|
|
229
|
+
setTrackMonitorMode(laneIndex: number, mode: EngineTrackMonitorMode, renderFrame = -1): void {
|
|
230
|
+
this.native.setTrackMonitorMode(laneIndex, trackMonitorModeCode(mode), renderFrame);
|
|
231
|
+
}
|
|
232
|
+
|
|
222
233
|
setMidiClips(clips: readonly EngineMidiClipSchedule[]): void {
|
|
223
234
|
this.native.setMidiClips(clips);
|
|
224
235
|
}
|
|
@@ -831,7 +842,7 @@ export class RealtimeEngine {
|
|
|
831
842
|
}
|
|
832
843
|
|
|
833
844
|
/** Sets a track lane strip's pan law in realtime. */
|
|
834
|
-
setTrackStripPanLaw(trackId: number, panLaw:
|
|
845
|
+
setTrackStripPanLaw(trackId: number, panLaw: PanLawInput): void {
|
|
835
846
|
this.native.setTrackStripPanLaw(trackId, panLawCode(panLaw));
|
|
836
847
|
}
|
|
837
848
|
|