@opendaw/studio-core 0.0.38 → 0.0.40

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 (52) hide show
  1. package/dist/AudioOfflineRenderer.d.ts.map +1 -1
  2. package/dist/AudioOfflineRenderer.js +4 -1
  3. package/dist/InstrumentFactories.d.ts.map +1 -1
  4. package/dist/InstrumentFactories.js +15 -12
  5. package/dist/capture/CaptureMidi.d.ts.map +1 -1
  6. package/dist/capture/CaptureMidi.js +9 -16
  7. package/dist/capture/RecordAudio.d.ts.map +1 -1
  8. package/dist/capture/RecordAudio.js +2 -1
  9. package/dist/capture/Recording.d.ts.map +1 -1
  10. package/dist/capture/Recording.js +1 -2
  11. package/dist/clouds/CloudAuthManager.js +2 -2
  12. package/dist/dawproject/DawProjectImport.d.ts +2 -2
  13. package/dist/dawproject/DawProjectImport.d.ts.map +1 -1
  14. package/dist/dawproject/DawProjectImport.js +4 -1
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -0
  18. package/dist/midi/MidiDevices.d.ts +1 -1
  19. package/dist/midi/MidiDevices.d.ts.map +1 -1
  20. package/dist/midi/MidiDevices.js +3 -3
  21. package/dist/midi/SoftwareMIDIInput.d.ts +16 -9
  22. package/dist/midi/SoftwareMIDIInput.d.ts.map +1 -1
  23. package/dist/midi/SoftwareMIDIInput.js +60 -18
  24. package/dist/processors.js +3 -3
  25. package/dist/processors.js.map +4 -4
  26. package/dist/project/Project.d.ts +3 -3
  27. package/dist/project/Project.d.ts.map +1 -1
  28. package/dist/project/Project.js +5 -4
  29. package/dist/project/ProjectMigration.d.ts +2 -2
  30. package/dist/project/ProjectMigration.d.ts.map +1 -1
  31. package/dist/project/ProjectMigration.js +0 -1
  32. package/dist/project/ProjectValidation.d.ts +5 -0
  33. package/dist/project/ProjectValidation.d.ts.map +1 -0
  34. package/dist/project/ProjectValidation.js +50 -0
  35. package/dist/ui/TimelineRange.d.ts +3 -1
  36. package/dist/ui/TimelineRange.d.ts.map +1 -1
  37. package/dist/ui/TimelineRange.js +7 -5
  38. package/dist/utils/AutofitUtils.d.ts +7 -0
  39. package/dist/utils/AutofitUtils.d.ts.map +1 -0
  40. package/dist/utils/AutofitUtils.js +78 -0
  41. package/dist/workers-main.js +2 -2
  42. package/dist/workers-main.js.map +3 -3
  43. package/dist/yjs/YMapper.d.ts +9 -0
  44. package/dist/yjs/YMapper.d.ts.map +1 -0
  45. package/dist/yjs/YMapper.js +45 -0
  46. package/dist/yjs/YService.d.ts +7 -0
  47. package/dist/yjs/YService.d.ts.map +1 -0
  48. package/dist/yjs/YService.js +69 -0
  49. package/dist/yjs/YSync.d.ts +17 -0
  50. package/dist/yjs/YSync.d.ts.map +1 -0
  51. package/dist/yjs/YSync.js +195 -0
  52. package/package.json +15 -15
