@opendaw/studio-core 0.0.130 → 0.0.132
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/AssetService.d.ts.map +1 -1
- package/dist/AssetService.js +11 -2
- package/dist/AudioOfflineRenderer.js +1 -1
- 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 +19 -3
- package/dist/Engine.d.ts +1 -1
- package/dist/Engine.d.ts.map +1 -1
- package/dist/EngineFacade.d.ts +1 -1
- package/dist/EngineFacade.d.ts.map +1 -1
- package/dist/EngineFacade.js +2 -2
- package/dist/EngineWorklet.d.ts +1 -1
- package/dist/EngineWorklet.d.ts.map +1 -1
- package/dist/EngineWorklet.js +9 -46
- package/dist/MonitoringRouter.d.ts +10 -0
- package/dist/MonitoringRouter.d.ts.map +1 -0
- package/dist/MonitoringRouter.js +89 -0
- package/dist/capture/CaptureAudio.d.ts +10 -0
- package/dist/capture/CaptureAudio.d.ts.map +1 -1
- package/dist/capture/CaptureAudio.js +105 -30
- package/dist/capture/RecordAudio.d.ts.map +1 -1
- package/dist/capture/RecordAudio.js +10 -2
- package/dist/capture/Recording.js +1 -1
- package/dist/processors.js +28 -28
- package/dist/processors.js.map +4 -4
- package/dist/project/AudioWavExport.d.ts +6 -0
- package/dist/project/AudioWavExport.d.ts.map +1 -0
- package/dist/project/AudioWavExport.js +15 -0
- package/dist/project/NoteMidiExport.d.ts +9 -0
- package/dist/project/NoteMidiExport.d.ts.map +1 -0
- package/dist/project/NoteMidiExport.js +27 -0
- package/dist/project/Project.d.ts.map +1 -1
- package/dist/project/Project.js +1 -2
- package/dist/project/ProjectApi.d.ts +3 -1
- package/dist/project/ProjectApi.d.ts.map +1 -1
- package/dist/project/ProjectApi.js +8 -0
- package/dist/project/ProjectStorage.d.ts.map +1 -1
- package/dist/project/ProjectStorage.js +1 -0
- package/dist/project/audio/AudioContentModifier.d.ts.map +1 -1
- package/dist/project/audio/AudioContentModifier.js +43 -3
- package/dist/project/index.d.ts +2 -0
- package/dist/project/index.d.ts.map +1 -1
- package/dist/project/index.js +2 -0
- package/dist/project/migration/MigrateValueEventCollection.test.js +3 -3
- package/dist/samples/OpenSampleAPI.d.ts +1 -0
- package/dist/samples/OpenSampleAPI.d.ts.map +1 -1
- package/dist/samples/OpenSampleAPI.js +1 -0
- package/dist/samples/SampleService.js +1 -1
- package/dist/soundfont/DefaultSoundfontLoader.d.ts.map +1 -1
- package/dist/soundfont/DefaultSoundfontLoader.js +3 -0
- package/dist/soundfont/OpenSoundfontAPI.d.ts +1 -0
- package/dist/soundfont/OpenSoundfontAPI.d.ts.map +1 -1
- package/dist/soundfont/OpenSoundfontAPI.js +1 -0
- package/dist/soundfont/SoundfontService.js +1 -1
- package/dist/sync-log/SyncLogWriter.d.ts.map +1 -1
- package/dist/sync-log/SyncLogWriter.js +3 -2
- package/dist/ui/clipboard/ClipboardUtils.js +1 -1
- package/dist/ui/clipboard/types/DevicesClipboardHandler.d.ts.map +1 -1
- package/dist/ui/clipboard/types/DevicesClipboardHandler.js +22 -12
- package/dist/ui/clipboard/types/DevicesClipboardHandler.test.js +124 -4
- package/dist/ysync/YService.d.ts.map +1 -1
- package/dist/ysync/YService.js +0 -5
- package/dist/ysync/YSync.d.ts +1 -0
- package/dist/ysync/YSync.d.ts.map +1 -1
- package/dist/ysync/YSync.js +127 -84
- package/dist/ysync/YSync.test.d.ts +2 -0
- package/dist/ysync/YSync.test.d.ts.map +1 -0
- package/dist/ysync/YSync.test.js +259 -0
- package/package.json +16 -15
|
@@ -8,29 +8,45 @@ import { RenderQuantum } from "../RenderQuantum";
|
|
|
8
8
|
export class CaptureAudio extends Capture {
|
|
9
9
|
#stream;
|
|
10
10
|
#streamGenerator;
|
|
11
|
+
#monitorGainNode;
|
|
12
|
+
#monitorPanNode;
|
|
11
13
|
#monitoringMode = "off";
|
|
12
14
|
#requestChannels = Option.None;
|
|
13
15
|
#gainDb = 0.0;
|
|
16
|
+
#monitorVolumeDb = 0.0;
|
|
17
|
+
#monitorPan = 0.0;
|
|
18
|
+
#monitorMuted = false;
|
|
14
19
|
#audioChain = null;
|
|
15
20
|
#preparedWorklet = null;
|
|
21
|
+
#monitorOutputDeviceId = Option.None;
|
|
22
|
+
#monitorAudioElement = null;
|
|
23
|
+
#monitorStreamDest = null;
|
|
16
24
|
constructor(manager, audioUnitBox, captureAudioBox) {
|
|
17
25
|
super(manager, audioUnitBox, captureAudioBox);
|
|
26
|
+
const { audioContext } = this.manager.project.env;
|
|
27
|
+
this.#monitorGainNode = audioContext.createGain();
|
|
28
|
+
this.#monitorGainNode.gain.value = dbToGain(this.#monitorVolumeDb);
|
|
29
|
+
this.#monitorPanNode = audioContext.createStereoPanner();
|
|
30
|
+
this.#monitorPanNode.pan.value = this.#monitorPan;
|
|
31
|
+
this.#monitorGainNode.connect(this.#monitorPanNode);
|
|
18
32
|
this.#stream = new MutableObservableOption();
|
|
19
33
|
this.#streamGenerator = Promises.sequentialize(() => this.#updateStream());
|
|
20
|
-
this.ownAll(
|
|
34
|
+
this.ownAll(Terminable.create(() => {
|
|
35
|
+
this.#disconnectMonitoring();
|
|
36
|
+
if (isDefined(this.#monitorAudioElement)) {
|
|
37
|
+
this.#monitorAudioElement.pause();
|
|
38
|
+
this.#monitorAudioElement.srcObject = null;
|
|
39
|
+
}
|
|
40
|
+
this.#monitorGainNode.disconnect();
|
|
41
|
+
this.#monitorPanNode.disconnect();
|
|
42
|
+
}), captureAudioBox.requestChannels.catchupAndSubscribe(owner => {
|
|
21
43
|
const channels = owner.getValue();
|
|
22
44
|
this.#requestChannels = channels === 1 || channels === 2 ? Option.wrap(channels) : Option.None;
|
|
23
45
|
this.#stream.ifSome(stream => this.#rebuildAudioChain(stream));
|
|
24
|
-
// Re-register monitoring if in effects mode (channel count may have changed)
|
|
25
|
-
if (this.#monitoringMode === "effects" && isDefined(this.#audioChain)) {
|
|
26
|
-
const engine = this.manager.project.engine;
|
|
27
|
-
engine.unregisterMonitoringSource(this.audioUnitBox.address.uuid);
|
|
28
|
-
engine.registerMonitoringSource(this.audioUnitBox.address.uuid, this.#audioChain.gainNode, this.#audioChain.channelCount);
|
|
29
|
-
}
|
|
30
46
|
}), captureAudioBox.gainDb.catchupAndSubscribe(owner => {
|
|
31
47
|
this.#gainDb = owner.getValue();
|
|
32
48
|
if (isDefined(this.#audioChain)) {
|
|
33
|
-
this.#audioChain.
|
|
49
|
+
this.#audioChain.recordGainNode.gain.value = dbToGain(this.#gainDb);
|
|
34
50
|
}
|
|
35
51
|
}), captureAudioBox.deviceId.catchupAndSubscribe(async () => {
|
|
36
52
|
if (this.armed.getValue()) {
|
|
@@ -71,8 +87,62 @@ export class CaptureAudio extends Capture {
|
|
|
71
87
|
get streamMediaTrack() {
|
|
72
88
|
return this.#stream.flatMap(stream => Option.wrap(stream.getAudioTracks().at(0)));
|
|
73
89
|
}
|
|
74
|
-
get outputNode() { return Option.wrap(this.#audioChain?.
|
|
90
|
+
get outputNode() { return Option.wrap(this.#audioChain?.recordGainNode); }
|
|
75
91
|
get effectiveChannelCount() { return this.#audioChain?.channelCount ?? 1; }
|
|
92
|
+
get monitorGainNode() { return this.#monitorGainNode; }
|
|
93
|
+
get monitorPanNode() { return this.#monitorPanNode; }
|
|
94
|
+
get monitorVolumeDb() { return this.#monitorVolumeDb; }
|
|
95
|
+
set monitorVolumeDb(value) {
|
|
96
|
+
this.#monitorVolumeDb = value;
|
|
97
|
+
this.#monitorGainNode.gain.value = this.#monitorMuted ? 0 : dbToGain(this.#monitorVolumeDb);
|
|
98
|
+
}
|
|
99
|
+
get monitorPan() { return this.#monitorPan; }
|
|
100
|
+
set monitorPan(value) {
|
|
101
|
+
this.#monitorPan = value;
|
|
102
|
+
this.#monitorPanNode.pan.value = value;
|
|
103
|
+
}
|
|
104
|
+
get monitorMuted() { return this.#monitorMuted; }
|
|
105
|
+
set monitorMuted(value) {
|
|
106
|
+
this.#monitorMuted = value;
|
|
107
|
+
this.#monitorGainNode.gain.value = value ? 0 : dbToGain(this.#monitorVolumeDb);
|
|
108
|
+
}
|
|
109
|
+
get monitorOutputDeviceId() { return this.#monitorOutputDeviceId; }
|
|
110
|
+
async setMonitorOutputDevice(deviceId) {
|
|
111
|
+
const oldDestination = this.#monitorDestination();
|
|
112
|
+
this.#monitorOutputDeviceId = deviceId;
|
|
113
|
+
if (isDefined(this.#monitorAudioElement)) {
|
|
114
|
+
this.#monitorAudioElement.pause();
|
|
115
|
+
this.#monitorAudioElement.srcObject = null;
|
|
116
|
+
this.#monitorAudioElement = null;
|
|
117
|
+
}
|
|
118
|
+
if (isDefined(this.#monitorStreamDest)) {
|
|
119
|
+
this.#monitorStreamDest.disconnect();
|
|
120
|
+
this.#monitorStreamDest = null;
|
|
121
|
+
}
|
|
122
|
+
if (deviceId.nonEmpty()) {
|
|
123
|
+
const { audioContext } = this.manager.project.env;
|
|
124
|
+
this.#monitorStreamDest = audioContext.createMediaStreamDestination();
|
|
125
|
+
const audio = new Audio();
|
|
126
|
+
audio.srcObject = this.#monitorStreamDest.stream;
|
|
127
|
+
try {
|
|
128
|
+
await audio.setSinkId(deviceId.unwrap());
|
|
129
|
+
await audio.play();
|
|
130
|
+
this.#monitorAudioElement = audio;
|
|
131
|
+
}
|
|
132
|
+
catch (reason) {
|
|
133
|
+
audio.srcObject = null;
|
|
134
|
+
this.#monitorStreamDest.disconnect();
|
|
135
|
+
this.#monitorStreamDest = null;
|
|
136
|
+
this.#monitorOutputDeviceId = Option.None;
|
|
137
|
+
RuntimeNotifier.info({ headline: "Output Device Error", message: `${reason}` });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (this.#monitoringMode !== "off" && isDefined(this.#audioChain)) {
|
|
142
|
+
this.#monitorPanNode.disconnect(oldDestination);
|
|
143
|
+
this.#monitorPanNode.connect(this.#monitorDestination());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
76
146
|
async prepareRecording() {
|
|
77
147
|
const { project } = this.manager;
|
|
78
148
|
const { env: { audioContext, audioWorklets, sampleManager, sampleService } } = project;
|
|
@@ -92,12 +162,12 @@ export class CaptureAudio extends Capture {
|
|
|
92
162
|
if (!isDefined(audioChain)) {
|
|
93
163
|
return Promise.reject("No audio chain available for recording.");
|
|
94
164
|
}
|
|
95
|
-
const {
|
|
165
|
+
const { recordGainNode, channelCount } = audioChain;
|
|
96
166
|
const recordingWorklet = audioWorklets.createRecording(channelCount, RenderQuantum);
|
|
97
167
|
recordingWorklet.bpm = project.timelineBox.bpm.getValue();
|
|
98
168
|
recordingWorklet.sampleService = sampleService;
|
|
99
169
|
sampleManager.record(recordingWorklet);
|
|
100
|
-
|
|
170
|
+
recordGainNode.connect(recordingWorklet);
|
|
101
171
|
this.#preparedWorklet = recordingWorklet;
|
|
102
172
|
}
|
|
103
173
|
startRecording() {
|
|
@@ -110,10 +180,10 @@ export class CaptureAudio extends Capture {
|
|
|
110
180
|
return Terminable.Empty;
|
|
111
181
|
}
|
|
112
182
|
this.#preparedWorklet = null;
|
|
113
|
-
const {
|
|
183
|
+
const { recordGainNode } = audioChain;
|
|
114
184
|
return RecordAudio.start({
|
|
115
185
|
recordingWorklet,
|
|
116
|
-
sourceNode:
|
|
186
|
+
sourceNode: recordGainNode,
|
|
117
187
|
sampleManager,
|
|
118
188
|
project,
|
|
119
189
|
capture: this,
|
|
@@ -157,47 +227,51 @@ export class CaptureAudio extends Capture {
|
|
|
157
227
|
});
|
|
158
228
|
}
|
|
159
229
|
#stopStream() {
|
|
230
|
+
this.#disconnectMonitoring();
|
|
160
231
|
this.#destroyAudioChain();
|
|
161
232
|
this.#stream.clear(stream => stream.getAudioTracks().forEach(track => track.stop()));
|
|
162
233
|
}
|
|
163
234
|
#rebuildAudioChain(stream) {
|
|
164
|
-
|
|
235
|
+
this.#disconnectMonitoring();
|
|
165
236
|
this.#destroyAudioChain();
|
|
166
237
|
const { audioContext } = this.manager.project.env;
|
|
167
238
|
const sourceNode = audioContext.createMediaStreamSource(stream);
|
|
168
|
-
const
|
|
169
|
-
|
|
239
|
+
const recordGainNode = audioContext.createGain();
|
|
240
|
+
recordGainNode.gain.value = dbToGain(this.#gainDb);
|
|
170
241
|
const streamChannelCount = Math.min(stream.getAudioTracks().at(0)?.getSettings().channelCount ?? 2, 2);
|
|
171
242
|
const channelCount = this.#requestChannels.unwrapOrElse(streamChannelCount);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
sourceNode.connect(
|
|
175
|
-
this.#audioChain = { sourceNode,
|
|
176
|
-
|
|
177
|
-
this.#connectMonitoring();
|
|
178
|
-
}
|
|
243
|
+
recordGainNode.channelCount = channelCount;
|
|
244
|
+
recordGainNode.channelCountMode = "explicit";
|
|
245
|
+
sourceNode.connect(recordGainNode);
|
|
246
|
+
this.#audioChain = { sourceNode, recordGainNode, channelCount };
|
|
247
|
+
this.#connectMonitoring();
|
|
179
248
|
}
|
|
180
249
|
#destroyAudioChain() {
|
|
181
250
|
if (isDefined(this.#audioChain)) {
|
|
182
|
-
const { sourceNode,
|
|
251
|
+
const { sourceNode, recordGainNode } = this.#audioChain;
|
|
183
252
|
sourceNode.disconnect();
|
|
184
|
-
|
|
253
|
+
recordGainNode.disconnect();
|
|
185
254
|
this.#audioChain = null;
|
|
186
255
|
}
|
|
187
256
|
}
|
|
257
|
+
#monitorDestination() {
|
|
258
|
+
return this.#monitorStreamDest ?? this.manager.project.env.audioContext.destination;
|
|
259
|
+
}
|
|
188
260
|
#connectMonitoring() {
|
|
189
261
|
if (!isDefined(this.#audioChain)) {
|
|
190
262
|
return;
|
|
191
263
|
}
|
|
264
|
+
const { sourceNode, channelCount } = this.#audioChain;
|
|
192
265
|
switch (this.#monitoringMode) {
|
|
193
266
|
case "off":
|
|
194
267
|
break;
|
|
195
268
|
case "direct":
|
|
196
|
-
|
|
269
|
+
sourceNode.connect(this.#monitorGainNode);
|
|
270
|
+
this.#monitorPanNode.connect(this.#monitorDestination());
|
|
197
271
|
break;
|
|
198
272
|
case "effects":
|
|
199
|
-
|
|
200
|
-
|
|
273
|
+
this.manager.project.engine.registerMonitoringSource(this.audioUnitBox.address.uuid, sourceNode, channelCount, this.#monitorGainNode);
|
|
274
|
+
this.#monitorPanNode.connect(this.#monitorDestination());
|
|
201
275
|
break;
|
|
202
276
|
}
|
|
203
277
|
}
|
|
@@ -209,11 +283,12 @@ export class CaptureAudio extends Capture {
|
|
|
209
283
|
case "off":
|
|
210
284
|
break;
|
|
211
285
|
case "direct":
|
|
212
|
-
this.#audioChain.
|
|
286
|
+
this.#audioChain.sourceNode.disconnect(this.#monitorGainNode);
|
|
287
|
+
this.#monitorPanNode.disconnect(this.#monitorDestination());
|
|
213
288
|
break;
|
|
214
289
|
case "effects":
|
|
215
|
-
this.#audioChain.gainNode.disconnect();
|
|
216
290
|
this.manager.project.engine.unregisterMonitoringSource(this.audioUnitBox.address.uuid);
|
|
291
|
+
this.#monitorPanNode.disconnect(this.#monitorDestination());
|
|
217
292
|
break;
|
|
218
293
|
}
|
|
219
294
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,UAAU,EAIb,MAAM,kBAAkB,CAAA;AAGzB,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;AAIjC,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;IAOD,MAAM,CAAC,MAAM,KAAK,GACd,kFAAgF,kBAAkB,KAChG,
|
|
1
|
+
{"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,UAAU,EAIb,MAAM,kBAAkB,CAAA;AAGzB,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;AAIjC,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;IAOD,MAAM,CAAC,MAAM,KAAK,GACd,kFAAgF,kBAAkB,KAChG,UAsNL,CAAA;;CACJ"}
|
|
@@ -124,8 +124,9 @@ export var RecordAudio;
|
|
|
124
124
|
pointer.refer(newFileBox.transientMarkers);
|
|
125
125
|
}
|
|
126
126
|
oldFileBox.delete();
|
|
127
|
-
});
|
|
127
|
+
}, false);
|
|
128
128
|
});
|
|
129
|
+
editing.mark();
|
|
129
130
|
};
|
|
130
131
|
terminator.ownAll(Terminable.create(() => {
|
|
131
132
|
tryCatch(() => sourceNode.disconnect(recordingWorklet));
|
|
@@ -178,10 +179,17 @@ export var RecordAudio;
|
|
|
178
179
|
editing.modify(() => {
|
|
179
180
|
currentTake.ifSome(take => {
|
|
180
181
|
const actualDurationInSeconds = take.regionBox.duration.getValue();
|
|
182
|
+
if (actualDurationInSeconds <= 0) {
|
|
183
|
+
take.regionBox.delete();
|
|
184
|
+
currentTake = Option.None;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
181
187
|
finalizeTake(take, actualDurationInSeconds);
|
|
182
188
|
currentWaveformOffset += actualDurationInSeconds;
|
|
183
189
|
});
|
|
184
|
-
|
|
190
|
+
if (currentTake.nonEmpty()) {
|
|
191
|
+
startNewTake(loopFrom);
|
|
192
|
+
}
|
|
185
193
|
}, false);
|
|
186
194
|
}
|
|
187
195
|
lastPosition = currentPosition;
|
|
@@ -37,7 +37,7 @@ export class Recording {
|
|
|
37
37
|
if (isRecording.getValue() || isCountingIn.getValue()) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
editing.modify(() => terminator.terminate()); // finalizes recording
|
|
40
|
+
editing.modify(() => terminator.terminate(), false); // finalizes recording
|
|
41
41
|
this.#isRecording = false;
|
|
42
42
|
};
|
|
43
43
|
terminator.ownAll(engine.isRecording.subscribe(stop), engine.isCountingIn.subscribe(stop), Terminable.create(() => Recording.#instance = Option.None));
|