@opendaw/studio-core 0.0.59 → 0.0.60

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 (109) hide show
  1. package/dist/AudioOfflineRenderer.d.ts +2 -2
  2. package/dist/AudioOfflineRenderer.d.ts.map +1 -1
  3. package/dist/AudioOfflineRenderer.js +9 -9
  4. package/dist/AudioUtils.d.ts +4 -0
  5. package/dist/AudioUtils.d.ts.map +1 -0
  6. package/dist/AudioUtils.js +20 -0
  7. package/dist/EffectBox.d.ts +2 -2
  8. package/dist/EffectBox.d.ts.map +1 -1
  9. package/dist/EffectFactories.d.ts +3 -0
  10. package/dist/EffectFactories.d.ts.map +1 -1
  11. package/dist/EffectFactories.js +17 -3
  12. package/dist/Engine.d.ts +1 -1
  13. package/dist/Engine.d.ts.map +1 -1
  14. package/dist/EngineFacade.d.ts +1 -1
  15. package/dist/EngineFacade.d.ts.map +1 -1
  16. package/dist/EngineFacade.js +1 -1
  17. package/dist/EngineWorklet.d.ts.map +1 -1
  18. package/dist/EngineWorklet.js +7 -8
  19. package/dist/FilePickerAcceptTypes.d.ts.map +1 -1
  20. package/dist/Preferences.d.ts +4 -0
  21. package/dist/Preferences.d.ts.map +1 -1
  22. package/dist/Preferences.js +2 -0
  23. package/dist/RecordingWorklet.d.ts +2 -1
  24. package/dist/RecordingWorklet.d.ts.map +1 -1
  25. package/dist/WavFile.d.ts +4 -7
  26. package/dist/WavFile.d.ts.map +1 -1
  27. package/dist/WavFile.js +22 -21
  28. package/dist/Workers.d.ts +2 -0
  29. package/dist/Workers.d.ts.map +1 -1
  30. package/dist/Workers.js +11 -0
  31. package/dist/capture/CaptureAudio.d.ts.map +1 -1
  32. package/dist/capture/CaptureAudio.js +0 -2
  33. package/dist/capture/RecordAudio.d.ts.map +1 -1
  34. package/dist/capture/RecordAudio.js +3 -1
  35. package/dist/cloud/CloudBackupProjects.js +4 -4
  36. package/dist/cloud/CloudBackupSamples.d.ts.map +1 -1
  37. package/dist/cloud/CloudBackupSamples.js +3 -9
  38. package/dist/cloud/CloudBackupSoundfonts.js +1 -1
  39. package/dist/dawproject/DawProjectExporter.d.ts.map +1 -1
  40. package/dist/dawproject/DawProjectExporter.js +2 -12
  41. package/dist/dawproject/DawProjectImporter.d.ts.map +1 -1
  42. package/dist/dawproject/DawProjectImporter.js +5 -3
  43. package/dist/index.d.ts +1 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -0
  46. package/dist/midi/MIDIReceiver.d.ts +12 -0
  47. package/dist/midi/MIDIReceiver.d.ts.map +1 -0
  48. package/dist/midi/MIDIReceiver.js +65 -0
  49. package/dist/midi/MidiDevices.d.ts +4 -1
  50. package/dist/midi/MidiDevices.d.ts.map +1 -1
  51. package/dist/midi/MidiDevices.js +40 -1
  52. package/dist/processors.js +17 -19
  53. package/dist/processors.js.map +4 -4
  54. package/dist/project/Project.d.ts.map +1 -1
  55. package/dist/project/Project.js +14 -2
  56. package/dist/project/ProjectApi.d.ts +13 -4
  57. package/dist/project/ProjectApi.d.ts.map +1 -1
  58. package/dist/project/ProjectApi.js +71 -29
  59. package/dist/project/ProjectBundle.d.ts +2 -2
  60. package/dist/project/ProjectBundle.d.ts.map +1 -1
  61. package/dist/project/ProjectBundle.js +3 -3
  62. package/dist/project/ProjectMeta.d.ts +2 -0
  63. package/dist/project/ProjectMeta.d.ts.map +1 -1
  64. package/dist/project/ProjectMeta.js +1 -0
  65. package/dist/project/ProjectMigration.d.ts.map +1 -1
  66. package/dist/project/ProjectMigration.js +47 -5
  67. package/dist/project/ProjectProfile.d.ts +1 -0
  68. package/dist/project/ProjectProfile.d.ts.map +1 -1
  69. package/dist/project/ProjectProfile.js +5 -0
  70. package/dist/project/audio/AudioContentFactory.d.ts +34 -0
  71. package/dist/project/audio/AudioContentFactory.d.ts.map +1 -0
  72. package/dist/project/audio/AudioContentFactory.js +107 -0
  73. package/dist/project/audio/AudioContentHelpers.d.ts +7 -0
  74. package/dist/project/audio/AudioContentHelpers.d.ts.map +1 -0
  75. package/dist/project/audio/AudioContentHelpers.js +17 -0
  76. package/dist/project/audio/AudioContentModifier.d.ts +10 -0
  77. package/dist/project/audio/AudioContentModifier.d.ts.map +1 -0
  78. package/dist/project/audio/AudioContentModifier.js +129 -0
  79. package/dist/project/audio/AudioFileBoxFactory.d.ts +8 -0
  80. package/dist/project/audio/AudioFileBoxFactory.d.ts.map +1 -0
  81. package/dist/project/audio/AudioFileBoxFactory.js +36 -0
  82. package/dist/project/audio/index.d.ts +4 -0
  83. package/dist/project/audio/index.d.ts.map +1 -0
  84. package/dist/project/audio/index.js +3 -0
  85. package/dist/project/index.d.ts +1 -0
  86. package/dist/project/index.d.ts.map +1 -1
  87. package/dist/project/index.js +1 -0
  88. package/dist/samples/DefaultSampleLoader.d.ts +2 -1
  89. package/dist/samples/DefaultSampleLoader.d.ts.map +1 -1
  90. package/dist/samples/DefaultSampleLoader.js +1 -1
  91. package/dist/samples/DefaultSampleLoaderManager.d.ts +3 -1
  92. package/dist/samples/DefaultSampleLoaderManager.d.ts.map +1 -1
  93. package/dist/samples/DefaultSampleLoaderManager.js +13 -0
  94. package/dist/samples/OpenSampleAPI.d.ts +2 -1
  95. package/dist/samples/OpenSampleAPI.d.ts.map +1 -1
  96. package/dist/samples/OpenSampleAPI.js +2 -2
  97. package/dist/samples/SampleAPI.d.ts +2 -1
  98. package/dist/samples/SampleAPI.d.ts.map +1 -1
  99. package/dist/samples/SampleProvider.d.ts +2 -1
  100. package/dist/samples/SampleProvider.d.ts.map +1 -1
  101. package/dist/samples/SampleService.d.ts.map +1 -1
  102. package/dist/samples/SampleStorage.d.ts +2 -1
  103. package/dist/samples/SampleStorage.d.ts.map +1 -1
  104. package/dist/samples/SampleStorage.js +6 -5
  105. package/dist/soundfont/OpenSoundfontAPI.d.ts.map +1 -1
  106. package/dist/soundfont/OpenSoundfontAPI.js +3 -3
  107. package/dist/workers-main.js +1 -5
  108. package/dist/workers-main.js.map +4 -4
  109. package/package.json +15 -15
