@opendaw/studio-core 0.0.19 → 0.0.21

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 (110) hide show
  1. package/dist/AudioDevices.d.ts +8 -0
  2. package/dist/AudioDevices.d.ts.map +1 -0
  3. package/dist/AudioDevices.js +34 -0
  4. package/dist/AudioUnitOrdering.d.ts +3 -0
  5. package/dist/AudioUnitOrdering.d.ts.map +1 -0
  6. package/dist/AudioUnitOrdering.js +7 -0
  7. package/dist/EffectBox.d.ts +2 -2
  8. package/dist/EffectBox.d.ts.map +1 -1
  9. package/dist/Engine.d.ts +31 -10
  10. package/dist/Engine.d.ts.map +1 -1
  11. package/dist/EngineFacade.d.ts +22 -11
  12. package/dist/EngineFacade.d.ts.map +1 -1
  13. package/dist/EngineFacade.js +39 -22
  14. package/dist/EngineWorklet.d.ts +22 -13
  15. package/dist/EngineWorklet.d.ts.map +1 -1
  16. package/dist/EngineWorklet.js +47 -56
  17. package/dist/MeterWorklet.d.ts.map +1 -1
  18. package/dist/MeterWorklet.js +2 -2
  19. package/dist/MidiDevices.d.ts +12 -0
  20. package/dist/MidiDevices.d.ts.map +1 -0
  21. package/dist/MidiDevices.js +92 -0
  22. package/dist/Project.d.ts +14 -3
  23. package/dist/Project.d.ts.map +1 -1
  24. package/dist/Project.js +27 -4
  25. package/dist/ProjectApi.d.ts +0 -1
  26. package/dist/ProjectApi.d.ts.map +1 -1
  27. package/dist/ProjectApi.js +16 -11
  28. package/dist/ProjectMigration.d.ts.map +1 -1
  29. package/dist/ProjectMigration.js +20 -3
  30. package/dist/RecordingWorklet.d.ts +15 -3
  31. package/dist/RecordingWorklet.d.ts.map +1 -1
  32. package/dist/RecordingWorklet.js +111 -16
  33. package/dist/WorkerAgents.d.ts +2 -2
  34. package/dist/WorkerAgents.d.ts.map +1 -1
  35. package/dist/Worklets.d.ts +1 -1
  36. package/dist/Worklets.d.ts.map +1 -1
  37. package/dist/Worklets.js +2 -2
  38. package/dist/capture/Capture.d.ts +22 -0
  39. package/dist/capture/Capture.d.ts.map +1 -0
  40. package/dist/capture/Capture.js +32 -0
  41. package/dist/capture/CaptureAudio.d.ts +17 -0
  42. package/dist/capture/CaptureAudio.d.ts.map +1 -0
  43. package/dist/capture/CaptureAudio.js +106 -0
  44. package/dist/capture/CaptureManager.d.ts +12 -0
  45. package/dist/capture/CaptureManager.d.ts.map +1 -0
  46. package/dist/capture/CaptureManager.js +38 -0
  47. package/dist/capture/CaptureMidi.d.ts +13 -0
  48. package/dist/capture/CaptureMidi.d.ts.map +1 -0
  49. package/dist/capture/CaptureMidi.js +106 -0
  50. package/dist/capture/RecordAudio.d.ts +19 -0
  51. package/dist/capture/RecordAudio.d.ts.map +1 -0
  52. package/dist/capture/RecordAudio.js +66 -0
  53. package/dist/capture/RecordMidi.d.ts +13 -0
  54. package/dist/capture/RecordMidi.d.ts.map +1 -0
  55. package/dist/capture/RecordMidi.js +85 -0
  56. package/dist/capture/RecordTrack.d.ts +7 -0
  57. package/dist/capture/RecordTrack.d.ts.map +1 -0
  58. package/dist/capture/RecordTrack.js +23 -0
  59. package/dist/capture/Recording.d.ts +9 -0
  60. package/dist/capture/Recording.d.ts.map +1 -0
  61. package/dist/capture/Recording.js +65 -0
  62. package/dist/capture/RecordingContext.d.ts +10 -0
  63. package/dist/capture/RecordingContext.d.ts.map +1 -0
  64. package/dist/capture/RecordingContext.js +1 -0
  65. package/dist/dawproject/AudioUnitExportLayout.d.ts +10 -0
  66. package/dist/dawproject/AudioUnitExportLayout.d.ts.map +1 -0
  67. package/dist/dawproject/AudioUnitExportLayout.js +64 -0
  68. package/dist/dawproject/BuiltinDevices.d.ts +9 -0
  69. package/dist/dawproject/BuiltinDevices.d.ts.map +1 -0
  70. package/dist/dawproject/BuiltinDevices.js +70 -0
  71. package/dist/dawproject/{DawProjectIO.d.ts → DawProject.d.ts} +6 -4
  72. package/dist/dawproject/DawProject.d.ts.map +1 -0
  73. package/dist/dawproject/DawProject.js +48 -0
  74. package/dist/dawproject/DawProjectExporter.d.ts +7 -6
  75. package/dist/dawproject/DawProjectExporter.d.ts.map +1 -1
  76. package/dist/dawproject/DawProjectExporter.js +245 -22
  77. package/dist/dawproject/DawProjectExporter.test.js +38 -6
  78. package/dist/dawproject/DawProjectImport.d.ts +12 -0
  79. package/dist/dawproject/DawProjectImport.d.ts.map +1 -0
  80. package/dist/dawproject/DawProjectImport.js +389 -0
  81. package/dist/dawproject/DawProjectImport.test.js +6 -7
  82. package/dist/dawproject/DeviceIO.d.ts +8 -0
  83. package/dist/dawproject/DeviceIO.d.ts.map +1 -0
  84. package/dist/dawproject/DeviceIO.js +63 -0
  85. package/dist/index.d.ts +13 -3
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +13 -3
  88. package/dist/processors.js +3 -3
  89. package/dist/processors.js.map +4 -4
  90. package/dist/samples/MainThreadSampleLoader.d.ts +1 -0
  91. package/dist/samples/MainThreadSampleLoader.d.ts.map +1 -1
  92. package/dist/samples/MainThreadSampleLoader.js +10 -6
  93. package/dist/samples/MainThreadSampleManager.d.ts +5 -5
  94. package/dist/samples/MainThreadSampleManager.d.ts.map +1 -1
  95. package/dist/samples/MainThreadSampleManager.js +1 -0
  96. package/dist/samples/SampleProvider.d.ts +2 -2
  97. package/dist/samples/SampleProvider.d.ts.map +1 -1
  98. package/dist/samples/SampleStorage.d.ts.map +1 -1
  99. package/dist/samples/SampleStorage.js +2 -3
  100. package/dist/workers.js +2 -2
  101. package/dist/workers.js.map +4 -4
  102. package/package.json +15 -15
  103. package/dist/dawproject/DawProjectIO.d.ts.map +0 -1
  104. package/dist/dawproject/DawProjectIO.js +0 -31
  105. package/dist/dawproject/DawProjectImporter.d.ts +0 -12
  106. package/dist/dawproject/DawProjectImporter.d.ts.map +0 -1
  107. package/dist/dawproject/DawProjectImporter.js +0 -273
  108. package/dist/samples/SamplePeaks.d.ts +0 -6
  109. package/dist/samples/SamplePeaks.d.ts.map +0 -1
  110. package/dist/samples/SamplePeaks.js +0 -9
