@opendaw/studio-core 0.0.37 → 0.0.38

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 (59) hide show
  1. package/dist/Engine.d.ts +2 -1
  2. package/dist/Engine.d.ts.map +1 -1
  3. package/dist/EngineFacade.d.ts +1 -0
  4. package/dist/EngineFacade.d.ts.map +1 -1
  5. package/dist/EngineFacade.js +3 -1
  6. package/dist/EngineWorklet.d.ts +1 -0
  7. package/dist/EngineWorklet.d.ts.map +1 -1
  8. package/dist/EngineWorklet.js +9 -2
  9. package/dist/RecordingWorklet.d.ts.map +1 -1
  10. package/dist/RecordingWorklet.js +12 -5
  11. package/dist/capture/CaptureMidi.js +12 -12
  12. package/dist/capture/RecordAudio.d.ts +2 -2
  13. package/dist/capture/RecordAudio.d.ts.map +1 -1
  14. package/dist/clouds/CloudAuthManager.d.ts.map +1 -1
  15. package/dist/clouds/CloudAuthManager.js +10 -2
  16. package/dist/clouds/CloudBackupSamples.d.ts.map +1 -1
  17. package/dist/clouds/CloudBackupSamples.js +6 -1
  18. package/dist/index.d.ts +1 -7
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -7
  21. package/dist/midi/MIDIMessageSubscriber.js +4 -4
  22. package/dist/midi/MidiDevices.d.ts +6 -2
  23. package/dist/midi/MidiDevices.d.ts.map +1 -1
  24. package/dist/midi/MidiDevices.js +11 -2
  25. package/dist/midi/SoftwareMIDIInput.d.ts +27 -0
  26. package/dist/midi/SoftwareMIDIInput.d.ts.map +1 -0
  27. package/dist/midi/SoftwareMIDIInput.js +42 -0
  28. package/dist/processors.js +3 -3
  29. package/dist/processors.js.map +3 -3
  30. package/dist/project/Project.d.ts +3 -3
  31. package/dist/project/Project.d.ts.map +1 -1
  32. package/dist/project/Project.js +3 -1
  33. package/dist/project/ProjectBundle.d.ts.map +1 -1
  34. package/dist/project/ProjectBundle.js +1 -1
  35. package/dist/project/ProjectEnv.d.ts +2 -2
  36. package/dist/project/ProjectEnv.d.ts.map +1 -1
  37. package/dist/samples/{MainThreadSampleLoader.d.ts → DefaultSampleLoader.d.ts} +4 -4
  38. package/dist/samples/DefaultSampleLoader.d.ts.map +1 -0
  39. package/dist/samples/{MainThreadSampleLoader.js → DefaultSampleLoader.js} +7 -2
  40. package/dist/samples/{MainThreadSampleManager.d.ts → DefaultSampleLoaderManager.d.ts} +4 -5
  41. package/dist/samples/DefaultSampleLoaderManager.d.ts.map +1 -0
  42. package/dist/samples/DefaultSampleLoaderManager.js +19 -0
  43. package/dist/samples/OpenSampleAPI.d.ts.map +1 -1
  44. package/dist/samples/OpenSampleAPI.js +3 -2
  45. package/dist/samples/P2PSampleProvider.d.ts +13 -0
  46. package/dist/samples/P2PSampleProvider.d.ts.map +1 -0
  47. package/dist/samples/P2PSampleProvider.js +350 -0
  48. package/dist/samples/SampleStorage.d.ts +7 -1
  49. package/dist/samples/SampleStorage.d.ts.map +1 -1
  50. package/dist/samples/SampleStorage.js +1 -1
  51. package/dist/samples/index.d.ts +9 -0
  52. package/dist/samples/index.d.ts.map +1 -0
  53. package/dist/samples/index.js +8 -0
  54. package/dist/workers-main.js +2 -2
  55. package/dist/workers-main.js.map +3 -3
  56. package/package.json +15 -14
  57. package/dist/samples/MainThreadSampleLoader.d.ts.map +0 -1
  58. package/dist/samples/MainThreadSampleManager.d.ts.map +0 -1
  59. package/dist/samples/MainThreadSampleManager.js +0 -22
@@ -1,7 +1,7 @@
1
1
  import { Procedure, Terminable, TerminableOwner, Terminator } from "@opendaw/lib-std";
2
2
  import { BoxGraph, Editing } from "@opendaw/lib-box";
3
3
  import { AudioBusBox, AudioUnitBox, BoxIO, RootBox, TimelineBox, UserInterfaceBox } from "@opendaw/studio-boxes";