@@ -48,7 +48,7 @@ export class CloudBackupSamples {
48
48
  progress((index + 1) / length);
49
49
  this.#log(`Uploading sample '${sample.name}'`);
50
50
  const arrayBuffer = await SampleStorage.get().load(UUID.parse(sample.uuid))
51
- .then(([{ frames: channels, numberOfChannels, numberOfFrames: numFrames, sampleRate }]) => WavFile.encodeFloats({ channels, numberOfChannels, numFrames, sampleRate }));
51
+ .then(([data]) => WavFile.encodeFloats(data));
52
52
  const path = CloudBackupSamples.pathFor(sample.uuid);
53
53
  await Promises.approvedRetry(() => this.#cloudHandler.upload(path, arrayBuffer), error => ({
54
54
  headline: "Upload failed",
@@ -103,14 +103,8 @@ export class CloudBackupSamples {
103
103
  progress((index + 1) / length);
104
104
  this.#log(`Downloading sample '${sample.name}'`);
105
105
  const path = CloudBackupSamples.pathFor(sample.uuid);
106
- const buffer = await Promises.guardedRetry(() => this.#cloudHandler.download(path), network.DefaultRetry);
107
- const waveAudio = WavFile.decodeFloats(buffer);
108
- const audioData = {
109
- sampleRate: waveAudio.sampleRate,
110
- numberOfFrames: waveAudio.numFrames,
111
- numberOfChannels: waveAudio.channels.length,
112
- frames: waveAudio.channels
113
- };
106
+ const buffer = await Promises.guardedRetry(() => this.#cloudHandler.download(path), network.defaultRetry);
107
+ const audioData = WavFile.decodeFloats(buffer);
114
108
  const shifts = SamplePeaks.findBestFit(audioData.numberOfFrames);
115
109
  const peaks = await Workers.Peak.generateAsync(Progress.Empty, shifts, audioData.frames, audioData.numberOfFrames, audioData.numberOfChannels);
116
110
  await SampleStorage.get().save({
@@ -100,7 +100,7 @@ export class CloudBackupSoundfonts {
100
100
  progress((index + 1) / length);
101
101
  this.#log(`Downloading soundfont '${soundfont.name}'`);
102
102
  const path = CloudBackupSoundfonts.pathFor(soundfont.uuid);
103
- const buffer = await Promises.guardedRetry(() => this.#cloudHandler.download(path), network.DefaultRetry);
103
+ const buffer = await Promises.guardedRetry(() => this.#cloudHandler.download(path), network.defaultRetry);
104
104
  await SoundfontStorage.get().save({
105
105
  uuid: UUID.parse(soundfont.uuid),
106
106
  file: buffer,
@@ -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,kBA4PlH,CAAA;CACJ"}
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"}
@@ -20,13 +20,7 @@ export var DawProjectExporter;
20
20
  .sort((a, b) => a.index.getValue() - b.index.getValue());
21
21
  boxGraph.boxes().forEach(box => box.accept({
22
22
  visitAudioFileBox: (box) => sampleManager.getOrCreate(box.address.uuid).data
23
- .ifSome(({ frames, numberOfFrames, sampleRate, numberOfChannels }) => resourcePacker.write(`samples/${box.fileName.getValue()}.wav`, WavFile.encodeFloats({
24
- channels: frames,
25
- duration: numberOfFrames * sampleRate,
26
- numberOfChannels,
27
- sampleRate,
28
- numFrames: numberOfFrames
29
- })))
23
+ .ifSome((data) => resourcePacker.write(`samples/${box.fileName.getValue()}.wav`, WavFile.encodeFloats(data)))
30
24
  }));
31
25
  const writeTransport = () => Xml.element({
32
26
  tempo: Xml.element({
@@ -63,11 +57,7 @@ export var DawProjectExporter;
63
57
  }, BuiltinDeviceSchema);
64
58
  });
65
59
  const colorForAudioType = (unitType) => {
66
- const cssColor = ColorCodes.forAudioType(unitType);
67
- if (cssColor === "") {
68
- return "red";
69
- }
70
- const [r, g, b] = Html.readCssVarColor(ColorCodes.forAudioType(unitType))[0];
60
+ const [r, g, b] = Html.readCssVarColor(ColorCodes.forAudioType(unitType).toString())[0];
71
61
  const RR = Math.round(r * 255).toString(16).padStart(2, "0");
72
62
  const GG = Math.round(g * 255).toString(16).padStart(2, "0");
73
63
  const BB = Math.round(b * 255).toString(16).padStart(2, "0");
@@ -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;AAuBhC,OAAO,EAMH,eAAe,EAGlB,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,CA2XxG,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,EAOhB,MAAM,yBAAyB,CAAA;AAwBhC,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,CA6XxG,CAAA;CAaJ"}
@@ -3,8 +3,8 @@ import { BoxGraph } from "@opendaw/lib-box";
3
3
  import { gainToDb, PPQN } from "@opendaw/lib-dsp";
4
4
  import { ChannelRole, ClipsSchema, DeviceRole, EqualizerSchema, LanesSchema, NotesSchema, PointsSchema, SendType, TrackSchema, WarpsSchema } from "@opendaw/lib-dawproject";
5
5
  import { AudioSendRouting, AudioUnitType, IconSymbol } from "@opendaw/studio-enums";
6
- import { AudioBusBox, AudioFileBox, AudioRegionBox, AudioUnitBox, AuxSendBox, BoxIO, CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, RootBox, TimelineBox, TrackBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, UserInterfaceBox } from "@opendaw/studio-boxes";
7
- import { AudioUnitOrdering, DeviceBoxUtils, InstrumentFactories, ColorCodes, TrackType } from "@opendaw/studio-adapters";
6
+ import { AudioBusBox, AudioFileBox, 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, DeviceBoxUtils, InstrumentFactories, TrackType } from "@opendaw/studio-adapters";
8
8
  import { DeviceIO } from "./DeviceIO";
9
9
  import { BuiltinDevices } from "./BuiltinDevices";
10
10
  export var DawProjectImport;
@@ -169,7 +169,7 @@ export var DawProjectImport;
169
169
  box.collection.refer(rootBox.audioBusses);
170
170
  box.label.setValue(track.name ?? "");
171
171
  box.icon.setValue(IconSymbol.toName(icon));
172
- box.color.setValue(ColorCodes.forAudioType(type));
172
+ box.color.setValue(ColorCodes.forAudioType(type).toString());
173
173
  box.enabled.setValue(true);
174
174
  box.output.refer(audioUnitBox.input);
175
175
  });
@@ -319,6 +319,7 @@ export var DawProjectImport;
319
319
  box.endInSeconds.setValue(asDefined(audio.duration, "Duration not defined"));
320
320
  }));
321
321
  audioIdSet.add(uuid, true);
322
+ const collectionBox = ValueEventCollectionBox.create(boxGraph, UUID.generate());
322
323
  AudioRegionBox.create(boxGraph, UUID.generate(), box => {
323
324
  const position = asDefined(clip.time, "Time not defined");
324
325
  const duration = asDefined(clip.duration, "Duration not defined");
@@ -331,6 +332,7 @@ export var DawProjectImport;
331
332
  box.mute.setValue(clip.enable === false);
332
333
  box.regions.refer(trackBox.regions);
333
334
  box.file.refer(audioFileBox);
335
+ box.events.refer(collectionBox.owners);
334
336
  });
335
337
  };
336
338
  return Promise.all(arrangement?.lanes?.lanes?.filter(timeline => isInstanceOf(timeline, LanesSchema))
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from "./ui";
11
11
  export * from "./ysync";
12
12
  export * from "./AudioDevices";
13
13
  export * from "./AudioOfflineRenderer";
14
+ export * from "./AudioUtils";
14
15
  export * from "./EffectBox";
15
16
  export * from "./EffectFactory";
16
17
  export * from "./EffectFactories";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,mBAAmB,UAAU,CAAA;AAC7B,cAAc,QAAQ,CAAA;AACtB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AAEvB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,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,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,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,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,mBAAmB,UAAU,CAAA;AAC7B,cAAc,QAAQ,CAAA;AACtB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AAEvB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,cAAc,CAAA;AAC5B,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,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,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
@@ -10,6 +10,7 @@ export * from "./ui";
10
10
  export * from "./ysync";
11
11
  export * from "./AudioDevices";
12
12
  export * from "./AudioOfflineRenderer";
13
+ export * from "./AudioUtils";
13
14
  export * from "./EffectBox";
14
15
  export * from "./EffectFactory";
15
16
  export * from "./EffectFactories";
@@ -0,0 +1,12 @@
1
+ import { int, Terminable } from "@opendaw/lib-std";
2
+ type MIDIMessageCallback = (deviceId: string, data: Uint8Array, timeMs: int) => void;
3
+ export declare class MIDIReceiver implements Terminable {
4
+ #private;
5
+ static create(context: BaseAudioContext, callback: MIDIMessageCallback): MIDIReceiver;
6
+ private constructor();
7
+ get sab(): SharedArrayBuffer;
8
+ get port(): MessagePort;
9
+ terminate(): void;
10
+ }
11
+ export {};
12
+ //# sourceMappingURL=MIDIReceiver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MIDIReceiver.d.ts","sourceRoot":"","sources":["../../src/midi/MIDIReceiver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAE,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAEhD,KAAK,mBAAmB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAA;AAEpF,qBAAa,YAAa,YAAW,UAAU;;IAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,YAAY;IAsBrF,OAAO;IAgBP,IAAI,GAAG,IAAI,iBAAiB,CAAmB;IAC/C,IAAI,IAAI,IAAI,WAAW,CAA6B;IAEpD,SAAS,IAAI,IAAI;CAwBpB"}
@@ -0,0 +1,65 @@
1
+ export class MIDIReceiver {
2
+ static create(context, callback) {
3
+ const MIDI_RING_SIZE = 2048;
4
+ const sab = new SharedArrayBuffer(MIDI_RING_SIZE * 2 * 4 + 8);
5
+ const channel = new MessageChannel();
6
+ const hasOutputLatency = context instanceof AudioContext;
7
+ return new MIDIReceiver(sab, channel, (deviceId, data, relativeTimeInMs) => {
8
+ let delay = 20.0; // default 20ms
9
+ if (hasOutputLatency) {
10
+ delay = context.outputLatency / 1000.0;
11
+ }
12
+ callback(deviceId, data, relativeTimeInMs + delay);
13
+ });
14
+ }
15
+ #sab;
16
+ #channel;
17
+ #indices;
18
+ #ring;
19
+ #messageMask;
20
+ #onMessage;
21
+ #deviceIds = new Map();
22
+ constructor(sab, channel, onMessage) {
23
+ this.#sab = sab;
24
+ this.#channel = channel;
25
+ this.#indices = new Uint32Array(sab, 0, 2);
26
+ this.#ring = new Uint32Array(sab, 8);
27
+ this.#messageMask = (this.#ring.length >> 1) - 1;
28
+ this.#onMessage = onMessage;
29
+ this.#channel.port1.onmessage = (event) => {
30
+ if (event.data?.registerDevice) {
31
+ this.#deviceIds.set(event.data.id, event.data.registerDevice);
32
+ }
33
+ this.#read();
34
+ };
35
+ }
36
+ get sab() { return this.#sab; }
37
+ get port() { return this.#channel.port2; }
38
+ terminate() { this.#channel.port1.close(); }
39
+ #read() {
40
+ let readIdx = Atomics.load(this.#indices, 1);
41
+ const writeIdx = Atomics.load(this.#indices, 0);
42
+ while (readIdx !== writeIdx) {
43
+ const offset = readIdx << 1;
44
+ const packed1 = this.#ring[offset];
45
+ const packed2 = this.#ring[offset + 1];
46
+ const length = packed1 >>> 30;
47
+ const deviceIdNum = (packed1 >>> 24) & 0x3F;
48
+ const status = (packed1 >>> 16) & 0xFF;
49
+ const data1 = (packed1 >>> 8) & 0xFF;
50
+ const data2 = packed1 & 0xFF;
51
+ const deviceId = this.#deviceIds.get(deviceIdNum) ?? "";
52
+ const data = new Uint8Array(length);
53
+ data[0] = status;
54
+ if (length > 1) {
55
+ data[1] = data1;
56
+ }
57
+ if (length > 2) {
58
+ data[2] = data2;
59
+ }
60
+ this.#onMessage(deviceId, data, packed2);
61
+ readIdx = (readIdx + 1) & this.#messageMask;
62
+ }
63
+ Atomics.store(this.#indices, 1, readIdx);
64
+ }
65
+ }
@@ -1,14 +1,17 @@
1
- import { byte, MutableObservableValue, ObservableOption, Observer, Option, Subscription } from "@opendaw/lib-std";
1
+ import { byte, MutableObservableValue, ObservableOption, Observer, Option, Procedure, Subscription } from "@opendaw/lib-std";
2
2
  import { SoftwareMIDIInput } from "./SoftwareMIDIInput";
3
3
  export declare class MidiDevices {
4
4
  #private;
5
5
  static canRequestMidiAccess(): boolean;
6
6
  static readonly softwareMIDIInput: SoftwareMIDIInput;
7
+ static createSoftwareMIDIOutput(procedure: Procedure<Uint8Array>, name: string, id?: string): MIDIOutput;
7
8
  static requestPermission(): Promise<undefined>;
8
9
  static get(): ObservableOption<MIDIAccess>;
9
10
  static subscribeMessageEvents(observer: Observer<MIDIMessageEvent>, channel?: byte): Subscription;
10
11
  static inputDevices(): ReadonlyArray<MIDIInput>;
12
+ static outputDevices(): ReadonlyArray<MIDIOutput>;
11
13
  static findInputDeviceById(id: string): Option<MIDIInput>;
14
+ static findOutputDeviceById(id: string): Option<MIDIOutput>;
12
15
  static externalInputDevices(): Option<ReadonlyArray<MIDIInput>>;
13
16
  static externalOutputDevices(): Option<ReadonlyArray<MIDIOutput>>;
14
17
  static panic(): void;
@@ -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,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"}
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,EAEN,SAAS,EACT,YAAY,EAGf,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;IAI9E,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU;WAgC3F,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,aAAa,IAAI,aAAa,CAAC,UAAU,CAAC;IAMjD,MAAM,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;IAIzD,MAAM,CAAC,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;IAI3D,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, Option, Terminator } from "@opendaw/lib-std";
10
+ import { Errors, Lazy, MutableObservableOption, MutableObservableValue, Notifier, Option, panic, Terminator, UUID } 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";
@@ -15,6 +15,37 @@ import { SoftwareMIDIInput } from "./SoftwareMIDIInput";
15
15
  export class MidiDevices {
16
16
  static canRequestMidiAccess() { return "requestMIDIAccess" in navigator; }
17
17
  static softwareMIDIInput = new SoftwareMIDIInput();
18
+ static #softwareMIDIOutputs = new Map();
19
+ static createSoftwareMIDIOutput(procedure, name, id) {
20
+ const output = new class {
21
+ id = id ?? UUID.toString(UUID.generate());
22
+ name = name;
23
+ manufacturer = null;
24
+ state = "connected";
25
+ connection = "open";
26
+ type = "output";
27
+ version = null;
28
+ send(data, _timestamp) {
29
+ if (data instanceof Uint8Array) {
30
+ procedure(data);
31
+ }
32
+ else if (Array.isArray(data) && data.every(value => typeof value === "number")) {
33
+ procedure(new Uint8Array(data));
34
+ }
35
+ else {
36
+ return panic("MIDI output data must be an array of numbers or a Uint8Array");
37
+ }
38
+ }
39
+ addEventListener(_type, _listener, _options) { }
40
+ removeEventListener(_type, _listener, _options) { }
41
+ close() { return Promise.resolve(this); }
42
+ open() { return Promise.resolve(this); }
43
+ dispatchEvent(_event) { return false; }
44
+ onstatechange = null;
45
+ };
46
+ MidiDevices.#softwareMIDIOutputs.set(output.id, output);
47
+ return output;
48
+ }
18
49
  static #memoizedRequest = Promises.memoizeAsync(() => navigator.requestMIDIAccess({ sysex: false }));
19
50
  static async requestPermission() {
20
51
  if (this.canRequestMidiAccess()) {
@@ -47,9 +78,17 @@ export class MidiDevices {
47
78
  return this.externalInputDevices()
48
79
  .mapOr((inputs) => Array.from(inputs.values()).concat(this.softwareMIDIInput), [this.softwareMIDIInput]);
49
80
  }
81
+ static outputDevices() {
82
+ const softwareOutputs = Array.from(this.#softwareMIDIOutputs.values());
83
+ return this.externalOutputDevices()
84
+ .mapOr((inputs) => Array.from(inputs.values()).concat(...softwareOutputs), softwareOutputs);
85
+ }
50
86
  static findInputDeviceById(id) {
51
87
  return Option.wrap(this.inputDevices().find(input => input.id === id));
52
88
  }
89
+ static findOutputDeviceById(id) {
90
+ return Option.wrap(this.outputDevices().find(output => output.id === id));
91
+ }
53
92
  static externalInputDevices() {
54
93
  return this.get().map(({ inputs }) => Array.from(inputs.values()));
55
94
  }