@opendaw/studio-adapters 0.0.106 → 0.0.108

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.
@@ -1,11 +1,12 @@
1
1
  import { int, Option } from "@opendaw/lib-std";
2
+ import { ppqn } from "@opendaw/lib-dsp";
2
3
  export type ProcessorOptions = {};
3
4
  export type EngineProcessorAttachment = {
4
5
  syncStreamBuffer: SharedArrayBuffer;
5
6
  controlFlagsBuffer: SharedArrayBuffer;
6
7
  hrClockBuffer: SharedArrayBuffer;
7
8
  project: ArrayBufferLike;
8
- exportConfiguration?: ExportStemsConfiguration;
9
+ exportConfiguration?: ExportConfiguration;
9
10
  options?: ProcessorOptions;
10
11
  };
11
12
  export type ExportStemConfiguration = {
@@ -15,10 +16,17 @@ export type ExportStemConfiguration = {
15
16
  skipChannelStrip?: boolean;
16
17
  fileName: string;
17
18
  };
18
- export type ExportStemsConfiguration = Record<string, ExportStemConfiguration>;
19
- export declare namespace ExportStemsConfiguration {
20
- const countStems: (config: Option<ExportStemsConfiguration>) => int;
19
+ export type ExportRange = "full" | {
20
+ start: ppqn;
21
+ end: ppqn;
22
+ };
23
+ export type ExportConfiguration = {
24
+ stems?: Record<string, ExportStemConfiguration>;
25
+ range?: ExportRange;
26
+ };
27
+ export declare namespace ExportConfiguration {
28
+ const countStems: (config: Option<ExportConfiguration>) => int;
21
29
  const sanitizeFileName: (name: string) => string;
22
- const sanitizeExportNamesInPlace: (configuration: ExportStemsConfiguration) => void;
30
+ const sanitizeExportNamesInPlace: (configuration: ExportConfiguration) => void;
23
31
  }
24
32
  //# sourceMappingURL=EngineProcessorAttachment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EngineProcessorAttachment.d.ts","sourceRoot":"","sources":["../src/EngineProcessorAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAE,MAAM,EAAC,MAAM,kBAAkB,CAAA;AAE5C,MAAM,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAGjC,MAAM,MAAM,yBAAyB,GAAG;IACpC,gBAAgB,EAAE,iBAAiB,CAAA;IACnC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,aAAa,EAAE,iBAAiB,CAAA;IAChC,OAAO,EAAE,eAAe,CAAA;IACxB,mBAAmB,CAAC,EAAE,wBAAwB,CAAA;IAC9C,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IAClC,mBAAmB,EAAE,OAAO,CAAA;IAC5B,YAAY,EAAE,OAAO,CAAA;IACrB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;AAE9E,yBAAiB,wBAAwB,CAAC;IAC/B,MAAM,UAAU,GAAI,QAAQ,MAAM,CAAC,wBAAwB,CAAC,KAAG,GAIhE,CAAA;IAEC,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MAA4D,CAAA;IAErG,MAAM,0BAA0B,GAAI,eAAe,wBAAwB,KAAG,IAiBpF,CAAA;CACJ"}
1
+ {"version":3,"file":"EngineProcessorAttachment.d.ts","sourceRoot":"","sources":["../src/EngineProcessorAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAa,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAErC,MAAM,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAGjC,MAAM,MAAM,yBAAyB,GAAG;IACpC,gBAAgB,EAAE,iBAAiB,CAAA;IACnC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,aAAa,EAAE,iBAAiB,CAAA;IAChC,OAAO,EAAE,eAAe,CAAA;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IAClC,mBAAmB,EAAE,OAAO,CAAA;IAC5B,YAAY,EAAE,OAAO,CAAA;IACrB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,CAAA;AAE7D,MAAM,MAAM,mBAAmB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IAC/C,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB,CAAA;AAED,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,UAAU,GAAI,QAAQ,MAAM,CAAC,mBAAmB,CAAC,KAAG,GAI3D,CAAA;IAEC,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MAA4D,CAAA;IAErG,MAAM,0BAA0B,GAAI,eAAe,mBAAmB,KAAG,IAmB/E,CAAA;CACJ"}
@@ -1,11 +1,16 @@
1
- export var ExportStemsConfiguration;
2
- (function (ExportStemsConfiguration) {
3
- ExportStemsConfiguration.countStems = (config) => config.match({
1
+ import { isDefined } from "@opendaw/lib-std";
2
+ export var ExportConfiguration;
3
+ (function (ExportConfiguration) {
4
+ ExportConfiguration.countStems = (config) => config.match({
4
5
  none: () => 1,
5
- some: (configuration) => Object.keys(configuration).length
6
+ some: cfg => isDefined(cfg.stems) ? Object.keys(cfg.stems).length : 1
6
7
  });
7
- ExportStemsConfiguration.sanitizeFileName = (name) => name.replace(/[<>:"/\\|?*\x00-\x1F]/g, "_").trim();
8
- ExportStemsConfiguration.sanitizeExportNamesInPlace = (configuration) => {
8
+ ExportConfiguration.sanitizeFileName = (name) => name.replace(/[<>:"/\\|?*\x00-\x1F]/g, "_").trim();
9
+ ExportConfiguration.sanitizeExportNamesInPlace = (configuration) => {
10
+ if (!isDefined(configuration.stems)) {
11
+ return;
12
+ }
13
+ const stems = configuration.stems;
9
14
  const sanitizedNames = new Map();
10
15
  const getUniqueName = (baseName) => {
11
16
  let count = sanitizedNames.get(baseName) ?? 0;
@@ -18,9 +23,9 @@ export var ExportStemsConfiguration;
18
23
  sanitizedNames.set(newName, 1);
19
24
  return newName;
20
25
  };
21
- Object.keys(configuration).forEach((key) => {
22
- const entry = configuration[key];
23
- entry.fileName = getUniqueName(ExportStemsConfiguration.sanitizeFileName(entry.fileName));
26
+ Object.keys(stems).forEach((key) => {
27
+ const entry = stems[key];
28
+ entry.fileName = getUniqueName(ExportConfiguration.sanitizeFileName(entry.fileName));
24
29
  });
25
30
  };
26
- })(ExportStemsConfiguration || (ExportStemsConfiguration = {}));
31
+ })(ExportConfiguration || (ExportConfiguration = {}));
@@ -54,7 +54,7 @@ _GateDeviceBoxAdapter_context = new WeakMap(), _GateDeviceBoxAdapter_box = new W
54
54
  inverse: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.inverse, ValueMapping.bool, StringMapping.bool, "Inverse"),
55
55
  threshold: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.threshold, ValueMapping.linear(-80.0, 0.0), StringMapping.decible, "Threshold"),
56
56
  return: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.return, ValueMapping.linear(0.0, 24.0), StringMapping.decible, "Return"),
57
- attack: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.attack, ValueMapping.linear(0.0, 50.0), StringMapping.numeric({ unit: "ms", fractionDigits: 1 }), "Attack"),
57
+ attack: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.attack, ValueMapping.linear(0.0, 1000.0), StringMapping.numeric({ unit: "ms", fractionDigits: 1 }), "Attack"),
58
58
  hold: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.hold, ValueMapping.linear(0.0, 500.0), StringMapping.numeric({ unit: "ms", fractionDigits: 1 }), "Hold"),
59
59
  release: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.release, ValueMapping.linear(1.0, 2000.0), StringMapping.numeric({ unit: "ms", fractionDigits: 1 }), "Release"),
60
60
  floor: __classPrivateFieldGet(this, _GateDeviceBoxAdapter_parametric, "f").createParameter(box.floor, ValueMapping.decibel(-72.0, -12.0, 0.0), StringMapping.decible, "Floor")
@@ -7,6 +7,7 @@ export declare class UserEditing implements Terminable {
7
7
  catchupAndSubscribe(observer: Observer<Option<Vertex>>): Subscription;
8
8
  follow(pointer: PointerField<Pointers.Editing>): void;
9
9
  edit(target: Vertex<Pointers.Editing | Pointers>): void;
10
+ editIfDifferent(target: Vertex<Pointers.Editing | Pointers>): void;
10
11
  isEditing(vertex: Vertex<Pointers.Editing | Pointers>): boolean;
11
12
  get(): Option<Vertex>;
12
13
  clear(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"UserEditing.d.ts","sourceRoot":"","sources":["../../src/editing/UserEditing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAY,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAC9F,OAAO,EAAC,YAAY,EAAE,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAE9C,qBAAa,WAAY,YAAW,UAAU;;gBAO9B,OAAO,EAAE,OAAO;IAK5B,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY;IAKrE,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI;IAOrD,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,IAAI;IAIvD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,OAAO;IAO/D,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;IAErB,KAAK,IAAI,IAAI;IAEb,SAAS,IAAI,IAAI;CAOpB"}
1
+ {"version":3,"file":"UserEditing.d.ts","sourceRoot":"","sources":["../../src/editing/UserEditing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAY,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAC9F,OAAO,EAAC,YAAY,EAAE,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAE9C,qBAAa,WAAY,YAAW,UAAU;;gBAO9B,OAAO,EAAE,OAAO;IAK5B,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY;IAKrE,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI;IAOrD,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,IAAI;IAIvD,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,IAAI;IAIlE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,OAAO;IAO/D,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;IAErB,KAAK,IAAI,IAAI;IAEb,SAAS,IAAI,IAAI;CAOpB"}
@@ -33,6 +33,11 @@ export class UserEditing {
33
33
  edit(target) {
34
34
  __classPrivateFieldGet(this, _UserEditing_pointer, "f").ifSome(pointer => __classPrivateFieldGet(this, _UserEditing_editing, "f").modify(() => pointer.refer(target), false));
35
35
  }
36
+ editIfDifferent(target) {
37
+ if (!this.isEditing(target)) {
38
+ this.edit(target);
39
+ }
40
+ }
36
41
  isEditing(vertex) {
37
42
  return __classPrivateFieldGet(this, _UserEditing_pointer, "f").match({
38
43
  none: () => false,
@@ -1 +1 @@
1
- {"version":3,"file":"InstrumentFactories.d.ts","sourceRoot":"","sources":["../../src/factories/InstrumentFactories.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,gBAAgB,EAChB,YAAY,EAEZ,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAElB,kBAAkB,EAElB,aAAa,EACb,qBAAqB,EACxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAC,IAAI,EAAa,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAKtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAGrD,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAqBvD,CAAA;IAEM,MAAM,IAAI,EAAE,iBAAiB,CAAC,YAAY,EAAE,aAAa,CA+B/D,CAAA;IAED,KAAY,mBAAmB,GAAG,aAAa,CAAC;QAC5C,IAAI,EAAE,IAAI,CAAA;QACV,IAAI,EAAE,IAAI,CAAC,KAAK,CAAA;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,iBAAiB,EAAE,MAAM,CAAA;QACzB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC,CAAA;IAEK,MAAM,SAAS,EAAE,iBAAiB,CAAC,mBAAmB,EAAE,kBAAkB,CA8BhF,CAAA;IAEM,MAAM,YAAY,EAAE,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,CA+BvE,CAAA;IAEM,MAAM,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,CAiBnE,CAAA;IAEM,MAAM,SAAS,EAAE,iBAAiB,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,kBAAkB,CAuBhG,CAAA;IAEM,MAAM,OAAO,EAAE,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,CAiB7D,CAAA;IAEM,MAAM,KAAK;;;;;;kBA5CgC,IAAI,CAAC,MAAM;kBAAQ,MAAM;;;;KA4Ce,CAAA;IAC1F,KAAY,IAAI,GAAG,MAAM,OAAO,KAAK,CAAA;CAYxC"}
1
+ {"version":3,"file":"InstrumentFactories.d.ts","sourceRoot":"","sources":["../../src/factories/InstrumentFactories.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,gBAAgB,EAChB,YAAY,EAEZ,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAElB,kBAAkB,EAElB,aAAa,EACb,qBAAqB,EACxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAC,IAAI,EAAa,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAKtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAGrD,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAoBvD,CAAA;IAEM,MAAM,IAAI,EAAE,iBAAiB,CAAC,YAAY,EAAE,aAAa,CAiB/D,CAAA;IAED,KAAY,mBAAmB,GAAG,aAAa,CAAC;QAC5C,IAAI,EAAE,IAAI,CAAA;QACV,IAAI,EAAE,IAAI,CAAC,KAAK,CAAA;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,iBAAiB,EAAE,MAAM,CAAA;QACzB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC,CAAA;IAEK,MAAM,SAAS,EAAE,iBAAiB,CAAC,mBAAmB,EAAE,kBAAkB,CA8BhF,CAAA;IAEM,MAAM,YAAY,EAAE,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,CA8BvE,CAAA;IAEM,MAAM,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,CAiBnE,CAAA;IAEM,MAAM,SAAS,EAAE,iBAAiB,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,kBAAkB,CAehG,CAAA;IAEM,MAAM,OAAO,EAAE,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,CAe7D,CAAA;IAEM,MAAM,KAAK;;;;;;kBAlCgC,IAAI,CAAC,MAAM;kBAAQ,MAAM;;;;KAkCe,CAAA;IAC1F,KAAY,IAAI,GAAG,MAAM,OAAO,KAAK,CAAA;CAYxC"}
@@ -30,27 +30,14 @@ export var InstrumentFactories;
30
30
  description: "Simple sampler",
31
31
  manualPage: DeviceManualUrls.Nano,
32
32
  trackType: TrackType.Notes,
33
- create: (boxGraph, host, name, icon, attachment) => {
34
- let audioFileBox;
33
+ create: (boxGraph, host, name, icon, attachment) => NanoDeviceBox.create(boxGraph, UUID.generate(), box => {
34
+ box.label.setValue(name);
35
+ box.icon.setValue(IconSymbol.toName(icon));
35
36
  if (isDefined(attachment)) {
36
- audioFileBox = attachment;
37
+ box.file.refer(attachment);
37
38
  }
38
- else {
39
- const fileUUID = UUID.parse("c1678daa-4a47-4cba-b88f-4f4e384663c3");
40
- const fileDuration = 5.340;
41
- audioFileBox = boxGraph.findBox(fileUUID)
42
- .unwrapOrElse(() => AudioFileBox.create(boxGraph, fileUUID, box => {
43
- box.fileName.setValue("Rhode");
44
- box.endInSeconds.setValue(fileDuration);
45
- }));
46
- }
47
- return NanoDeviceBox.create(boxGraph, UUID.generate(), box => {
48
- box.label.setValue(name);
49
- box.icon.setValue(IconSymbol.toName(icon));
50
- box.file.refer(audioFileBox);
51
- box.host.refer(host);
52
- });
53
- }
39
+ box.host.refer(host);
40
+ })
54
41
  };
55
42
  InstrumentFactories.Playfield = {
56
43
  defaultName: "Playfield",
@@ -89,7 +76,6 @@ export var InstrumentFactories;
89
76
  create: (boxGraph, host, name, icon, _attachment) => VaporisateurDeviceBox.create(boxGraph, UUID.generate(), box => {
90
77
  box.label.setValue(name);
91
78
  box.icon.setValue(IconSymbol.toName(icon));
92
- box.tune.setInitValue(0.0);
93
79
  box.cutoff.setInitValue(8000.0);
94
80
  box.resonance.setInitValue(0.1);
95
81
  box.attack.setInitValue(0.005);
@@ -126,18 +112,11 @@ export var InstrumentFactories;
126
112
  description: "Soundfont Player",
127
113
  manualPage: DeviceManualUrls.Soundfont,
128
114
  trackType: TrackType.Notes,
129
- create: (boxGraph, host, name, icon, attachment) => {
130
- attachment ?? (attachment = { uuid: "d9f51577-2096-4671-9067-27ca2e12b329", name: "Upright Piano KW" });
131
- const soundFontUUIDAsString = attachment.uuid;
132
- const soundfontUUID = UUID.parse(soundFontUUIDAsString);
133
- const soundfontBox = useSoundfontFile(boxGraph, soundfontUUID, attachment.name);
134
- return SoundfontDeviceBox.create(boxGraph, UUID.generate(), box => {
135
- box.label.setValue(name);
136
- box.icon.setValue(IconSymbol.toName(icon));
137
- box.host.refer(host);
138
- box.file.refer(soundfontBox);
139
- });
140
- }
115
+ create: (boxGraph, host, name, icon) => SoundfontDeviceBox.create(boxGraph, UUID.generate(), box => {
116
+ box.label.setValue(name);
117
+ box.icon.setValue(IconSymbol.toName(icon));
118
+ box.host.refer(host);
119
+ })
141
120
  };
142
121
  InstrumentFactories.Apparat = {
143
122
  defaultName: "Apparat",
@@ -146,7 +125,7 @@ export var InstrumentFactories;
146
125
  description: "User-scripted instrument",
147
126
  manualPage: DeviceManualUrls.Apparat,
148
127
  trackType: TrackType.Notes,
149
- create: (boxGraph, host, name, icon, _attachment) => ApparatDeviceBox.create(boxGraph, UUID.generate(), box => {
128
+ create: (boxGraph, host, name, icon) => ApparatDeviceBox.create(boxGraph, UUID.generate(), box => {
150
129
  box.label.setValue(name);
151
130
  box.icon.setValue(IconSymbol.toName(icon));
152
131
  box.host.refer(host);
@@ -1,4 +1,4 @@
1
- import { ExportStemsConfiguration } from "./EngineProcessorAttachment";
1
+ import { ExportConfiguration } from "./EngineProcessorAttachment";
2
2
  export interface OfflineEngineInitializeConfig {
3
3
  sampleRate: number;
4
4
  numberOfChannels: number;
@@ -6,7 +6,7 @@ export interface OfflineEngineInitializeConfig {
6
6
  syncStreamBuffer: SharedArrayBuffer;
7
7
  controlFlagsBuffer: SharedArrayBuffer;
8
8
  project: ArrayBufferLike;
9
- exportConfiguration?: ExportStemsConfiguration;
9
+ exportConfiguration?: ExportConfiguration;
10
10
  }
11
11
  export interface OfflineEngineRenderConfig {
12
12
  silenceThresholdDb?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"offline-renderer.d.ts","sourceRoot":"","sources":["../src/offline-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,wBAAwB,EAAC,MAAM,6BAA6B,CAAA;AAEpE,MAAM,WAAW,6BAA6B;IAC1C,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,iBAAiB,CAAA;IACnC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,OAAO,EAAE,eAAe,CAAA;IACxB,mBAAmB,CAAC,EAAE,wBAAwB,CAAA;CACjD;AAED,MAAM,WAAW,yBAAyB;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,qBAAqB;IAClC,UAAU,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAClE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC9C,IAAI,IAAI,IAAI,CAAA;CACf"}
1
+ {"version":3,"file":"offline-renderer.d.ts","sourceRoot":"","sources":["../src/offline-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAC,MAAM,6BAA6B,CAAA;AAE/D,MAAM,WAAW,6BAA6B;IAC1C,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,iBAAiB,CAAA;IACnC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,OAAO,EAAE,eAAe,CAAA;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;CAC5C;AAED,MAAM,WAAW,yBAAyB;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,qBAAqB;IAClC,UAAU,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAClE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC9C,IAAI,IAAI,IAAI,CAAA;CACf"}
@@ -1,11 +1,15 @@
1
- import { Attempt } from "@opendaw/lib-std";
1
+ import { Attempt, int } from "@opendaw/lib-std";
2
2
  import { AudioUnitBox } from "@opendaw/studio-boxes";
3
3
  import { ProjectSkeleton } from "../project/ProjectSkeleton";
4
+ import { PresetHeader } from "./PresetHeader";
4
5
  export declare namespace PresetDecoder {
5
- const decode: (bytes: ArrayBufferLike, target: ProjectSkeleton) => void;
6
+ const decode: (bytes: ArrayBufferLike, target: ProjectSkeleton) => ReadonlyArray<AudioUnitBox>;
7
+ const peekHasTimeline: (arrayBuffer: ArrayBuffer) => boolean;
6
8
  const replaceAudioUnit: (arrayBuffer: ArrayBuffer, targetAudioUnitBox: AudioUnitBox, options?: {
7
9
  keepMIDIEffects?: boolean;
8
10
  keepAudioEffects?: boolean;
11
+ keepTimeline?: boolean;
9
12
  }) => Attempt<void, string>;
13
+ const insertEffectChain: (bytes: ArrayBufferLike, targetAudioUnit: AudioUnitBox, insertIndex: int, kind: PresetHeader.ChainKind) => Attempt<void, string>;
10
14
  }
11
15
  //# sourceMappingURL=PresetDecoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresetDecoder.d.ts","sourceRoot":"","sources":["../../src/preset/PresetDecoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,OAAO,EASV,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAEH,YAAY,EAOf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAC,eAAe,EAAC,MAAM,4BAA4B,CAAA;AAK1D,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,OAAO,eAAe,EAAE,QAAQ,eAAe,SAoErE,CAAA;IAEM,MAAM,gBAAgB,GAAI,aAAa,WAAW,EAAE,oBAAoB,YAAY,EAAE,UAAU;QACnG,eAAe,CAAC,EAAE,OAAO,CAAA;QACzB,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC7B,KAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAyGvB,CAAA;CACJ"}
1
+ {"version":3,"file":"PresetDecoder.d.ts","sourceRoot":"","sources":["../../src/preset/PresetDecoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,OAAO,EAGP,GAAG,EAQN,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAEH,YAAY,EAOf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAC,eAAe,EAAC,MAAM,4BAA4B,CAAA;AAE1D,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAG3C,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,OAAO,eAAe,EAAE,QAAQ,eAAe,KAAG,aAAa,CAAC,YAAY,CA4ElG,CAAA;IAEM,MAAM,eAAe,GAAI,aAAa,WAAW,KAAG,OAY1D,CAAA;IAEM,MAAM,gBAAgB,GAAI,aAAa,WAAW,EAAE,oBAAoB,YAAY,EAAE,UAAU;QACnG,eAAe,CAAC,EAAE,OAAO,CAAA;QACzB,gBAAgB,CAAC,EAAE,OAAO,CAAA;QAC1B,YAAY,CAAC,EAAE,OAAO,CAAA;KACzB,KAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAsHvB,CAAA;IAEM,MAAM,iBAAiB,GAC1B,OAAO,eAAe,EACtB,iBAAiB,YAAY,EAC7B,aAAa,GAAG,EAChB,MAAM,YAAY,CAAC,SAAS,KAC7B,OAAO,CAAC,IAAI,EAAE,MAAM,CA+FtB,CAAA;CACJ"}
@@ -1,6 +1,6 @@
1
- import { asDefined, asInstanceOf, Attempts, ByteArrayInput, isAbsent, isDefined, isInstanceOf, Option, RuntimeNotifier, UUID } from "@opendaw/lib-std";
2
- import { BoxGraph, PointerField } from "@opendaw/lib-box";
3
- import { AudioUnitType } from "@opendaw/studio-enums";
1
+ import { asDefined, asInstanceOf, Attempts, ByteArrayInput, isAbsent, isDefined, isInstanceOf, Option, RuntimeNotifier, tryCatch, UUID } from "@opendaw/lib-std";
2
+ import { BoxGraph, IndexedBox, PointerField } from "@opendaw/lib-box";
3
+ import { AudioUnitType, Pointers } from "@opendaw/studio-enums";
4
4
  import { AudioFileBox, AudioUnitBox, BoxIO, CaptureAudioBox, CaptureMidiBox, SoundfontFileBox, TrackBox } from "@opendaw/studio-boxes";
5
5
  import { ProjectSkeleton } from "../project/ProjectSkeleton";
6
6
  import { TransferUtils } from "../transfer";
@@ -15,14 +15,15 @@ export var PresetDecoder;
15
15
  headline: "Could Not Import Preset",
16
16
  message: "Invalid preset file"
17
17
  }).then();
18
- return;
18
+ return [];
19
19
  }
20
- if (header.readInt() !== PresetHeader.FORMAT_VERSION) {
20
+ const version = header.readInt();
21
+ if (version !== PresetHeader.FORMAT_VERSION) {
21
22
  RuntimeNotifier.info({
22
23
  headline: "Could Not Import Preset",
23
- message: "Invalid preset version"
24
+ message: `Unsupported preset version ${version} (this build supports ${PresetHeader.FORMAT_VERSION}).`
24
25
  }).then();
25
- return;
26
+ return [];
26
27
  }
27
28
  const sourceBoxGraph = new BoxGraph(Option.wrap(BoxIO.create));
28
29
  try {
@@ -33,12 +34,17 @@ export var PresetDecoder;
33
34
  headline: "Could Not Import Preset",
34
35
  message: String(reason)
35
36
  }).then();
36
- return;
37
+ return [];
38
+ }
39
+ const summary = {};
40
+ for (const box of sourceBoxGraph.boxes()) {
41
+ summary[box.name] = (summary[box.name] ?? 0) + 1;
37
42
  }
43
+ console.info(`PresetDecoder.decode: source graph boxes`, summary);
38
44
  const sourceAudioUnitBoxes = sourceBoxGraph.boxes()
39
45
  .filter(box => isInstanceOf(box, AudioUnitBox))
40
46
  .filter(box => box.type.getValue() !== AudioUnitType.Output);
41
- const excludeBox = (box) => TransferUtils.shouldExclude(box) || TransferUtils.excludeTimelinePredicate(box);
47
+ const excludeBox = (box) => TransferUtils.shouldExclude(box);
42
48
  const dependencies = Array.from(sourceBoxGraph.dependenciesOf(sourceAudioUnitBoxes, {
43
49
  alwaysFollowMandatory: true,
44
50
  stopAtResources: true,
@@ -48,34 +54,64 @@ export var PresetDecoder;
48
54
  const uuidMap = TransferUtils.generateMap(sourceAudioUnitBoxes, dependencies, rootBox.audioUnits.address.uuid, primaryAudioBusBox.address.uuid);
49
55
  TransferUtils.copyBoxes(uuidMap, target.boxGraph, sourceAudioUnitBoxes, dependencies);
50
56
  TransferUtils.reorderAudioUnits(uuidMap, sourceAudioUnitBoxes, rootBox);
51
- sourceAudioUnitBoxes
57
+ const importedAudioUnits = sourceAudioUnitBoxes
52
58
  .map(source => asInstanceOf(rootBox.graph
53
59
  .findBox(uuidMap.get(source.address.uuid).target)
54
60
  .unwrap("Target AudioUnit has not been copied"), AudioUnitBox))
55
- .filter(box => box.type.getValue() !== AudioUnitType.Output)
56
- .forEach((audioUnitBox) => {
61
+ .filter(box => box.type.getValue() !== AudioUnitType.Output);
62
+ importedAudioUnits.forEach((audioUnitBox) => {
57
63
  const inputBox = audioUnitBox.input.pointerHub.incoming().at(0)?.box;
58
- if (isDefined(inputBox)) {
59
- audioUnitBox.capture.targetVertex.ifSome(({ box: captureBox }) => {
60
- if (captureBox instanceof CaptureMidiBox) {
61
- TrackBox.create(target.boxGraph, UUID.generate(), box => {
62
- box.index.setValue(0);
63
- box.type.setValue(TrackType.Notes);
64
- box.target.refer(audioUnitBox);
65
- box.tracks.refer(audioUnitBox.tracks);
66
- });
67
- }
68
- else if (captureBox instanceof CaptureAudioBox) {
69
- TrackBox.create(target.boxGraph, UUID.generate(), box => {
70
- box.index.setValue(0);
71
- box.type.setValue(TrackType.Audio);
72
- box.target.refer(audioUnitBox);
73
- box.tracks.refer(audioUnitBox.tracks);
74
- });
75
- }
76
- });
64
+ if (!isDefined(inputBox)) {
65
+ return;
77
66
  }
67
+ const existingTrackCount = audioUnitBox.tracks.pointerHub.incoming().length;
68
+ console.info(`PresetDecoder.decode: AudioUnit ${UUID.toString(audioUnitBox.address.uuid)} has ${existingTrackCount} pre-copied track(s)`);
69
+ if (existingTrackCount > 0) {
70
+ return;
71
+ }
72
+ audioUnitBox.capture.targetVertex.ifSome(({ box: captureBox }) => {
73
+ if (captureBox instanceof CaptureMidiBox) {
74
+ TrackBox.create(target.boxGraph, UUID.generate(), box => {
75
+ box.index.setValue(0);
76
+ box.type.setValue(TrackType.Notes);
77
+ box.target.refer(audioUnitBox);
78
+ box.tracks.refer(audioUnitBox.tracks);
79
+ });
80
+ }
81
+ else if (captureBox instanceof CaptureAudioBox) {
82
+ TrackBox.create(target.boxGraph, UUID.generate(), box => {
83
+ box.index.setValue(0);
84
+ box.type.setValue(TrackType.Audio);
85
+ box.target.refer(audioUnitBox);
86
+ box.tracks.refer(audioUnitBox.tracks);
87
+ });
88
+ }
89
+ });
78
90
  });
91
+ return importedAudioUnits;
92
+ };
93
+ PresetDecoder.peekHasTimeline = (arrayBuffer) => {
94
+ if (arrayBuffer.byteLength < 8) {
95
+ return false;
96
+ }
97
+ const header = new ByteArrayInput(arrayBuffer.slice(0, 8));
98
+ if (header.readInt() !== PresetHeader.MAGIC_HEADER_OPEN) {
99
+ return false;
100
+ }
101
+ if (header.readInt() !== PresetHeader.FORMAT_VERSION) {
102
+ return false;
103
+ }
104
+ const sourceBoxGraph = new BoxGraph(Option.wrap(BoxIO.create));
105
+ const decoded = tryCatch(() => sourceBoxGraph.fromArrayBuffer(arrayBuffer.slice(8), false));
106
+ if (decoded.status === "failure") {
107
+ return false;
108
+ }
109
+ for (const box of sourceBoxGraph.boxes()) {
110
+ if (isInstanceOf(box, TrackBox)) {
111
+ return true;
112
+ }
113
+ }
114
+ return false;
79
115
  };
80
116
  PresetDecoder.replaceAudioUnit = (arrayBuffer, targetAudioUnitBox, options) => {
81
117
  console.debug("ReplaceAudioUnit with preset...");
@@ -101,8 +137,10 @@ export var PresetDecoder;
101
137
  }
102
138
  const replaceMIDIEffects = options?.keepMIDIEffects !== true;
103
139
  const replaceAudioEffects = options?.keepAudioEffects !== true;
140
+ const replaceTimeline = options?.keepTimeline !== true;
104
141
  console.debug("replaceMIDIEffects", replaceMIDIEffects);
105
142
  console.debug("replaceAudioEffects", replaceAudioEffects);
143
+ console.debug("replaceTimeline", replaceTimeline);
106
144
  asDefined(targetAudioUnitBox.input.pointerHub.incoming().at(0)?.box, "Target has no input").delete();
107
145
  if (replaceMIDIEffects) {
108
146
  targetAudioUnitBox.midiEffects.pointerHub.incoming().forEach(({ box }) => box.delete());
@@ -120,16 +158,30 @@ export var PresetDecoder;
120
158
  sourceAudioUnitBox.audioEffects.pointerHub.incoming().forEach(({ box }) => box.delete());
121
159
  sourceBoxGraph.endTransaction();
122
160
  }
123
- // We do not take track or capture boxes into account
124
- const excludeBox = (box) => box.accept({
125
- visitTrackBox: (_box) => true,
126
- visitCaptureMidiBox: (_box) => true,
127
- visitCaptureAudioBox: (_box) => true
128
- }) === true;
161
+ const sourceHasTracks = sourceAudioUnitBox.tracks.pointerHub.incoming().length > 0;
162
+ if (sourceHasTracks && replaceTimeline) {
163
+ targetAudioUnitBox.tracks.pointerHub.incoming().forEach(({ box }) => box.delete());
164
+ }
165
+ // Capture boxes live on the target's AudioUnit already and must not be duplicated.
166
+ // When the caller wants to keep the target's existing timeline, also exclude TrackBox
167
+ // (and everything reached through it) so source tracks aren't copied in.
168
+ const excludeBox = (box) => {
169
+ if (box.accept({
170
+ visitCaptureMidiBox: (_box) => true,
171
+ visitCaptureAudioBox: (_box) => true
172
+ }) === true) {
173
+ return true;
174
+ }
175
+ if (!replaceTimeline && TransferUtils.excludeTimelinePredicate(box)) {
176
+ return true;
177
+ }
178
+ return false;
179
+ };
129
180
  const uuidMap = UUID.newSet(({ source }) => source);
130
181
  const dependencies = Array.from(sourceBoxGraph.dependenciesOf(sourceAudioUnitBox, {
131
182
  excludeBox,
132
- alwaysFollowMandatory: false
183
+ alwaysFollowMandatory: true,
184
+ stopAtResources: true
133
185
  }).boxes);
134
186
  uuidMap.addMany([
135
187
  {
@@ -154,9 +206,9 @@ export var PresetDecoder;
154
206
  }
155
207
  });
156
208
  PointerField.decodeWith({
157
- map: (_pointer, newAddress) => newAddress.map(address => uuidMap.opt(address.uuid).match({
158
- none: () => address,
159
- some: ({ target }) => address.moveTo(target)
209
+ map: (_pointer, newAddress) => newAddress.flatMap(address => uuidMap.opt(address.uuid).match({
210
+ some: ({ target }) => Option.wrap(address.moveTo(target)),
211
+ none: () => targetBoxGraph.findBox(address.uuid).nonEmpty() ? Option.wrap(address) : Option.None
160
212
  }))
161
213
  }, () => {
162
214
  dependencies
@@ -175,4 +227,104 @@ export var PresetDecoder;
175
227
  });
176
228
  return Attempts.Ok;
177
229
  };
230
+ PresetDecoder.insertEffectChain = (bytes, targetAudioUnit, insertIndex, kind) => {
231
+ const headerInput = new ByteArrayInput(bytes.slice(0, 8));
232
+ if (headerInput.readInt() !== PresetHeader.MAGIC_HEADER_OPEN) {
233
+ return Attempts.err("Invalid preset header");
234
+ }
235
+ const version = headerInput.readInt();
236
+ if (version !== PresetHeader.FORMAT_VERSION) {
237
+ return Attempts.err(`Unsupported preset version ${version} (this build supports ${PresetHeader.FORMAT_VERSION}).`);
238
+ }
239
+ const sourceGraph = new BoxGraph(Option.wrap(BoxIO.create));
240
+ const loaded = tryCatch(() => sourceGraph.fromArrayBuffer(bytes.slice(8), false));
241
+ if (loaded.status === "failure") {
242
+ return Attempts.err(`Failed to decode preset: ${String(loaded.error)}`);
243
+ }
244
+ const sourceAudioUnit = sourceGraph.boxes()
245
+ .filter(box => isInstanceOf(box, AudioUnitBox))
246
+ .map(box => asInstanceOf(box, AudioUnitBox))
247
+ .find(box => box.type.getValue() !== AudioUnitType.Output);
248
+ if (isAbsent(sourceAudioUnit)) {
249
+ return Attempts.err("Preset contains no audio unit");
250
+ }
251
+ const sourceField = kind === PresetHeader.ChainKind.Audio
252
+ ? sourceAudioUnit.audioEffects
253
+ : sourceAudioUnit.midiEffects;
254
+ const effects = IndexedBox.collectIndexedBoxes(sourceField);
255
+ if (effects.length === 0) {
256
+ return Attempts.err("Preset contains no effects of the requested kind");
257
+ }
258
+ const targetField = kind === PresetHeader.ChainKind.Audio
259
+ ? targetAudioUnit.audioEffects
260
+ : targetAudioUnit.midiEffects;
261
+ const targetFieldAddress = targetField.address;
262
+ const hostPointerType = kind === PresetHeader.ChainKind.Audio
263
+ ? Pointers.AudioEffectHost
264
+ : Pointers.MIDIEffectHost;
265
+ const targetGraph = targetAudioUnit.graph;
266
+ const count = effects.length;
267
+ const existing = IndexedBox.collectIndexedBoxes(targetField);
268
+ for (let i = existing.length - 1; i >= 0; i--) {
269
+ const box = existing[i];
270
+ const current = box.index.getValue();
271
+ if (current >= insertIndex) {
272
+ box.index.setValue(current + count);
273
+ }
274
+ }
275
+ const excludeBox = (box) => TransferUtils.shouldExclude(box)
276
+ || TransferUtils.excludeTimelinePredicate(box)
277
+ || box instanceof AudioUnitBox;
278
+ const effectSet = new Set(effects);
279
+ const dependencies = Array.from(sourceGraph.dependenciesOf(effects, {
280
+ alwaysFollowMandatory: true,
281
+ stopAtResources: true,
282
+ excludeBox
283
+ }).boxes).filter(box => !effectSet.has(box));
284
+ const existingPreservedUuids = UUID.newSet(uuid => uuid);
285
+ dependencies.forEach(source => {
286
+ if (source.resource === "preserved" && targetGraph.findBox(source.address.uuid).nonEmpty()) {
287
+ existingPreservedUuids.add(source.address.uuid);
288
+ }
289
+ });
290
+ const uuidMap = UUID.newSet(({ source }) => source);
291
+ uuidMap.addMany([
292
+ ...effects.map(box => ({ source: box.address.uuid, target: UUID.generate() })),
293
+ ...dependencies.map(box => ({
294
+ source: box.address.uuid,
295
+ target: box.resource === "preserved" ? box.address.uuid : UUID.generate()
296
+ }))
297
+ ]);
298
+ PointerField.decodeWith({
299
+ map: (pointer, address) => {
300
+ if (pointer.pointerType === hostPointerType) {
301
+ return Option.wrap(targetFieldAddress);
302
+ }
303
+ return address.flatMap(addr => uuidMap.opt(addr.uuid).match({
304
+ some: ({ target }) => Option.wrap(addr.moveTo(target)),
305
+ none: () => targetGraph.findBox(addr.uuid).nonEmpty() ? Option.wrap(addr) : Option.None
306
+ }));
307
+ }
308
+ }, () => {
309
+ effects.forEach((source, i) => {
310
+ const input = new ByteArrayInput(source.toArrayBuffer());
311
+ const uuid = uuidMap.get(source.address.uuid).target;
312
+ targetGraph.createBox(source.name, uuid, box => {
313
+ box.read(input);
314
+ if (IndexedBox.isIndexedBox(box)) {
315
+ box.index.setValue(insertIndex + i);
316
+ }
317
+ });
318
+ });
319
+ dependencies.forEach(source => {
320
+ if (existingPreservedUuids.hasKey(source.address.uuid)) {
321
+ return;
322
+ }
323
+ const input = new ByteArrayInput(source.toArrayBuffer());
324
+ const uuid = uuidMap.get(source.address.uuid).target;
325
+ targetGraph.createBox(source.name, uuid, box => box.read(input));
326
+ });
327
+ });
328
+ return Attempts.Ok;
329
+ };
178
330
  })(PresetDecoder || (PresetDecoder = {}));
@@ -1,5 +1,11 @@
1
+ import { Box } from "@opendaw/lib-box";
1
2
  import { AudioUnitBox } from "@opendaw/studio-boxes";
3
+ import { PresetHeader } from "./PresetHeader";
2
4
  export declare namespace PresetEncoder {
3
- const encode: (audioUnitBox: AudioUnitBox) => ArrayBufferLike;
5
+ const encode: (audioUnitBox: AudioUnitBox, options?: {
6
+ excludeEffect?: (box: Box) => boolean;
7
+ includeTimeline?: boolean;
8
+ }) => ArrayBufferLike;
9
+ const encodeEffects: (effects: ReadonlyArray<Box>, kind: PresetHeader.ChainKind) => ArrayBufferLike;
4
10
  }
5
11
  //# sourceMappingURL=PresetEncoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresetEncoder.d.ts","sourceRoot":"","sources":["../../src/preset/PresetEncoder.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAKlD,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,cAAc,YAAY,KAAG,eAuBnD,CAAA;CACJ"}
1
+ {"version":3,"file":"PresetEncoder.d.ts","sourceRoot":"","sources":["../../src/preset/PresetEncoder.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAA2B,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAC,YAAY,EAA4D,MAAM,uBAAuB,CAAA;AAI7G,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAE3C,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,cAAc,YAAY,EAC1B,UAAS;QACL,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;QACtC,eAAe,CAAC,EAAE,OAAO,CAAA;KACvB,KAAG,eA+B/B,CAAA;IAEM,MAAM,aAAa,GACtB,SAAS,aAAa,CAAC,GAAG,CAAC,EAC3B,MAAM,YAAY,CAAC,SAAS,KAC7B,eA0EF,CAAA;CACJ"}
@@ -1,29 +1,115 @@
1
- import { Arrays, ByteArrayOutput } from "@opendaw/lib-std";
1
+ import { Arrays, ByteArrayInput, ByteArrayOutput, Option, UUID } from "@opendaw/lib-std";
2
+ import { IndexedBox, PointerField } from "@opendaw/lib-box";
3
+ import { AudioUnitBox, CaptureAudioBox, CaptureMidiBox, NoopInstrumentBox } from "@opendaw/studio-boxes";
4
+ import { AudioUnitType, Pointers } from "@opendaw/studio-enums";
2
5
  import { ProjectSkeleton } from "../project/ProjectSkeleton";
3
6
  import { TransferUtils } from "../transfer";
4
7
  import { PresetHeader } from "./PresetHeader";
5
8
  export var PresetEncoder;
6
9
  (function (PresetEncoder) {
7
- PresetEncoder.encode = (audioUnitBox) => {
10
+ PresetEncoder.encode = (audioUnitBox, options = {}) => {
8
11
  const header = ByteArrayOutput.create();
9
12
  header.writeInt(PresetHeader.MAGIC_HEADER_OPEN);
10
13
  header.writeInt(PresetHeader.FORMAT_VERSION);
11
14
  const preset = ProjectSkeleton.empty({ createOutputMaximizer: false, createDefaultUser: false });
12
15
  const { boxGraph, mandatoryBoxes: { rootBox, primaryAudioBusBox } } = preset;
13
16
  const audioUnitBoxes = [audioUnitBox];
14
- const excludeBox = (box) => TransferUtils.shouldExclude(box) || TransferUtils.excludeTimelinePredicate(box);
17
+ const excludeEffect = options.excludeEffect ?? (() => false);
18
+ const includeTimeline = options.includeTimeline === true;
19
+ const excludeBox = (box) => TransferUtils.shouldExclude(box)
20
+ || (!includeTimeline && TransferUtils.excludeTimelinePredicate(box))
21
+ || excludeEffect(box);
15
22
  boxGraph.beginTransaction();
16
23
  const dependencies = Array.from(audioUnitBox.graph.dependenciesOf(audioUnitBoxes, {
17
24
  alwaysFollowMandatory: true,
18
25
  stopAtResources: true,
19
26
  excludeBox
20
27
  }).boxes);
28
+ const summary = {};
29
+ for (const dep of dependencies) {
30
+ summary[dep.name] = (summary[dep.name] ?? 0) + 1;
31
+ }
32
+ console.info(`PresetEncoder.encode: includeTimeline=${includeTimeline}, deps=${dependencies.length}`, summary);
21
33
  const uuidMap = TransferUtils.generateMap(audioUnitBoxes, dependencies, rootBox.audioUnits.address.uuid, primaryAudioBusBox.address.uuid);
22
34
  TransferUtils.copyBoxes(uuidMap, boxGraph, audioUnitBoxes, dependencies);
23
35
  TransferUtils.reorderAudioUnits(uuidMap, audioUnitBoxes, rootBox);
24
36
  boxGraph.endTransaction();
25
- console.debug("SAVING...");
26
- boxGraph.debugBoxes();
37
+ return Arrays.concatArrayBuffers(header.toArrayBuffer(), boxGraph.toArrayBuffer());
38
+ };
39
+ PresetEncoder.encodeEffects = (effects, kind) => {
40
+ const header = ByteArrayOutput.create();
41
+ header.writeInt(PresetHeader.MAGIC_HEADER_OPEN);
42
+ header.writeInt(PresetHeader.FORMAT_VERSION);
43
+ const preset = ProjectSkeleton.empty({ createOutputMaximizer: false, createDefaultUser: false });
44
+ const { boxGraph, mandatoryBoxes: { rootBox, primaryAudioBusBox } } = preset;
45
+ boxGraph.beginTransaction();
46
+ const captureBox = kind === PresetHeader.ChainKind.Audio
47
+ ? CaptureAudioBox.create(boxGraph, UUID.generate())
48
+ : CaptureMidiBox.create(boxGraph, UUID.generate());
49
+ const wrapperAudioUnit = AudioUnitBox.create(boxGraph, UUID.generate(), box => {
50
+ box.collection.refer(rootBox.audioUnits);
51
+ box.output.refer(primaryAudioBusBox.input);
52
+ box.index.setValue(0);
53
+ box.type.setValue(AudioUnitType.Instrument);
54
+ box.capture.refer(captureBox);
55
+ });
56
+ NoopInstrumentBox.create(boxGraph, UUID.generate(), box => {
57
+ box.host.refer(wrapperAudioUnit.input);
58
+ });
59
+ if (effects.length > 0) {
60
+ const sourceGraph = effects[0].graph;
61
+ const excludeBox = (box) => TransferUtils.shouldExclude(box)
62
+ || TransferUtils.excludeTimelinePredicate(box)
63
+ || box instanceof AudioUnitBox;
64
+ const effectSet = new Set(effects);
65
+ const dependencies = Array.from(sourceGraph.dependenciesOf(effects, {
66
+ alwaysFollowMandatory: true,
67
+ stopAtResources: true,
68
+ excludeBox
69
+ }).boxes).filter(box => !effectSet.has(box));
70
+ const uuidMap = UUID.newSet(({ source }) => source);
71
+ uuidMap.addMany([
72
+ ...effects.map(box => ({ source: box.address.uuid, target: UUID.generate() })),
73
+ ...dependencies.map(box => ({
74
+ source: box.address.uuid,
75
+ target: box.resource === "preserved" ? box.address.uuid : UUID.generate()
76
+ }))
77
+ ]);
78
+ const targetField = kind === PresetHeader.ChainKind.Audio
79
+ ? wrapperAudioUnit.audioEffects
80
+ : wrapperAudioUnit.midiEffects;
81
+ const hostPointerType = kind === PresetHeader.ChainKind.Audio
82
+ ? Pointers.AudioEffectHost
83
+ : Pointers.MIDIEffectHost;
84
+ PointerField.decodeWith({
85
+ map: (pointer, address) => {
86
+ if (pointer.pointerType === hostPointerType) {
87
+ return Option.wrap(targetField.address);
88
+ }
89
+ return address.flatMap(addr => uuidMap.opt(addr.uuid).match({
90
+ some: ({ target }) => Option.wrap(addr.moveTo(target)),
91
+ none: () => boxGraph.findBox(addr.uuid).nonEmpty() ? Option.wrap(addr) : Option.None
92
+ }));
93
+ }
94
+ }, () => {
95
+ effects.forEach((source, i) => {
96
+ const input = new ByteArrayInput(source.toArrayBuffer());
97
+ const uuid = uuidMap.get(source.address.uuid).target;
98
+ boxGraph.createBox(source.name, uuid, box => {
99
+ box.read(input);
100
+ if (IndexedBox.isIndexedBox(box)) {
101
+ box.index.setValue(i);
102
+ }
103
+ });
104
+ });
105
+ dependencies.forEach(source => {
106
+ const input = new ByteArrayInput(source.toArrayBuffer());
107
+ const uuid = uuidMap.get(source.address.uuid).target;
108
+ boxGraph.createBox(source.name, uuid, box => box.read(input));
109
+ });
110
+ });
111
+ }
112
+ boxGraph.endTransaction();
27
113
  return Arrays.concatArrayBuffers(header.toArrayBuffer(), boxGraph.toArrayBuffer());
28
114
  };
29
115
  })(PresetEncoder || (PresetEncoder = {}));
@@ -1,5 +1,9 @@
1
1
  export declare namespace PresetHeader {
2
2
  const MAGIC_HEADER_OPEN = 1330664005;
3
3
  const FORMAT_VERSION = 1;
4
+ enum ChainKind {
5
+ Midi = 0,
6
+ Audio = 1
7
+ }
4
8
  }
5
9
  //# sourceMappingURL=PresetHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresetHeader.d.ts","sourceRoot":"","sources":["../../src/preset/PresetHeader.ts"],"names":[],"mappings":"AAAA,yBAAiB,YAAY,CAAC;IACnB,MAAM,iBAAiB,aAAa,CAAA;IACpC,MAAM,cAAc,IAAI,CAAA;CAClC"}
1
+ {"version":3,"file":"PresetHeader.d.ts","sourceRoot":"","sources":["../../src/preset/PresetHeader.ts"],"names":[],"mappings":"AAAA,yBAAiB,YAAY,CAAC;IACnB,MAAM,iBAAiB,aAAa,CAAA;IACpC,MAAM,cAAc,IAAI,CAAA;IAE/B,KAAY,SAAS;QAAE,IAAI,IAAI;QAAE,KAAK,IAAI;KAAC;CAC9C"}
@@ -2,4 +2,9 @@ export var PresetHeader;
2
2
  (function (PresetHeader) {
3
3
  PresetHeader.MAGIC_HEADER_OPEN = 0x4F505245; // OPRE
4
4
  PresetHeader.FORMAT_VERSION = 1;
5
+ let ChainKind;
6
+ (function (ChainKind) {
7
+ ChainKind[ChainKind["Midi"] = 0] = "Midi";
8
+ ChainKind[ChainKind["Audio"] = 1] = "Audio";
9
+ })(ChainKind = PresetHeader.ChainKind || (PresetHeader.ChainKind = {}));
5
10
  })(PresetHeader || (PresetHeader = {}));
@@ -1 +1 @@
1
- {"version":3,"file":"VertexSelection.d.ts","sourceRoot":"","sources":["../../src/selection/VertexSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,SAAS,EACT,OAAO,EACP,GAAG,EAGH,SAAS,EACT,SAAS,EACT,iBAAiB,EAEjB,YAAY,EAGf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAU,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAe,MAAM,kBAAkB,CAAA;AACpF,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAE9C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAErD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS,CAAC,gBAAgB,CAAC;;IAQnD,QAAQ,CAAC,OAAO,EAAE,OAAO;IAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ;gBAA7C,OAAO,EAAE,OAAO,EAAW,QAAQ,EAAE,QAAQ;IAOlE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI;IAQ/C,OAAO,IAAI,IAAI;IASf,uBAAuB,CAAC,CAAC,SAAS,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,gBAAgB,CAAC,EACtC,GAAG,EAAE,SAAS,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC;IAIzG,MAAM,CAAC,GAAG,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAmB7D,QAAQ,CAAC,GAAG,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAW/D,WAAW,IAAI,IAAI;IAInB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,aAAa,CAAC,gBAAgB,CAAC;IAQhF,OAAO,IAAI,OAAO;IAClB,QAAQ,IAAI,OAAO;IAEnB,KAAK,IAAI,GAAG;IAEZ,UAAU,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO;IAEjD,QAAQ,IAAI,aAAa,CAAC,gBAAgB,CAAC;IAE3C,SAAS,CAAC,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,YAAY;IAEtE,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,YAAY;CA4BnF"}
1
+ {"version":3,"file":"VertexSelection.d.ts","sourceRoot":"","sources":["../../src/selection/VertexSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,SAAS,EACT,OAAO,EACP,GAAG,EAGH,SAAS,EACT,SAAS,EACT,iBAAiB,EAEjB,YAAY,EAGf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAU,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAe,MAAM,kBAAkB,CAAA;AACpF,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAE9C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAErD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS,CAAC,gBAAgB,CAAC;;IAQnD,QAAQ,CAAC,OAAO,EAAE,OAAO;IAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ;gBAA7C,OAAO,EAAE,OAAO,EAAW,QAAQ,EAAE,QAAQ;IAOlE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI;IAQ/C,OAAO,IAAI,IAAI;IASf,uBAAuB,CAAC,CAAC,SAAS,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,gBAAgB,CAAC,EACtC,GAAG,EAAE,SAAS,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC;IAIzG,MAAM,CAAC,GAAG,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAoB7D,QAAQ,CAAC,GAAG,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAW/D,WAAW,IAAI,IAAI;IAInB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,aAAa,CAAC,gBAAgB,CAAC;IAQhF,OAAO,IAAI,OAAO;IAClB,QAAQ,IAAI,OAAO;IAEnB,KAAK,IAAI,GAAG;IAEZ,UAAU,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO;IAEjD,QAAQ,IAAI,aAAa,CAAC,gBAAgB,CAAC;IAE3C,SAAS,CAAC,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,YAAY;IAEtE,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,YAAY;CA4BnF"}
@@ -65,6 +65,9 @@ export class VertexSelection {
65
65
  this.editing.modify(() => {
66
66
  const selection = __classPrivateFieldGet(this, _VertexSelection_target, "f").unwrap();
67
67
  for (const selectable of selectables) {
68
+ if (!selectable.isAttached()) {
69
+ continue;
70
+ }
68
71
  if (!__classPrivateFieldGet(this, _VertexSelection_selectableMap, "f").hasKey(selectable.address)) {
69
72
  SelectionBox.create(this.boxGraph, UUID.generate(), box => {
70
73
  box.selectable.refer(selectable);
@@ -1,4 +1,4 @@
1
- import { Arrays, asInstanceOf, assert, ByteArrayInput, clamp, isDefined, isInstanceOf, SetMultimap, UUID } from "@opendaw/lib-std";
1
+ import { Arrays, asInstanceOf, assert, ByteArrayInput, clamp, isDefined, isInstanceOf, Option, SetMultimap, UUID } from "@opendaw/lib-std";
2
2
  import { AudioUnitBox, AuxSendBox, TrackBox } from "@opendaw/studio-boxes";
3
3
  import { IndexedBox, PointerField } from "@opendaw/lib-box";
4
4
  import { UnionBoxTypes } from "../unions";
@@ -54,9 +54,9 @@ export var TransferUtils;
54
54
  return false;
55
55
  };
56
56
  PointerField.decodeWith({
57
- map: (_pointer, address) => address.map(addr => uuidMap.opt(addr.uuid).match({
58
- none: () => addr,
59
- some: ({ target }) => addr.moveTo(target)
57
+ map: (_pointer, address) => address.flatMap(addr => uuidMap.opt(addr.uuid).match({
58
+ some: ({ target }) => Option.wrap(addr.moveTo(target)),
59
+ none: () => targetBoxGraph.findBox(addr.uuid).nonEmpty() ? Option.wrap(addr) : Option.None
60
60
  }))
61
61
  }, () => {
62
62
  audioUnitBoxes.forEach((source) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendaw/studio-adapters",
3
- "version": "0.0.106",
3
+ "version": "0.0.108",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,18 +23,18 @@
23
23
  "test": "echo \"No tests to run\""
24
24
  },
25
25
  "dependencies": {
26
- "@opendaw/lib-box": "^0.0.83",
27
- "@opendaw/lib-dsp": "^0.0.81",
28
- "@opendaw/lib-fusion": "^0.0.90",
29
- "@opendaw/lib-runtime": "^0.0.76",
30
- "@opendaw/lib-std": "^0.0.75",
31
- "@opendaw/studio-boxes": "^0.0.88",
32
- "@opendaw/studio-enums": "^0.0.72",
26
+ "@opendaw/lib-box": "^0.0.85",
27
+ "@opendaw/lib-dsp": "^0.0.83",
28
+ "@opendaw/lib-fusion": "^0.0.92",
29
+ "@opendaw/lib-runtime": "^0.0.78",
30
+ "@opendaw/lib-std": "^0.0.77",
31
+ "@opendaw/studio-boxes": "^0.0.90",
32
+ "@opendaw/studio-enums": "^0.0.74",
33
33
  "soundfont2": "^0.5.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@opendaw/eslint-config": "^0.0.27",
37
- "@opendaw/typescript-config": "^0.0.29"
36
+ "@opendaw/eslint-config": "^0.0.28",
37
+ "@opendaw/typescript-config": "^0.0.31"
38
38
  },
39
- "gitHead": "f1e165645318e34365685eca48d4daa84e6ebb6a"
39
+ "gitHead": "df20edf139f65d8d3146ae44e0e800054550bf12"
40
40
  }