@opendaw/studio-core 0.0.6

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 (53) hide show
  1. package/README.md +1 -0
  2. package/dist/Engine.d.ts +21 -0
  3. package/dist/Engine.d.ts.map +1 -0
  4. package/dist/Engine.js +1 -0
  5. package/dist/EngineFacade.d.ts +28 -0
  6. package/dist/EngineFacade.d.ts.map +1 -0
  7. package/dist/EngineFacade.js +50 -0
  8. package/dist/EngineWorklet.d.ts +30 -0
  9. package/dist/EngineWorklet.d.ts.map +1 -0
  10. package/dist/EngineWorklet.js +168 -0
  11. package/dist/MeterWorklet.d.ts +15 -0
  12. package/dist/MeterWorklet.d.ts.map +1 -0
  13. package/dist/MeterWorklet.js +33 -0
  14. package/dist/Mixer.d.ts +13 -0
  15. package/dist/Mixer.d.ts.map +1 -0
  16. package/dist/Mixer.js +110 -0
  17. package/dist/Project.d.ts +41 -0
  18. package/dist/Project.d.ts.map +1 -0
  19. package/dist/Project.js +173 -0
  20. package/dist/ProjectEnv.d.ts +5 -0
  21. package/dist/ProjectEnv.d.ts.map +1 -0
  22. package/dist/ProjectEnv.js +1 -0
  23. package/dist/RecordingWorklet.d.ts +9 -0
  24. package/dist/RecordingWorklet.d.ts.map +1 -0
  25. package/dist/RecordingWorklet.js +42 -0
  26. package/dist/Wav.d.ts +6 -0
  27. package/dist/Wav.d.ts.map +1 -0
  28. package/dist/Wav.js +46 -0
  29. package/dist/WorkletFactory.d.ts +8 -0
  30. package/dist/WorkletFactory.d.ts.map +1 -0
  31. package/dist/WorkletFactory.js +9 -0
  32. package/dist/Worklets.d.ts +22 -0
  33. package/dist/Worklets.d.ts.map +1 -0
  34. package/dist/Worklets.js +36 -0
  35. package/dist/engine-processor.js +6 -0
  36. package/dist/engine-processor.js.map +7 -0
  37. package/dist/index.d.ts +13 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +12 -0
  40. package/dist/meter-processor.js +2 -0
  41. package/dist/meter-processor.js.map +7 -0
  42. package/dist/recording-processor.js +4 -0
  43. package/dist/recording-processor.js.map +7 -0
  44. package/dist/sync-log/Commit.d.ts +26 -0
  45. package/dist/sync-log/Commit.d.ts.map +1 -0
  46. package/dist/sync-log/Commit.js +76 -0
  47. package/dist/sync-log/SyncLogReader.d.ts +12 -0
  48. package/dist/sync-log/SyncLogReader.d.ts.map +1 -0
  49. package/dist/sync-log/SyncLogReader.js +51 -0
  50. package/dist/sync-log/SyncLogWriter.d.ts +10 -0
  51. package/dist/sync-log/SyncLogWriter.d.ts.map +1 -0
  52. package/dist/sync-log/SyncLogWriter.js +49 -0
  53. package/package.json +48 -0
