@opendaw/studio-core 0.0.93 → 0.0.95

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 +1 @@
1
- {"version":3,"file":"EffectFactories.d.ts","sourceRoot":"","sources":["../src/EffectFactories.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAG7C,yBAAiB,eAAe,CAAC;IACtB,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,KAAK,EAAE,aAYnB,CAAA;IAEM,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,SAAS,EAAE,aAsBvB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,KAAK,EAAE,aAanB,CAAA;IAEM,MAAM,cAAc,EAAE,aAa5B,CAAA;IAEM,MAAM,SAAS,EAAE,aAavB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,MAAM,EAAE,aAcpB,CAAA;IAEM,MAAM,OAAO,EAAE,aAarB,CAAA;IAEM,MAAM,IAAI,EAAE,aAalB,CAAA;IAEM,MAAM,KAAK,EAAE,aAcnB,CAAA;IAEM,MAAM,MAAM,EAAE,aAapB,CAAA;IAEM,MAAM,OAAO,EAAE,aAqCrB,CAAA;IAEM,MAAM,SAAS;;;;;KAAyC,CAAA;IACxD,MAAM,UAAU;;;;;;;;;;;KAEtB,CAAA;IACM,MAAM,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA4B,CAAA;IACjF,MAAM,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA6B,CAAA;IACnF,MAAM,WAAW;;;;;;;;;;;;;;;KAAgC,CAAA;IACxD,KAAY,cAAc,GAAG,MAAM,OAAO,SAAS,CAAA;IACnD,KAAY,eAAe,GAAG,MAAM,OAAO,UAAU,CAAA;CACxD"}
1
+ {"version":3,"file":"EffectFactories.d.ts","sourceRoot":"","sources":["../src/EffectFactories.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAG7C,yBAAiB,eAAe,CAAC;IACtB,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,KAAK,EAAE,aAYnB,CAAA;IAEM,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,SAAS,EAAE,aAsBvB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,KAAK,EAAE,aAcnB,CAAA;IAEM,MAAM,cAAc,EAAE,aAa5B,CAAA;IAEM,MAAM,SAAS,EAAE,aAavB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,MAAM,EAAE,aAcpB,CAAA;IAEM,MAAM,OAAO,EAAE,aAarB,CAAA;IAEM,MAAM,IAAI,EAAE,aAalB,CAAA;IAEM,MAAM,KAAK,EAAE,aAcnB,CAAA;IAEM,MAAM,MAAM,EAAE,aAapB,CAAA;IAEM,MAAM,OAAO,EAAE,aAqCrB,CAAA;IAEM,MAAM,SAAS;;;;;KAAyC,CAAA;IACxD,MAAM,UAAU;;;;;;;;;;;KAEtB,CAAA;IACM,MAAM,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA4B,CAAA;IACjF,MAAM,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA6B,CAAA;IACnF,MAAM,WAAW;;;;;;;;;;;;;;;KAAgC,CAAA;IACxD,KAAY,cAAc,GAAG,MAAM,OAAO,SAAS,CAAA;IACnD,KAAY,eAAe,GAAG,MAAM,OAAO,UAAU,CAAA;CACxD"}
@@ -91,6 +91,7 @@ export var EffectFactories;
91
91
  box.label.setValue("Delay");
92
92
  box.index.setValue(index);
93
93
  box.host.refer(hostField);
94
+ box.version.setValue(1);
94
95
  })
95
96
  };
