@opendaw/studio-core 0.0.94 → 0.0.96
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/EffectBox.d.ts +2 -2
- package/dist/EffectBox.d.ts.map +1 -1
- package/dist/EffectFactories.d.ts +3 -0
- package/dist/EffectFactories.d.ts.map +1 -1
- package/dist/EffectFactories.js +16 -2
- package/dist/Mixer.js +1 -1
- package/dist/capture/CaptureAudio.d.ts +6 -0
- package/dist/capture/CaptureAudio.d.ts.map +1 -1
- package/dist/capture/CaptureAudio.js +75 -9
- package/dist/capture/RecordAudio.d.ts +2 -4
- package/dist/capture/RecordAudio.d.ts.map +1 -1
- package/dist/capture/RecordAudio.js +4 -11
- package/dist/capture/RecordMidi.d.ts.map +1 -1
- package/dist/capture/RecordMidi.js +5 -3
- package/dist/dawproject/DawProjectExporter.d.ts.map +1 -1
- package/dist/dawproject/DawProjectExporter.js +76 -3
- package/dist/dawproject/DawProjectImporter.d.ts.map +1 -1
- package/dist/dawproject/DawProjectImporter.js +71 -3
- package/dist/processors.js +8 -8
- package/dist/processors.js.map +4 -4
- package/dist/samples/GlobalSampleLoaderManager.js +2 -2
- package/package.json +13 -13
package/dist/EffectBox.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ArpeggioDeviceBox, MaximizerDeviceBox, CompressorDeviceBox, CrusherDeviceBox, DattorroReverbDeviceBox, DelayDeviceBox, FoldDeviceBox, ModularDeviceBox, PitchDeviceBox, RevampDeviceBox, ReverbDeviceBox, StereoToolDeviceBox, TidalDeviceBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, VelocityDeviceBox, ZeitgeistDeviceBox } from "@opendaw/studio-boxes";
|
|
2
|
-
export type EffectBox = ArpeggioDeviceBox | PitchDeviceBox | VelocityDeviceBox | ZeitgeistDeviceBox | UnknownMidiEffectDeviceBox | MaximizerDeviceBox | DelayDeviceBox | ReverbDeviceBox | RevampDeviceBox | StereoToolDeviceBox | TidalDeviceBox | ModularDeviceBox | UnknownAudioEffectDeviceBox | CompressorDeviceBox | CrusherDeviceBox | FoldDeviceBox | DattorroReverbDeviceBox;
|
|
1
|
+
import { ArpeggioDeviceBox, MaximizerDeviceBox, CompressorDeviceBox, CrusherDeviceBox, DattorroReverbDeviceBox, DelayDeviceBox, FoldDeviceBox, GateDeviceBox, ModularDeviceBox, PitchDeviceBox, RevampDeviceBox, ReverbDeviceBox, StereoToolDeviceBox, TidalDeviceBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, VelocityDeviceBox, ZeitgeistDeviceBox } from "@opendaw/studio-boxes";
|
|
2
|
+
export type EffectBox = ArpeggioDeviceBox | PitchDeviceBox | VelocityDeviceBox | ZeitgeistDeviceBox | UnknownMidiEffectDeviceBox | MaximizerDeviceBox | DelayDeviceBox | ReverbDeviceBox | RevampDeviceBox | StereoToolDeviceBox | TidalDeviceBox | ModularDeviceBox | UnknownAudioEffectDeviceBox | CompressorDeviceBox | GateDeviceBox | CrusherDeviceBox | FoldDeviceBox | DattorroReverbDeviceBox;
|
|
3
3
|
//# sourceMappingURL=EffectBox.d.ts.map
|
package/dist/EffectBox.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EffectBox.d.ts","sourceRoot":"","sources":["../src/EffectBox.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,EACrB,MAAM,uBAAuB,CAAA;AAE9B,MAAM,MAAM,SAAS,GACf,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,0BAA0B,GACxG,kBAAkB,GAAG,cAAc,GAAG,eAAe,GAAG,eAAe,GAAG,mBAAmB,GAAG,cAAc,GAC9G,gBAAgB,GAAG,2BAA2B,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"EffectBox.d.ts","sourceRoot":"","sources":["../src/EffectBox.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,EACrB,MAAM,uBAAuB,CAAA;AAE9B,MAAM,MAAM,SAAS,GACf,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,0BAA0B,GACxG,kBAAkB,GAAG,cAAc,GAAG,eAAe,GAAG,eAAe,GAAG,mBAAmB,GAAG,cAAc,GAC9G,gBAAgB,GAAG,2BAA2B,GAAG,mBAAmB,GAAG,aAAa,GACpF,gBAAgB,GAAG,aAAa,GAAG,uBAAuB,CAAA"}
|
|
@@ -9,6 +9,7 @@ export declare namespace EffectFactories {
|
|
|
9
9
|
const DattorroReverb: EffectFactory;
|
|
10
10
|
const Maximizer: EffectFactory;
|
|
11
11
|
const Compressor: EffectFactory;
|
|
12
|
+
const Gate: EffectFactory;
|
|
12
13
|
const Reverb: EffectFactory;
|
|
13
14
|
const Crusher: EffectFactory;
|
|
14
15
|
const Fold: EffectFactory;
|
|
@@ -24,6 +25,7 @@ export declare namespace EffectFactories {
|
|
|
24
25
|
const AudioNamed: {
|
|
25
26
|
StereoTool: EffectFactory;
|
|
26
27
|
Compressor: EffectFactory;
|
|
28
|
+
Gate: EffectFactory;
|
|
27
29
|
Delay: EffectFactory;
|
|
28
30
|
Reverb: EffectFactory;
|
|
29
31
|
DattorroReverb: EffectFactory;
|
|
@@ -38,6 +40,7 @@ export declare namespace EffectFactories {
|
|
|
38
40
|
const MergedNamed: {
|
|
39
41
|
StereoTool: EffectFactory;
|
|
40
42
|
Compressor: EffectFactory;
|
|
43
|
+
Gate: EffectFactory;
|
|
41
44
|
Delay: EffectFactory;
|
|
42
45
|
Reverb: EffectFactory;
|
|
43
46
|
DattorroReverb: EffectFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EffectFactories.d.ts","sourceRoot":"","sources":["../src/EffectFactories.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EffectFactories.d.ts","sourceRoot":"","sources":["../src/EffectFactories.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAG7C,yBAAiB,eAAe,CAAC;IACtB,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,KAAK,EAAE,aAYnB,CAAA;IAEM,MAAM,QAAQ,EAAE,aAYtB,CAAA;IAEM,MAAM,SAAS,EAAE,aAsBvB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,KAAK,EAAE,aAcnB,CAAA;IAEM,MAAM,cAAc,EAAE,aAa5B,CAAA;IAEM,MAAM,SAAS,EAAE,aAavB,CAAA;IAEM,MAAM,UAAU,EAAE,aAaxB,CAAA;IAEM,MAAM,IAAI,EAAE,aAalB,CAAA;IAEM,MAAM,MAAM,EAAE,aAcpB,CAAA;IAEM,MAAM,OAAO,EAAE,aAarB,CAAA;IAEM,MAAM,IAAI,EAAE,aAalB,CAAA;IAEM,MAAM,KAAK,EAAE,aAcnB,CAAA;IAEM,MAAM,MAAM,EAAE,aAapB,CAAA;IAEM,MAAM,OAAO,EAAE,aAqCrB,CAAA;IAEM,MAAM,SAAS;;;;;KAAyC,CAAA;IACxD,MAAM,UAAU;;;;;;;;;;;;KAEtB,CAAA;IACM,MAAM,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA4B,CAAA;IACjF,MAAM,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA6B,CAAA;IACnF,MAAM,WAAW;;;;;;;;;;;;;;;;KAAgC,CAAA;IACxD,KAAY,cAAc,GAAG,MAAM,OAAO,SAAS,CAAA;IACnD,KAAY,eAAe,GAAG,MAAM,OAAO,UAAU,CAAA;CACxD"}
|
package/dist/EffectFactories.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UUID } from "@opendaw/lib-std";
|
|
2
|
-
import { ArpeggioDeviceBox, CompressorDeviceBox, CrusherDeviceBox, DattorroReverbDeviceBox, DelayDeviceBox, FoldDeviceBox, GrooveShuffleBox, MaximizerDeviceBox, ModularAudioInputBox, ModularAudioOutputBox, ModularBox, ModularDeviceBox, ModuleConnectionBox, PitchDeviceBox, RevampDeviceBox, ReverbDeviceBox, StereoToolDeviceBox, TidalDeviceBox, VelocityDeviceBox, ZeitgeistDeviceBox } from "@opendaw/studio-boxes";
|
|
2
|
+
import { ArpeggioDeviceBox, CompressorDeviceBox, CrusherDeviceBox, DattorroReverbDeviceBox, DelayDeviceBox, FoldDeviceBox, GateDeviceBox, GrooveShuffleBox, MaximizerDeviceBox, ModularAudioInputBox, ModularAudioOutputBox, ModularBox, ModularDeviceBox, ModuleConnectionBox, PitchDeviceBox, RevampDeviceBox, ReverbDeviceBox, StereoToolDeviceBox, TidalDeviceBox, VelocityDeviceBox, ZeitgeistDeviceBox } from "@opendaw/studio-boxes";
|
|
3
3
|
import { IconSymbol } from "@opendaw/studio-enums";
|
|
4
4
|
import { DeviceManualUrls } from "@opendaw/studio-adapters";
|
|
5
5
|
import { EffectParameterDefaults } from "./EffectParameterDefaults";
|
|
@@ -91,6 +91,7 @@ export var EffectFactories;
|
|
|
91
91
|
box.label.setValue("Delay");
|
|
92
92
|
box.index.setValue(index);
|
|
93
93
|
box.host.refer(hostField);
|
|
94
|
+
box.version.setValue(1);
|
|
94
95
|
})
|
|
95
96
|
};
|
|
96
97
|
EffectFactories.DattorroReverb = {
|
|
@@ -132,6 +133,19 @@ export var EffectFactories;
|
|
|
132
133
|
box.host.refer(hostField);
|
|
133
134
|
})
|
|
134
135
|
};
|
|
136
|
+
EffectFactories.Gate = {
|
|
137
|
+
defaultName: "Gate",
|
|
138
|
+
defaultIcon: IconSymbol.Gate,
|
|
139
|
+
description: "Attenuates signals below a threshold to reduce noise",
|
|
140
|
+
manualPage: DeviceManualUrls.Gate,
|
|
141
|
+
separatorBefore: false,
|
|
142
|
+
type: "audio",
|
|
143
|
+
create: ({ boxGraph }, hostField, index) => GateDeviceBox.create(boxGraph, UUID.generate(), box => {
|
|
144
|
+
box.label.setValue("Gate");
|
|
145
|
+
box.index.setValue(index);
|
|
146
|
+
box.host.refer(hostField);
|
|
147
|
+
})
|
|
148
|
+
};
|
|
135
149
|
EffectFactories.Reverb = {
|
|
136
150
|
defaultName: "Cheap Reverb",
|
|
137
151
|
defaultIcon: IconSymbol.Cube,
|
|
@@ -239,7 +253,7 @@ export var EffectFactories;
|
|
|
239
253
|
};
|
|
240
254
|
EffectFactories.MidiNamed = { Arpeggio: EffectFactories.Arpeggio, Pitch: EffectFactories.Pitch, Velocity: EffectFactories.Velocity, Zeitgeist: EffectFactories.Zeitgeist };
|
|
241
255
|
EffectFactories.AudioNamed = {
|
|
242
|
-
StereoTool: EffectFactories.StereoTool, Compressor: EffectFactories.Compressor, Delay: EffectFactories.Delay, Reverb: EffectFactories.Reverb, DattorroReverb: EffectFactories.DattorroReverb, Revamp: EffectFactories.Revamp, Crusher: EffectFactories.Crusher, Fold: EffectFactories.Fold, Tidal: EffectFactories.Tidal, Maximizer: EffectFactories.Maximizer
|
|
256
|
+
StereoTool: EffectFactories.StereoTool, Compressor: EffectFactories.Compressor, Gate: EffectFactories.Gate, Delay: EffectFactories.Delay, Reverb: EffectFactories.Reverb, DattorroReverb: EffectFactories.DattorroReverb, Revamp: EffectFactories.Revamp, Crusher: EffectFactories.Crusher, Fold: EffectFactories.Fold, Tidal: EffectFactories.Tidal, Maximizer: EffectFactories.Maximizer
|
|
243
257
|
};
|
|
244
258
|
EffectFactories.MidiList = Object.values(EffectFactories.MidiNamed);
|
|
245
259
|
EffectFactories.AudioList = Object.values(EffectFactories.AudioNamed);
|
package/dist/Mixer.js
CHANGED
|
@@ -5,12 +5,18 @@ import { CaptureDevices } from "./CaptureDevices";
|
|
|
5
5
|
export declare class CaptureAudio extends Capture<CaptureAudioBox> {
|
|
6
6
|
#private;
|
|
7
7
|
constructor(manager: CaptureDevices, audioUnitBox: AudioUnitBox, captureAudioBox: CaptureAudioBox);
|
|
8
|
+
get isMonitoring(): boolean;
|
|
9
|
+
set isMonitoring(value: boolean);
|
|
8
10
|
get gainDb(): number;
|
|
11
|
+
get requestChannels(): Option<1 | 2>;
|
|
12
|
+
set requestChannels(value: 1 | 2);
|
|
9
13
|
get stream(): MutableObservableOption<MediaStream>;
|
|
10
14
|
get streamDeviceId(): Option<string>;
|
|
11
15
|
get label(): string;
|
|
12
16
|
get deviceLabel(): Option<string>;
|
|
13
17
|
get streamMediaTrack(): Option<MediaStreamTrack>;
|
|
18
|
+
get outputNode(): Option<AudioNode>;
|
|
19
|
+
get effectiveChannelCount(): number;
|
|
14
20
|
prepareRecording(): Promise<void>;
|
|
15
21
|
startRecording(): Terminable;
|
|
16
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaptureAudio.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,uBAAuB,
|
|
1
|
+
{"version":3,"file":"CaptureAudio.d.ts","sourceRoot":"","sources":["../../src/capture/CaptureAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,uBAAuB,EAEvB,MAAM,EAEN,UAAU,EACb,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAC,YAAY,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAI/C,qBAAa,YAAa,SAAQ,OAAO,CAAC,eAAe,CAAC;;gBAa1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;IAkCjG,IAAI,YAAY,IAAI,OAAO,CAA4B;IACvD,IAAI,YAAY,CAAC,KAAK,EAAE,OAAO,EAS9B;IACD,IAAI,MAAM,IAAI,MAAM,CAAsB;IAC1C,IAAI,eAAe,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAA+B;IACnE,IAAI,eAAe,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAmD;IACnF,IAAI,MAAM,IAAI,uBAAuB,CAAC,WAAW,CAAC,CAAsB;IACxE,IAAI,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC,CAEnC;IACD,IAAI,KAAK,IAAI,MAAM,CAAsE;IACzF,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,CAA+D;IAChG,IAAI,gBAAgB,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAE/C;IACD,IAAI,UAAU,IAAI,MAAM,CAAC,SAAS,CAAC,CAAiD;IACpF,IAAI,qBAAqB,IAAI,MAAM,CAAsD;IAEnF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC,cAAc,IAAI,UAAU;CAyG/B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Errors, isDefined, isUndefined, MutableObservableOption, Option, RuntimeNotifier, Terminable } from "@opendaw/lib-std";
|
|
2
|
+
import { dbToGain } from "@opendaw/lib-dsp";
|
|
2
3
|
import { Promises } from "@opendaw/lib-runtime";
|
|
3
4
|
import { Capture } from "./Capture";
|
|
4
5
|
import { RecordAudio } from "./RecordAudio";
|
|
@@ -6,8 +7,10 @@ import { AudioDevices } from "../AudioDevices";
|
|
|
6
7
|
export class CaptureAudio extends Capture {
|
|
7
8
|
#stream;
|
|
8
9
|
#streamGenerator;
|
|
10
|
+
#isMonitoring = false;
|
|
9
11
|
#requestChannels = Option.None;
|
|
10
12
|
#gainDb = 0.0;
|
|
13
|
+
#audioChain = null;
|
|
11
14
|
constructor(manager, audioUnitBox, captureAudioBox) {
|
|
12
15
|
super(manager, audioUnitBox, captureAudioBox);
|
|
13
16
|
this.#stream = new MutableObservableOption();
|
|
@@ -15,7 +18,13 @@ export class CaptureAudio extends Capture {
|
|
|
15
18
|
this.ownAll(captureAudioBox.requestChannels.catchupAndSubscribe(owner => {
|
|
16
19
|
const channels = owner.getValue();
|
|
17
20
|
this.#requestChannels = channels === 1 || channels === 2 ? Option.wrap(channels) : Option.None;
|
|
18
|
-
|
|
21
|
+
this.#stream.ifSome(stream => this.#rebuildAudioChain(stream));
|
|
22
|
+
}), captureAudioBox.gainDb.catchupAndSubscribe(owner => {
|
|
23
|
+
this.#gainDb = owner.getValue();
|
|
24
|
+
if (isDefined(this.#audioChain)) {
|
|
25
|
+
this.#audioChain.gainNode.gain.value = dbToGain(this.#gainDb);
|
|
26
|
+
}
|
|
27
|
+
}), captureAudioBox.deviceId.catchupAndSubscribe(async () => {
|
|
19
28
|
if (this.armed.getValue()) {
|
|
20
29
|
await this.#streamGenerator();
|
|
21
30
|
}
|
|
@@ -29,7 +38,23 @@ export class CaptureAudio extends Capture {
|
|
|
29
38
|
}
|
|
30
39
|
}));
|
|
31
40
|
}
|
|
41
|
+
get isMonitoring() { return this.#isMonitoring; }
|
|
42
|
+
set isMonitoring(value) {
|
|
43
|
+
if (this.#isMonitoring === value) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.#isMonitoring = value;
|
|
47
|
+
if (this.#isMonitoring) {
|
|
48
|
+
this.armed.setValue(true);
|
|
49
|
+
this.#connectMonitoring();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.#disconnectMonitoring();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
32
55
|
get gainDb() { return this.#gainDb; }
|
|
56
|
+
get requestChannels() { return this.#requestChannels; }
|
|
57
|
+
set requestChannels(value) { this.captureBox.requestChannels.setValue(value); }
|
|
33
58
|
get stream() { return this.#stream; }
|
|
34
59
|
get streamDeviceId() {
|
|
35
60
|
return this.streamMediaTrack.map(settings => settings.getSettings().deviceId ?? "");
|
|
@@ -39,6 +64,8 @@ export class CaptureAudio extends Capture {
|
|
|
39
64
|
get streamMediaTrack() {
|
|
40
65
|
return this.#stream.flatMap(stream => Option.wrap(stream.getAudioTracks().at(0)));
|
|
41
66
|
}
|
|
67
|
+
get outputNode() { return Option.wrap(this.#audioChain?.gainNode); }
|
|
68
|
+
get effectiveChannelCount() { return this.#audioChain?.gainNode.channelCount ?? 1; }
|
|
42
69
|
async prepareRecording() {
|
|
43
70
|
const { project } = this.manager;
|
|
44
71
|
const { env: { audioContext } } = project;
|
|
@@ -58,23 +85,21 @@ export class CaptureAudio extends Capture {
|
|
|
58
85
|
startRecording() {
|
|
59
86
|
const { project } = this.manager;
|
|
60
87
|
const { env: { audioContext, audioWorklets, sampleManager } } = project;
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
console.warn("No audio
|
|
88
|
+
const audioChain = this.#audioChain;
|
|
89
|
+
if (!isDefined(audioChain)) {
|
|
90
|
+
console.warn("No audio chain available for recording.");
|
|
64
91
|
return Terminable.Empty;
|
|
65
92
|
}
|
|
66
|
-
const
|
|
67
|
-
const channelCount =
|
|
93
|
+
const { gainNode } = audioChain;
|
|
94
|
+
const channelCount = gainNode.channelCount;
|
|
68
95
|
const numChunks = 128;
|
|
69
96
|
const recordingWorklet = audioWorklets.createRecording(channelCount, numChunks);
|
|
70
97
|
return RecordAudio.start({
|
|
71
98
|
recordingWorklet,
|
|
72
|
-
|
|
99
|
+
sourceNode: gainNode,
|
|
73
100
|
sampleManager,
|
|
74
|
-
audioContext,
|
|
75
101
|
project,
|
|
76
102
|
capture: this,
|
|
77
|
-
gainDb: this.#gainDb,
|
|
78
103
|
outputLatency: audioContext.outputLatency ?? 0
|
|
79
104
|
});
|
|
80
105
|
}
|
|
@@ -105,6 +130,7 @@ export class CaptureAudio extends Capture {
|
|
|
105
130
|
const gotDeviceId = settings?.deviceId;
|
|
106
131
|
console.debug(`new stream. device requested: ${deviceId ?? "default"}, got: ${gotDeviceId ?? "unknown"}. channelCount requested: ${channelCount}, got: ${settings?.channelCount}`);
|
|
107
132
|
if (isUndefined(deviceId) || deviceId === gotDeviceId) {
|
|
133
|
+
this.#rebuildAudioChain(stream);
|
|
108
134
|
this.#stream.wrap(stream);
|
|
109
135
|
}
|
|
110
136
|
else {
|
|
@@ -114,6 +140,46 @@ export class CaptureAudio extends Capture {
|
|
|
114
140
|
});
|
|
115
141
|
}
|
|
116
142
|
#stopStream() {
|
|
143
|
+
this.#destroyAudioChain();
|
|
117
144
|
this.#stream.clear(stream => stream.getAudioTracks().forEach(track => track.stop()));
|
|
118
145
|
}
|
|
146
|
+
#rebuildAudioChain(stream) {
|
|
147
|
+
const wasMonitoring = this.#isMonitoring && isDefined(this.#audioChain);
|
|
148
|
+
this.#destroyAudioChain();
|
|
149
|
+
const { audioContext } = this.manager.project.env;
|
|
150
|
+
const sourceNode = audioContext.createMediaStreamSource(stream);
|
|
151
|
+
const gainNode = audioContext.createGain();
|
|
152
|
+
gainNode.gain.value = dbToGain(this.#gainDb);
|
|
153
|
+
const streamChannelCount = stream.getAudioTracks().at(0)?.getSettings().channelCount ?? 1;
|
|
154
|
+
const requestMono = this.#requestChannels.mapOr(channels => channels === 1, false);
|
|
155
|
+
if (requestMono && streamChannelCount === 2) {
|
|
156
|
+
gainNode.channelCount = 1;
|
|
157
|
+
gainNode.channelCountMode = "explicit";
|
|
158
|
+
}
|
|
159
|
+
sourceNode.connect(gainNode);
|
|
160
|
+
this.#audioChain = { sourceNode, gainNode };
|
|
161
|
+
if (wasMonitoring || this.#isMonitoring) {
|
|
162
|
+
this.#connectMonitoring();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
#destroyAudioChain() {
|
|
166
|
+
if (isDefined(this.#audioChain)) {
|
|
167
|
+
const { sourceNode, gainNode } = this.#audioChain;
|
|
168
|
+
sourceNode.disconnect();
|
|
169
|
+
gainNode.disconnect();
|
|
170
|
+
this.#audioChain = null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
#connectMonitoring() {
|
|
174
|
+
if (isDefined(this.#audioChain)) {
|
|
175
|
+
const { audioContext } = this.manager.project.env;
|
|
176
|
+
this.#audioChain.gainNode.connect(audioContext.destination);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
#disconnectMonitoring() {
|
|
180
|
+
if (isDefined(this.#audioChain)) {
|
|
181
|
+
const { audioContext } = this.manager.project.env;
|
|
182
|
+
this.#audioChain.gainNode.disconnect(audioContext.destination);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
119
185
|
}
|
|
@@ -6,15 +6,13 @@ import { Capture } from "./Capture";
|
|
|
6
6
|
export declare namespace RecordAudio {
|
|
7
7
|
type RecordAudioContext = {
|
|
8
8
|
recordingWorklet: RecordingWorklet;
|
|
9
|
-
|
|
9
|
+
sourceNode: AudioNode;
|
|
10
10
|
sampleManager: SampleLoaderManager;
|
|
11
|
-
audioContext: AudioContext;
|
|
12
11
|
project: Project;
|
|
13
12
|
capture: Capture;
|
|
14
|
-
gainDb: number;
|
|
15
13
|
outputLatency: number;
|
|
16
14
|
};
|
|
17
|
-
export const start: ({ recordingWorklet,
|
|
15
|
+
export const start: ({ recordingWorklet, sourceNode, sampleManager, project, capture, outputLatency }: RecordAudioContext) => Terminable;
|
|
18
16
|
export {};
|
|
19
17
|
}
|
|
20
18
|
//# sourceMappingURL=RecordAudio.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAUvG,OAAO,EAAa,mBAAmB,EAA2B,MAAM,0BAA0B,CAAA;AAClG,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,WAAW,CAAC;IACzB,KAAK,kBAAkB,GAAG;QACtB,gBAAgB,EAAE,gBAAgB,CAAA;QAClC,
|
|
1
|
+
{"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAUvG,OAAO,EAAa,mBAAmB,EAA2B,MAAM,0BAA0B,CAAA;AAClG,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,WAAW,CAAC;IACzB,KAAK,kBAAkB,GAAG;QACtB,gBAAgB,EAAE,gBAAgB,CAAA;QAClC,UAAU,EAAE,SAAS,CAAA;QACrB,aAAa,EAAE,mBAAmB,CAAA;QAClC,OAAO,EAAE,OAAO,CAAA;QAChB,OAAO,EAAE,OAAO,CAAA;QAChB,aAAa,EAAE,MAAM,CAAA;KACxB,CAAA;IAQD,MAAM,CAAC,MAAM,KAAK,GACd,kFAAgF,kBAAkB,KAChG,UAiKL,CAAA;;CACJ"}
|
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import { asInstanceOf, Option, quantizeFloor, Terminable, Terminator, UUID } from "@opendaw/lib-std";
|
|
2
|
-
import {
|
|
2
|
+
import { PPQN, TimeBase } from "@opendaw/lib-dsp";
|
|
3
3
|
import { AudioFileBox, AudioPitchStretchBox, AudioRegionBox, TrackBox, ValueEventCollectionBox, WarpMarkerBox } from "@opendaw/studio-boxes";
|
|
4
4
|
import { ColorCodes, TrackType, UnionBoxTypes } from "@opendaw/studio-adapters";
|
|
5
5
|
import { RecordTrack } from "./RecordTrack";
|
|
6
6
|
export var RecordAudio;
|
|
7
7
|
(function (RecordAudio) {
|
|
8
|
-
RecordAudio.start = ({ recordingWorklet,
|
|
8
|
+
RecordAudio.start = ({ recordingWorklet, sourceNode, sampleManager, project, capture, outputLatency }) => {
|
|
9
9
|
const terminator = new Terminator();
|
|
10
10
|
const beats = PPQN.fromSignature(1, project.timelineBox.signature.denominator.getValue());
|
|
11
11
|
const { editing, engine, boxGraph, timelineBox } = project;
|
|
12
12
|
const originalUuid = recordingWorklet.uuid;
|
|
13
13
|
sampleManager.record(recordingWorklet);
|
|
14
|
-
const streamSource = audioContext.createMediaStreamSource(mediaStream);
|
|
15
|
-
const streamGain = audioContext.createGain();
|
|
16
|
-
streamGain.gain.value = dbToGain(gainDb);
|
|
17
|
-
streamSource.connect(streamGain);
|
|
18
|
-
recordingWorklet.own(Terminable.create(() => {
|
|
19
|
-
streamGain.disconnect();
|
|
20
|
-
streamSource.disconnect();
|
|
21
|
-
}));
|
|
22
14
|
let fileBox = Option.None;
|
|
23
15
|
let currentTake = Option.None;
|
|
24
16
|
let lastPosition = 0;
|
|
@@ -110,6 +102,7 @@ export var RecordAudio;
|
|
|
110
102
|
currentTake = Option.wrap(createTakeRegion(position, currentWaveformOffset, true));
|
|
111
103
|
};
|
|
112
104
|
terminator.ownAll(Terminable.create(() => {
|
|
105
|
+
sourceNode.disconnect(recordingWorklet);
|
|
113
106
|
if (recordingWorklet.numberOfFrames === 0 || fileBox.isEmpty()) {
|
|
114
107
|
console.debug("Abort recording audio.");
|
|
115
108
|
sampleManager.remove(originalUuid);
|
|
@@ -142,7 +135,7 @@ export var RecordAudio;
|
|
|
142
135
|
}
|
|
143
136
|
lastPosition = currentPosition;
|
|
144
137
|
if (fileBox.isEmpty()) {
|
|
145
|
-
|
|
138
|
+
sourceNode.connect(recordingWorklet);
|
|
146
139
|
editing.modify(() => {
|
|
147
140
|
fileBox = Option.wrap(createFileBox());
|
|
148
141
|
const position = quantizeFloor(currentPosition, beats);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordMidi.d.ts","sourceRoot":"","sources":["../../src/capture/RecordMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,QAAQ,EAAuC,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGrI,OAAO,EAAa,UAAU,EAA2B,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,UAAU,CAAC;IACxB,KAAK,iBAAiB,GAAG;QACrB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAA;IAgBD,MAAM,CAAC,MAAM,KAAK,GAAI,gCAA8B,iBAAiB,KAAG,
|
|
1
|
+
{"version":3,"file":"RecordMidi.d.ts","sourceRoot":"","sources":["../../src/capture/RecordMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,QAAQ,EAAuC,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAGrI,OAAO,EAAa,UAAU,EAA2B,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAA;AAClC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,UAAU,CAAC;IACxB,KAAK,iBAAiB,GAAG;QACrB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CAAA;IAgBD,MAAM,CAAC,MAAM,KAAK,GAAI,gCAA8B,iBAAiB,KAAG,UAkJvE,CAAA;;CACJ"}
|
|
@@ -91,12 +91,14 @@ export var RecordMidi;
|
|
|
91
91
|
const loopEnabled = loopArea.enabled.getValue();
|
|
92
92
|
const loopFrom = loopArea.from.getValue();
|
|
93
93
|
const loopTo = loopArea.to.getValue();
|
|
94
|
-
const loopDurationPPQN = loopTo - loopFrom;
|
|
95
94
|
const allowTakes = project.engine.preferences.settings.recording.allowTakes;
|
|
96
95
|
if (loopEnabled && allowTakes && currentTake.nonEmpty() && currentPosition < lastPosition) {
|
|
97
|
-
positionOffset += loopDurationPPQN;
|
|
98
96
|
editing.modify(() => {
|
|
99
|
-
currentTake.ifSome(take =>
|
|
97
|
+
currentTake.ifSome(take => {
|
|
98
|
+
const actualDurationPPQN = take.regionBox.duration.getValue();
|
|
99
|
+
finalizeTake(take, actualDurationPPQN);
|
|
100
|
+
positionOffset += actualDurationPPQN;
|
|
101
|
+
});
|
|
100
102
|
startNewTake(loopFrom);
|
|
101
103
|
}, false);
|
|
102
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DawProjectExporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"DawProjectExporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectExporter.ts"],"names":[],"mappings":"AAKA,OAAO,EAcH,mBAAmB,EAMnB,aAAa,EAahB,MAAM,yBAAyB,CAAA;AAgBhC,OAAO,EAAwD,eAAe,EAAE,mBAAmB,EAAC,MAAM,0BAA0B,CAAA;AAKpI,yBAAiB,kBAAkB,CAAC;IAChC,UAAiB,cAAc;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAAA;KAAC;IAE5F,MAAM,KAAK,GAAI,UAAU,eAAe,EAAE,eAAe,mBAAmB,EAAE,gBAAgB,cAAc,kBAqTlH,CAAA;CACJ"}
|
|
@@ -3,10 +3,10 @@ import { Xml } from "@opendaw/lib-xml";
|
|
|
3
3
|
import { dbToGain, PPQN } from "@opendaw/lib-dsp";
|
|
4
4
|
import { AddressIdEncoder, BooleanField } from "@opendaw/lib-box";
|
|
5
5
|
import { Html } from "@opendaw/lib-dom";
|
|
6
|
-
import { ApplicationSchema, ArrangementSchema, AudioAlgorithm, AudioSchema, BuiltinDeviceSchema, ChannelRole, ChannelSchema, ClipSchema, ClipsSchema, DeviceRole, FileReferenceSchema, LanesSchema, NoteSchema, NotesSchema, ParameterEncoder, ProjectSchema, RealParameterSchema, TimeSignatureParameterSchema, TimeUnit, TrackSchema, TransportSchema, Unit, WarpSchema, WarpsSchema } from "@opendaw/lib-dawproject";
|
|
6
|
+
import { ApplicationSchema, ArrangementSchema, AudioAlgorithm, AudioSchema, AutomationTargetSchema, BuiltinDeviceSchema, ChannelRole, ChannelSchema, ClipSchema, ClipsSchema, DeviceRole, FileReferenceSchema, LanesSchema, NoteSchema, NotesSchema, ParameterEncoder, PointsSchema, ProjectSchema, RealParameterSchema, RealPointSchema, TempoAutomationConverter, TimeSignatureParameterSchema, TimeSignaturePointSchema, TimeUnit, TrackSchema, TransportSchema, Unit, WarpSchema, WarpsSchema } from "@opendaw/lib-dawproject";
|
|
7
7
|
import { AudioUnitType } from "@opendaw/studio-enums";
|
|
8
|
-
import { AudioFileBox, AudioUnitBox, NoteEventBox, NoteEventCollectionBox, TrackBox } from "@opendaw/studio-boxes";
|
|
9
|
-
import { ColorCodes, DeviceBoxUtils } from "@opendaw/studio-adapters";
|
|
8
|
+
import { AudioFileBox, AudioUnitBox, NoteEventBox, NoteEventCollectionBox, SignatureEventBox, TrackBox, ValueEventBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
|
|
9
|
+
import { ColorCodes, DeviceBoxUtils, InterpolationFieldAdapter } from "@opendaw/studio-adapters";
|
|
10
10
|
import { AudioUnitExportLayout } from "./AudioUnitExportLayout";
|
|
11
11
|
import { DeviceIO } from "./DeviceIO";
|
|
12
12
|
import { WavFile } from "../WavFile";
|
|
@@ -205,6 +205,77 @@ export var DawProjectExporter;
|
|
|
205
205
|
content: []
|
|
206
206
|
}, ClipSchema)]
|
|
207
207
|
}, ClipsSchema);
|
|
208
|
+
const writeTempoAutomation = () => {
|
|
209
|
+
const { tempoTrack } = timelineBox;
|
|
210
|
+
if (!tempoTrack.enabled.getValue()) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const targetVertex = tempoTrack.events.targetVertex;
|
|
214
|
+
if (targetVertex.isEmpty()) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
const collectionBox = asInstanceOf(targetVertex.unwrap().box, ValueEventCollectionBox);
|
|
218
|
+
const events = collectionBox.events.pointerHub.incoming()
|
|
219
|
+
.map(({ box }) => asInstanceOf(box, ValueEventBox))
|
|
220
|
+
.sort((eventA, eventB) => eventA.position.getValue() - eventB.position.getValue());
|
|
221
|
+
if (events.length === 0) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const minBpm = tempoTrack.minBpm.getValue();
|
|
225
|
+
const maxBpm = tempoTrack.maxBpm.getValue();
|
|
226
|
+
const tempoParameterId = ids.getOrCreate(timelineBox.bpm.address);
|
|
227
|
+
return Xml.element({
|
|
228
|
+
unit: Unit.BPM,
|
|
229
|
+
target: Xml.element({
|
|
230
|
+
parameter: tempoParameterId
|
|
231
|
+
}, AutomationTargetSchema),
|
|
232
|
+
points: events.map(event => {
|
|
233
|
+
const interpolation = InterpolationFieldAdapter.read(event.interpolation);
|
|
234
|
+
return Xml.element({
|
|
235
|
+
time: event.position.getValue() / PPQN.Quarter,
|
|
236
|
+
value: TempoAutomationConverter.normalizedToBpm(event.value.getValue(), minBpm, maxBpm),
|
|
237
|
+
interpolation: TempoAutomationConverter.toDawProjectInterpolation(interpolation)
|
|
238
|
+
}, RealPointSchema);
|
|
239
|
+
})
|
|
240
|
+
}, PointsSchema);
|
|
241
|
+
};
|
|
242
|
+
const writeSignatureAutomation = () => {
|
|
243
|
+
const { signatureTrack, signature } = timelineBox;
|
|
244
|
+
if (!signatureTrack.enabled.getValue()) {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
const events = signatureTrack.events.pointerHub.incoming()
|
|
248
|
+
.map(({ box }) => asInstanceOf(box, SignatureEventBox))
|
|
249
|
+
.sort((eventA, eventB) => eventA.index.getValue() - eventB.index.getValue());
|
|
250
|
+
if (events.length === 0) {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
// Calculate absolute positions for each event
|
|
254
|
+
const points = [];
|
|
255
|
+
let accumulatedPpqn = 0;
|
|
256
|
+
let currentNominator = signature.nominator.getValue();
|
|
257
|
+
let currentDenominator = signature.denominator.getValue();
|
|
258
|
+
// Add the initial signature at time 0
|
|
259
|
+
points.push(Xml.element({
|
|
260
|
+
time: 0,
|
|
261
|
+
numerator: currentNominator,
|
|
262
|
+
denominator: currentDenominator
|
|
263
|
+
}, TimeSignaturePointSchema));
|
|
264
|
+
for (const event of events) {
|
|
265
|
+
const barDuration = PPQN.fromSignature(currentNominator, currentDenominator);
|
|
266
|
+
accumulatedPpqn += barDuration * event.relativePosition.getValue();
|
|
267
|
+
currentNominator = event.nominator.getValue();
|
|
268
|
+
currentDenominator = event.denominator.getValue();
|
|
269
|
+
points.push(Xml.element({
|
|
270
|
+
time: accumulatedPpqn / PPQN.Quarter,
|
|
271
|
+
numerator: currentNominator,
|
|
272
|
+
denominator: currentDenominator
|
|
273
|
+
}, TimeSignaturePointSchema));
|
|
274
|
+
}
|
|
275
|
+
return Xml.element({
|
|
276
|
+
points
|
|
277
|
+
}, PointsSchema);
|
|
278
|
+
};
|
|
208
279
|
const writeLanes = () => {
|
|
209
280
|
return audioUnits
|
|
210
281
|
.flatMap(audioUnitBox => audioUnitBox.tracks.pointerHub.incoming()
|
|
@@ -230,6 +301,8 @@ export var DawProjectExporter;
|
|
|
230
301
|
transport: writeTransport(),
|
|
231
302
|
structure: writeStructure(),
|
|
232
303
|
arrangement: Xml.element({
|
|
304
|
+
tempoAutomation: writeTempoAutomation(),
|
|
305
|
+
timeSignatureAutomation: writeSignatureAutomation(),
|
|
233
306
|
lanes: Xml.element({
|
|
234
307
|
lanes: writeLanes(),
|
|
235
308
|
timeUnit: TimeUnit.BEATS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DawProjectImporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,
|
|
1
|
+
{"version":3,"file":"DawProjectImporter.d.ts","sourceRoot":"","sources":["../../src/dawproject/DawProjectImporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBH,IAAI,EAEP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAcH,aAAa,EAUhB,MAAM,yBAAyB,CAAA;AA2BhC,OAAO,EAOH,eAAe,EAElB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAIvC,yBAAiB,gBAAgB,CAAC;IAW9B,KAAY,MAAM,GAAG;QACjB,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,QAAQ,EAAE,eAAe,CAAA;KAC5B,CAAA;IAOM,MAAM,IAAI,GAAU,QAAQ,aAAa,EAAE,WAAW,UAAU,CAAC,gBAAgB,KAAG,OAAO,CAAC,MAAM,CAycxG,CAAA;CAaJ"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ArrayMultimap, asDefined, asInstanceOf, assert, Color, ifDefined, isDefined, isInstanceOf, isUndefined, NumberComparator, Option, panic, UUID, ValueMapping } from "@opendaw/lib-std";
|
|
2
2
|
import { BoxGraph } from "@opendaw/lib-box";
|
|
3
3
|
import { gainToDb, PPQN } from "@opendaw/lib-dsp";
|
|
4
|
-
import { ChannelRole, ClipsSchema, DeviceRole, EqualizerSchema, LanesSchema, NotesSchema, PointsSchema, SendType, TrackSchema, WarpsSchema } from "@opendaw/lib-dawproject";
|
|
4
|
+
import { ChannelRole, ClipsSchema, DeviceRole, EqualizerSchema, LanesSchema, NotesSchema, PointsSchema, RealPointSchema, SendType, TempoAutomationConverter, TimeSignaturePointSchema, TrackSchema, WarpsSchema } from "@opendaw/lib-dawproject";
|
|
5
5
|
import { AudioSendRouting, AudioUnitType, IconSymbol } from "@opendaw/studio-enums";
|
|
6
|
-
import { AudioBusBox, AudioFileBox, AudioPitchStretchBox, AudioRegionBox, AudioUnitBox, AuxSendBox, BoxIO, CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, RootBox, TimelineBox, TrackBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, UserInterfaceBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
|
|
7
|
-
import { AudioUnitOrdering, ColorCodes, InstrumentFactories, TrackType } from "@opendaw/studio-adapters";
|
|
6
|
+
import { AudioBusBox, AudioFileBox, AudioPitchStretchBox, AudioRegionBox, AudioUnitBox, AuxSendBox, BoxIO, CaptureAudioBox, CaptureMidiBox, GrooveShuffleBox, NoteEventBox, NoteEventCollectionBox, NoteRegionBox, RootBox, SignatureEventBox, TimelineBox, TrackBox, UnknownAudioEffectDeviceBox, UnknownMidiEffectDeviceBox, UserInterfaceBox, ValueEventBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
|
|
7
|
+
import { AudioUnitOrdering, ColorCodes, InstrumentFactories, InterpolationFieldAdapter, TrackType } from "@opendaw/studio-adapters";
|
|
8
8
|
import { BuiltinDevices } from "./BuiltinDevices";
|
|
9
9
|
import { AudioContentHelpers } from "../project/audio/AudioContentHelpers";
|
|
10
10
|
export var DawProjectImport;
|
|
@@ -344,6 +344,74 @@ export var DawProjectImport;
|
|
|
344
344
|
box.playMode.refer(pitchStretch);
|
|
345
345
|
});
|
|
346
346
|
};
|
|
347
|
+
const readTempoAutomation = (tempoAutomation) => {
|
|
348
|
+
const points = tempoAutomation.points;
|
|
349
|
+
if (!isDefined(points) || points.length === 0) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const minBpm = timelineBox.tempoTrack.minBpm.getValue();
|
|
353
|
+
const maxBpm = timelineBox.tempoTrack.maxBpm.getValue();
|
|
354
|
+
const collectionBox = ValueEventCollectionBox.create(boxGraph, UUID.generate());
|
|
355
|
+
timelineBox.tempoTrack.events.refer(collectionBox.owners);
|
|
356
|
+
timelineBox.tempoTrack.enabled.setValue(true);
|
|
357
|
+
points
|
|
358
|
+
.filter((point) => isInstanceOf(point, RealPointSchema))
|
|
359
|
+
.forEach(point => {
|
|
360
|
+
const eventBox = ValueEventBox.create(boxGraph, UUID.generate(), box => {
|
|
361
|
+
box.position.setValue(point.time * PPQN.Quarter);
|
|
362
|
+
box.index.setValue(0);
|
|
363
|
+
box.value.setValue(TempoAutomationConverter.bpmToNormalized(point.value, minBpm, maxBpm));
|
|
364
|
+
box.events.refer(collectionBox.events);
|
|
365
|
+
});
|
|
366
|
+
const interpolation = TempoAutomationConverter.fromDawProjectInterpolation(point.interpolation);
|
|
367
|
+
InterpolationFieldAdapter.write(eventBox.interpolation, interpolation);
|
|
368
|
+
});
|
|
369
|
+
};
|
|
370
|
+
ifDefined(arrangement.tempoAutomation, readTempoAutomation);
|
|
371
|
+
const readSignatureAutomation = (signatureAutomation) => {
|
|
372
|
+
const points = signatureAutomation.points;
|
|
373
|
+
if (!isDefined(points) || points.length === 0) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const signaturePoints = points
|
|
377
|
+
.filter((point) => isInstanceOf(point, TimeSignaturePointSchema))
|
|
378
|
+
.sort((pointA, pointB) => pointA.time - pointB.time);
|
|
379
|
+
if (signaturePoints.length === 0) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// First point at or near time 0 sets the base signature
|
|
383
|
+
const firstPoint = signaturePoints[0];
|
|
384
|
+
const firstPointIsAtStart = Number(firstPoint.time) < 0.001;
|
|
385
|
+
if (firstPointIsAtStart) {
|
|
386
|
+
timelineBox.signature.nominator.setValue(firstPoint.numerator);
|
|
387
|
+
timelineBox.signature.denominator.setValue(firstPoint.denominator);
|
|
388
|
+
}
|
|
389
|
+
// Remaining points become SignatureEventBox instances
|
|
390
|
+
const eventsToCreate = firstPointIsAtStart ? signaturePoints.slice(1) : signaturePoints;
|
|
391
|
+
if (eventsToCreate.length === 0) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
timelineBox.signatureTrack.enabled.setValue(true);
|
|
395
|
+
let prevPpqn = 0;
|
|
396
|
+
let prevNominator = timelineBox.signature.nominator.getValue();
|
|
397
|
+
let prevDenominator = timelineBox.signature.denominator.getValue();
|
|
398
|
+
eventsToCreate.forEach((point, index) => {
|
|
399
|
+
const currentPpqn = point.time * PPQN.Quarter;
|
|
400
|
+
const barDuration = PPQN.fromSignature(prevNominator, prevDenominator);
|
|
401
|
+
const relativePosition = Math.max(1, Math.round((currentPpqn - prevPpqn) / barDuration));
|
|
402
|
+
SignatureEventBox.create(boxGraph, UUID.generate(), box => {
|
|
403
|
+
box.index.setValue(index);
|
|
404
|
+
box.relativePosition.setValue(relativePosition);
|
|
405
|
+
box.nominator.setValue(point.numerator);
|
|
406
|
+
box.denominator.setValue(point.denominator);
|
|
407
|
+
box.events.refer(timelineBox.signatureTrack.events);
|
|
408
|
+
});
|
|
409
|
+
prevPpqn = prevPpqn + relativePosition * barDuration;
|
|
410
|
+
prevNominator = point.numerator;
|
|
411
|
+
prevDenominator = point.denominator;
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
ifDefined(arrangement.timeSignatureAutomation, readSignatureAutomation);
|
|
347
415
|
return Promise.all(arrangement?.lanes?.lanes?.filter(timeline => isInstanceOf(timeline, LanesSchema))
|
|
348
416
|
.map(readLane) ?? []);
|
|
349
417
|
};
|