@libraz/libsonare 1.3.2 → 1.3.3
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 +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +236 -68
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +1 -1
- package/dist/sonare-rt.js +1 -1
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +347 -9
- package/dist/worklet.js +1184 -100
- package/dist/worklet.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +13 -1
- package/src/module_state.ts +82 -17
- package/src/opfs_clip_pages.ts +43 -9
- package/src/realtime_engine.ts +174 -109
- package/src/sonare.js.d.ts +59 -0
- package/src/web_midi.ts +15 -11
- package/src/worklet.ts +1402 -66
package/dist/worklet.js
CHANGED
|
@@ -39,6 +39,7 @@ function makeSonareError(raw, thrown) {
|
|
|
39
39
|
}
|
|
40
40
|
function wrapModuleErrors(raw) {
|
|
41
41
|
const cache = /* @__PURE__ */ new Map();
|
|
42
|
+
const objectCache = /* @__PURE__ */ new WeakMap();
|
|
42
43
|
const convert = (error) => {
|
|
43
44
|
const ptr = nativeExceptionPtr(error);
|
|
44
45
|
if (ptr !== null) {
|
|
@@ -46,6 +47,83 @@ function wrapModuleErrors(raw) {
|
|
|
46
47
|
}
|
|
47
48
|
throw error;
|
|
48
49
|
};
|
|
50
|
+
const wrapNativeObject = (value) => {
|
|
51
|
+
if (value === null || typeof value !== "object") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof Promise) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
const objectValue = value;
|
|
58
|
+
const cached = objectCache.get(objectValue);
|
|
59
|
+
if (cached) {
|
|
60
|
+
return cached;
|
|
61
|
+
}
|
|
62
|
+
const methodCache = /* @__PURE__ */ new Map();
|
|
63
|
+
const wrapped = new Proxy(objectValue, {
|
|
64
|
+
get(target, prop, receiver) {
|
|
65
|
+
const member = Reflect.get(target, prop, receiver);
|
|
66
|
+
if (typeof member !== "function") {
|
|
67
|
+
return member;
|
|
68
|
+
}
|
|
69
|
+
const cachedMethod = methodCache.get(prop);
|
|
70
|
+
if (cachedMethod) {
|
|
71
|
+
return cachedMethod;
|
|
72
|
+
}
|
|
73
|
+
const method = member;
|
|
74
|
+
const wrappedMethod = (...args) => {
|
|
75
|
+
try {
|
|
76
|
+
return wrapNativeObject(Reflect.apply(method, target, args));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return convert(error);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
methodCache.set(prop, wrappedMethod);
|
|
82
|
+
return wrappedMethod;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
objectCache.set(objectValue, wrapped);
|
|
86
|
+
return wrapped;
|
|
87
|
+
};
|
|
88
|
+
const wrapFunction = (value) => {
|
|
89
|
+
const fnCache = /* @__PURE__ */ new Map();
|
|
90
|
+
return new Proxy(value, {
|
|
91
|
+
get(target, prop, receiver) {
|
|
92
|
+
const member = Reflect.get(target, prop, receiver);
|
|
93
|
+
if (typeof member !== "function") {
|
|
94
|
+
return member;
|
|
95
|
+
}
|
|
96
|
+
const cachedMember = fnCache.get(prop);
|
|
97
|
+
if (cachedMember) {
|
|
98
|
+
return cachedMember;
|
|
99
|
+
}
|
|
100
|
+
const fn = member;
|
|
101
|
+
const wrappedMember = (...args) => {
|
|
102
|
+
try {
|
|
103
|
+
return wrapNativeObject(Reflect.apply(fn, target, args));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return convert(error);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
fnCache.set(prop, wrappedMember);
|
|
109
|
+
return wrappedMember;
|
|
110
|
+
},
|
|
111
|
+
apply(t, thisArg, args) {
|
|
112
|
+
try {
|
|
113
|
+
return wrapNativeObject(Reflect.apply(t, thisArg, args));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return convert(error);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
construct(t, args, newTarget) {
|
|
119
|
+
try {
|
|
120
|
+
return wrapNativeObject(Reflect.construct(t, args, newTarget));
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return convert(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
};
|
|
49
127
|
return new Proxy(raw, {
|
|
50
128
|
get(target, prop, receiver) {
|
|
51
129
|
const value = Reflect.get(target, prop, receiver);
|
|
@@ -56,23 +134,7 @@ function wrapModuleErrors(raw) {
|
|
|
56
134
|
if (cached) {
|
|
57
135
|
return cached;
|
|
58
136
|
}
|
|
59
|
-
const
|
|
60
|
-
const wrapped = new Proxy(fn, {
|
|
61
|
-
apply(t, thisArg, args) {
|
|
62
|
-
try {
|
|
63
|
-
return Reflect.apply(t, thisArg, args);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
return convert(error);
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
construct(t, args, newTarget) {
|
|
69
|
-
try {
|
|
70
|
-
return Reflect.construct(t, args, newTarget);
|
|
71
|
-
} catch (error) {
|
|
72
|
-
return convert(error);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
});
|
|
137
|
+
const wrapped = wrapFunction(value);
|
|
76
138
|
cache.set(prop, wrapped);
|
|
77
139
|
return wrapped;
|
|
78
140
|
}
|
|
@@ -732,9 +794,6 @@ function engineCapabilities() {
|
|
|
732
794
|
};
|
|
733
795
|
}
|
|
734
796
|
var RealtimeEngine = class {
|
|
735
|
-
nativeExt() {
|
|
736
|
-
return this.native;
|
|
737
|
-
}
|
|
738
797
|
constructor(sampleRate = 48e3, maxBlockSize = 128, commandCapacity = 1024, telemetryCapacity = 1024) {
|
|
739
798
|
const module2 = getSonareModule();
|
|
740
799
|
const capabilities = engineCapabilities();
|
|
@@ -761,8 +820,14 @@ var RealtimeEngine = class {
|
|
|
761
820
|
setParameterSmoothed(paramId, value, renderFrame = -1) {
|
|
762
821
|
this.native.setParameterSmoothed(paramId, value, renderFrame);
|
|
763
822
|
}
|
|
823
|
+
setSoloMute(laneIndex, solo, mute, renderFrame = -1) {
|
|
824
|
+
this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
|
|
825
|
+
}
|
|
826
|
+
setMidiClips(clips) {
|
|
827
|
+
this.native.setMidiClips(clips);
|
|
828
|
+
}
|
|
764
829
|
setBuiltinInstrument(config = {}, destinationId = config.destinationId ?? 0) {
|
|
765
|
-
this.
|
|
830
|
+
this.native.setBuiltinInstrument(destinationId, config);
|
|
766
831
|
}
|
|
767
832
|
/**
|
|
768
833
|
* Bind the patch-driven NativeSynth to a realtime MIDI destination. `patch`
|
|
@@ -774,7 +839,7 @@ var RealtimeEngine = class {
|
|
|
774
839
|
* binding convenience, not part of the NativeSynth patch itself.
|
|
775
840
|
*/
|
|
776
841
|
setSynthInstrument(patch = {}, destinationId = (typeof patch === "object" ? patch.destinationId : void 0) ?? 0) {
|
|
777
|
-
this.
|
|
842
|
+
this.native.setSynthInstrument(destinationId, patch);
|
|
778
843
|
}
|
|
779
844
|
/**
|
|
780
845
|
* Load (parse) SoundFont 2 bytes into the engine so SF2 instruments can be
|
|
@@ -783,7 +848,7 @@ var RealtimeEngine = class {
|
|
|
783
848
|
* not referenced afterwards. Replaces any previously loaded SoundFont.
|
|
784
849
|
*/
|
|
785
850
|
loadSoundFont(data) {
|
|
786
|
-
this.
|
|
851
|
+
this.native.loadSoundFont(data);
|
|
787
852
|
}
|
|
788
853
|
/**
|
|
789
854
|
* Bind a GS-compatible SoundFont player to a realtime MIDI destination, fed
|
|
@@ -795,13 +860,13 @@ var RealtimeEngine = class {
|
|
|
795
860
|
* synthesizer GM fallback bank (the data-free floor).
|
|
796
861
|
*/
|
|
797
862
|
setSf2Instrument(config = {}, destinationId = config.destinationId ?? 0) {
|
|
798
|
-
this.
|
|
863
|
+
this.native.setSf2Instrument(destinationId, config);
|
|
799
864
|
}
|
|
800
865
|
clearMidiInstrument(destinationId = 0) {
|
|
801
|
-
this.
|
|
866
|
+
this.native.clearMidiInstrument(destinationId);
|
|
802
867
|
}
|
|
803
868
|
midiInstrumentCount() {
|
|
804
|
-
return this.
|
|
869
|
+
return this.native.midiInstrumentCount();
|
|
805
870
|
}
|
|
806
871
|
/**
|
|
807
872
|
* Bind a live MIDI CC to an engine automation parameter. The MIDI event still
|
|
@@ -809,7 +874,7 @@ var RealtimeEngine = class {
|
|
|
809
874
|
* mapped into [minValue, maxValue] for `paramId`.
|
|
810
875
|
*/
|
|
811
876
|
bindMidiCc(channel, controller, paramId, options = {}) {
|
|
812
|
-
this.
|
|
877
|
+
this.native.bindMidiCc(
|
|
813
878
|
channel,
|
|
814
879
|
controller,
|
|
815
880
|
paramId,
|
|
@@ -818,42 +883,42 @@ var RealtimeEngine = class {
|
|
|
818
883
|
);
|
|
819
884
|
}
|
|
820
885
|
clearMidiCcBindings() {
|
|
821
|
-
this.
|
|
886
|
+
this.native.clearMidiCcBindings();
|
|
822
887
|
}
|
|
823
888
|
midiCcBindingCount() {
|
|
824
|
-
return this.
|
|
889
|
+
return this.native.midiCcBindingCount();
|
|
825
890
|
}
|
|
826
891
|
/** Install/replace a live non-destructive MIDI-FX insert for one destination. */
|
|
827
892
|
setMidiFx(destinationId, configJson) {
|
|
828
|
-
this.
|
|
893
|
+
this.native.setMidiFx(destinationId, configJson);
|
|
829
894
|
}
|
|
830
895
|
clearMidiFx(destinationId = 0) {
|
|
831
|
-
this.
|
|
896
|
+
this.native.clearMidiFx(destinationId);
|
|
832
897
|
}
|
|
833
898
|
/** Enable the engine-owned live MIDI input source for a destination. */
|
|
834
899
|
setMidiInputSource(destinationId = 0) {
|
|
835
|
-
this.
|
|
900
|
+
this.native.setMidiInputSource(destinationId);
|
|
836
901
|
}
|
|
837
902
|
clearMidiInputSource() {
|
|
838
|
-
this.
|
|
903
|
+
this.native.clearMidiInputSource();
|
|
839
904
|
}
|
|
840
905
|
midiInputPendingCount() {
|
|
841
|
-
return this.
|
|
906
|
+
return this.native.midiInputPendingCount();
|
|
842
907
|
}
|
|
843
908
|
pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples = 0) {
|
|
844
|
-
this.
|
|
909
|
+
this.native.pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
|
|
845
910
|
}
|
|
846
911
|
pushMidiInputNoteOff(group, channel, note, velocity = 0, portTimeSamples = 0) {
|
|
847
|
-
this.
|
|
912
|
+
this.native.pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
|
|
848
913
|
}
|
|
849
914
|
pushMidiInputCc(group, channel, controller, value, portTimeSamples = 0) {
|
|
850
|
-
this.
|
|
915
|
+
this.native.pushMidiInputCc(group, channel, controller, value, portTimeSamples);
|
|
851
916
|
}
|
|
852
917
|
pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame = -1) {
|
|
853
|
-
this.
|
|
918
|
+
this.native.pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
|
|
854
919
|
}
|
|
855
920
|
pushMidiNoteOff(destinationId, group, channel, note, velocity = 0, renderFrame = -1) {
|
|
856
|
-
this.
|
|
921
|
+
this.native.pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
|
|
857
922
|
}
|
|
858
923
|
/**
|
|
859
924
|
* Queue an immediate (live) MIDI control change to a MIDI destination
|
|
@@ -862,21 +927,21 @@ var RealtimeEngine = class {
|
|
|
862
927
|
* immediate. Mirrors the Node/Python/C-ABI `pushMidiCc`.
|
|
863
928
|
*/
|
|
864
929
|
pushMidiCc(destinationId, group, channel, controller, value, renderFrame = -1) {
|
|
865
|
-
this.
|
|
930
|
+
this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
|
|
866
931
|
}
|
|
867
932
|
/**
|
|
868
933
|
* Queue a MIDI panic (all-notes-off) releasing every sounding note at
|
|
869
934
|
* `renderFrame` (-1 = immediate). Mirrors the C-ABI `pushMidiPanic`.
|
|
870
935
|
*/
|
|
871
936
|
pushMidiPanic(renderFrame = -1) {
|
|
872
|
-
this.
|
|
937
|
+
this.native.pushMidiPanic(renderFrame);
|
|
873
938
|
}
|
|
874
939
|
/**
|
|
875
940
|
* Remove all registered parameters (and their automation lanes). Control-thread
|
|
876
941
|
* only; not realtime-safe. Mirrors the C-ABI `clearParameters`.
|
|
877
942
|
*/
|
|
878
943
|
clearParameters() {
|
|
879
|
-
this.
|
|
944
|
+
this.native.clearParameters();
|
|
880
945
|
}
|
|
881
946
|
/** Read back the current transport state snapshot. */
|
|
882
947
|
getTransportState() {
|
|
@@ -897,9 +962,18 @@ var RealtimeEngine = class {
|
|
|
897
962
|
setTempo(bpm) {
|
|
898
963
|
this.native.setTempo(bpm);
|
|
899
964
|
}
|
|
965
|
+
setTempoSegments(segments) {
|
|
966
|
+
this.native.setTempoSegments([...segments]);
|
|
967
|
+
}
|
|
900
968
|
setTimeSignature(numerator, denominator) {
|
|
901
969
|
this.native.setTimeSignature(numerator, denominator);
|
|
902
970
|
}
|
|
971
|
+
setTimeSignatureSegments(segments) {
|
|
972
|
+
this.native.setTimeSignatureSegments([...segments]);
|
|
973
|
+
}
|
|
974
|
+
sampleAtPpq(ppq) {
|
|
975
|
+
return Number(this.native.sampleAtPpq(ppq));
|
|
976
|
+
}
|
|
903
977
|
setLoop(startPpq, endPpq, enabled = true) {
|
|
904
978
|
this.native.setLoop(startPpq, endPpq, enabled);
|
|
905
979
|
}
|
|
@@ -968,21 +1042,81 @@ var RealtimeEngine = class {
|
|
|
968
1042
|
clipCount() {
|
|
969
1043
|
return this.native.clipCount();
|
|
970
1044
|
}
|
|
1045
|
+
setTrackLanes(lanes) {
|
|
1046
|
+
this.native.setTrackLanes(
|
|
1047
|
+
lanes.map((lane) => typeof lane === "number" ? { trackId: lane } : lane)
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
setTrackBuses(buses) {
|
|
1051
|
+
this.native.setTrackBuses(buses);
|
|
1052
|
+
}
|
|
1053
|
+
setBusStripJson(busId, sceneJson) {
|
|
1054
|
+
try {
|
|
1055
|
+
JSON.parse(sceneJson);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
const message = error instanceof Error ? error.message : "invalid bus strip JSON";
|
|
1058
|
+
throw new SonareError(2 /* InvalidFormat */, "InvalidFormat", message);
|
|
1059
|
+
}
|
|
1060
|
+
this.native.setBusStripJson(busId, sceneJson);
|
|
1061
|
+
}
|
|
1062
|
+
setTrackStripJson(trackId, sceneJson) {
|
|
1063
|
+
try {
|
|
1064
|
+
JSON.parse(sceneJson);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
const message = error instanceof Error ? error.message : "invalid track strip JSON";
|
|
1067
|
+
throw new SonareError(2 /* InvalidFormat */, "InvalidFormat", message);
|
|
1068
|
+
}
|
|
1069
|
+
this.native.setTrackStripJson(trackId, sceneJson);
|
|
1070
|
+
}
|
|
1071
|
+
setTrackStripEqBand(trackId, bandIndex, band) {
|
|
1072
|
+
this.native.setTrackStripEqBandJson(
|
|
1073
|
+
trackId,
|
|
1074
|
+
bandIndex,
|
|
1075
|
+
typeof band === "string" ? band : JSON.stringify(band)
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
setTrackStripEqBandJson(trackId, bandIndex, bandJson) {
|
|
1079
|
+
this.native.setTrackStripEqBandJson(trackId, bandIndex, bandJson);
|
|
1080
|
+
}
|
|
1081
|
+
setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass = false) {
|
|
1082
|
+
this.native.setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass);
|
|
1083
|
+
}
|
|
1084
|
+
setMasterStripJson(sceneJson) {
|
|
1085
|
+
try {
|
|
1086
|
+
JSON.parse(sceneJson);
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
const message = error instanceof Error ? error.message : "invalid master strip JSON";
|
|
1089
|
+
throw new SonareError(2 /* InvalidFormat */, "InvalidFormat", message);
|
|
1090
|
+
}
|
|
1091
|
+
this.native.setMasterStripJson(sceneJson);
|
|
1092
|
+
}
|
|
1093
|
+
setMasterStripEqBand(bandIndex, band) {
|
|
1094
|
+
this.native.setMasterStripEqBandJson(
|
|
1095
|
+
bandIndex,
|
|
1096
|
+
typeof band === "string" ? band : JSON.stringify(band)
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
setMasterStripEqBandJson(bandIndex, bandJson) {
|
|
1100
|
+
this.native.setMasterStripEqBandJson(bandIndex, bandJson);
|
|
1101
|
+
}
|
|
1102
|
+
setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass = false) {
|
|
1103
|
+
this.native.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
|
|
1104
|
+
}
|
|
971
1105
|
createClipPageProvider(numChannels, numSamples, pageFrames) {
|
|
972
|
-
const id = this.
|
|
1106
|
+
const id = this.native.createClipPageProvider(numChannels, numSamples, pageFrames);
|
|
973
1107
|
return new ClipPageProvider(this, id);
|
|
974
1108
|
}
|
|
975
1109
|
supplyClipPage(providerId, pageIndex, channels) {
|
|
976
|
-
this.
|
|
1110
|
+
this.native.supplyClipPage(providerId, pageIndex, channels);
|
|
977
1111
|
}
|
|
978
1112
|
clearClipPage(providerId, pageIndex) {
|
|
979
|
-
this.
|
|
1113
|
+
this.native.clearClipPage(providerId, pageIndex);
|
|
980
1114
|
}
|
|
981
1115
|
destroyClipPageProvider(providerId) {
|
|
982
|
-
this.
|
|
1116
|
+
this.native.destroyClipPageProvider(providerId);
|
|
983
1117
|
}
|
|
984
1118
|
popClipPageRequest() {
|
|
985
|
-
return this.
|
|
1119
|
+
return this.native.popClipPageRequest();
|
|
986
1120
|
}
|
|
987
1121
|
setCaptureBuffer(numChannels, capacityFrames) {
|
|
988
1122
|
this.native.setCaptureBuffer(numChannels, capacityFrames);
|
|
@@ -1100,7 +1234,7 @@ async function init(options) {
|
|
|
1100
1234
|
}
|
|
1101
1235
|
initPromise = (async () => {
|
|
1102
1236
|
try {
|
|
1103
|
-
const createModule = (await import("./sonare.js")).default;
|
|
1237
|
+
const createModule = options?.moduleFactory ?? (await import("./sonare.js")).default;
|
|
1104
1238
|
module = await createModule(options);
|
|
1105
1239
|
setSonareModule(module);
|
|
1106
1240
|
} catch (error) {
|
|
@@ -1115,8 +1249,20 @@ function isInitialized() {
|
|
|
1115
1249
|
}
|
|
1116
1250
|
|
|
1117
1251
|
// src/worklet.ts
|
|
1252
|
+
var ENGINE_MIXER_TARGET_BASE = 1297612800;
|
|
1253
|
+
var ENGINE_MIXER_PARAM_FADER_DB = 1;
|
|
1254
|
+
var ENGINE_MIXER_PARAM_PAN = 2;
|
|
1255
|
+
function engineMixerLaneTarget(laneIndex, paramKind) {
|
|
1256
|
+
return ENGINE_MIXER_TARGET_BASE | (laneIndex & 255) << 8 | paramKind & 255;
|
|
1257
|
+
}
|
|
1258
|
+
function engineMixerBusTarget(busIndex, paramKind) {
|
|
1259
|
+
return ENGINE_MIXER_TARGET_BASE | (254 - busIndex & 255) << 8 | paramKind & 255;
|
|
1260
|
+
}
|
|
1261
|
+
function engineMixerMasterTarget(paramKind) {
|
|
1262
|
+
return ENGINE_MIXER_TARGET_BASE | 255 << 8 | paramKind & 255;
|
|
1263
|
+
}
|
|
1118
1264
|
var SONARE_METER_RING_HEADER_INTS = 4;
|
|
1119
|
-
var SONARE_METER_RING_RECORD_FLOATS =
|
|
1265
|
+
var SONARE_METER_RING_RECORD_FLOATS = 14;
|
|
1120
1266
|
var SONARE_SPECTRUM_RING_HEADER_INTS = 5;
|
|
1121
1267
|
var SONARE_FRAME_LANE_BASE = 16777216;
|
|
1122
1268
|
function encodeFrameLo(frame) {
|
|
@@ -1207,7 +1353,19 @@ function isEngineSyncMessage(value) {
|
|
|
1207
1353
|
if (!isRecord(value) || typeof value.type !== "string") {
|
|
1208
1354
|
return false;
|
|
1209
1355
|
}
|
|
1210
|
-
return value.type === "syncClips" || value.type === "syncMarkers" || value.type === "syncMetronome" || value.type === "syncAutomation";
|
|
1356
|
+
return value.type === "syncClips" || value.type === "syncClipsDelta" || value.type === "syncMidiClips" || value.type === "syncMarkers" || value.type === "syncMetronome" || value.type === "syncAutomation" || value.type === "syncTempo" || value.type === "syncMixer" || value.type === "syncCapture" || value.type === "syncTrackStripEqBand" || value.type === "syncMasterStripEqBand" || value.type === "syncTrackStripInsertBypassed" || value.type === "syncMasterStripInsertBypassed" || value.type === "syncBuiltinInstrument" || value.type === "syncSynthInstrument" || value.type === "syncSf2Instrument" || value.type === "syncLoadSoundFont" || value.type === "syncMidiNoteOn" || value.type === "syncMidiNoteOff" || value.type === "syncMidiCc" || value.type === "syncMidiPanic";
|
|
1357
|
+
}
|
|
1358
|
+
function isEngineCaptureRequestMessage(value) {
|
|
1359
|
+
return isRecord(value) && value.type === "captureRequest" && typeof value.requestId === "number" && (value.op === "status" || value.op === "read" || value.op === "reset");
|
|
1360
|
+
}
|
|
1361
|
+
function isEngineCaptureResponseMessage(value) {
|
|
1362
|
+
return isRecord(value) && value.type === "captureResponse" && typeof value.requestId === "number" && typeof value.ok === "boolean";
|
|
1363
|
+
}
|
|
1364
|
+
function isEngineTransportRequestMessage(value) {
|
|
1365
|
+
return isRecord(value) && value.type === "transportRequest" && typeof value.requestId === "number" && value.op === "state";
|
|
1366
|
+
}
|
|
1367
|
+
function isEngineTransportResponseMessage(value) {
|
|
1368
|
+
return isRecord(value) && value.type === "transportResponse" && typeof value.requestId === "number" && typeof value.ok === "boolean";
|
|
1211
1369
|
}
|
|
1212
1370
|
function isRealtimeVoiceChangerMessage(value) {
|
|
1213
1371
|
if (!isRecord(value) || typeof value.type !== "string") {
|
|
@@ -1219,7 +1377,7 @@ function isEngineTelemetryRecord(value) {
|
|
|
1219
1377
|
return isRecord(value) && typeof value.type === "number" && typeof value.error === "number" && typeof value.renderFrame === "number" && typeof value.timelineSample === "number" && typeof value.audibleTimelineSample === "number" && typeof value.graphLatencySamplesQ8 === "number" && typeof value.value === "number";
|
|
1220
1378
|
}
|
|
1221
1379
|
function isMeterSnapshot(value) {
|
|
1222
|
-
return isRecord(value) && value.type === "meter" && typeof value.frame === "number" && typeof value.peakDbL === "number" && typeof value.peakDbR === "number" && typeof value.rmsDbL === "number" && typeof value.rmsDbR === "number" && typeof value.correlation === "number";
|
|
1380
|
+
return isRecord(value) && value.type === "meter" && typeof value.frame === "number" && typeof value.peakDbL === "number" && typeof value.peakDbR === "number" && typeof value.rmsDbL === "number" && typeof value.rmsDbR === "number" && typeof value.correlation === "number" && (typeof value.targetId === "number" || value.targetId === void 0);
|
|
1223
1381
|
}
|
|
1224
1382
|
function sonareMeterRingBufferByteLength(capacity) {
|
|
1225
1383
|
const clampedCapacity = Math.max(1, Math.floor(capacity));
|
|
@@ -1237,19 +1395,27 @@ function createSonareMeterRingBuffer(capacity = 128) {
|
|
|
1237
1395
|
}
|
|
1238
1396
|
function readSonareMeterRingBuffer(ring, readIndex = 0) {
|
|
1239
1397
|
const writeIndex = Atomics.load(ring.header, 0);
|
|
1398
|
+
const recordFloats = Atomics.load(ring.header, 2) || SONARE_METER_RING_RECORD_FLOATS;
|
|
1240
1399
|
const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));
|
|
1241
1400
|
const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);
|
|
1242
1401
|
const meters = [];
|
|
1243
1402
|
for (let index = firstReadable; index < writeIndex; index++) {
|
|
1244
|
-
const offset = index % ring.capacity *
|
|
1403
|
+
const offset = index % ring.capacity * recordFloats;
|
|
1245
1404
|
meters.push({
|
|
1246
1405
|
type: "meter",
|
|
1247
|
-
frame: decodeFrame(ring.records[offset], ring.records[offset +
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1406
|
+
frame: decodeFrame(ring.records[offset], ring.records[offset + 1]),
|
|
1407
|
+
targetId: ring.records[offset + 2],
|
|
1408
|
+
peakDbL: ring.records[offset + 3],
|
|
1409
|
+
peakDbR: ring.records[offset + 4],
|
|
1410
|
+
rmsDbL: ring.records[offset + 5],
|
|
1411
|
+
rmsDbR: ring.records[offset + 6],
|
|
1412
|
+
correlation: ring.records[offset + 7],
|
|
1413
|
+
truePeakDbL: ring.records[offset + 8],
|
|
1414
|
+
truePeakDbR: ring.records[offset + 9],
|
|
1415
|
+
momentaryLufs: ring.records[offset + 10],
|
|
1416
|
+
shortTermLufs: ring.records[offset + 11],
|
|
1417
|
+
integratedLufs: ring.records[offset + 12],
|
|
1418
|
+
gainReductionDb: ring.records[offset + 13]
|
|
1253
1419
|
});
|
|
1254
1420
|
}
|
|
1255
1421
|
return { nextReadIndex: writeIndex, meters };
|
|
@@ -1509,12 +1675,19 @@ function telemetryFromEngine(telemetry) {
|
|
|
1509
1675
|
function meterFromEngine(meter) {
|
|
1510
1676
|
return {
|
|
1511
1677
|
type: "meter",
|
|
1678
|
+
targetId: meter.targetId,
|
|
1512
1679
|
frame: meter.renderFrame,
|
|
1513
1680
|
peakDbL: meter.peakDbL,
|
|
1514
1681
|
peakDbR: meter.peakDbR,
|
|
1515
1682
|
rmsDbL: meter.rmsDbL,
|
|
1516
1683
|
rmsDbR: meter.rmsDbR,
|
|
1517
|
-
correlation: meter.correlation
|
|
1684
|
+
correlation: meter.correlation,
|
|
1685
|
+
truePeakDbL: meter.truePeakDbL,
|
|
1686
|
+
truePeakDbR: meter.truePeakDbR,
|
|
1687
|
+
momentaryLufs: meter.momentaryLufs,
|
|
1688
|
+
shortTermLufs: meter.shortTermLufs,
|
|
1689
|
+
integratedLufs: meter.integratedLufs,
|
|
1690
|
+
gainReductionDb: meter.gainReductionDb
|
|
1518
1691
|
};
|
|
1519
1692
|
}
|
|
1520
1693
|
function magnitudeToDb(value) {
|
|
@@ -1669,12 +1842,19 @@ var SonareWorkletProcessor = class {
|
|
|
1669
1842
|
const denominator = Math.sqrt(sumL * sumR);
|
|
1670
1843
|
const meter = {
|
|
1671
1844
|
type: "meter",
|
|
1845
|
+
targetId: 0,
|
|
1672
1846
|
frame: this.processedFrames,
|
|
1673
1847
|
peakDbL: toDb(peakL),
|
|
1674
1848
|
peakDbR: toDb(peakR),
|
|
1675
1849
|
rmsDbL: toDb(rmsL),
|
|
1676
1850
|
rmsDbR: toDb(rmsR),
|
|
1677
|
-
correlation: denominator > 0 ? sumLR / denominator : 0
|
|
1851
|
+
correlation: denominator > 0 ? sumLR / denominator : 0,
|
|
1852
|
+
truePeakDbL: toDb(peakL),
|
|
1853
|
+
truePeakDbR: toDb(peakR),
|
|
1854
|
+
momentaryLufs: Number.NaN,
|
|
1855
|
+
shortTermLufs: Number.NaN,
|
|
1856
|
+
integratedLufs: Number.NaN,
|
|
1857
|
+
gainReductionDb: Number.NaN
|
|
1678
1858
|
};
|
|
1679
1859
|
this.transport.onMeter?.(meter);
|
|
1680
1860
|
if (this.meterRing) {
|
|
@@ -1691,12 +1871,19 @@ var SonareWorkletProcessor = class {
|
|
|
1691
1871
|
const writeIndex = Atomics.load(ring.header, 0);
|
|
1692
1872
|
const offset = writeIndex % ring.capacity * SONARE_METER_RING_RECORD_FLOATS;
|
|
1693
1873
|
ring.records[offset] = encodeFrameLo(meter.frame);
|
|
1694
|
-
ring.records[offset + 1] = meter.
|
|
1695
|
-
ring.records[offset + 2] = meter.
|
|
1696
|
-
ring.records[offset + 3] = meter.
|
|
1697
|
-
ring.records[offset + 4] = meter.
|
|
1698
|
-
ring.records[offset + 5] = meter.
|
|
1699
|
-
ring.records[offset + 6] =
|
|
1874
|
+
ring.records[offset + 1] = encodeFrameHi(meter.frame);
|
|
1875
|
+
ring.records[offset + 2] = meter.targetId;
|
|
1876
|
+
ring.records[offset + 3] = meter.peakDbL;
|
|
1877
|
+
ring.records[offset + 4] = meter.peakDbR;
|
|
1878
|
+
ring.records[offset + 5] = meter.rmsDbL;
|
|
1879
|
+
ring.records[offset + 6] = meter.rmsDbR;
|
|
1880
|
+
ring.records[offset + 7] = meter.correlation;
|
|
1881
|
+
ring.records[offset + 8] = meter.truePeakDbL;
|
|
1882
|
+
ring.records[offset + 9] = meter.truePeakDbR;
|
|
1883
|
+
ring.records[offset + 10] = meter.momentaryLufs;
|
|
1884
|
+
ring.records[offset + 11] = meter.shortTermLufs;
|
|
1885
|
+
ring.records[offset + 12] = meter.integratedLufs;
|
|
1886
|
+
ring.records[offset + 13] = meter.gainReductionDb;
|
|
1700
1887
|
Atomics.store(ring.header, 0, writeIndex + 1);
|
|
1701
1888
|
}
|
|
1702
1889
|
publishSpectrum(left, right) {
|
|
@@ -1761,6 +1948,7 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
1761
1948
|
// Latest metronome gains/click length pushed via 'syncMetronome'. The
|
|
1762
1949
|
// SetMetronome command only toggles enabled state; the config arrives here.
|
|
1763
1950
|
this.metronomeConfig = { ...DEFAULT_METRONOME_CONFIG };
|
|
1951
|
+
this.liveClips = /* @__PURE__ */ new Map();
|
|
1764
1952
|
this.sampleRate = options.sampleRate ?? 48e3;
|
|
1765
1953
|
this.blockSize = options.blockSize ?? 128;
|
|
1766
1954
|
this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));
|
|
@@ -1864,8 +2052,28 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
1864
2052
|
}
|
|
1865
2053
|
switch (message.type) {
|
|
1866
2054
|
case "syncClips":
|
|
2055
|
+
this.liveClips.clear();
|
|
2056
|
+
for (const clip of message.clips) {
|
|
2057
|
+
if (clip.id !== void 0) {
|
|
2058
|
+
this.liveClips.set(clip.id, clip);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
1867
2061
|
this.engine.setClips(message.clips);
|
|
1868
2062
|
break;
|
|
2063
|
+
case "syncClipsDelta":
|
|
2064
|
+
for (const clipId of message.removeIds) {
|
|
2065
|
+
this.liveClips.delete(clipId);
|
|
2066
|
+
}
|
|
2067
|
+
for (const clip of message.upserts) {
|
|
2068
|
+
if (clip.id !== void 0) {
|
|
2069
|
+
this.liveClips.set(clip.id, clip);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
this.engine.setClips(Array.from(this.liveClips.values()));
|
|
2073
|
+
break;
|
|
2074
|
+
case "syncMidiClips":
|
|
2075
|
+
this.engine.setMidiClips(message.clips);
|
|
2076
|
+
break;
|
|
1869
2077
|
case "syncMarkers":
|
|
1870
2078
|
this.engine.setMarkers(message.markers);
|
|
1871
2079
|
break;
|
|
@@ -1876,6 +2084,184 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
1876
2084
|
case "syncAutomation":
|
|
1877
2085
|
this.engine.setAutomationLane(message.paramId, message.points);
|
|
1878
2086
|
break;
|
|
2087
|
+
case "syncTempo":
|
|
2088
|
+
if (message.tempoSegments) {
|
|
2089
|
+
this.engine.setTempoSegments(message.tempoSegments);
|
|
2090
|
+
} else {
|
|
2091
|
+
this.engine.setTempo(message.bpm);
|
|
2092
|
+
}
|
|
2093
|
+
if (message.timeSignatureSegments) {
|
|
2094
|
+
this.engine.setTimeSignatureSegments(message.timeSignatureSegments);
|
|
2095
|
+
} else {
|
|
2096
|
+
this.engine.setTimeSignature(
|
|
2097
|
+
message.timeSignature.numerator,
|
|
2098
|
+
message.timeSignature.denominator
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
break;
|
|
2102
|
+
case "syncMixer":
|
|
2103
|
+
if (message.buses) {
|
|
2104
|
+
this.engine.setTrackBuses(message.buses);
|
|
2105
|
+
}
|
|
2106
|
+
this.engine.setTrackLanes(message.lanes);
|
|
2107
|
+
for (const strip of message.trackStrips ?? []) {
|
|
2108
|
+
this.engine.setTrackStripJson(strip.trackId, strip.sceneJson);
|
|
2109
|
+
}
|
|
2110
|
+
for (const strip of message.busStrips ?? []) {
|
|
2111
|
+
this.engine.setBusStripJson(strip.busId, strip.sceneJson);
|
|
2112
|
+
}
|
|
2113
|
+
if (message.masterStripJson) {
|
|
2114
|
+
this.engine.setMasterStripJson(message.masterStripJson);
|
|
2115
|
+
}
|
|
2116
|
+
break;
|
|
2117
|
+
case "syncCapture":
|
|
2118
|
+
this.engine.setCaptureBuffer(message.channels, message.bufferFrames);
|
|
2119
|
+
this.engine.setCaptureSource(message.source);
|
|
2120
|
+
this.engine.setRecordOffsetSamples(message.recordOffsetSamples);
|
|
2121
|
+
this.engine.setInputMonitor(message.inputMonitor.enabled, message.inputMonitor.gain);
|
|
2122
|
+
break;
|
|
2123
|
+
case "syncTrackStripEqBand":
|
|
2124
|
+
this.engine.setTrackStripEqBandJson(message.trackId, message.bandIndex, message.bandJson);
|
|
2125
|
+
break;
|
|
2126
|
+
case "syncMasterStripEqBand":
|
|
2127
|
+
this.engine.setMasterStripEqBandJson(message.bandIndex, message.bandJson);
|
|
2128
|
+
break;
|
|
2129
|
+
case "syncTrackStripInsertBypassed":
|
|
2130
|
+
this.engine.setTrackStripInsertBypassed(
|
|
2131
|
+
message.trackId,
|
|
2132
|
+
message.insertIndex,
|
|
2133
|
+
message.bypassed,
|
|
2134
|
+
message.resetOnBypass
|
|
2135
|
+
);
|
|
2136
|
+
break;
|
|
2137
|
+
case "syncMasterStripInsertBypassed":
|
|
2138
|
+
this.engine.setMasterStripInsertBypassed(
|
|
2139
|
+
message.insertIndex,
|
|
2140
|
+
message.bypassed,
|
|
2141
|
+
message.resetOnBypass
|
|
2142
|
+
);
|
|
2143
|
+
break;
|
|
2144
|
+
case "syncBuiltinInstrument":
|
|
2145
|
+
this.engine.setBuiltinInstrument(message.config, message.destinationId);
|
|
2146
|
+
break;
|
|
2147
|
+
case "syncSynthInstrument":
|
|
2148
|
+
this.engine.setSynthInstrument(message.patch, message.destinationId);
|
|
2149
|
+
break;
|
|
2150
|
+
case "syncLoadSoundFont":
|
|
2151
|
+
this.engine.loadSoundFont(message.data);
|
|
2152
|
+
break;
|
|
2153
|
+
case "syncSf2Instrument":
|
|
2154
|
+
this.engine.setSf2Instrument(message.config, message.destinationId);
|
|
2155
|
+
break;
|
|
2156
|
+
case "syncMidiNoteOn":
|
|
2157
|
+
this.engine.pushMidiNoteOn(
|
|
2158
|
+
message.destinationId,
|
|
2159
|
+
message.group,
|
|
2160
|
+
message.channel,
|
|
2161
|
+
message.note,
|
|
2162
|
+
message.velocity,
|
|
2163
|
+
message.renderFrame
|
|
2164
|
+
);
|
|
2165
|
+
break;
|
|
2166
|
+
case "syncMidiNoteOff":
|
|
2167
|
+
this.engine.pushMidiNoteOff(
|
|
2168
|
+
message.destinationId,
|
|
2169
|
+
message.group,
|
|
2170
|
+
message.channel,
|
|
2171
|
+
message.note,
|
|
2172
|
+
message.velocity,
|
|
2173
|
+
message.renderFrame
|
|
2174
|
+
);
|
|
2175
|
+
break;
|
|
2176
|
+
case "syncMidiCc":
|
|
2177
|
+
this.engine.pushMidiCc(
|
|
2178
|
+
message.destinationId,
|
|
2179
|
+
message.group,
|
|
2180
|
+
message.channel,
|
|
2181
|
+
message.controller,
|
|
2182
|
+
message.value,
|
|
2183
|
+
message.renderFrame
|
|
2184
|
+
);
|
|
2185
|
+
break;
|
|
2186
|
+
case "syncMidiPanic":
|
|
2187
|
+
this.engine.pushMidiPanic(message.renderFrame);
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
receiveCaptureRequest(message) {
|
|
2192
|
+
if (this.closed) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
try {
|
|
2196
|
+
if (message.op === "status") {
|
|
2197
|
+
const status = this.engine.captureStatus();
|
|
2198
|
+
this.transport?.postMessage?.({
|
|
2199
|
+
type: "captureResponse",
|
|
2200
|
+
requestId: message.requestId,
|
|
2201
|
+
ok: true,
|
|
2202
|
+
status: {
|
|
2203
|
+
capturedFrames: status.capturedFrames,
|
|
2204
|
+
overflowCount: status.overflowCount,
|
|
2205
|
+
armed: status.armed,
|
|
2206
|
+
punchEnabled: status.punchEnabled,
|
|
2207
|
+
source: status.source,
|
|
2208
|
+
recordOffsetSamples: status.recordOffsetSamples
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
if (message.op === "read") {
|
|
2214
|
+
const captured = this.engine.capturedAudio();
|
|
2215
|
+
const channels = [];
|
|
2216
|
+
for (let ch = 0; ch < captured.length; ch++) {
|
|
2217
|
+
const source = captured[ch];
|
|
2218
|
+
const copy = [];
|
|
2219
|
+
for (let i = 0; i < source.length; i++) {
|
|
2220
|
+
copy.push(Number(source[i]));
|
|
2221
|
+
}
|
|
2222
|
+
channels.push(copy);
|
|
2223
|
+
}
|
|
2224
|
+
this.transport?.postMessage?.({
|
|
2225
|
+
type: "captureResponse",
|
|
2226
|
+
requestId: message.requestId,
|
|
2227
|
+
ok: true,
|
|
2228
|
+
channels
|
|
2229
|
+
});
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
this.engine.resetCapture();
|
|
2233
|
+
this.transport?.postMessage?.({
|
|
2234
|
+
type: "captureResponse",
|
|
2235
|
+
requestId: message.requestId,
|
|
2236
|
+
ok: true
|
|
2237
|
+
});
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
this.transport?.postMessage?.({
|
|
2240
|
+
type: "captureResponse",
|
|
2241
|
+
requestId: message.requestId,
|
|
2242
|
+
ok: false,
|
|
2243
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
receiveTransportRequest(message) {
|
|
2248
|
+
if (this.closed) {
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
try {
|
|
2252
|
+
this.transport?.postMessage?.({
|
|
2253
|
+
type: "transportResponse",
|
|
2254
|
+
requestId: message.requestId,
|
|
2255
|
+
ok: true,
|
|
2256
|
+
state: this.engine.getTransportState()
|
|
2257
|
+
});
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
this.transport?.postMessage?.({
|
|
2260
|
+
type: "transportResponse",
|
|
2261
|
+
requestId: message.requestId,
|
|
2262
|
+
ok: false,
|
|
2263
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2264
|
+
});
|
|
1879
2265
|
}
|
|
1880
2266
|
}
|
|
1881
2267
|
destroy() {
|
|
@@ -1956,6 +2342,14 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
1956
2342
|
case 17 /* SeekMarker */:
|
|
1957
2343
|
this.engine.seekMarker(Math.trunc(Number(command.targetId ?? 0)), sampleTime);
|
|
1958
2344
|
break;
|
|
2345
|
+
case 10 /* SetSoloMute */:
|
|
2346
|
+
this.engine.setSoloMute(
|
|
2347
|
+
Math.trunc(Number(command.targetId ?? 0)),
|
|
2348
|
+
Boolean((Number(command.argInt ?? 0) & 2) !== 0),
|
|
2349
|
+
Boolean((Number(command.argInt ?? 0) & 1) !== 0),
|
|
2350
|
+
sampleTime
|
|
2351
|
+
);
|
|
2352
|
+
break;
|
|
1959
2353
|
default:
|
|
1960
2354
|
this.publishTelemetryRecord({
|
|
1961
2355
|
type: 1 /* Error */,
|
|
@@ -1987,10 +2381,12 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
1987
2381
|
}
|
|
1988
2382
|
for (const item of this.engine.drainMeterTelemetry(64)) {
|
|
1989
2383
|
const meter = meterFromEngine(item);
|
|
1990
|
-
if (meter.frame - this.lastMeterFrame < this.meterIntervalFrames) {
|
|
2384
|
+
if (meter.frame !== this.lastMeterFrame && meter.frame - this.lastMeterFrame < this.meterIntervalFrames) {
|
|
1991
2385
|
continue;
|
|
1992
2386
|
}
|
|
1993
|
-
this.lastMeterFrame
|
|
2387
|
+
if (meter.frame !== this.lastMeterFrame) {
|
|
2388
|
+
this.lastMeterFrame = meter.frame;
|
|
2389
|
+
}
|
|
1994
2390
|
if (this.meterRing) {
|
|
1995
2391
|
this.writeMeterRing(meter);
|
|
1996
2392
|
} else {
|
|
@@ -2007,12 +2403,19 @@ var _SonareRealtimeEngineWorkletProcessor = class _SonareRealtimeEngineWorkletPr
|
|
|
2007
2403
|
const writeIndex = Atomics.load(ring.header, 0);
|
|
2008
2404
|
const offset = writeIndex % ring.capacity * SONARE_METER_RING_RECORD_FLOATS;
|
|
2009
2405
|
ring.records[offset] = encodeFrameLo(meter.frame);
|
|
2010
|
-
ring.records[offset + 1] = meter.
|
|
2011
|
-
ring.records[offset + 2] = meter.
|
|
2012
|
-
ring.records[offset + 3] = meter.
|
|
2013
|
-
ring.records[offset + 4] = meter.
|
|
2014
|
-
ring.records[offset + 5] = meter.
|
|
2015
|
-
ring.records[offset + 6] =
|
|
2406
|
+
ring.records[offset + 1] = encodeFrameHi(meter.frame);
|
|
2407
|
+
ring.records[offset + 2] = meter.targetId;
|
|
2408
|
+
ring.records[offset + 3] = meter.peakDbL;
|
|
2409
|
+
ring.records[offset + 4] = meter.peakDbR;
|
|
2410
|
+
ring.records[offset + 5] = meter.rmsDbL;
|
|
2411
|
+
ring.records[offset + 6] = meter.rmsDbR;
|
|
2412
|
+
ring.records[offset + 7] = meter.correlation;
|
|
2413
|
+
ring.records[offset + 8] = meter.truePeakDbL;
|
|
2414
|
+
ring.records[offset + 9] = meter.truePeakDbR;
|
|
2415
|
+
ring.records[offset + 10] = meter.momentaryLufs;
|
|
2416
|
+
ring.records[offset + 11] = meter.shortTermLufs;
|
|
2417
|
+
ring.records[offset + 12] = meter.integratedLufs;
|
|
2418
|
+
ring.records[offset + 13] = meter.gainReductionDb;
|
|
2016
2419
|
Atomics.store(ring.header, 0, writeIndex + 1);
|
|
2017
2420
|
}
|
|
2018
2421
|
commandRingFromSharedBuffer(sharedBuffer, fallbackCapacity) {
|
|
@@ -2144,9 +2547,28 @@ var SonareRtRealtimeEngineRuntime = class {
|
|
|
2144
2547
|
this.metronomeConfig.clickSamples
|
|
2145
2548
|
);
|
|
2146
2549
|
break;
|
|
2550
|
+
case "syncTempo":
|
|
2551
|
+
this.module._sonare_rt_engine_set_tempo(this.engine, message.bpm);
|
|
2552
|
+
break;
|
|
2147
2553
|
case "syncClips":
|
|
2554
|
+
case "syncClipsDelta":
|
|
2555
|
+
case "syncMidiClips":
|
|
2148
2556
|
case "syncMarkers":
|
|
2149
2557
|
case "syncAutomation":
|
|
2558
|
+
case "syncMixer":
|
|
2559
|
+
case "syncCapture":
|
|
2560
|
+
case "syncTrackStripEqBand":
|
|
2561
|
+
case "syncMasterStripEqBand":
|
|
2562
|
+
case "syncTrackStripInsertBypassed":
|
|
2563
|
+
case "syncMasterStripInsertBypassed":
|
|
2564
|
+
case "syncBuiltinInstrument":
|
|
2565
|
+
case "syncSynthInstrument":
|
|
2566
|
+
case "syncSf2Instrument":
|
|
2567
|
+
case "syncLoadSoundFont":
|
|
2568
|
+
case "syncMidiNoteOn":
|
|
2569
|
+
case "syncMidiNoteOff":
|
|
2570
|
+
case "syncMidiCc":
|
|
2571
|
+
case "syncMidiPanic":
|
|
2150
2572
|
if (this.telemetryRing) {
|
|
2151
2573
|
writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {
|
|
2152
2574
|
type: 1 /* Error */,
|
|
@@ -2161,6 +2583,28 @@ var SonareRtRealtimeEngineRuntime = class {
|
|
|
2161
2583
|
break;
|
|
2162
2584
|
}
|
|
2163
2585
|
}
|
|
2586
|
+
receiveCaptureRequest(message, port) {
|
|
2587
|
+
if (this.closed) {
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
port?.postMessage?.({
|
|
2591
|
+
type: "captureResponse",
|
|
2592
|
+
requestId: message.requestId,
|
|
2593
|
+
ok: false,
|
|
2594
|
+
error: "Capture read-back is not supported by the sonare-rt runtime."
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
receiveTransportRequest(message, port) {
|
|
2598
|
+
if (this.closed) {
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
port?.postMessage?.({
|
|
2602
|
+
type: "transportResponse",
|
|
2603
|
+
requestId: message.requestId,
|
|
2604
|
+
ok: false,
|
|
2605
|
+
error: "Transport state read-back is not supported by the sonare-rt runtime."
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2164
2608
|
destroy() {
|
|
2165
2609
|
if (this.closed) {
|
|
2166
2610
|
return;
|
|
@@ -2338,6 +2782,10 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2338
2782
|
this.meterReadIndex = 0;
|
|
2339
2783
|
this.telemetryListeners = /* @__PURE__ */ new Set();
|
|
2340
2784
|
this.meterListeners = /* @__PURE__ */ new Set();
|
|
2785
|
+
this.captureRequestId = 1;
|
|
2786
|
+
this.captureRequests = /* @__PURE__ */ new Map();
|
|
2787
|
+
this.transportRequestId = 1;
|
|
2788
|
+
this.transportRequests = /* @__PURE__ */ new Map();
|
|
2341
2789
|
this.destroyed = false;
|
|
2342
2790
|
this.node = node;
|
|
2343
2791
|
this.capabilities = capabilities;
|
|
@@ -2348,11 +2796,31 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2348
2796
|
this.resolveReady = resolve;
|
|
2349
2797
|
this.rejectReady = reject;
|
|
2350
2798
|
});
|
|
2351
|
-
if (capabilities.
|
|
2799
|
+
if (!capabilities.readyMessage) {
|
|
2352
2800
|
this.resolveReady();
|
|
2353
2801
|
}
|
|
2354
2802
|
this.node.port.onmessage = (event) => {
|
|
2355
|
-
if (
|
|
2803
|
+
if (isEngineCaptureResponseMessage(event.data)) {
|
|
2804
|
+
const pending = this.captureRequests.get(event.data.requestId);
|
|
2805
|
+
if (pending) {
|
|
2806
|
+
this.captureRequests.delete(event.data.requestId);
|
|
2807
|
+
if (event.data.ok) {
|
|
2808
|
+
pending.resolve(event.data);
|
|
2809
|
+
} else {
|
|
2810
|
+
pending.reject(new Error(event.data.error ?? "Capture request failed"));
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
} else if (isEngineTransportResponseMessage(event.data)) {
|
|
2814
|
+
const pending = this.transportRequests.get(event.data.requestId);
|
|
2815
|
+
if (pending) {
|
|
2816
|
+
this.transportRequests.delete(event.data.requestId);
|
|
2817
|
+
if (event.data.ok) {
|
|
2818
|
+
pending.resolve(event.data);
|
|
2819
|
+
} else {
|
|
2820
|
+
pending.reject(new Error(event.data.error ?? "Transport request failed"));
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
} else if (isEngineTelemetryRecord(event.data)) {
|
|
2356
2824
|
this.emitTelemetry(event.data);
|
|
2357
2825
|
} else if (isMeterSnapshot(event.data)) {
|
|
2358
2826
|
this.emitMeter(event.data);
|
|
@@ -2406,7 +2874,10 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2406
2874
|
telemetrySharedBuffer: telemetryRing?.sharedBuffer,
|
|
2407
2875
|
telemetryRingCapacity: telemetryRing?.capacity,
|
|
2408
2876
|
meterSharedBuffer: meterRing?.sharedBuffer,
|
|
2409
|
-
meterRingCapacity: meterRing?.capacity
|
|
2877
|
+
meterRingCapacity: meterRing?.capacity,
|
|
2878
|
+
wasmBinary: options.wasmBinary,
|
|
2879
|
+
initialSyncMessages: options.initialSyncMessages,
|
|
2880
|
+
initialCommands: options.initialCommands
|
|
2410
2881
|
};
|
|
2411
2882
|
const factory = options.nodeFactory ?? ((ctx, name, nodeOptions) => new AudioWorkletNode(ctx, name, nodeOptions));
|
|
2412
2883
|
const node = factory(context, processorName, {
|
|
@@ -2426,7 +2897,8 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2426
2897
|
engineAbiVersion: detectedCapabilities?.engineAbiVersion,
|
|
2427
2898
|
expectedEngineAbiVersion: detectedCapabilities?.expectedEngineAbiVersion,
|
|
2428
2899
|
abiCompatible: detectedCapabilities?.abiCompatible,
|
|
2429
|
-
degradedReason
|
|
2900
|
+
degradedReason,
|
|
2901
|
+
readyMessage: runtimeTarget === "sonare-rt" || runtimeTarget === "embind" && moduleUrl !== void 0 && !options.nodeFactory
|
|
2430
2902
|
},
|
|
2431
2903
|
commandRing,
|
|
2432
2904
|
telemetryRing,
|
|
@@ -2463,6 +2935,32 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2463
2935
|
this.node.port.postMessage(command);
|
|
2464
2936
|
return true;
|
|
2465
2937
|
}
|
|
2938
|
+
requestCaptureStatus() {
|
|
2939
|
+
return this.sendCaptureRequest("status").then((response) => {
|
|
2940
|
+
if (!response.status) {
|
|
2941
|
+
throw new Error("Capture status response is missing status.");
|
|
2942
|
+
}
|
|
2943
|
+
return response.status;
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
requestCapturedAudio() {
|
|
2947
|
+
return this.sendCaptureRequest("read").then(
|
|
2948
|
+
(response) => (response.channels ?? []).map(
|
|
2949
|
+
(channel) => channel instanceof Float32Array ? channel : new Float32Array(channel)
|
|
2950
|
+
)
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
requestCaptureReset() {
|
|
2954
|
+
return this.sendCaptureRequest("reset").then(() => void 0);
|
|
2955
|
+
}
|
|
2956
|
+
requestTransportState() {
|
|
2957
|
+
return this.sendTransportRequest().then((response) => {
|
|
2958
|
+
if (!response.state) {
|
|
2959
|
+
throw new Error("Transport state response is missing state.");
|
|
2960
|
+
}
|
|
2961
|
+
return response.state;
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2466
2964
|
pollTelemetry() {
|
|
2467
2965
|
if (!this.telemetryRing) {
|
|
2468
2966
|
return [];
|
|
@@ -2507,6 +3005,14 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2507
3005
|
this.destroyed = true;
|
|
2508
3006
|
this.node.port.postMessage({ type: 3 /* TransportStop */, sampleTime: -1 });
|
|
2509
3007
|
this.node.disconnect();
|
|
3008
|
+
for (const pending of this.captureRequests.values()) {
|
|
3009
|
+
pending.reject(new Error("Realtime engine node is destroyed."));
|
|
3010
|
+
}
|
|
3011
|
+
this.captureRequests.clear();
|
|
3012
|
+
for (const pending of this.transportRequests.values()) {
|
|
3013
|
+
pending.reject(new Error("Realtime engine node is destroyed."));
|
|
3014
|
+
}
|
|
3015
|
+
this.transportRequests.clear();
|
|
2510
3016
|
this.telemetryListeners.clear();
|
|
2511
3017
|
this.meterListeners.clear();
|
|
2512
3018
|
}
|
|
@@ -2520,14 +3026,50 @@ var SonareRealtimeEngineNode = class _SonareRealtimeEngineNode {
|
|
|
2520
3026
|
listener(meter);
|
|
2521
3027
|
}
|
|
2522
3028
|
}
|
|
3029
|
+
sendCaptureRequest(op) {
|
|
3030
|
+
if (this.destroyed) {
|
|
3031
|
+
return Promise.reject(new Error("Realtime engine node is destroyed."));
|
|
3032
|
+
}
|
|
3033
|
+
const requestId = this.captureRequestId++;
|
|
3034
|
+
const promise = new Promise((resolve, reject) => {
|
|
3035
|
+
this.captureRequests.set(requestId, { resolve, reject });
|
|
3036
|
+
});
|
|
3037
|
+
this.node.port.postMessage({ type: "captureRequest", requestId, op });
|
|
3038
|
+
return promise;
|
|
3039
|
+
}
|
|
3040
|
+
sendTransportRequest() {
|
|
3041
|
+
if (this.destroyed) {
|
|
3042
|
+
return Promise.reject(new Error("Realtime engine node is destroyed."));
|
|
3043
|
+
}
|
|
3044
|
+
const requestId = this.transportRequestId++;
|
|
3045
|
+
const promise = new Promise((resolve, reject) => {
|
|
3046
|
+
this.transportRequests.set(requestId, { resolve, reject });
|
|
3047
|
+
});
|
|
3048
|
+
this.node.port.postMessage({ type: "transportRequest", requestId, op: "state" });
|
|
3049
|
+
return promise;
|
|
3050
|
+
}
|
|
2523
3051
|
};
|
|
2524
3052
|
var SonareEngine = class _SonareEngine {
|
|
2525
3053
|
constructor(context, realtimeNode, offlineEngine, sampleRate, offlineBlockSize, offlineChannelCount) {
|
|
2526
3054
|
this.automationLanes = /* @__PURE__ */ new Map();
|
|
2527
3055
|
this.clips = /* @__PURE__ */ new Map();
|
|
3056
|
+
this.midiClips = /* @__PURE__ */ new Map();
|
|
2528
3057
|
this.markers = /* @__PURE__ */ new Map();
|
|
3058
|
+
this.trackLaneIds = [];
|
|
3059
|
+
this.trackSends = /* @__PURE__ */ new Map();
|
|
3060
|
+
this.buses = [];
|
|
3061
|
+
this.trackStripJson = /* @__PURE__ */ new Map();
|
|
3062
|
+
this.busStripJson = /* @__PURE__ */ new Map();
|
|
3063
|
+
this.tempoBpm = 120;
|
|
3064
|
+
this.timeSignature = { numerator: 4, denominator: 4 };
|
|
3065
|
+
this.tempoSegments = [{ startPpq: 0, bpm: 120 }];
|
|
3066
|
+
this.timeSignatureSegments = [
|
|
3067
|
+
{ startPpq: 0, numerator: 4, denominator: 4 }
|
|
3068
|
+
];
|
|
2529
3069
|
this.nextClipId = 1;
|
|
2530
3070
|
this.nextMarkerId = 1;
|
|
3071
|
+
this.transportPlaying = false;
|
|
3072
|
+
this.pendingInstrumentSync = [];
|
|
2531
3073
|
this.destroyed = false;
|
|
2532
3074
|
this.context = context;
|
|
2533
3075
|
this.realtimeNode = realtimeNode;
|
|
@@ -2538,8 +3080,21 @@ var SonareEngine = class _SonareEngine {
|
|
|
2538
3080
|
this.offlineBlockSize = offlineBlockSize;
|
|
2539
3081
|
this.offlineChannelCount = offlineChannelCount;
|
|
2540
3082
|
this.transport = {
|
|
2541
|
-
play: (sampleTime = -1) =>
|
|
2542
|
-
|
|
3083
|
+
play: (sampleTime = -1) => {
|
|
3084
|
+
const ok = this.realtimeNode.play(sampleTime);
|
|
3085
|
+
if (ok) {
|
|
3086
|
+
this.transportPlaying = true;
|
|
3087
|
+
}
|
|
3088
|
+
return ok;
|
|
3089
|
+
},
|
|
3090
|
+
stop: (sampleTime = -1) => {
|
|
3091
|
+
const ok = this.realtimeNode.stop(sampleTime);
|
|
3092
|
+
if (ok) {
|
|
3093
|
+
this.transportPlaying = false;
|
|
3094
|
+
this.flushPendingInstrumentSync();
|
|
3095
|
+
}
|
|
3096
|
+
return ok;
|
|
3097
|
+
},
|
|
2543
3098
|
seekPpq: (ppq, sampleTime = -1) => {
|
|
2544
3099
|
this.offlineEngine.seekPpq(ppq, sampleTime);
|
|
2545
3100
|
return this.realtimeNode.seekPpq(ppq, sampleTime);
|
|
@@ -2550,6 +3105,7 @@ var SonareEngine = class _SonareEngine {
|
|
|
2550
3105
|
return this.realtimeNode.seekSample(timelineSample, sampleTime);
|
|
2551
3106
|
},
|
|
2552
3107
|
setTempo: (bpm) => this.setTempo(bpm),
|
|
3108
|
+
setTempoSegments: (segments) => this.setTempoSegments(segments),
|
|
2553
3109
|
setLoop: (startPpq, endPpq, enabled = true) => this.setLoop(startPpq, endPpq, enabled)
|
|
2554
3110
|
};
|
|
2555
3111
|
}
|
|
@@ -2584,13 +3140,37 @@ var SonareEngine = class _SonareEngine {
|
|
|
2584
3140
|
await this.context.resume?.();
|
|
2585
3141
|
}
|
|
2586
3142
|
setTempo(bpm) {
|
|
3143
|
+
this.tempoBpm = bpm;
|
|
3144
|
+
this.tempoSegments = [{ startPpq: 0, bpm }];
|
|
2587
3145
|
this.offlineEngine.setTempo(bpm);
|
|
3146
|
+
this.postTempoSync();
|
|
2588
3147
|
this.realtimeNode.sendCommand({
|
|
2589
3148
|
type: 6 /* SetTempoMap */,
|
|
2590
3149
|
sampleTime: -1,
|
|
2591
3150
|
argFloat: bpm
|
|
2592
3151
|
});
|
|
2593
3152
|
}
|
|
3153
|
+
setTempoSegments(segments) {
|
|
3154
|
+
this.tempoSegments = segments.map((segment) => ({ ...segment }));
|
|
3155
|
+
this.tempoBpm = this.tempoSegments[0]?.bpm ?? this.tempoBpm;
|
|
3156
|
+
this.offlineEngine.setTempoSegments(this.tempoSegments);
|
|
3157
|
+
this.postTempoSync();
|
|
3158
|
+
}
|
|
3159
|
+
setTimeSignature(numerator, denominator) {
|
|
3160
|
+
this.timeSignature = { numerator, denominator };
|
|
3161
|
+
this.timeSignatureSegments = [{ startPpq: 0, numerator, denominator }];
|
|
3162
|
+
this.offlineEngine.setTimeSignature(numerator, denominator);
|
|
3163
|
+
this.postTempoSync();
|
|
3164
|
+
}
|
|
3165
|
+
setTimeSignatureSegments(segments) {
|
|
3166
|
+
this.timeSignatureSegments = segments.map((segment) => ({ ...segment }));
|
|
3167
|
+
const first = this.timeSignatureSegments[0];
|
|
3168
|
+
if (first) {
|
|
3169
|
+
this.timeSignature = { numerator: first.numerator, denominator: first.denominator };
|
|
3170
|
+
}
|
|
3171
|
+
this.offlineEngine.setTimeSignatureSegments(this.timeSignatureSegments);
|
|
3172
|
+
this.postTempoSync();
|
|
3173
|
+
}
|
|
2594
3174
|
setLoop(startPpq, endPpq, enabled = true) {
|
|
2595
3175
|
this.offlineEngine.setLoop(startPpq, endPpq, enabled);
|
|
2596
3176
|
return this.realtimeNode.sendCommand({
|
|
@@ -2601,6 +3181,17 @@ var SonareEngine = class _SonareEngine {
|
|
|
2601
3181
|
argInt: Math.round(endPpq * 1e6)
|
|
2602
3182
|
});
|
|
2603
3183
|
}
|
|
3184
|
+
countInEndSample(startSample, bars) {
|
|
3185
|
+
return this.offlineEngine.countInEndSample(startSample, bars);
|
|
3186
|
+
}
|
|
3187
|
+
async getTransportState() {
|
|
3188
|
+
const state = await this.realtimeNode.requestTransportState();
|
|
3189
|
+
this.latestTransportState = state;
|
|
3190
|
+
return state;
|
|
3191
|
+
}
|
|
3192
|
+
cachedTransportState() {
|
|
3193
|
+
return this.latestTransportState;
|
|
3194
|
+
}
|
|
2604
3195
|
setParam(nodeId, param, value) {
|
|
2605
3196
|
const paramId = this.resolveParamId(nodeId, param);
|
|
2606
3197
|
this.offlineEngine.setParameter(paramId, value);
|
|
@@ -2631,12 +3222,198 @@ var SonareEngine = class _SonareEngine {
|
|
|
2631
3222
|
return parameters;
|
|
2632
3223
|
}
|
|
2633
3224
|
setSoloMute(target, solo, mute) {
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
3225
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3226
|
+
this.offlineEngine.setSoloMute(laneIndex, solo, mute);
|
|
3227
|
+
return this.realtimeNode.sendCommand({
|
|
3228
|
+
type: 10 /* SetSoloMute */,
|
|
3229
|
+
targetId: laneIndex,
|
|
3230
|
+
sampleTime: -1,
|
|
3231
|
+
argInt: (mute ? 1 : 0) | (solo ? 2 : 0)
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
3234
|
+
setStripGain(target, db) {
|
|
3235
|
+
if (target === "master") {
|
|
3236
|
+
const paramId2 = engineMixerMasterTarget(ENGINE_MIXER_PARAM_FADER_DB);
|
|
3237
|
+
this.offlineEngine.setParameter(paramId2, db);
|
|
3238
|
+
return this.realtimeNode.sendCommand({
|
|
3239
|
+
type: 1 /* SetParamSmoothed */,
|
|
3240
|
+
targetId: paramId2,
|
|
3241
|
+
sampleTime: -1,
|
|
3242
|
+
argFloat: db
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3246
|
+
const paramId = engineMixerLaneTarget(laneIndex, ENGINE_MIXER_PARAM_FADER_DB);
|
|
3247
|
+
this.offlineEngine.setParameter(paramId, db);
|
|
3248
|
+
return this.realtimeNode.sendCommand({
|
|
3249
|
+
type: 1 /* SetParamSmoothed */,
|
|
3250
|
+
targetId: paramId,
|
|
3251
|
+
sampleTime: -1,
|
|
3252
|
+
argFloat: db
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
setStripPan(target, pan) {
|
|
3256
|
+
if (target === "master") {
|
|
3257
|
+
const paramId2 = engineMixerMasterTarget(ENGINE_MIXER_PARAM_PAN);
|
|
3258
|
+
this.offlineEngine.setParameter(paramId2, pan);
|
|
3259
|
+
return this.realtimeNode.sendCommand({
|
|
3260
|
+
type: 1 /* SetParamSmoothed */,
|
|
3261
|
+
targetId: paramId2,
|
|
3262
|
+
sampleTime: -1,
|
|
3263
|
+
argFloat: pan
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3267
|
+
const paramId = engineMixerLaneTarget(laneIndex, ENGINE_MIXER_PARAM_PAN);
|
|
3268
|
+
this.offlineEngine.setParameter(paramId, pan);
|
|
3269
|
+
return this.realtimeNode.sendCommand({
|
|
3270
|
+
type: 1 /* SetParamSmoothed */,
|
|
3271
|
+
targetId: paramId,
|
|
3272
|
+
sampleTime: -1,
|
|
3273
|
+
argFloat: pan
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Declares the mixer track lanes in an explicit order.
|
|
3278
|
+
*
|
|
3279
|
+
* Lane indices are append-only: once a track id occupies a lane, its index
|
|
3280
|
+
* stays fixed for the engine's lifetime. The given list must therefore start
|
|
3281
|
+
* with the already-declared lane ids in their current order and may only
|
|
3282
|
+
* append new track ids after them. Entries carrying `sends` replace that
|
|
3283
|
+
* track's send list; entries without `sends` leave existing sends untouched.
|
|
3284
|
+
*
|
|
3285
|
+
* @param lanes Track ids or lane descriptors in the desired lane order.
|
|
3286
|
+
*/
|
|
3287
|
+
setTrackLanes(lanes) {
|
|
3288
|
+
const entries = lanes.map((lane) => typeof lane === "number" ? { trackId: lane } : lane);
|
|
3289
|
+
const ids = [];
|
|
3290
|
+
for (const entry of entries) {
|
|
3291
|
+
if (!Number.isInteger(entry.trackId) || entry.trackId <= 0) {
|
|
3292
|
+
throw new Error(`Invalid track id for mixer lane: ${String(entry.trackId)}`);
|
|
3293
|
+
}
|
|
3294
|
+
ids.push(entry.trackId);
|
|
3295
|
+
}
|
|
3296
|
+
if (new Set(ids).size !== ids.length) {
|
|
3297
|
+
throw new Error("Duplicate track id in mixer lane list");
|
|
3298
|
+
}
|
|
3299
|
+
for (let index = 0; index < this.trackLaneIds.length; index++) {
|
|
3300
|
+
if (ids[index] !== this.trackLaneIds[index]) {
|
|
3301
|
+
throw new Error(
|
|
3302
|
+
"Mixer lanes are append-only: keep existing lanes in order and only append new track ids"
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
for (const entry of entries) {
|
|
3307
|
+
if (entry.sends) {
|
|
3308
|
+
this.trackSends.set(
|
|
3309
|
+
entry.trackId,
|
|
3310
|
+
entry.sends.map((send) => ({ ...send }))
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
this.trackLaneIds.splice(0, this.trackLaneIds.length, ...ids);
|
|
3315
|
+
this.syncMixer();
|
|
3316
|
+
}
|
|
3317
|
+
setSends(target, sends) {
|
|
3318
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3319
|
+
const trackId = this.trackLaneIds[laneIndex];
|
|
3320
|
+
this.trackSends.set(
|
|
3321
|
+
trackId,
|
|
3322
|
+
sends.map((send) => ({ ...send }))
|
|
2639
3323
|
);
|
|
3324
|
+
this.syncMixer();
|
|
3325
|
+
}
|
|
3326
|
+
setTrackBuses(buses) {
|
|
3327
|
+
this.buses.splice(0, this.buses.length, ...buses.map((bus) => ({ ...bus })));
|
|
3328
|
+
this.syncMixer();
|
|
3329
|
+
}
|
|
3330
|
+
setBusGain(busId, db) {
|
|
3331
|
+
const busIndex = this.ensureBus(busId);
|
|
3332
|
+
this.buses[busIndex] = { ...this.buses[busIndex], busId, gainDb: db };
|
|
3333
|
+
this.offlineEngine.setTrackBuses(this.buses);
|
|
3334
|
+
const paramId = engineMixerBusTarget(busIndex, ENGINE_MIXER_PARAM_FADER_DB);
|
|
3335
|
+
this.offlineEngine.setParameter(paramId, db);
|
|
3336
|
+
return this.realtimeNode.sendCommand({
|
|
3337
|
+
type: 1 /* SetParamSmoothed */,
|
|
3338
|
+
targetId: paramId,
|
|
3339
|
+
sampleTime: -1,
|
|
3340
|
+
argFloat: db
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
setTrackStripJson(target, sceneJson) {
|
|
3344
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3345
|
+
const trackId = this.trackLaneIds[laneIndex];
|
|
3346
|
+
this.offlineEngine.setTrackStripJson(trackId, sceneJson);
|
|
3347
|
+
this.trackStripJson.set(trackId, sceneJson);
|
|
3348
|
+
this.syncMixer();
|
|
3349
|
+
}
|
|
3350
|
+
setTrackStripEqBand(target, bandIndex, band) {
|
|
3351
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3352
|
+
const trackId = this.trackLaneIds[laneIndex];
|
|
3353
|
+
const bandJson = typeof band === "string" ? band : JSON.stringify(band);
|
|
3354
|
+
this.offlineEngine.setTrackStripEqBandJson(trackId, bandIndex, bandJson);
|
|
3355
|
+
this.postSync({ type: "syncTrackStripEqBand", trackId, bandIndex, bandJson });
|
|
3356
|
+
}
|
|
3357
|
+
setTrackStripInsertBypassed(target, insertIndex, bypassed, resetOnBypass = false) {
|
|
3358
|
+
const laneIndex = this.ensureTrackLane(target);
|
|
3359
|
+
const trackId = this.trackLaneIds[laneIndex];
|
|
3360
|
+
this.offlineEngine.setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass);
|
|
3361
|
+
this.postSync({
|
|
3362
|
+
type: "syncTrackStripInsertBypassed",
|
|
3363
|
+
trackId,
|
|
3364
|
+
insertIndex,
|
|
3365
|
+
bypassed,
|
|
3366
|
+
resetOnBypass
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
setStripEq(target, bandIndex, band) {
|
|
3370
|
+
if (target === "master") {
|
|
3371
|
+
this.setMasterStripEqBand(bandIndex, band);
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
this.setTrackStripEqBand(target, bandIndex, band);
|
|
3375
|
+
}
|
|
3376
|
+
setStripInsertBypassed(target, insertIndex, bypassed, resetOnBypass = false) {
|
|
3377
|
+
if (target === "master") {
|
|
3378
|
+
this.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
|
|
3379
|
+
return;
|
|
3380
|
+
}
|
|
3381
|
+
this.setTrackStripInsertBypassed(target, insertIndex, bypassed, resetOnBypass);
|
|
3382
|
+
}
|
|
3383
|
+
setStripInserts(target, sceneJson) {
|
|
3384
|
+
if (target === "master") {
|
|
3385
|
+
this.setMasterStripJson(sceneJson);
|
|
3386
|
+
return;
|
|
3387
|
+
}
|
|
3388
|
+
this.setTrackStripJson(target, sceneJson);
|
|
3389
|
+
}
|
|
3390
|
+
setBusStripJson(busId, sceneJson) {
|
|
3391
|
+
this.ensureBus(busId);
|
|
3392
|
+
this.offlineEngine.setBusStripJson(busId, sceneJson);
|
|
3393
|
+
this.busStripJson.set(busId, sceneJson);
|
|
3394
|
+
this.syncMixer();
|
|
3395
|
+
}
|
|
3396
|
+
setMasterStripJson(sceneJson) {
|
|
3397
|
+
this.offlineEngine.setMasterStripJson(sceneJson);
|
|
3398
|
+
this.masterStripJson = sceneJson;
|
|
3399
|
+
this.syncMixer();
|
|
3400
|
+
}
|
|
3401
|
+
setMasterStripEqBand(bandIndex, band) {
|
|
3402
|
+
const bandJson = typeof band === "string" ? band : JSON.stringify(band);
|
|
3403
|
+
this.offlineEngine.setMasterStripEqBandJson(bandIndex, bandJson);
|
|
3404
|
+
this.postSync({ type: "syncMasterStripEqBand", bandIndex, bandJson });
|
|
3405
|
+
}
|
|
3406
|
+
setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass = false) {
|
|
3407
|
+
this.offlineEngine.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
|
|
3408
|
+
this.postSync({
|
|
3409
|
+
type: "syncMasterStripInsertBypassed",
|
|
3410
|
+
insertIndex,
|
|
3411
|
+
bypassed,
|
|
3412
|
+
resetOnBypass
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
setMasterChain(sceneJson) {
|
|
3416
|
+
this.setMasterStripJson(sceneJson);
|
|
2640
3417
|
}
|
|
2641
3418
|
addClip(trackId, buffer, startPpq, opts = {}) {
|
|
2642
3419
|
const id = opts.id ?? this.nextClipId++;
|
|
@@ -2644,18 +3421,108 @@ var SonareEngine = class _SonareEngine {
|
|
|
2644
3421
|
...opts,
|
|
2645
3422
|
id,
|
|
2646
3423
|
channels: buffer,
|
|
2647
|
-
startPpq
|
|
3424
|
+
startPpq,
|
|
3425
|
+
trackId: this.resolveTargetId(trackId)
|
|
2648
3426
|
};
|
|
3427
|
+
this.ensureTrackLane(trackId);
|
|
2649
3428
|
this.clips.set(id, clip);
|
|
2650
|
-
this.
|
|
2651
|
-
void trackId;
|
|
3429
|
+
this.syncClipsDelta([clip], []);
|
|
2652
3430
|
return id;
|
|
2653
3431
|
}
|
|
2654
3432
|
removeClip(clipId) {
|
|
2655
3433
|
this.clips.delete(clipId);
|
|
2656
|
-
this.
|
|
3434
|
+
this.syncClipsDelta([], [clipId]);
|
|
3435
|
+
}
|
|
3436
|
+
setMidiClips(clips) {
|
|
3437
|
+
this.midiClips.clear();
|
|
3438
|
+
for (const clip of clips) {
|
|
3439
|
+
const id = clip.id ?? this.nextClipId++;
|
|
3440
|
+
this.midiClips.set(id, { ...clip, id, events: clip.events.map((event) => ({ ...event })) });
|
|
3441
|
+
}
|
|
3442
|
+
this.syncMidiClips();
|
|
3443
|
+
}
|
|
3444
|
+
setBuiltinInstrument(trackId, config = {}) {
|
|
3445
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3446
|
+
this.offlineEngine.setBuiltinInstrument(config, destinationId);
|
|
3447
|
+
this.postInstrumentSync({ type: "syncBuiltinInstrument", destinationId, config });
|
|
3448
|
+
}
|
|
3449
|
+
setSynthInstrument(trackId, patch = {}) {
|
|
3450
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3451
|
+
this.offlineEngine.setSynthInstrument(patch, destinationId);
|
|
3452
|
+
this.postInstrumentSync({ type: "syncSynthInstrument", destinationId, patch });
|
|
3453
|
+
}
|
|
3454
|
+
loadSoundFont(data) {
|
|
3455
|
+
this.offlineEngine.loadSoundFont(data);
|
|
3456
|
+
this.postInstrumentSync({ type: "syncLoadSoundFont", data });
|
|
3457
|
+
}
|
|
3458
|
+
setSf2Instrument(trackId, config = {}) {
|
|
3459
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3460
|
+
this.offlineEngine.setSf2Instrument(config, destinationId);
|
|
3461
|
+
this.postInstrumentSync({ type: "syncSf2Instrument", destinationId, config });
|
|
3462
|
+
}
|
|
3463
|
+
pushMidiNoteOn(trackId, group, channel, note, velocity, renderFrame = -1) {
|
|
3464
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3465
|
+
this.offlineEngine.pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
|
|
3466
|
+
this.postSync({
|
|
3467
|
+
type: "syncMidiNoteOn",
|
|
3468
|
+
destinationId,
|
|
3469
|
+
group,
|
|
3470
|
+
channel,
|
|
3471
|
+
note,
|
|
3472
|
+
velocity,
|
|
3473
|
+
renderFrame
|
|
3474
|
+
});
|
|
3475
|
+
}
|
|
3476
|
+
pushMidiNoteOff(trackId, group, channel, note, velocity = 0, renderFrame = -1) {
|
|
3477
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3478
|
+
this.offlineEngine.pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
|
|
3479
|
+
this.postSync({
|
|
3480
|
+
type: "syncMidiNoteOff",
|
|
3481
|
+
destinationId,
|
|
3482
|
+
group,
|
|
3483
|
+
channel,
|
|
3484
|
+
note,
|
|
3485
|
+
velocity,
|
|
3486
|
+
renderFrame
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
pushMidiCc(trackId, group, channel, controller, value, renderFrame = -1) {
|
|
3490
|
+
const destinationId = this.resolveTargetId(trackId);
|
|
3491
|
+
this.offlineEngine.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
|
|
3492
|
+
this.postSync({
|
|
3493
|
+
type: "syncMidiCc",
|
|
3494
|
+
destinationId,
|
|
3495
|
+
group,
|
|
3496
|
+
channel,
|
|
3497
|
+
controller,
|
|
3498
|
+
value,
|
|
3499
|
+
renderFrame
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
pushMidiPanic(renderFrame = -1) {
|
|
3503
|
+
this.offlineEngine.pushMidiPanic(renderFrame);
|
|
3504
|
+
this.postSync({ type: "syncMidiPanic", renderFrame });
|
|
3505
|
+
}
|
|
3506
|
+
configureCapture(options) {
|
|
3507
|
+
const bufferFrames = Math.trunc(options.bufferFrames);
|
|
3508
|
+
const channels = Math.trunc(options.channels ?? this.offlineChannelCount);
|
|
3509
|
+
const source = options.source ?? "output";
|
|
3510
|
+
const recordOffsetSamples = Math.trunc(options.recordOffsetSamples ?? 0);
|
|
3511
|
+
const inputMonitor = {
|
|
3512
|
+
enabled: Boolean(options.inputMonitor?.enabled),
|
|
3513
|
+
gain: options.inputMonitor?.gain ?? 1
|
|
3514
|
+
};
|
|
3515
|
+
this.offlineEngine.setCaptureBuffer(channels, bufferFrames);
|
|
3516
|
+
this.offlineEngine.setCaptureSource(source);
|
|
3517
|
+
this.offlineEngine.setRecordOffsetSamples(recordOffsetSamples);
|
|
3518
|
+
this.offlineEngine.setInputMonitor(inputMonitor.enabled, inputMonitor.gain);
|
|
3519
|
+
this.captureConfig = { bufferFrames, channels, source, recordOffsetSamples, inputMonitor };
|
|
3520
|
+
this.postSync({ type: "syncCapture", ...this.captureConfig });
|
|
2657
3521
|
}
|
|
2658
3522
|
armRecord(trackId, enabled) {
|
|
3523
|
+
if (enabled && !this.captureConfig) {
|
|
3524
|
+
throw new Error("Capture buffer is not configured");
|
|
3525
|
+
}
|
|
2659
3526
|
this.offlineEngine.armCapture(enabled);
|
|
2660
3527
|
return this.realtimeNode.sendCommand({
|
|
2661
3528
|
type: 13 /* ArmRecord */,
|
|
@@ -2665,8 +3532,8 @@ var SonareEngine = class _SonareEngine {
|
|
|
2665
3532
|
});
|
|
2666
3533
|
}
|
|
2667
3534
|
punch(inPpq, outPpq) {
|
|
2668
|
-
const inSample = this.
|
|
2669
|
-
const outSample = this.
|
|
3535
|
+
const inSample = this.offlineEngine.sampleAtPpq(inPpq);
|
|
3536
|
+
const outSample = this.offlineEngine.sampleAtPpq(outPpq);
|
|
2670
3537
|
this.offlineEngine.setCapturePunch(inSample, outSample, true);
|
|
2671
3538
|
return this.realtimeNode.sendCommand({
|
|
2672
3539
|
type: 14 /* Punch */,
|
|
@@ -2675,6 +3542,16 @@ var SonareEngine = class _SonareEngine {
|
|
|
2675
3542
|
argFloat: outSample
|
|
2676
3543
|
});
|
|
2677
3544
|
}
|
|
3545
|
+
captureStatus() {
|
|
3546
|
+
return this.realtimeNode.requestCaptureStatus();
|
|
3547
|
+
}
|
|
3548
|
+
capturedAudio() {
|
|
3549
|
+
return this.realtimeNode.requestCapturedAudio();
|
|
3550
|
+
}
|
|
3551
|
+
async resetCapture() {
|
|
3552
|
+
this.offlineEngine.resetCapture();
|
|
3553
|
+
await this.realtimeNode.requestCaptureReset();
|
|
3554
|
+
}
|
|
2678
3555
|
setMetronome(opts) {
|
|
2679
3556
|
this.offlineEngine.setMetronome(opts);
|
|
2680
3557
|
this.postSync({ type: "syncMetronome", config: opts });
|
|
@@ -2690,6 +3567,55 @@ var SonareEngine = class _SonareEngine {
|
|
|
2690
3567
|
this.syncMarkers();
|
|
2691
3568
|
return id;
|
|
2692
3569
|
}
|
|
3570
|
+
/**
|
|
3571
|
+
* Replaces the whole marker set in one call.
|
|
3572
|
+
*
|
|
3573
|
+
* Entries without an `id` are assigned fresh ids; entries carrying an `id`
|
|
3574
|
+
* keep it (ids must be positive and unique within the list). Returns the
|
|
3575
|
+
* resolved markers in the order given, so a caller can map its own marker
|
|
3576
|
+
* identities to the engine ids used by `seekMarker`/`setLoopFromMarkers`.
|
|
3577
|
+
*
|
|
3578
|
+
* @param markers The full marker list (an empty list clears all markers).
|
|
3579
|
+
* @returns The markers with their resolved engine ids.
|
|
3580
|
+
*/
|
|
3581
|
+
setMarkers(markers) {
|
|
3582
|
+
const resolved = [];
|
|
3583
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3584
|
+
for (const marker of markers) {
|
|
3585
|
+
if (!Number.isFinite(marker.ppq)) {
|
|
3586
|
+
throw new Error(`Invalid marker ppq: ${String(marker.ppq)}`);
|
|
3587
|
+
}
|
|
3588
|
+
if (marker.id !== void 0) {
|
|
3589
|
+
if (!Number.isInteger(marker.id) || marker.id <= 0) {
|
|
3590
|
+
throw new Error(`Invalid marker id: ${String(marker.id)}`);
|
|
3591
|
+
}
|
|
3592
|
+
if (seen.has(marker.id)) {
|
|
3593
|
+
throw new Error(`Duplicate marker id: ${marker.id}`);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
const id = marker.id ?? this.nextMarkerId++;
|
|
3597
|
+
seen.add(id);
|
|
3598
|
+
if (id >= this.nextMarkerId) {
|
|
3599
|
+
this.nextMarkerId = id + 1;
|
|
3600
|
+
}
|
|
3601
|
+
resolved.push({ id, ppq: marker.ppq, name: marker.name ?? "" });
|
|
3602
|
+
}
|
|
3603
|
+
this.markers.clear();
|
|
3604
|
+
for (const marker of resolved) {
|
|
3605
|
+
this.markers.set(marker.id, marker);
|
|
3606
|
+
}
|
|
3607
|
+
this.syncMarkers();
|
|
3608
|
+
return resolved.map((marker) => ({ ...marker }));
|
|
3609
|
+
}
|
|
3610
|
+
markerCount() {
|
|
3611
|
+
return this.offlineEngine.markerCount();
|
|
3612
|
+
}
|
|
3613
|
+
markerByIndex(index) {
|
|
3614
|
+
return this.offlineEngine.markerByIndex(index);
|
|
3615
|
+
}
|
|
3616
|
+
marker(markerId) {
|
|
3617
|
+
return this.offlineEngine.marker(markerId);
|
|
3618
|
+
}
|
|
2693
3619
|
seekMarker(markerId) {
|
|
2694
3620
|
this.offlineEngine.seekMarker(markerId);
|
|
2695
3621
|
return this.realtimeNode.sendCommand({
|
|
@@ -2698,6 +3624,12 @@ var SonareEngine = class _SonareEngine {
|
|
|
2698
3624
|
sampleTime: -1
|
|
2699
3625
|
});
|
|
2700
3626
|
}
|
|
3627
|
+
setLoopFromMarkers(startMarkerId, endMarkerId) {
|
|
3628
|
+
this.offlineEngine.setLoopFromMarkers(startMarkerId, endMarkerId);
|
|
3629
|
+
const start = this.offlineEngine.marker(startMarkerId);
|
|
3630
|
+
const end = this.offlineEngine.marker(endMarkerId);
|
|
3631
|
+
return this.setLoop(start.ppq, end.ppq, true);
|
|
3632
|
+
}
|
|
2701
3633
|
async renderOffline(totalFrames) {
|
|
2702
3634
|
const frames = Math.max(0, Math.floor(totalFrames));
|
|
2703
3635
|
const inputs = [];
|
|
@@ -2728,16 +3660,80 @@ var SonareEngine = class _SonareEngine {
|
|
|
2728
3660
|
this.realtimeNode.destroy();
|
|
2729
3661
|
this.offlineEngine.destroy();
|
|
2730
3662
|
}
|
|
2731
|
-
|
|
3663
|
+
syncClipsDelta(upserts, removeIds) {
|
|
2732
3664
|
const clips = Array.from(this.clips.values());
|
|
2733
3665
|
this.offlineEngine.setClips(clips);
|
|
2734
|
-
this.postSync({
|
|
3666
|
+
this.postSync({
|
|
3667
|
+
type: "syncClipsDelta",
|
|
3668
|
+
upserts,
|
|
3669
|
+
removeIds
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
syncMidiClips() {
|
|
3673
|
+
const clips = Array.from(this.midiClips.values());
|
|
3674
|
+
this.offlineEngine.setMidiClips(clips);
|
|
3675
|
+
this.postSync({ type: "syncMidiClips", clips });
|
|
3676
|
+
}
|
|
3677
|
+
syncMixer() {
|
|
3678
|
+
const lanes = this.trackLaneIds.map((trackId) => {
|
|
3679
|
+
const sends = this.trackSends.get(trackId);
|
|
3680
|
+
return sends && sends.length > 0 ? { trackId, sends: sends.map((send) => ({ ...send })) } : { trackId };
|
|
3681
|
+
});
|
|
3682
|
+
const buses = this.buses.map((bus) => ({ ...bus }));
|
|
3683
|
+
this.offlineEngine.setTrackBuses(buses);
|
|
3684
|
+
if (lanes.length > 0) {
|
|
3685
|
+
this.offlineEngine.setTrackLanes(lanes);
|
|
3686
|
+
}
|
|
3687
|
+
const trackStrips = Array.from(this.trackStripJson, ([trackId, sceneJson]) => ({
|
|
3688
|
+
trackId,
|
|
3689
|
+
sceneJson
|
|
3690
|
+
}));
|
|
3691
|
+
const busStrips = Array.from(this.busStripJson, ([busId, sceneJson]) => ({
|
|
3692
|
+
busId,
|
|
3693
|
+
sceneJson
|
|
3694
|
+
}));
|
|
3695
|
+
this.postSync({
|
|
3696
|
+
type: "syncMixer",
|
|
3697
|
+
lanes,
|
|
3698
|
+
buses,
|
|
3699
|
+
trackStrips,
|
|
3700
|
+
busStrips,
|
|
3701
|
+
masterStripJson: this.masterStripJson
|
|
3702
|
+
});
|
|
2735
3703
|
}
|
|
2736
3704
|
syncMarkers() {
|
|
2737
3705
|
const markers = Array.from(this.markers.values()).sort((a, b) => a.ppq - b.ppq);
|
|
2738
3706
|
this.offlineEngine.setMarkers(markers);
|
|
2739
3707
|
this.postSync({ type: "syncMarkers", markers });
|
|
2740
3708
|
}
|
|
3709
|
+
postInstrumentSync(message) {
|
|
3710
|
+
if (this.destroyed) {
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
if (this.transportPlaying) {
|
|
3714
|
+
this.pendingInstrumentSync.push(message);
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
this.postSync(message);
|
|
3718
|
+
}
|
|
3719
|
+
flushPendingInstrumentSync() {
|
|
3720
|
+
if (this.destroyed || this.pendingInstrumentSync.length === 0) {
|
|
3721
|
+
return;
|
|
3722
|
+
}
|
|
3723
|
+
const pending = this.pendingInstrumentSync.splice(0);
|
|
3724
|
+
for (const message of pending) {
|
|
3725
|
+
this.postSync(message);
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
postTempoSync() {
|
|
3729
|
+
this.postSync({
|
|
3730
|
+
type: "syncTempo",
|
|
3731
|
+
bpm: this.tempoBpm,
|
|
3732
|
+
timeSignature: { ...this.timeSignature },
|
|
3733
|
+
tempoSegments: this.tempoSegments.map((segment) => ({ ...segment })),
|
|
3734
|
+
timeSignatureSegments: this.timeSignatureSegments.map((segment) => ({ ...segment }))
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
2741
3737
|
// Posts an out-of-band control-sync message to the worklet engine processor.
|
|
2742
3738
|
// Sync messages use a string `type` so the worklet's message handler routes
|
|
2743
3739
|
// them to receiveSync() (numeric `type` is reserved for SonareEngineCommandRecord).
|
|
@@ -2764,15 +3760,38 @@ var SonareEngine = class _SonareEngine {
|
|
|
2764
3760
|
const parsed = Number.parseInt(target, 10);
|
|
2765
3761
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
2766
3762
|
}
|
|
3763
|
+
ensureTrackLane(target) {
|
|
3764
|
+
const trackId = this.resolveTargetId(target);
|
|
3765
|
+
if (!Number.isInteger(trackId) || trackId <= 0) {
|
|
3766
|
+
throw new Error(`Invalid track id for mixer lane: ${String(target)}`);
|
|
3767
|
+
}
|
|
3768
|
+
const existing = this.trackLaneIds.indexOf(trackId);
|
|
3769
|
+
if (existing >= 0) {
|
|
3770
|
+
return existing;
|
|
3771
|
+
}
|
|
3772
|
+
this.trackLaneIds.push(trackId);
|
|
3773
|
+
this.syncMixer();
|
|
3774
|
+
return this.trackLaneIds.length - 1;
|
|
3775
|
+
}
|
|
3776
|
+
ensureBus(busId) {
|
|
3777
|
+
const resolved = Math.trunc(busId);
|
|
3778
|
+
if (!Number.isInteger(resolved) || resolved <= 0) {
|
|
3779
|
+
throw new Error(`Invalid bus id for mixer bus: ${String(busId)}`);
|
|
3780
|
+
}
|
|
3781
|
+
const existing = this.buses.findIndex((bus) => bus.busId === resolved);
|
|
3782
|
+
if (existing >= 0) {
|
|
3783
|
+
return existing;
|
|
3784
|
+
}
|
|
3785
|
+
this.buses.push({ busId: resolved });
|
|
3786
|
+
this.syncMixer();
|
|
3787
|
+
return this.buses.length - 1;
|
|
3788
|
+
}
|
|
2767
3789
|
curveCode(curve) {
|
|
2768
3790
|
if (typeof curve === "number") {
|
|
2769
3791
|
return curve;
|
|
2770
3792
|
}
|
|
2771
3793
|
return curve === "exponential" ? 1 : 0;
|
|
2772
3794
|
}
|
|
2773
|
-
ppqToApproxSample(ppq) {
|
|
2774
|
-
return Math.max(0, Math.round(ppq * 60 / 120 * this.sampleRate));
|
|
2775
|
-
}
|
|
2776
3795
|
};
|
|
2777
3796
|
var _SonareRealtimeVoiceChangerWorkletProcessor = class _SonareRealtimeVoiceChangerWorkletProcessor {
|
|
2778
3797
|
constructor(options = {}) {
|
|
@@ -2993,23 +4012,33 @@ function registerSonareRealtimeEngineWorkletProcessor(name = "sonare-realtime-en
|
|
|
2993
4012
|
class RegisteredSonareRealtimeEngineWorkletProcessor extends Base {
|
|
2994
4013
|
constructor(options) {
|
|
2995
4014
|
super();
|
|
4015
|
+
this.pendingMessages = [];
|
|
2996
4016
|
const port = this.port;
|
|
2997
4017
|
const processorOptions = options?.processorOptions ?? {};
|
|
2998
4018
|
if (processorOptions.runtimeTarget === "sonare-rt") {
|
|
2999
4019
|
void this.initializeSonareRt(processorOptions, port);
|
|
3000
4020
|
} else {
|
|
3001
|
-
this.
|
|
3002
|
-
postMessage: (message) => port?.postMessage?.(message),
|
|
3003
|
-
onMeter: (meter) => port?.postMessage?.(meter)
|
|
3004
|
-
});
|
|
4021
|
+
void this.initializeEmbind(processorOptions, port);
|
|
3005
4022
|
}
|
|
3006
4023
|
const onMessage = (event) => {
|
|
4024
|
+
if (!this.bridge && !this.rtBridge) {
|
|
4025
|
+
if (this.pendingMessages.length < 1024) {
|
|
4026
|
+
this.pendingMessages.push(event.data);
|
|
4027
|
+
}
|
|
4028
|
+
return;
|
|
4029
|
+
}
|
|
3007
4030
|
if (isEngineCommandRecord(event.data)) {
|
|
3008
4031
|
this.bridge?.receiveCommand(event.data);
|
|
3009
4032
|
this.rtBridge?.receiveCommand(event.data);
|
|
3010
4033
|
} else if (isEngineSyncMessage(event.data)) {
|
|
3011
4034
|
this.bridge?.receiveSync(event.data);
|
|
3012
4035
|
this.rtBridge?.receiveSync(event.data);
|
|
4036
|
+
} else if (isEngineCaptureRequestMessage(event.data)) {
|
|
4037
|
+
this.bridge?.receiveCaptureRequest(event.data);
|
|
4038
|
+
this.rtBridge?.receiveCaptureRequest(event.data, port);
|
|
4039
|
+
} else if (isEngineTransportRequestMessage(event.data)) {
|
|
4040
|
+
this.bridge?.receiveTransportRequest(event.data);
|
|
4041
|
+
this.rtBridge?.receiveTransportRequest(event.data, port);
|
|
3013
4042
|
}
|
|
3014
4043
|
};
|
|
3015
4044
|
if (port?.addEventListener) {
|
|
@@ -3032,6 +4061,60 @@ function registerSonareRealtimeEngineWorkletProcessor(name = "sonare-realtime-en
|
|
|
3032
4061
|
}
|
|
3033
4062
|
return true;
|
|
3034
4063
|
}
|
|
4064
|
+
replayPendingMessages(port) {
|
|
4065
|
+
const messages = this.pendingMessages.splice(0);
|
|
4066
|
+
for (const data of messages) {
|
|
4067
|
+
if (isEngineCommandRecord(data)) {
|
|
4068
|
+
this.bridge?.receiveCommand(data);
|
|
4069
|
+
this.rtBridge?.receiveCommand(data);
|
|
4070
|
+
} else if (isEngineSyncMessage(data)) {
|
|
4071
|
+
this.bridge?.receiveSync(data);
|
|
4072
|
+
this.rtBridge?.receiveSync(data);
|
|
4073
|
+
} else if (isEngineCaptureRequestMessage(data)) {
|
|
4074
|
+
this.bridge?.receiveCaptureRequest(data);
|
|
4075
|
+
this.rtBridge?.receiveCaptureRequest(data, port);
|
|
4076
|
+
} else if (isEngineTransportRequestMessage(data)) {
|
|
4077
|
+
this.bridge?.receiveTransportRequest(data);
|
|
4078
|
+
this.rtBridge?.receiveTransportRequest(data, port);
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
async initializeEmbind(options, port) {
|
|
4083
|
+
try {
|
|
4084
|
+
const initPromise2 = globalThis.SonareEmbindInitPromise;
|
|
4085
|
+
if (initPromise2) {
|
|
4086
|
+
await initPromise2;
|
|
4087
|
+
}
|
|
4088
|
+
if (!isInitialized()) {
|
|
4089
|
+
const moduleFactory = globalThis.SonareEmbindModuleFactory;
|
|
4090
|
+
if (!moduleFactory) {
|
|
4091
|
+
throw new Error("embind realtime engine module is not initialized.");
|
|
4092
|
+
}
|
|
4093
|
+
await init({
|
|
4094
|
+
locateFile: (path) => path,
|
|
4095
|
+
wasmBinary: options.wasmBinary,
|
|
4096
|
+
moduleFactory
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
this.bridge = new SonareRealtimeEngineWorkletProcessor(options, {
|
|
4100
|
+
postMessage: (message) => port?.postMessage?.(message),
|
|
4101
|
+
onMeter: (meter) => port?.postMessage?.(meter)
|
|
4102
|
+
});
|
|
4103
|
+
for (const message of options.initialSyncMessages ?? []) {
|
|
4104
|
+
this.bridge.receiveSync(message);
|
|
4105
|
+
}
|
|
4106
|
+
for (const command of options.initialCommands ?? []) {
|
|
4107
|
+
this.bridge.receiveCommand(command);
|
|
4108
|
+
}
|
|
4109
|
+
this.replayPendingMessages(port);
|
|
4110
|
+
port?.postMessage?.({ type: "ready", runtimeTarget: "embind" });
|
|
4111
|
+
} catch (error) {
|
|
4112
|
+
port?.postMessage?.({
|
|
4113
|
+
type: "error",
|
|
4114
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4115
|
+
});
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
3035
4118
|
async initializeSonareRt(options, port) {
|
|
3036
4119
|
try {
|
|
3037
4120
|
if (!options.rtModuleUrl) {
|
|
@@ -3057,6 +4140,7 @@ function registerSonareRealtimeEngineWorkletProcessor(name = "sonare-realtime-en
|
|
|
3057
4140
|
telemetrySharedBuffer: options.telemetrySharedBuffer,
|
|
3058
4141
|
telemetryRingCapacity: options.telemetryRingCapacity
|
|
3059
4142
|
});
|
|
4143
|
+
this.replayPendingMessages(port);
|
|
3060
4144
|
port?.postMessage?.({ type: "ready", runtimeTarget: "sonare-rt" });
|
|
3061
4145
|
} catch (error) {
|
|
3062
4146
|
port?.postMessage?.({
|