@opendaw/studio-core 0.0.100 → 0.0.102

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.
Files changed (114) hide show
  1. package/dist/AssetService.js +2 -2
  2. package/dist/EffectFactories.d.ts +2 -0
  3. package/dist/EffectFactories.d.ts.map +1 -1
  4. package/dist/EffectFactories.js +44 -24
  5. package/dist/Engine.d.ts +5 -0
  6. package/dist/Engine.d.ts.map +1 -1
  7. package/dist/EngineFacade.d.ts +5 -0
  8. package/dist/EngineFacade.d.ts.map +1 -1
  9. package/dist/EngineFacade.js +23 -2
  10. package/dist/EngineWorklet.d.ts +5 -0
  11. package/dist/EngineWorklet.d.ts.map +1 -1
  12. package/dist/EngineWorklet.js +107 -11
  13. package/dist/HRClockWorker.d.ts +7 -0
  14. package/dist/HRClockWorker.d.ts.map +1 -0
  15. package/dist/HRClockWorker.js +54 -0
  16. package/dist/OfflineEngineRenderer.d.ts.map +1 -1
  17. package/dist/OfflineEngineRenderer.js +6 -2
  18. package/dist/RecordingWorklet.d.ts +2 -1
  19. package/dist/RecordingWorklet.d.ts.map +1 -1
  20. package/dist/RecordingWorklet.js +9 -1
  21. package/dist/Storage.d.ts.map +1 -1
  22. package/dist/Storage.js +1 -0
  23. package/dist/StudioPreferences.d.ts +9 -0
  24. package/dist/StudioPreferences.d.ts.map +1 -1
  25. package/dist/StudioSettings.d.ts +14 -0
  26. package/dist/StudioSettings.d.ts.map +1 -1
  27. package/dist/StudioSettings.js +19 -2
  28. package/dist/capture/CaptureAudio.d.ts +3 -1
  29. package/dist/capture/CaptureAudio.d.ts.map +1 -1
  30. package/dist/capture/CaptureAudio.js +51 -29
  31. package/dist/capture/MonitoringMode.d.ts +2 -0
  32. package/dist/capture/MonitoringMode.d.ts.map +1 -0
  33. package/dist/capture/MonitoringMode.js +1 -0
  34. package/dist/capture/RecordAudio.d.ts.map +1 -1
  35. package/dist/capture/RecordAudio.js +1 -0
  36. package/dist/capture/index.d.ts +1 -0
  37. package/dist/capture/index.d.ts.map +1 -1
  38. package/dist/capture/index.js +1 -0
  39. package/dist/dawproject/DawProjectImporter.d.ts.map +1 -1
  40. package/dist/dawproject/DawProjectImporter.js +4 -0
  41. package/dist/processors.js +24 -24
  42. package/dist/processors.js.map +4 -4
  43. package/dist/project/Project.d.ts +7 -1
  44. package/dist/project/Project.d.ts.map +1 -1
  45. package/dist/project/Project.js +58 -8
  46. package/dist/project/ProjectApi.d.ts +1 -1
  47. package/dist/project/ProjectApi.d.ts.map +1 -1
  48. package/dist/project/ProjectApi.js +17 -3
  49. package/dist/project/ProjectMigration.d.ts.map +1 -1
  50. package/dist/project/ProjectMigration.js +3 -2
  51. package/dist/project/audio/AudioContentFactory.d.ts +5 -0
  52. package/dist/project/audio/AudioContentFactory.d.ts.map +1 -1
  53. package/dist/project/audio/AudioContentFactory.js +13 -0
  54. package/dist/project/migration/MigrateNeuralAmpDeviceBox.d.ts +4 -0
  55. package/dist/project/migration/MigrateNeuralAmpDeviceBox.d.ts.map +1 -0
  56. package/dist/project/migration/MigrateNeuralAmpDeviceBox.js +29 -0
  57. package/dist/project/migration/index.d.ts +1 -0
  58. package/dist/project/migration/index.d.ts.map +1 -1
  59. package/dist/project/migration/index.js +1 -0
  60. package/dist/ui/{generic → clipboard}/ClipboardManager.d.ts +3 -3
  61. package/dist/ui/clipboard/ClipboardManager.d.ts.map +1 -0
  62. package/dist/ui/clipboard/ClipboardManager.js +147 -0
  63. package/dist/ui/clipboard/ClipboardUtils.d.ts +12 -0
  64. package/dist/ui/clipboard/ClipboardUtils.d.ts.map +1 -0
  65. package/dist/ui/clipboard/ClipboardUtils.js +94 -0
  66. package/dist/ui/{generic → clipboard}/ContextMenu.d.ts +1 -1
  67. package/dist/ui/clipboard/ContextMenu.d.ts.map +1 -0
  68. package/dist/ui/{generic → clipboard}/ContextMenu.js +1 -1
  69. package/dist/ui/clipboard/types/AudioUnitsClipboardHandler.d.ts +18 -0
  70. package/dist/ui/clipboard/types/AudioUnitsClipboardHandler.d.ts.map +1 -0
  71. package/dist/ui/clipboard/types/AudioUnitsClipboardHandler.js +215 -0
  72. package/dist/ui/clipboard/types/DevicesClipboardHandler.d.ts +18 -0
  73. package/dist/ui/clipboard/types/DevicesClipboardHandler.d.ts.map +1 -0
  74. package/dist/ui/clipboard/types/DevicesClipboardHandler.js +188 -0
  75. package/dist/ui/clipboard/types/NotesClipboardHandler.d.ts +21 -0
  76. package/dist/ui/clipboard/types/NotesClipboardHandler.d.ts.map +1 -0
  77. package/dist/ui/clipboard/types/NotesClipboardHandler.js +72 -0
  78. package/dist/ui/clipboard/types/RegionsClipboardHandler.d.ts +24 -0
  79. package/dist/ui/clipboard/types/RegionsClipboardHandler.d.ts.map +1 -0
  80. package/dist/ui/clipboard/types/RegionsClipboardHandler.js +137 -0
  81. package/dist/ui/clipboard/types/ValuesClipboardHandler.d.ts +22 -0
  82. package/dist/ui/clipboard/types/ValuesClipboardHandler.d.ts.map +1 -0
  83. package/dist/ui/clipboard/types/ValuesClipboardHandler.js +135 -0
  84. package/dist/ui/index.d.ts +14 -3
  85. package/dist/ui/index.d.ts.map +1 -1
  86. package/dist/ui/index.js +14 -3
  87. package/dist/ui/menu/MenuItems.d.ts.map +1 -0
  88. package/dist/ui/timeline/RegionClipResolver.d.ts +5 -1
  89. package/dist/ui/timeline/RegionClipResolver.d.ts.map +1 -1
  90. package/dist/ui/timeline/RegionClipResolver.js +5 -1
  91. package/dist/ui/timeline/RegionKeepExistingResolver.d.ts +34 -0
  92. package/dist/ui/timeline/RegionKeepExistingResolver.d.ts.map +1 -0
  93. package/dist/ui/timeline/RegionKeepExistingResolver.js +171 -0
  94. package/dist/ui/timeline/RegionOverlapResolver.d.ts +33 -0
  95. package/dist/ui/timeline/RegionOverlapResolver.d.ts.map +1 -0
  96. package/dist/ui/timeline/RegionOverlapResolver.js +79 -0
  97. package/dist/ui/timeline/RegionPushExistingResolver.d.ts +31 -0
  98. package/dist/ui/timeline/RegionPushExistingResolver.d.ts.map +1 -0
  99. package/dist/ui/timeline/RegionPushExistingResolver.js +159 -0
  100. package/dist/ui/timeline/TimelineFocus.d.ts +14 -0
  101. package/dist/ui/timeline/TimelineFocus.d.ts.map +1 -0
  102. package/dist/ui/timeline/TimelineFocus.js +45 -0
  103. package/dist/ui/timeline/TrackResolver.d.ts +11 -0
  104. package/dist/ui/timeline/TrackResolver.d.ts.map +1 -0
  105. package/dist/ui/timeline/TrackResolver.js +5 -0
  106. package/dist/workers-main.js +1 -1
  107. package/dist/workers-main.js.map +3 -3
  108. package/package.json +62 -62
  109. package/dist/ui/generic/ClipboardManager.d.ts.map +0 -1
  110. package/dist/ui/generic/ClipboardManager.js +0 -77
  111. package/dist/ui/generic/ContextMenu.d.ts.map +0 -1
  112. package/dist/ui/generic/MenuItems.d.ts.map +0 -1
  113. /package/dist/ui/{generic → menu}/MenuItems.d.ts +0 -0
  114. /package/dist/ui/{generic → menu}/MenuItems.js +0 -0