@@ -0,0 +1,92 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Lazy, MutableObservableOption, MutableObservableValue, Notifier, warn } from "@opendaw/lib-std";
11
+ import { MidiData } from "@opendaw/lib-midi";
12
+ import { Promises } from "@opendaw/lib-runtime";
13
+ export class MidiDevices {
14
+ static canRequestMidiAccess() { return "requestMIDIAccess" in navigator; }
15
+ static async requestPermission() {
16
+ if (this.canRequestMidiAccess()) {
17
+ const { status, value: midiAccess, error } = await Promises.tryCatch(navigator.requestMIDIAccess({ sysex: false }));
18
+ if (status === "rejected") {
19
+ console.warn(error);
20
+ return warn("Could not request MIDI");
21
+ }
22
+ const numberOfInputs = midiAccess.inputs.size;
23
+ const numberOfOutputs = midiAccess.outputs.size;
24
+ console.debug(`MIDI access granted: ${numberOfInputs} inputs, ${numberOfOutputs} outputs`);
25
+ this.#midiAccess.wrap(midiAccess);
26
+ }
27
+ else {
28
+ return warn("This browser does not support MIDI");
29
+ }
30
+ }
31
+ static get() { return this.#midiAccess; }
32
+ static inputs() {
33
+ return this.get().map(({ inputs }) => Array.from(inputs.values()));
34
+ }
35
+ static outputs() {
36
+ return this.get().map(({ outputs }) => Array.from(outputs.values()));
37
+ }
38
+ static panic() {
39
+ this.get().ifSome((midiAccess) => {
40
+ for (let note = 0; note < 128; note++) {
41
+ for (let channel = 0; channel < 16; channel++) {
42
+ const data = MidiData.noteOff(channel, note);
43
+ const event = new MessageEvent("midimessage", { data });
44
+ for (let input of midiAccess.inputs.values()) {
45
+ input.dispatchEvent(event);
46
+ }
47
+ for (let output of midiAccess.outputs.values()) {
48
+ output.send(data);
49
+ }
50
+ }
51
+ }
52
+ });
53
+ }
54
+ static available() {
55
+ const scope = this;
56
+ return new class {
57
+ #notifier = new Notifier();
58
+ constructor() {
59
+ const subscription = scope.get().subscribe(option => {
60
+ if (option.nonEmpty()) {
61
+ subscription.terminate();
62
+ this.#notifier.notify(this);
63
+ } // MIDIAccess cannot be turned off
64
+ });
65
+ }
66
+ setValue(value) {
67
+ if (!value || scope.#midiAccess.nonEmpty() || scope.#isRequesting) {
68
+ return;
69
+ }
70
+ console.debug("Request MIDI access");
71
+ scope.#isRequesting = true;
72
+ scope.requestPermission().finally(() => scope.#isRequesting = false);
73
+ }
74
+ getValue() { return scope.#midiAccess.nonEmpty(); }
75
+ catchupAndSubscribe(observer) {
76
+ observer(this);
77
+ return this.#notifier.subscribe(observer);
78
+ }
79
+ subscribe(observer) {
80
+ return this.#notifier.subscribe(observer);
81
+ }
82
+ };
83
+ }
84
+ static #isRequesting = false;
85
+ static #midiAccess = new MutableObservableOption();
86
+ }
87
+ __decorate([
88
+ Lazy,
89
+ __metadata("design:type", Function),
90
+ __metadata("design:paramtypes", []),
91
+ __metadata("design:returntype", Object)
92
+ ], MidiDevices, "available", null);
package/dist/Project.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Terminable, TerminableOwner, Terminator } from "@opendaw/lib-std";
1
+ import { Procedure, Terminable, TerminableOwner, Terminator } from "@opendaw/lib-std";
2
2
  import { BoxGraph, Editing } from "@opendaw/lib-box";
3
3
  import { AudioBusBox, AudioUnitBox, BoxIO, RootBox, TimelineBox, UserInterfaceBox } from "@opendaw/studio-boxes";
4
4
  import { BoxAdapters, BoxAdaptersContext, ClipSequencing, ParameterFieldAdapters, ProjectDecoder, RootBoxAdapter, SampleManager, TimelineBoxAdapter, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
@@ -6,6 +6,14 @@ import { LiveStreamBroadcaster, LiveStreamReceiver } from "@opendaw/lib-fusion";
6
6
  import { ProjectEnv } from "./ProjectEnv";
7
7
  import { Mixer } from "./Mixer";
8
8
  import { ProjectApi } from "./ProjectApi";
9
+ import { CaptureManager } from "./capture/CaptureManager";
10
+ import { EngineFacade } from "./EngineFacade";
11
+ import { EngineWorklet } from "./EngineWorklet";
12
+ import { Worklets } from "./Worklets";
13
+ export type RestartWorklet = {
14
+ unload: Procedure<unknown>;
15
+ load: Procedure<EngineWorklet>;
16
+ };
9
17
  export declare class Project implements BoxAdaptersContext, Terminable, TerminableOwner {
10
18
  #private;
11
19
  static new(env: ProjectEnv): Project;
@@ -18,6 +26,7 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
18
26
  readonly masterAudioUnit: AudioUnitBox;
19
27
  readonly timelineBox: TimelineBox;
20
28
  readonly api: ProjectApi;
29
+ readonly captureManager: CaptureManager;
21
30
  readonly editing: Editing;
22
31
  readonly selection: VertexSelection;
23
32
  readonly boxAdapters: BoxAdapters;
@@ -25,14 +34,17 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
25
34
  readonly parameterFieldAdapters: ParameterFieldAdapters;
26
35
  readonly liveStreamReceiver: LiveStreamReceiver;
27
36
  readonly mixer: Mixer;
37
+ readonly engine: EngineFacade;
28
38
  private constructor();
39
+ startAudioWorklet(worklets: Worklets, restart: RestartWorklet): EngineWorklet;
29
40
  own<T extends Terminable>(terminable: T): T;
30
41
  ownAll<T extends Terminable>(...terminables: Array<T>): void;
31
42
  spawn(): Terminator;
43
+ get env(): ProjectEnv;
32
44
  get bpm(): number;
33
45
  get rootBoxAdapter(): RootBoxAdapter;
34
46
  get timelineBoxAdapter(): TimelineBoxAdapter;
35
- get audioManager(): SampleManager;
47
+ get sampleManager(): SampleManager;
36
48
  get clipSequencing(): ClipSequencing;
37
49
  get isAudioContext(): boolean;
38
50
  get isMainThread(): boolean;
@@ -41,6 +53,5 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
41
53
  toArrayBuffer(): ArrayBufferLike;
42
54
  copy(): Project;
43
55
  terminate(): void;
44
- toDawProject(): string;
45
56
  }
46
57
  //# sourceMappingURL=Project.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../src/Project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,UAAU,EAAE,eAAe,EAAE,UAAU,EAAO,MAAM,kBAAkB,CAAA;AAC9G,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAA;AAClD,OAAO,EACH,WAAW,EACX,YAAY,EACZ,KAAK,EAEL,OAAO,EACP,WAAW,EACX,gBAAgB,EACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,WAAW,EACX,kBAAkB,EAClB,cAAc,EAGd,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EAClB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,qBAAqB,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAE7E,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AACvC,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAKvC,qBAAa,OAAQ,YAAW,kBAAkB,EAAE,UAAU,EAAE,eAAe;;IAC3E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO;IAuCpC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO;IAM/D,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,GAAG,OAAO;IAQ5E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAE1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAA;IAC3C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAA;IAClC,QAAQ,CAAC,eAAe,EAAE,YAAY,CAAA;IACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IAEjC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAA;IAC/C,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAA;IACvD,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAA;IAC/C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IAErB,OAAO;IA8BP,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC;IAC3C,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;IAC5D,KAAK,IAAI,UAAU;IAEnB,IAAI,GAAG,IAAI,MAAM,CAAyC;IAC1D,IAAI,cAAc,IAAI,cAAc,CAAmE;IACvG,IAAI,kBAAkB,IAAI,kBAAkB,CAA2E;IACvH,IAAI,YAAY,IAAI,aAAa,CAAiC;IAClE,IAAI,cAAc,IAAI,cAAc,CAAkD;IACtF,IAAI,cAAc,IAAI,OAAO,CAAe;IAC5C,IAAI,YAAY,IAAI,OAAO,CAAc;IACzC,IAAI,qBAAqB,IAAI,qBAAqB,CAAkD;IAEpG,IAAI,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAWtC;IAED,aAAa,IAAI,eAAe;IAiBhC,IAAI,IAAI,OAAO;IAEf,SAAS,IAAI,IAAI;IAEjB,YAAY,IAAI,MAAM;CACzB"}
1
+ {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../src/Project.ts"],"names":[],"mappings":"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"}
package/dist/Project.js CHANGED
@@ -7,8 +7,9 @@ import { AudioUnitType } from "@opendaw/studio-enums";
7
7
  import { Mixer } from "./Mixer";
8
8
  import { ProjectApi } from "./ProjectApi";
9
9
  import { ProjectMigration } from "./ProjectMigration";
10
+ import { CaptureManager } from "./capture/CaptureManager";
11
+ import { EngineFacade } from "./EngineFacade";
10
12
  // Main Entry Point for a Project
11
- //
12
13
  export class Project {
13
14
  static new(env) {
14
15
  const boxGraph = new BoxGraph(Option.wrap(BoxIO.create));
@@ -66,6 +67,7 @@ export class Project {
66
67
  masterAudioUnit;
67
68
  timelineBox;
68
69
  api;
70
+ captureManager;
69
71
  editing;
70
72
  selection;
71
73
  boxAdapters;
@@ -73,6 +75,7 @@ export class Project {
73
75
  parameterFieldAdapters;
74
76
  liveStreamReceiver;
75
77
  mixer;
78
+ engine = new EngineFacade();
76
79
  constructor(env, boxGraph, { rootBox, userInterfaceBox, masterBusBox, masterAudioUnit, timelineBox }) {
77
80
  this.#env = env;
78
81
  this.boxGraph = boxGraph;
@@ -81,8 +84,8 @@ export class Project {
81
84
  this.masterBusBox = masterBusBox;
82
85
  this.masterAudioUnit = masterAudioUnit;
83
86
  this.timelineBox = timelineBox;
84
- this.liveStreamReceiver = this.#terminator.own(new LiveStreamReceiver());
85
87
  this.api = new ProjectApi(this);
88
+ this.captureManager = this.#terminator.own(new CaptureManager(this));
86
89
  this.editing = new Editing(this.boxGraph);
87
90
  this.selection = new VertexSelection(this.editing, this.boxGraph);
88
91
  this.parameterFieldAdapters = new ParameterFieldAdapters();
@@ -90,16 +93,37 @@ export class Project {
90
93
  this.userEditingManager = new UserEditingManager(this.editing);
91
94
  this.userEditingManager.follow(this.userInterfaceBox);
92
95
  this.selection.switch(this.userInterfaceBox.selection);
96
+ this.liveStreamReceiver = this.#terminator.own(new LiveStreamReceiver());
93
97
  this.mixer = new Mixer(this.rootBoxAdapter.audioUnits);
94
98
  console.debug(`Project was created on ${this.rootBoxAdapter.created.toString()}`);
95
99
  }
100
+ startAudioWorklet(worklets, restart) {
101
+ console.debug(`start AudioWorklet`);
102
+ const lifecycle = this.#terminator.spawn();
103
+ const engine = lifecycle.own(worklets.createEngine(this));
104
+ const handler = async (event) => {
105
+ console.warn(event);
106
+ // we will only accept the first error
107
+ engine.removeEventListener("error", handler);
108
+ engine.removeEventListener("processorerror", handler);
109
+ restart.unload(event);
110
+ lifecycle.terminate();
111
+ restart.load(this.startAudioWorklet(worklets, restart));
112
+ };
113
+ engine.addEventListener("error", handler);
114
+ engine.addEventListener("processorerror", handler);
115
+ engine.connect(engine.context.destination);
116
+ this.engine.setClient(engine);
117
+ return engine;
118
+ }
96
119
  own(terminable) { return this.#terminator.own(terminable); }
97
120
  ownAll(...terminables) { return this.#terminator.ownAll(...terminables); }
98
121
  spawn() { return this.#terminator.spawn(); }
122
+ get env() { return this.#env; }
99
123
  get bpm() { return this.timelineBox.bpm.getValue(); }
100
124
  get rootBoxAdapter() { return this.boxAdapters.adapterFor(this.rootBox, RootBoxAdapter); }
101
125
  get timelineBoxAdapter() { return this.boxAdapters.adapterFor(this.timelineBox, TimelineBoxAdapter); }
102
- get audioManager() { return this.#env.sampleManager; }
126
+ get sampleManager() { return this.#env.sampleManager; }
103
127
  get clipSequencing() { return panic("Only available in audio context"); }
104
128
  get isAudioContext() { return false; }
105
129
  get isMainThread() { return true; }
@@ -134,5 +158,4 @@ export class Project {
134
158
  }
135
159
  copy() { return Project.load(this.#env, this.toArrayBuffer()); }
136
160
  terminate() { this.#terminator.terminate(); }
137
- toDawProject() { return panic("Not implemented"); }
138
161
  }
@@ -39,7 +39,6 @@ export type NoteRegionParams = {
39
39
  };
40
40
  export declare class ProjectApi {
41
41
  #private;
42
- static readonly AudioUnitOrdering: Record<string, int>;
43
42
  constructor(project: Project);
44
43
  setBpm(value: number): void;
45
44
  catchupAndSubscribeBpm(observer: Observer<number>): Subscription;
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../src/ProjectApi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../src/ProjectApi.ts"],"names":[],"mappings":"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"}
@@ -2,17 +2,12 @@ import { asDefined, asInstanceOf, assert, clamp, Option, Strings, UUID } from "@
2
2
  import { PPQN } from "@opendaw/lib-dsp";
3
3
  import { IndexedBox, StringField } from "@opendaw/lib-box";
4
4
  import { AudioUnitType } from "@opendaw/studio-enums";
5
- import { AudioBusBox, AudioUnitBox, NoteClipBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, TrackBox, ValueClipBox, ValueEventCollectionBox, ValueRegionBox } from "@opendaw/studio-boxes";
5
+ import { AudioBusBox, AudioUnitBox, CaptureAudioBox, CaptureMidiBox, NoteClipBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, TrackBox, ValueClipBox, ValueEventCollectionBox, ValueRegionBox } from "@opendaw/studio-boxes";
6
6
  import { IconSymbol, TrackType } from "@opendaw/studio-adapters";
7
7
  import { ColorCodes } from "./ColorCodes";
8
+ import { AudioUnitOrdering } from "./AudioUnitOrdering";
8
9
  // noinspection JSUnusedGlobalSymbols
9
10
  export class ProjectApi {
10
- static AudioUnitOrdering = {
11
- [AudioUnitType.Instrument]: 0,
12
- [AudioUnitType.Aux]: 1,
13
- [AudioUnitType.Bus]: 2,
14
- [AudioUnitType.Output]: 3
15
- };
16
11
  #project;
17
12
  constructor(project) { this.#project = project; }
18
13
  setBpm(value) {
@@ -34,7 +29,7 @@ export class ProjectApi {
34
29
  const inputBox = asDefined(asInstanceOf(box, AudioUnitBox).input.pointerHub.incoming().at(0)).box;
35
30
  return "label" in inputBox && inputBox.label instanceof StringField ? inputBox.label.getValue() : "N/A";
36
31
  });
37
- const audioUnitBox = this.#createAudioUnit(AudioUnitType.Instrument, index);
32
+ const audioUnitBox = this.#createAudioUnit(AudioUnitType.Instrument, this.#trackTypeToCapture(boxGraph, trackType), index);
38
33
  const uniqueName = Strings.getUniqueName(existingNames, name ?? defaultName);
39
34
  const iconSymbol = icon ?? defaultIcon;
40
35
  const instrumentBox = create(boxGraph, audioUnitBox.input, uniqueName, iconSymbol);
@@ -58,7 +53,7 @@ export class ProjectApi {
58
53
  box.icon.setValue(IconSymbol.toName(icon));
59
54
  box.color.setValue(color);
60
55
  });
61
- const audioUnitBox = this.#createAudioUnit(type);
56
+ const audioUnitBox = this.#createAudioUnit(type, Option.None);
62
57
  TrackBox.create(boxGraph, UUID.generate(), box => {
63
58
  box.tracks.refer(audioUnitBox.tracks);
64
59
  box.target.refer(audioUnitBox);
@@ -182,7 +177,7 @@ export class ProjectApi {
182
177
  IndexedBox.removeOrder(rootBox.audioUnits, audioUnitBox.index.getValue());
183
178
  audioUnitBox.delete();
184
179
  }
185
- #createAudioUnit(type, index) {
180
+ #createAudioUnit(type, capture, index) {
186
181
  const { boxGraph, rootBox, masterBusBox } = this.#project;
187
182
  const insertIndex = index ?? this.#sortAudioUnitOrdering(type);
188
183
  console.debug(`createAudioUnit type: ${type}, insertIndex: ${insertIndex}`);
@@ -191,6 +186,7 @@ export class ProjectApi {
191
186
  box.output.refer(masterBusBox.input);
192
187
  box.index.setValue(insertIndex);
193
188
  box.type.setValue(type);
189
+ capture.ifSome(capture => box.capture.refer(capture));
194
190
  });
195
191
  }
196
192
  #createTrack({ field, target, trackType, insertIndex }) {
@@ -203,7 +199,6 @@ export class ProjectApi {
203
199
  });
204
200
  }
205
201
  #sortAudioUnitOrdering(type) {
206
- const { AudioUnitOrdering } = ProjectApi;
207
202
  const { rootBox } = this.#project;
208
203
  const boxes = IndexedBox.collectIndexedBoxes(rootBox.audioUnits, AudioUnitBox);
209
204
  const order = AudioUnitOrdering[type];
@@ -219,4 +214,14 @@ export class ProjectApi {
219
214
  }
220
215
  return insertIndex;
221
216
  }
217
+ #trackTypeToCapture(boxGraph, trackType) {
218
+ switch (trackType) {
219
+ case TrackType.Audio:
220
+ return Option.wrap(CaptureAudioBox.create(boxGraph, UUID.generate()));
221
+ case TrackType.Notes:
222
+ return Option.wrap(CaptureMidiBox.create(boxGraph, UUID.generate()));
223
+ default:
224
+ return Option.None;
225
+ }
226
+ }
222
227
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMigration.d.ts","sourceRoot":"","sources":["../src/ProjectMigration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAUvD,qBAAa,gBAAgB;IACzB,MAAM,CAAC,OAAO,CAAC,EAAC,QAAQ,EAAE,cAAc,EAAC,EAAE,cAAc,CAAC,QAAQ,GAAG,IAAI;CAoD5E"}
1
+ {"version":3,"file":"ProjectMigration.d.ts","sourceRoot":"","sources":["../src/ProjectMigration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAevD,qBAAa,gBAAgB;IACzB,MAAM,CAAC,OAAO,CAAC,EAAC,QAAQ,EAAE,cAAc,EAAC,EAAE,cAAc,CAAC,QAAQ,GAAG,IAAI;CAoE5E"}
@@ -1,5 +1,6 @@
1
- import { GrooveShuffleBox, ValueEventCurveBox } from "@opendaw/studio-boxes";
2
- import { asInstanceOf, UUID } from "@opendaw/lib-std";
1
+ import { CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, ValueEventCurveBox } from "@opendaw/studio-boxes";
2
+ import { asDefined, asInstanceOf, UUID } from "@opendaw/lib-std";
3
+ import { AudioUnitType } from "@opendaw/studio-enums";
3
4
  export class ProjectMigration {
4
5
  static migrate({ boxGraph, mandatoryBoxes }) {
5
6
  const { rootBox } = mandatoryBoxes;
@@ -16,7 +17,8 @@ export class ProjectMigration {
16
17
  boxGraph.endTransaction();
17
18
  }
18
19
  // TODO We can remove this when we delete all not-migrated, local(!) project files from my machine
19
- boxGraph.boxes().forEach(box => box.accept({
20
+ // We need to run on a copy, because we might add more boxes during the migration
21
+ boxGraph.boxes().slice().forEach(box => box.accept({
20
22
  visitZeitgeistDeviceBox: (box) => {
21
23
  if (box.groove.targetAddress.isEmpty()) {
22
24
  console.debug("Migrate 'ZeitgeistDeviceBox' to GrooveShuffleBox");
@@ -54,6 +56,21 @@ export class ProjectMigration {
54
56
  boxGraph.endTransaction();
55
57
  }
56
58
  }
59
+ },
60
+ visitAudioUnitBox: (box) => {
61
+ if (box.type.getValue() !== AudioUnitType.Instrument || box.capture.nonEmpty()) {
62
+ return;
63
+ }
64
+ boxGraph.beginTransaction();
65
+ const captureBox = asDefined(box.input.pointerHub.incoming().at(0)?.box
66
+ .accept({
67
+ visitVaporisateurDeviceBox: () => CaptureMidiBox.create(boxGraph, UUID.generate()),
68
+ visitNanoDeviceBox: () => CaptureMidiBox.create(boxGraph, UUID.generate()),
69
+ visitPlayfieldDeviceBox: () => CaptureMidiBox.create(boxGraph, UUID.generate()),
70
+ visitTapeDeviceBox: () => CaptureAudioBox.create(boxGraph, UUID.generate())
71
+ }));
72
+ box.capture.refer(captureBox);
73
+ boxGraph.endTransaction();
57
74
  }
58
75
  }));
59
76
  }
@@ -1,6 +1,18 @@
1
- import { RingBuffer } from "@opendaw/studio-adapters";
2
- export declare class RecordingWorklet extends AudioWorkletNode {
1
+ import { int, Observer, Option, Subscription, Terminable, UUID } from "@opendaw/lib-std";
2
+ import { AudioData, RingBuffer, SampleLoader, SampleLoaderState } from "@opendaw/studio-adapters";
3
+ import { Peaks } from "@opendaw/lib-fusion";
4
+ export declare class RecordingWorklet extends AudioWorkletNode implements Terminable, SampleLoader {
3
5
  #private;
4
- constructor(context: BaseAudioContext, config: RingBuffer.Config);
6
+ readonly uuid: UUID.Format;
7
+ constructor(context: BaseAudioContext, config: RingBuffer.Config, outputLatency: number);
8
+ get numberOfFrames(): int;
9
+ get data(): Option<AudioData>;
10
+ get peaks(): Option<Peaks>;
11
+ get state(): SampleLoaderState;
12
+ invalidate(): void;
13
+ subscribe(observer: Observer<SampleLoaderState>): Subscription;
14
+ finalize(): Promise<void>;
15
+ terminate(): void;
16
+ toString(): string;
5
17
  }
6
18
  //# sourceMappingURL=RecordingWorklet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RecordingWorklet.d.ts","sourceRoot":"","sources":["../src/RecordingWorklet.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,UAAU,EAAC,MAAM,0BAA0B,CAAA;AAIrE,qBAAa,gBAAiB,SAAQ,gBAAgB;;gBAGtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM;CAuBnE"}
1
+ {"version":3,"file":"RecordingWorklet.d.ts","sourceRoot":"","sources":["../src/RecordingWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,GAAG,EAIH,QAAQ,EACR,MAAM,EAEN,YAAY,EACZ,UAAU,EACV,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,SAAS,EAET,UAAU,EACV,YAAY,EACZ,iBAAiB,EAEpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,KAAK,EAAgC,MAAM,qBAAqB,CAAA;AA2CxE,qBAAa,gBAAiB,SAAQ,gBAAiB,YAAW,UAAU,EAAE,YAAY;;IACtF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAkB;gBAahC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM;IAgCvF,IAAI,cAAc,IAAI,GAAG,CAA6C;IACtE,IAAI,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,CAAoB;IACjD,IAAI,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAqB;IAC/C,IAAI,KAAK,IAAI,iBAAiB,CAAqB;IAEnD,UAAU,IAAI,IAAI;IAElB,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,YAAY;IAQxD,QAAQ;IAyBd,SAAS,IAAI,IAAI;IAKjB,QAAQ,IAAI,MAAM;CAMrB"}
@@ -1,31 +1,126 @@
1
- import { EmptyExec } from "@opendaw/lib-std";
2
- import { Files } from "@opendaw/lib-dom";
1
+ import { Arrays, assert, ByteArrayInput, isUndefined, Notifier, Option, Progress, Terminable, UUID } from "@opendaw/lib-std";
3
2
  import { mergeChunkPlanes, RingBuffer } from "@opendaw/studio-adapters";
4
- import { encodeWavFloat } from "./Wav";
3
+ import { SamplePeaks, SamplePeakWorker } from "@opendaw/lib-fusion";
5
4
  import { RenderQuantum } from "./RenderQuantum";
5
+ import { WorkerAgents } from "./WorkerAgents";
6
+ import { SampleStorage } from "./samples/SampleStorage";
7
+ import { BPMTools } from "@opendaw/lib-dsp";
8
+ class PeaksWriter {
9
+ numChannels;
10
+ data;
11
+ stages;
12
+ dataOffset = 0;
13
+ shift = 7;
14
+ dataIndex;
15
+ numFrames = 0 | 0;
16
+ constructor(numChannels) {
17
+ this.numChannels = numChannels;
18
+ this.data = Arrays.create(() => new Int32Array(1 << 20), numChannels); // TODO auto-resize
19
+ this.dataIndex = new Int32Array(numChannels);
20
+ this.stages = [this];
21
+ }
22
+ get numPeaks() { return Math.ceil(this.numFrames / (1 << this.shift)); }
23
+ unitsEachPeak() { return 1 << this.shift; }
24
+ append(frames) {
25
+ for (let channel = 0; channel < this.numChannels; ++channel) {
26
+ const channelFrames = frames[channel];
27
+ assert(channelFrames.length === RenderQuantum, "Invalid number of frames.");
28
+ let min = Number.POSITIVE_INFINITY;
29
+ let max = Number.NEGATIVE_INFINITY;
30
+ for (let i = 0; i < RenderQuantum; ++i) {
31
+ const frame = channelFrames[i];
32
+ min = Math.min(frame, min);
33
+ max = Math.max(frame, max);
34
+ }
35
+ this.data[channel][this.dataIndex[channel]++] = SamplePeakWorker.pack(min, max);
36
+ }
37
+ this.numFrames += RenderQuantum;
38
+ }
39
+ nearest(_unitsPerPixel) { return this.stages.at(0) ?? null; }
40
+ }
6
41
  export class RecordingWorklet extends AudioWorkletNode {
42
+ uuid = UUID.generate();
43
+ #output;
44
+ #notifier;
7
45
  #reader;
8
- constructor(context, config) {
46
+ #peakWriter;
47
+ #data = Option.None;
48
+ #peaks = Option.None;
49
+ #isRecording = true;
50
+ #truncateLatency;
51
+ #state = { type: "record" };
52
+ constructor(context, config, outputLatency) {
9
53
  super(context, "recording-processor", {
10
54
  numberOfInputs: 1,
11
55
  channelCount: config.numberOfChannels,
12
56
  channelCountMode: "explicit",
13
57
  processorOptions: config
14
58
  });
15
- const sampleRate = this.context.sampleRate;
16
- const output = [];
17
- const seconds = 3.1452;
18
- const numFrames = Math.ceil(sampleRate * seconds);
19
- console.debug(`numFrames: ${numFrames}`);
59
+ if (isUndefined(outputLatency)) {
60
+ // TODO Talk to the user
61
+ console.warn("outputLatency is undefined. Please use Chrome.");
62
+ }
63
+ this.#peakWriter = new PeaksWriter(config.numberOfChannels);
64
+ this.#truncateLatency = Math.floor((outputLatency ?? 0) * this.context.sampleRate / RenderQuantum);
65
+ this.#output = [];
66
+ this.#notifier = new Notifier();
20
67
  this.#reader = RingBuffer.reader(config, array => {
21
- output.push(array);
22
- if (output.length * RenderQuantum >= numFrames) {
23
- this.#reader.stop();
24
- const channels = mergeChunkPlanes(output, numFrames);
25
- const wav = encodeWavFloat({ channels, numFrames, sampleRate });
26
- Files.save(wav, { suggestedName: "recording.wav" })
27
- .then(() => console.debug("WAV saved"), EmptyExec);
68
+ if (this.#isRecording) {
69
+ if (this.#truncateLatency === 0) {
70
+ this.#output.push(array);
71
+ this.#peakWriter.append(array);
72
+ }
73
+ else {
74
+ if (--this.#truncateLatency === 0) {
75
+ this.#peaks = Option.wrap(this.#peakWriter);
76
+ }
77
+ }
28
78
  }
29
79
  });
30
80
  }
81
+ get numberOfFrames() { return this.#output.length * RenderQuantum; }
82
+ get data() { return this.#data; }
83
+ get peaks() { return this.#peaks; }
84
+ get state() { return this.#state; }
85
+ invalidate() { }
86
+ subscribe(observer) {
87
+ if (this.#state.type === "loaded") {
88
+ observer(this.#state);
89
+ return Terminable.Empty;
90
+ }
91
+ return this.#notifier.subscribe(observer);
92
+ }
93
+ async finalize() {
94
+ this.#reader.stop();
95
+ this.#isRecording = false;
96
+ const sample_rate = this.context.sampleRate;
97
+ const numberOfFrames = this.#output.length * RenderQuantum;
98
+ const numberOfChannels = this.channelCount;
99
+ const frames = mergeChunkPlanes(this.#output, RenderQuantum, numberOfFrames);
100
+ const audioData = {
101
+ sampleRate: sample_rate,
102
+ numberOfChannels,
103
+ numberOfFrames,
104
+ frames
105
+ };
106
+ this.#data = Option.wrap(audioData);
107
+ const shifts = SamplePeaks.findBestFit(numberOfFrames);
108
+ const peaks = await WorkerAgents
109
+ .Peak.generateAsync(Progress.Empty, shifts, frames, numberOfFrames, numberOfChannels);
110
+ this.#peaks = Option.wrap(SamplePeaks.from(new ByteArrayInput(peaks)));
111
+ const bpm = BPMTools.detect(frames[0], sample_rate);
112
+ const duration = numberOfFrames / sample_rate;
113
+ const meta = { name: "Recording", bpm, sample_rate, duration };
114
+ await SampleStorage.store(this.uuid, audioData, peaks, meta);
115
+ this.#setState({ type: "loaded" });
116
+ }
117
+ terminate() {
118
+ this.#reader.stop();
119
+ this.#isRecording = false;
120
+ }
121
+ toString() { return `{RecordingWorklet}`; }
122
+ #setState(value) {
123
+ this.#state = value;
124
+ this.#notifier.notify(this.#state);
125
+ }
31
126
  }
@@ -1,10 +1,10 @@
1
1
  import { Option } from "@opendaw/lib-std";
2
- import type { OpfsProtocol, PeakProtocol } from "@opendaw/lib-fusion";
2
+ import type { OpfsProtocol, SamplePeakProtocol } from "@opendaw/lib-fusion";
3
3
  import { Messenger } from "@opendaw/lib-runtime";
4
4
  export declare class WorkerAgents {
5
5
  static install(workerURL: string): void;
6
6
  static messenger: Option<Messenger>;
7
- static get Peak(): PeakProtocol;
7
+ static get Peak(): SamplePeakProtocol;
8
8
  static get Opfs(): OpfsProtocol;
9
9
  }
10
10
  //# sourceMappingURL=WorkerAgents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerAgents.d.ts","sourceRoot":"","sources":["../src/WorkerAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,MAAM,EAAY,MAAM,kBAAkB,CAAA;AACzE,OAAO,KAAK,EAAC,YAAY,EAAE,YAAY,EAAC,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAE5D,qBAAa,YAAY;IACrB,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKvC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAc;IAGjD,MAAM,KAAK,IAAI,IAAI,YAAY,CAa9B;IAGD,MAAM,KAAK,IAAI,IAAI,YAAY,CAS9B;CACJ"}
1
+ {"version":3,"file":"WorkerAgents.d.ts","sourceRoot":"","sources":["../src/WorkerAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,MAAM,EAAY,MAAM,kBAAkB,CAAA;AACzE,OAAO,KAAK,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAEzE,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAE5D,qBAAa,YAAY;IACrB,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKvC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAc;IAGjD,MAAM,KAAK,IAAI,IAAI,kBAAkB,CAapC;IAGD,MAAM,KAAK,IAAI,IAAI,YAAY,CAS9B;CACJ"}
@@ -11,6 +11,6 @@ export declare class Worklets {
11
11
  constructor(context: BaseAudioContext);
12
12
  createMeter(numberOfChannels: int): MeterWorklet;
13
13
  createEngine(project: Project, exportConfiguration?: ExportStemsConfiguration): EngineWorklet;
14
- createRecording(numberOfChannels: int, numChunks: int): RecordingWorklet;
14
+ createRecording(numberOfChannels: int, numChunks: int, outputLatency: number): RecordingWorklet;
15
15
  }
16
16
  //# sourceMappingURL=Worklets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Worklets.d.ts","sourceRoot":"","sources":["../src/Worklets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,wBAAwB,EAAa,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,qBAAa,QAAQ;;WACJ,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQtF,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,QAAQ;gBAMnC,OAAO,EAAE,gBAAgB;IAErC,WAAW,CAAC,gBAAgB,EAAE,GAAG,GAAG,YAAY;IAIhD,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,wBAAwB,GAAG,aAAa;IAI7F,eAAe,CAAC,gBAAgB,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,GAAG,gBAAgB;CAO3E"}
1
+ {"version":3,"file":"Worklets.d.ts","sourceRoot":"","sources":["../src/Worklets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,wBAAwB,EAAa,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,qBAAa,QAAQ;;WACJ,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQtF,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,QAAQ;gBAMnC,OAAO,EAAE,gBAAgB;IAErC,WAAW,CAAC,gBAAgB,EAAE,GAAG,GAAG,YAAY;IAIhD,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,wBAAwB,GAAG,aAAa;IAI7F,eAAe,CAAC,gBAAgB,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,GAAG,gBAAgB;CAOlG"}
package/dist/Worklets.js CHANGED
@@ -21,11 +21,11 @@ export class Worklets {
21
21
  createEngine(project, exportConfiguration) {
22
22
  return new EngineWorklet(this.#context, project, exportConfiguration);
23
23
  }
24
- createRecording(numberOfChannels, numChunks) {
24
+ createRecording(numberOfChannels, numChunks, outputLatency) {
25
25
  const audioBytes = numberOfChannels * numChunks * RenderQuantum * Float32Array.BYTES_PER_ELEMENT;
26
26
  const pointerBytes = Int32Array.BYTES_PER_ELEMENT * 2;
27
27
  const sab = new SharedArrayBuffer(audioBytes + pointerBytes);
28
28
  const buffer = { sab, numChunks, numberOfChannels, bufferSize: RenderQuantum };
29
- return new RecordingWorklet(this.#context, buffer);
29
+ return new RecordingWorklet(this.#context, buffer, outputLatency);
30
30
  }
31
31
  }
@@ -0,0 +1,22 @@
1
+ import { MutableObservableValue, Option, Terminable, UUID } from "@opendaw/lib-std";
2
+ import { AudioUnitBox } from "@opendaw/studio-boxes";
3
+ import { CaptureBox } from "@opendaw/studio-adapters";
4
+ import { RecordingContext } from "./RecordingContext";
5
+ import { CaptureManager } from "./CaptureManager";
6
+ export declare abstract class Capture<BOX extends CaptureBox = CaptureBox> implements Terminable {
7
+ #private;
8
+ protected constructor(manager: CaptureManager, audioUnitBox: AudioUnitBox, captureBox: BOX);
9
+ abstract get deviceLabel(): Option<string>;
10
+ abstract prepareRecording(context: RecordingContext): Promise<void>;
11
+ abstract startRecording(context: RecordingContext): Terminable;
12
+ get uuid(): UUID.Format;
13
+ get manager(): CaptureManager;
14
+ get audioUnitBox(): AudioUnitBox;
15
+ get captureBox(): BOX;
16
+ get armed(): MutableObservableValue<boolean>;
17
+ get deviceId(): MutableObservableValue<Option<string>>;
18
+ own<T extends Terminable>(terminable: T): T;
19
+ ownAll<T extends Terminable>(...terminables: ReadonlyArray<T>): void;
20
+ terminate(): void;
21
+ }
22
+ //# sourceMappingURL=Capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Capture.d.ts","sourceRoot":"","sources":["../../src/capture/Capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,sBAAsB,EACtB,MAAM,EACN,UAAU,EAEV,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAA;AAEnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,8BAAsB,OAAO,CAAC,GAAG,SAAS,UAAU,GAAG,UAAU,CAAE,YAAW,UAAU;;IAUpF,SAAS,aAAa,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG;IAmB1F,QAAQ,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAAA;IAC1C,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,UAAU;IAE9D,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,CAAyC;IAChE,IAAI,OAAO,IAAI,cAAc,CAAuB;IACpD,IAAI,YAAY,IAAI,YAAY,CAA4B;IAC5D,IAAI,UAAU,IAAI,GAAG,CAA0B;IAC/C,IAAI,KAAK,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAqB;IACjE,IAAI,QAAQ,IAAI,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAwB;IAE9E,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC;IAC3C,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI;IACpE,SAAS,IAAI,IAAI;CACpB"}