@@ -0,0 +1,173 @@
1
+ import { asInstanceOf, ByteArrayOutput, Option, panic, Terminator, UUID } from "@opendaw/lib-std";
2
+ import { BoxGraph, Editing } from "@opendaw/lib-box";
3
+ import { AudioBusBox, AudioUnitBox, BoxIO, GrooveShuffleBox, RootBox, TimelineBox, UserInterfaceBox, ValueEventCurveBox } from "@opendaw/studio-boxes";
4
+ import { BoxAdapters, IconSymbol, ParameterFieldAdapters, ProjectDecoder, RootBoxAdapter, TimelineBoxAdapter, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
5
+ import { LiveStreamReceiver } from "@opendaw/lib-fusion";
6
+ import { AudioUnitType } from "@opendaw/studio-enums";
7
+ import { Mixer } from "./Mixer";
8
+ // Main Entry Point for a Project
9
+ //
10
+ export class Project {
11
+ static new(env) {
12
+ const boxGraph = new BoxGraph(Option.wrap(BoxIO.create));
13
+ const isoString = new Date().toISOString();
14
+ console.debug(`New Project created on ${isoString}`);
15
+ boxGraph.beginTransaction();
16
+ const grooveShuffleBox = GrooveShuffleBox.create(boxGraph, UUID.generate(), box => {
17
+ box.label.setValue("Groove Shuffle");
18
+ });
19
+ const rootBox = RootBox.create(boxGraph, UUID.generate(), box => {
20
+ box.groove.refer(grooveShuffleBox);
21
+ box.created.setValue(isoString);
22
+ });
23
+ const userInterfaceBox = UserInterfaceBox.create(boxGraph, UUID.generate());
24
+ const masterBusBox = AudioBusBox.create(boxGraph, UUID.generate(), box => {
25
+ box.collection.refer(rootBox.audioBusses);
26
+ box.label.setValue("Output");
27
+ box.icon.setValue(IconSymbol.toName(IconSymbol.SpeakerHeadphone));
28
+ box.color.setValue(/*Colors.blue*/ "hsl(189, 100%, 65%)"); // TODO
29
+ });
30
+ const masterAudioUnit = AudioUnitBox.create(boxGraph, UUID.generate(), box => {
31
+ box.type.setValue(AudioUnitType.Output);
32
+ box.collection.refer(rootBox.audioUnits);
33
+ box.output.refer(rootBox.outputDevice);
34
+ box.index.setValue(0);
35
+ });
36
+ const timelineBox = TimelineBox.create(boxGraph, UUID.generate());
37
+ rootBox.timeline.refer(timelineBox.root);
38
+ userInterfaceBox.root.refer(rootBox.users);
39
+ masterBusBox.output.refer(masterAudioUnit.input);
40
+ boxGraph.endTransaction();
41
+ return new Project(env, boxGraph, {
42
+ rootBox,
43
+ userInterfaceBox,
44
+ masterBusBox,
45
+ masterAudioUnit,
46
+ timelineBox
47
+ });
48
+ }
49
+ static load(env, arrayBuffer) {
50
+ const skeleton = ProjectDecoder.decode(arrayBuffer);
51
+ this.#migrate(skeleton);
52
+ return new Project(env, skeleton.boxGraph, skeleton.mandatoryBoxes);
53
+ }
54
+ static #migrate({ boxGraph, mandatoryBoxes }) {
55
+ const { rootBox } = mandatoryBoxes;
56
+ if (rootBox.groove.targetAddress.isEmpty()) {
57
+ console.debug("Migrate to global GrooveShuffleBox");
58
+ boxGraph.beginTransaction();
59
+ rootBox.groove.refer(GrooveShuffleBox.create(boxGraph, UUID.generate()));
60
+ boxGraph.endTransaction();
61
+ }
62
+ const globalShuffle = asInstanceOf(rootBox.groove.targetVertex.unwrap(), GrooveShuffleBox).label;
63
+ if (globalShuffle.getValue() !== "Groove Shuffle") {
64
+ boxGraph.beginTransaction();
65
+ globalShuffle.setValue("Groove Shuffle");
66
+ boxGraph.endTransaction();
67
+ }
68
+ // TODO We can remove this when we delete all not-migrated, local(!) project files from my machine
69
+ boxGraph.boxes().forEach(box => box.accept({
70
+ visitZeitgeistDeviceBox: (box) => {
71
+ if (box.groove.targetAddress.isEmpty()) {
72
+ console.debug("Migrate 'ZeitgeistDeviceBox' to GrooveShuffleBox");
73
+ boxGraph.beginTransaction();
74
+ box.groove.refer(rootBox.groove.targetVertex.unwrap());
75
+ boxGraph.endTransaction();
76
+ }
77
+ },
78
+ visitValueEventBox: (eventBox) => {
79
+ const slope = eventBox.slope.getValue();
80
+ if (isNaN(slope)) {
81
+ return;
82
+ } // already migrated, nothing to do
83
+ if (slope === 0.0) { // never set
84
+ console.debug("Migrate 'ValueEventBox'");
85
+ boxGraph.beginTransaction();
86
+ eventBox.slope.setValue(NaN);
87
+ boxGraph.endTransaction();
88
+ }
89
+ else if (eventBox.interpolation.getValue() === 1) { // linear
90
+ if (slope === 0.5) {
91
+ console.debug("Migrate 'ValueEventBox' to linear");
92
+ boxGraph.beginTransaction();
93
+ eventBox.slope.setValue(NaN);
94
+ boxGraph.endTransaction();
95
+ }
96
+ else {
97
+ console.debug("Migrate 'ValueEventBox' to new ValueEventCurveBox");
98
+ boxGraph.beginTransaction();
99
+ ValueEventCurveBox.create(boxGraph, UUID.generate(), box => {
100
+ box.event.refer(eventBox.interpolation);
101
+ box.slope.setValue(slope);
102
+ });
103
+ eventBox.slope.setValue(NaN);
104
+ boxGraph.endTransaction();
105
+ }
106
+ }
107
+ }
108
+ }));
109
+ }
110
+ #terminator = new Terminator();
111
+ #env;
112
+ boxGraph;
113
+ rootBox;
114
+ userInterfaceBox;
115
+ masterBusBox;
116
+ masterAudioUnit;
117
+ timelineBox;
118
+ editing;
119
+ selection;
120
+ boxAdapters;
121
+ userEditingManager;
122
+ parameterFieldAdapters;
123
+ liveStreamReceiver;
124
+ mixer;
125
+ constructor(env, boxGraph, { rootBox, userInterfaceBox, masterBusBox, masterAudioUnit, timelineBox }) {
126
+ this.#env = env;
127
+ this.boxGraph = boxGraph;
128
+ this.rootBox = rootBox;
129
+ this.userInterfaceBox = userInterfaceBox;
130
+ this.masterBusBox = masterBusBox;
131
+ this.masterAudioUnit = masterAudioUnit;
132
+ this.timelineBox = timelineBox;
133
+ this.liveStreamReceiver = this.#terminator.own(new LiveStreamReceiver());
134
+ this.editing = new Editing(this.boxGraph);
135
+ this.selection = new VertexSelection(this.editing, this.boxGraph);
136
+ this.parameterFieldAdapters = new ParameterFieldAdapters();
137
+ this.boxAdapters = this.#terminator.own(new BoxAdapters(this));
138
+ this.userEditingManager = new UserEditingManager(this.editing);
139
+ this.userEditingManager.follow(this.userInterfaceBox);
140
+ this.selection.switch(this.userInterfaceBox.selection);
141
+ this.mixer = new Mixer(this.rootBoxAdapter.audioUnits);
142
+ console.debug(`Project was created on ${this.rootBoxAdapter.created.toString()}`);
143
+ }
144
+ own(terminable) { return this.#terminator.own(terminable); }
145
+ ownAll(...terminables) { return this.#terminator.ownAll(...terminables); }
146
+ spawn() { return this.#terminator.spawn(); }
147
+ get bpm() { return this.timelineBox.bpm.getValue(); }
148
+ get rootBoxAdapter() { return this.boxAdapters.adapterFor(this.rootBox, RootBoxAdapter); }
149
+ get timelineBoxAdapter() { return this.boxAdapters.adapterFor(this.timelineBox, TimelineBoxAdapter); }
150
+ get audioManager() { return this.#env.audioManager; }
151
+ get clipSequencing() { return panic("Only available in audio context"); }
152
+ get isAudioContext() { return false; }
153
+ get isMainThread() { return true; }
154
+ get liveStreamBroadcaster() { return panic("Only available in audio context"); }
155
+ toArrayBuffer() {
156
+ const output = ByteArrayOutput.create();
157
+ output.writeInt(ProjectDecoder.MAGIC_HEADER_OPEN);
158
+ output.writeInt(ProjectDecoder.FORMAT_VERSION);
159
+ // store all boxes
160
+ const boxGraphChunk = this.boxGraph.toArrayBuffer();
161
+ output.writeInt(boxGraphChunk.byteLength);
162
+ output.writeBytes(new Int8Array(boxGraphChunk));
163
+ // store mandatory boxes' addresses
164
+ UUID.toDataOutput(output, this.rootBox.address.uuid);
165
+ UUID.toDataOutput(output, this.userInterfaceBox.address.uuid);
166
+ UUID.toDataOutput(output, this.masterBusBox.address.uuid);
167
+ UUID.toDataOutput(output, this.masterAudioUnit.address.uuid);
168
+ UUID.toDataOutput(output, this.timelineBox.address.uuid);
169
+ return output.toArrayBuffer();
170
+ }
171
+ copy() { return Project.load(this.#env, this.toArrayBuffer()); }
172
+ terminate() { this.#terminator.terminate(); }
173
+ }
@@ -0,0 +1,5 @@
1
+ import { AudioLoaderManager } from "@opendaw/studio-adapters";
2
+ export interface ProjectEnv {
3
+ audioManager: AudioLoaderManager;
4
+ }
5
+ //# sourceMappingURL=ProjectEnv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectEnv.d.ts","sourceRoot":"","sources":["../src/ProjectEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAA;AAE3D,MAAM,WAAW,UAAU;IACvB,YAAY,EAAE,kBAAkB,CAAA;CACnC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { int } from "@opendaw/lib-std";
2
+ import { WorkletFactory } from "./WorkletFactory";
3
+ export declare class RecordingWorklet extends AudioWorkletNode {
4
+ #private;
5
+ static bootFactory(context: BaseAudioContext, url: string): Promise<WorkletFactory<RecordingWorklet>>;
6
+ static create(factory: WorkletFactory<RecordingWorklet>, numChannels: int, numChunks?: int): RecordingWorklet;
7
+ private constructor();
8
+ }
9
+ //# sourceMappingURL=RecordingWorklet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordingWorklet.d.ts","sourceRoot":"","sources":["../src/RecordingWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAC,MAAM,kBAAkB,CAAA;AAG/C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAK/C,qBAAa,gBAAiB,SAAQ,gBAAgB;;WACrC,WAAW,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;IAI3G,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,GAAE,GAAQ,GAAG,gBAAgB;IAUjH,OAAO;CAuBV"}
@@ -0,0 +1,42 @@
1
+ import { EmptyExec } from "@opendaw/lib-std";
2
+ import { Files } from "@opendaw/lib-dom";
3
+ import { mergeChunkPlanes, RingBuffer } from "@opendaw/studio-adapters";
4
+ import { WorkletFactory } from "./WorkletFactory";
5
+ import { encodeWavFloat } from "./Wav";
6
+ const RenderQuantum = 128;
7
+ export class RecordingWorklet extends AudioWorkletNode {
8
+ static async bootFactory(context, url) {
9
+ return WorkletFactory.boot(context, url);
10
+ }
11
+ static create(factory, numChannels, numChunks = 64) {
12
+ const audioBytes = numChannels * numChunks * RenderQuantum * Float32Array.BYTES_PER_ELEMENT;
13
+ const pointerBytes = Int32Array.BYTES_PER_ELEMENT * 2;
14
+ const sab = new SharedArrayBuffer(audioBytes + pointerBytes);
15
+ const buffer = { sab, numChunks, numChannels, bufferSize: RenderQuantum };
16
+ return factory.create(context => new RecordingWorklet(context, buffer));
17
+ }
18
+ #reader;
19
+ constructor(context, config) {
20
+ super(context, "recording-processor", {
21
+ numberOfInputs: 1,
22
+ channelCount: config.numChannels,
23
+ channelCountMode: "explicit",
24
+ processorOptions: config
25
+ });
26
+ const sampleRate = this.context.sampleRate;
27
+ const output = [];
28
+ const seconds = 3.1452;
29
+ const numFrames = Math.ceil(sampleRate * seconds);
30
+ console.debug(`numFrames: ${numFrames}`);
31
+ this.#reader = RingBuffer.reader(config, array => {
32
+ output.push(array);
33
+ if (output.length * RenderQuantum >= numFrames) {
34
+ this.#reader.stop();
35
+ const channels = mergeChunkPlanes(output, numFrames);
36
+ const wav = encodeWavFloat({ channels, numFrames, sampleRate });
37
+ Files.save(wav, { suggestedName: "recording.wav" })
38
+ .then(() => console.debug("WAV saved"), EmptyExec);
39
+ }
40
+ });
41
+ }
42
+ }
package/dist/Wav.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export declare const encodeWavFloat: (audio: {
2
+ channels: ReadonlyArray<Float32Array>;
3
+ sampleRate: number;
4
+ numFrames: number;
5
+ } | AudioBuffer) => ArrayBuffer;
6
+ //# sourceMappingURL=Wav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Wav.d.ts","sourceRoot":"","sources":["../src/Wav.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,GAAI,OAAO;IAClC,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAA;CACpB,GAAG,WAAW,KAAG,WA2CjB,CAAA"}
package/dist/Wav.js ADDED
@@ -0,0 +1,46 @@
1
+ import { Arrays } from "@opendaw/lib-std";
2
+ export const encodeWavFloat = (audio) => {
3
+ const MAGIC_RIFF = 0x46464952;
4
+ const MAGIC_WAVE = 0x45564157;
5
+ const MAGIC_FMT = 0x20746d66;
6
+ const MAGIC_DATA = 0x61746164;
7
+ const bytesPerChannel = Float32Array.BYTES_PER_ELEMENT;
8
+ const sampleRate = audio.sampleRate;
9
+ let numFrames;
10
+ let numberOfChannels;
11
+ let channels;
12
+ if (audio instanceof AudioBuffer) {
13
+ channels = Arrays.create(index => audio.getChannelData(index), audio.numberOfChannels);
14
+ numFrames = audio.length;
15
+ numberOfChannels = audio.numberOfChannels;
16
+ }
17
+ else {
18
+ channels = audio.channels;
19
+ numFrames = audio.numFrames;
20
+ numberOfChannels = audio.channels.length;
21
+ }
22
+ const size = 44 + numFrames * numberOfChannels * bytesPerChannel;
23
+ const buf = new ArrayBuffer(size);
24
+ const view = new DataView(buf);
25
+ view.setUint32(0, MAGIC_RIFF, true);
26
+ view.setUint32(4, size - 8, true);
27
+ view.setUint32(8, MAGIC_WAVE, true);
28
+ view.setUint32(12, MAGIC_FMT, true);
29
+ view.setUint32(16, 16, true); // chunk length
30
+ view.setUint16(20, 3, true); // compression
31
+ view.setUint16(22, numberOfChannels, true);
32
+ view.setUint32(24, sampleRate, true);
33
+ view.setUint32(28, sampleRate * numberOfChannels * bytesPerChannel, true);
34
+ view.setUint16(32, numberOfChannels * bytesPerChannel, true);
35
+ view.setUint16(34, 8 * bytesPerChannel, true);
36
+ view.setUint32(36, MAGIC_DATA, true);
37
+ view.setUint32(40, numberOfChannels * numFrames * bytesPerChannel, true);
38
+ let w = 44;
39
+ for (let i = 0; i < numFrames; ++i) {
40
+ for (let j = 0; j < numberOfChannels; ++j) {
41
+ view.setFloat32(w, channels[j][i], true);
42
+ w += bytesPerChannel;
43
+ }
44
+ }
45
+ return view.buffer;
46
+ };
@@ -0,0 +1,8 @@
1
+ import { Func } from "@opendaw/lib-std";
2
+ export declare class WorkletFactory<W extends AudioWorkletNode> {
3
+ #private;
4
+ static boot<W extends AudioWorkletNode>(context: BaseAudioContext, moduleURL: string): Promise<WorkletFactory<W>>;
5
+ constructor(context: BaseAudioContext);
6
+ create(factory: Func<BaseAudioContext, W>): W;
7
+ }
8
+ //# sourceMappingURL=WorkletFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkletFactory.d.ts","sourceRoot":"","sources":["../src/WorkletFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAGrC,qBAAa,cAAc,CAAC,CAAC,SAAS,gBAAgB;;IAClD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;gBAM1E,OAAO,EAAE,gBAAgB;IAErC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,GAAG,CAAC;CAChD"}
@@ -0,0 +1,9 @@
1
+ import { Promises } from "@opendaw/lib-runtime";
2
+ export class WorkletFactory {
3
+ static boot(context, moduleURL) {
4
+ return Promises.retry(() => context.audioWorklet.addModule(moduleURL).then(() => new WorkletFactory(context)));
5
+ }
6
+ #context;
7
+ constructor(context) { this.#context = context; }
8
+ create(factory) { return factory(this.#context); }
9
+ }
@@ -0,0 +1,22 @@
1
+ import { int } from "@opendaw/lib-std";
2
+ import { ExportStemsConfiguration } from "@opendaw/studio-adapters";
3
+ import { WorkletFactory } from "./WorkletFactory";
4
+ import { EngineWorklet } from "./EngineWorklet";
5
+ import { MeterWorklet } from "./MeterWorklet";
6
+ import { RecordingWorklet } from "./RecordingWorklet";
7
+ import { Project } from "./Project";
8
+ export type WorkletUrls = {
9
+ meter: string;
10
+ engine: string;
11
+ recording: string;
12
+ };
13
+ export declare class Worklets {
14
+ #private;
15
+ static install(context: AudioContext, { engine, meter, recording }: WorkletUrls): Promise<Worklets>;
16
+ static get(context: AudioContext): Worklets;
17
+ constructor(engine: WorkletFactory<EngineWorklet>, meter: WorkletFactory<MeterWorklet>, recording: WorkletFactory<RecordingWorklet>);
18
+ createMeter(numberOfChannels: int): MeterWorklet;
19
+ createEngine(project: Project, exportConfiguration?: ExportStemsConfiguration): EngineWorklet;
20
+ createRecording(numberOfChannels: int, numChunks: int): RecordingWorklet;
21
+ }
22
+ //# sourceMappingURL=Worklets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Worklets.d.ts","sourceRoot":"","sources":["../src/Worklets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,wBAAwB,EAAC,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAEjC,MAAM,MAAM,WAAW,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,qBAAa,QAAQ;;WACJ,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYvG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ;gBAQ/B,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,EACrC,KAAK,EAAE,cAAc,CAAC,YAAY,CAAC,EACnC,SAAS,EAAE,cAAc,CAAC,gBAAgB,CAAC;IAMvD,WAAW,CAAC,gBAAgB,EAAE,GAAG,GAAG,YAAY;IAIhD,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,wBAAwB,GAAG,aAAa;IAI7F,eAAe,CAAC,gBAAgB,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,GAAG,gBAAgB;CAG3E"}
@@ -0,0 +1,36 @@
1
+ import { asDefined } from "@opendaw/lib-std";
2
+ import { EngineWorklet } from "./EngineWorklet";
3
+ import { MeterWorklet } from "./MeterWorklet";
4
+ import { RecordingWorklet } from "./RecordingWorklet";
5
+ export class Worklets {
6
+ static async install(context, { engine, meter, recording }) {
7
+ return Promise.all([
8
+ EngineWorklet.bootFactory(context, engine),
9
+ MeterWorklet.bootFactory(context, meter),
10
+ RecordingWorklet.bootFactory(context, recording)
11
+ ]).then(([engine, meter, recording]) => {
12
+ const worklets = new Worklets(engine, meter, recording);
13
+ this.#map.set(context, worklets);
14
+ return worklets;
15
+ });
16
+ }
17
+ static get(context) { return asDefined(this.#map.get(context), "Worklets not installed"); }
18
+ static #map = new WeakMap();
19
+ #meter;
20
+ #engine;
21
+ #recording;
22
+ constructor(engine, meter, recording) {
23
+ this.#meter = meter;
24
+ this.#engine = engine;
25
+ this.#recording = recording;
26
+ }
27
+ createMeter(numberOfChannels) {
28
+ return MeterWorklet.create(this.#meter, numberOfChannels);
29
+ }
30
+ createEngine(project, exportConfiguration) {
31
+ return this.#engine.create(context => new EngineWorklet(context, project, exportConfiguration));
32
+ }
33
+ createRecording(numberOfChannels, numChunks) {
34
+ return RecordingWorklet.create(this.#recording, numberOfChannels, numChunks);
35
+ }
36
+ }