@@ -0,0 +1,94 @@
1
+ import { ByteArrayInput, ByteArrayOutput, Option, UUID } from "@opendaw/lib-std";
2
+ import { BoxGraph, PointerField } from "@opendaw/lib-box";
3
+ import { BoxIO } from "@opendaw/studio-boxes";
4
+ export var ClipboardUtils;
5
+ (function (ClipboardUtils) {
6
+ ClipboardUtils.extractMetadata = (data) => {
7
+ const input = new ByteArrayInput(data);
8
+ const metadataLength = input.readInt();
9
+ const metadataBytes = new Int8Array(metadataLength);
10
+ input.readBytes(metadataBytes);
11
+ return metadataBytes.buffer;
12
+ };
13
+ ClipboardUtils.serializeBoxes = (boxes, metadata = new ArrayBuffer(0)) => {
14
+ const uniqueBoxes = UUID.newSet(box => box.address.uuid);
15
+ boxes.forEach(box => uniqueBoxes.add(box));
16
+ const typeCounts = new Map();
17
+ uniqueBoxes.forEach(box => typeCounts.set(box.name, (typeCounts.get(box.name) ?? 0) + 1));
18
+ const types = [...typeCounts.entries()].map(([type, count]) => `${type}: ${count}`).join(", ");
19
+ const clipboardGraph = new BoxGraph(Option.wrap(BoxIO.create));
20
+ clipboardGraph.beginTransaction();
21
+ uniqueBoxes.forEach(sourceBox => {
22
+ const input = new ByteArrayInput(sourceBox.toArrayBuffer());
23
+ clipboardGraph.createBox(sourceBox.name, sourceBox.address.uuid, box => box.read(input));
24
+ });
25
+ clipboardGraph.endTransaction();
26
+ const graphData = clipboardGraph.toArrayBuffer();
27
+ const output = ByteArrayOutput.create();
28
+ output.writeInt(metadata.byteLength);
29
+ output.writeBytes(new Int8Array(metadata));
30
+ output.writeInt(graphData.byteLength);
31
+ output.writeBytes(new Int8Array(graphData));
32
+ const arrayBuffer = output.toArrayBuffer();
33
+ console.debug(`Clipboard copy (${arrayBuffer.byteLength >> 10}kB): ${types}`);
34
+ return arrayBuffer;
35
+ };
36
+ ClipboardUtils.deserializeBoxes = (data, targetGraph, options) => {
37
+ const input = new ByteArrayInput(data);
38
+ input.skip(input.readInt());
39
+ const graphDataLength = input.readInt();
40
+ const graphData = new Int8Array(graphDataLength);
41
+ input.readBytes(graphData);
42
+ const clipboardGraph = new BoxGraph(Option.wrap(BoxIO.create));
43
+ clipboardGraph.fromArrayBuffer(graphData.buffer);
44
+ const skippedExternalUuids = UUID.newSet(uuid => uuid);
45
+ clipboardGraph.boxes().forEach(box => {
46
+ if (box.resource === "preserved" && targetGraph.findBox(box.address.uuid).nonEmpty()) {
47
+ skippedExternalUuids.add(box.address.uuid);
48
+ }
49
+ });
50
+ const shouldSkipBox = (box) => {
51
+ if (options.excludeBox?.(box)) {
52
+ return true;
53
+ }
54
+ if (box.resource === "preserved" && skippedExternalUuids.hasKey(box.address.uuid)) {
55
+ return true;
56
+ }
57
+ for (const [, targetAddress] of box.outgoingEdges()) {
58
+ if (skippedExternalUuids.hasKey(targetAddress.uuid) && !targetAddress.isBox()) {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ };
64
+ const sourceBoxes = clipboardGraph.boxes().filter(box => !shouldSkipBox(box));
65
+ const typeCounts = new Map();
66
+ sourceBoxes.forEach(box => typeCounts.set(box.name, (typeCounts.get(box.name) ?? 0) + 1));
67
+ console.debug("Clipboard paste:", [...typeCounts.entries()].map(([type, count]) => `${type}: ${count}`).join(", "));
68
+ const uuidMap = UUID.newSet(({ source }) => source);
69
+ sourceBoxes.forEach(box => {
70
+ const isExternal = box.resource === "preserved";
71
+ uuidMap.add({ source: box.address.uuid, target: isExternal ? box.address.uuid : UUID.generate() });
72
+ });
73
+ skippedExternalUuids.forEach(uuid => uuidMap.add({ source: uuid, target: uuid }));
74
+ const result = [];
75
+ PointerField.decodeWith({
76
+ map: (pointer, address) => {
77
+ const remappedInternal = address.flatMap(addr => uuidMap.opt(addr.uuid).map(({ target }) => addr.moveTo(target)));
78
+ if (remappedInternal.nonEmpty()) {
79
+ return remappedInternal;
80
+ }
81
+ return options.mapPointer(pointer, address);
82
+ }
83
+ }, () => sourceBoxes.forEach(sourceBox => {
84
+ const inputStream = new ByteArrayInput(sourceBox.toArrayBuffer());
85
+ const targetUuid = uuidMap.get(sourceBox.address.uuid).target;
86
+ targetGraph.createBox(sourceBox.name, targetUuid, box => {
87
+ box.read(inputStream);
88
+ options.modifyBox?.(box);
89
+ result.push(box);
90
+ });
91
+ }));
92
+ return result;
93
+ };
94
+ })(ClipboardUtils || (ClipboardUtils = {}));
@@ -1,5 +1,5 @@
1
1
  import { Client, Subscription } from "@opendaw/lib-std";
2
- import { MenuItem } from "./MenuItems";
2
+ import { MenuItem } from "../menu/MenuItems";
3
3
  export declare namespace ContextMenu {
4
4
  const CONTEXT_MENU_EVENT_TYPE: "--context-menu";
5
5
  type MenuFactory = (menuItem: MenuItem, client: Client) => void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextMenu.d.ts","sourceRoot":"","sources":["../../../src/ui/clipboard/ContextMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAU,YAAY,EAAC,MAAM,kBAAkB,CAAA;AAE7D,OAAO,EAAC,QAAQ,EAAe,MAAM,mBAAmB,CAAA;AAExD,yBAAiB,WAAW,CAAC;IAClB,MAAM,uBAAuB,EAAG,gBAAyB,CAAA;IAEhE,KAAY,WAAW,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAEtE,UAAiB,SAAS;QACtB,QAAQ,CAAC,GAAG,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QAEpC,IAAI,MAAM,IAAI,MAAM,CAAA;KACvB;IAqCM,MAAM,OAAO,GAAI,OAAO,WAAW,EAAE,aAAa,WAAW,KAAG,YAkBhD,CAAA;IAEhB,MAAM,SAAS,GAAI,QAAQ,WAAW,EAAE,SAAS,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,KAAG,YAI/D,CAAA;CAChC"}
@@ -1,6 +1,6 @@
1
1
  import { Option } from "@opendaw/lib-std";
2
2
  import { Events } from "@opendaw/lib-dom";
3
- import { MenuItem } from "./MenuItems";
3
+ import { MenuItem } from "../menu/MenuItems";
4
4
  export var ContextMenu;
5
5
  (function (ContextMenu) {
6
6
  ContextMenu.CONTEXT_MENU_EVENT_TYPE = "--context-menu";
@@ -0,0 +1,18 @@
1
+ import { Option, Provider } from "@opendaw/lib-std";
2
+ import { BoxEditing, BoxGraph } from "@opendaw/lib-box";
3
+ import { AudioUnitBoxAdapter, RootBoxAdapter, UserEditing } from "@opendaw/studio-adapters";
4
+ import { ClipboardEntry, ClipboardHandler } from "../ClipboardManager";
5
+ type ClipboardAudioUnits = ClipboardEntry<"audio-units">;
6
+ export declare namespace AudioUnitsClipboard {
7
+ type Context = {
8
+ readonly getEnabled: Provider<boolean>;
9
+ readonly editing: BoxEditing;
10
+ readonly boxGraph: BoxGraph;
11
+ readonly rootBoxAdapter: RootBoxAdapter;
12
+ readonly audioUnitEditing: UserEditing;
13
+ readonly getEditedAudioUnit: Provider<Option<AudioUnitBoxAdapter>>;
14
+ };
15
+ const createHandler: ({ getEnabled, editing, boxGraph, rootBoxAdapter, audioUnitEditing, getEditedAudioUnit }: Context) => ClipboardHandler<ClipboardAudioUnits>;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=AudioUnitsClipboardHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioUnitsClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/AudioUnitsClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,MAAM,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAClF,OAAO,EAAM,UAAU,EAAE,QAAQ,EAAa,MAAM,kBAAkB,CAAA;AAWtE,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,UAAU,CAAA;QAC5B,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"}
@@ -0,0 +1,215 @@
1
+ import { ByteArrayInput, ByteArrayOutput, Option } from "@opendaw/lib-std";
2
+ import { IndexedBox } from "@opendaw/lib-box";
3
+ import { AudioUnitType, Pointers } from "@opendaw/studio-enums";
4
+ import { AudioBusBox, AudioUnitBox, AuxSendBox, CaptureAudioBox, CaptureMidiBox, MIDIControllerBox, RootBox } from "@opendaw/studio-boxes";
5
+ import { AudioUnitOrdering } from "@opendaw/studio-adapters";
6
+ import { ClipboardUtils } from "../ClipboardUtils";
7
+ export var AudioUnitsClipboard;
8
+ (function (AudioUnitsClipboard) {
9
+ const encodeMetadata = (metadata) => {
10
+ const output = ByteArrayOutput.create();
11
+ output.writeString(metadata.type);
12
+ return output.toArrayBuffer();
13
+ };
14
+ const decodeMetadata = (buffer) => {
15
+ const input = new ByteArrayInput(buffer);
16
+ return { type: input.readString() };
17
+ };
18
+ AudioUnitsClipboard.createHandler = ({ getEnabled, editing, boxGraph, rootBoxAdapter, audioUnitEditing, getEditedAudioUnit }) => {
19
+ const copyAudioUnit = () => {
20
+ const optAudioUnit = getEditedAudioUnit();
21
+ if (optAudioUnit.isEmpty()) {
22
+ return Option.None;
23
+ }
24
+ const audioUnitAdapter = optAudioUnit.unwrap();
25
+ const audioUnitBox = audioUnitAdapter.box;
26
+ const isOutput = audioUnitAdapter.type === AudioUnitType.Output;
27
+ const dependencies = Array.from(audioUnitBox.graph.dependenciesOf(audioUnitBox, {
28
+ alwaysFollowMandatory: true,
29
+ stopAtResources: true,
30
+ excludeBox: (box) => {
31
+ if (box.ephemeral) {
32
+ return true;
33
+ }
34
+ if (box.name === RootBox.ClassName) {
35
+ return true;
36
+ }
37
+ if (box.name === AudioBusBox.ClassName) {
38
+ return true;
39
+ }
40
+ if (box.name === AuxSendBox.ClassName) {
41
+ return true;
42
+ }
43
+ if (box.name === MIDIControllerBox.ClassName) {
44
+ return true;
45
+ }
46
+ if (isOutput && box.name === CaptureAudioBox.ClassName) {
47
+ return true;
48
+ }
49
+ if (isOutput && box.name === CaptureMidiBox.ClassName) {
50
+ return true;
51
+ }
52
+ return false;
53
+ }
54
+ }).boxes);
55
+ const metadata = { type: audioUnitAdapter.type };
56
+ const allBoxes = [audioUnitBox, ...dependencies];
57
+ const data = ClipboardUtils.serializeBoxes(allBoxes, encodeMetadata(metadata));
58
+ return Option.wrap({ type: "audio-units", data });
59
+ };
60
+ return {
61
+ canCopy: () => getEnabled() && getEditedAudioUnit().nonEmpty(),
62
+ canCut: () => {
63
+ if (!getEnabled()) {
64
+ return false;
65
+ }
66
+ const optAudioUnit = getEditedAudioUnit();
67
+ if (optAudioUnit.isEmpty()) {
68
+ return false;
69
+ }
70
+ return !optAudioUnit.unwrap().isOutput;
71
+ },
72
+ canPaste: (entry) => getEnabled() && entry.type === "audio-units",
73
+ copy: copyAudioUnit,
74
+ cut: () => {
75
+ const optAudioUnit = getEditedAudioUnit();
76
+ if (optAudioUnit.isEmpty()) {
77
+ return Option.None;
78
+ }
79
+ const audioUnit = optAudioUnit.unwrap();
80
+ if (audioUnit.isOutput) {
81
+ return Option.None;
82
+ }
83
+ const result = copyAudioUnit();
84
+ result.ifSome(() => {
85
+ editing.modify(() => {
86
+ audioUnit.box.delete();
87
+ rootBoxAdapter.audioUnits.adapters()
88
+ .forEach((adapter, index) => adapter.indexField.setValue(index));
89
+ });
90
+ });
91
+ return result;
92
+ },
93
+ paste: (entry) => {
94
+ if (entry.type !== "audio-units" || !getEnabled()) {
95
+ return;
96
+ }
97
+ const metadata = decodeMetadata(ClipboardUtils.extractMetadata(entry.data));
98
+ const isOutputPaste = metadata.type === AudioUnitType.Output;
99
+ if (isOutputPaste) {
100
+ // Split into two transactions to ensure deletion notifications fire
101
+ // before new boxes are created (avoids "already has input" conflict)
102
+ editing.modify(() => clearOutputContent(rootBoxAdapter));
103
+ editing.modify(() => pasteOutputContent(entry.data, boxGraph, rootBoxAdapter));
104
+ }
105
+ else {
106
+ editing.modify(() => {
107
+ const pastedBox = pasteNewAudioUnit(entry.data, boxGraph, rootBoxAdapter, getEditedAudioUnit());
108
+ if (pastedBox) {
109
+ audioUnitEditing.edit(pastedBox.editing);
110
+ }
111
+ });
112
+ }
113
+ }
114
+ };
115
+ };
116
+ const clearOutputContent = (rootBoxAdapter) => {
117
+ const outputAdapter = rootBoxAdapter.audioUnits.adapters().find(adapter => adapter.isOutput);
118
+ if (!outputAdapter) {
119
+ return;
120
+ }
121
+ outputAdapter.tracks.collection.adapters().forEach(track => track.box.delete());
122
+ const inputAdapter = outputAdapter.input.adapter();
123
+ if (inputAdapter.nonEmpty() && inputAdapter.unwrap().type === "instrument") {
124
+ inputAdapter.unwrap().box.delete();
125
+ }
126
+ outputAdapter.midiEffects.adapters().forEach(effect => effect.box.delete());
127
+ outputAdapter.audioEffects.adapters().forEach(effect => effect.box.delete());
128
+ };
129
+ const pasteOutputContent = (data, boxGraph, rootBoxAdapter) => {
130
+ const outputAdapter = rootBoxAdapter.audioUnits.adapters().find(adapter => adapter.isOutput);
131
+ if (!outputAdapter) {
132
+ return;
133
+ }
134
+ const outputBox = outputAdapter.box;
135
+ const primaryBusAddress = rootBoxAdapter.audioBusses.adapters().at(0)?.address;
136
+ if (!primaryBusAddress) {
137
+ return;
138
+ }
139
+ ClipboardUtils.deserializeBoxes(data, boxGraph, {
140
+ mapPointer: (pointer, address) => {
141
+ if (pointer.pointerType === Pointers.TrackCollection) {
142
+ return Option.wrap(outputBox.tracks.address);
143
+ }
144
+ if (pointer.pointerType === Pointers.InstrumentHost) {
145
+ return Option.wrap(outputBox.input.address);
146
+ }
147
+ if (pointer.pointerType === Pointers.MIDIEffectHost) {
148
+ return Option.wrap(outputBox.midiEffects.address);
149
+ }
150
+ if (pointer.pointerType === Pointers.AudioEffectHost) {
151
+ return Option.wrap(outputBox.audioEffects.address);
152
+ }
153
+ if (pointer.pointerType === Pointers.AudioOutput) {
154
+ return address.map(addr => addr.moveTo(primaryBusAddress.uuid));
155
+ }
156
+ return Option.None;
157
+ },
158
+ excludeBox: box => box.name === AudioUnitBox.ClassName || box.name === AudioBusBox.ClassName || box.name === RootBox.ClassName
159
+ });
160
+ };
161
+ const pasteNewAudioUnit = (data, boxGraph, rootBoxAdapter, currentAudioUnit) => {
162
+ const rootBox = rootBoxAdapter.box;
163
+ const primaryBusAddress = rootBoxAdapter.audioBusses.adapters().at(0)?.address;
164
+ if (!primaryBusAddress) {
165
+ return undefined;
166
+ }
167
+ const boxes = ClipboardUtils.deserializeBoxes(data, boxGraph, {
168
+ mapPointer: (pointer, address) => {
169
+ if (pointer.pointerType === Pointers.AudioUnits) {
170
+ return Option.wrap(rootBox.audioUnits.address);
171
+ }
172
+ if (pointer.pointerType === Pointers.AudioOutput) {
173
+ return address.map(addr => addr.moveTo(primaryBusAddress.uuid));
174
+ }
175
+ return Option.None;
176
+ }
177
+ });
178
+ const pastedAudioUnit = boxes.find(box => box.name === AudioUnitBox.ClassName);
179
+ if (!pastedAudioUnit) {
180
+ return undefined;
181
+ }
182
+ const insertAfterIndex = currentAudioUnit
183
+ .map(adapter => adapter.indexField.getValue())
184
+ .unwrapOrElse(() => -1);
185
+ reorderAudioUnitsAfterPaste(pastedAudioUnit, insertAfterIndex, rootBoxAdapter);
186
+ return pastedAudioUnit;
187
+ };
188
+ const reorderAudioUnitsAfterPaste = (pastedAudioUnit, insertAfterIndex, rootBoxAdapter) => {
189
+ const rootBox = rootBoxAdapter.box;
190
+ const allAudioUnits = IndexedBox.collectIndexedBoxes(rootBox.audioUnits, AudioUnitBox);
191
+ allAudioUnits.toSorted((a, b) => {
192
+ const orderA = AudioUnitOrdering[a.type.getValue()];
193
+ const orderB = AudioUnitOrdering[b.type.getValue()];
194
+ const orderDiff = orderA - orderB;
195
+ if (orderDiff !== 0) {
196
+ return orderDiff;
197
+ }
198
+ const aIsPasted = a === pastedAudioUnit;
199
+ const bIsPasted = b === pastedAudioUnit;
200
+ if (aIsPasted && !bIsPasted) {
201
+ if (insertAfterIndex === -1) {
202
+ return -1;
203
+ }
204
+ return b.index.getValue() <= insertAfterIndex ? 1 : -1;
205
+ }
206
+ if (bIsPasted && !aIsPasted) {
207
+ if (insertAfterIndex === -1) {
208
+ return 1;
209
+ }
210
+ return a.index.getValue() <= insertAfterIndex ? -1 : 1;
211
+ }
212
+ return a.index.getValue() - b.index.getValue();
213
+ }).forEach((box, index) => box.index.setValue(index));
214
+ };
215
+ })(AudioUnitsClipboard || (AudioUnitsClipboard = {}));
@@ -0,0 +1,18 @@
1
+ import { Option, Provider } from "@opendaw/lib-std";
2
+ import { BoxEditing, BoxGraph } from "@opendaw/lib-box";
3
+ import { BoxAdapters, DeviceBoxAdapter, DeviceHost, FilteredSelection } from "@opendaw/studio-adapters";
4
+ import { ClipboardEntry, ClipboardHandler } from "../ClipboardManager";
5
+ type ClipboardDevices = ClipboardEntry<"devices">;
6
+ export declare namespace DevicesClipboard {
7
+ type Context = {
8
+ readonly getEnabled: Provider<boolean>;
9
+ readonly editing: BoxEditing;
10
+ readonly selection: FilteredSelection<DeviceBoxAdapter>;
11
+ readonly boxGraph: BoxGraph;
12
+ readonly boxAdapters: BoxAdapters;
13
+ readonly getHost: Provider<Option<DeviceHost>>;
14
+ };
15
+ const createHandler: ({ getEnabled, editing, selection, boxGraph, boxAdapters, getHost }: Context) => ClipboardHandler<ClipboardDevices>;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=DevicesClipboardHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevicesClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/DevicesClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkD,MAAM,EAAY,QAAQ,EAAkB,MAAM,kBAAkB,CAAA;AAC7H,OAAO,EAAM,UAAU,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAE1D,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,UAAU,CAAA;QAC5B,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,CA0J3E,CAAA;CACJ"}
@@ -0,0 +1,188 @@
1
+ import { ByteArrayInput, ByteArrayOutput, isDefined, Option, RuntimeNotifier } from "@opendaw/lib-std";
2
+ import { Pointers } from "@opendaw/studio-enums";
3
+ import { DeviceBoxUtils, Devices } from "@opendaw/studio-adapters";
4
+ import { ClipboardUtils } from "../ClipboardUtils";
5
+ export var DevicesClipboard;
6
+ (function (DevicesClipboard) {
7
+ const encodeMetadata = (metadata) => {
8
+ const output = ByteArrayOutput.create();
9
+ output.writeBoolean(metadata.hasInstrument);
10
+ output.writeString(metadata.instrumentContent);
11
+ output.writeInt(metadata.midiEffectCount);
12
+ output.writeInt(metadata.midiEffectMaxIndex);
13
+ output.writeInt(metadata.audioEffectCount);
14
+ output.writeInt(metadata.audioEffectMaxIndex);
15
+ return output.toArrayBuffer();
16
+ };
17
+ const decodeMetadata = (buffer) => {
18
+ const input = new ByteArrayInput(buffer);
19
+ return {
20
+ hasInstrument: input.readBoolean(),
21
+ instrumentContent: input.readString(),
22
+ midiEffectCount: input.readInt(),
23
+ midiEffectMaxIndex: input.readInt(),
24
+ audioEffectCount: input.readInt(),
25
+ audioEffectMaxIndex: input.readInt()
26
+ };
27
+ };
28
+ DevicesClipboard.createHandler = ({ getEnabled, editing, selection, boxGraph, boxAdapters, getHost }) => {
29
+ const isCopyable = (adapter) => adapter.box.tags.copyable !== false;
30
+ const copyableSelected = () => selection.selected().filter(isCopyable);
31
+ const copyDevices = () => {
32
+ const selected = copyableSelected();
33
+ if (selected.length === 0) {
34
+ return Option.None;
35
+ }
36
+ let instrument = null;
37
+ const midiEffects = [];
38
+ const audioEffects = [];
39
+ for (const adapter of selected) {
40
+ if (adapter.type === "instrument") {
41
+ instrument = adapter;
42
+ }
43
+ else if (adapter.type === "midi-effect") {
44
+ midiEffects.push(adapter);
45
+ }
46
+ else if (adapter.type === "audio-effect") {
47
+ audioEffects.push(adapter);
48
+ }
49
+ }
50
+ if (instrument === null && midiEffects.length === 0 && audioEffects.length === 0) {
51
+ return Option.None;
52
+ }
53
+ midiEffects.sort((a, b) => a.indexField.getValue() - b.indexField.getValue());
54
+ audioEffects.sort((a, b) => a.indexField.getValue() - b.indexField.getValue());
55
+ const midiEffectMaxIndex = midiEffects.length > 0
56
+ ? midiEffects[midiEffects.length - 1].indexField.getValue()
57
+ : 0;
58
+ const audioEffectMaxIndex = audioEffects.length > 0
59
+ ? audioEffects[audioEffects.length - 1].indexField.getValue()
60
+ : 0;
61
+ const deviceBoxes = [
62
+ ...(instrument !== null ? [instrument.box] : []),
63
+ ...midiEffects.map(adapter => adapter.box),
64
+ ...audioEffects.map(adapter => adapter.box)
65
+ ];
66
+ const dependencies = deviceBoxes.flatMap(box => Array.from(boxGraph.dependenciesOf(box, {
67
+ alwaysFollowMandatory: true,
68
+ excludeBox: (dep) => dep.ephemeral || DeviceBoxUtils.isDeviceBox(dep)
69
+ }).boxes).filter(dep => dep.resource === "preserved"));
70
+ const allBoxes = [...deviceBoxes, ...dependencies];
71
+ const instrumentContent = instrument !== null
72
+ ? instrument.box.tags.content ?? ""
73
+ : "";
74
+ const metadata = {
75
+ hasInstrument: instrument !== null,
76
+ instrumentContent,
77
+ midiEffectCount: midiEffects.length,
78
+ midiEffectMaxIndex,
79
+ audioEffectCount: audioEffects.length,
80
+ audioEffectMaxIndex
81
+ };
82
+ const data = ClipboardUtils.serializeBoxes(allBoxes, encodeMetadata(metadata));
83
+ return Option.wrap({ type: "devices", data });
84
+ };
85
+ return {
86
+ canCopy: () => getEnabled() && copyableSelected().length > 0,
87
+ canCut: () => getEnabled() && copyableSelected().length > 0,
88
+ canPaste: (entry) => getEnabled() && entry.type === "devices",
89
+ copy: copyDevices,
90
+ cut: () => {
91
+ const result = copyDevices();
92
+ result.ifSome(() => {
93
+ const optHost = getHost();
94
+ if (optHost.isEmpty()) {
95
+ return;
96
+ }
97
+ const host = optHost.unwrap();
98
+ const selected = new Set(selection.selected().filter(adapter => adapter.type !== "instrument"));
99
+ const remainingMidi = host.midiEffects.adapters().filter(adapter => !selected.has(adapter));
100
+ const remainingAudio = host.audioEffects.adapters().filter(adapter => !selected.has(adapter));
101
+ editing.modify(() => {
102
+ selected.forEach(adapter => adapter.box.delete());
103
+ remainingMidi.forEach((adapter, index) => adapter.indexField.setValue(index));
104
+ remainingAudio.forEach((adapter, index) => adapter.indexField.setValue(index));
105
+ });
106
+ });
107
+ return result;
108
+ },
109
+ paste: (entry) => {
110
+ if (entry.type !== "devices" || !getEnabled()) {
111
+ return;
112
+ }
113
+ const optHost = getHost();
114
+ if (optHost.isEmpty()) {
115
+ return;
116
+ }
117
+ const host = optHost.unwrap();
118
+ const metadata = decodeMetadata(ClipboardUtils.extractMetadata(entry.data));
119
+ const selected = selection.selected();
120
+ const selectedInstrument = selected.find(adapter => adapter.type === "instrument");
121
+ const selectedMidiEffects = selected.filter(adapter => adapter.type === "midi-effect");
122
+ const selectedAudioEffects = selected.filter(adapter => adapter.type === "audio-effect");
123
+ let replaceInstrument = metadata.hasInstrument && isDefined(selectedInstrument)
124
+ && selectedInstrument.box.tags.copyable !== false;
125
+ if (replaceInstrument && isDefined(selectedInstrument)) {
126
+ const selectedContent = selectedInstrument.box.tags.content;
127
+ if (isDefined(selectedContent) && metadata.instrumentContent !== ""
128
+ && selectedContent !== metadata.instrumentContent) {
129
+ RuntimeNotifier.info({
130
+ headline: "Incompatible Instrument",
131
+ message: `Cannot replace a '${selectedContent}' instrument with a '${metadata.instrumentContent}' instrument.`
132
+ }).finally();
133
+ replaceInstrument = false;
134
+ }
135
+ }
136
+ const midiInsertIndex = selectedMidiEffects.length > 0
137
+ ? selectedMidiEffects.reduce((max, adapter) => Math.max(max, adapter.indexField.getValue()), -1) + 1
138
+ : 0;
139
+ const audioInsertIndex = selectedAudioEffects.length > 0
140
+ ? selectedAudioEffects.reduce((max, adapter) => Math.max(max, adapter.indexField.getValue()), -1) + 1
141
+ : 0;
142
+ editing.modify(() => {
143
+ selection.deselectAll();
144
+ if (replaceInstrument && isDefined(selectedInstrument)) {
145
+ selectedInstrument.box.delete();
146
+ }
147
+ for (const adapter of host.midiEffects.adapters()) {
148
+ const currentIndex = adapter.indexField.getValue();
149
+ if (currentIndex >= midiInsertIndex) {
150
+ adapter.indexField.setValue(currentIndex + metadata.midiEffectCount);
151
+ }
152
+ }
153
+ for (const adapter of host.audioEffects.adapters()) {
154
+ const currentIndex = adapter.indexField.getValue();
155
+ if (currentIndex >= audioInsertIndex) {
156
+ adapter.indexField.setValue(currentIndex + metadata.audioEffectCount);
157
+ }
158
+ }
159
+ const boxes = ClipboardUtils.deserializeBoxes(entry.data, boxGraph, {
160
+ mapPointer: pointer => {
161
+ if (pointer.pointerType === Pointers.InstrumentHost && replaceInstrument) {
162
+ return Option.wrap(host.inputField.address);
163
+ }
164
+ if (pointer.pointerType === Pointers.MIDIEffectHost) {
165
+ return Option.wrap(host.midiEffectsField.address);
166
+ }
167
+ if (pointer.pointerType === Pointers.AudioEffectHost) {
168
+ return Option.wrap(host.audioEffectsField.address);
169
+ }
170
+ return Option.None;
171
+ },
172
+ excludeBox: box => DeviceBoxUtils.isInstrumentDeviceBox(box) && !replaceInstrument
173
+ });
174
+ const deviceBoxes = boxes.filter(box => DeviceBoxUtils.isDeviceBox(box));
175
+ const newMidiEffects = deviceBoxes
176
+ .filter((box) => DeviceBoxUtils.isEffectDeviceBox(box) && box.tags.deviceType === "midi-effect")
177
+ .sort((a, b) => a.index.getValue() - b.index.getValue());
178
+ const newAudioEffects = deviceBoxes
179
+ .filter((box) => DeviceBoxUtils.isEffectDeviceBox(box) && box.tags.deviceType === "audio-effect")
180
+ .sort((a, b) => a.index.getValue() - b.index.getValue());
181
+ newMidiEffects.forEach((box, idx) => box.index.setValue(midiInsertIndex + idx));
182
+ newAudioEffects.forEach((box, idx) => box.index.setValue(audioInsertIndex + idx));
183
+ selection.select(...deviceBoxes.map(box => boxAdapters.adapterFor(box, Devices.isAny)));
184
+ });
185
+ }
186
+ };
187
+ };
188
+ })(DevicesClipboard || (DevicesClipboard = {}));
@@ -0,0 +1,21 @@
1
+ import { Procedure, Provider, Selection } from "@opendaw/lib-std";
2
+ import { Address, BoxEditing, BoxGraph } from "@opendaw/lib-box";
3
+ import { ppqn } from "@opendaw/lib-dsp";
4
+ import { BoxAdapters, NoteEventBoxAdapter } from "@opendaw/studio-adapters";
5
+ import { ClipboardEntry, ClipboardHandler } from "../ClipboardManager";
6
+ type ClipboardNotes = ClipboardEntry<"notes">;
7
+ export declare namespace NotesClipboard {
8
+ type Context = {
9
+ readonly getEnabled: Provider<boolean>;
10
+ readonly getPosition: Provider<ppqn>;
11
+ readonly setPosition: Procedure<ppqn>;
12
+ readonly editing: BoxEditing;
13
+ readonly selection: Selection<NoteEventBoxAdapter>;
14
+ readonly targetAddress: Address;
15
+ readonly boxGraph: BoxGraph;
16
+ readonly boxAdapters: BoxAdapters;
17
+ };
18
+ const createHandler: ({ getEnabled, getPosition, setPosition, editing, selection, targetAddress, boxGraph, boxAdapters }: Context) => ClipboardHandler<ClipboardNotes>;
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=NotesClipboardHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotesClipboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/ui/clipboard/types/NotesClipboardHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAA;AACxG,OAAO,EAAC,OAAO,EAAO,UAAU,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAGrC,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,0BAA0B,CAAA;AACzE,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AAGpE,KAAK,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;AAE7C,yBAAiB,cAAc,CAAC;IAC5B,KAAY,OAAO,GAAG;QAClB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACtC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QACpC,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,IAAI,CAAC,CAAA;QACrC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;QAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAA;QAClD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;QAC/B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;QAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;KACpC,CAAA;IAcM,MAAM,aAAa,GAAI,oGASG,OAAO,KAAG,gBAAgB,CAAC,cAAc,CAsDzE,CAAA;CACJ"}