@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.
Files changed (34) hide show
  1. package/dist/AssetService.d.ts +4 -3
  2. package/dist/AssetService.d.ts.map +1 -1
  3. package/dist/AssetService.js +3 -5
  4. package/dist/RecordingWorklet.d.ts +2 -0
  5. package/dist/RecordingWorklet.d.ts.map +1 -1
  6. package/dist/RecordingWorklet.js +8 -25
  7. package/dist/capture/CaptureAudio.d.ts.map +1 -1
  8. package/dist/capture/CaptureAudio.js +2 -1
  9. package/dist/capture/RecordMidi.d.ts.map +1 -1
  10. package/dist/capture/RecordMidi.js +24 -2
  11. package/dist/processors.js +8 -8
  12. package/dist/processors.js.map +3 -3
  13. package/dist/project/ProjectEnv.d.ts +4 -0
  14. package/dist/project/ProjectEnv.d.ts.map +1 -1
  15. package/dist/samples/SampleService.d.ts +5 -3
  16. package/dist/samples/SampleService.d.ts.map +1 -1
  17. package/dist/samples/SampleService.js +16 -7
  18. package/dist/soundfont/SoundfontService.d.ts +2 -2
  19. package/dist/soundfont/SoundfontService.d.ts.map +1 -1
  20. package/dist/soundfont/SoundfontService.js +3 -3
  21. package/dist/ui/renderer/audio.d.ts +13 -1
  22. package/dist/ui/renderer/audio.d.ts.map +1 -1
  23. package/dist/ui/renderer/audio.js +28 -20
  24. package/dist/ui/renderer/fading.d.ts.map +1 -1
  25. package/dist/ui/renderer/fading.js +23 -20
  26. package/dist/ui/renderer/index.d.ts +1 -0
  27. package/dist/ui/renderer/index.d.ts.map +1 -1
  28. package/dist/ui/renderer/index.js +1 -0
  29. package/dist/ui/renderer/notes.d.ts.map +1 -1
  30. package/dist/ui/renderer/notes.js +7 -4
  31. package/dist/ui/renderer/riffle.d.ts +3 -0
  32. package/dist/ui/renderer/riffle.d.ts.map +1 -0
  33. package/dist/ui/renderer/riffle.js +106 -0
  34. package/package.json +6 -6
@@ -1,4 +1,4 @@
1
- import { Class, Procedure, Progress, UUID } from "@opendaw/lib-std";
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 constructor(onUpdate: Procedure<T>);
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,EAML,SAAS,EACT,QAAQ,EAER,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,CAAA;KACrC,CAAA;CACJ;AAED,8BAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS;IAMrC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IAL/D,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,aAAgC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IAEzD,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"}
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"}
@@ -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
- onUpdate;
6
- constructor(onUpdate) {
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,EAEH,GAAG,EAEH,QAAQ,EACR,MAAM,EAEN,SAAS,EAET,YAAY,EACZ,UAAU,EAEV,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,SAAS,EAAW,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAC,KAAK,EAAc,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAEH,UAAU,EACV,YAAY,EACZ,iBAAiB,EAEpB,MAAM,0BAA0B,CAAA;AAMjC,qBAAa,gBAAiB,SAAQ,gBAAiB,YAAW,UAAU,EAAE,YAAY;;IAGtF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAkB;gBAc/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;IAEpF,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;CAsCrB"}
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"}
@@ -1,10 +1,7 @@
1
- import { ByteArrayInput, Notifier, Option, panic, Progress, Terminable, Terminator, UUID } from "@opendaw/lib-std";
2
- import { AudioData, BPMTools } from "@opendaw/lib-dsp";
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(sample_rate, totalSamples, numberOfChannels);
82
- mergedFrames.forEach((frame, i) => audioData.frames[i].set(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 shifts = SamplePeaks.findBestFit(totalSamples);
85
- const peaks = await Workers
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;IA0BvC,cAAc,IAAI,UAAU;CAsH/B"}
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,UAmJvE,CAAA;;CACJ"}
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
- currentTake = Option.wrap(createTakeRegion(pos, null));
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
- activeNotes.delete(signal.pitch);
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;