4
- import { BoxAdapters, BoxAdaptersContext, ClipSequencing, ParameterFieldAdapters, ProcessorOptions, ProjectDecoder, RootBoxAdapter, SampleManager, TimelineBoxAdapter, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
4
+ import { BoxAdapters, BoxAdaptersContext, ClipSequencing, ParameterFieldAdapters, ProcessorOptions, ProjectDecoder, RootBoxAdapter, SampleLoaderManager, TimelineBoxAdapter, UserEditingManager, VertexSelection } from "@opendaw/studio-adapters";
5
5
  import { LiveStreamBroadcaster, LiveStreamReceiver } from "@opendaw/lib-fusion";
6
6
  import { ProjectEnv } from "./ProjectEnv";
7
7
  import { Mixer } from "../Mixer";
@@ -46,14 +46,14 @@ export declare class Project implements BoxAdaptersContext, Terminable, Terminab
46
46
  get bpm(): number;
47
47
  get rootBoxAdapter(): RootBoxAdapter;
48
48
  get timelineBoxAdapter(): TimelineBoxAdapter;
49
- get sampleManager(): SampleManager;
49
+ get sampleManager(): SampleLoaderManager;
50
50
  get clipSequencing(): ClipSequencing;
51
51
  get isAudioContext(): boolean;
52
52
  get isMainThread(): boolean;
53
53
  get liveStreamBroadcaster(): LiveStreamBroadcaster;
54
54
  get skeleton(): ProjectDecoder.Skeleton;
55
55
  toArrayBuffer(): ArrayBufferLike;
56
- copy(): Project;
56
+ copy(env?: Partial<ProjectEnv>): Project;
57
57
  invalid(): boolean;
58
58
  terminate(): void;
59
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../src/project/Project.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,SAAS,EAET,UAAU,EACV,eAAe,EACf,UAAU,EAEb,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAA;AAClD,OAAO,EACH,WAAW,EACX,YAAY,EACZ,KAAK,EAGL,OAAO,EACP,WAAW,EAEX,gBAAgB,EACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,WAAW,EACX,kBAAkB,EAClB,cAAc,EAGd,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,aAAa,EACb,kBAAkB,EAElB,kBAAkB,EAClB,eAAe,EAClB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAC,qBAAqB,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAE7E,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AACvC,OAAO,EAAC,KAAK,EAAC,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAA;AAEjD,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,CAAA;CAAE,CAAA;AAG3F,qBAAa,OAAQ,YAAW,kBAAkB,EAAE,UAAU,EAAE,eAAe;;IAC3E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO;IAuCpC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO;IAM/D,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,GAAG,OAAO;IAQ5E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAE1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAA;IAC3C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAA;IAClC,QAAQ,CAAC,eAAe,EAAE,YAAY,CAAA;IACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IAEjC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAA;IAC/C,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAA;IACvD,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAA;IAC/C,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,MAAM,eAAqB;IAEpC,OAAO;IA+BP,iBAAiB,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa;IAoBtF,cAAc,CAAC,OAAO,GAAE,OAAc,GAAG,IAAI;IAM7C,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC;IAC3C,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;IAC5D,KAAK,IAAI,UAAU;IAEnB,IAAI,GAAG,IAAI,UAAU,CAAmB;IACxC,IAAI,GAAG,IAAI,MAAM,CAAyC;IAC1D,IAAI,cAAc,IAAI,cAAc,CAAmE;IACvG,IAAI,kBAAkB,IAAI,kBAAkB,CAA2E;IACvH,IAAI,aAAa,IAAI,aAAa,CAAiC;IACnE,IAAI,cAAc,IAAI,cAAc,CAAkD;IACtF,IAAI,cAAc,IAAI,OAAO,CAAe;IAC5C,IAAI,YAAY,IAAI,OAAO,CAAc;IACzC,IAAI,qBAAqB,IAAI,qBAAqB,CAAkD;IAEpG,IAAI,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAWtC;IAED,aAAa,IAAI,eAAe;IAiBhC,IAAI,IAAI,OAAO;IAEf,OAAO,IAAI,OAAO;IAelB,SAAS,IAAI,IAAI;CAIpB"}
1
+ {"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../src/project/Project.ts"],"names":[],"mappings":"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"}
@@ -167,7 +167,9 @@ export class Project {
167
167
  UUID.toDataOutput(output, this.timelineBox.address.uuid);
168
168
  return output.toArrayBuffer();
169
169
  }
