@opendaw/studio-core 0.0.114 → 0.0.116

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 (33) hide show
  1. package/dist/AudioConsolidation.d.ts +6 -0
  2. package/dist/AudioConsolidation.d.ts.map +1 -0
  3. package/dist/AudioConsolidation.js +97 -0
  4. package/dist/Workers.d.ts.map +1 -1
  5. package/dist/Workers.js +1 -0
  6. package/dist/capture/RecordAudio.d.ts.map +1 -1
  7. package/dist/capture/RecordAudio.js +7 -7
  8. package/dist/capture/Recording.js +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/processors.js +1 -1
  13. package/dist/processors.js.map +2 -2
  14. package/dist/samples/SampleStorage.d.ts +1 -0
  15. package/dist/samples/SampleStorage.d.ts.map +1 -1
  16. package/dist/samples/SampleStorage.js +4 -0
  17. package/dist/soundfont/DefaultSoundfontLoader.d.ts +2 -2
  18. package/dist/soundfont/DefaultSoundfontLoader.d.ts.map +1 -1
  19. package/dist/soundfont/{DefaultSoundfontLoaderManager.d.ts → GlobalSoundfontLoaderManager.d.ts} +2 -2
  20. package/dist/soundfont/GlobalSoundfontLoaderManager.d.ts.map +1 -0
  21. package/dist/soundfont/{DefaultSoundfontLoaderManager.js → GlobalSoundfontLoaderManager.js} +1 -1
  22. package/dist/soundfont/index.d.ts +1 -1
  23. package/dist/soundfont/index.d.ts.map +1 -1
  24. package/dist/soundfont/index.js +1 -1
  25. package/dist/ui/menu/MenuItems.d.ts +3 -3
  26. package/dist/ui/menu/MenuItems.d.ts.map +1 -1
  27. package/dist/ui/renderer/notes.d.ts +2 -2
  28. package/dist/ui/renderer/notes.d.ts.map +1 -1
  29. package/dist/ui/renderer/notes.js +1 -2
  30. package/dist/workers-main.js +1 -1
  31. package/dist/workers-main.js.map +2 -2
  32. package/package.json +11 -11
  33. package/dist/soundfont/DefaultSoundfontLoaderManager.d.ts.map +0 -1
