@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.
- package/dist/AudioDevices.d.ts +8 -0
- package/dist/AudioDevices.d.ts.map +1 -0
- package/dist/AudioDevices.js +34 -0
- package/dist/AudioUnitOrdering.d.ts +3 -0
- package/dist/AudioUnitOrdering.d.ts.map +1 -0
- package/dist/AudioUnitOrdering.js +7 -0
- package/dist/EffectBox.d.ts +2 -2
- package/dist/EffectBox.d.ts.map +1 -1
- package/dist/Engine.d.ts +31 -10
- package/dist/Engine.d.ts.map +1 -1
- package/dist/EngineFacade.d.ts +22 -11
- package/dist/EngineFacade.d.ts.map +1 -1
- package/dist/EngineFacade.js +39 -22
- package/dist/EngineWorklet.d.ts +22 -13
- package/dist/EngineWorklet.d.ts.map +1 -1
- package/dist/EngineWorklet.js +47 -56
- package/dist/MeterWorklet.d.ts.map +1 -1
- package/dist/MeterWorklet.js +2 -2
- package/dist/MidiDevices.d.ts +12 -0
- package/dist/MidiDevices.d.ts.map +1 -0
- package/dist/MidiDevices.js +92 -0
- package/dist/Project.d.ts +14 -3
- package/dist/Project.d.ts.map +1 -1
- package/dist/Project.js +27 -4
- package/dist/ProjectApi.d.ts +0 -1
- package/dist/ProjectApi.d.ts.map +1 -1
- package/dist/ProjectApi.js +16 -11
- package/dist/ProjectMigration.d.ts.map +1 -1
- package/dist/ProjectMigration.js +20 -3
- package/dist/RecordingWorklet.d.ts +15 -3
- package/dist/RecordingWorklet.d.ts.map +1 -1
- package/dist/RecordingWorklet.js +111 -16
- package/dist/WorkerAgents.d.ts +2 -2
- package/dist/WorkerAgents.d.ts.map +1 -1
- package/dist/Worklets.d.ts +1 -1
- package/dist/Worklets.d.ts.map +1 -1
- package/dist/Worklets.js +2 -2
- package/dist/capture/Capture.d.ts +22 -0
- package/dist/capture/Capture.d.ts.map +1 -0
- package/dist/capture/Capture.js +32 -0
- package/dist/capture/CaptureAudio.d.ts +17 -0
- package/dist/capture/CaptureAudio.d.ts.map +1 -0
- package/dist/capture/CaptureAudio.js +106 -0
- package/dist/capture/CaptureManager.d.ts +12 -0
- package/dist/capture/CaptureManager.d.ts.map +1 -0
- package/dist/capture/CaptureManager.js +38 -0
- package/dist/capture/CaptureMidi.d.ts +13 -0
- package/dist/capture/CaptureMidi.d.ts.map +1 -0
- package/dist/capture/CaptureMidi.js +106 -0
- package/dist/capture/RecordAudio.d.ts +19 -0
- package/dist/capture/RecordAudio.d.ts.map +1 -0
- package/dist/capture/RecordAudio.js +66 -0
- package/dist/capture/RecordMidi.d.ts +13 -0
- package/dist/capture/RecordMidi.d.ts.map +1 -0
- package/dist/capture/RecordMidi.js +85 -0
- package/dist/capture/RecordTrack.d.ts +7 -0
- package/dist/capture/RecordTrack.d.ts.map +1 -0
- package/dist/capture/RecordTrack.js +23 -0
- package/dist/capture/Recording.d.ts +9 -0
- package/dist/capture/Recording.d.ts.map +1 -0
- package/dist/capture/Recording.js +65 -0
- package/dist/capture/RecordingContext.d.ts +10 -0
- package/dist/capture/RecordingContext.d.ts.map +1 -0
- package/dist/capture/RecordingContext.js +1 -0
- package/dist/dawproject/AudioUnitExportLayout.d.ts +10 -0
- package/dist/dawproject/AudioUnitExportLayout.d.ts.map +1 -0
- package/dist/dawproject/AudioUnitExportLayout.js +64 -0
- package/dist/dawproject/BuiltinDevices.d.ts +9 -0
- package/dist/dawproject/BuiltinDevices.d.ts.map +1 -0
- package/dist/dawproject/BuiltinDevices.js +70 -0
- package/dist/dawproject/{DawProjectIO.d.ts → DawProject.d.ts} +6 -4
- package/dist/dawproject/DawProject.d.ts.map +1 -0
- package/dist/dawproject/DawProject.js +48 -0
- package/dist/dawproject/DawProjectExporter.d.ts +7 -6
- package/dist/dawproject/DawProjectExporter.d.ts.map +1 -1
- package/dist/dawproject/DawProjectExporter.js +245 -22
- package/dist/dawproject/DawProjectExporter.test.js +38 -6
- package/dist/dawproject/DawProjectImport.d.ts +12 -0
- package/dist/dawproject/DawProjectImport.d.ts.map +1 -0
- package/dist/dawproject/DawProjectImport.js +389 -0
- package/dist/dawproject/DawProjectImport.test.js +6 -7
- package/dist/dawproject/DeviceIO.d.ts +8 -0
- package/dist/dawproject/DeviceIO.d.ts.map +1 -0
- package/dist/dawproject/DeviceIO.js +63 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/processors.js +3 -3
- package/dist/processors.js.map +4 -4
- package/dist/samples/MainThreadSampleLoader.d.ts +1 -0
- package/dist/samples/MainThreadSampleLoader.d.ts.map +1 -1
- package/dist/samples/MainThreadSampleLoader.js +10 -6
- package/dist/samples/MainThreadSampleManager.d.ts +5 -5
- package/dist/samples/MainThreadSampleManager.d.ts.map +1 -1
- package/dist/samples/MainThreadSampleManager.js +1 -0
- package/dist/samples/SampleProvider.d.ts +2 -2
- package/dist/samples/SampleProvider.d.ts.map +1 -1
- package/dist/samples/SampleStorage.d.ts.map +1 -1
- package/dist/samples/SampleStorage.js +2 -3
- package/dist/workers.js +2 -2
- package/dist/workers.js.map +4 -4
- package/package.json +15 -15
- package/dist/dawproject/DawProjectIO.d.ts.map +0 -1
- package/dist/dawproject/DawProjectIO.js +0 -31
- package/dist/dawproject/DawProjectImporter.d.ts +0 -12
- package/dist/dawproject/DawProjectImporter.d.ts.map +0 -1
- package/dist/dawproject/DawProjectImporter.js +0 -273
- package/dist/samples/SamplePeaks.d.ts +0 -6
- package/dist/samples/SamplePeaks.d.ts.map +0 -1
- 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
|
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
|
package/dist/Project.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../src/Project.ts"],"names":[],"mappings":"AAAA,OAAO,
|
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
|
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
|
}
|
package/dist/ProjectApi.d.ts
CHANGED
@@ -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;
|
package/dist/ProjectApi.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../src/ProjectApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,EACL,GAAG,EACH,QAAQ,EACR,MAAM,EAEN,YAAY,EAEf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,
|
1
|
+
{"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../src/ProjectApi.ts"],"names":[],"mappings":"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"}
|
package/dist/ProjectApi.js
CHANGED
@@ -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;
|
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"}
|
package/dist/ProjectMigration.js
CHANGED
@@ -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
|
-
|
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 {
|
2
|
-
|
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
|
-
|
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":"
|
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"}
|
package/dist/RecordingWorklet.js
CHANGED
@@ -1,31 +1,126 @@
|
|
1
|
-
import {
|
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 {
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
}
|
package/dist/WorkerAgents.d.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import { Option } from "@opendaw/lib-std";
|
2
|
-
import type { OpfsProtocol,
|
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():
|
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,
|
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"}
|
package/dist/Worklets.d.ts
CHANGED
@@ -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
|
package/dist/Worklets.d.ts.map
CHANGED
@@ -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;
|
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"}
|