@opendaw/studio-core 0.0.122 → 0.0.123
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 +4 -3
- package/dist/AssetService.d.ts.map +1 -1
- package/dist/AssetService.js +3 -5
- package/dist/RecordingWorklet.d.ts +2 -0
- package/dist/RecordingWorklet.d.ts.map +1 -1
- package/dist/RecordingWorklet.js +8 -25
- package/dist/capture/CaptureAudio.d.ts.map +1 -1
- package/dist/capture/CaptureAudio.js +2 -1
- package/dist/capture/RecordMidi.d.ts.map +1 -1
- package/dist/capture/RecordMidi.js +24 -2
- package/dist/processors.js +8 -8
- package/dist/processors.js.map +3 -3
- package/dist/project/ProjectEnv.d.ts +4 -0
- package/dist/project/ProjectEnv.d.ts.map +1 -1
- package/dist/samples/SampleService.d.ts +5 -3
- package/dist/samples/SampleService.d.ts.map +1 -1
- package/dist/samples/SampleService.js +16 -7
- package/dist/soundfont/SoundfontService.d.ts +2 -2
- package/dist/soundfont/SoundfontService.d.ts.map +1 -1
- package/dist/soundfont/SoundfontService.js +3 -3
- package/dist/ui/renderer/audio.d.ts +13 -1
- package/dist/ui/renderer/audio.d.ts.map +1 -1
- package/dist/ui/renderer/audio.js +28 -20
- package/dist/ui/renderer/fading.d.ts.map +1 -1
- package/dist/ui/renderer/fading.js +23 -20
- package/dist/ui/renderer/index.d.ts +1 -0
- package/dist/ui/renderer/index.d.ts.map +1 -1
- package/dist/ui/renderer/index.js +1 -0
- package/dist/ui/renderer/notes.d.ts.map +1 -1
- package/dist/ui/renderer/notes.js +7 -4
- package/dist/ui/renderer/riffle.d.ts +3 -0
- package/dist/ui/renderer/riffle.d.ts.map +1 -0
- package/dist/ui/renderer/riffle.js +106 -0
- package/package.json +6 -6
package/dist/AssetService.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class,
|
|
1
|
+
import { Class, Notifier, Observer, Progress, Subscription, UUID } from "@opendaw/lib-std";
|
|
2
2
|
import { BoxGraph } from "@opendaw/lib-box";
|
|
3
3
|
import { Sample, Soundfont } from "@opendaw/studio-adapters";
|
|
4
4
|
import { AudioFileBox, SoundfontFileBox } from "@opendaw/studio-boxes";
|
|
@@ -8,15 +8,16 @@ export declare namespace AssetService {
|
|
|
8
8
|
name?: string;
|
|
9
9
|
arrayBuffer: ArrayBuffer;
|
|
10
10
|
progressHandler?: Progress.Handler;
|
|
11
|
+
origin?: "import" | "recording";
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
export declare abstract class AssetService<T extends Sample | Soundfont> {
|
|
14
|
-
protected readonly onUpdate: Procedure<T>;
|
|
15
15
|
protected abstract readonly nameSingular: string;
|
|
16
16
|
protected abstract readonly namePlural: string;
|
|
17
17
|
protected abstract readonly boxType: Class<AudioFileBox | SoundfontFileBox>;
|
|
18
18
|
protected abstract readonly filePickerOptions: FilePickerOptions;
|
|
19
|
-
protected
|
|
19
|
+
protected readonly notifier: Notifier<T>;
|
|
20
|
+
subscribe(observer: Observer<T>): Subscription;
|
|
20
21
|
browse(multiple: boolean): Promise<ReadonlyArray<T>>;
|
|
21
22
|
abstract importFile(args: AssetService.ImportArgs): Promise<T>;
|
|
22
23
|
replaceMissingFiles(boxGraph: BoxGraph, manager: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetService.d.ts","sourceRoot":"","sources":["../src/AssetService.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,
|
|
1
|
+
{"version":3,"file":"AssetService.d.ts","sourceRoot":"","sources":["../src/AssetService.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EAKL,QAAQ,EACR,QAAQ,EAER,QAAQ,EAER,YAAY,EACZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAEpE,yBAAiB,YAAY,CAAC;IAC1B,KAAY,UAAU,GAAG;QACrB,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;QACzB,eAAe,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;QACnC,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;KAClC,CAAA;CACJ;AAED,8BAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS;IAC3D,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAChD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC9C,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,GAAG,gBAAgB,CAAC,CAAA;IAC3E,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAA;IAEhE,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAoB;IAE5D,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,YAAY;IAExC,MAAM,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAI1D,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAExD,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE;QAAE,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAsCjG,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAoChH,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CAClE"}
|
package/dist/AssetService.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { DefaultObservableValue, Errors, isInstanceOf, isNotUndefined, panic, Progress, RuntimeNotifier, UUID } from "@opendaw/lib-std";
|
|
1
|
+
import { DefaultObservableValue, Errors, isInstanceOf, isNotUndefined, Notifier, panic, Progress, RuntimeNotifier, UUID } from "@opendaw/lib-std";
|
|
2
2
|
import { Files } from "@opendaw/lib-dom";
|
|
3
3
|
import { Promises } from "@opendaw/lib-runtime";
|
|
4
4
|
export class AssetService {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.onUpdate = onUpdate;
|
|
8
|
-
}
|
|
5
|
+
notifier = new Notifier();
|
|
6
|
+
subscribe(observer) { return this.notifier.subscribe(observer); }
|
|
9
7
|
async browse(multiple) {
|
|
10
8
|
return this.browseFiles(multiple, this.filePickerOptions);
|
|
11
9
|
}
|
|
@@ -2,6 +2,7 @@ import { int, Observer, Option, Procedure, Subscription, Terminable, UUID } from
|
|
|
2
2
|
import { AudioData } from "@opendaw/lib-dsp";
|
|
3
3
|
import { Peaks } from "@opendaw/lib-fusion";
|
|
4
4
|
import { RingBuffer, SampleLoader, SampleLoaderState } from "@opendaw/studio-adapters";
|
|
5
|
+
import { SampleService } from "./samples";
|
|
5
6
|
export declare class RecordingWorklet extends AudioWorkletNode implements Terminable, SampleLoader {
|
|
6
7
|
#private;
|
|
7
8
|
readonly uuid: UUID.Bytes;
|
|
@@ -9,6 +10,7 @@ export declare class RecordingWorklet extends AudioWorkletNode implements Termin
|
|
|
9
10
|
own<T extends Terminable>(terminable: T): T;
|
|
10
11
|
limit(count: int): void;
|
|
11
12
|
set onSaved(callback: Procedure<UUID.Bytes>);
|
|
13
|
+
set sampleService(service: SampleService);
|
|
12
14
|
setFillLength(value: int): void;
|
|
13
15
|
get numberOfFrames(): int;
|
|
14
16
|
get data(): Option<AudioData>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordingWorklet.d.ts","sourceRoot":"","sources":["../src/RecordingWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"RecordingWorklet.d.ts","sourceRoot":"","sources":["../src/RecordingWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAY,QAAQ,EAAE,MAAM,EAAS,SAAS,EAAE,YAAY,EAAE,UAAU,EAAc,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAC9H,OAAO,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAC,KAAK,EAAC,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAmB,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAC,MAAM,0BAA0B,CAAA;AAGtG,OAAO,EAAC,aAAa,EAAC,MAAM,WAAW,CAAA;AAEvC,qBAAa,gBAAiB,SAAQ,gBAAiB,YAAW,UAAU,EAAE,YAAY;;IAGtF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAkB;gBAe/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM;IAuBhE,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC;IAE3C,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAOvB,IAAI,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAyC;IACpF,IAAI,aAAa,CAAC,OAAO,EAAE,aAAa,EAA8C;IAEtF,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAE/B,IAAI,cAAc,IAAI,GAAG,CAA6C;IACtE,IAAI,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,CAAoB;IACjD,IAAI,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAA6E;IACvG,IAAI,KAAK,IAAI,iBAAiB,CAAqB;IAEnD,UAAU,IAAI,IAAI;IAElB,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,YAAY;IAQ9D,SAAS,IAAI,IAAI;IAMjB,QAAQ,IAAI,MAAM;CAsBrB"}
|
package/dist/RecordingWorklet.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AudioData
|
|
3
|
-
import { SamplePeaks } from "@opendaw/lib-fusion";
|
|
1
|
+
import { Notifier, Option, panic, Terminable, Terminator, UUID } from "@opendaw/lib-std";
|
|
2
|
+
import { AudioData } from "@opendaw/lib-dsp";
|
|
4
3
|
import { mergeChunkPlanes, RingBuffer } from "@opendaw/studio-adapters";
|
|
5
|
-
import { SampleStorage } from "./samples";
|
|
6
4
|
import { RenderQuantum } from "./RenderQuantum";
|
|
7
|
-
import { Workers } from "./Workers";
|
|
8
5
|
import { PeaksWriter } from "./PeaksWriter";
|
|
9
6
|
export class RecordingWorklet extends AudioWorkletNode {
|
|
10
7
|
#terminator = new Terminator();
|
|
@@ -19,6 +16,7 @@ export class RecordingWorklet extends AudioWorkletNode {
|
|
|
19
16
|
#limitSamples = Number.POSITIVE_INFINITY;
|
|
20
17
|
#state = { type: "record" };
|
|
21
18
|
#onSaved = Option.None;
|
|
19
|
+
#sampleService = Option.None;
|
|
22
20
|
constructor(context, config) {
|
|
23
21
|
super(context, "recording-processor", {
|
|
24
22
|
numberOfInputs: 1,
|
|
@@ -48,6 +46,7 @@ export class RecordingWorklet extends AudioWorkletNode {
|
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
set onSaved(callback) { this.#onSaved = Option.wrap(callback); }
|
|
49
|
+
set sampleService(service) { this.#sampleService = Option.wrap(service); }
|
|
51
50
|
setFillLength(value) { this.#peakWriter.numFrames = value; }
|
|
52
51
|
get numberOfFrames() { return this.#output.length * RenderQuantum; }
|
|
53
52
|
get data() { return this.#data; }
|
|
@@ -74,31 +73,15 @@ export class RecordingWorklet extends AudioWorkletNode {
|
|
|
74
73
|
return panic("No recording data available");
|
|
75
74
|
}
|
|
76
75
|
const totalSamples = this.#limitSamples;
|
|
77
|
-
const sample_rate = this.context.sampleRate;
|
|
78
|
-
const numberOfChannels = this.channelCount;
|
|
79
76
|
const mergedFrames = mergeChunkPlanes(this.#output, RenderQuantum, this.#output.length * RenderQuantum)
|
|
80
77
|
.map(frame => frame.slice(-totalSamples));
|
|
81
|
-
const audioData = AudioData.create(
|
|
82
|
-
mergedFrames.forEach((frame,
|
|
78
|
+
const audioData = AudioData.create(this.context.sampleRate, totalSamples, this.channelCount);
|
|
79
|
+
mergedFrames.forEach((frame, index) => audioData.frames[index].set(frame));
|
|
83
80
|
this.#data = Option.wrap(audioData);
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
.Peak.generateAsync(Progress.Empty, shifts, audioData.frames, totalSamples, numberOfChannels);
|
|
87
|
-
this.#peaks = Option.wrap(SamplePeaks.from(new ByteArrayInput(peaks)));
|
|
88
|
-
const bpm = BPMTools.detect(audioData.frames[0], sample_rate);
|
|
89
|
-
const duration = totalSamples / sample_rate;
|
|
90
|
-
const meta = { name: "Recording", bpm, sample_rate, duration, origin: "recording" };
|
|
91
|
-
const sample = {
|
|
92
|
-
uuid: this.uuid,
|
|
93
|
-
audio: audioData,
|
|
94
|
-
peaks: peaks,
|
|
95
|
-
meta
|
|
96
|
-
};
|
|
97
|
-
await SampleStorage.get().save(sample);
|
|
98
|
-
this.#onSaved.ifSome(callback => callback(this.uuid));
|
|
81
|
+
const sample = await this.#sampleService.unwrap("SampleService not set").importRecording(audioData);
|
|
82
|
+
this.#onSaved.ifSome(callback => callback(UUID.parse(sample.uuid)));
|
|
99
83
|
this.#setState({ type: "loaded" });
|
|
100
84
|
this.terminate();
|
|
101
|
-
return sample;
|
|
102
85
|
}
|
|
103
86
|
#setState(value) {
|
|
104
87
|
this.#state = value;
|
|
@@ -1 +1 @@
|
|
|
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;AAK/C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,qBAAa,YAAa,SAAQ,OAAO,CAAC,eAAe,CAAC;;gBAe1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;IAwCjG,IAAI,YAAY,IAAI,OAAO,CAAwC;IACnE,IAAI,cAAc,IAAI,cAAc,CAA8B;IAClE,IAAI,cAAc,CAAC,KAAK,EAAE,cAAc,EAQvC;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,CAA6C;IAE1E,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
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;AAK/C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,qBAAa,YAAa,SAAQ,OAAO,CAAC,eAAe,CAAC;;gBAe1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;IAwCjG,IAAI,YAAY,IAAI,OAAO,CAAwC;IACnE,IAAI,cAAc,IAAI,cAAc,CAA8B;IAClE,IAAI,cAAc,CAAC,KAAK,EAAE,cAAc,EAQvC;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,CAA6C;IAE1E,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BvC,cAAc,IAAI,UAAU;CAsH/B"}
|
|
@@ -75,7 +75,7 @@ export class CaptureAudio extends Capture {
|
|
|
75
75
|
get effectiveChannelCount() { return this.#audioChain?.channelCount ?? 1; }
|
|
76
76
|
async prepareRecording() {
|
|
77
77
|
const { project } = this.manager;
|
|
78
|
-
const { env: { audioContext, audioWorklets, sampleManager } } = project;
|
|
78
|
+
const { env: { audioContext, audioWorklets, sampleManager, sampleService } } = project;
|
|
79
79
|
if (isUndefined(audioContext.outputLatency)) {
|
|
80
80
|
const approved = RuntimeNotifier.approve({
|
|
81
81
|
headline: "Warning",
|
|
@@ -94,6 +94,7 @@ export class CaptureAudio extends Capture {
|
|
|
94
94
|
}
|
|
95
95
|
const { gainNode, channelCount } = audioChain;
|
|
96
96
|
const recordingWorklet = audioWorklets.createRecording(channelCount, RenderQuantum);
|
|
97
|
+
recordingWorklet.sampleService = sampleService;
|
|
97
98
|
sampleManager.record(recordingWorklet);
|
|
98
99
|
gainNode.connect(recordingWorklet);
|
|
99
100
|
this.#preparedWorklet = recordingWorklet;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordMidi.d.ts","sourceRoot":"","sources":["../../src/capture/RecordMidi.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,QAAQ,EAKR,UAAU,EAGb,MAAM,kBAAkB,CAAA;AAGzB,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,EAIH,QAAQ,EAKR,UAAU,EAGb,MAAM,kBAAkB,CAAA;AAGzB,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,UA2KvE,CAAA;;CACJ"}
|
|
@@ -13,6 +13,7 @@ export var RecordMidi;
|
|
|
13
13
|
const { loopArea } = timelineBox;
|
|
14
14
|
const terminator = new Terminator();
|
|
15
15
|
const activeNotes = new Map();
|
|
16
|
+
const pendingNotes = new Map();
|
|
16
17
|
const latency = PPQN.secondsToPulses(audioContext.outputLatency ?? 10.0, timelineBox.bpm.getValue());
|
|
17
18
|
let currentTake = Option.None;
|
|
18
19
|
let lastPosition = 0;
|
|
@@ -79,6 +80,19 @@ export var RecordMidi;
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
};
|
|
83
|
+
const flushPendingNotes = (take) => {
|
|
84
|
+
for (const [pitch, velocity] of pendingNotes) {
|
|
85
|
+
const event = NoteEventBox.create(boxGraph, UUID.generate(), box => {
|
|
86
|
+
box.position.setValue(0);
|
|
87
|
+
box.duration.setValue(MIN_NOTE_DURATION);
|
|
88
|
+
box.pitch.setValue(pitch);
|
|
89
|
+
box.velocity.setValue(velocity);
|
|
90
|
+
box.events.refer(take.collection.events);
|
|
91
|
+
});
|
|
92
|
+
activeNotes.set(pitch, { event, take, creationOffset: positionOffset });
|
|
93
|
+
}
|
|
94
|
+
pendingNotes.clear();
|
|
95
|
+
};
|
|
82
96
|
const startNewTake = (position) => {
|
|
83
97
|
const previousTrack = currentTake.mapOr(take => take.trackBox, null);
|
|
84
98
|
currentTake = Option.wrap(createTakeRegion(position, previousTrack));
|
|
@@ -107,7 +121,9 @@ export var RecordMidi;
|
|
|
107
121
|
if (currentTake.isEmpty()) {
|
|
108
122
|
editing.modify(() => {
|
|
109
123
|
const pos = quantizeFloor(currentPosition, beats);
|
|
110
|
-
|
|
124
|
+
const take = createTakeRegion(pos, null);
|
|
125
|
+
currentTake = Option.wrap(take);
|
|
126
|
+
flushPendingNotes(take);
|
|
111
127
|
}, false);
|
|
112
128
|
}
|
|
113
129
|
currentTake.ifSome(({ regionBox, collection }) => {
|
|
@@ -140,6 +156,7 @@ export var RecordMidi;
|
|
|
140
156
|
if (NoteSignal.isOn(signal)) {
|
|
141
157
|
const { pitch, velocity } = signal;
|
|
142
158
|
if (currentTake.isEmpty()) {
|
|
159
|
+
pendingNotes.set(pitch, velocity);
|
|
143
160
|
return;
|
|
144
161
|
}
|
|
145
162
|
const take = currentTake.unwrap();
|
|
@@ -160,7 +177,12 @@ export var RecordMidi;
|
|
|
160
177
|
}, false);
|
|
161
178
|
}
|
|
162
179
|
else if (NoteSignal.isOff(signal)) {
|
|
163
|
-
|
|
180
|
+
if (pendingNotes.has(signal.pitch)) {
|
|
181
|
+
pendingNotes.delete(signal.pitch);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
activeNotes.delete(signal.pitch);
|
|
185
|
+
}
|
|
164
186
|
}
|
|
165
187
|
}));
|
|
166
188
|
return terminator;
|