@opendaw/studio-core 0.0.42 → 0.0.43

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.
@@ -1,18 +1,12 @@
1
- import { Procedure, Progress, UUID } from "@opendaw/lib-std";
1
+ import { Procedure } from "@opendaw/lib-std";
2
2
  import { Sample } from "@opendaw/studio-adapters";
3
- export declare namespace SampleService {
4
- type ImportArgs = {
5
- uuid?: UUID.Bytes;
6
- name: string;
7
- arrayBuffer: ArrayBuffer;
8
- progressHandler?: Progress.Handler;
9
- };
10
- }
11
- export declare class SampleService {
3
+ import { AssetService } from "../AssetService";
4
+ export declare class SampleService extends AssetService<Sample> {
12
5
  readonly audioContext: AudioContext;
13
- readonly onUpdate: Procedure<Sample>;
6
+ protected readonly namePlural: string;
7
+ protected readonly nameSingular: string;
14
8
  constructor(audioContext: AudioContext, onUpdate: Procedure<Sample>);
15
- browseForSamples(multiple: boolean): Promise<void>;
16
- importSample({ uuid, name, arrayBuffer, progressHandler }: SampleService.ImportArgs): Promise<Sample>;
9
+ browse(multiple: boolean): Promise<ReadonlyArray<Sample>>;
10
+ importFile({ uuid, name, arrayBuffer, progressHandler }: AssetService.ImportArgs): Promise<Sample>;
17
11
  }
18
12
  //# sourceMappingURL=SampleService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SampleService.d.ts","sourceRoot":"","sources":["../../src/samples/SampleService.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,SAAS,EACT,QAAQ,EAER,IAAI,EACP,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAY,MAAM,EAAiB,MAAM,0BAA0B,CAAA;AAG1E,yBAAiB,aAAa,CAAC;IAC3B,KAAY,UAAU,GAAG;QACrB,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,WAAW,CAAC;QACzB,eAAe,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAA;KACrC,CAAA;CACJ;AAED,qBAAa,aAAa;IACV,QAAQ,CAAC,YAAY,EAAE,YAAY;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC;gBAAhE,YAAY,EAAE,YAAY,EAAW,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC;IAE/E,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiClD,YAAY,CAAC,EAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,eAAgC,EAAC,EACzD,aAAa,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAoClE"}
1
+ {"version":3,"file":"SampleService.d.ts","sourceRoot":"","sources":["../../src/samples/SampleService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,SAAS,EAAiB,MAAM,kBAAkB,CAAA;AAI/E,OAAO,EAAY,MAAM,EAAiB,MAAM,0BAA0B,CAAA;AAE1E,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAE5C,qBAAa,aAAc,SAAQ,YAAY,CAAC,MAAM,CAAC;IAIvC,QAAQ,CAAC,YAAY,EAAE,YAAY;IAH/C,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAY;IACjD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAW;gBAE7B,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC;IAItE,MAAM,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAIzD,UAAU,CAAC,EAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,eAAgC,EAAC,EACzD,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAoC/D"}
@@ -1,52 +1,21 @@
1
- import { Arrays, DefaultObservableValue, Errors, panic, Progress, RuntimeNotifier, UUID } from "@opendaw/lib-std";
1
+ import { Arrays, isUndefined, Progress, UUID } from "@opendaw/lib-std";
2
2
  import { estimateBpm } from "@opendaw/lib-dsp";
3
- import { Files } from "@opendaw/lib-dom";
4
3
  import { Promises } from "@opendaw/lib-runtime";
5
4
  import { SamplePeaks } from "@opendaw/lib-fusion";
6
5
  import { FilePickerAcceptTypes, SampleStorage, Workers } from "../index";
7
- export class SampleService {
6
+ import { AssetService } from "../AssetService";
7
+ export class SampleService extends AssetService {
8
8
  audioContext;
9
- onUpdate;
9
+ namePlural = "Samples";
10
+ nameSingular = "Sample";
10
11
  constructor(audioContext, onUpdate) {
12
+ super(onUpdate);
11
13
  this.audioContext = audioContext;
12
- this.onUpdate = onUpdate;
13
14
  }
14
- async browseForSamples(multiple) {
15
- const { error, status, value: files } = await Promises.tryCatch(Files.open({ ...FilePickerAcceptTypes.WavFiles, multiple }));
16
- if (status === "rejected") {
17
- if (Errors.isAbort(error)) {
18
- return;
19
- }
20
- else {
21
- return panic(String(error));
22
- }
23
- }
24
- const progress = new DefaultObservableValue(0.0);
25
- const dialog = RuntimeNotifier.progress({
26
- headline: `Importing ${files.length === 1 ? "Sample" : "Samples"}...`, progress
27
- });
28
- const progressHandler = Progress.split(value => progress.setValue(value), files.length);
29
- const rejected = [];
30
- for (const [index, file] of files.entries()) {
31
- const arrayBuffer = await file.arrayBuffer();
32
- const { status, error } = await Promises.tryCatch(this.importSample({
33
- name: file.name,
34
- arrayBuffer: arrayBuffer,
35
- progressHandler: progressHandler[index]
36
- }));
37
- if (status === "rejected") {
38
- rejected.push(String(error));
39
- }
40
- }
41
- dialog.terminate();
42
- if (rejected.length > 0) {
43
- await RuntimeNotifier.info({
44
- headline: "Sample Import Issues",
45
- message: `${rejected.join(", ")} could not be imported.`
46
- });
47
- }
15
+ async browse(multiple) {
16
+ return this.browseFiles(multiple, FilePickerAcceptTypes.WavFiles);
48
17
  }
49
- async importSample({ uuid, name, arrayBuffer, progressHandler = Progress.Empty }) {
18
+ async importFile({ uuid, name, arrayBuffer, progressHandler = Progress.Empty }) {
50
19
  console.debug(`importSample '${name}' (${arrayBuffer.byteLength >> 10}kb)`);
51
20
  console.time("UUID.sha256");
52
21
  uuid ??= await UUID.sha256(arrayBuffer); // Must run before decodeAudioData laster, because it will detach the ArrayBuffer
@@ -69,7 +38,7 @@ export class SampleService {
69
38
  const peaks = await Workers.Peak.generateAsync(progressHandler, shifts, audioData.frames, audioData.numberOfFrames, audioData.numberOfChannels);
70
39
  const meta = {
71
40
  bpm: estimateBpm(audioBuffer.duration),
72
- name: name.substring(0, name.lastIndexOf(".")),
41
+ name: isUndefined(name) ? "Unnnamed" : name.substring(0, name.lastIndexOf(".")),
73
42
  duration: audioBuffer.duration,
74
43
  sample_rate: audioBuffer.sampleRate,
75
44
  origin: "import"
@@ -44,7 +44,7 @@ export class OpenSoundfontAPI {
44
44
  else {
45
45
  chunks.push(value);
46
46
  loaded += value.length;
47
- progress(loaded / total);
47
+ progress(Math.min(1.0, loaded / total));
48
48
  reader.read().then(nextChunk, reject);
49
49
  }
50
50
  };
@@ -1,20 +1,14 @@
1
- import { Option, Procedure, Progress, UUID } from "@opendaw/lib-std";
1
+ import { Option, Procedure } from "@opendaw/lib-std";
2
2
  import { Soundfont } from "@opendaw/studio-adapters";
3
- export declare namespace SoundfontService {
4
- type ImportArgs = {
5
- uuid?: UUID.Bytes;
6
- arrayBuffer: ArrayBuffer;
7
- progressHandler?: Progress.Handler;
8
- };
9
- }
10
- export declare class SoundfontService {
3
+ import { AssetService } from "../AssetService";
4
+ export declare class SoundfontService extends AssetService<Soundfont> {
11
5
  #private;
12
- readonly onUpdate: Procedure<Soundfont>;
6
+ protected readonly namePlural: string;
7
+ protected readonly nameSingular: string;
13
8
  constructor(onUpdate: Procedure<Soundfont>);
14
9
  get local(): Option<ReadonlyArray<Soundfont>>;
15
10
  get remote(): Option<ReadonlyArray<Soundfont>>;
16
- browseForSoundfont(multiple?: boolean): Promise<ReadonlyArray<Soundfont>>;
17
- importSoundfont({ uuid, arrayBuffer }: SoundfontService.ImportArgs): Promise<Soundfont>;
18
- deleteSoundfont(uuid: UUID.Bytes): Promise<void>;
11
+ browse(multiple?: boolean): Promise<ReadonlyArray<Soundfont>>;
12
+ importFile({ uuid, arrayBuffer }: AssetService.ImportArgs): Promise<Soundfont>;
19
13
  }
20
14
  //# sourceMappingURL=SoundfontService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SoundfontService.d.ts","sourceRoot":"","sources":["../../src/soundfont/SoundfontService.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,MAAM,EAEN,SAAS,EACT,QAAQ,EAER,IAAI,EACP,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAC,SAAS,EAAoB,MAAM,0BAA0B,CAAA;AAMrE,yBAAiB,gBAAgB,CAAC;IAC9B,KAAY,UAAU,GAAG;QACrB,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC;QAClB,WAAW,EAAE,WAAW,CAAC;QACzB,eAAe,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAA;KACrC,CAAA;CACJ;AAED,qBAAa,gBAAgB;;IAIb,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC;gBAA9B,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC;IAUnD,IAAI,KAAK,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAqB;IAClE,IAAI,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAsB;IAE9D,kBAAkB,CAAC,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IA+BhF,eAAe,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,EAAE,gBAAgB,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IAiCrF,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzD"}
1
+ {"version":3,"file":"SoundfontService.d.ts","sourceRoot":"","sources":["../../src/soundfont/SoundfontService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAS,SAAS,EAAkC,MAAM,kBAAkB,CAAA;AAClG,OAAO,EAAC,SAAS,EAAoB,MAAM,0BAA0B,CAAA;AAKrE,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAG5C,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,SAAS,CAAC;;IACzD,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAe;IACpD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAc;gBAKzC,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC;IAY1C,IAAI,KAAK,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAqB;IAClE,IAAI,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAsB;IAE9D,MAAM,CAAC,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAIpE,UAAU,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,EAAE,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;CAyCrF"}
@@ -1,16 +1,17 @@
1
- import { Arrays, DefaultObservableValue, Errors, Option, panic, Progress, RuntimeNotifier, UUID } from "@opendaw/lib-std";
2
- import { Files } from "@opendaw/lib-dom";
3
- import { Promises } from "@opendaw/lib-runtime";
1
+ import { Arrays, Option, panic, RuntimeNotifier, tryCatch, UUID } from "@opendaw/lib-std";
4
2
  import { SoundFont2 } from "soundfont2";
5
3
  import { SoundfontStorage } from "./SoundfontStorage";
6
4
  import { FilePickerAcceptTypes } from "../FilePickerAcceptTypes";
7
5
  import { OpenSoundfontAPI } from "./OpenSoundfontAPI";
8
- export class SoundfontService {
9
- onUpdate;
6
+ import { AssetService } from "../AssetService";
7
+ import { Wait } from "@opendaw/lib-runtime";
8
+ export class SoundfontService extends AssetService {
9
+ namePlural = "Soundfonts";
10
+ nameSingular = "Soundfont";
10
11
  #local = Option.None;
11
12
  #remote = Option.None;
12
13
  constructor(onUpdate) {
13
- this.onUpdate = onUpdate;
14
+ super(onUpdate);
14
15
  Promise.all([
15
16
  SoundfontStorage.get().list(),
16
17
  OpenSoundfontAPI.get().all()
@@ -21,62 +22,34 @@ export class SoundfontService {
21
22
  }
22
23
  get local() { return this.#local; }
23
24
  get remote() { return this.#remote; }
24
- async browseForSoundfont(multiple = false) {
25
- const { error, status, value: files } = await Promises.tryCatch(Files.open({ ...FilePickerAcceptTypes.SoundfontFiles, multiple }));
26
- if (status === "rejected") {
27
- if (Errors.isAbort(error)) {
28
- return [];
29
- }
30
- else {
31
- return panic(String(error));
32
- }
33
- }
34
- const progress = new DefaultObservableValue(0.0);
35
- const dialog = RuntimeNotifier.progress({
36
- headline: `Importing ${files.length === 1 ? "Soundfont" : "Soundfonts"}...`, progress
37
- });
38
- const progressHandler = Progress.split(value => progress.setValue(value), files.length);
39
- const soundfonts = [];
40
- const rejected = [];
41
- for (const [index, file] of files.entries()) {
42
- const arrayBuffer = await file.arrayBuffer();
43
- const { status, value, error } = await Promises.tryCatch(this.importSoundfont({
44
- arrayBuffer,
45
- progressHandler: progressHandler[index]
46
- }));
47
- if (status === "rejected") {
48
- rejected.push(String(error));
49
- }
50
- else {
51
- soundfonts.push(value);
52
- }
53
- }
54
- dialog.terminate();
55
- if (rejected.length > 0) {
56
- await RuntimeNotifier.info({
57
- headline: "Soundfont Import Issues",
58
- message: `${rejected.join(", ")} could not be imported.`
59
- });
60
- }
61
- return soundfonts;
25
+ async browse(multiple = false) {
26
+ return this.browseFiles(multiple, FilePickerAcceptTypes.SoundfontFiles);
62
27
  }
63
- async importSoundfont({ uuid, arrayBuffer }) {
28
+ async importFile({ uuid, arrayBuffer }) {
64
29
  if (this.#local.isEmpty()) {
65
30
  return panic("Local soundfont storage has not been read.");
66
31
  }
67
32
  if (arrayBuffer.byteLength > (1 << 24)) {
68
33
  await RuntimeNotifier.approve({
69
34
  headline: "Soundfont Import",
70
- message: `The soundfont you are trying to import is ${(arrayBuffer.byteLength >> 20)}mb. This may cause memory issues. Do you want to continue?`,
35
+ message: `The soundfont you are trying to import is ${(arrayBuffer.byteLength >> 20)}mb. This may cause memory issues. Do you really want to continue?`,
71
36
  approveText: "Import",
72
37
  cancelText: "Cancel"
73
38
  });
74
39
  }
40
+ const updater = RuntimeNotifier.progress({ headline: `Import ${this.nameSingular}` });
41
+ await Wait.frame();
75
42
  console.debug(`importSoundfont (${arrayBuffer.byteLength >> 10}kb)`);
76
43
  console.time("UUID.sha256");
77
44
  uuid ??= await UUID.sha256(arrayBuffer);
78
45
  console.timeEnd("UUID.sha256");
79
- const soundFont2 = new SoundFont2(new Uint8Array(arrayBuffer));
46
+ console.time("SoundFont2");
47
+ const { status, value: soundFont2, error } = tryCatch(() => new SoundFont2(new Uint8Array(arrayBuffer)));
48
+ console.timeEnd("SoundFont2");
49
+ if (status === "failure") {
50
+ updater.terminate();
51
+ return panic(error);
52
+ }
80
53
  const meta = {
81
54
  name: soundFont2.metaData.name,
82
55
  url: "unknown",
@@ -85,14 +58,12 @@ export class SoundfontService {
85
58
  };
86
59
  await SoundfontStorage.get().save({ uuid, file: arrayBuffer, meta });
87
60
  const soundfont = { uuid: UUID.toString(uuid), ...meta };
88
- this.onUpdate(soundfont);
89
61
  const list = this.#local.unwrap();
90
62
  if (!list.some(other => other.uuid === soundfont.uuid)) {
91
63
  list.push(soundfont);
92
64
  }
65
+ this.onUpdate(soundfont);
66
+ updater.terminate();
93
67
  return soundfont;
94
68
  }
95
- async deleteSoundfont(uuid) {
96
- return SoundfontStorage.get().deleteItem(uuid);
97
- }
98
69
  }