@libraz/libsonare 1.3.3 → 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/dist/index.d.ts +1 -1
- package/dist/index.js +202 -21
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +2 -2
- package/dist/sonare-rt.js +2 -2
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +589 -156
- package/dist/worklet.js +622 -110
- package/dist/worklet.js.map +1 -1
- package/package.json +1 -1
- package/src/codes.ts +6 -1
- package/src/effects_mastering.ts +119 -10
- package/src/feature_music.ts +31 -13
- package/src/feature_spectral.ts +7 -1
- package/src/index.ts +14 -0
- package/src/mixer.ts +9 -0
- package/src/project.ts +104 -6
- package/src/public_types.ts +56 -0
- package/src/quick_analysis.ts +18 -14
- package/src/realtime_engine.ts +141 -2
- package/src/realtime_voice_changer.ts +2 -1
- package/src/sonare.js.d.ts +83 -0
- package/src/stream_types.ts +7 -0
- package/src/validation.ts +7 -0
- package/src/worklet/audio_types.ts +2 -0
- package/src/worklet/guards.ts +146 -0
- package/src/worklet/messages.ts +461 -0
- package/src/worklet/protocol.ts +767 -0
- package/src/worklet.ts +541 -1106
package/src/quick_analysis.ts
CHANGED
|
@@ -107,20 +107,24 @@ export function detectKeyCandidates(
|
|
|
107
107
|
options: KeyDetectionOptions = {},
|
|
108
108
|
): KeyCandidate[] {
|
|
109
109
|
validateAnalysisInput('detectKeyCandidates', samples, sampleRate, options);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.
|
|
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
|
/**
|
package/src/realtime_engine.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { panLawCode, panModeCode, sendTimingCode } from './codes';
|
|
1
2
|
import { ErrorCode, SonareError } from './errors';
|
|
2
3
|
import { getSonareModule } from './module_state';
|
|
3
4
|
import type { SynthPatch } from './project';
|
|
4
|
-
import type { EqBand } from './public_types';
|
|
5
|
+
import type { EqBand, PanLaw, PanMode, SendTiming } from './public_types';
|
|
5
6
|
import type {
|
|
6
7
|
WasmClipPageRequest,
|
|
7
8
|
WasmEngineAutomationPoint,
|
|
@@ -14,9 +15,11 @@ import type {
|
|
|
14
15
|
WasmEngineGraphSpec,
|
|
15
16
|
WasmEngineMarker,
|
|
16
17
|
WasmEngineMeterTelemetry,
|
|
18
|
+
WasmEngineMeterTelemetryWide,
|
|
17
19
|
WasmEngineMetronomeConfig,
|
|
18
20
|
WasmEngineParameterInfo,
|
|
19
21
|
WasmEngineProcessWithMonitorResult,
|
|
22
|
+
WasmEngineScopeTelemetry,
|
|
20
23
|
WasmEngineTelemetry,
|
|
21
24
|
WasmEngineTempoSegment,
|
|
22
25
|
WasmEngineTimeSignatureSegment,
|
|
@@ -38,6 +41,8 @@ export type EngineFreezeOptions = WasmEngineFreezeOptions;
|
|
|
38
41
|
export type EngineFreezeResult = WasmEngineFreezeResult;
|
|
39
42
|
export type EngineTelemetry = WasmEngineTelemetry;
|
|
40
43
|
export type EngineMeterTelemetry = WasmEngineMeterTelemetry;
|
|
44
|
+
export type EngineMeterTelemetryWide = WasmEngineMeterTelemetryWide;
|
|
45
|
+
export type EngineScopeTelemetry = WasmEngineScopeTelemetry;
|
|
41
46
|
export type EngineTransportState = WasmEngineTransportState;
|
|
42
47
|
export type EngineTempoSegment = WasmEngineTempoSegment;
|
|
43
48
|
export type EngineTimeSignatureSegment = WasmEngineTimeSignatureSegment;
|
|
@@ -46,16 +51,39 @@ export interface EngineTrackSend {
|
|
|
46
51
|
busId: number;
|
|
47
52
|
levelDb?: number;
|
|
48
53
|
enabled?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Pre/post-fader tap point. Defaults to post-fader when omitted, matching the
|
|
56
|
+
* historical lane-send behavior and the scene-JSON default.
|
|
57
|
+
*/
|
|
58
|
+
sendTiming?: SendTiming | number;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
export interface EngineTrackLane {
|
|
52
62
|
trackId: number;
|
|
53
63
|
sends?: EngineTrackSend[];
|
|
64
|
+
/**
|
|
65
|
+
* Bus the lane's post-fader output sums into instead of the master mix
|
|
66
|
+
* (group/folder routing); 0 or absent keeps the lane on the master mix.
|
|
67
|
+
*/
|
|
68
|
+
outputBusId?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Input channel layout of the source feeding this lane (`SonareChannelLayout`:
|
|
71
|
+
* 0 mono, 1 stereo, 2 5.1, 3 7.1). Absent defaults to stereo. Stored but inert
|
|
72
|
+
* until the surround DSP path lands.
|
|
73
|
+
*/
|
|
74
|
+
sourceChannelLayout?: number;
|
|
54
75
|
}
|
|
55
76
|
|
|
56
77
|
export interface EngineBus {
|
|
57
78
|
busId: number;
|
|
58
79
|
gainDb?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Channel layout of the bus (`SonareChannelLayout`: 0 mono, 1 stereo, 2 5.1,
|
|
82
|
+
* 3 7.1). A surround layout makes this a surround group bus: lanes routed to
|
|
83
|
+
* it are surround-panned and it sums into the master plane-by-plane. Defaults
|
|
84
|
+
* to stereo.
|
|
85
|
+
*/
|
|
86
|
+
channelLayout?: number;
|
|
59
87
|
}
|
|
60
88
|
|
|
61
89
|
export interface EngineMidiEvent {
|
|
@@ -382,6 +410,16 @@ export class RealtimeEngine {
|
|
|
382
410
|
this.native.seekSample(timelineSample, renderFrame);
|
|
383
411
|
}
|
|
384
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Snaps every in-flight parameter ramp (engine-level smoothed params, mixer
|
|
415
|
+
* lane fader/pan/gate, bus gains) to its target value. Offline renders call
|
|
416
|
+
* this after a priming process() block so the first audible block renders at
|
|
417
|
+
* settled values instead of ramping in from defaults.
|
|
418
|
+
*/
|
|
419
|
+
settleParameters(): void {
|
|
420
|
+
this.native.settleParameters();
|
|
421
|
+
}
|
|
422
|
+
|
|
385
423
|
seekPpq(ppq: number, renderFrame = -1): void {
|
|
386
424
|
this.native.seekPpq(ppq, renderFrame);
|
|
387
425
|
}
|
|
@@ -500,10 +538,35 @@ export class RealtimeEngine {
|
|
|
500
538
|
|
|
501
539
|
setTrackLanes(lanes: Array<number | EngineTrackLane>): void {
|
|
502
540
|
this.native.setTrackLanes(
|
|
503
|
-
lanes.map((lane) =>
|
|
541
|
+
lanes.map((lane) => {
|
|
542
|
+
if (typeof lane === 'number') {
|
|
543
|
+
return { trackId: lane };
|
|
544
|
+
}
|
|
545
|
+
if (!lane.sends) {
|
|
546
|
+
return lane;
|
|
547
|
+
}
|
|
548
|
+
// Normalize each send's pre/post tap point to the integer the native
|
|
549
|
+
// layer reads (defaults to post-fader when omitted).
|
|
550
|
+
return {
|
|
551
|
+
...lane,
|
|
552
|
+
sends: lane.sends.map((send) => ({
|
|
553
|
+
...send,
|
|
554
|
+
// Post-fader (0) is the default for an omitted sendTiming.
|
|
555
|
+
sendTiming: send.sendTiming === undefined ? 0 : sendTimingCode(send.sendTiming),
|
|
556
|
+
})),
|
|
557
|
+
};
|
|
558
|
+
}),
|
|
504
559
|
);
|
|
505
560
|
}
|
|
506
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Keys one insert of a lane strip from another lane's post-strip audio
|
|
564
|
+
* (ducking/sidechainRouter inserts). sourceTrackId 0 removes the binding.
|
|
565
|
+
*/
|
|
566
|
+
setLaneSidechain(trackId: number, insertIndex: number, sourceTrackId: number): void {
|
|
567
|
+
this.native.setLaneSidechain(trackId, insertIndex, sourceTrackId);
|
|
568
|
+
}
|
|
569
|
+
|
|
507
570
|
setTrackBuses(buses: EngineBus[]): void {
|
|
508
571
|
this.native.setTrackBuses(buses);
|
|
509
572
|
}
|
|
@@ -578,6 +641,55 @@ export class RealtimeEngine {
|
|
|
578
641
|
this.native.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
|
|
579
642
|
}
|
|
580
643
|
|
|
644
|
+
/**
|
|
645
|
+
* Changes one track-strip insert parameter in realtime, addressed by the
|
|
646
|
+
* processor's JSON-key parameter name (see {@link masteringInsertParamInfo}).
|
|
647
|
+
* Applied at the next block head via the engine command queue; safe during
|
|
648
|
+
* playback. Throws if the track, insert, or name is unknown, the param is not
|
|
649
|
+
* realtime-safe, or the command queue is full.
|
|
650
|
+
*/
|
|
651
|
+
setTrackStripInsertParamByName(
|
|
652
|
+
trackId: number,
|
|
653
|
+
insertIndex: number,
|
|
654
|
+
paramName: string,
|
|
655
|
+
value: number,
|
|
656
|
+
): void {
|
|
657
|
+
this.native.setTrackStripInsertParamByName(trackId, insertIndex, paramName, value);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Master-strip counterpart of {@link setTrackStripInsertParamByName}. */
|
|
661
|
+
setMasterStripInsertParamByName(insertIndex: number, paramName: string, value: number): void {
|
|
662
|
+
this.native.setMasterStripInsertParamByName(insertIndex, paramName, value);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** Sets a track lane strip's pan position in realtime (glitch-free). */
|
|
666
|
+
setTrackStripPan(trackId: number, pan: number): void {
|
|
667
|
+
this.native.setTrackStripPan(trackId, pan);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/** Sets a track lane strip's pan law in realtime. */
|
|
671
|
+
setTrackStripPanLaw(trackId: number, panLaw: PanLaw | number): void {
|
|
672
|
+
this.native.setTrackStripPanLaw(trackId, panLawCode(panLaw));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** Sets a track lane strip's pan mode in realtime. */
|
|
676
|
+
setTrackStripPanMode(trackId: number, panMode: PanMode | number): void {
|
|
677
|
+
this.native.setTrackStripPanMode(trackId, panModeCode(panMode));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/** Sets a track lane strip's dual-pan left/right positions in realtime. */
|
|
681
|
+
setTrackStripDualPan(trackId: number, leftPan: number, rightPan: number): void {
|
|
682
|
+
this.native.setTrackStripDualPan(trackId, leftPan, rightPan);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Sets a track lane strip's inter-channel alignment delay (whole samples).
|
|
687
|
+
* Adjusts strip latency, so PDC and reported graph latency are refreshed.
|
|
688
|
+
*/
|
|
689
|
+
setTrackStripChannelDelaySamples(trackId: number, delaySamples: number): void {
|
|
690
|
+
this.native.setTrackStripChannelDelaySamples(trackId, delaySamples);
|
|
691
|
+
}
|
|
692
|
+
|
|
581
693
|
createClipPageProvider(
|
|
582
694
|
numChannels: number,
|
|
583
695
|
numSamples: number,
|
|
@@ -695,6 +807,33 @@ export class RealtimeEngine {
|
|
|
695
807
|
return this.native.drainMeterTelemetry(maxRecords);
|
|
696
808
|
}
|
|
697
809
|
|
|
810
|
+
/**
|
|
811
|
+
* Drains pending meter telemetry as per-plane (wide) records for a surround
|
|
812
|
+
* target. Use this for a surround mix target; {@link drainMeterTelemetry}
|
|
813
|
+
* stays the stereo fast path. The two share one queue — call only one per
|
|
814
|
+
* target. The live AudioWorklet path owns the queue via the stereo drain, so
|
|
815
|
+
* this wide drain is for an offline (non-worklet) engine instance; per-plane
|
|
816
|
+
* surround meters are not delivered over the live worklet meter ring.
|
|
817
|
+
*/
|
|
818
|
+
drainMeterTelemetryWide(maxRecords = 1024): EngineMeterTelemetryWide[] {
|
|
819
|
+
return this.native.drainMeterTelemetryWide(maxRecords);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Enables per-target spectrum + vectorscope capture. @param intervalFrames is
|
|
824
|
+
* the minimum render-frame gap between snapshots (0 disables). @param bandCount
|
|
825
|
+
* is the FFT band resolution (1..64); changing it re-prepares the tap. Returns
|
|
826
|
+
* the band count actually applied.
|
|
827
|
+
*/
|
|
828
|
+
configureScopeTelemetry(intervalFrames: number, bandCount: number): number {
|
|
829
|
+
return this.native.configureScopeTelemetry(intervalFrames, bandCount);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** Drains pending spectrum + vectorscope snapshots (per mix target). */
|
|
833
|
+
drainScopeTelemetry(maxRecords = 1024): EngineScopeTelemetry[] {
|
|
834
|
+
return this.native.drainScopeTelemetry(maxRecords);
|
|
835
|
+
}
|
|
836
|
+
|
|
698
837
|
destroy(): void {
|
|
699
838
|
this.native.delete();
|
|
700
839
|
}
|
|
@@ -259,7 +259,8 @@ export class RealtimeVoiceChanger {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
export function realtimeVoiceChangerPresetNames(): VoicePresetId[] {
|
|
262
|
-
|
|
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 {
|
package/src/sonare.js.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Type declarations for the Emscripten-generated WASM module with embind
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { SpectralRegionOp, SurroundPan } from './public_types';
|
|
6
|
+
|
|
5
7
|
export interface SonareModuleOptions {
|
|
6
8
|
locateFile?: (path: string, prefix: string) => string;
|
|
7
9
|
wasmBinary?: ArrayBuffer | Uint8Array;
|
|
@@ -542,11 +544,22 @@ export interface WasmEngineTrackSend {
|
|
|
542
544
|
export interface WasmEngineTrackLane {
|
|
543
545
|
trackId: number;
|
|
544
546
|
sends?: WasmEngineTrackSend[];
|
|
547
|
+
/**
|
|
548
|
+
* Bus the lane's post-fader output sums into instead of the master mix
|
|
549
|
+
* (group/folder routing); 0 or absent keeps the lane on the master mix.
|
|
550
|
+
*/
|
|
551
|
+
outputBusId?: number;
|
|
552
|
+
/**
|
|
553
|
+
* Input channel layout of the source feeding this lane (`SonareChannelLayout`:
|
|
554
|
+
* 0 mono, 1 stereo, 2 5.1, 3 7.1). Absent defaults to stereo.
|
|
555
|
+
*/
|
|
556
|
+
sourceChannelLayout?: number;
|
|
545
557
|
}
|
|
546
558
|
|
|
547
559
|
export interface WasmEngineBus {
|
|
548
560
|
busId: number;
|
|
549
561
|
gainDb?: number;
|
|
562
|
+
channelLayout?: number;
|
|
550
563
|
}
|
|
551
564
|
|
|
552
565
|
export interface WasmClipPageRequest {
|
|
@@ -576,6 +589,18 @@ export interface WasmEngineMarker {
|
|
|
576
589
|
id: number;
|
|
577
590
|
ppq: number;
|
|
578
591
|
name?: string;
|
|
592
|
+
kind?: number;
|
|
593
|
+
keyFifths?: number;
|
|
594
|
+
keyMinor?: boolean;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export interface WasmProjectMarker {
|
|
598
|
+
id: number;
|
|
599
|
+
ppq: number;
|
|
600
|
+
name?: string;
|
|
601
|
+
kind?: number;
|
|
602
|
+
keyFifths?: number;
|
|
603
|
+
keyMinor?: boolean;
|
|
579
604
|
}
|
|
580
605
|
|
|
581
606
|
export interface WasmEngineMetronomeConfig {
|
|
@@ -646,6 +671,38 @@ export interface WasmEngineMeterTelemetry {
|
|
|
646
671
|
droppedRecords: number;
|
|
647
672
|
}
|
|
648
673
|
|
|
674
|
+
export interface WasmEngineMeterTelemetryWide {
|
|
675
|
+
targetId: number;
|
|
676
|
+
renderFrame: number;
|
|
677
|
+
seq: number;
|
|
678
|
+
channelCount: number;
|
|
679
|
+
peakDb: number[];
|
|
680
|
+
rmsDb: number[];
|
|
681
|
+
truePeakDb: number[];
|
|
682
|
+
maxTruePeakDb: number;
|
|
683
|
+
correlation: number;
|
|
684
|
+
monoCompatWidth: number;
|
|
685
|
+
momentaryLufs: number;
|
|
686
|
+
shortTermLufs: number;
|
|
687
|
+
integratedLufs: number;
|
|
688
|
+
gainReductionDb: number;
|
|
689
|
+
droppedRecords: number;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export interface WasmEngineScopeTelemetry {
|
|
693
|
+
targetId: number;
|
|
694
|
+
renderFrame: number;
|
|
695
|
+
seq: number;
|
|
696
|
+
droppedRecords: number;
|
|
697
|
+
bands: number[];
|
|
698
|
+
/**
|
|
699
|
+
* Goniometer/vectorscope sample points as `{ left, right }` objects. The
|
|
700
|
+
* Python surface exposes the same data as `(left, right)` tuples; this
|
|
701
|
+
* representation difference is intentional, the values match.
|
|
702
|
+
*/
|
|
703
|
+
points: { left: number; right: number }[];
|
|
704
|
+
}
|
|
705
|
+
|
|
649
706
|
export interface WasmEngineCaptureStatus {
|
|
650
707
|
capturedFrames: number;
|
|
651
708
|
overflowCount: number;
|
|
@@ -740,6 +797,7 @@ export interface WasmRealtimeEngine {
|
|
|
740
797
|
play: (renderFrame: number) => void;
|
|
741
798
|
stop: (renderFrame: number) => void;
|
|
742
799
|
seekSample: (timelineSample: number, renderFrame: number) => void;
|
|
800
|
+
settleParameters: () => void;
|
|
743
801
|
seekPpq: (ppq: number, renderFrame: number) => void;
|
|
744
802
|
setTempo: (bpm: number) => void;
|
|
745
803
|
setTempoSegments: (segments: WasmEngineTempoSegment[]) => void;
|
|
@@ -768,6 +826,7 @@ export interface WasmRealtimeEngine {
|
|
|
768
826
|
setClips: (clips: WasmEngineClip[]) => void;
|
|
769
827
|
clipCount: () => number;
|
|
770
828
|
setTrackLanes: (lanes: Array<number | WasmEngineTrackLane>) => void;
|
|
829
|
+
setLaneSidechain: (trackId: number, insertIndex: number, sourceTrackId: number) => void;
|
|
771
830
|
setTrackBuses: (buses: WasmEngineBus[]) => void;
|
|
772
831
|
setBusStripJson: (busId: number, sceneJson: string) => void;
|
|
773
832
|
setTrackStripJson: (trackId: number, sceneJson: string) => void;
|
|
@@ -785,6 +844,18 @@ export interface WasmRealtimeEngine {
|
|
|
785
844
|
bypassed: boolean,
|
|
786
845
|
resetOnBypass: boolean,
|
|
787
846
|
) => void;
|
|
847
|
+
setTrackStripInsertParamByName: (
|
|
848
|
+
trackId: number,
|
|
849
|
+
insertIndex: number,
|
|
850
|
+
paramName: string,
|
|
851
|
+
value: number,
|
|
852
|
+
) => void;
|
|
853
|
+
setMasterStripInsertParamByName: (insertIndex: number, paramName: string, value: number) => void;
|
|
854
|
+
setTrackStripPan: (trackId: number, pan: number) => void;
|
|
855
|
+
setTrackStripPanLaw: (trackId: number, panLaw: number) => void;
|
|
856
|
+
setTrackStripPanMode: (trackId: number, panMode: number) => void;
|
|
857
|
+
setTrackStripDualPan: (trackId: number, leftPan: number, rightPan: number) => void;
|
|
858
|
+
setTrackStripChannelDelaySamples: (trackId: number, delaySamples: number) => void;
|
|
788
859
|
createClipPageProvider: (numChannels: number, numSamples: number, pageFrames: number) => number;
|
|
789
860
|
supplyClipPage: (providerId: number, pageIndex: number, channels: Float32Array[]) => void;
|
|
790
861
|
clearClipPage: (providerId: number, pageIndex: number) => void;
|
|
@@ -877,6 +948,9 @@ export interface WasmRealtimeEngine {
|
|
|
877
948
|
freezeOffline: (options: WasmEngineFreezeOptions) => WasmEngineFreezeResult;
|
|
878
949
|
drainTelemetry: (maxRecords: number) => WasmEngineTelemetry[];
|
|
879
950
|
drainMeterTelemetry: (maxRecords: number) => WasmEngineMeterTelemetry[];
|
|
951
|
+
drainMeterTelemetryWide: (maxRecords: number) => WasmEngineMeterTelemetryWide[];
|
|
952
|
+
configureScopeTelemetry: (intervalFrames: number, bandCount: number) => number;
|
|
953
|
+
drainScopeTelemetry: (maxRecords: number) => WasmEngineScopeTelemetry[];
|
|
880
954
|
delete: () => void;
|
|
881
955
|
}
|
|
882
956
|
|
|
@@ -1283,6 +1357,8 @@ export interface SonareModule {
|
|
|
1283
1357
|
targetLufs: number,
|
|
1284
1358
|
ceilingDb: number,
|
|
1285
1359
|
truePeakOversample: number,
|
|
1360
|
+
releaseMs: number,
|
|
1361
|
+
applyGainAtInputRate: boolean,
|
|
1286
1362
|
) => WasmMasteringResult;
|
|
1287
1363
|
masteringProcessorNames: () => string[];
|
|
1288
1364
|
masteringPairProcessorNames: () => string[];
|
|
@@ -1449,6 +1525,12 @@ export interface SonareModule {
|
|
|
1449
1525
|
options: Record<string, unknown>,
|
|
1450
1526
|
) => WasmMixResult;
|
|
1451
1527
|
trim: (samples: Float32Array, sampleRate: number, thresholdDb: number) => Float32Array;
|
|
1528
|
+
spectralEdit: (
|
|
1529
|
+
samples: Float32Array,
|
|
1530
|
+
sampleRate: number,
|
|
1531
|
+
ops: SpectralRegionOp[],
|
|
1532
|
+
options: Record<string, unknown>,
|
|
1533
|
+
) => Float32Array;
|
|
1452
1534
|
|
|
1453
1535
|
// Features - Spectrogram
|
|
1454
1536
|
stft: (
|
|
@@ -2033,6 +2115,7 @@ export interface WasmMixer {
|
|
|
2033
2115
|
setChannelDelaySamples: (stripIndex: number, delaySamples: number) => void;
|
|
2034
2116
|
setVcaOffsetDb: (stripIndex: number, offsetDb: number) => void;
|
|
2035
2117
|
setDualPan: (stripIndex: number, leftPan: number, rightPan: number) => void;
|
|
2118
|
+
setSurroundPan: (stripIndex: number, pan: SurroundPan) => void;
|
|
2036
2119
|
addSend: (
|
|
2037
2120
|
stripIndex: number,
|
|
2038
2121
|
id: string,
|
package/src/stream_types.ts
CHANGED
|
@@ -75,6 +75,11 @@ export interface FrameBuffer {
|
|
|
75
75
|
/** Number of mel bands; flat `mel` is `[nFrames * nMels]` row-major. */
|
|
76
76
|
nMels: number;
|
|
77
77
|
timestamps: Float32Array;
|
|
78
|
+
/**
|
|
79
|
+
* Mel spectrogram in LINEAR power (not dB) — the raw per-frame mel energies.
|
|
80
|
+
* The quantized read paths (`readFramesU8` / `readFramesI16`) convert to dB
|
|
81
|
+
* before packing, so their `mel` is dB-scaled; this float buffer is not.
|
|
82
|
+
*/
|
|
78
83
|
mel: Float32Array;
|
|
79
84
|
chroma: Float32Array;
|
|
80
85
|
onsetStrength: Float32Array;
|
|
@@ -109,6 +114,7 @@ export interface StreamFramesU8 {
|
|
|
109
114
|
nFrames: number;
|
|
110
115
|
nMels: number;
|
|
111
116
|
timestamps: Float32Array;
|
|
117
|
+
/** Row-major `[nFrames * nMels]` mel in dB, quantized over `[melDbMin, melDbMax]`. */
|
|
112
118
|
mel: Uint8Array;
|
|
113
119
|
chroma: Uint8Array;
|
|
114
120
|
onsetStrength: Uint8Array;
|
|
@@ -121,6 +127,7 @@ export interface StreamFramesI16 {
|
|
|
121
127
|
nFrames: number;
|
|
122
128
|
nMels: number;
|
|
123
129
|
timestamps: Float32Array;
|
|
130
|
+
/** Row-major `[nFrames * nMels]` mel in dB, quantized over `[melDbMin, melDbMax]`. */
|
|
124
131
|
mel: Int16Array;
|
|
125
132
|
chroma: Int16Array;
|
|
126
133
|
onsetStrength: Int16Array;
|
package/src/validation.ts
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* Per-call validation options accepted by guarded wrappers. Empty-buffer
|
|
3
3
|
* checks are always performed; pass `{ validate: false }` to opt out of the
|
|
4
4
|
* O(n) NaN/Inf scan on hot paths.
|
|
5
|
+
*
|
|
6
|
+
* `{ validate: false }` only skips this JS-side pre-scan (which raises a
|
|
7
|
+
* `RangeError` naming the exact offending index). It is NOT a way to push
|
|
8
|
+
* non-finite samples into the core: the native layer always re-validates the
|
|
9
|
+
* buffer (see `validate_offline_audio_input` in the C++ core), matching the C
|
|
10
|
+
* ABI / Node / Python surfaces, so an NaN/Inf buffer still throws — just with a
|
|
11
|
+
* generic native message instead of the indexed JS one.
|
|
5
12
|
*/
|
|
6
13
|
export interface ValidateOptions {
|
|
7
14
|
validate?: boolean;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SonareEngineCaptureRequestMessage,
|
|
3
|
+
SonareEngineCaptureResponseMessage,
|
|
4
|
+
SonareEngineSyncMessage,
|
|
5
|
+
SonareEngineTransportRequestMessage,
|
|
6
|
+
SonareEngineTransportResponseMessage,
|
|
7
|
+
SonareRealtimeVoiceChangerMessage,
|
|
8
|
+
SonareWorkletMessage,
|
|
9
|
+
} from './messages';
|
|
10
|
+
import {
|
|
11
|
+
isRecord,
|
|
12
|
+
type SonareEngineCommandRecord,
|
|
13
|
+
type SonareEngineTelemetryRecord,
|
|
14
|
+
type SonareWorkletMeterSnapshot,
|
|
15
|
+
} from './protocol';
|
|
16
|
+
|
|
17
|
+
export function isWorkletMessage(value: unknown): value is SonareWorkletMessage {
|
|
18
|
+
if (!isRecord(value) || typeof value.type !== 'string') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return (
|
|
22
|
+
value.type === 'scheduleInsertAutomation' ||
|
|
23
|
+
value.type === 'setMeterInterval' ||
|
|
24
|
+
value.type === 'destroy'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isEngineCommandRecord(value: unknown): value is SonareEngineCommandRecord {
|
|
29
|
+
return isRecord(value) && typeof value.type === 'number';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isEngineSyncMessage(value: unknown): value is SonareEngineSyncMessage {
|
|
33
|
+
if (!isRecord(value) || typeof value.type !== 'string') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return (
|
|
37
|
+
value.type === 'syncClips' ||
|
|
38
|
+
value.type === 'syncClipsDelta' ||
|
|
39
|
+
value.type === 'syncMidiClips' ||
|
|
40
|
+
value.type === 'syncMarkers' ||
|
|
41
|
+
value.type === 'syncMetronome' ||
|
|
42
|
+
value.type === 'syncAutomation' ||
|
|
43
|
+
value.type === 'syncTempo' ||
|
|
44
|
+
value.type === 'syncMixer' ||
|
|
45
|
+
value.type === 'syncCapture' ||
|
|
46
|
+
value.type === 'syncTrackStripEqBand' ||
|
|
47
|
+
value.type === 'syncMasterStripEqBand' ||
|
|
48
|
+
value.type === 'syncTrackStripInsertBypassed' ||
|
|
49
|
+
value.type === 'syncMasterStripInsertBypassed' ||
|
|
50
|
+
value.type === 'syncTrackStripInsertParamByName' ||
|
|
51
|
+
value.type === 'syncMasterStripInsertParamByName' ||
|
|
52
|
+
value.type === 'syncTrackStripPan' ||
|
|
53
|
+
value.type === 'syncTrackStripPanLaw' ||
|
|
54
|
+
value.type === 'syncTrackStripPanMode' ||
|
|
55
|
+
value.type === 'syncTrackStripDualPan' ||
|
|
56
|
+
value.type === 'syncTrackStripChannelDelaySamples' ||
|
|
57
|
+
value.type === 'syncBuiltinInstrument' ||
|
|
58
|
+
value.type === 'syncSynthInstrument' ||
|
|
59
|
+
value.type === 'syncSf2Instrument' ||
|
|
60
|
+
value.type === 'syncLoadSoundFont' ||
|
|
61
|
+
value.type === 'syncMidiNoteOn' ||
|
|
62
|
+
value.type === 'syncMidiNoteOff' ||
|
|
63
|
+
value.type === 'syncMidiCc' ||
|
|
64
|
+
value.type === 'syncMidiPanic'
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isEngineCaptureRequestMessage(
|
|
69
|
+
value: unknown,
|
|
70
|
+
): value is SonareEngineCaptureRequestMessage {
|
|
71
|
+
return (
|
|
72
|
+
isRecord(value) &&
|
|
73
|
+
value.type === 'captureRequest' &&
|
|
74
|
+
typeof value.requestId === 'number' &&
|
|
75
|
+
(value.op === 'status' || value.op === 'read' || value.op === 'reset')
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isEngineCaptureResponseMessage(
|
|
80
|
+
value: unknown,
|
|
81
|
+
): value is SonareEngineCaptureResponseMessage {
|
|
82
|
+
return (
|
|
83
|
+
isRecord(value) &&
|
|
84
|
+
value.type === 'captureResponse' &&
|
|
85
|
+
typeof value.requestId === 'number' &&
|
|
86
|
+
typeof value.ok === 'boolean'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function isEngineTransportRequestMessage(
|
|
91
|
+
value: unknown,
|
|
92
|
+
): value is SonareEngineTransportRequestMessage {
|
|
93
|
+
return (
|
|
94
|
+
isRecord(value) &&
|
|
95
|
+
value.type === 'transportRequest' &&
|
|
96
|
+
typeof value.requestId === 'number' &&
|
|
97
|
+
value.op === 'state'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function isEngineTransportResponseMessage(
|
|
102
|
+
value: unknown,
|
|
103
|
+
): value is SonareEngineTransportResponseMessage {
|
|
104
|
+
return (
|
|
105
|
+
isRecord(value) &&
|
|
106
|
+
value.type === 'transportResponse' &&
|
|
107
|
+
typeof value.requestId === 'number' &&
|
|
108
|
+
typeof value.ok === 'boolean'
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function isRealtimeVoiceChangerMessage(
|
|
113
|
+
value: unknown,
|
|
114
|
+
): value is SonareRealtimeVoiceChangerMessage {
|
|
115
|
+
if (!isRecord(value) || typeof value.type !== 'string') {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return value.type === 'setConfig' || value.type === 'reset' || value.type === 'destroy';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isEngineTelemetryRecord(value: unknown): value is SonareEngineTelemetryRecord {
|
|
122
|
+
return (
|
|
123
|
+
isRecord(value) &&
|
|
124
|
+
typeof value.type === 'number' &&
|
|
125
|
+
typeof value.error === 'number' &&
|
|
126
|
+
typeof value.renderFrame === 'number' &&
|
|
127
|
+
typeof value.timelineSample === 'number' &&
|
|
128
|
+
typeof value.audibleTimelineSample === 'number' &&
|
|
129
|
+
typeof value.graphLatencySamplesQ8 === 'number' &&
|
|
130
|
+
typeof value.value === 'number'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function isMeterSnapshot(value: unknown): value is SonareWorkletMeterSnapshot {
|
|
135
|
+
return (
|
|
136
|
+
isRecord(value) &&
|
|
137
|
+
value.type === 'meter' &&
|
|
138
|
+
typeof value.frame === 'number' &&
|
|
139
|
+
typeof value.peakDbL === 'number' &&
|
|
140
|
+
typeof value.peakDbR === 'number' &&
|
|
141
|
+
typeof value.rmsDbL === 'number' &&
|
|
142
|
+
typeof value.rmsDbR === 'number' &&
|
|
143
|
+
typeof value.correlation === 'number' &&
|
|
144
|
+
(typeof value.targetId === 'number' || value.targetId === undefined)
|
|
145
|
+
);
|
|
146
|
+
}
|