@opendaw/studio-core 0.0.45 → 0.0.47

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,6 +1,6 @@
1
- import { Func, Procedure, Terminable, TerminableOwner, Terminator } from "@opendaw/lib-std";
1
+ import { Func, Procedure, Terminable, TerminableOwner, Terminator, UUID } from "@opendaw/lib-std";
2
2
  import { BoxEditing, BoxGraph } from "@opendaw/lib-box";
3
- import { AudioBusBox, AudioUnitBox, BoxIO, RootBox, TimelineBox, UserInterfaceBox } from "@opendaw/studio-boxes";
3
+ import { AudioBusBox, AudioUnitBox, BoxIO, MIDIOutputDeviceBox, RootBox, TimelineBox, UserInterfaceBox } from "@opendaw/studio-boxes";
4
4
  import { BoxAdapters, BoxAdaptersContext, ClipSequencing, ParameterFieldAdapters, ProcessorOptions, ProjectSkeleton, RootBoxAdapter, SampleLoaderManager, SoundfontLoaderManager, TimelineBoxAdapter, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
5
5
  import { LiveStreamBroadcaster, LiveStreamReceiver } from "@opendaw/lib-fusion";
6
6
  import { ProjectEnv } from "./ProjectEnv";
@@ -10,6 +10,7 @@ import { CaptureDevices } from "../capture";
10
10
  import { EngineFacade } from "../EngineFacade";
11
11
  import { EngineWorklet } from "../EngineWorklet";
12
12
  import { MIDILearning } from "../midi";
13
+ import { ppqn } from "@opendaw/lib-dsp";
13
14
  export type RestartWorklet = {
14
15
  unload: Func<unknown, Promise<unknown>>;
15
16
  load: Procedure<EngineWorklet>;
@@ -21,7 +22,7 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
21
22
  #private;
22
23
  static new(env: ProjectEnv, options?: ProjectCreateOptions): Project;
23
24
  static load(env: ProjectEnv, arrayBuffer: ArrayBuffer): Project;
24
- static skeleton(env: ProjectEnv, skeleton: ProjectSkeleton): Project;
25
+ static skeleton(env: ProjectEnv, skeleton: ProjectSkeleton, followFirstUser?: boolean): Project;
25
26
  readonly boxGraph: BoxGraph<BoxIO.TypeMap>;
26
27
  readonly rootBox: RootBox;
27
28
  readonly userInterfaceBoxes: ReadonlyArray<UserInterfaceBox>;
@@ -56,10 +57,15 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
56
57
  get isAudioContext(): boolean;
57
58
  get isMainThread(): boolean;
58
59
  get liveStreamBroadcaster(): LiveStreamBroadcaster;
60
+ get signatureDuration(): ppqn;
59
61
  get skeleton(): ProjectSkeleton;
62
+ collectSampleUUIDs(): ReadonlyArray<UUID.Bytes>;
60
63
  toArrayBuffer(): ArrayBufferLike;
61
64
  copy(env?: Partial<ProjectEnv>): Project;
62
65
  invalid(): boolean;
66
+ connectMIDIOutput(box: MIDIOutputDeviceBox, device: MIDIOutput): void;
67
+ disconnectMIDIOutput(uuid: UUID.Bytes): void;
68
+ receivedMIDIFromEngine(target: UUID.Bytes, data: Uint8Array, relativeTimeInMs: number): void;
63
69
  terminate(): void;
64
70
  }
65
71
  //# sourceMappingURL=Project.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../src/project/Project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../src/project/Project.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Arrays, ByteArrayOutput, Option, panic, safeExecute, Terminator, UUID } from "@opendaw/lib-std";
1
+ import { Arrays, ByteArrayOutput, isDefined, Option, panic, safeExecute, Terminator, UUID } from "@opendaw/lib-std";
2
2
  import { BoxEditing, BoxGraph } from "@opendaw/lib-box";
3
3
  import { AudioBusBox, AudioUnitBox, BoxIO, CompressorDeviceBox, GrooveShuffleBox, RootBox, TimelineBox, UserInterfaceBox } from "@opendaw/studio-boxes";
4
4
  import { BoxAdapters, IconSymbol, ParameterFieldAdapters, ProjectDecoder, RootBoxAdapter, TimelineBoxAdapter, UnionBoxTypes, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
@@ -12,6 +12,7 @@ import { EngineFacade } from "../EngineFacade";
12
12
  import { MIDILearning } from "../midi";
13
13
  import { ProjectValidation } from "./ProjectValidation";
14
14
  import { Preferences } from "../Preferences";
15
+ import { PPQN } from "@opendaw/lib-dsp";
15
16
  // Main Entry Point for a Project
16
17
  export class Project {
17
18
  static new(env, options) {
@@ -51,27 +52,36 @@ export class Project {
51
52
  rootBox.timeline.refer(timelineBox.root);
52
53
  primaryAudioBus.output.refer(primaryAudioOutputUnit.input);
53
54
  const userInterfaceBoxes = [];
54
- if (options?.noDefaultUser !== true) {
55
+ const createDefaultUser = options?.noDefaultUser !== true;
56
+ if (createDefaultUser) {
55
57
  const userInterfaceBox = UserInterfaceBox.create(boxGraph, UUID.generate());
56
58
  userInterfaceBox.root.refer(rootBox.users);
57
59
  userInterfaceBoxes.push(userInterfaceBox);
58
60
  }
59
61
  boxGraph.endTransaction();
60
- return new Project(env, boxGraph, {
62
+ const project = new Project(env, boxGraph, {
61
63
  rootBox,
62
64
  primaryAudioBus,
63
65
  primaryAudioOutputUnit,
64
66
  userInterfaceBoxes,
65
67
  timelineBox
66
68
  });
69
+ if (createDefaultUser) {
70
+ project.follow(userInterfaceBoxes[0]);
71
+ }
72
+ return project;
67
73
  }
68
74
  static load(env, arrayBuffer) {
69
75
  return this.skeleton(env, ProjectDecoder.decode(arrayBuffer));
70
76
  }
71
- static skeleton(env, skeleton) {
77
+ static skeleton(env, skeleton, followFirstUser = true) {
72
78
  ProjectMigration.migrate(skeleton);
73
79
  ProjectValidation.validate(skeleton);
74
- return new Project(env, skeleton.boxGraph, skeleton.mandatoryBoxes);
80
+ const project = new Project(env, skeleton.boxGraph, skeleton.mandatoryBoxes);
81
+ if (followFirstUser) {
82
+ project.follow(project.userInterfaceBoxes[0]);
83
+ }
84
+ return project;
75
85
  }
76
86
  #terminator = new Terminator();
77
87
  #env;
@@ -92,6 +102,7 @@ export class Project {
92
102
  midiLearning;
93
103
  mixer;
94
104
  engine = new EngineFacade();
105
+ #midiOutputs = UUID.newSet(({ box: { address: { uuid } } }) => uuid);
95
106
  constructor(env, boxGraph, { rootBox, userInterfaceBoxes, primaryAudioBus, primaryAudioOutputUnit, timelineBox }) {
96
107
  this.#env = env;
97
108
  this.boxGraph = boxGraph;
@@ -111,7 +122,7 @@ export class Project {
111
122
  this.captureDevices = this.#terminator.own(new CaptureDevices(this));
112
123
  this.mixer = new Mixer(this.rootBoxAdapter.audioUnits);
113
124
  // TODO We are probably doing that from the outside
114
- if (this.userInterfaceBoxes.length > 0) {
125
+ if (this.userInterfaceBoxes.length === 1) {
115
126
  this.follow(this.userInterfaceBoxes[0]);
116
127
  }
117
128
  console.debug(`Project was created on ${this.rootBoxAdapter.created.toString()}`);
@@ -159,6 +170,10 @@ export class Project {
159
170
  get isAudioContext() { return false; }
160
171
  get isMainThread() { return true; }
161
172
  get liveStreamBroadcaster() { return panic("Only available in audio context"); }
173
+ get signatureDuration() {
174
+ const { nominator, denominator } = this.timelineBox.signature;
175
+ return PPQN.fromSignature(nominator.getValue(), denominator.getValue());
176
+ }
162
177
  get skeleton() {
163
178
  return {
164
179
  boxGraph: this.boxGraph,
@@ -171,6 +186,11 @@ export class Project {
171
186
  }
172
187
  };
173
188
  }
189
+ collectSampleUUIDs() {
190
+ return this.boxGraph.boxes()
191
+ .filter(box => box.accept({ visitAudioFileBox: (_box) => true }))
192
+ .map(box => box.address.uuid);
193
+ }
174
194
  toArrayBuffer() {
175
195
  const output = ByteArrayOutput.create();
176
196
  output.writeInt(ProjectDecoder.MAGIC_HEADER_OPEN);
@@ -198,6 +218,27 @@ export class Project {
198
218
  }
199
219
  }) ?? false);
200
220
  }
221
+ connectMIDIOutput(box, device) {
222
+ console.debug("connectMIDIOutput", UUID.toString(box.address.uuid), device.name, device.manufacturer);
223
+ this.disconnectMIDIOutput(box.address.uuid);
224
+ this.#midiOutputs.add({
225
+ box, device, subscription: box.host.subscribe(pointer => {
226
+ if (pointer.isEmpty()) {
227
+ this.disconnectMIDIOutput(box.address.uuid);
228
+ }
229
+ })
230
+ });
231
+ }
232
+ disconnectMIDIOutput(uuid) {
233
+ const entry = this.#midiOutputs.removeByKeyIfExist(uuid);
234
+ if (isDefined(entry)) {
235
+ console.debug("disconnectMIDIOutput", UUID.toString(uuid));
236
+ entry.subscription.terminate();
237
+ }
238
+ }
239
+ receivedMIDIFromEngine(target, data, relativeTimeInMs) {
240
+ this.#midiOutputs.opt(target).ifSome(({ box, device }) => device.send(data, performance.now() + relativeTimeInMs + box.delay.getValue()));
241
+ }
201
242
  terminate() {
202
243
  console.debug("Project terminated");
203
244
  this.#terminator.terminate();
@@ -139,7 +139,7 @@ export class ProjectApi {
139
139
  box.hue.setValue(hue ?? ColorCodes.forTrackType(type));
140
140
  box.mute.setValue(false);
141
141
  box.duration.setValue(duration);
142
- box.loopDuration.setValue(PPQN.Bar);
142
+ box.loopDuration.setValue(this.#project.signatureDuration);
143
143
  box.events.refer(events.owners);
144
144
  box.regions.refer(trackBox.regions);
145
145
  }));
@@ -0,0 +1,19 @@
1
+ import { int } from "@opendaw/lib-std";
2
+ import { TimelineRange } from "./TimelineRange";
3
+ export declare namespace TimeGrid {
4
+ type Signature = [int, int];
5
+ type Options = {
6
+ minLength?: number;
7
+ };
8
+ type Fragment = {
9
+ bars: int;
10
+ beats: int;
11
+ ticks: int;
12
+ isBar: boolean;
13
+ isBeat: boolean;
14
+ pulse: number;
15
+ };
16
+ type Designer = (fragment: Fragment) => void;
17
+ const fragment: ([nominator, denominator]: Signature, range: TimelineRange, designer: Designer, options?: Options) => void;
18
+ }
19
+ //# sourceMappingURL=TimeGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimeGrid.d.ts","sourceRoot":"","sources":["../../src/ui/TimeGrid.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAgB,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAE7C,yBAAiB,QAAQ,CAAC;IACtB,KAAY,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAClC,KAAY,OAAO,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5C,KAAY,QAAQ,GAAG;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5G,KAAY,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;IAE5C,MAAM,QAAQ,GAAI,0BAA0B,SAAS,EACnC,OAAO,aAAa,EAAE,UAAU,QAAQ,EAAE,UAAU,OAAO,KAAG,IA4DtF,CAAA;CACJ"}
@@ -0,0 +1,74 @@
1
+ import { PPQN } from "@opendaw/lib-dsp";
2
+ import { quantizeFloor } from "@opendaw/lib-std";
3
+ export var TimeGrid;
4
+ (function (TimeGrid) {
5
+ TimeGrid.fragment = ([nominator, denominator], range, designer, options) => {
6
+ const unitsPerPixel = range.unitsPerPixel;
7
+ if (unitsPerPixel <= 0) {
8
+ return;
9
+ }
10
+ const barPulses = PPQN.fromSignature(nominator, denominator);
11
+ const beatPulses = PPQN.fromSignature(1, denominator);
12
+ const minLength = options?.minLength ?? 48;
13
+ let interval = barPulses;
14
+ let pixel = interval / unitsPerPixel;
15
+ if (pixel > minLength) {
16
+ // scaling down the interval until we hit the minimum length
17
+ while (pixel > minLength) {
18
+ if (interval > barPulses) {
19
+ // Above bar level: divide by 2
20
+ interval /= 2;
21
+ }
22
+ else if (interval > beatPulses) {
23
+ // Between beat and bar level: divide by nominator
24
+ interval /= nominator;
25
+ }
26
+ else {
27
+ // Below beat level: divide by 2
28
+ interval /= 2;
29
+ }
30
+ pixel = interval / unitsPerPixel;
31
+ }
32
+ }
33
+ if (pixel < minLength) {
34
+ // scaling up the interval until we hit the minimum length
35
+ while (pixel < minLength) {
36
+ if (interval < beatPulses) {
37
+ // Below beat level: multiply by 2
38
+ const nextInterval = interval * 2;
39
+ // If doubling exceeds beat level, jump to beat level instead
40
+ if (nextInterval > beatPulses) {
41
+ interval = beatPulses;
42
+ }
43
+ else {
44
+ interval = nextInterval;
45
+ }
46
+ }
47
+ else if (interval < barPulses) {
48
+ // Between beat and bar level: multiply by nominator
49
+ const nextInterval = interval * nominator;
50
+ // If multiplying exceeds bar level, jump to bar level instead
51
+ if (nextInterval > barPulses) {
52
+ interval = barPulses;
53
+ }
54
+ else {
55
+ interval = nextInterval;
56
+ }
57
+ }
58
+ else {
59
+ // At or above bar level: multiply by 2
60
+ interval *= 2;
61
+ }
62
+ pixel = interval / unitsPerPixel;
63
+ }
64
+ }
65
+ const p0 = quantizeFloor(range.unitMin, interval);
66
+ const p1 = range.unitMax;
67
+ for (let pulse = p0; pulse < p1; pulse += interval) {
68
+ const { bars, beats, semiquavers, ticks } = PPQN.toParts(pulse, nominator, denominator);
69
+ const isBeat = ticks === 0 && semiquavers === 0;
70
+ const isBar = isBeat && beats === 0;
71
+ designer({ bars, beats, ticks, isBar, isBeat, pulse });
72
+ }
73
+ };
74
+ })(TimeGrid || (TimeGrid = {}));
@@ -1,2 +1,3 @@
1
+ export * from "./TimeGrid";
1
2
  export * from "./TimelineRange";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA"}
package/dist/ui/index.js CHANGED
@@ -1 +1,2 @@
1
+ export * from "./TimeGrid";
1
2
  export * from "./TimelineRange";