@@ -0,0 +1,6 @@
1
+ import { AudioRegionBoxAdapter } from "@opendaw/studio-adapters";
2
+ import { Project, SampleService } from "./index";
3
+ export declare namespace AudioConsolidation {
4
+ const flatten: (project: Project, sampleService: SampleService, regions: ReadonlyArray<AudioRegionBoxAdapter>, abortSignal?: AbortSignal) => Promise<void>;
5
+ }
6
+ //# sourceMappingURL=AudioConsolidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioConsolidation.d.ts","sourceRoot":"","sources":["../src/AudioConsolidation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,qBAAqB,EAA2B,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAIH,OAAO,EACP,aAAa,EAGhB,MAAM,SAAS,CAAA;AAEhB,yBAAiB,kBAAkB,CAAC;IACzB,MAAM,OAAO,GAAU,SAAS,OAAO,EAChB,eAAe,aAAa,EAC5B,SAAS,aAAa,CAAC,qBAAqB,CAAC,EAC7C,cAAc,WAAW,KAAG,OAAO,CAAC,IAAI,CAwFrE,CAAA;CACJ"}
@@ -0,0 +1,97 @@
1
+ import { Arrays, Errors, isDefined, Option, RuntimeNotifier, UUID } from "@opendaw/lib-std";
2
+ import { AudioData, RegionCollection } from "@opendaw/lib-dsp";
3
+ import { Promises } from "@opendaw/lib-runtime";
4
+ import { AudioContentFactory, AudioFileBoxFactory, OfflineEngineRenderer, WavFile, Workers } from "./index";
5
+ export var AudioConsolidation;
6
+ (function (AudioConsolidation) {
7
+ AudioConsolidation.flatten = async (project, sampleService, regions, abortSignal) => {
8
+ if (regions.length === 0) {
9
+ return;
10
+ }
11
+ const sorted = regions.toSorted(RegionCollection.Comparator);
12
+ const first = Arrays.getFirst(sorted, "No first region");
13
+ const last = Arrays.getLast(sorted, "No last region");
14
+ const rangeMin = first.position;
15
+ const rangeMax = last.complete;
16
+ const trackBoxAdapter = first.trackBoxAdapter.unwrap("Has no trackAdapter");
17
+ const audioUnitUuid = UUID.toString(trackBoxAdapter.audioUnit.address.uuid);
18
+ const sampleRate = sampleService.audioContext.sampleRate;
19
+ const durationSeconds = project.tempoMap.intervalToSeconds(rangeMin, rangeMax);
20
+ const numSamples = Math.ceil(durationSeconds * sampleRate);
21
+ const exportConfiguration = {
22
+ [audioUnitUuid]: {
23
+ includeAudioEffects: false,
24
+ includeSends: false,
25
+ useInstrumentOutput: true,
26
+ fileName: `Merged ${first.file.fileName}`
27
+ }
28
+ };
29
+ const selectedUuids = UUID.newSet(uuid => uuid);
30
+ sorted.forEach(region => selectedUuids.add(region.uuid));
31
+ const allRegionsInRange = [...trackBoxAdapter.regions.collection.iterateRange(rangeMin, rangeMax)];
32
+ const nonSelectedUuids = allRegionsInRange
33
+ .filter(region => !selectedUuids.hasKey(region.uuid))
34
+ .map(region => region.uuid);
35
+ const copiedProject = project.copy();
36
+ copiedProject.boxGraph.beginTransaction();
37
+ nonSelectedUuids.forEach(uuid => copiedProject.boxGraph.findBox(uuid).ifSome(box => box.delete()));
38
+ copiedProject.boxGraph.endTransaction();
39
+ const abortController = new AbortController();
40
+ if (isDefined(abortSignal)) {
41
+ abortSignal.addEventListener("abort", () => abortController.abort());
42
+ }
43
+ const dialog = RuntimeNotifier.progress({
44
+ headline: "Flattening Audio Regions...",
45
+ cancel: () => abortController.abort()
46
+ });
47
+ const renderResult = await Promises.tryCatch((async () => {
48
+ const renderer = await OfflineEngineRenderer.create(copiedProject, Option.wrap(exportConfiguration), sampleRate);
49
+ try {
50
+ await renderer.waitForLoading();
51
+ renderer.setPosition(rangeMin);
52
+ renderer.play();
53
+ await renderer.waitForLoading();
54
+ const channels = await renderer.step(numSamples);
55
+ const audioData = AudioData.create(sampleRate, numSamples, 2);
56
+ audioData.frames[0].set(channels[0]);
57
+ audioData.frames[1].set(channels[1]);
58
+ return audioData;
59
+ }
60
+ finally {
61
+ renderer.terminate();
62
+ }
63
+ })());
64
+ if (renderResult.status === "rejected") {
65
+ dialog.terminate();
66
+ if (!Errors.isAbort(renderResult.error)) {
67
+ await RuntimeNotifier.info({ headline: "Flatten Failed", message: String(renderResult.error) });
68
+ }
69
+ return;
70
+ }
71
+ const audioData = renderResult.value;
72
+ dialog.message = "Importing sample...";
73
+ const importResult = await Promises.tryCatch(sampleService.importFile({ name: "flatten", arrayBuffer: WavFile.encodeFloats(audioData) }));
74
+ if (importResult.status === "rejected") {
75
+ dialog.terminate();
76
+ await RuntimeNotifier.info({ headline: "Flatten Failed", message: String(importResult.error) });
77
+ return;
78
+ }
79
+ const sample = importResult.value;
80
+ const sampleUuid = UUID.parse(sample.uuid);
81
+ dialog.terminate();
82
+ const audioFileBoxModifier = await AudioFileBoxFactory.createModifier(Workers.Transients, project.boxGraph, audioData, sampleUuid, sample.name);
83
+ project.editing.modify(() => {
84
+ const audioFileBox = audioFileBoxModifier();
85
+ allRegionsInRange.forEach(region => region.box.delete());
86
+ AudioContentFactory.createNotStretchedRegion({
87
+ boxGraph: project.boxGraph,
88
+ targetTrack: trackBoxAdapter.box,
89
+ audioFileBox,
90
+ sample,
91
+ position: rangeMin,
92
+ name: first.label
93
+ });
94
+ project.trackUserCreatedSample(sampleUuid);
95
+ });
96
+ };
97
+ })(AudioConsolidation || (AudioConsolidation = {}));
@@ -1 +1 @@
1
- {"version":3,"file":"Workers.d.ts","sourceRoot":"","sources":["../src/Workers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAY,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AACzE,OAAO,KAAK,EAAY,iBAAiB,EAAC,MAAM,kBAAkB,CAAA;AAElE,qBAAa,OAAO;WACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhD,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAc;IAGjD,MAAM,KAAK,IAAI,IAAI,kBAAkB,CAapC;IAGD,MAAM,KAAK,IAAI,IAAI,YAAY,CAS9B;IAGD,MAAM,KAAK,UAAU,IAAI,iBAAiB,CAMzC;CACJ"}
1
+ {"version":3,"file":"Workers.d.ts","sourceRoot":"","sources":["../src/Workers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAY,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AACzE,OAAO,KAAK,EAAY,iBAAiB,EAAC,MAAM,kBAAkB,CAAA;AAElE,qBAAa,OAAO;WACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhD,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAc;IAGjD,MAAM,KAAK,IAAI,IAAI,kBAAkB,CAapC;IAGD,MAAM,KAAK,IAAI,IAAI,YAAY,CAU9B;IAGD,MAAM,KAAK,UAAU,IAAI,iBAAiB,CAMzC;CACJ"}
package/dist/Workers.js CHANGED
@@ -39,6 +39,7 @@ export class Workers {
39
39
  .sender(this.messenger.unwrap("Workers are not installed").channel("opfs"), router => new class {
40
40
  write(path, data) { return router.dispatchAndReturn(this.write, path, data); }
41
41
  read(path) { return router.dispatchAndReturn(this.read, path); }
42
+ exists(path) { return router.dispatchAndReturn(this.exists, path); }
42
43
  delete(path) { return router.dispatchAndReturn(this.delete, path); }
43
44
  list(path) { return router.dispatchAndReturn(this.list, path); }
44
45
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,UAAU,EAA6B,MAAM,kBAAkB,CAAA;AAG3H,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;AAGjC,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,UAmKL,CAAA;;CACJ"}
1
+ {"version":3,"file":"RecordAudio.d.ts","sourceRoot":"","sources":["../../src/capture/RecordAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,UAAU,EAA6B,MAAM,kBAAkB,CAAA;AAG3H,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,UAgKL,CAAA;;CACJ"}
@@ -2,6 +2,7 @@ import { asInstanceOf, Option, quantizeFloor, Terminable, Terminator, tryCatch,
2
2
  import { PPQN, TimeBase } from "@opendaw/lib-dsp";
3
3
  import { AudioFileBox, AudioRegionBox, TrackBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
4
4
  import { ColorCodes, TrackType, UnionBoxTypes } from "@opendaw/studio-adapters";
5
+ import { Recording } from "./Recording";
5
6
  import { RecordTrack } from "./RecordTrack";
6
7
  export var RecordAudio;
7
8
  (function (RecordAudio) {
@@ -16,7 +17,6 @@ export var RecordAudio;
16
17
  let lastPosition = 0;
17
18
  let currentWaveformOffset = outputLatency;
18
19
  let takeNumber = 0;
19
- let hadCountIn = false;
20
20
  const { env: { audioContext: { sampleRate } }, engine: { preferences: { settings: { recording } } } } = project;
21
21
  const { loopArea } = timelineBox;
22
22
  const createFileBox = () => {
@@ -108,7 +108,7 @@ export var RecordAudio;
108
108
  currentTake.ifSome(({ regionBox: { duration } }) => {
109
109
  recordingWorklet.limit(Math.ceil((currentWaveformOffset + duration.getValue()) * sampleRate));
110
110
  });
111
- fileBox.ifSome(fb => fb.endInSeconds.setValue(recordingWorklet.numberOfFrames / sampleRate));
111
+ fileBox.ifSome(({ endInSeconds }) => endInSeconds.setValue(recordingWorklet.numberOfFrames / sampleRate));
112
112
  }
113
113
  }), engine.position.catchupAndSubscribe(owner => {
114
114
  const isCountingIn = engine.isCountingIn.getValue();
@@ -117,9 +117,7 @@ export var RecordAudio;
117
117
  return;
118
118
  }
119
119
  const currentPosition = owner.getValue();
120
- // During count-in, just track that we had one
121
120
  if (isCountingIn) {
122
- hadCountIn = true;
123
121
  return;
124
122
  }
125
123
  // From here on, isRecording is true
@@ -143,10 +141,12 @@ export var RecordAudio;
143
141
  const preRecordingFrames = recordingWorklet.numberOfFrames;
144
142
  const preRecordingSeconds = preRecordingFrames / sampleRate;
145
143
  // If there was count-in, use pre-recording frames as offset; otherwise use outputLatency
146
- const waveformOffset = hadCountIn ? preRecordingSeconds : outputLatency;
144
+ const countedIn = Recording.wasCountingIn();
145
+ console.debug("countedIn", countedIn);
146
+ const waveformOffset = countedIn ? preRecordingSeconds : outputLatency;
147
147
  editing.modify(() => {
148
148
  fileBox = Option.wrap(createFileBox());
149
- const position = quantizeFloor(currentPosition, beats);
149
+ const position = countedIn ? quantizeFloor(currentPosition, beats) : currentPosition;
150
150
  currentTake = Option.wrap(createTakeRegion(position, waveformOffset, null));
151
151
  }, false);
152
152
  currentWaveformOffset = waveformOffset;
@@ -160,7 +160,7 @@ export var RecordAudio;
160
160
  duration.setValue(takeSeconds);
161
161
  loopDuration.setValue(takeSeconds);
162
162
  recordingWorklet.setFillLength(recordingWorklet.numberOfFrames);
163
- fileBox.ifSome(fb => fb.endInSeconds.setValue(totalSeconds));
163
+ fileBox.ifSome(box => box.endInSeconds.setValue(totalSeconds));
164
164
  }
165
165
  else {
166
166
  terminator.terminate();
@@ -39,7 +39,7 @@ export class Recording {
39
39
  this.#isRecording = false;
40
40
  };
41
41
  terminator.ownAll(engine.isRecording.subscribe(stop), engine.isCountingIn.subscribe(stop), Terminable.create(() => Recording.#instance = Option.None));
42
- this.#instance = Option.wrap(new Recording(countIn, engine.position.getValue()));
42
+ this.#instance = Option.wrap(new Recording(countIn && !engine.isPlaying.getValue(), engine.position.getValue()));
43
43
  return terminator;
44
44
  }
45
45
  static #isRecording = false;
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./soundfont";
9
9
  export * from "./sync-log";
10
10
  export * from "./ui";
11
11
  export * from "./ysync";
12
+ export * from "./AudioConsolidation";
12
13
  export * from "./AudioDevices";
13
14
  export * from "./AudioOfflineRenderer";
14
15
  export * from "./OfflineEngineRenderer";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,mBAAmB,UAAU,CAAA;AAC7B,cAAc,QAAQ,CAAA;AACtB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AAEvB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,mBAAmB,UAAU,CAAA;AAC7B,cAAc,QAAQ,CAAA;AACtB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AAEvB,cAAc,sBAAsB,CAAA;AACpC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from "./soundfont";
8
8
  export * from "./sync-log";
9
9
  export * from "./ui";
10
10
  export * from "./ysync";
11
+ export * from "./AudioConsolidation";
11
12
  export * from "./AudioDevices";
12
13
  export * from "./AudioOfflineRenderer";
13
14
  export * from "./OfflineEngineRenderer";