@opendaw/studio-core 0.0.127 → 0.0.129

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.
@@ -20,9 +20,9 @@ import { AudioUnitFreeze } from "../AudioUnitFreeze";
20
20
  export class Project {
21
21
  static new(env, options) {
22
22
  const createDefaultUser = options?.noDefaultUser !== true;
23
- const createOutputCompressor = StudioPreferences.settings.engine["auto-create-output-compressor"];
23
+ const createOutputMaximizer = StudioPreferences.settings.engine["auto-create-output-maximizer"];
24
24
  const { boxGraph, mandatoryBoxes } = ProjectSkeleton.empty({
25
- createOutputCompressor,
25
+ createOutputMaximizer,
26
26
  createDefaultUser
27
27
  });
28
28
  const project = new Project(env, boxGraph, mandatoryBoxes);
@@ -123,7 +123,7 @@ export class Project {
123
123
  }
124
124
  else if (update instanceof DeleteUpdate && update.name === AudioFileBox.ClassName) {
125
125
  this.#unregisterSample(update.uuid);
126
- this.#deleteUserCreatedSample(update.uuid);
126
+ this.#deleteUserCreatedSample(update.uuid).finally();
127
127
  }
128
128
  }
129
129
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"AudioUnitsClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/AudioUnitsClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,OAAO,EAAO,MAAM,EAAY,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAC1G,OAAO,EAAM,QAAQ,EAAa,MAAM,kBAAkB,CAAA;AAW1D,OAAO,EAAC,mBAAmB,EAAqB,cAAc,EAAE,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAC5G,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AAGpE,KAAK,mBAAmB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;AAMxD,yBAAiB,mBAAmB,CAAC;IACjC,KAAY,OAAO,GAAG;QAClB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;QAC3B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;QACvC,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAA;QACtC,QAAQ,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAA;KACrE,CAAA;IAaM,MAAM,aAAa,GAAI,yFAOG,OAAO,KAAG,gBAAgB,CAAC,mBAAmB,CAsE9E,CAAA;CA6GJ"}
1
+ {"version":3,"file":"AudioUnitsClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/AudioUnitsClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,OAAO,EAAO,MAAM,EAAY,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAC1G,OAAO,EAAM,QAAQ,EAAa,MAAM,kBAAkB,CAAA;AAW1D,OAAO,EAAC,mBAAmB,EAAqB,cAAc,EAAE,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAC5G,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AAGpE,KAAK,mBAAmB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;AAMxD,yBAAiB,mBAAmB,CAAC;IACjC,KAAY,OAAO,GAAG;QAClB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;QAC3B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;QACvC,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAA;QACtC,QAAQ,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAA;KACrE,CAAA;IAaM,MAAM,aAAa,GAAI,yFAOG,OAAO,KAAG,gBAAgB,CAAC,mBAAmB,CAsE9E,CAAA;CA+GJ"}
@@ -138,6 +138,9 @@ export var AudioUnitsClipboard;
138
138
  }