170
- copy() { return Project.load(this.#env, this.toArrayBuffer()); }
170
+ copy(env) {
171
+ return Project.load({ ...this.#env, ...env }, this.toArrayBuffer());
172
+ }
171
173
  invalid() {
172
174
  return this.boxGraph.boxes().some(box => box.accept({
173
175
  visitTrackBox: (box) => {
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectBundle.d.ts","sourceRoot":"","sources":["../../src/project/ProjectBundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,sBAAsB,EAAiB,SAAS,EAAE,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAInH,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAM/C,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAU,gCAA8B,cAAc,EAC5C,UAAU,sBAAsB,CAAC,SAAS,CAAC,KAAG,OAAO,CAAC,WAAW,CAuB7F,CAAA;IAEM,MAAM,MAAM,GAAU,KAAK,UAAU,EACf,aAAa,WAAW,EACxB,kBAAkB,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,cAAc,CA0BjF,CAAA;CAyBJ"}
1
+ {"version":3,"file":"ProjectBundle.d.ts","sourceRoot":"","sources":["../../src/project/ProjectBundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,sBAAsB,EAAiB,SAAS,EAAE,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAInH,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAK/C,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAU,gCAA8B,cAAc,EAC5C,UAAU,sBAAsB,CAAC,SAAS,CAAC,KAAG,OAAO,CAAC,WAAW,CAuB7F,CAAA;IAEM,MAAM,MAAM,GAAU,KAAK,UAAU,EACf,aAAa,WAAW,EACxB,kBAAkB,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,cAAc,CA0BjF,CAAA;CAyBJ"}
@@ -4,7 +4,7 @@ import { Project } from "./Project";
4
4
  import { ProjectPaths } from "./ProjectPaths";
5
5
  import { ProjectProfile } from "./ProjectProfile";
6
6
  import { Workers } from "../Workers";
7
- import { SampleStorage } from "../samples/SampleStorage";
7
+ import { SampleStorage } from "../samples";
8
8
  export var ProjectBundle;
9
9
  (function (ProjectBundle) {
10
10
  ProjectBundle.encode = async ({ uuid, project, meta, cover }, progress) => {
@@ -1,8 +1,8 @@
1
- import { SampleManager } from "@opendaw/studio-adapters";
1
+ import { SampleLoaderManager } from "@opendaw/studio-adapters";
2
2
  import { AudioWorklets } from "../AudioWorklets";
3
3
  export interface ProjectEnv {
4
4
  audioContext: AudioContext;
5
5
  audioWorklets: AudioWorklets;
6
- sampleManager: SampleManager;
6
+ sampleManager: SampleLoaderManager;
7
7
  }
8
8
  //# sourceMappingURL=ProjectEnv.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectEnv.d.ts","sourceRoot":"","sources":["../../src/project/ProjectEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAE9C,MAAM,WAAW,UAAU;IACvB,YAAY,EAAE,YAAY,CAAA;IAC1B,aAAa,EAAE,aAAa,CAAA;IAC5B,aAAa,EAAE,aAAa,CAAA;CAC/B"}
1
+ {"version":3,"file":"ProjectEnv.d.ts","sourceRoot":"","sources":["../../src/project/ProjectEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAC,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAE9C,MAAM,WAAW,UAAU;IACvB,YAAY,EAAE,YAAY,CAAA;IAC1B,aAAa,EAAE,aAAa,CAAA;IAC5B,aAAa,EAAE,mBAAmB,CAAA;CACrC"}
@@ -1,10 +1,10 @@
1
1
  import { Observer, Option, Subscription, UUID } from "@opendaw/lib-std";
2
2
  import { Peaks } from "@opendaw/lib-fusion";
3
3
  import { AudioData, SampleLoader, SampleLoaderState, SampleMetaData } from "@opendaw/studio-adapters";
4
- import { MainThreadSampleManager } from "./MainThreadSampleManager";
5
- export declare class MainThreadSampleLoader implements SampleLoader {
4
+ import { DefaultSampleLoaderManager } from "./DefaultSampleLoaderManager";
5
+ export declare class DefaultSampleLoader implements SampleLoader {
6
6
  #private;
7
- constructor(manager: MainThreadSampleManager, uuid: UUID.Bytes);
7
+ constructor(manager: DefaultSampleLoaderManager, uuid: UUID.Bytes);
8
8
  invalidate(): void;
9
9
  subscribe(observer: Observer<SampleLoaderState>): Subscription;
10
10
  get uuid(): UUID.Bytes;
@@ -14,4 +14,4 @@ export declare class MainThreadSampleLoader implements SampleLoader {
14
14
  get state(): SampleLoaderState;
15
15
  toString(): string;
16
16
  }
17
- //# sourceMappingURL=MainThreadSampleLoader.d.ts.map
17
+ //# sourceMappingURL=DefaultSampleLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultSampleLoader.d.ts","sourceRoot":"","sources":["../../src/samples/DefaultSampleLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,QAAQ,EACR,MAAM,EAEN,YAAY,EAEZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAC,KAAK,EAAc,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAC,SAAS,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAEnG,OAAO,EAAC,0BAA0B,EAAC,MAAM,8BAA8B,CAAA;AAGvE,qBAAa,mBAAoB,YAAW,YAAY;;gBAYxC,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK;IAQjE,UAAU,IAAI,IAAI;IASlB,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,YAAY;IAQ9D,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,CAAoB;IAC1C,IAAI,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,CAAoB;IACjD,IAAI,IAAI,IAAI,MAAM,CAAC,cAAc,CAAC,CAAoB;IACtD,IAAI,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAqB;IAC/C,IAAI,KAAK,IAAI,iBAAiB,CAAqB;IAEnD,QAAQ,IAAI,MAAM;CAmErB"}
@@ -3,7 +3,7 @@ import { Promises } from "@opendaw/lib-runtime";
3
3
  import { SamplePeaks } from "@opendaw/lib-fusion";
4
4
  import { Workers } from "../Workers";
5
5
  import { SampleStorage } from "./SampleStorage";
6
- export class MainThreadSampleLoader {
6
+ export class DefaultSampleLoader {
7
7
  #manager;
8
8
  #uuid;
9
9
  #notifier;
@@ -82,7 +82,12 @@ export class MainThreadSampleLoader {
82
82
  const [audio, meta] = fetchResult.value;
83
83
  const shifts = SamplePeaks.findBestFit(audio.numberOfFrames);
84
84
  const peaks = await Workers.Peak.generateAsync(peakProgress, shifts, audio.frames, audio.numberOfFrames, audio.numberOfChannels);
85
- const storeResult = await Promises.tryCatch(SampleStorage.saveSample(this.#uuid, audio, peaks, meta));
85
+ const storeResult = await Promises.tryCatch(SampleStorage.saveSample({
86
+ uuid: this.#uuid,
87
+ audio: audio,
88
+ peaks: peaks,
89
+ meta: meta
90
+ }));
86
91
  if (this.#version !== version) {
87
92
  return;
88
93
  }
@@ -1,14 +1,13 @@
1
1
  import { Progress, UUID } from "@opendaw/lib-std";
2
- import { AudioData, SampleLoader, SampleManager, SampleMetaData } from "@opendaw/studio-adapters";
3
2
  import { SampleProvider } from "./SampleProvider";
4
- export declare class MainThreadSampleManager implements SampleManager, SampleProvider {
3
+ import { AudioData, SampleLoader, SampleLoaderManager, SampleMetaData } from "@opendaw/studio-adapters";
4
+ export declare class DefaultSampleLoaderManager implements SampleLoaderManager, SampleProvider {
5
5
  #private;
6
- constructor(api: SampleProvider, context: AudioContext);
7
- get context(): AudioContext;
6
+ constructor(provider: SampleProvider);
8
7
  fetch(uuid: UUID.Bytes, progress: Progress.Handler): Promise<[AudioData, SampleMetaData]>;
9
8
  remove(uuid: UUID.Bytes): void;
10
9
  invalidate(uuid: UUID.Bytes): void;
11
10
  record(loader: SampleLoader): void;
12
11
  getOrCreate(uuid: UUID.Bytes): SampleLoader;
13
12
  }
14
- //# sourceMappingURL=MainThreadSampleManager.d.ts.map
13
+ //# sourceMappingURL=DefaultSampleLoaderManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultSampleLoaderManager.d.ts","sourceRoot":"","sources":["../../src/samples/DefaultSampleLoaderManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAa,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,SAAS,EAAE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAErG,qBAAa,0BAA2B,YAAW,mBAAmB,EAAE,cAAc;;gBAItE,QAAQ,EAAE,cAAc;IAKpC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAIzF,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK;IACvB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK;IAE3B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAElC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,YAAY;CAG9C"}
@@ -0,0 +1,19 @@
1
+ import { UUID } from "@opendaw/lib-std";
2
+ import { DefaultSampleLoader } from "./DefaultSampleLoader";
3
+ export class DefaultSampleLoaderManager {
4
+ #provider;
5
+ #loaders;
6
+ constructor(provider) {
7
+ this.#provider = provider;
8
+ this.#loaders = UUID.newSet(loader => loader.uuid);
9
+ }
10
+ fetch(uuid, progress) {
11
+ return this.#provider.fetch(uuid, progress);
12
+ }
13
+ remove(uuid) { this.#loaders.removeByKey(uuid); }
14
+ invalidate(uuid) { this.#loaders.opt(uuid).ifSome(loader => loader.invalidate()); }
15
+ record(loader) { this.#loaders.add(loader); }
16
+ getOrCreate(uuid) {
17
+ return this.#loaders.getOrCreate(uuid, uuid => new DefaultSampleLoader(this, uuid));
18
+ }
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"OpenSampleAPI.d.ts","sourceRoot":"","sources":["../../src/samples/OpenSampleAPI.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,SAAS,EAGT,SAAS,EACT,IAAI,EACP,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAC1E,OAAO,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAY9C,qBAAa,aAAc,YAAW,SAAS;IAC3C,MAAM,CAAC,QAAQ,CAAC,OAAO,wCAAuC;IAC9D,MAAM,CAAC,QAAQ,CAAC,QAAQ,2CAA0C;IAGlE,MAAM,CAAC,GAAG,IAAI,aAAa;IAE3B,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS;IAStD,OAAO;IAED,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAKrC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAQtC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAkC3G,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC/E,YAAY,IAAI,OAAO;CAC1B"}
1
+ {"version":3,"file":"OpenSampleAPI.d.ts","sourceRoot":"","sources":["../../src/samples/OpenSampleAPI.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,SAAS,EAGT,SAAS,EACT,IAAI,EACP,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAC1E,OAAO,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAY9C,qBAAa,aAAc,YAAW,SAAS;IAC3C,MAAM,CAAC,QAAQ,CAAC,OAAO,wCAAuC;IAC9D,MAAM,CAAC,QAAQ,CAAC,QAAQ,2CAA0C;IAGlE,MAAM,CAAC,GAAG,IAAI,aAAa;IAE3B,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS;IAStD,OAAO;IAED,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAKrC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAQtC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAmC3G,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC/E,YAAY,IAAI,OAAO;CAC1B"}
@@ -45,7 +45,7 @@ export class OpenSampleAPI {
45
45
  else {
46
46
  return x;
47
47
  } });
48
- return Object.freeze({ ...sample, cloud: "cloud:openDAW" });
48
+ return Object.freeze({ ...sample, origin: "openDAW" });
49
49
  }
50
50
  async load(context, uuid, progress) {
51
51
  console.debug(`load ${UUID.toString(uuid)}`);
@@ -78,7 +78,8 @@ export class OpenSampleAPI {
78
78
  bpm,
79
79
  name,
80
80
  duration: audioBuffer.duration,
81
- sample_rate: audioBuffer.sampleRate
81
+ sample_rate: audioBuffer.sampleRate,
82
+ origin: "openDAW"
82
83
  }])));
83
84
  }
84
85
  async upload(arrayBuffer, metaData) {
@@ -0,0 +1,13 @@
1
+ import { Progress, UUID } from "@opendaw/lib-std";
2
+ import { AudioData, SampleMetaData } from "@opendaw/studio-adapters";
3
+ import * as Y from "yjs";
4
+ import { WebrtcProvider } from "y-webrtc";
5
+ import { SampleProvider } from "./SampleProvider";
6
+ export declare class P2PSampleProvider implements SampleProvider {
7
+ #private;
8
+ static create(doc: Y.Doc, provider: WebrtcProvider): P2PSampleProvider;
9
+ constructor(doc: Y.Doc, provider: WebrtcProvider);
10
+ fetch(uuid: UUID.Bytes, progress: Progress.Handler): Promise<[AudioData, SampleMetaData]>;
11
+ share(uuidAsString: string, audioData: AudioData, peaks: ArrayBuffer, metadata: SampleMetaData): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=P2PSampleProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"P2PSampleProvider.d.ts","sourceRoot":"","sources":["../../src/samples/P2PSampleProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,QAAQ,EAAE,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAC5F,OAAO,EAAC,SAAS,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAClE,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AACxB,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAA;AACvC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAkC/C,qBAAa,iBAAkB,YAAW,cAAc;;IACpD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,cAAc,GAAG,iBAAiB;gBAc1D,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,cAAc;IAY1C,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAmDzF,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAkSvH"}
@@ -0,0 +1,350 @@
1
+ import { asDefined, isDefined, isNotUndefined, panic, UUID } from "@opendaw/lib-std";
2
+ import { SampleStorage } from "./SampleStorage";
3
+ export class P2PSampleProvider {
4
+ static create(doc, provider) {
5
+ return new P2PSampleProvider(doc, provider);
6
+ }
7
+ #doc;
8
+ #provider;
9
+ #samplesMap;
10
+ #CHUNK_SIZE = 16384;
11
+ #CHANNEL_LABEL = "openDAW-samples";
12
+ #peerChannels = new Map();
13
+ #activeTransfers = new Map();
14
+ #pendingFetches = new Map();
15
+ constructor(doc, provider) {
16
+ this.#doc = doc;
17
+ this.#provider = provider;
18
+ this.#samplesMap = doc.getMap("samples");
19
+ console.log(`[P2P] Initializing with provider:`, provider);
20
+ console.log(`[P2P] Provider maxConns:`, provider.maxConns);
21
+ console.log(`[P2P] Provider filterBcConns:`, provider.filterBcConns);
22
+ console.log(`[P2P] Provider room:`, provider.room);
23
+ console.log(`[P2P] Provider webrtcConns size:`, provider.webrtcConns?.size);
24
+ this.#init();
25
+ }
26
+ async fetch(uuid, progress) {
27
+ const uuidAsString = UUID.toString(uuid);
28
+ console.log(`[P2P] Fetching sample: ${uuidAsString}`);
29
+ try {
30
+ const [audio, _, meta] = await SampleStorage.loadSample(UUID.parse(uuidAsString));
31
+ console.log(`[P2P] Sample found in storage: ${uuidAsString}`);
32
+ progress(1.0);
33
+ return [audio, meta];
34
+ }
35
+ catch (error) {
36
+ console.log(`[P2P] Sample not in storage, requesting from peers: ${uuidAsString}`);
37
+ }
38
+ const existingPromise = this.#pendingFetches.get(uuidAsString);
39
+ if (isNotUndefined(existingPromise)) {
40
+ console.log(`[P2P] Sample already being fetched: ${uuidAsString}`);
41
+ return existingPromise;
42
+ }
43
+ this.#requestSampleFromPeers(uuidAsString);
44
+ const promise = new Promise((resolve, reject) => {
45
+ const timeout = setTimeout(() => {
46
+ console.warn(`[P2P] Sample fetch timeout: ${uuidAsString}`);
47
+ this.#pendingFetches.delete(uuidAsString);
48
+ this.#activeTransfers.delete(uuidAsString);
49
+ reject(new Error(`No sample source available for ${uuid}`));
50
+ }, 30000);
51
+ this.#activeTransfers.set(uuidAsString, {
52
+ chunks: [],
53
+ expectedSize: 0,
54
+ receivedSize: 0,
55
+ metadata: {},
56
+ resolve,
57
+ reject,
58
+ progress,
59
+ timeout
60
+ });
61
+ });
62
+ this.#pendingFetches.set(uuidAsString, promise);
63
+ return promise;
64
+ }
65
+ #requestSampleFromPeers(uuid) {
66
+ console.log(`[P2P] Requesting sample from ${this.#peerChannels.size} peers: ${uuid}`);
67
+ this.#peerChannels.forEach(channel => {
68
+ if (channel.readyState === "open") {
69
+ channel.send(JSON.stringify({
70
+ type: "request",
71
+ uuid: uuid
72
+ }));
73
+ }
74
+ });
75
+ }
76
+ async share(uuidAsString, audioData, peaks, metadata) {
77
+ console.log(`[P2P] Sharing sample: ${uuidAsString}`);
78
+ await SampleStorage.saveSample({
79
+ uuid: UUID.parse(uuidAsString),
80
+ audio: audioData,
81
+ peaks: peaks,
82
+ meta: metadata
83
+ });
84
+ this.#samplesMap.set(uuidAsString, {
85
+ uuid: uuidAsString,
86
+ ownerId: this.#getMyId(),
87
+ metadata,
88
+ timestamp: Date.now()
89
+ });
90
+ const zipData = await this.#createZipFile(audioData, peaks, metadata);
91
+ console.log(`[P2P] Broadcasting sample to ${this.#peerChannels.size} peers (${zipData.byteLength} bytes): ${uuidAsString}`);
92
+ this.#peerChannels.forEach((channel) => {
93
+ if (channel.readyState === "open") {
94
+ this.#sendSample(channel, uuidAsString, zipData, metadata);
95
+ }
96
+ });
97
+ }
98
+ async #createZipFile(audioData, peaks, metadata) {
99
+ const { default: JSZip } = await import("jszip");
100
+ const zip = new JSZip();
101
+ zip.file("version", "1");
102
+ zip.file("metadata.json", JSON.stringify(metadata));
103
+ zip.file("audio.bin", this.#audioDataToArrayBuffer(audioData), { binary: true });
104
+ zip.file("peaks.bin", peaks, { binary: true });
105
+ return await zip.generateAsync({ type: "arraybuffer" });
106
+ }
107
+ async #extractZipFile(zipBuffer) {
108
+ const { default: JSZip } = await import("jszip");
109
+ const zip = new JSZip();
110
+ await zip.loadAsync(zipBuffer);
111
+ const version = await zip.file("version")?.async("string");
112
+ if (version !== "1") {
113
+ return panic(`Unsupported zip version: ${version}`);
114
+ }
115
+ const metadataText = await zip.file("metadata.json")?.async("string");
116
+ if (!isDefined(metadataText)) {
117
+ return panic("Missing metadata.json in zip");
118
+ }
119
+ const metadata = JSON.parse(metadataText);
120
+ const audioBuffer = await zip.file("audio.bin")?.async("arraybuffer");
121
+ if (!isDefined(audioBuffer)) {
122
+ return panic("Missing audio.bin in zip");
123
+ }
124
+ const peaks = await zip.file("peaks.bin")?.async("arraybuffer");
125
+ if (!isDefined(peaks)) {
126
+ return panic("Missing peaks.bin in zip");
127
+ }
128
+ const audioData = this.#arrayBufferToAudioData(audioBuffer);
129
+ return { audioData, peaks, metadata };
130
+ }
131
+ #init() {
132
+ this.#setupPeers();
133
+ this.#setupSampleListener();
134
+ }
135
+ #setupPeers() {
136
+ console.log(`[P2P] Setting up peer management`);
137
+ this.#provider.on("peers", (event) => {
138
+ console.log(`[P2P] Peers event:`, event);
139
+ event.added.forEach(peerId => {
140
+ console.log(`[P2P] Peer connected: ${peerId}`);
141
+ if (event.webrtcPeers.includes(peerId)) {
142
+ console.log(`[P2P] Setting up WebRTC data channel for: ${peerId}`);
143
+ this.#createChannel(peerId);
144
+ this.#setupIncomingChannelHandler(peerId);
145
+ }
146
+ else if (event.bcPeers.includes(peerId)) {
147
+ console.log(`[P2P] Peer is using BroadcastChannel (same device): ${peerId}`);
148
+ console.log(`[P2P] Sample sharing not available for BroadcastChannel peers`);
149
+ }
150
+ });
151
+ event.removed.forEach(peerId => {
152
+ console.log(`[P2P] Peer disconnected: ${peerId}`);
153
+ this.#peerChannels.delete(peerId);
154
+ });
155
+ console.log(`[P2P] Total WebRTC peers: ${event.webrtcPeers.length}`);
156
+ console.log(`[P2P] Total BroadcastChannel peers: ${event.bcPeers.length}`);
157
+ console.log(`[P2P] Total active data channels: ${this.#peerChannels.size}`);
158
+ });
159
+ }
160
+ #setupIncomingChannelHandler(peerId) {
161
+ const conn = this.#provider.webrtcConns?.get(peerId);
162
+ if (!conn?.peer)
163
+ return;
164
+ conn.peer.ondatachannel = (event) => {
165
+ if (event.channel.label === this.#CHANNEL_LABEL) {
166
+ console.log(`[P2P] Incoming data channel from: ${peerId}`);
167
+ this.#setupChannel(event.channel, peerId);
168
+ }
169
+ };
170
+ }
171
+ #createChannel(peerId) {
172
+ console.log(`[P2P] Creating data channel for peer: ${peerId}`);
173
+ const conn = this.#provider.webrtcConns?.get(peerId);
174
+ if (!conn?.peer) {
175
+ console.warn(`[P2P] No WebRTC connection found for peer: ${peerId}`);
176
+ return;
177
+ }
178
+ console.log(`[P2P] WebRTC connection state for ${peerId}:`, conn.peer.connectionState);
179
+ const channel = conn.peer.createDataChannel(this.#CHANNEL_LABEL, {
180
+ ordered: true,
181
+ maxRetransmits: 3
182
+ });
183
+ this.#setupChannel(channel, peerId);
184
+ }
185
+ #setupChannel(channel, peerId) {
186
+ channel.binaryType = "arraybuffer";
187
+ channel.onopen = () => {
188
+ console.log(`[P2P] Data channel opened: ${peerId}`);
189
+ this.#peerChannels.set(peerId, channel);
190
+ };
191
+ channel.onclose = () => {
192
+ console.log(`[P2P] Data channel closed: ${peerId}`);
193
+ this.#peerChannels.delete(peerId);
194
+ };
195
+ channel.onmessage = (event) => this.#handleMessage(event.data, peerId);
196
+ }
197
+ #setupSampleListener() {
198
+ this.#samplesMap.observe((event) => {
199
+ event.changes.keys.forEach((change, uuid) => {
200
+ if (change.action === "add") {
201
+ const sampleRef = this.#samplesMap.get(uuid);
202
+ if (sampleRef?.ownerId === this.#getMyId()) {
203
+ console.log(`[P2P] Own sample reference added: ${uuid}`);
204
+ }
205
+ }
206
+ });
207
+ });
208
+ }
209
+ #sendSample(channel, uuid, zipData, metadata) {
210
+ console.log(`[P2P] Starting sample transfer: ${uuid} (${zipData.byteLength} bytes)`);
211
+ channel.send(JSON.stringify({
212
+ type: "start",
213
+ uuid,
214
+ size: zipData.byteLength,
215
+ metadata
216
+ }));
217
+ const chunks = Math.ceil(zipData.byteLength / this.#CHUNK_SIZE);
218
+ for (let i = 0; i < chunks; i++) {
219
+ const chunk = zipData.slice(i * this.#CHUNK_SIZE, (i + 1) * this.#CHUNK_SIZE);
220
+ channel.send(chunk);
221
+ }
222
+ channel.send(JSON.stringify({ type: "complete", uuid }));
223
+ console.log(`[P2P] Sample transfer completed: ${uuid} (${chunks} chunks)`);
224
+ }
225
+ #handleMessage(data, peerId) {
226
+ if (typeof data === "string") {
227
+ const msg = JSON.parse(data);
228
+ if (msg.type === "start") {
229
+ console.log(`[P2P] Receiving sample from ${peerId}: ${msg.uuid} (${msg.size} bytes)`);
230
+ const transfer = this.#activeTransfers.get(msg.uuid);
231
+ if (isNotUndefined(transfer)) {
232
+ transfer.expectedSize = msg.size;
233
+ transfer.metadata = msg.metadata;
234
+ }
235
+ else {
236
+ console.warn(`[P2P] Received sample start for unknown transfer: ${msg.uuid}`);
237
+ }
238
+ }
239
+ else if (msg.type === "complete") {
240
+ console.log(`[P2P] Sample transfer complete from ${peerId}: ${msg.uuid}`);
241
+ this.#complete(msg.uuid);
242
+ }
243
+ else if (msg.type === "request") {
244
+ console.log(`[P2P] Sample request from ${peerId}: ${msg.uuid}`);
245
+ this.#handleSampleRequest(msg.uuid);
246
+ }
247
+ }
248
+ else {
249
+ const uuid = Array.from(this.#activeTransfers.keys()).find(id => {
250
+ const transfer = this.#activeTransfers.get(id);
251
+ if (isNotUndefined(transfer)) {
252
+ return transfer.receivedSize < transfer.expectedSize;
253
+ }
254
+ return false;
255
+ });
256
+ if (isNotUndefined(uuid)) {
257
+ const transfer = this.#activeTransfers.get(uuid);
258
+ if (isNotUndefined(transfer)) {
259
+ transfer.chunks.push(data);
260
+ transfer.receivedSize += data.byteLength;
261
+ const progress = transfer.receivedSize / transfer.expectedSize;
262
+ console.log(`[P2P] Receiving sample ${uuid}: ${Math.round(progress * 100)}% (${transfer.receivedSize}/${transfer.expectedSize} bytes)`);
263
+ transfer.progress(progress);
264
+ }
265
+ }
266
+ }
267
+ }
268
+ #handleSampleRequest(uuid) {
269
+ SampleStorage.loadSample(UUID.parse(uuid)).then(async ([audio, peaks, meta]) => {
270
+ console.log(`[P2P] Responding to sample request: ${uuid}`);
271
+ const zipData = await this.#createZipFile(audio, peaks.toArrayBuffer(), meta);
272
+ this.#peerChannels.forEach(channel => {
273
+ if (channel.readyState === "open") {
274
+ this.#sendSample(channel, uuid, zipData, meta);
275
+ }
276
+ });
277
+ }).catch(error => {
278
+ console.log(`[P2P] Sample not available for request: ${uuid}`, error);
279
+ });
280
+ }
281
+ async #complete(uuid) {
282
+ const transfer = asDefined(this.#activeTransfers.get(uuid), `Expected active transfer for ${uuid}`);
283
+ console.log(`[P2P] Reconstructing sample: ${uuid}`);
284
+ const zipBuffer = new ArrayBuffer(transfer.expectedSize);
285
+ const zipView = new Uint8Array(zipBuffer);
286
+ let offset = 0;
287
+ transfer.chunks.forEach(chunk => {
288
+ zipView.set(new Uint8Array(chunk), offset);
289
+ offset += chunk.byteLength;
290
+ });
291
+ try {
292
+ const { audioData, peaks, metadata } = await this.#extractZipFile(zipBuffer);
293
+ await SampleStorage.saveSample({
294
+ uuid: UUID.parse(uuid),
295
+ audio: audioData,
296
+ peaks: peaks,
297
+ meta: metadata
298
+ });
299
+ console.log(`[P2P] Sample saved successfully: ${uuid}`);
300
+ transfer.resolve([audioData, metadata]);
301
+ this.#pendingFetches.delete(uuid);
302
+ }
303
+ catch (error) {
304
+ console.error(`[P2P] Failed to save sample ${uuid}:`, error);
305
+ transfer.reject(error);
306
+ this.#pendingFetches.delete(uuid);
307
+ }
308
+ finally {
309
+ clearTimeout(transfer.timeout);
310
+ }
311
+ this.#activeTransfers.delete(uuid);
312
+ }
313
+ #getMyId() {
314
+ return this.#provider.awareness.clientID.toString();
315
+ }
316
+ #audioDataToArrayBuffer(audioData) {
317
+ const totalSamples = audioData.numberOfFrames * audioData.numberOfChannels;
318
+ const buffer = new ArrayBuffer(4 + 4 + 4 + totalSamples * 4);
319
+ const view = new DataView(buffer);
320
+ view.setUint32(0, audioData.sampleRate, true);
321
+ view.setUint32(4, audioData.numberOfFrames, true);
322
+ view.setUint32(8, audioData.numberOfChannels, true);
323
+ let offset = 12;
324
+ for (let frame = 0; frame < audioData.numberOfFrames; frame++) {
325
+ for (let channel = 0; channel < audioData.numberOfChannels; channel++) {
326
+ view.setFloat32(offset, audioData.frames[channel][frame], true);
327
+ offset += 4;
328
+ }
329
+ }
330
+ return buffer;
331
+ }
332
+ #arrayBufferToAudioData(buffer) {
333
+ const view = new DataView(buffer);
334
+ const sampleRate = view.getUint32(0, true);
335
+ const numberOfFrames = view.getUint32(4, true);
336
+ const numberOfChannels = view.getUint32(8, true);
337
+ const frames = [];
338
+ for (let channel = 0; channel < numberOfChannels; channel++) {
339
+ frames.push(new Float32Array(numberOfFrames));
340
+ }
341
+ let offset = 12;
342
+ for (let frame = 0; frame < numberOfFrames; frame++) {
343
+ for (let channel = 0; channel < numberOfChannels; channel++) {
344
+ frames[channel][frame] = view.getFloat32(offset, true);
345
+ offset += 4;
346
+ }
347
+ }
348
+ return { sampleRate, numberOfFrames, numberOfChannels, frames };
349
+ }
350
+ }
@@ -4,7 +4,13 @@ import { AudioData, Sample, SampleMetaData } from "@opendaw/studio-adapters";
4
4
  export declare namespace SampleStorage {
5
5
  const clean: () => Promise<void>;
6
6
  const Folder = "samples/v2";
7
- const saveSample: (uuid: UUID.Bytes, audio: AudioData, peaks: ArrayBuffer, meta: SampleMetaData) => Promise<void>;
7
+ type New = {
8
+ uuid: UUID.Bytes;
9
+ audio: AudioData;
10
+ peaks: ArrayBuffer;
11
+ meta: SampleMetaData;
12
+ };
13
+ const saveSample: ({ uuid, audio, peaks, meta }: New) => Promise<void>;
8
14
  const updateSampleMeta: (uuid: UUID.Bytes, meta: SampleMetaData) => Promise<void>;
9
15
  const loadSample: (uuid: UUID.Bytes) => Promise<[AudioData, Peaks, SampleMetaData]>;
10
16
  const deleteSample: (uuid: UUID.Bytes) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"SampleStorage.d.ts","sourceRoot":"","sources":["../../src/samples/SampleStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAEhE,OAAO,EAAC,KAAK,EAAc,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAI1E,yBAAiB,aAAa,CAAC;IACpB,MAAM,KAAK,qBAA2D,CAAA;IAEtE,MAAM,MAAM,eAAe,CAAA;IAE3B,MAAM,UAAU,GAAU,MAAM,IAAI,CAAC,KAAK,EAChB,OAAO,SAAS,EAChB,OAAO,WAAW,EAClB,MAAM,cAAc,KAAG,OAAO,CAAC,IAAI,CAanE,CAAA;IAEM,MAAM,gBAAgB,GAAU,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,cAAc,KAAG,OAAO,CAAC,IAAI,CAG3F,CAAA;IAEM,MAAM,UAAU,GAAU,MAAM,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,cAAc,CAAC,CAe7F,CAAA;IAEM,MAAM,YAAY,GAAU,MAAM,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,IAAI,CAMjE,CAAA;IAEM,MAAM,cAAc,QAAa,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAGjE,CAAA;IAEM,MAAM,cAAc,GAAU,KAAK,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,IAAI,CAGlF,CAAA;IAEM,MAAM,WAAW,QAAa,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAOjE,CAAA;CACJ"}
1
+ {"version":3,"file":"SampleStorage.d.ts","sourceRoot":"","sources":["../../src/samples/SampleStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAEhE,OAAO,EAAC,KAAK,EAAc,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAC,MAAM,0BAA0B,CAAA;AAI1E,yBAAiB,aAAa,CAAC;IACpB,MAAM,KAAK,qBAA2D,CAAA;IAEtE,MAAM,MAAM,eAAe,CAAA;IAElC,KAAY,GAAG,GAAG;QACd,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC;QACjB,KAAK,EAAE,SAAS,CAAC;QACjB,KAAK,EAAE,WAAW,CAAC;QACnB,IAAI,EAAE,cAAc,CAAA;KACvB,CAAA;IAEM,MAAM,UAAU,GAAU,8BAA4B,GAAG,KAAG,OAAO,CAAC,IAAI,CAa9E,CAAA;IAEM,MAAM,gBAAgB,GAAU,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,cAAc,KAAG,OAAO,CAAC,IAAI,CAG3F,CAAA;IAEM,MAAM,UAAU,GAAU,MAAM,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,cAAc,CAAC,CAe7F,CAAA;IAEM,MAAM,YAAY,GAAU,MAAM,IAAI,CAAC,KAAK,KAAG,OAAO,CAAC,IAAI,CAMjE,CAAA;IAEM,MAAM,cAAc,QAAa,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAGjE,CAAA;IAEM,MAAM,cAAc,GAAU,KAAK,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,IAAI,CAGlF,CAAA;IAEM,MAAM,WAAW,QAAa,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAOjE,CAAA;CACJ"}
@@ -7,7 +7,7 @@ export var SampleStorage;
7
7
  (function (SampleStorage) {
8
8
  SampleStorage.clean = () => Workers.Opfs.delete("samples/v1").catch(EmptyExec);
9
9
  SampleStorage.Folder = "samples/v2";
10
- SampleStorage.saveSample = async (uuid, audio, peaks, meta) => {
10
+ SampleStorage.saveSample = async ({ uuid, audio, peaks, meta }) => {
11
11
  const path = `${SampleStorage.Folder}/${UUID.toString(uuid)}`;
12
12
  const data = new Uint8Array(WavFile.encodeFloats({
13
13
  channels: audio.frames.slice(),
@@ -0,0 +1,9 @@
1
+ export * from "./DefaultSampleLoader";
2
+ export * from "./DefaultSampleLoaderManager";
3
+ export * from "./OpenSampleAPI";
4
+ export * from "./P2PSampleProvider";
5
+ export * from "./SampleAPI";
6
+ export * from "./SampleImporter";
7
+ export * from "./SampleProvider";
8
+ export * from "./SampleStorage";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/samples/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,8 @@
1
+ export * from "./DefaultSampleLoader";
2
+ export * from "./DefaultSampleLoaderManager";
3
+ export * from "./OpenSampleAPI";
4
+ export * from "./P2PSampleProvider";
5
+ export * from "./SampleAPI";
6
+ export * from "./SampleImporter";
7
+ export * from "./SampleProvider";
8
+ export * from "./SampleStorage";