@@ -1 +1 @@
1
- {"version":3,"file":"AudioOfflineRenderer.d.ts","sourceRoot":"","sources":["../src/AudioOfflineRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,GAAG,EAAE,MAAM,EAAmC,MAAM,kBAAkB,CAAA;AAI9G,OAAO,EAAC,wBAAwB,EAAC,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAIjD,yBAAiB,oBAAoB,CAAC;IAC3B,MAAM,KAAK,GAAU,QAAQ,OAAO,EACf,MAAM,WAAW,EACjB,wBAAwB,MAAM,CAAC,wBAAwB,CAAC,EACxD,aAAY,GAAY,KAAG,OAAO,CAAC,IAAI,CA+BlE,CAAA;CAiDJ"}
1
+ {"version":3,"file":"AudioOfflineRenderer.d.ts","sourceRoot":"","sources":["../src/AudioOfflineRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,GAAG,EAAE,MAAM,EAAmC,MAAM,kBAAkB,CAAA;AAI9G,OAAO,EAAC,wBAAwB,EAAC,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAIjD,yBAAiB,oBAAoB,CAAC;IAC3B,MAAM,KAAK,GAAU,QAAQ,OAAO,EACf,MAAM,WAAW,EACjB,wBAAwB,MAAM,CAAC,wBAAwB,CAAC,EACxD,aAAY,GAAY,KAAG,OAAO,CAAC,IAAI,CAgClE,CAAA;CAiDJ"}
@@ -8,8 +8,11 @@ import { AudioWorklets } from "./AudioWorklets";
8
8
  export var AudioOfflineRenderer;
9
9
  (function (AudioOfflineRenderer) {
10
10
  AudioOfflineRenderer.start = async (source, meta, optExportConfiguration, sampleRate = 48_000) => {
11
- const project = source.copy();
12
11
  const numStems = ExportStemsConfiguration.countStems(optExportConfiguration);
12
+ if (numStems === 0) {
13
+ return panic("Nothing to export");
14
+ }
15
+ const project = source.copy();
13
16
  const progress = new DefaultObservableValue(0.0);
14
17
  const dialog = RuntimeNotifier.progress({ headline: "Rendering...", progress });
15
18
  project.boxGraph.beginTransaction();
@@ -1 +1 @@
1
- {"version":3,"file":"InstrumentFactories.d.ts","sourceRoot":"","sources":["../src/InstrumentFactories.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAGrD,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,IAAI,EAAE,iBAelB,CAAA;IAEM,MAAM,IAAI,EAAE,iBAkBlB,CAAA;IAEM,MAAM,SAAS,EAAE,iBAiCvB,CAAA;IAEM,MAAM,YAAY,EAAE,iBAiB1B,CAAA;IAEM,MAAM,KAAK;;;;;KAAwC,CAAA;IAC1D,KAAY,IAAI,GAAG,MAAM,OAAO,KAAK,CAAA;CAMxC"}
1
+ {"version":3,"file":"InstrumentFactories.d.ts","sourceRoot":"","sources":["../src/InstrumentFactories.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAGrD,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,IAAI,EAAE,iBAelB,CAAA;IAEM,MAAM,IAAI,EAAE,iBAoBlB,CAAA;IAEM,MAAM,SAAS,EAAE,iBAkCvB,CAAA;IAEM,MAAM,YAAY,EAAE,iBAiB1B,CAAA;IAEM,MAAM,KAAK;;;;;KAAwC,CAAA;IAC1D,KAAY,IAAI,GAAG,MAAM,OAAO,KAAK,CAAA;CAQxC"}
@@ -26,9 +26,11 @@ export var InstrumentFactories;
26
26
  trackType: TrackType.Notes,
27
27
  create: (boxGraph, host, name, icon) => {
28
28
  const fileUUID = UUID.parse("c1678daa-4a47-4cba-b88f-4f4e384663c3");
29
+ const fileDuration = 5.340;
29
30
  const audioFileBox = boxGraph.findBox(fileUUID)
30
31
  .unwrapOrElse(() => AudioFileBox.create(boxGraph, fileUUID, box => {
31
32
  box.fileName.setValue("Rhode");
33
+ box.endInSeconds.setValue(fileDuration);
32
34
  }));
33
35
  return NanoDeviceBox.create(boxGraph, UUID.generate(), box => {
34
36
  box.label.setValue(name);
@@ -50,17 +52,17 @@ export var InstrumentFactories;
50
52
  box.host.refer(host);
51
53
  });
52
54
  const files = [
53
- useFile(boxGraph, UUID.parse("8bb2c6e8-9a6d-4d32-b7ec-1263594ef367"), "909 Bassdrum"),
54
- useFile(boxGraph, UUID.parse("0017fa18-a5eb-4d9d-b6f2-e2ddd30a3010"), "909 Snare"),
55
- useFile(boxGraph, UUID.parse("28d14cb9-1dc6-4193-9dd7-4e881f25f520"), "909 Low Tom"),
56
- useFile(boxGraph, UUID.parse("21f92306-d6e7-446c-a34b-b79620acfefc"), "909 Mid Tom"),
57
- useFile(boxGraph, UUID.parse("ad503883-8a72-46ab-a05b-a84149953e17"), "909 High Tom"),
58
- useFile(boxGraph, UUID.parse("cfee850b-7658-4d08-9e3b-79d196188504"), "909 Rimshot"),
59
- useFile(boxGraph, UUID.parse("32a6f36f-06eb-4b84-bb57-5f51103eb9e6"), "909 Clap"),
60
- useFile(boxGraph, UUID.parse("e0ac4b39-23fb-4a56-841d-c9e0ff440cab"), "909 Closed Hat"),
61
- useFile(boxGraph, UUID.parse("51c5eea4-391c-4743-896a-859692ec1105"), "909 Open Hat"),
62
- useFile(boxGraph, UUID.parse("42a56ff6-89b6-4f2e-8a66-5a41d316f4cb"), "909 Crash"),
63
- useFile(boxGraph, UUID.parse("87cde966-b799-4efc-a994-069e703478d3"), "909 Ride")
55
+ useFile(boxGraph, UUID.parse("8bb2c6e8-9a6d-4d32-b7ec-1263594ef367"), "909 Bassdrum", 0.509),
56
+ useFile(boxGraph, UUID.parse("0017fa18-a5eb-4d9d-b6f2-e2ddd30a3010"), "909 Snare", 0.235),
57
+ useFile(boxGraph, UUID.parse("28d14cb9-1dc6-4193-9dd7-4e881f25f520"), "909 Low Tom", 0.509),
58
+ useFile(boxGraph, UUID.parse("21f92306-d6e7-446c-a34b-b79620acfefc"), "909 Mid Tom", 0.385),
59
+ useFile(boxGraph, UUID.parse("ad503883-8a72-46ab-a05b-a84149953e17"), "909 High Tom", 0.511),
60
+ useFile(boxGraph, UUID.parse("cfee850b-7658-4d08-9e3b-79d196188504"), "909 Rimshot", 0.150),
61
+ useFile(boxGraph, UUID.parse("32a6f36f-06eb-4b84-bb57-5f51103eb9e6"), "909 Clap", 0.507),
62
+ useFile(boxGraph, UUID.parse("e0ac4b39-23fb-4a56-841d-c9e0ff440cab"), "909 Closed Hat", 0.154),
63
+ useFile(boxGraph, UUID.parse("51c5eea4-391c-4743-896a-859692ec1105"), "909 Open Hat", 0.502),
64
+ useFile(boxGraph, UUID.parse("42a56ff6-89b6-4f2e-8a66-5a41d316f4cb"), "909 Crash", 1.055),
65
+ useFile(boxGraph, UUID.parse("87cde966-b799-4efc-a994-069e703478d3"), "909 Ride", 1.720)
64
66
  ];
65
67
  const samples = files.map((file, index) => PlayfieldSampleBox.create(boxGraph, UUID.generate(), box => {
66
68
  box.device.refer(deviceBox.samples);
@@ -90,8 +92,9 @@ export var InstrumentFactories;
90
92
  })
91
93
  };
92
94
  InstrumentFactories.Named = { Vaporisateur: InstrumentFactories.Vaporisateur, Playfield: InstrumentFactories.Playfield, Nano: InstrumentFactories.Nano, Tape: InstrumentFactories.Tape };
93
- const useFile = (boxGraph, fileUUID, name) => boxGraph.findBox(fileUUID)
95
+ const useFile = (boxGraph, fileUUID, name, duration) => boxGraph.findBox(fileUUID)
94
96
  .unwrapOrElse(() => AudioFileBox.create(boxGraph, fileUUID, box => {
95
97
  box.fileName.setValue(name);
98
+ box.endInSeconds.setValue(duration);
96
99
  }));
97
100
  })(InstrumentFactories || (InstrumentFactories = {}));
@@ -1 +1 @@
1
- {"version":3,"file":"CaptureMidi.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAQH,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,UAAU,EACb,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAA;AAEnD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAI/C,qBAAa,WAAY,SAAQ,OAAO,CAAC,cAAc,CAAC;;gBAOxC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc;IA8B/F,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAEhC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY;IAE5D,IAAI,KAAK,IAAI,MAAM,CAgBlB;IAED,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAIhC;IAEK,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBvC,cAAc,IAAI,UAAU;CAgD/B"}
1
+ {"version":3,"file":"CaptureMidi.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAOH,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,UAAU,EACb,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAA;AAEnD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAI/C,qBAAa,WAAY,SAAQ,OAAO,CAAC,cAAc,CAAC;;gBAOxC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc;IA+B/F,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAEhC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY;IAE5D,IAAI,KAAK,IAAI,MAAM,CAgBlB;IAED,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAIhC;IAEK,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBvC,cAAc,IAAI,UAAU;CA6C/B"}
@@ -1,4 +1,4 @@
1
- import { assert, Errors, isDefined, isUndefined, Notifier, Option, Terminable } from "@opendaw/lib-std";
1
+ import { Errors, isDefined, isUndefined, Notifier, Option, Terminable } from "@opendaw/lib-std";
2
2
  import { Events } from "@opendaw/lib-dom";
3
3
  import { MidiData } from "@opendaw/lib-midi";
4
4
  import { Promises } from "@opendaw/lib-runtime";
@@ -33,7 +33,7 @@ export class CaptureMidi extends Capture {
33
33
  else {
34
34
  this.#stopStream();
35
35
  }
36
- }), this.#notifier.subscribe((signal) => manager.project.engine.noteSignal(signal)));
36
+ }), this.#notifier.subscribe((signal) => manager.project.engine.noteSignal(signal)), Terminable.create(() => this.#stopStream()));
37
37
  }
38
38
  notify(signal) { this.#notifier.notify(signal); }
39
39
  subscribeNotes(observer) { return this.#notifier.subscribe(observer); }
@@ -70,11 +70,7 @@ export class CaptureMidi extends Capture {
70
70
  return Errors.warn("MIDI not available");
71
71
  }
72
72
  }
73
- const optInputs = MidiDevices.inputDevices();
74
- if (optInputs.isEmpty()) {
75
- return Errors.warn("MIDI not available");
76
- }
77
- const inputs = optInputs.unwrap();
73
+ const inputs = MidiDevices.inputDevices();
78
74
  if (inputs.length === 0) {
79
75
  return;
80
76
  }
@@ -87,23 +83,20 @@ export class CaptureMidi extends Capture {
87
83
  }
88
84
  }
89
85
  startRecording() {
90
- const availableInputDevices = MidiDevices.inputDevices();
91
- assert(availableInputDevices.nonEmpty(), "No MIDI input devices found");
92
86
  return RecordMidi.start({ notifier: this.#notifier, project: this.manager.project, capture: this });
93
87
  }
94
88
  async #updateStream() {
95
- if (MidiDevices.get().isEmpty()) {
89
+ if (MidiDevices.get().isEmpty() && MidiDevices.canRequestMidiAccess()) {
96
90
  await MidiDevices.requestPermission();
97
91
  }
98
- const availableInputDevices = MidiDevices.inputDevices();
99
- const available = availableInputDevices.unwrap();
100
- const capturing = this.deviceId.getValue().match({
101
- none: () => available,
102
- some: id => available.filter(device => id === device.id)
92
+ const inputs = MidiDevices.inputDevices();
93
+ const explicit = this.deviceId.getValue().match({
94
+ none: () => inputs,
95
+ some: id => inputs.filter(device => id === device.id)
103
96
  });
104
97
  const activeNotes = new Int8Array(128);
105
98
  this.#stream.ifSome(terminable => terminable.terminate());
106
- this.#stream = Option.wrap(Terminable.many(...capturing.map(input => Events.subscribe(input, "midimessage", (event) => {
99
+ this.#stream = Option.wrap(Terminable.many(...explicit.map(input => Events.subscribe(input, "midimessage", (event) => {
107
100
  const data = event.data;
108
101
  if (isDefined(data) &&
109
102
  this.#filterChannel.mapOr(channel => MidiData.readChannel(data) === channel, true)) {
@@ -1 +1 @@
1
- {"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGvG,OAAO,EAAC,mBAAmB,EAAY,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAIjC,yBAAiB,WAAW,CAAC;IACzB,KAAK,kBAAkB,GAAG;QACtB,gBAAgB,EAAE,gBAAgB,CAAA;QAClC,WAAW,EAAE,WAAW,CAAA;QACxB,aAAa,EAAE,mBAAmB,CAAA;QAClC,YAAY,EAAE,YAAY,CAAA;QAC1B,OAAO,EAAE,OAAO,CAAA;QAChB,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;KACjB,CAAA;IAED,MAAM,CAAC,MAAM,KAAK,GACd,0FAAwF,kBAAkB,KACxG,UAqEL,CAAA;;CACJ"}
1
+ {"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGvG,OAAO,EAAC,mBAAmB,EAAY,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAIjC,yBAAiB,WAAW,CAAC;IACzB,KAAK,kBAAkB,GAAG;QACtB,gBAAgB,EAAE,gBAAgB,CAAA;QAClC,WAAW,EAAE,WAAW,CAAA;QACxB,aAAa,EAAE,mBAAmB,CAAA;QAClC,YAAY,EAAE,YAAY,CAAA;QAC1B,OAAO,EAAE,OAAO,CAAA;QAChB,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;KACjB,CAAA;IAED,MAAM,CAAC,MAAM,KAAK,GACd,0FAAwF,kBAAkB,KACxG,UAsEL,CAAA;;CACJ"}
@@ -48,8 +48,9 @@ export var RecordAudio;
48
48
  recordingWorklet.terminate();
49
49
  }
50
50
  else {
51
- const { regionBox: { duration } } = recordingData.unwrap("No recording data available");
51
+ const { regionBox: { duration }, fileBox } = recordingData.unwrap("No recording data available");
52
52
  recordingWorklet.limit(PPQN.pulsesToSamples(duration.getValue(), bpm, sampleRate) | 0);
53
+ fileBox.endInSeconds.setValue(recordingWorklet.numberOfFrames / sampleRate);
53
54
  }
54
55
  }), engine.position.catchupAndSubscribe(owner => {
55
56
  if (!engine.isRecording.getValue()) {
@@ -1 +1 @@
1
- {"version":3,"file":"Recording.d.ts","sourceRoot":"","sources":["../../src/capture/Recording.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,UAAU,EAAa,MAAM,kBAAkB,CAAA;AAK7F,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAE1C,qBAAa,SAAS;;IAClB,MAAM,KAAK,WAAW,IAAI,OAAO,CAA2B;WAE/C,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IA8D3E,OAAO;CACV"}
1
+ {"version":3,"file":"Recording.d.ts","sourceRoot":"","sources":["../../src/capture/Recording.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,UAAU,EAAa,MAAM,kBAAkB,CAAA;AAK7F,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAE1C,qBAAa,SAAS;;IAClB,MAAM,KAAK,WAAW,IAAI,OAAO,CAA2B;WAE/C,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IA6D3E,OAAO;CACV"}
@@ -31,8 +31,7 @@ export class Recording {
31
31
  if (isRecording.getValue() || isCountingIn.getValue()) {
32
32
  return;
33
33
  }
34
- editing.mark();
35
- terminator.terminate();
34
+ editing.modify(() => terminator.terminate()); // finalizes recording
36
35
  this.#isRecording = false;
37
36
  };
38
37
  terminator.ownAll(engine.isRecording.subscribe(stop), engine.isCountingIn.subscribe(stop), Terminable.create(() => Recording.#instance = Option.None));
@@ -130,7 +130,7 @@ export class CloudAuthManager {
130
130
  async #oauthDropbox() {
131
131
  return this.#oauthPkceFlow({
132
132
  service: "dropbox",
133
- clientId: "jtehjzxaxf3bf1l",
133
+ clientId: asDefined(import.meta.env?.VITE_DROPBOX_CLIENT_ID, "Missing VITE_DROPBOX_CLIENT_ID"),
134
134
  authUrlBase: "https://www.dropbox.com/oauth2/authorize",
135
135
  tokenUrl: "https://api.dropboxapi.com/oauth2/token",
136
136
  scope: "", // Dropbox scope is optional
@@ -140,7 +140,7 @@ export class CloudAuthManager {
140
140
  });
141
141
  }
142
142
  async #oauthGoogle() {
143
- const clientId = "628747153367-gt1oqcn3trr9l9a7jhigja6l1t3f1oik.apps.googleusercontent.com";
143
+ const clientId = asDefined(import.meta.env?.VITE_GOOGLE_CLIENT_ID, "Missing VITE_GOOGLE_CLIENT_ID");
144
144
  const scope = "https://www.googleapis.com/auth/drive.appdata";
145
145
  const redirectUri = `${location.origin}/auth-callback.html`;
146
146
  const params = new URLSearchParams({
@@ -1,11 +1,11 @@
1
1
  import { UUID } from "@opendaw/lib-std";
2
2
  import { ProjectSchema } from "@opendaw/lib-dawproject";
3
- import { ProjectDecoder } from "@opendaw/studio-adapters";
3
+ import { ProjectSkeleton } from "@opendaw/studio-adapters";
4
4
  import { DawProject } from "./DawProject";
5
5
  export declare namespace DawProjectImport {
6
6
  type Result = {
7
7
  audioIds: ReadonlyArray<UUID.Bytes>;
8
- skeleton: ProjectDecoder.Skeleton;
8
+ skeleton: ProjectSkeleton;
9
9
  };
10
10
  const read: (schema: ProjectSchema, resources: DawProject.ResourceProvider) => Promise<Result>;
11
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DawProjectImport.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImport.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,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,KAAK,CAAC,CAAC;QACpC,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"}
1
+ {"version":3,"file":"DawProjectImport.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImport.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,EAOhB,MAAM,yBAAyB,CAAA;AAuBhC,OAAO,EAAyC,eAAe,EAAY,MAAM,0BAA0B,CAAA;AAC3G,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAQvC,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,CAgXxG,CAAA;CAaJ"}
@@ -317,7 +317,10 @@ export var DawProjectImport;
317
317
  assert(external !== true, "File cannot be external");
318
318
  const { uuid, name } = resources.fromPath(path);
319
319
  const audioFileBox = boxGraph.findBox(uuid)
320
- .unwrapOrElse(() => AudioFileBox.create(boxGraph, uuid, box => box.fileName.setValue(name)));
320
+ .unwrapOrElse(() => AudioFileBox.create(boxGraph, uuid, box => {
321
+ box.fileName.setValue(name);
322
+ box.endInSeconds.setValue(asDefined(audio.duration, "Duration not defined"));
323
+ }));
321
324
  audioIdSet.add(uuid, true);
322
325
  AudioRegionBox.create(boxGraph, UUID.generate(), box => {
323
326
  const position = asDefined(clip.time, "Time not defined");
package/dist/index.d.ts CHANGED
@@ -26,6 +26,10 @@ export * from "./clouds/CloudAuthManager";
26
26
  export * from "./clouds/CloudHandler";
27
27
  export * from "./clouds/CloudBackup";
28
28
  export * from "./ui/TimelineRange";
29
+ export * from "./utils/AutofitUtils";
30
+ export * from "./yjs/YService";
31
+ export * from "./yjs/YSync";
32
+ export * from "./yjs/YMapper";
29
33
  export * from "./AudioDevices";
30
34
  export * from "./AudioOfflineRenderer";
31
35
  export * from "./AudioUnitOrdering";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AAEnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAE7C,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AAEnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AAExC,cAAc,iBAAiB,CAAA;AAE/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AAExC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AAEpC,cAAc,oBAAoB,CAAA;AAElC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AAEnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAE7C,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AAEnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AAExC,cAAc,iBAAiB,CAAA;AAE/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AAExC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AAEpC,cAAc,oBAAoB,CAAA;AAElC,cAAc,sBAAsB,CAAA;AAEpC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAE7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -26,6 +26,10 @@ export * from "./clouds/CloudAuthManager";
26
26
  export * from "./clouds/CloudHandler";
27
27
  export * from "./clouds/CloudBackup";
28
28
  export * from "./ui/TimelineRange";
29
+ export * from "./utils/AutofitUtils";
30
+ export * from "./yjs/YService";
31
+ export * from "./yjs/YSync";
32
+ export * from "./yjs/YMapper";
29
33
  export * from "./AudioDevices";
30
34
  export * from "./AudioOfflineRenderer";
31
35
  export * from "./AudioUnitOrdering";
@@ -7,7 +7,7 @@ export declare class MidiDevices {
7
7
  static requestPermission(): Promise<undefined>;
8
8
  static get(): ObservableOption<MIDIAccess>;
9
9
  static subscribeMessageEvents(observer: Observer<MIDIMessageEvent>, channel?: byte): Subscription;
10
- static inputDevices(): Option<ReadonlyArray<MIDIInput>>;
10
+ static inputDevices(): ReadonlyArray<MIDIInput>;
11
11
  static findInputDeviceById(id: string): Option<MIDIInput>;
12
12
  static externalInputDevices(): Option<ReadonlyArray<MIDIInput>>;
13
13
  static externalOutputDevices(): Option<ReadonlyArray<MIDIOutput>>;
@@ -1 +1 @@
1
- {"version":3,"file":"MidiDevices.d.ts","sourceRoot":"","sources":["../../src/midi/MidiDevices.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,IAAI,EAIJ,sBAAsB,EAEtB,gBAAgB,EAEhB,QAAQ,EACR,MAAM,EACN,YAAY,EAEf,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAErD,qBAAa,WAAW;;IACpB,MAAM,CAAC,oBAAoB,IAAI,OAAO;IAEtC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAA0B;WAIjE,iBAAiB;IAiB9B,MAAM,CAAC,GAAG,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAE1C,MAAM,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY;IAWjG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAKvD,MAAM,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;IAIzD,MAAM,CAAC,oBAAoB,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAI/D,MAAM,CAAC,qBAAqB,IAAI,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAIjE,MAAM,CAAC,KAAK,IAAI,IAAI;IAkBpB,MAAM,CAAC,SAAS,IAAI,sBAAsB,CAAC,OAAO,CAAC;CAqCtD"}
1
+ {"version":3,"file":"MidiDevices.d.ts","sourceRoot":"","sources":["../../src/midi/MidiDevices.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,IAAI,EAIJ,sBAAsB,EAEtB,gBAAgB,EAEhB,QAAQ,EACR,MAAM,EACN,YAAY,EAEf,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAA;AAErD,qBAAa,WAAW;;IACpB,MAAM,CAAC,oBAAoB,IAAI,OAAO;IAEtC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAA0B;WAIjE,iBAAiB;IAiB9B,MAAM,CAAC,GAAG,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAE1C,MAAM,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY;IAWjG,MAAM,CAAC,YAAY,IAAI,aAAa,CAAC,SAAS,CAAC;IAK/C,MAAM,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;IAIzD,MAAM,CAAC,oBAAoB,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAI/D,MAAM,CAAC,qBAAqB,IAAI,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAIjE,MAAM,CAAC,KAAK,IAAI,IAAI;IAkBpB,MAAM,CAAC,SAAS,IAAI,sBAAsB,CAAC,OAAO,CAAC;CAqCtD"}
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { Errors, Lazy, MutableObservableOption, MutableObservableValue, Notifier, Terminator } from "@opendaw/lib-std";
10
+ import { Errors, Lazy, MutableObservableOption, MutableObservableValue, Notifier, Option, Terminator } from "@opendaw/lib-std";
11
11
  import { MidiData } from "@opendaw/lib-midi";
12
12
  import { Promises } from "@opendaw/lib-runtime";
13
13
  import { MIDIMessageSubscriber } from "./MIDIMessageSubscriber";
@@ -45,10 +45,10 @@ export class MidiDevices {
45
45
  }
46
46
  static inputDevices() {
47
47
  return this.externalInputDevices()
48
- .map((inputs) => Array.from(inputs.values()).concat(this.softwareMIDIInput));
48
+ .mapOr((inputs) => Array.from(inputs.values()).concat(this.softwareMIDIInput), [this.softwareMIDIInput]);
49
49
  }
50
50
  static findInputDeviceById(id) {
51
- return this.inputDevices().map(inputs => inputs.find(input => input.id === id));
51
+ return Option.wrap(this.inputDevices().find(input => input.id === id));
52
52
  }
53
53
  static externalInputDevices() {
54
54
  return this.get().map(({ inputs }) => Array.from(inputs.values()));
@@ -1,5 +1,7 @@
1
- import { byte, Terminable, unitValue } from "@opendaw/lib-std";
2
- export declare class SoftwareMIDIInput implements MIDIInput, Terminable {
1
+ import { byte, int, Nullable, ObservableValue, unitValue } from "@opendaw/lib-std";
2
+ type OnMidiMessage = Nullable<(this: MIDIInput, ev: MIDIMessageEvent) => any>;
3
+ type OnStateChange = Nullable<(this: MIDIPort, ev: MIDIConnectionEvent) => any>;
4
+ export declare class SoftwareMIDIInput implements MIDIInput {
3
5
  #private;
4
6
  readonly manufacturer: string | null;
5
7
  readonly connection: MIDIPortConnectionState;
@@ -8,13 +10,18 @@ export declare class SoftwareMIDIInput implements MIDIInput, Terminable {
8
10
  readonly state: MIDIPortDeviceState;
9
11
  readonly type: MIDIPortType;
10
12
  readonly version: string | null;
11
- onmidimessage: ((this: MIDIInput, ev: MIDIMessageEvent) => any) | null;
12
- onstatechange: ((this: MIDIPort, ev: MIDIConnectionEvent) => any) | null;
13
- channel: byte;
13
+ onstatechange: OnStateChange;
14
14
  constructor();
15
- sendNoteOnEvent(note: byte, velocity?: unitValue): void;
16
- sendNoteOffEvent(note: byte): void;
17
- sendMIDIMessageData(data: Uint8Array): void;
15
+ get onmidimessage(): OnMidiMessage;
16
+ set onmidimessage(value: OnMidiMessage);
17
+ get countListeners(): ObservableValue<int>;
18
+ sendNoteOn(note: byte, velocity?: unitValue): void;
19
+ sendNoteOff(note: byte): void;
20
+ releaseAllNotes(): void;
21
+ hasActiveNote(note: byte): boolean;
22
+ hasActiveNotes(): boolean;
23
+ get channel(): byte;
24
+ set channel(value: byte);
18
25
  open(): Promise<MIDIPort>;
19
26
  close(): Promise<MIDIPort>;
20
27
  addEventListener<K extends keyof MIDIInputEventMap>(type: K, listener: (this: MIDIInput, ev: MIDIInputEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
@@ -22,6 +29,6 @@ export declare class SoftwareMIDIInput implements MIDIInput, Terminable {
22
29
  dispatchEvent(event: MIDIMessageEvent): boolean;
23
30
  removeEventListener<K extends keyof MIDIInputEventMap>(type: K, listener: (this: MIDIInput, ev: MIDIInputEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
24
31
  removeEventListener<K extends keyof MIDIPortEventMap>(type: K, listener: (this: MIDIPort, ev: MIDIPortEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
25
- terminate(): void;
26
32
  }
33
+ export {};
27
34
  //# sourceMappingURL=SoftwareMIDIInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SoftwareMIDIInput.d.ts","sourceRoot":"","sources":["../../src/midi/SoftwareMIDIInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAsB,UAAU,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAGhF,qBAAa,iBAAkB,YAAW,SAAS,EAAE,UAAU;;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAY;IAChD,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAS;IACrD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAwB;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAsB;IAClD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAc;IACjD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAU;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAU;IAIzC,aAAa,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,KAAK,GAAG,CAAC,GAAG,IAAI,CAAO;IAC7E,aAAa,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,mBAAmB,KAAK,GAAG,CAAC,GAAG,IAAI,CAAO;IAC/E,OAAO,EAAE,IAAI,CAAI;;IAIjB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,SAAe,GAAG,IAAI;IAK5D,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIlC,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAK3C,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC;IAC1B,gBAAgB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI;IAC7K,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI;IAI1K,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO;IAI/C,mBAAmB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI;IAC7K,mBAAmB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI;IAK1K,SAAS,IAAI,IAAI;CAGpB"}
1
+ {"version":3,"file":"SoftwareMIDIInput.d.ts","sourceRoot":"","sources":["../../src/midi/SoftwareMIDIInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,IAAI,EAGJ,GAAG,EAEH,QAAQ,EACR,eAAe,EAEf,SAAS,EACZ,MAAM,kBAAkB,CAAA;AAGzB,KAAK,aAAa,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,KAAK,GAAG,CAAC,CAAA;AAE7E,KAAK,aAAa,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,mBAAmB,KAAK,GAAG,CAAC,CAAA;AAE/E,qBAAa,iBAAkB,YAAW,SAAS;;IAC/C,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAY;IAChD,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAS;IACrD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAwB;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAsB;IAClD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAc;IACjD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAU;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAU;IAMzC,aAAa,EAAE,aAAa,CAAO;;IAWnC,IAAI,aAAa,IAAI,aAAa,CAA6B;IAC/D,IAAI,aAAa,CAAC,KAAK,EAAE,aAAa,EAGrC;IAED,IAAI,cAAc,IAAI,eAAe,CAAC,GAAG,CAAC,CAA8B;IAExE,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,SAAe,GAAG,IAAI;IAOvD,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAO7B,eAAe,IAAI,IAAI;IASvB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAClC,cAAc,IAAI,OAAO;IAEzB,IAAI,OAAO,IAAI,IAAI,CAAuB;IAC1C,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,EAItB;IAED,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC;IAC1B,gBAAgB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI;IAC7K,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI;IAK1K,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO;IAI/C,mBAAmB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI;IAC7K,mBAAmB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI;CAc7K"}
@@ -1,4 +1,4 @@
1
- import { clamp, safeExecute } from "@opendaw/lib-std";
1
+ import { assert, clamp, DefaultObservableValue, isNull, safeExecute } from "@opendaw/lib-std";
2
2
  import { MidiData } from "@opendaw/lib-midi";
3
3
  export class SoftwareMIDIInput {
4
4
  manufacturer = "openDAW";
@@ -9,34 +9,76 @@ export class SoftwareMIDIInput {
9
9
  type = "input";
10
10
  version = "1.0.0";
11
11
  #dispatcher;
12
- onmidimessage = null;
13
- onstatechange = null; // has no effect. always on.
14
- channel = 0; // 0...16
15
- constructor() { this.#dispatcher = new EventTarget(); }
16
- sendNoteOnEvent(note, velocity = 1.0) {
17
- const velocityByte = Math.round(clamp(velocity, 0, 1) * 127);
18
- this.sendMIDIMessageData(MidiData.noteOn(this.channel, note, velocityByte));
19
- }
20
- sendNoteOffEvent(note) {
21
- this.sendMIDIMessageData(MidiData.noteOff(this.channel, note));
22
- }
23
- sendMIDIMessageData(data) {
24
- const eventInit = { data };
25
- this.dispatchEvent(new MessageEvent("midimessage", eventInit));
12
+ #countListeners;
13
+ #activeNotes;
14
+ onstatechange = null; // has no effect. this device is always connected.
15
+ #onmidimessage = null;
16
+ #channel = 0; // 0...15
17
+ constructor() {
18
+ this.#dispatcher = new EventTarget();
19
+ this.#countListeners = new DefaultObservableValue(0);
20
+ this.#activeNotes = new Uint8Array(128);
21
+ }
22
+ get onmidimessage() { return this.#onmidimessage; }
23
+ set onmidimessage(value) {
24
+ this.#onmidimessage = value;
25
+ if (isNull(value)) {
26
+ this.#changeListenerCount(-1);
27
+ }
28
+ else {
29
+ this.#changeListenerCount(1);
30
+ }
31
+ }
32
+ get countListeners() { return this.#countListeners; }
33
+ sendNoteOn(note, velocity = 1.0) {
34
+ assert(note >= 0 && note <= 127, `Note must be between 0 and 127, but was ${note}`);
35
+ this.#activeNotes[note]++;
36
+ const velocityByte = Math.round(clamp(velocity, 0.0, 1.0) * 127.0);
37
+ this.#sendMIDIMessageData(MidiData.noteOn(this.#channel, note, velocityByte));
38
+ }
39
+ sendNoteOff(note) {
40
+ assert(note >= 0 && note <= 127, `Note must be between 0 and 127, but was ${note}`);
41
+ this.#activeNotes[note]--;
42
+ this.#sendMIDIMessageData(MidiData.noteOff(this.#channel, note));
43
+ assert(this.#activeNotes[note] >= 0, "Negative count of active notes");
44
+ }
45
+ releaseAllNotes() {
46
+ this.#activeNotes.forEach((count, note) => {
47
+ for (let i = 0; i < count; i++) {
48
+ this.#sendMIDIMessageData(MidiData.noteOff(this.#channel, note));
49
+ }
50
+ });
51
+ this.#activeNotes.fill(0);
52
+ }
53
+ hasActiveNote(note) { return this.#activeNotes[note] > 0; }
54
+ hasActiveNotes() { return this.#activeNotes.some(count => count > 0); }
55
+ get channel() { return this.#channel; }
56
+ set channel(value) {
57
+ if (this.#channel === value) {
58
+ return;
59
+ }
60
+ this.releaseAllNotes();
61
+ this.#channel = value;
26
62
  }
27
63
  open() { return Promise.resolve(this); }
28
64
  close() { return Promise.resolve(this); }
29
65
  addEventListener(type, listener, options) {
30
66
  this.#dispatcher.addEventListener(type, listener, options);
67
+ this.#changeListenerCount(1);
31
68
  }
32
69
  dispatchEvent(event) {
33
- safeExecute(this.onmidimessage, event);
70
+ safeExecute(this.#onmidimessage, event);
34
71
  return this.#dispatcher.dispatchEvent(event);
35
72
  }
36
73
  removeEventListener(type, listener, options) {
37
74
  this.#dispatcher.removeEventListener(type, listener, options);
75
+ this.#changeListenerCount(-1);
76
+ }
77
+ #sendMIDIMessageData(data) {
78
+ const eventInit = { data };
79
+ this.dispatchEvent(new MessageEvent("midimessage", eventInit));
38
80
  }
39
- terminate() {
40
- this.#dispatcher.dispatchEvent(new Event("close"));
81
+ #changeListenerCount(delta) {
82
+ this.#countListeners.setValue(this.#countListeners.getValue() + delta);
41
83
  }
42
84
  }