139
139
  ClipboardUtils.deserializeBoxes(data, boxGraph, {
140
140
  mapPointer: (pointer, address) => {
141
+ if (address.isEmpty()) {
142
+ return Option.None;
143
+ }
141
144
  if (pointer.pointerType === Pointers.TrackCollection) {
142
145
  return Option.wrap(outputBox.tracks.address);
143
146
  }
@@ -169,6 +172,9 @@ export var AudioUnitsClipboard;
169
172
  }
170
173
  const boxes = ClipboardUtils.deserializeBoxes(data, boxGraph, {
171
174
  mapPointer: (pointer, address) => {
175
+ if (address.isEmpty()) {
176
+ return Option.None;
177
+ }
172
178
  if (pointer.pointerType === Pointers.AudioUnits) {
173
179
  return Option.wrap(rootBox.audioUnits.address);
174
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DevicesClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/DevicesClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,OAAO,EAKP,MAAM,EAEN,QAAQ,EAGX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAM,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAEH,WAAW,EACX,gBAAgB,EAEhB,UAAU,EAGV,iBAAiB,EAGpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AAGpE,KAAK,gBAAgB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;AAajD,yBAAiB,gBAAgB,CAAC;IAC9B,KAAY,OAAO,GAAG;QAClB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,CAAA;QACvD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;QAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;QACjC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;KACjD,CAAA;IAyBM,MAAM,aAAa,GAAI,oEAOG,OAAO,KAAG,gBAAgB,CAAC,gBAAgB,CA2M3E,CAAA;CACJ"}
1
+ {"version":3,"file":"DevicesClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/DevicesClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,OAAO,EAKP,MAAM,EAEN,QAAQ,EAGX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAM,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAEH,WAAW,EACX,gBAAgB,EAEhB,UAAU,EAGV,iBAAiB,EAGpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AAGpE,KAAK,gBAAgB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;AAajD,yBAAiB,gBAAgB,CAAC;IAC9B,KAAY,OAAO,GAAG;QAClB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,CAAA;QACvD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;QAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;QACjC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;KACjD,CAAA;IAyBM,MAAM,aAAa,GAAI,oEAOG,OAAO,KAAG,gBAAgB,CAAC,gBAAgB,CAwN3E,CAAA;CACJ"}
@@ -1,6 +1,6 @@
1
1
  import { ByteArrayInput, ByteArrayOutput, isDefined, isInstanceOf, isNotNull, Option, RuntimeNotifier } from "@opendaw/lib-std";
2
2
  import { Pointers } from "@opendaw/studio-enums";
3
- import { TrackBox } from "@opendaw/studio-boxes";
3
+ import { RootBox, TrackBox } from "@opendaw/studio-boxes";
4
4
  import { DeviceBoxUtils, Devices } from "@opendaw/studio-adapters";
5
5
  import { ClipboardUtils } from "../ClipboardUtils";
6
6
  export var DevicesClipboard;
@@ -69,11 +69,16 @@ export var DevicesClipboard;
69
69
  .filter(pointer => pointer.mandatory && !pointer.box.ephemeral
70
70
  && !isDefined(pointer.box.resource))
71
71
  .map(pointer => pointer.box);
72
+ const mandatoryDeps = Array.from(boxGraph.dependenciesOf(box, {
73
+ alwaysFollowMandatory: true,
74
+ excludeBox: (dep) => dep.ephemeral || DeviceBoxUtils.isDeviceBox(dep)
75
+ || dep.name === RootBox.ClassName
76
+ }).boxes).filter(dep => !isDefined(dep.resource));
72
77
  const preserved = [box, ...ownedChildren].flatMap(root => Array.from(boxGraph.dependenciesOf(root, {
73
78
  alwaysFollowMandatory: true,
74
79
  excludeBox: (dep) => dep.ephemeral || DeviceBoxUtils.isDeviceBox(dep)
75
80
  }).boxes).filter(dep => dep.resource === "preserved"));
76
- return [...ownedChildren, ...preserved];
81
+ return [...ownedChildren, ...mandatoryDeps, ...preserved];
77
82
  });
78
83
  const trackContent = [];
79
84
  if (isNotNull(instrument)) {
@@ -194,7 +199,10 @@ export var DevicesClipboard;
194
199
  }
195
200
  }
196
201
  const boxes = ClipboardUtils.deserializeBoxes(entry.data, boxGraph, {
197
- mapPointer: pointer => {
202
+ mapPointer: (pointer, address) => {
203
+ if (address.isEmpty()) {
204
+ return Option.None;
205
+ }
198
206
  if (pointer.pointerType === Pointers.InstrumentHost && replaceInstrument) {
199
207
  return Option.wrap(host.inputField.address);
200
208
  }
@@ -210,6 +218,13 @@ export var DevicesClipboard;
210
218
  if (pointer.pointerType === Pointers.Automation && replaceInstrument) {
211
219
  return Option.wrap(host.audioUnitBoxAdapter().address);
212
220
  }
221
+ if (pointer.pointerType === Pointers.MIDIDevice) {
222
+ const rootBox = boxGraph.boxes()
223
+ .find(box => isInstanceOf(box, RootBox));
224
+ if (isDefined(rootBox)) {
225
+ return Option.wrap(rootBox.outputMidiDevices.address);
226
+ }
227
+ }
213
228
  return Option.None;
214
229
  },
215
230
  excludeBox: box => !replaceInstrument
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it, beforeEach } from "vitest";
2
2
  import { isDefined, isInstanceOf, Option, UUID } from "@opendaw/lib-std";
3
3
  import { BoxEditing } from "@opendaw/lib-box";
4
- import { ApparatDeviceBox, AudioFileBox, AudioUnitBox, CompressorDeviceBox, NoteEventCollectionBox, NoteRegionBox, PlayfieldDeviceBox, PlayfieldSampleBox, TapeDeviceBox, TrackBox, VaporisateurDeviceBox, ValueEventCollectionBox, ValueRegionBox, WerkstattParameterBox, WerkstattSampleBox } from "@opendaw/studio-boxes";
4
+ import { ApparatDeviceBox, AudioFileBox, AudioUnitBox, CompressorDeviceBox, MIDIOutputBox, MIDIOutputDeviceBox, NoteEventCollectionBox, NoteRegionBox, PlayfieldDeviceBox, PlayfieldSampleBox, RootBox, TapeDeviceBox, TrackBox, VaporisateurDeviceBox, ValueEventCollectionBox, ValueRegionBox, WerkstattParameterBox, WerkstattSampleBox } from "@opendaw/studio-boxes";
5
5
  import { AudioUnitType, Pointers } from "@opendaw/studio-enums";
6
6
  import { DeviceBoxUtils, ProjectSkeleton, TrackType } from "@opendaw/studio-adapters";
7
7
  import { ClipboardUtils } from "../ClipboardUtils";
@@ -9,8 +9,8 @@ describe("DevicesClipboardHandler", () => {
9
9
  let source;
10
10
  let target;
11
11
  beforeEach(() => {
12
- source = ProjectSkeleton.empty({ createDefaultUser: true, createOutputCompressor: false });
13
- target = ProjectSkeleton.empty({ createDefaultUser: true, createOutputCompressor: false });
12
+ source = ProjectSkeleton.empty({ createDefaultUser: true, createOutputMaximizer: false });
13
+ target = ProjectSkeleton.empty({ createDefaultUser: true, createOutputMaximizer: false });
14
14
  });
15
15
  const createAudioUnit = (skeleton, index = 1) => {
16
16
  const { boxGraph, mandatoryBoxes: { rootBox, primaryAudioBusBox } } = skeleton;
@@ -193,6 +193,11 @@ describe("DevicesClipboardHandler", () => {
193
193
  .filter(pointer => pointer.mandatory && !pointer.box.ephemeral
194
194
  && !isDefined(pointer.box.resource))
195
195
  .map(pointer => pointer.box);
196
+ const mandatoryDeps = Array.from(boxGraph.dependenciesOf(deviceBox, {
197
+ alwaysFollowMandatory: true,
198
+ excludeBox: (dep) => dep.ephemeral || DeviceBoxUtils.isDeviceBox(dep)
199
+ || dep.name === RootBox.ClassName
200
+ }).boxes).filter(dep => !isDefined(dep.resource));
196
201
  const preserved = [deviceBox, ...ownedChildren].flatMap(root => Array.from(boxGraph.dependenciesOf(root, {
197
202
  alwaysFollowMandatory: true,
198
203
  excludeBox: (dep) => dep.ephemeral || DeviceBoxUtils.isDeviceBox(dep)
@@ -219,7 +224,7 @@ describe("DevicesClipboardHandler", () => {
219
224
  }
220
225
  }
221
226
  const seen = new Set();
222
- return [...ownedChildren, ...preserved, ...trackContent].filter(box => {
227
+ return [...ownedChildren, ...mandatoryDeps, ...preserved, ...trackContent].filter(box => {
223
228
  const uuid = UUID.toString(box.address.uuid);
224
229
  if (seen.has(uuid))
225
230
  return false;
@@ -958,6 +963,179 @@ describe("DevicesClipboardHandler", () => {
958
963
  // ─────────────────────────────────────────────────────────
959
964
  // isInstanceOf type narrowing
960
965
  // ─────────────────────────────────────────────────────────
966
+ // ─────────────────────────────────────────────────────────
967
+ // MIDIOutputDeviceBox copy & paste
968
+ // ─────────────────────────────────────────────────────────
969
+ const addMIDIOutputInstrument = (skeleton, audioUnit, label) => {
970
+ const { boxGraph } = skeleton;
971
+ let device;
972
+ boxGraph.beginTransaction();
973
+ device = MIDIOutputDeviceBox.create(boxGraph, UUID.generate(), box => {
974
+ box.label.setValue(label);
975
+ box.host.refer(audioUnit.input);
976
+ });
977
+ boxGraph.endTransaction();
978
+ return device;
979
+ };
980
+ const connectMIDIOutput = (skeleton, deviceBox, outputId, outputLabel) => {
981
+ const { boxGraph, mandatoryBoxes: { rootBox } } = skeleton;
982
+ let midiOutputBox;
983
+ boxGraph.beginTransaction();
984
+ midiOutputBox = MIDIOutputBox.create(boxGraph, UUID.generate(), box => {
985
+ box.id.setValue(outputId);
986
+ box.label.setValue(outputLabel);
987
+ box.root.refer(rootBox.outputMidiDevices);
988
+ });
989
+ deviceBox.device.refer(midiOutputBox.device);
990
+ boxGraph.endTransaction();
991
+ return midiOutputBox;
992
+ };
993
+ describe("MIDIOutputDeviceBox copy & paste", () => {
994
+ it("collects MIDIOutputBox as dependency when device pointer is set", () => {
995
+ const audioUnit = createAudioUnit(source);
996
+ const midiDevice = addMIDIOutputInstrument(source, audioUnit, "MIDIOut");
997
+ connectMIDIOutput(source, midiDevice, "output-1", "MIDI Port 1");
998
+ const deps = collectDeviceDependencies(midiDevice, source.boxGraph);
999
+ expect(deps.filter(box => isInstanceOf(box, MIDIOutputBox)).length).toBe(1);
1000
+ });
1001
+ it("does not collect RootBox as dependency", () => {
1002
+ const audioUnit = createAudioUnit(source);
1003
+ const midiDevice = addMIDIOutputInstrument(source, audioUnit, "MIDIOut");
1004
+ connectMIDIOutput(source, midiDevice, "output-1", "MIDI Port 1");
1005
+ const deps = collectDeviceDependencies(midiDevice, source.boxGraph);
1006
+ expect(deps.filter(box => isInstanceOf(box, RootBox)).length).toBe(0);
1007
+ });
1008
+ it("does not collect MIDIOutputBox when device pointer is empty", () => {
1009
+ const audioUnit = createAudioUnit(source);
1010
+ const midiDevice = addMIDIOutputInstrument(source, audioUnit, "MIDIOut");
1011
+ const deps = collectDeviceDependencies(midiDevice, source.boxGraph);
1012
+ expect(deps.filter(box => isInstanceOf(box, MIDIOutputBox)).length).toBe(0);
1013
+ });
1014
+ it("paste MIDIOutput with empty device pointer does not fill it", () => {
1015
+ const sourceAU = createAudioUnit(source);
1016
+ const sourceMidi = addMIDIOutputInstrument(source, sourceAU, "Source MIDI");
1017
+ const deps = collectDeviceDependencies(sourceMidi, source.boxGraph, sourceAU);
1018
+ const data = ClipboardUtils.serializeBoxes([sourceMidi, ...deps]);
1019
+ const targetAU = createAudioUnit(target);
1020
+ target.boxGraph.beginTransaction();
1021
+ const boxes = ClipboardUtils.deserializeBoxes(data, target.boxGraph, {
1022
+ mapPointer: (pointer, address) => {
1023
+ if (address.isEmpty()) {
1024
+ return Option.None;
1025
+ }
1026
+ if (pointer.pointerType === Pointers.InstrumentHost) {
1027
+ return Option.wrap(targetAU.input.address);
1028
+ }
1029
+ if (pointer.pointerType === Pointers.MIDIDevice) {
1030
+ return Option.wrap(target.mandatoryBoxes.rootBox.outputMidiDevices.address);
1031
+ }
1032
+ if (pointer.pointerType === Pointers.TrackCollection) {
1033
+ return Option.wrap(targetAU.tracks.address);
1034
+ }
1035
+ if (pointer.pointerType === Pointers.Automation) {
1036
+ return Option.wrap(targetAU.address);
1037
+ }
1038
+ return Option.None;
1039
+ },
1040
+ excludeBox: () => false
1041
+ });
1042
+ target.boxGraph.endTransaction();
1043
+ const pastedDevice = boxes.find(box => isInstanceOf(box, MIDIOutputDeviceBox));
1044
+ expect(pastedDevice).toBeDefined();
1045
+ expect(pastedDevice.device.isEmpty()).toBe(true);
1046
+ });
1047
+ it("paste-replace MIDIOutput with connected device does not throw", () => {
1048
+ const sourceAU = createAudioUnit(source);
1049
+ const sourceMidi = addMIDIOutputInstrument(source, sourceAU, "Source MIDI");
1050
+ connectMIDIOutput(source, sourceMidi, "output-1", "MIDI Port 1");
1051
+ const deps = collectDeviceDependencies(sourceMidi, source.boxGraph, sourceAU);
1052
+ const data = ClipboardUtils.serializeBoxes([sourceMidi, ...deps]);
1053
+ const targetAU = createAudioUnit(target);
1054
+ const targetVapo = addVaporisateur(target, targetAU, "Target Vapo");
1055
+ expect(() => {
1056
+ target.boxGraph.beginTransaction();
1057
+ targetVapo.delete();
1058
+ ClipboardUtils.deserializeBoxes(data, target.boxGraph, {
1059
+ ...makePasteMapper(targetAU, true),
1060
+ mapPointer: (pointer, address) => {
1061
+ const base = makePasteMapper(targetAU, true).mapPointer(pointer);
1062
+ if (base.nonEmpty()) {
1063
+ return base;
1064
+ }
1065
+ if (pointer.pointerType === Pointers.MIDIDevice) {
1066
+ return Option.wrap(target.mandatoryBoxes.rootBox.outputMidiDevices.address);
1067
+ }
1068
+ return Option.None;
1069
+ }
1070
+ });
1071
+ target.boxGraph.endTransaction();
1072
+ }).not.toThrow();
1073
+ });
1074
+ it("pasted MIDIOutputBox.root points to target RootBox.outputMidiDevices", () => {
1075
+ const sourceAU = createAudioUnit(source);
1076
+ const sourceMidi = addMIDIOutputInstrument(source, sourceAU, "Source MIDI");
1077
+ connectMIDIOutput(source, sourceMidi, "output-1", "MIDI Port 1");
1078
+ const deps = collectDeviceDependencies(sourceMidi, source.boxGraph, sourceAU);
1079
+ const data = ClipboardUtils.serializeBoxes([sourceMidi, ...deps]);
1080
+ const targetAU = createAudioUnit(target);
1081
+ target.boxGraph.beginTransaction();
1082
+ const boxes = ClipboardUtils.deserializeBoxes(data, target.boxGraph, {
1083
+ mapPointer: (pointer, address) => {
1084
+ if (pointer.pointerType === Pointers.InstrumentHost) {
1085
+ return Option.wrap(targetAU.input.address);
1086
+ }
1087
+ if (pointer.pointerType === Pointers.MIDIDevice) {
1088
+ return Option.wrap(target.mandatoryBoxes.rootBox.outputMidiDevices.address);
1089
+ }
1090
+ if (pointer.pointerType === Pointers.TrackCollection) {
1091
+ return Option.wrap(targetAU.tracks.address);
1092
+ }
1093
+ if (pointer.pointerType === Pointers.Automation) {
1094
+ return Option.wrap(targetAU.address);
1095
+ }
1096
+ return Option.None;
1097
+ },
1098
+ excludeBox: () => false
1099
+ });
1100
+ target.boxGraph.endTransaction();
1101
+ const pastedMidiOutputBox = boxes.find(box => isInstanceOf(box, MIDIOutputBox));
1102
+ expect(pastedMidiOutputBox).toBeDefined();
1103
+ expect(pastedMidiOutputBox.root.targetVertex.unwrap().box).toBe(target.mandatoryBoxes.rootBox);
1104
+ });
1105
+ it("pasted MIDIOutputDeviceBox.device points to pasted MIDIOutputBox", () => {
1106
+ const sourceAU = createAudioUnit(source);
1107
+ const sourceMidi = addMIDIOutputInstrument(source, sourceAU, "Source MIDI");
1108
+ connectMIDIOutput(source, sourceMidi, "output-1", "MIDI Port 1");
1109
+ const deps = collectDeviceDependencies(sourceMidi, source.boxGraph, sourceAU);
1110
+ const data = ClipboardUtils.serializeBoxes([sourceMidi, ...deps]);
1111
+ const targetAU = createAudioUnit(target);
1112
+ target.boxGraph.beginTransaction();
1113
+ const boxes = ClipboardUtils.deserializeBoxes(data, target.boxGraph, {
1114
+ mapPointer: (pointer, address) => {
1115
+ if (pointer.pointerType === Pointers.InstrumentHost) {
1116
+ return Option.wrap(targetAU.input.address);
1117
+ }
1118
+ if (pointer.pointerType === Pointers.MIDIDevice) {
1119
+ return Option.wrap(target.mandatoryBoxes.rootBox.outputMidiDevices.address);
1120
+ }
1121
+ if (pointer.pointerType === Pointers.TrackCollection) {
1122
+ return Option.wrap(targetAU.tracks.address);
1123
+ }
1124
+ if (pointer.pointerType === Pointers.Automation) {
1125
+ return Option.wrap(targetAU.address);
1126
+ }
1127
+ return Option.None;
1128
+ },
1129
+ excludeBox: () => false
1130
+ });
1131
+ target.boxGraph.endTransaction();
1132
+ const pastedDevice = boxes.find(box => isInstanceOf(box, MIDIOutputDeviceBox));
1133
+ const pastedMidiOutput = boxes.find(box => isInstanceOf(box, MIDIOutputBox));
1134
+ expect(pastedDevice).toBeDefined();
1135
+ expect(pastedMidiOutput).toBeDefined();
1136
+ expect(pastedDevice.device.targetVertex.unwrap().box).toBe(pastedMidiOutput);
1137
+ });
1138
+ });
961
1139
  describe("isInstanceOf type narrowing", () => {
962
1140
  it("narrows TrackBox type directly without intermediate null variable", () => {
963
1141
  const audioUnit = createAudioUnit(source);
@@ -1 +1 @@
1
- {"version":3,"file":"YSync.d.ts","sourceRoot":"","sources":["../../src/ysync/YSync.ts"],"names":[],"mappings":"AAAA,OAAO,EASH,QAAQ,EAER,UAAU,EAGb,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAa,QAAQ,EAA2D,MAAM,kBAAkB,CAAA;AAE/G,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACvB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAC/B,CAAA;AAED,qBAAa,KAAK,CAAC,CAAC,CAAE,YAAW,UAAU;;IACvC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO;WAItB,YAAY,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;WAcnE,QAAQ,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAyBhE,EAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IAQrD,SAAS,IAAI,IAAI;CAiLpB"}
1
+ {"version":3,"file":"YSync.d.ts","sourceRoot":"","sources":["../../src/ysync/YSync.ts"],"names":[],"mappings":"AAAA,OAAO,EASH,QAAQ,EAER,UAAU,EAGb,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAa,QAAQ,EAA2D,MAAM,kBAAkB,CAAA;AAE/G,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACvB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAC/B,CAAA;AAED,qBAAa,KAAK,CAAC,CAAC,CAAE,YAAW,UAAU;;IACvC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO;WAItB,YAAY,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;WAcnE,QAAQ,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAyBhE,EAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IAQrD,SAAS,IAAI,IAAI;CAkLpB"}
@@ -50,8 +50,8 @@ export class YSync {
50
50
  #setupYjs() {
51
51
  const eventHandler = (events, { origin, local }) => {
52
52
  const originLabel = typeof origin === "string" ? origin : "WebsocketProvider";
53
- console.debug(`got ${events.length} ${local ? "local" : "external"} updates from '${originLabel}'`);
54
53
  const isHistoryReplay = typeof origin === "string" && origin.startsWith("[history]");
54
+ console.debug(`got ${events.length} ${local ? "local" : "external"} updates from '${originLabel}, isHistoryReplay: ${isHistoryReplay}'`);
55
55
  if (local && !isHistoryReplay) {
56
56
  return;
57
57
  }
@@ -111,6 +111,7 @@ export class YSync {
111
111
  }
112
112
  }
113
113
  #updateValue(path, key) {
114
+ console.debug(`#updateValue`, path, key);
114
115
  const vertexOption = this.#boxGraph.findVertex(YMapper.pathToAddress(path, key));
115
116
  if (vertexOption.isEmpty()) {
116
117
  console.debug(`Vertex at '${path}' does not exist. Ignoring.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendaw/studio-core",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -33,17 +33,17 @@
33
33
  "test": "vitest run --config vitest.config.ts"
34
34
  },
35
35
  "dependencies": {
36
- "@opendaw/lib-box": "^0.0.79",
37
- "@opendaw/lib-dawproject": "^0.0.64",
38
- "@opendaw/lib-dom": "^0.0.78",
39
- "@opendaw/lib-dsp": "^0.0.78",
40
- "@opendaw/lib-fusion": "^0.0.86",
41
- "@opendaw/lib-runtime": "^0.0.74",
42
- "@opendaw/lib-std": "^0.0.73",
36
+ "@opendaw/lib-box": "^0.0.80",
37
+ "@opendaw/lib-dawproject": "^0.0.65",
38
+ "@opendaw/lib-dom": "^0.0.79",
39
+ "@opendaw/lib-dsp": "^0.0.79",
40
+ "@opendaw/lib-fusion": "^0.0.87",
41
+ "@opendaw/lib-runtime": "^0.0.75",
42
+ "@opendaw/lib-std": "^0.0.74",
43
43
  "@opendaw/nam-wasm": "^1.0.3",
44
- "@opendaw/studio-adapters": "^0.0.100",
45
- "@opendaw/studio-boxes": "^0.0.84",
46
- "@opendaw/studio-enums": "^0.0.69",
44
+ "@opendaw/studio-adapters": "^0.0.101",
45
+ "@opendaw/studio-boxes": "^0.0.85",
46
+ "@opendaw/studio-enums": "^0.0.70",
47
47
  "dropbox": "^10.34.0",
48
48
  "y-websocket": "^1.4.5",
49
49
  "yjs": "^13.6.27",
@@ -57,10 +57,10 @@
57
57
  "@ffmpeg/ffmpeg": "^0.12.15",
58
58
  "@ffmpeg/util": "^0.12.2",
59
59
  "@opendaw/eslint-config": "^0.0.27",
60
- "@opendaw/studio-core-processors": "^0.0.104",
61
- "@opendaw/studio-core-workers": "^0.0.95",
62
- "@opendaw/studio-forge-boxes": "^0.0.84",
60
+ "@opendaw/studio-core-processors": "^0.0.105",
61
+ "@opendaw/studio-core-workers": "^0.0.96",
62
+ "@opendaw/studio-forge-boxes": "^0.0.85",
63
63
  "@opendaw/typescript-config": "^0.0.29"
64
64
  },
65
- "gitHead": "0de255fcdacefee9ab663ceeb0d4424a278c5dc4"
65
+ "gitHead": "ee79696d07f80c597f2a9dcbb2e2a4b61a50c7df"
66
66
  }