@opendaw/studio-core 0.0.18 → 0.0.20

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 (111) hide show
  1. package/dist/AudioInputDevices.d.ts +8 -0
  2. package/dist/AudioInputDevices.d.ts.map +1 -0
  3. package/dist/AudioInputDevices.js +30 -0
  4. package/dist/AudioUnitOrdering.d.ts +3 -0
  5. package/dist/AudioUnitOrdering.d.ts.map +1 -0
  6. package/dist/AudioUnitOrdering.js +7 -0
  7. package/dist/EffectBox.d.ts +2 -2
  8. package/dist/EffectBox.d.ts.map +1 -1
  9. package/dist/Engine.d.ts +31 -10
  10. package/dist/Engine.d.ts.map +1 -1
  11. package/dist/EngineFacade.d.ts +22 -11
  12. package/dist/EngineFacade.d.ts.map +1 -1
  13. package/dist/EngineFacade.js +39 -22
  14. package/dist/EngineWorklet.d.ts +22 -13
  15. package/dist/EngineWorklet.d.ts.map +1 -1
  16. package/dist/EngineWorklet.js +47 -56
  17. package/dist/MeterWorklet.d.ts.map +1 -1
  18. package/dist/MeterWorklet.js +2 -2
  19. package/dist/Project.d.ts +7 -3
  20. package/dist/Project.d.ts.map +1 -1
  21. package/dist/Project.js +22 -4
  22. package/dist/ProjectApi.d.ts +0 -1
  23. package/dist/ProjectApi.d.ts.map +1 -1
  24. package/dist/ProjectApi.js +16 -11
  25. package/dist/ProjectEnv.d.ts +1 -0
  26. package/dist/ProjectEnv.d.ts.map +1 -1
  27. package/dist/ProjectMigration.d.ts.map +1 -1
  28. package/dist/ProjectMigration.js +20 -3
  29. package/dist/RecordingWorklet.d.ts +15 -3
  30. package/dist/RecordingWorklet.d.ts.map +1 -1
  31. package/dist/RecordingWorklet.js +111 -16
  32. package/dist/WorkerAgents.d.ts +2 -2
  33. package/dist/WorkerAgents.d.ts.map +1 -1
  34. package/dist/Worklets.d.ts +1 -1
  35. package/dist/Worklets.d.ts.map +1 -1
  36. package/dist/Worklets.js +2 -2
  37. package/dist/capture/Capture.d.ts +22 -0
  38. package/dist/capture/Capture.d.ts.map +1 -0
  39. package/dist/capture/Capture.js +32 -0
  40. package/dist/capture/CaptureAudio.d.ts +17 -0
  41. package/dist/capture/CaptureAudio.d.ts.map +1 -0
  42. package/dist/capture/CaptureAudio.js +106 -0
  43. package/dist/capture/CaptureManager.d.ts +12 -0
  44. package/dist/capture/CaptureManager.d.ts.map +1 -0
  45. package/dist/capture/CaptureManager.js +38 -0
  46. package/dist/capture/CaptureMidi.d.ts +13 -0
  47. package/dist/capture/CaptureMidi.d.ts.map +1 -0
  48. package/dist/capture/CaptureMidi.js +50 -0
  49. package/dist/capture/RecordAudio.d.ts +21 -0
  50. package/dist/capture/RecordAudio.d.ts.map +1 -0
  51. package/dist/capture/RecordAudio.js +66 -0
  52. package/dist/capture/RecordMidi.d.ts +15 -0
  53. package/dist/capture/RecordMidi.d.ts.map +1 -0
  54. package/dist/capture/RecordMidi.js +85 -0
  55. package/dist/capture/RecordTrack.d.ts +7 -0
  56. package/dist/capture/RecordTrack.d.ts.map +1 -0
  57. package/dist/capture/RecordTrack.js +23 -0
  58. package/dist/capture/Recording.d.ts +9 -0
  59. package/dist/capture/Recording.d.ts.map +1 -0
  60. package/dist/capture/Recording.js +65 -0
  61. package/dist/capture/RecordingContext.d.ts +14 -0
  62. package/dist/capture/RecordingContext.d.ts.map +1 -0
  63. package/dist/capture/RecordingContext.js +1 -0
  64. package/dist/dawproject/AudioUnitExportLayout.d.ts +10 -0
  65. package/dist/dawproject/AudioUnitExportLayout.d.ts.map +1 -0
  66. package/dist/dawproject/AudioUnitExportLayout.js +64 -0
  67. package/dist/dawproject/BuiltinDevices.d.ts +9 -0
  68. package/dist/dawproject/BuiltinDevices.d.ts.map +1 -0
  69. package/dist/dawproject/BuiltinDevices.js +70 -0
  70. package/dist/dawproject/DawProject.d.ts +22 -0
  71. package/dist/dawproject/DawProject.d.ts.map +1 -0
  72. package/dist/dawproject/DawProject.js +49 -0
  73. package/dist/dawproject/DawProjectExporter.d.ts +9 -0
  74. package/dist/dawproject/DawProjectExporter.d.ts.map +1 -0
  75. package/dist/dawproject/DawProjectExporter.js +252 -0
  76. package/dist/dawproject/DawProjectExporter.test.d.ts +2 -0
  77. package/dist/dawproject/DawProjectExporter.test.d.ts.map +1 -0
  78. package/dist/dawproject/DawProjectExporter.test.js +49 -0
  79. package/dist/dawproject/DawProjectImport.d.ts +12 -0
  80. package/dist/dawproject/DawProjectImport.d.ts.map +1 -0
  81. package/dist/dawproject/DawProjectImport.js +389 -0
  82. package/dist/dawproject/DawProjectImport.test.d.ts +2 -0
  83. package/dist/dawproject/DawProjectImport.test.d.ts.map +1 -0
  84. package/dist/dawproject/DawProjectImport.test.js +15 -0
  85. package/dist/dawproject/DeviceIO.d.ts +8 -0
  86. package/dist/dawproject/DeviceIO.d.ts.map +1 -0
  87. package/dist/dawproject/DeviceIO.js +63 -0
  88. package/dist/index.d.ts +12 -2
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +12 -2
  91. package/dist/processors.js +3 -3
  92. package/dist/processors.js.map +4 -4
  93. package/dist/samples/MainThreadSampleLoader.d.ts +1 -0
  94. package/dist/samples/MainThreadSampleLoader.d.ts.map +1 -1
  95. package/dist/samples/MainThreadSampleLoader.js +10 -6
  96. package/dist/samples/MainThreadSampleManager.d.ts +5 -5
  97. package/dist/samples/MainThreadSampleManager.d.ts.map +1 -1
  98. package/dist/samples/MainThreadSampleManager.js +1 -0
  99. package/dist/samples/SampleProvider.d.ts +2 -2
  100. package/dist/samples/SampleProvider.d.ts.map +1 -1
  101. package/dist/samples/SampleStorage.d.ts.map +1 -1
  102. package/dist/samples/SampleStorage.js +2 -3
  103. package/dist/workers.js +2 -2
  104. package/dist/workers.js.map +4 -4
  105. package/package.json +17 -17
  106. package/dist/DawProjectIO.d.ts +0 -7
  107. package/dist/DawProjectIO.d.ts.map +0 -1
  108. package/dist/DawProjectIO.js +0 -73
  109. package/dist/samples/SamplePeaks.d.ts +0 -6
  110. package/dist/samples/SamplePeaks.d.ts.map +0 -1
  111. package/dist/samples/SamplePeaks.js +0 -9