96
97
  EffectFactories.DattorroReverb = {
@@ -5,6 +5,8 @@ import { CaptureDevices } from "./CaptureDevices";
5
5
  export declare class CaptureAudio extends Capture<CaptureAudioBox> {
6
6
  #private;
7
7
  constructor(manager: CaptureDevices, audioUnitBox: AudioUnitBox, captureAudioBox: CaptureAudioBox);
8
+ get isMonitoring(): boolean;
9
+ set isMonitoring(value: boolean);
8
10
  get gainDb(): number;
9
11
  get stream(): MutableObservableOption<MediaStream>;
10
12
  get streamDeviceId(): Option<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"CaptureAudio.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,uBAAuB,EACvB,MAAM,EAEN,UAAU,EACb,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAC,YAAY,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAI/C,qBAAa,YAAa,SAAQ,OAAO,CAAC,eAAe,CAAC;;gBAQ1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;IA4BjG,IAAI,MAAM,IAAI,MAAM,CAAsB;IAE1C,IAAI,MAAM,IAAI,uBAAuB,CAAC,WAAW,CAAC,CAAsB;IAExE,IAAI,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC,CAEnC;IAED,IAAI,KAAK,IAAI,MAAM,CAAsE;IAEzF,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAA+D;IAEhG,IAAI,gBAAgB,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAE/C;IAEK,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC,cAAc,IAAI,UAAU;CA8D/B"}
1
+ {"version":3,"file":"CaptureAudio.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,uBAAuB,EAEvB,MAAM,EAEN,UAAU,EACb,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAC,YAAY,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAI/C,qBAAa,YAAa,SAAQ,OAAO,CAAC,eAAe,CAAC;;gBAW1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;IAiCjG,IAAI,YAAY,IAAI,OAAO,CAA4B;IACvD,IAAI,YAAY,CAAC,KAAK,EAAE,OAAO,EAS9B;IACD,IAAI,MAAM,IAAI,MAAM,CAAsB;IAC1C,IAAI,MAAM,IAAI,uBAAuB,CAAC,WAAW,CAAC,CAAsB;IACxE,IAAI,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC,CAEnC;IACD,IAAI,KAAK,IAAI,MAAM,CAAsE;IACzF,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAA+D;IAChG,IAAI,gBAAgB,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAE/C;IAEK,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC,cAAc,IAAI,UAAU;CAqF/B"}
@@ -1,4 +1,5 @@
1
1
  import { Errors, isDefined, isUndefined, MutableObservableOption, Option, RuntimeNotifier, Terminable } from "@opendaw/lib-std";
2
+ import { dbToGain } from "@opendaw/lib-dsp";
2
3
  import { Promises } from "@opendaw/lib-runtime";
3
4
  import { Capture } from "./Capture";
4
5
  import { RecordAudio } from "./RecordAudio";
@@ -6,8 +7,10 @@ import { AudioDevices } from "../AudioDevices";
6
7
  export class CaptureAudio extends Capture {
7
8
  #stream;
8
9
  #streamGenerator;
10
+ #isMonitoring = false;
9
11
  #requestChannels = Option.None;
10
12
  #gainDb = 0.0;
13
+ #monitor = null;
11
14
  constructor(manager, audioUnitBox, captureAudioBox) {
12
15
  super(manager, audioUnitBox, captureAudioBox);
13
16
  this.#stream = new MutableObservableOption();
@@ -15,7 +18,12 @@ export class CaptureAudio extends Capture {
15
18
  this.ownAll(captureAudioBox.requestChannels.catchupAndSubscribe(owner => {
16
19
  const channels = owner.getValue();
17
20
  this.#requestChannels = channels === 1 || channels === 2 ? Option.wrap(channels) : Option.None;
18
- }), captureAudioBox.gainDb.catchupAndSubscribe(owner => this.#gainDb = owner.getValue()), captureAudioBox.deviceId.catchupAndSubscribe(async () => {
21
+ }), captureAudioBox.gainDb.catchupAndSubscribe(owner => {
22
+ this.#gainDb = owner.getValue();
23
+ if (isDefined(this.#monitor)) {
24
+ this.#monitor.gainNode.gain.value = dbToGain(this.#gainDb);
25
+ }
26
+ }), captureAudioBox.deviceId.catchupAndSubscribe(async () => {
19
27
  if (this.armed.getValue()) {
20
28
  await this.#streamGenerator();
21
29
  }
@@ -29,6 +37,20 @@ export class CaptureAudio extends Capture {
29
37
  }
30
38
  }));
31
39
  }
40
+ get isMonitoring() { return this.#isMonitoring; }
41
+ set isMonitoring(value) {
42
+ if (this.#isMonitoring === value) {
43
+ return;
44
+ }
45
+ this.#isMonitoring = value;
46
+ if (this.#isMonitoring) {
47
+ this.armed.setValue(true);
48
+ this.#stream.ifSome(stream => this.#connectMonitoring(stream));
49
+ }
50
+ else {
51
+ this.#disconnectMonitoring();
52
+ }
53
+ }
32
54
  get gainDb() { return this.#gainDb; }
33
55
  get stream() { return this.#stream; }
34
56
  get streamDeviceId() {
@@ -106,6 +128,9 @@ export class CaptureAudio extends Capture {
106
128
  console.debug(`new stream. device requested: ${deviceId ?? "default"}, got: ${gotDeviceId ?? "unknown"}. channelCount requested: ${channelCount}, got: ${settings?.channelCount}`);
107
129
  if (isUndefined(deviceId) || deviceId === gotDeviceId) {
108
130
  this.#stream.wrap(stream);
131
+ if (this.#isMonitoring) {
132
+ this.#connectMonitoring(stream);
133
+ }
109
134
  }
110
135
  else {
111
136
  stream.getAudioTracks().forEach(track => track.stop());
@@ -114,6 +139,24 @@ export class CaptureAudio extends Capture {
114
139
  });
115
140
  }
116
141
  #stopStream() {
142
+ this.#disconnectMonitoring();
117
143
  this.#stream.clear(stream => stream.getAudioTracks().forEach(track => track.stop()));
118
144
  }
145
+ #connectMonitoring(stream) {
146
+ this.#disconnectMonitoring();
147
+ const { audioContext } = this.manager.project.env;
148
+ const sourceNode = audioContext.createMediaStreamSource(stream);
149
+ const gainNode = audioContext.createGain();
150
+ gainNode.gain.value = dbToGain(this.#gainDb);
151
+ sourceNode.connect(gainNode);
152
+ gainNode.connect(audioContext.destination);
153
+ this.#monitor = { sourceNode, gainNode };
154
+ }
155
+ #disconnectMonitoring() {
156
+ if (isDefined(this.#monitor)) {
157
+ const { sourceNode, gainNode } = this.#monitor;
158
+ sourceNode.disconnect();
159
+ gainNode.disconnect();
160
+ }
161
+ }
119
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RecordMidi.d.ts","sourceRoot":"","sources":["../../src/capture/RecordMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,QAAQ,EAAuC,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGrI,OAAO,EAAa,UAAU,EAA2B,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,UAAU,CAAC;IACxB,KAAK,iBAAiB,GAAG;QACrB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAA;IAgBD,MAAM,CAAC,MAAM,KAAK,GAAI,gCAA8B,iBAAiB,KAAG,UAgJvE,CAAA;;CACJ"}
1
+ {"version":3,"file":"RecordMidi.d.ts","sourceRoot":"","sources":["../../src/capture/RecordMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,QAAQ,EAAuC,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGrI,OAAO,EAAa,UAAU,EAA2B,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,UAAU,CAAC;IACxB,KAAK,iBAAiB,GAAG;QACrB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAA;IAgBD,MAAM,CAAC,MAAM,KAAK,GAAI,gCAA8B,iBAAiB,KAAG,UAkJvE,CAAA;;CACJ"}
@@ -91,12 +91,14 @@ export var RecordMidi;
91
91
  const loopEnabled = loopArea.enabled.getValue();
92
92
  const loopFrom = loopArea.from.getValue();
93
93
  const loopTo = loopArea.to.getValue();
94
- const loopDurationPPQN = loopTo - loopFrom;
95
94
  const allowTakes = project.engine.preferences.settings.recording.allowTakes;
96
95
  if (loopEnabled && allowTakes && currentTake.nonEmpty() && currentPosition < lastPosition) {
97
- positionOffset += loopDurationPPQN;
98
96
  editing.modify(() => {
99
- currentTake.ifSome(take => finalizeTake(take, loopDurationPPQN));
97
+ currentTake.ifSome(take => {
98
+ const actualDurationPPQN = take.regionBox.duration.getValue();
99
+ finalizeTake(take, actualDurationPPQN);
100
+ positionOffset += actualDurationPPQN;
101
+ });
100
102
  startNewTake(loopFrom);
101
103
  }, false);
102
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DawProjectExporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.ts"],"names":[],"mappings":"AAKA,OAAO,EAaH,mBAAmB,EAKnB,aAAa,EAUhB,MAAM,yBAAyB,CAAA;AAahC,OAAO,EAA6B,eAAe,EAAE,mBAAmB,EAAC,MAAM,0BAA0B,CAAA;AAKzG,yBAAiB,kBAAkB,CAAC;IAChC,UAAiB,cAAc;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAAA;KAAC;IAE5F,MAAM,KAAK,GAAI,UAAU,eAAe,EAAE,eAAe,mBAAmB,EAAE,gBAAgB,cAAc,kBAoPlH,CAAA;CACJ"}
1
+ {"version":3,"file":"DawProjectExporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.ts"],"names":[],"mappings":"AAKA,OAAO,EAcH,mBAAmB,EAMnB,aAAa,EAahB,MAAM,yBAAyB,CAAA;AAgBhC,OAAO,EAAwD,eAAe,EAAE,mBAAmB,EAAC,MAAM,0BAA0B,CAAA;AAKpI,yBAAiB,kBAAkB,CAAC;IAChC,UAAiB,cAAc;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAAA;KAAC;IAE5F,MAAM,KAAK,GAAI,UAAU,eAAe,EAAE,eAAe,mBAAmB,EAAE,gBAAgB,cAAc,kBAqTlH,CAAA;CACJ"}
@@ -3,10 +3,10 @@ import { Xml } from "@opendaw/lib-xml";
3
3
  import { dbToGain, PPQN } from "@opendaw/lib-dsp";
4
4
  import { AddressIdEncoder, BooleanField } from "@opendaw/lib-box";
5
5
  import { Html } from "@opendaw/lib-dom";
6
- 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";
6
+ import { ApplicationSchema, ArrangementSchema, AudioAlgorithm, AudioSchema, AutomationTargetSchema, BuiltinDeviceSchema, ChannelRole, ChannelSchema, ClipSchema, ClipsSchema, DeviceRole, FileReferenceSchema, LanesSchema, NoteSchema, NotesSchema, ParameterEncoder, PointsSchema, ProjectSchema, RealParameterSchema, RealPointSchema, TempoAutomationConverter, TimeSignatureParameterSchema, TimeSignaturePointSchema, TimeUnit, TrackSchema, TransportSchema, Unit, WarpSchema, WarpsSchema } from "@opendaw/lib-dawproject";
7
7
  import { AudioUnitType } from "@opendaw/studio-enums";
8
- import { AudioFileBox, AudioUnitBox, NoteEventBox, NoteEventCollectionBox, TrackBox } from "@opendaw/studio-boxes";
9
- import { ColorCodes, DeviceBoxUtils } from "@opendaw/studio-adapters";
8
+ import { AudioFileBox, AudioUnitBox, NoteEventBox, NoteEventCollectionBox, SignatureEventBox, TrackBox, ValueEventBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
9
+ import { ColorCodes, DeviceBoxUtils, InterpolationFieldAdapter } from "@opendaw/studio-adapters";
10
10
  import { AudioUnitExportLayout } from "./AudioUnitExportLayout";
11
11
  import { DeviceIO } from "./DeviceIO";
12
12
  import { WavFile } from "../WavFile";
@@ -205,6 +205,77 @@ export var DawProjectExporter;
205
205
  content: []
206
206
  }, ClipSchema)]
207
207
  }, ClipsSchema);
208
+ const writeTempoAutomation = () => {
209
+ const { tempoTrack } = timelineBox;
210
+ if (!tempoTrack.enabled.getValue()) {
211
+ return undefined;
212
+ }
213
+ const targetVertex = tempoTrack.events.targetVertex;
214
+ if (targetVertex.isEmpty()) {
215
+ return undefined;
216
+ }
217
+ const collectionBox = asInstanceOf(targetVertex.unwrap().box, ValueEventCollectionBox);
218
+ const events = collectionBox.events.pointerHub.incoming()
219
+ .map(({ box }) => asInstanceOf(box, ValueEventBox))
220
+ .sort((eventA, eventB) => eventA.position.getValue() - eventB.position.getValue());
221
+ if (events.length === 0) {
222
+ return undefined;
223
+ }
224
+ const minBpm = tempoTrack.minBpm.getValue();
225
+ const maxBpm = tempoTrack.maxBpm.getValue();
226
+ const tempoParameterId = ids.getOrCreate(timelineBox.bpm.address);
227
+ return Xml.element({
228
+ unit: Unit.BPM,
229
+ target: Xml.element({
230
+ parameter: tempoParameterId
231
+ }, AutomationTargetSchema),
232
+ points: events.map(event => {
233
+ const interpolation = InterpolationFieldAdapter.read(event.interpolation);
234
+ return Xml.element({
235
+ time: event.position.getValue() / PPQN.Quarter,
236
+ value: TempoAutomationConverter.normalizedToBpm(event.value.getValue(), minBpm, maxBpm),
237
+ interpolation: TempoAutomationConverter.toDawProjectInterpolation(interpolation)
238
+ }, RealPointSchema);
239
+ })
240
+ }, PointsSchema);
241
+ };
242
+ const writeSignatureAutomation = () => {
243
+ const { signatureTrack, signature } = timelineBox;
244
+ if (!signatureTrack.enabled.getValue()) {
245
+ return undefined;
246
+ }
247
+ const events = signatureTrack.events.pointerHub.incoming()
248
+ .map(({ box }) => asInstanceOf(box, SignatureEventBox))
249
+ .sort((eventA, eventB) => eventA.index.getValue() - eventB.index.getValue());
250
+ if (events.length === 0) {
251
+ return undefined;
252
+ }
253
+ // Calculate absolute positions for each event
254
+ const points = [];
255
+ let accumulatedPpqn = 0;
256
+ let currentNominator = signature.nominator.getValue();
257
+ let currentDenominator = signature.denominator.getValue();
258
+ // Add the initial signature at time 0
259
+ points.push(Xml.element({
260
+ time: 0,
261
+ numerator: currentNominator,
262
+ denominator: currentDenominator
263
+ }, TimeSignaturePointSchema));
264
+ for (const event of events) {
265
+ const barDuration = PPQN.fromSignature(currentNominator, currentDenominator);
266
+ accumulatedPpqn += barDuration * event.relativePosition.getValue();
267
+ currentNominator = event.nominator.getValue();
268
+ currentDenominator = event.denominator.getValue();
269
+ points.push(Xml.element({
270
+ time: accumulatedPpqn / PPQN.Quarter,
271
+ numerator: currentNominator,
272
+ denominator: currentDenominator
273
+ }, TimeSignaturePointSchema));
274
+ }
275
+ return Xml.element({
276
+ points
277
+ }, PointsSchema);
278
+ };
208
279
  const writeLanes = () => {
209
280
  return audioUnits
210
281
  .flatMap(audioUnitBox => audioUnitBox.tracks.pointerHub.incoming()
@@ -230,6 +301,8 @@ export var DawProjectExporter;
230
301
  transport: writeTransport(),
231
302
  structure: writeStructure(),
232
303
  arrangement: Xml.element({
304
+ tempoAutomation: writeTempoAutomation(),
305
+ timeSignatureAutomation: writeSignatureAutomation(),
233
306
  lanes: Xml.element({
234
307
  lanes: writeLanes(),
235
308
  timeUnit: TimeUnit.BEATS
@@ -1 +1 @@
1
- {"version":3,"file":"DawProjectImporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,EAOhB,MAAM,yBAAyB,CAAA;AAyBhC,OAAO,EAMH,eAAe,EAElB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAIvC,yBAAiB,gBAAgB,CAAC;IAW9B,KAAY,MAAM,GAAG;QACjB,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,QAAQ,EAAE,eAAe,CAAA;KAC5B,CAAA;IAOM,MAAM,IAAI,GAAU,QAAQ,aAAa,EAAE,WAAW,UAAU,CAAC,gBAAgB,KAAG,OAAO,CAAC,MAAM,CAyYxG,CAAA;CAaJ"}
1
+ {"version":3,"file":"DawProjectImporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,EAUhB,MAAM,yBAAyB,CAAA;AA2BhC,OAAO,EAOH,eAAe,EAElB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAIvC,yBAAiB,gBAAgB,CAAC;IAW9B,KAAY,MAAM,GAAG;QACjB,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,QAAQ,EAAE,eAAe,CAAA;KAC5B,CAAA;IAOM,MAAM,IAAI,GAAU,QAAQ,aAAa,EAAE,WAAW,UAAU,CAAC,gBAAgB,KAAG,OAAO,CAAC,MAAM,CAycxG,CAAA;CAaJ"}
@@ -1,10 +1,10 @@
1
1
  import { ArrayMultimap, asDefined, asInstanceOf, assert, Color, ifDefined, isDefined, isInstanceOf, isUndefined, NumberComparator, Option, panic, UUID, ValueMapping } from "@opendaw/lib-std";
2
2
  import { BoxGraph } from "@opendaw/lib-box";
3
3
  import { gainToDb, PPQN } from "@opendaw/lib-dsp";
4
- import { ChannelRole, ClipsSchema, DeviceRole, EqualizerSchema, LanesSchema, NotesSchema, PointsSchema, SendType, TrackSchema, WarpsSchema } from "@opendaw/lib-dawproject";
4
+ import { ChannelRole, ClipsSchema, DeviceRole, EqualizerSchema, LanesSchema, NotesSchema, PointsSchema, RealPointSchema, SendType, TempoAutomationConverter, TimeSignaturePointSchema, TrackSchema, WarpsSchema } from "@opendaw/lib-dawproject";
5
5
  import { AudioSendRouting, AudioUnitType, IconSymbol } from "@opendaw/studio-enums";
6
- import { AudioBusBox, AudioFileBox, AudioPitchStretchBox, AudioRegionBox, AudioUnitBox, AuxSendBox, BoxIO, CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, RootBox, TimelineBox, TrackBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, UserInterfaceBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
7
- import { AudioUnitOrdering, ColorCodes, InstrumentFactories, TrackType } from "@opendaw/studio-adapters";
6
+ import { AudioBusBox, AudioFileBox, AudioPitchStretchBox, AudioRegionBox, AudioUnitBox, AuxSendBox, BoxIO, CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, RootBox, SignatureEventBox, TimelineBox, TrackBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, UserInterfaceBox, ValueEventBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
7
+ import { AudioUnitOrdering, ColorCodes, InstrumentFactories, InterpolationFieldAdapter, TrackType } from "@opendaw/studio-adapters";
8
8
  import { BuiltinDevices } from "./BuiltinDevices";
9
9
  import { AudioContentHelpers } from "../project/audio/AudioContentHelpers";
10
10
  export var DawProjectImport;
@@ -344,6 +344,74 @@ export var DawProjectImport;
344
344
  box.playMode.refer(pitchStretch);
345
345
  });
346
346
  };
347
+ const readTempoAutomation = (tempoAutomation) => {
348
+ const points = tempoAutomation.points;
349
+ if (!isDefined(points) || points.length === 0) {
350
+ return;
351
+ }
352
+ const minBpm = timelineBox.tempoTrack.minBpm.getValue();
353
+ const maxBpm = timelineBox.tempoTrack.maxBpm.getValue();
354
+ const collectionBox = ValueEventCollectionBox.create(boxGraph, UUID.generate());
355
+ timelineBox.tempoTrack.events.refer(collectionBox.owners);
356
+ timelineBox.tempoTrack.enabled.setValue(true);
357
+ points
358
+ .filter((point) => isInstanceOf(point, RealPointSchema))
359
+ .forEach(point => {
360
+ const eventBox = ValueEventBox.create(boxGraph, UUID.generate(), box => {
361
+ box.position.setValue(point.time * PPQN.Quarter);
362
+ box.index.setValue(0);
363
+ box.value.setValue(TempoAutomationConverter.bpmToNormalized(point.value, minBpm, maxBpm));
364
+ box.events.refer(collectionBox.events);
365
+ });
366
+ const interpolation = TempoAutomationConverter.fromDawProjectInterpolation(point.interpolation);
367
+ InterpolationFieldAdapter.write(eventBox.interpolation, interpolation);
368
+ });
369
+ };
370
+ ifDefined(arrangement.tempoAutomation, readTempoAutomation);
371
+ const readSignatureAutomation = (signatureAutomation) => {
372
+ const points = signatureAutomation.points;
373
+ if (!isDefined(points) || points.length === 0) {
374
+ return;
375
+ }
376
+ const signaturePoints = points
377
+ .filter((point) => isInstanceOf(point, TimeSignaturePointSchema))
378
+ .sort((pointA, pointB) => pointA.time - pointB.time);
379
+ if (signaturePoints.length === 0) {
380
+ return;
381
+ }
382
+ // First point at or near time 0 sets the base signature
383
+ const firstPoint = signaturePoints[0];
384
+ const firstPointIsAtStart = Number(firstPoint.time) < 0.001;
385
+ if (firstPointIsAtStart) {
386
+ timelineBox.signature.nominator.setValue(firstPoint.numerator);
387
+ timelineBox.signature.denominator.setValue(firstPoint.denominator);
388
+ }
389
+ // Remaining points become SignatureEventBox instances
390
+ const eventsToCreate = firstPointIsAtStart ? signaturePoints.slice(1) : signaturePoints;
391
+ if (eventsToCreate.length === 0) {
392
+ return;
393
+ }
394
+ timelineBox.signatureTrack.enabled.setValue(true);
395
+ let prevPpqn = 0;
396
+ let prevNominator = timelineBox.signature.nominator.getValue();
397
+ let prevDenominator = timelineBox.signature.denominator.getValue();
398
+ eventsToCreate.forEach((point, index) => {
399
+ const currentPpqn = point.time * PPQN.Quarter;
400
+ const barDuration = PPQN.fromSignature(prevNominator, prevDenominator);
401
+ const relativePosition = Math.max(1, Math.round((currentPpqn - prevPpqn) / barDuration));
402
+ SignatureEventBox.create(boxGraph, UUID.generate(), box => {
403
+ box.index.setValue(index);
404
+ box.relativePosition.setValue(relativePosition);
405
+ box.nominator.setValue(point.numerator);
406
+ box.denominator.setValue(point.denominator);
407
+ box.events.refer(timelineBox.signatureTrack.events);
408
+ });
409
+ prevPpqn = prevPpqn + relativePosition * barDuration;
410
+ prevNominator = point.numerator;
411
+ prevDenominator = point.denominator;
412
+ });
413
+ };
414
+ ifDefined(arrangement.timeSignatureAutomation, readSignatureAutomation);
347
415
  return Promise.all(arrangement?.lanes?.lanes?.filter(timeline => isInstanceOf(timeline, LanesSchema))
348
416
  .map(readLane) ?? []);
349
417
  };