@@ -0,0 +1,9 @@
1
+ import { EqualizerSchema } from "@opendaw/lib-dawproject";
2
+ import { RevampDeviceBox } from "@opendaw/studio-boxes";
3
+ import { BoxGraph, Field } from "@opendaw/lib-box";
4
+ import { int } from "@opendaw/lib-std";
5
+ import { Pointers } from "@opendaw/studio-enums";
6
+ export declare namespace BuiltinDevices {
7
+ const equalizer: (boxGraph: BoxGraph, equalizer: EqualizerSchema, field: Field<Pointers.MidiEffectHost> | Field<Pointers.AudioEffectHost>, index: int) => RevampDeviceBox;
8
+ }
9
+ //# sourceMappingURL=BuiltinDevices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BuiltinDevices.d.ts","sourceRoot":"","sources":["../../src/dawproject/BuiltinDevices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,eAAe,EAAmB,MAAM,yBAAyB,CAAA;AACjG,OAAO,EAAC,eAAe,EAA0B,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAC,QAAQ,EAAE,KAAK,EAAC,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAY,GAAG,EAAO,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAG9C,yBAAiB,cAAc,CAAC;IACrB,MAAM,SAAS,GAAI,UAAU,QAAQ,EAClB,WAAW,eAAe,EAC1B,OAAO,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,EACvE,OAAO,GAAG,KAAG,eA4DtC,CAAA;CACJ"}
@@ -0,0 +1,70 @@
1
+ import { EqBandType, ParameterDecoder } from "@opendaw/lib-dawproject";
2
+ import { RevampDeviceBox } from "@opendaw/studio-boxes";
3
+ import { ifDefined, UUID } from "@opendaw/lib-std";
4
+ import { semitoneToHz } from "@opendaw/lib-dsp";
5
+ export var BuiltinDevices;
6
+ (function (BuiltinDevices) {
7
+ BuiltinDevices.equalizer = (boxGraph, equalizer, field, index) => {
8
+ const mapOrder = (order) => {
9
+ switch (order) {
10
+ case 1:
11
+ case 2:
12
+ return 0;
13
+ case 3:
14
+ case 4:
15
+ return 1;
16
+ case 5:
17
+ case 6:
18
+ return 2;
19
+ default:
20
+ return 3;
21
+ }
22
+ };
23
+ const readPass = (schema, pass) => {
24
+ const { order, frequency, q, enabled } = pass;
25
+ order.setValue(mapOrder(schema.order));
26
+ frequency.setValue(semitoneToHz(schema.freq.value));
27
+ ifDefined(schema.Q?.value, value => q.setValue(value));
28
+ ifDefined(schema.enabled?.value, value => enabled.setValue(value));
29
+ };
30
+ const readShelf = (schema, pass) => {
31
+ const { frequency, gain, enabled } = pass;
32
+ frequency.setValue(ParameterDecoder.readValue(schema.freq));
33
+ ifDefined(schema.gain?.value, value => gain.setValue(value));
34
+ ifDefined(schema.enabled?.value, value => enabled.setValue(value));
35
+ };
36
+ return RevampDeviceBox.create(boxGraph, UUID.generate(), box => {
37
+ box.host.refer(field);
38
+ box.index.setValue(index);
39
+ box.label.setValue(equalizer.deviceName ?? "Revamp");
40
+ let bellIndex = 0;
41
+ equalizer.bands.forEach((band) => {
42
+ switch (band.type) {
43
+ case EqBandType.HIGH_PASS:
44
+ return readPass(band, box.highPass);
45
+ case EqBandType.LOW_PASS:
46
+ return readPass(band, box.lowPass);
47
+ case EqBandType.HIGH_SHELF:
48
+ return readShelf(band, box.highShelf);
49
+ case EqBandType.LOW_SHELF:
50
+ return readShelf(band, box.lowShelf);
51
+ case EqBandType.BELL: {
52
+ if (bellIndex === 3) {
53
+ return;
54
+ }
55
+ const bell = [box.lowBell, box.midBell, box.highBell][bellIndex++];
56
+ const { frequency, gain, q, enabled } = bell;
57
+ frequency.setValue(ParameterDecoder.readValue(band.freq));
58
+ ifDefined(band.Q?.value, value => q.setValue(value));
59
+ ifDefined(band.gain?.value, value => gain.setValue(value));
60
+ ifDefined(band.enabled?.value, value => enabled.setValue(value));
61
+ return;
62
+ }
63
+ default:
64
+ console.warn(`Cannot map band type: ${band.type} to Revamp`);
65
+ return;
66
+ }
67
+ });
68
+ });
69
+ };
70
+ })(BuiltinDevices || (BuiltinDevices = {}));
@@ -0,0 +1,22 @@
1
+ import { UUID } from "@opendaw/lib-std";
2
+ import { MetaDataSchema, ProjectSchema } from "@opendaw/lib-dawproject";
3
+ import { Project } from "../Project";
4
+ export declare namespace DawProject {
5
+ type Resource = {
6
+ uuid: UUID.Format;
7
+ path: string;
8
+ name: string;
9
+ buffer: ArrayBuffer;
10
+ };
11
+ interface ResourceProvider {
12
+ fromPath(path: string): Resource;
13
+ fromUUID(uuid: UUID.Format): Resource;
14
+ }
15
+ const decode: (buffer: ArrayBuffer | NonSharedBuffer) => Promise<{
16
+ metaData: MetaDataSchema;
17
+ project: ProjectSchema;
18
+ resources: ResourceProvider;
19
+ }>;
20
+ const encode: (project: Project, metaData: MetaDataSchema) => Promise<ArrayBuffer>;
21
+ }
22
+ //# sourceMappingURL=DawProject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DawProject.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProject.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,IAAI,EAAC,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAsB,cAAc,EAAE,aAAa,EAAC,MAAM,yBAAyB,CAAA;AAC1F,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAGlC,yBAAiB,UAAU,CAAC;IACxB,KAAY,QAAQ,GAAG;QAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,CAAA;IAE7F,UAAiB,gBAAgB;QAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAA;QAChC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAA;KACxC;IAEM,MAAM,MAAM,GAAU,QAAQ,WAAW,GAAG,eAAe,KAAG,OAAO,CAAC;QACzE,QAAQ,EAAE,cAAc,CAAC;QACzB,OAAO,EAAE,aAAa,CAAC;QACvB,SAAS,EAAE,gBAAgB,CAAA;KAC9B,CAyBA,CAAA;IAEM,MAAM,MAAM,GAAI,SAAS,OAAO,EAAE,UAAU,cAAc,KAAG,OAAO,CAAC,WAAW,CAgBtF,CAAA;CACJ"}
@@ -0,0 +1,49 @@
1
+ import JSZip from "jszip";
2
+ import { Xml } from "@opendaw/lib-xml";
3
+ import { asDefined, panic, UUID } from "@opendaw/lib-std";
4
+ import { FileReferenceSchema, MetaDataSchema, ProjectSchema } from "@opendaw/lib-dawproject";
5
+ import { DawProjectExporter } from "./DawProjectExporter";
6
+ export var DawProject;
7
+ (function (DawProject) {
8
+ DawProject.decode = async (buffer) => {
9
+ const zip = await JSZip.loadAsync(buffer);
10
+ const metaData = Xml.parse(asDefined(await zip.file("metadata.xml")
11
+ ?.async("string"), "No metadata.xml found"), MetaDataSchema);
12
+ const projectXml = asDefined(await zip.file("project.xml")
13
+ ?.async("string"), "No project.xml found");
14
+ console.debug(projectXml);
15
+ const project = Xml.parse(projectXml, ProjectSchema);
16
+ const resourceFiles = Object.entries(zip.files).filter(([_, file]) => !file.dir && !file.name.endsWith(".xml"));
17
+ const resources = await Promise.all(resourceFiles.map(async ([path, file]) => {
18
+ const name = path.substring(path.lastIndexOf("/") + 1);
19
+ const buffer = await file.async("arraybuffer");
20
+ const uuid = await UUID.sha256(new Uint8Array(buffer).buffer);
21
+ return { uuid, path, name, buffer };
22
+ }));
23
+ return {
24
+ metaData, project, resources: {
25
+ fromPath: (path) => resources
26
+ .find(resource => resource.path === path) ?? panic("Resource not found"),
27
+ fromUUID: (uuid) => resources
28
+ .find(resource => UUID.equals(resource.uuid, uuid)) ?? panic("Resource not found")
29
+ }
30
+ };
31
+ };
32
+ DawProject.encode = (project, metaData) => {
33
+ const zip = new JSZip();
34
+ const projectSchema = DawProjectExporter.write(project, {
35
+ write: (path, buffer) => {
36
+ zip.file(path, buffer);
37
+ return Xml.element({ path, external: false }, FileReferenceSchema);
38
+ }
39
+ });
40
+ const metaDataXml = Xml.pretty(Xml.toElement("MetaData", metaData));
41
+ const projectXml = Xml.pretty(Xml.toElement("Project", projectSchema));
42
+ console.debug("encode");
43
+ console.debug(metaDataXml);
44
+ console.debug(projectXml);
45
+ zip.file("metadata.xml", metaDataXml);
46
+ zip.file("project.xml", projectXml);
47
+ return zip.generateAsync({ type: "arraybuffer" });
48
+ };
49
+ })(DawProject || (DawProject = {}));
@@ -0,0 +1,9 @@
1
+ import { FileReferenceSchema, ProjectSchema } from "@opendaw/lib-dawproject";
2
+ import { Project } from "../Project";
3
+ export declare namespace DawProjectExporter {
4
+ interface ResourcePacker {
5
+ write(path: string, buffer: ArrayBufferLike): FileReferenceSchema;
6
+ }
7
+ const write: (project: Project, resourcePacker: ResourcePacker) => ProjectSchema;
8
+ }
9
+ //# sourceMappingURL=DawProjectExporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DawProjectExporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.ts"],"names":[],"mappings":"AAGA,OAAO,EAaH,mBAAmB,EAKnB,aAAa,EAUhB,MAAM,yBAAyB,CAAA;AAchC,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAQlC,yBAAiB,kBAAkB,CAAC;IAChC,UAAiB,cAAc;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAAA;KAAC;IAE5F,MAAM,KAAK,GAAI,SAAS,OAAO,EAAE,gBAAgB,cAAc,kBA4PrE,CAAA;CACJ"}
@@ -0,0 +1,252 @@
1
+ import { asDefined, asInstanceOf, Color, ifDefined, isInstanceOf, Option, UUID } from "@opendaw/lib-std";
2
+ import { Xml } from "@opendaw/lib-xml";
3
+ import { dbToGain, PPQN } from "@opendaw/lib-dsp";
4
+ import { ApplicationSchema, ArrangementSchema, AudioAlgorithm, AudioSchema, BuiltinDeviceSchema, ChannelRole, ChannelSchema, ClipSchema, ClipsSchema, DeviceRole, FileReferenceSchema, LanesSchema, NoteSchema, NotesSchema, ParameterEncoder, ProjectSchema, RealParameterSchema, TimeSignatureParameterSchema, TimeUnit, TrackSchema, TransportSchema, Unit, WarpSchema, WarpsSchema } from "@opendaw/lib-dawproject";
5
+ import { AddressIdEncoder, BooleanField } from "@opendaw/lib-box";
6
+ import { AudioUnitType } from "@opendaw/studio-enums";
7
+ import { AudioFileBox, AudioUnitBox, NoteEventBox, NoteEventCollectionBox, TrackBox } from "@opendaw/studio-boxes";
8
+ import { AudioUnitExportLayout } from "./AudioUnitExportLayout";
9
+ import { ColorCodes } from "../ColorCodes";
10
+ import { Html } from "@opendaw/lib-dom";
11
+ import { encodeWavFloat } from "../Wav";
12
+ import { DeviceBoxUtils } from "@opendaw/studio-adapters";
13
+ import { DeviceIO } from "./DeviceIO";
14
+ export var DawProjectExporter;
15
+ (function (DawProjectExporter) {
16
+ DawProjectExporter.write = (project, resourcePacker) => {
17
+ const ids = new AddressIdEncoder();
18
+ const { boxGraph, timelineBox, rootBox, sampleManager } = project;
19
+ const audioUnits = rootBox.audioUnits.pointerHub.incoming()
20
+ .map(({ box }) => asInstanceOf(box, AudioUnitBox))
21
+ .sort((a, b) => a.index.getValue() - b.index.getValue());
22
+ boxGraph.boxes().forEach(box => box.accept({
23
+ visitAudioFileBox: (box) => sampleManager.getOrCreate(box.address.uuid).data
24
+ .ifSome(({ frames, numberOfFrames, sampleRate, numberOfChannels }) => resourcePacker.write(`samples/${box.fileName.getValue()}.wav`, encodeWavFloat({
25
+ channels: frames,
26
+ duration: numberOfFrames * sampleRate,
27
+ numberOfChannels,
28
+ sampleRate,
29
+ numFrames: numberOfFrames
30
+ })))
31
+ }));
32
+ const writeTransport = () => Xml.element({
33
+ tempo: Xml.element({
34
+ id: ids.getOrCreate(timelineBox.bpm.address),
35
+ value: timelineBox.bpm.getValue(),
36
+ unit: Unit.BPM
37
+ }, RealParameterSchema),
38
+ timeSignature: Xml.element({
39
+ numerator: timelineBox.signature.nominator.getValue(),
40
+ denominator: timelineBox.signature.denominator.getValue()
41
+ }, TimeSignatureParameterSchema)
42
+ }, TransportSchema);
43
+ const writeDevices = (field, deviceRole) => field.pointerHub
44
+ .incoming().map(({ box }) => {
45
+ const enabled = ("enabled" in box && isInstanceOf(box.enabled, BooleanField)
46
+ ? Option.wrap(box.enabled)
47
+ : Option.None)
48
+ .mapOr(field => ParameterEncoder.bool(ids.getOrCreate(field.address), field.getValue(), "On/Off"), undefined);
49
+ const deviceID = box.name;
50
+ const deviceName = DeviceBoxUtils.lookupLabelField(box).getValue();
51
+ const deviceVendor = "openDAW";
52
+ const id = ids.getOrCreate(box.address);
53
+ return Xml.element({
54
+ id,
55
+ deviceID,
56
+ deviceRole,
57
+ deviceName,
58
+ deviceVendor,
59
+ enabled,
60
+ loaded: true,
61
+ automatedParameters: [],
62
+ state: resourcePacker
63
+ .write(`presets/${UUID.toString(box.address.uuid)}`, DeviceIO.exportDevice(box))
64
+ }, BuiltinDeviceSchema);
65
+ });
66
+ const colorForAudioType = (unitType) => {
67
+ const cssColor = ColorCodes.forAudioType(unitType);
68
+ if (cssColor === "") {
69
+ return "red";
70
+ }
71
+ const [r, g, b] = Html.readCssVarColor(ColorCodes.forAudioType(unitType))[0];
72
+ const RR = Math.round(r * 255).toString(16).padStart(2, "0");
73
+ const GG = Math.round(g * 255).toString(16).padStart(2, "0");
74
+ const BB = Math.round(b * 255).toString(16).padStart(2, "0");
75
+ return `#${RR}${GG}${BB}`;
76
+ };
77
+ const writeStructure = () => {
78
+ const tracks = AudioUnitExportLayout.layout(audioUnits);
79
+ const writeAudioUnitBox = (audioUnitBox, tracks) => {
80
+ const unitType = audioUnitBox.type.getValue();
81
+ const color = colorForAudioType(unitType);
82
+ const isPrimary = unitType === AudioUnitType.Output;
83
+ const isAux = unitType === AudioUnitType.Aux;
84
+ const inputBox = audioUnitBox.input.pointerHub.incoming().at(0)?.box;
85
+ const contentType = isPrimary ? "audio notes" // we copied that value from bitwig
86
+ : isAux ? "audio" : (() =>
87
+ // TODO Another location to remember to put devices in...?
88
+ // We could also read the first track type?
89
+ inputBox?.accept({
90
+ visitTapeDeviceBox: () => "audio",
91
+ visitVaporisateurDeviceBox: () => "notes",
92
+ visitNanoDeviceBox: () => "notes",
93
+ visitPlayfieldDeviceBox: () => "notes",
94
+ visitAudioBusBox: () => "tracks"
95
+ }))() ?? undefined;
96
+ return Xml.element({
97
+ id: ids.getOrCreate(audioUnitBox.address),
98
+ name: DeviceBoxUtils.lookupLabelField(inputBox).getValue(),
99
+ loaded: true,
100
+ color,
101
+ contentType,
102
+ channel: Xml.element({
103
+ id: ifDefined(inputBox, ({ address }) => ids.getOrCreate(address)),
104
+ destination: isPrimary
105
+ ? undefined
106
+ : audioUnitBox.output.targetVertex
107
+ .mapOr(({ box }) => ids.getOrCreate(box.address), undefined),
108
+ role: (() => {
109
+ switch (unitType) {
110
+ case AudioUnitType.Instrument:
111
+ return ChannelRole.REGULAR;
112
+ case AudioUnitType.Aux:
113
+ return ChannelRole.EFFECT;
114
+ case AudioUnitType.Bus:
115
+ case AudioUnitType.Output:
116
+ return ChannelRole.MASTER;
117
+ }
118
+ })(),
119
+ devices: [
120
+ ...(writeDevices(audioUnitBox.midiEffects, DeviceRole.NOTE_FX)),
121
+ ...(writeDevices(audioUnitBox.input, DeviceRole.INSTRUMENT)),
122
+ ...(writeDevices(audioUnitBox.audioEffects, DeviceRole.AUDIO_FX))
123
+ ],
124
+ volume: ParameterEncoder.linear(ids.getOrCreate(audioUnitBox.volume.address), dbToGain(audioUnitBox.volume.getValue()), 0.0, 2.0, "Volume"),
125
+ pan: ParameterEncoder.normalized(ids.getOrCreate(audioUnitBox.panning.address), (audioUnitBox.panning.getValue() + 1.0) / 2.0, 0.0, 1.0, "Pan")
126
+ }, ChannelSchema),
127
+ tracks
128
+ }, TrackSchema);
129
+ };
130
+ const writeTracks = (tracks) => tracks.map((track) => writeAudioUnitBox(track.audioUnit, writeTracks(track.children)));
131
+ return writeTracks(tracks);
132
+ };
133
+ const writeAudioRegion = (region) => {
134
+ const audioFileBox = asInstanceOf(region.file.targetVertex.unwrap("No file at region").box, AudioFileBox);
135
+ const audioElement = sampleManager.getOrCreate(audioFileBox.address.uuid).data
136
+ .map(({ numberOfFrames, sampleRate, numberOfChannels }) => Xml.element({
137
+ duration: numberOfFrames / sampleRate,
138
+ channels: numberOfChannels,
139
+ sampleRate,
140
+ algorithm: AudioAlgorithm.REPITCH,
141
+ file: Xml.element({
142
+ path: `samples/${audioFileBox.fileName.getValue()}.wav`,
143
+ external: false
144
+ }, FileReferenceSchema)
145
+ }, AudioSchema));
146
+ const duration = region.duration.getValue() / PPQN.Quarter;
147
+ return Xml.element({
148
+ clips: [Xml.element({
149
+ time: region.position.getValue() / PPQN.Quarter,
150
+ duration,
151
+ contentTimeUnit: TimeUnit.BEATS,
152
+ playStart: 0.0,
153
+ loopStart: 0.0,
154
+ loopEnd: region.loopDuration.getValue() / PPQN.Quarter,
155
+ enable: !region.mute.getValue(),
156
+ name: region.label.getValue(),
157
+ color: Color.hslToHex(region.hue.getValue(), 1.0, 0.60),
158
+ content: [Xml.element({
159
+ content: audioElement.mapOr(element => [element], []),
160
+ contentTimeUnit: "beats",
161
+ warps: [
162
+ Xml.element({
163
+ time: 0.0,
164
+ contentTime: 0.0
165
+ }, WarpSchema),
166
+ Xml.element({
167
+ time: duration,
168
+ contentTime: audioElement.mapOr(element => element.duration, 0)
169
+ }, WarpSchema)
170
+ ]
171
+ }, WarpsSchema)]
172
+ }, ClipSchema)]
173
+ }, ClipsSchema);
174
+ };
175
+ const writeNoteRegion = (region) => {
176
+ const collectionBox = asInstanceOf(region.events.targetVertex
177
+ .unwrap("No notes in region").box, NoteEventCollectionBox);
178
+ return Xml.element({
179
+ clips: [Xml.element({
180
+ time: region.position.getValue() / PPQN.Quarter,
181
+ duration: region.duration.getValue() / PPQN.Quarter,
182
+ contentTimeUnit: TimeUnit.BEATS,
183
+ playStart: 0.0,
184
+ loopStart: 0.0,
185
+ loopEnd: region.loopDuration.getValue() / PPQN.Quarter,
186
+ enable: !region.mute.getValue(),
187
+ name: region.label.getValue(),
188
+ color: Color.hslToHex(region.hue.getValue(), 1.0, 0.60),
189
+ content: [Xml.element({
190
+ notes: collectionBox.events.pointerHub.incoming()
191
+ .map(({ box }) => asInstanceOf(box, NoteEventBox))
192
+ .map(box => Xml.element({
193
+ time: box.position.getValue() / PPQN.Quarter,
194
+ duration: box.duration.getValue() / PPQN.Quarter,
195
+ key: box.pitch.getValue(),
196
+ channel: 0,
197
+ vel: box.velocity.getValue(),
198
+ rel: box.velocity.getValue()
199
+ }, NoteSchema))
200
+ }, NotesSchema)]
201
+ }, ClipSchema)]
202
+ }, ClipsSchema);
203
+ };
204
+ // TODO Implement!
205
+ const writeValueRegion = (region) => Xml.element({
206
+ clips: [Xml.element({
207
+ time: region.position.getValue() / PPQN.Quarter,
208
+ duration: region.duration.getValue() / PPQN.Quarter,
209
+ contentTimeUnit: TimeUnit.BEATS,
210
+ playStart: 0.0,
211
+ loopStart: 0.0,
212
+ loopEnd: region.loopDuration.getValue() / PPQN.Quarter,
213
+ enable: !region.mute.getValue(),
214
+ name: region.label.getValue(),
215
+ color: Color.hslToHex(region.hue.getValue(), 1.0, 0.60),
216
+ content: []
217
+ }, ClipSchema)]
218
+ }, ClipsSchema);
219
+ const writeLanes = () => {
220
+ return audioUnits
221
+ .flatMap(audioUnitBox => audioUnitBox.tracks.pointerHub.incoming()
222
+ .map(({ box }) => asInstanceOf(box, TrackBox))
223
+ .sort((a, b) => a.index.getValue() - b.index.getValue())
224
+ .map(trackBox => Xml.element({
225
+ id: ids.getOrCreate(trackBox.address),
226
+ track: ids.getOrCreate(audioUnitBox.address),
227
+ lanes: trackBox.regions.pointerHub.incoming()
228
+ .map(({ box }) => asDefined(box.accept({
229
+ visitAudioRegionBox: (region) => writeAudioRegion(region),
230
+ visitNoteRegionBox: (region) => writeNoteRegion(region),
231
+ visitValueRegionBox: (region) => writeValueRegion(region)
232
+ }), "Could not write region."))
233
+ }, LanesSchema)));
234
+ };
235
+ return Xml.element({
236
+ version: "1.0",
237
+ application: Xml.element({
238
+ name: "openDAW",
239
+ version: "0.1"
240
+ }, ApplicationSchema),
241
+ transport: writeTransport(),
242
+ structure: writeStructure(),
243
+ arrangement: Xml.element({
244
+ lanes: Xml.element({
245
+ lanes: writeLanes(),
246
+ timeUnit: TimeUnit.BEATS
247
+ }, LanesSchema)
248
+ }, ArrangementSchema),
249
+ scenes: []
250
+ }, ProjectSchema);
251
+ };
252
+ })(DawProjectExporter || (DawProjectExporter = {}));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=DawProjectExporter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DawProjectExporter.test.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { describe, it } from "vitest";
2
+ import { fileURLToPath } from "url";
3
+ import * as path from "node:path";
4
+ import * as fs from "node:fs";
5
+ import { Project } from "../Project";
6
+ import { Option, panic, Terminable } from "@opendaw/lib-std";
7
+ import { Xml } from "@opendaw/lib-xml";
8
+ import { FileReferenceSchema } from "@opendaw/lib-dawproject";
9
+ import { DawProjectExporter } from "./DawProjectExporter";
10
+ describe("DawProjectExport", () => {
11
+ it("export", async () => {
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+ const projectPath = "../../../../../test-files/all-devices.od";
14
+ const buffer = fs.readFileSync(path.join(__dirname, projectPath));
15
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
16
+ const project = Project.load({
17
+ sampleRate: 44100,
18
+ sampleManager: new class {
19
+ record(loader) {
20
+ throw new Error("Method not implemented.");
21
+ }
22
+ getOrCreate(format) {
23
+ return new class {
24
+ data = Option.None;
25
+ peaks = Option.None;
26
+ uuid = format;
27
+ state = { type: "progress", progress: 0.0 };
28
+ meta = Option.None;
29
+ invalidate() { throw new Error("Method not implemented."); }
30
+ subscribe(_observer) {
31
+ return Terminable.Empty;
32
+ }
33
+ };
34
+ }
35
+ invalidate(_uuid) {
36
+ return panic("Method not implemented.");
37
+ }
38
+ }
39
+ }, arrayBuffer);
40
+ const schema = DawProjectExporter.write(project, {
41
+ write: (path, buffer) => {
42
+ console.debug(`store ${buffer.byteLength} bytes at ${path}`);
43
+ return Xml.element({ path, external: false }, FileReferenceSchema);
44
+ }
45
+ });
46
+ // console.dir(schema, {depth: Number.MAX_SAFE_INTEGER})
47
+ console.debug(Xml.pretty(Xml.toElement("Project", schema)));
48
+ });
49
+ });
@@ -0,0 +1,12 @@
1
+ import { UUID } from "@opendaw/lib-std";
2
+ import { ProjectSchema } from "@opendaw/lib-dawproject";
3
+ import { ProjectDecoder } from "@opendaw/studio-adapters";
4
+ import { DawProject } from "./DawProject";
5
+ export declare namespace DawProjectImport {
6
+ type Result = {
7
+ audioIds: ReadonlyArray<UUID.Format>;
8
+ skeleton: ProjectDecoder.Skeleton;
9
+ };
10
+ const read: (schema: ProjectSchema, resources: DawProject.ResourceProvider) => Promise<Result>;
11
+ }
12
+ //# sourceMappingURL=DawProjectImport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DawProjectImport.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImport.ts"],"names":[],"mappings":"AAAA,OAAO,EAgBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,EAOhB,MAAM,yBAAyB,CAAA;AAuBhC,OAAO,EAAyC,cAAc,EAAY,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAQvC,yBAAiB,gBAAgB,CAAC;IAW9B,KAAY,MAAM,GAAG;QACjB,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAA;KACpC,CAAA;IAOM,MAAM,IAAI,GAAU,QAAQ,aAAa,EAAE,WAAW,UAAU,CAAC,gBAAgB,KAAG,OAAO,CAAC,MAAM,CA6WxG,CAAA;CAaJ"}