@opendaw/studio-core 0.0.47 → 0.0.49
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/AudioOfflineRenderer.d.ts +2 -2
- package/dist/AudioOfflineRenderer.d.ts.map +1 -1
- package/dist/AudioOfflineRenderer.js +5 -68
- package/dist/Engine.d.ts +3 -3
- package/dist/Engine.d.ts.map +1 -1
- package/dist/EngineFacade.d.ts +2 -2
- package/dist/EngineFacade.d.ts.map +1 -1
- package/dist/EngineFacade.js +3 -3
- package/dist/EngineWorklet.d.ts +1 -1
- package/dist/EngineWorklet.d.ts.map +1 -1
- package/dist/EngineWorklet.js +9 -5
- package/dist/ffmpeg/FFmpegConverter.d.ts +6 -0
- package/dist/ffmpeg/FFmpegConverter.d.ts.map +1 -0
- package/dist/ffmpeg/FFmpegConverter.js +1 -0
- package/dist/ffmpeg/FFmpegWorker.d.ts +18 -0
- package/dist/ffmpeg/FFmpegWorker.d.ts.map +1 -0
- package/dist/ffmpeg/FFmpegWorker.js +115 -0
- package/dist/ffmpeg/flac.d.ts +12 -0
- package/dist/ffmpeg/flac.d.ts.map +1 -0
- package/dist/ffmpeg/flac.js +32 -0
- package/dist/ffmpeg/index.d.ts +3 -0
- package/dist/ffmpeg/index.d.ts.map +1 -0
- package/dist/ffmpeg/index.js +2 -0
- package/dist/ffmpeg/mp3.d.ts +13 -0
- package/dist/ffmpeg/mp3.d.ts.map +1 -0
- package/dist/ffmpeg/mp3.js +37 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/processors.js +14 -14
- package/dist/processors.js.map +4 -4
- package/dist/project/ProjectApi.d.ts +1 -9
- package/dist/project/ProjectApi.d.ts.map +1 -1
- package/dist/project/ProjectApi.js +3 -67
- package/dist/project/ProjectUtils.d.ts +12 -0
- package/dist/project/ProjectUtils.d.ts.map +1 -0
- package/dist/project/ProjectUtils.js +144 -0
- package/dist/project/index.d.ts +1 -0
- package/dist/project/index.d.ts.map +1 -1
- package/dist/project/index.js +1 -0
- package/dist/workers-main.js +2 -2
- package/dist/workers-main.js.map +2 -2
- package/package.json +21 -15
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { int, Option } from "@opendaw/lib-std";
|
|
2
2
|
import { ExportStemsConfiguration } from "@opendaw/studio-adapters";
|
|
3
|
-
import { Project
|
|
3
|
+
import { Project } from "./project";
|
|
4
4
|
export declare namespace AudioOfflineRenderer {
|
|
5
|
-
const start: (source: Project,
|
|
5
|
+
const start: (source: Project, optExportConfiguration: Option<ExportStemsConfiguration>, sampleRate?: int) => Promise<AudioBuffer>;
|
|
6
6
|
}
|
|
7
7
|
//# sourceMappingURL=AudioOfflineRenderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioOfflineRenderer.d.ts","sourceRoot":"","sources":["../src/AudioOfflineRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"AudioOfflineRenderer.d.ts","sourceRoot":"","sources":["../src/AudioOfflineRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,GAAG,EAAE,MAAM,EAAmC,MAAM,kBAAkB,CAAA;AAItG,OAAO,EAAC,wBAAwB,EAAC,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,yBAAiB,oBAAoB,CAAC;IAC3B,MAAM,KAAK,GAAU,QAAQ,OAAO,EACf,wBAAwB,MAAM,CAAC,wBAAwB,CAAC,EACxD,aAAY,GAAY,KAAG,OAAO,CAAC,WAAW,CA4BzE,CAAA;CACJ"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { DefaultObservableValue,
|
|
1
|
+
import { DefaultObservableValue, panic, RuntimeNotifier, TimeSpan } from "@opendaw/lib-std";
|
|
2
2
|
import { PPQN } from "@opendaw/lib-dsp";
|
|
3
|
-
import { AnimationFrame
|
|
4
|
-
import {
|
|
3
|
+
import { AnimationFrame } from "@opendaw/lib-dom";
|
|
4
|
+
import { Wait } from "@opendaw/lib-runtime";
|
|
5
5
|
import { ExportStemsConfiguration } from "@opendaw/studio-adapters";
|
|
6
|
-
import { WavFile } from "./WavFile";
|
|
7
6
|
import { AudioWorklets } from "./AudioWorklets";
|
|
8
7
|
export var AudioOfflineRenderer;
|
|
9
8
|
(function (AudioOfflineRenderer) {
|
|
10
|
-
AudioOfflineRenderer.start = async (source,
|
|
9
|
+
AudioOfflineRenderer.start = async (source, optExportConfiguration, sampleRate = 48_000) => {
|
|
11
10
|
const numStems = ExportStemsConfiguration.countStems(optExportConfiguration);
|
|
12
11
|
if (numStems === 0) {
|
|
13
12
|
return panic("Nothing to export");
|
|
@@ -38,68 +37,6 @@ export var AudioOfflineRenderer;
|
|
|
38
37
|
terminable.terminate();
|
|
39
38
|
dialog.terminate();
|
|
40
39
|
project.terminate();
|
|
41
|
-
|
|
42
|
-
await saveWavFile(buffer, meta);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
await saveZipFile(buffer, meta, Object.values(optExportConfiguration.unwrap()).map(({ fileName }) => fileName));
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
const saveWavFile = async (buffer, meta) => {
|
|
49
|
-
const approved = await RuntimeNotifier.approve({
|
|
50
|
-
headline: "Save Wav-File",
|
|
51
|
-
message: "",
|
|
52
|
-
approveText: "Save"
|
|
53
|
-
});
|
|
54
|
-
if (!approved) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const wavFile = WavFile.encodeFloats(buffer);
|
|
58
|
-
const suggestedName = `${meta.name}.wav`;
|
|
59
|
-
const saveResult = await Promises.tryCatch(Files.save(wavFile, { suggestedName }));
|
|
60
|
-
if (saveResult.status === "rejected" && !Errors.isAbort(saveResult.error)) {
|
|
61
|
-
panic(String(saveResult.error));
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
const saveZipFile = async (buffer, meta, trackNames) => {
|
|
65
|
-
const { default: JSZip } = await import("jszip");
|
|
66
|
-
const dialog = RuntimeNotifier.progress({ headline: "Creating Zip File..." });
|
|
67
|
-
const numStems = buffer.numberOfChannels >> 1;
|
|
68
|
-
const zip = new JSZip();
|
|
69
|
-
for (let stemIndex = 0; stemIndex < numStems; stemIndex++) {
|
|
70
|
-
const l = buffer.getChannelData(stemIndex * 2);
|
|
71
|
-
const r = buffer.getChannelData(stemIndex * 2 + 1);
|
|
72
|
-
const file = WavFile.encodeFloats({
|
|
73
|
-
channels: [l, r],
|
|
74
|
-
sampleRate: buffer.sampleRate,
|
|
75
|
-
numFrames: buffer.length
|
|
76
|
-
});
|
|
77
|
-
zip.file(`${trackNames[stemIndex]}.wav`, file, { binary: true });
|
|
78
|
-
}
|
|
79
|
-
const { status, value: arrayBuffer, error } = await Promises.tryCatch(zip.generateAsync({
|
|
80
|
-
type: "arraybuffer",
|
|
81
|
-
compression: "DEFLATE",
|
|
82
|
-
compressionOptions: { level: 6 }
|
|
83
|
-
}));
|
|
84
|
-
dialog.terminate();
|
|
85
|
-
if (status === "rejected") {
|
|
86
|
-
await RuntimeNotifier.info({
|
|
87
|
-
headline: "Error",
|
|
88
|
-
message: `Could not create zip file: ${String(error)}`
|
|
89
|
-
});
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const approved = await RuntimeNotifier.approve({
|
|
93
|
-
headline: "Save Zip",
|
|
94
|
-
message: `Size: ${arrayBuffer.byteLength >> 20}M`,
|
|
95
|
-
approveText: "Save"
|
|
96
|
-
});
|
|
97
|
-
if (!approved) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const saveResult = await Promises.tryCatch(Files.save(arrayBuffer, { suggestedName: `${meta.name}.zip` }));
|
|
101
|
-
if (saveResult.status === "rejected" && !Errors.isAbort(saveResult.error)) {
|
|
102
|
-
panic(String(saveResult.error));
|
|
103
|
-
}
|
|
40
|
+
return buffer;
|
|
104
41
|
};
|
|
105
42
|
})(AudioOfflineRenderer || (AudioOfflineRenderer = {}));
|
package/dist/Engine.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ppqn } from "@opendaw/lib-dsp";
|
|
2
1
|
import { int, MutableObservableValue, Nullable, ObservableValue, Observer, Subscription, Terminable, UUID } from "@opendaw/lib-std";
|
|
2
|
+
import { ppqn } from "@opendaw/lib-dsp";
|
|
3
3
|
import { ClipNotification, NoteSignal } from "@opendaw/studio-adapters";
|
|
4
|
-
import { Project } from "./project
|
|
4
|
+
import { Project } from "./project";
|
|
5
5
|
export interface Engine extends Terminable {
|
|
6
6
|
play(): void;
|
|
7
7
|
stop(): void;
|
|
@@ -25,7 +25,7 @@ export interface Engine extends Terminable {
|
|
|
25
25
|
get metronomeEnabled(): ObservableValue<boolean>;
|
|
26
26
|
get playbackTimestamp(): ObservableValue<ppqn>;
|
|
27
27
|
get playbackTimestampEnabled(): MutableObservableValue<boolean>;
|
|
28
|
-
get
|
|
28
|
+
get countInBarsTotal(): ObservableValue<int>;
|
|
29
29
|
get countInBeatsRemaining(): ObservableValue<number>;
|
|
30
30
|
get markerState(): ObservableValue<Nullable<[UUID.Bytes, int]>>;
|
|
31
31
|
get project(): Project;
|
package/dist/Engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../src/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../src/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,GAAG,EACH,sBAAsB,EACtB,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,0BAA0B,CAAA;AACrE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAEjC,MAAM,WAAW,MAAO,SAAQ,UAAU;IACtC,IAAI,IAAI,IAAI,CAAA;IACZ,IAAI,IAAI,IAAI,CAAA;IACZ,WAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAA;IACjC,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC7C,aAAa,IAAI,IAAI,CAAA;IACrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,IAAI,IAAI,IAAI,CAAA;IACZ,KAAK,IAAI,IAAI,CAAA;IACb,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IACpC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,CAAA;IAC5D,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACxC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;IAC1D,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;IAC3D,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,YAAY,CAAA;IAE7E,IAAI,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,CAAA;IACrC,IAAI,SAAS,IAAI,eAAe,CAAC,OAAO,CAAC,CAAA;IACzC,IAAI,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,YAAY,IAAI,eAAe,CAAC,OAAO,CAAC,CAAA;IAC5C,IAAI,gBAAgB,IAAI,eAAe,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,iBAAiB,IAAI,eAAe,CAAC,IAAI,CAAC,CAAA;IAC9C,IAAI,wBAAwB,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAC/D,IAAI,gBAAgB,IAAI,eAAe,CAAC,GAAG,CAAC,CAAA;IAC5C,IAAI,qBAAqB,IAAI,eAAe,CAAC,MAAM,CAAC,CAAA;IACpD,IAAI,WAAW,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAC/D,IAAI,OAAO,IAAI,OAAO,CAAA;CACzB"}
|
package/dist/EngineFacade.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { ppqn } from "@opendaw/lib-dsp";
|
|
|
3
3
|
import { ClipNotification, NoteSignal } from "@opendaw/studio-adapters";
|
|
4
4
|
import { Engine } from "./Engine";
|
|
5
5
|
import { EngineWorklet } from "./EngineWorklet";
|
|
6
|
-
import { Project } from "./project
|
|
6
|
+
import { Project } from "./project";
|
|
7
7
|
export declare class EngineFacade implements Engine {
|
|
8
8
|
#private;
|
|
9
9
|
constructor();
|
|
@@ -22,7 +22,7 @@ export declare class EngineFacade implements Engine {
|
|
|
22
22
|
get metronomeEnabled(): MutableObservableValue<boolean>;
|
|
23
23
|
get playbackTimestamp(): ObservableValue<ppqn>;
|
|
24
24
|
get playbackTimestampEnabled(): MutableObservableValue<boolean>;
|
|
25
|
-
get
|
|
25
|
+
get countInBarsTotal(): MutableObservableValue<int>;
|
|
26
26
|
get countInBeatsRemaining(): ObservableValue<int>;
|
|
27
27
|
get markerState(): DefaultObservableValue<Nullable<[UUID.Bytes, int]>>;
|
|
28
28
|
get project(): Project;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EngineFacade.d.ts","sourceRoot":"","sources":["../src/EngineFacade.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,sBAAsB,EACtB,GAAG,EACH,sBAAsB,EACtB,QAAQ,EACR,eAAe,EACf,QAAQ,EAER,YAAY,EAEZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,0BAA0B,CAAA;AACrE,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,OAAO,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"EngineFacade.d.ts","sourceRoot":"","sources":["../src/EngineFacade.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,sBAAsB,EACtB,GAAG,EACH,sBAAsB,EACtB,QAAQ,EACR,eAAe,EACf,QAAQ,EAER,YAAY,EAEZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,0BAA0B,CAAA;AACrE,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAEjC,qBAAa,YAAa,YAAW,MAAM;;;IAmBvC,UAAU,CAAC,OAAO,EAAE,aAAa;IAoBjC,aAAa,IAAI,IAAI;IAErB,cAAc,IAAI,IAAI;IAMtB,IAAI,IAAI,IAAI;IACZ,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,IAAI;IAClC,WAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI;IACjC,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAC7C,aAAa,IAAI,IAAI;IAErB,IAAI,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,CAAwB;IAC7D,IAAI,SAAS,IAAI,eAAe,CAAC,OAAO,CAAC,CAAyB;IAClE,IAAI,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,CAA2B;IACtE,IAAI,YAAY,IAAI,eAAe,CAAC,OAAO,CAAC,CAA4B;IACxE,IAAI,gBAAgB,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAgC;IACvF,IAAI,iBAAiB,IAAI,eAAe,CAAC,IAAI,CAAC,CAAiC;IAC/E,IAAI,wBAAwB,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAwC;IACvG,IAAI,gBAAgB,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAgC;IACnF,IAAI,qBAAqB,IAAI,eAAe,CAAC,GAAG,CAAC,CAAqC;IACtF,IAAI,WAAW,IAAI,sBAAsB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAA2B;IACjG,IAAI,OAAO,IAAI,OAAO,CAAmE;IAEzF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IAGxC,KAAK,IAAI,IAAI;IACb,UAAU,IAAI,MAAM;IACpB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,YAAY;IAG7E,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY;IAG5D,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI;IAGxC,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAGpC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI;IAG1D,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI;IAI3D,SAAS,IAAI,IAAI;CAIpB"}
|
package/dist/EngineFacade.js
CHANGED
|
@@ -4,7 +4,7 @@ export class EngineFacade {
|
|
|
4
4
|
#lifecycle = this.#terminator.own(new Terminator());
|
|
5
5
|
#playbackTimestamp = new DefaultObservableValue(0.0);
|
|
6
6
|
#playbackTimestampEnabled = new DefaultObservableValue(true);
|
|
7
|
-
#
|
|
7
|
+
#countInBarsTotal = new DefaultObservableValue(1);
|
|
8
8
|
#countInBeatsRemaining = new DefaultObservableValue(0);
|
|
9
9
|
#position = new DefaultObservableValue(0.0);
|
|
10
10
|
#isPlaying = new DefaultObservableValue(false);
|
|
@@ -17,7 +17,7 @@ export class EngineFacade {
|
|
|
17
17
|
setWorklet(worklet) {
|
|
18
18
|
this.#worklet = Option.wrap(worklet);
|
|
19
19
|
this.#lifecycle.terminate();
|
|
20
|
-
this.#lifecycle.ownAll(worklet.playbackTimestamp.catchupAndSubscribe(owner => this.#playbackTimestamp.setValue(owner.getValue())), worklet.playbackTimestampEnabled.catchupAndSubscribe(owner => this.#playbackTimestampEnabled.setValue(owner.getValue())), worklet.
|
|
20
|
+
this.#lifecycle.ownAll(worklet.playbackTimestamp.catchupAndSubscribe(owner => this.#playbackTimestamp.setValue(owner.getValue())), worklet.playbackTimestampEnabled.catchupAndSubscribe(owner => this.#playbackTimestampEnabled.setValue(owner.getValue())), worklet.countInBarsTotal.catchupAndSubscribe(owner => this.#countInBarsTotal.setValue(owner.getValue())), worklet.countInBeatsRemaining.catchupAndSubscribe(owner => this.#countInBeatsRemaining.setValue(owner.getValue())), worklet.position.catchupAndSubscribe(owner => this.#position.setValue(owner.getValue())), worklet.isPlaying.catchupAndSubscribe(owner => this.#isPlaying.setValue(owner.getValue())), worklet.isRecording.catchupAndSubscribe(owner => this.#isRecording.setValue(owner.getValue())), worklet.isCountingIn.catchupAndSubscribe(owner => this.#isCountingIn.setValue(owner.getValue())), worklet.metronomeEnabled.catchupAndSubscribe(owner => this.#metronomeEnabled.setValue(owner.getValue())), worklet.markerState.catchupAndSubscribe(owner => this.#markerState.setValue(owner.getValue())), this.#metronomeEnabled.catchupAndSubscribe(owner => worklet.metronomeEnabled.setValue(owner.getValue())), this.#playbackTimestampEnabled.catchupAndSubscribe(owner => worklet.playbackTimestampEnabled.setValue(owner.getValue())), this.#countInBarsTotal.catchupAndSubscribe(owner => worklet.countInBarsTotal.setValue(owner.getValue())));
|
|
21
21
|
}
|
|
22
22
|
assertWorklet() { this.#worklet.unwrap("No worklet available"); }
|
|
23
23
|
releaseWorklet() {
|
|
@@ -37,7 +37,7 @@ export class EngineFacade {
|
|
|
37
37
|
get metronomeEnabled() { return this.#metronomeEnabled; }
|
|
38
38
|
get playbackTimestamp() { return this.#playbackTimestamp; }
|
|
39
39
|
get playbackTimestampEnabled() { return this.#playbackTimestampEnabled; }
|
|
40
|
-
get
|
|
40
|
+
get countInBarsTotal() { return this.#countInBarsTotal; }
|
|
41
41
|
get countInBeatsRemaining() { return this.#countInBeatsRemaining; }
|
|
42
42
|
get markerState() { return this.#markerState; }
|
|
43
43
|
get project() { return this.#worklet.unwrap("No worklet to get project").project; }
|
package/dist/EngineWorklet.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare class EngineWorklet extends AudioWorkletNode implements Engine {
|
|
|
17
17
|
get isPlaying(): ObservableValue<boolean>;
|
|
18
18
|
get isRecording(): ObservableValue<boolean>;
|
|
19
19
|
get isCountingIn(): ObservableValue<boolean>;
|
|
20
|
-
get
|
|
20
|
+
get countInBarsTotal(): MutableObservableValue<int>;
|
|
21
21
|
get countInBeatsRemaining(): ObservableValue<number>;
|
|
22
22
|
get position(): ObservableValue<ppqn>;
|
|
23
23
|
get playbackTimestamp(): MutableObservableValue<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EngineWorklet.d.ts","sourceRoot":"","sources":["../src/EngineWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,GAAG,EACH,sBAAsB,EAEtB,QAAQ,EACR,eAAe,EACf,QAAQ,EAER,YAAY,EAGZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAIrC,OAAO,EAEH,gBAAgB,EAOhB,wBAAwB,EACxB,UAAU,EACV,gBAAgB,EACnB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,qBAAa,aAAc,SAAQ,gBAAiB,YAAW,MAAM;;IACjE,MAAM,CAAC,EAAE,EAAE,GAAG,CAAQ;IAEtB,QAAQ,CAAC,EAAE,SAAqB;gBAqBpB,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,OAAO,EAChB,mBAAmB,CAAC,EAAE,wBAAwB,EAC9C,OAAO,CAAC,EAAE,gBAAgB;
|
|
1
|
+
{"version":3,"file":"EngineWorklet.d.ts","sourceRoot":"","sources":["../src/EngineWorklet.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,GAAG,EACH,sBAAsB,EAEtB,QAAQ,EACR,eAAe,EACf,QAAQ,EAER,YAAY,EAGZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAIrC,OAAO,EAEH,gBAAgB,EAOhB,wBAAwB,EACxB,UAAU,EACV,gBAAgB,EACnB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAGjC,qBAAa,aAAc,SAAQ,gBAAiB,YAAW,MAAM;;IACjE,MAAM,CAAC,EAAE,EAAE,GAAG,CAAQ;IAEtB,QAAQ,CAAC,EAAE,SAAqB;gBAqBpB,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,OAAO,EAChB,mBAAmB,CAAC,EAAE,wBAAwB,EAC9C,OAAO,CAAC,EAAE,gBAAgB;IAoItC,IAAI,IAAI,IAAI;IACZ,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,IAAI;IAClC,WAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI;IACjC,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAC7C,aAAa,IAAI,IAAI;IACrB,KAAK,IAAI,IAAI;IAEb,IAAI,SAAS,IAAI,eAAe,CAAC,OAAO,CAAC,CAAyB;IAClE,IAAI,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,CAA2B;IACtE,IAAI,YAAY,IAAI,eAAe,CAAC,OAAO,CAAC,CAA4B;IACxE,IAAI,gBAAgB,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAgC;IACnF,IAAI,qBAAqB,IAAI,eAAe,CAAC,MAAM,CAAC,CAAqC;IACzF,IAAI,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,CAAwB;IAC7D,IAAI,iBAAiB,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAiC;IACxF,IAAI,wBAAwB,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAwC;IACvG,IAAI,gBAAgB,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAgC;IACvF,IAAI,WAAW,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAA2B;IAC1F,IAAI,OAAO,IAAI,OAAO,CAAuB;IAE7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IACxC,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IACpC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY;IAC5D,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI;IACxC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI;IAI1D,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI;IAG3D,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,YAAY;IAQ7E,SAAS,IAAI,IAAI;CAIpB"}
|
package/dist/EngineWorklet.js
CHANGED
|
@@ -14,7 +14,7 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
14
14
|
#isPlaying = new DefaultObservableValue(false);
|
|
15
15
|
#isRecording = new DefaultObservableValue(false);
|
|
16
16
|
#isCountingIn = new DefaultObservableValue(false);
|
|
17
|
-
#
|
|
17
|
+
#countInBarsTotal = new DefaultObservableValue(1);
|
|
18
18
|
#countInBeatsRemaining = new DefaultObservableValue(0);
|
|
19
19
|
#metronomeEnabled = new DefaultObservableValue(false);
|
|
20
20
|
#markerState = new DefaultObservableValue(null);
|
|
@@ -29,7 +29,6 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
29
29
|
this.#isPlaying.setValue(state.isPlaying);
|
|
30
30
|
this.#isRecording.setValue(state.isRecording);
|
|
31
31
|
this.#isCountingIn.setValue(state.isCountingIn);
|
|
32
|
-
this.#countInBeatsTotal.setValue(state.countInBeatsTotal);
|
|
33
32
|
this.#countInBeatsRemaining.setValue(state.countInBeatsRemaining);
|
|
34
33
|
this.#playbackTimestamp.setValue(state.playbackTimestamp);
|
|
35
34
|
this.#position.setValue(state.position); // This must be the last to handle the state values before
|
|
@@ -56,7 +55,9 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
56
55
|
play() { dispatcher.dispatchAndForget(this.play); }
|
|
57
56
|
stop(reset) { dispatcher.dispatchAndForget(this.stop, reset); }
|
|
58
57
|
setPosition(position) { dispatcher.dispatchAndForget(this.setPosition, position); }
|
|
59
|
-
prepareRecordingState(countIn) {
|
|
58
|
+
prepareRecordingState(countIn) {
|
|
59
|
+
dispatcher.dispatchAndForget(this.prepareRecordingState, countIn);
|
|
60
|
+
}
|
|
60
61
|
stopRecording() { dispatcher.dispatchAndForget(this.stopRecording); }
|
|
61
62
|
setMetronomeEnabled(enabled) {
|
|
62
63
|
dispatcher.dispatchAndForget(this.setMetronomeEnabled, enabled);
|
|
@@ -64,6 +65,9 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
64
65
|
setPlaybackTimestampEnabled(enabled) {
|
|
65
66
|
dispatcher.dispatchAndForget(this.setPlaybackTimestampEnabled, enabled);
|
|
66
67
|
}
|
|
68
|
+
setCountInBarsTotal(value) {
|
|
69
|
+
dispatcher.dispatchAndForget(this.setCountInBarsTotal, value);
|
|
70
|
+
}
|
|
67
71
|
queryLoadingComplete() {
|
|
68
72
|
return dispatcher.dispatchAndReturn(this.queryLoadingComplete);
|
|
69
73
|
}
|
|
@@ -135,7 +139,7 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
135
139
|
this.#project.receivedMIDIFromEngine(target, data, relativeTimeInMs + delay);
|
|
136
140
|
}
|
|
137
141
|
});
|
|
138
|
-
this.#terminator.ownAll(AnimationFrame.add(() => reader.tryRead()), project.liveStreamReceiver.connect(messenger.channel("engine-live-data")), new SyncSource(project.boxGraph, messenger.channel("engine-sync"), false), this.#metronomeEnabled.catchupAndSubscribe(owner => this.#commands.setMetronomeEnabled(owner.getValue())), this.#playbackTimestampEnabled.catchupAndSubscribe(owner => this.#commands.setPlaybackTimestampEnabled(owner.getValue())));
|
|
142
|
+
this.#terminator.ownAll(AnimationFrame.add(() => reader.tryRead()), project.liveStreamReceiver.connect(messenger.channel("engine-live-data")), new SyncSource(project.boxGraph, messenger.channel("engine-sync"), false), this.#metronomeEnabled.catchupAndSubscribe(owner => this.#commands.setMetronomeEnabled(owner.getValue())), this.#playbackTimestampEnabled.catchupAndSubscribe(owner => this.#commands.setPlaybackTimestampEnabled(owner.getValue())), this.#countInBarsTotal.catchupAndSubscribe(owner => this.#commands.setCountInBarsTotal(owner.getValue())));
|
|
139
143
|
}
|
|
140
144
|
play() { this.#commands.play(); }
|
|
141
145
|
stop(reset = false) { this.#commands.stop(reset); }
|
|
@@ -146,7 +150,7 @@ export class EngineWorklet extends AudioWorkletNode {
|
|
|
146
150
|
get isPlaying() { return this.#isPlaying; }
|
|
147
151
|
get isRecording() { return this.#isRecording; }
|
|
148
152
|
get isCountingIn() { return this.#isCountingIn; }
|
|
149
|
-
get
|
|
153
|
+
get countInBarsTotal() { return this.#countInBarsTotal; }
|
|
150
154
|
get countInBeatsRemaining() { return this.#countInBeatsRemaining; }
|
|
151
155
|
get position() { return this.#position; }
|
|
152
156
|
get playbackTimestamp() { return this.#playbackTimestamp; }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AcceptedSource } from "./FFmpegWorker";
|
|
2
|
+
import { Progress } from "@opendaw/lib-std";
|
|
3
|
+
export interface FFmpegConverter<OPTIONS> {
|
|
4
|
+
convert(source: AcceptedSource, progress: Progress.Handler, options?: OPTIONS): Promise<ArrayBuffer>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=FFmpegConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FFmpegConverter.d.ts","sourceRoot":"","sources":["../../src/ffmpeg/FFmpegConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAEzC,MAAM,WAAW,eAAe,CAAC,OAAO;IACpC,OAAO,CAAC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,QAAQ,CAAC,OAAO,EAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CACnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Notifier, Progress, unitValue } from "@opendaw/lib-std";
|
|
2
|
+
import type { FFmpeg } from "@ffmpeg/ffmpeg";
|
|
3
|
+
import { Mp3Converter } from "./mp3";
|
|
4
|
+
import { FlacConverter } from "./flac";
|
|
5
|
+
export type AcceptedSource = File | Blob;
|
|
6
|
+
export declare class FFmpegWorker {
|
|
7
|
+
#private;
|
|
8
|
+
static load(progress?: Progress.Handler): Promise<FFmpegWorker>;
|
|
9
|
+
constructor(ffmpeg: FFmpeg);
|
|
10
|
+
get ffmpeg(): FFmpeg;
|
|
11
|
+
get loaded(): boolean;
|
|
12
|
+
get progressNotifier(): Notifier<unitValue>;
|
|
13
|
+
mp3Converter(): Mp3Converter;
|
|
14
|
+
flacConverter(): FlacConverter;
|
|
15
|
+
fetchFileData(source: string): Promise<Uint8Array>;
|
|
16
|
+
cleanupFiles(files: string[]): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=FFmpegWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FFmpegWorker.d.ts","sourceRoot":"","sources":["../../src/ffmpeg/FFmpegWorker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,QAAQ,EAAY,QAAQ,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAA;AACzF,OAAO,KAAK,EAAC,MAAM,EAAW,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAC,YAAY,EAAC,MAAM,OAAO,CAAA;AAClC,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAA;AAEpC,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAA;AAExC,qBAAa,YAAY;;WACR,IAAI,CAAC,QAAQ,GAAE,QAAQ,CAAC,OAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;gBAOzE,MAAM,EAAE,MAAM;IAO1B,IAAI,MAAM,IAAI,MAAM,CAAsB;IAC1C,IAAI,MAAM,IAAI,OAAO,CAA6B;IAClD,IAAI,gBAAgB,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAgC;IAG3E,YAAY,IAAI,YAAY;IAG5B,aAAa,IAAI,aAAa;IAExB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAQlD,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { EmptyExec, Lazy, Notifier, Progress } from "@opendaw/lib-std";
|
|
11
|
+
import { Mp3Converter } from "./mp3";
|
|
12
|
+
import { FlacConverter } from "./flac";
|
|
13
|
+
export class FFmpegWorker {
|
|
14
|
+
static async load(progress = Progress.Empty) {
|
|
15
|
+
return Loader.loadOrAttach(progress);
|
|
16
|
+
}
|
|
17
|
+
#ffmpeg;
|
|
18
|
+
#progressNotifier;
|
|
19
|
+
constructor(ffmpeg) {
|
|
20
|
+
this.#ffmpeg = ffmpeg;
|
|
21
|
+
this.#progressNotifier = new Notifier();
|
|
22
|
+
this.#ffmpeg.on("log", ({ message }) => console.debug("[FFmpeg]", message));
|
|
23
|
+
this.#ffmpeg.on("progress", event => this.#progressNotifier.notify(event.progress));
|
|
24
|
+
}
|
|
25
|
+
get ffmpeg() { return this.#ffmpeg; }
|
|
26
|
+
get loaded() { return this.#ffmpeg.loaded; }
|
|
27
|
+
get progressNotifier() { return this.#progressNotifier; }
|
|
28
|
+
mp3Converter() { return new Mp3Converter(this); }
|
|
29
|
+
flacConverter() { return new FlacConverter(this); }
|
|
30
|
+
async fetchFileData(source) {
|
|
31
|
+
const response = await fetch(source);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Failed to fetch file: ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
36
|
+
}
|
|
37
|
+
async cleanupFiles(files) {
|
|
38
|
+
return Promise.all(files.map(file => this.#ffmpeg.deleteFile(file).catch())).then(EmptyExec);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
__decorate([
|
|
42
|
+
Lazy,
|
|
43
|
+
__metadata("design:type", Function),
|
|
44
|
+
__metadata("design:paramtypes", []),
|
|
45
|
+
__metadata("design:returntype", Mp3Converter)
|
|
46
|
+
], FFmpegWorker.prototype, "mp3Converter", null);
|
|
47
|
+
__decorate([
|
|
48
|
+
Lazy,
|
|
49
|
+
__metadata("design:type", Function),
|
|
50
|
+
__metadata("design:paramtypes", []),
|
|
51
|
+
__metadata("design:returntype", FlacConverter)
|
|
52
|
+
], FFmpegWorker.prototype, "flacConverter", null);
|
|
53
|
+
class Loader {
|
|
54
|
+
static async loadOrAttach(progress) {
|
|
55
|
+
if (this.#loader === null) {
|
|
56
|
+
this.#loader = new Loader();
|
|
57
|
+
}
|
|
58
|
+
const subscription = this.#loader.#progressNotifier.subscribe(progress);
|
|
59
|
+
return this.#loader.load().finally(() => subscription.terminate());
|
|
60
|
+
}
|
|
61
|
+
static #loader = null;
|
|
62
|
+
#progressNotifier = new Notifier();
|
|
63
|
+
async load() {
|
|
64
|
+
const { FFmpeg } = await import("@ffmpeg/ffmpeg");
|
|
65
|
+
const ffmpeg = new FFmpeg();
|
|
66
|
+
ffmpeg.on("log", ({ type, message }) => {
|
|
67
|
+
console.debug(`[FFmpeg ${type}]`, message);
|
|
68
|
+
});
|
|
69
|
+
ffmpeg.on("progress", event => {
|
|
70
|
+
this.#progressNotifier.notify(event.progress);
|
|
71
|
+
});
|
|
72
|
+
const baseURL = "https://package.opendaw.studio"; // mirror of https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm
|
|
73
|
+
console.debug("[FFmpeg] Downloading core files...");
|
|
74
|
+
const downloadWithProgress = async (url) => {
|
|
75
|
+
const response = await fetch(url);
|
|
76
|
+
if (!response.ok)
|
|
77
|
+
throw new Error(`Failed to fetch ${url}`);
|
|
78
|
+
const contentLength = response.headers.get("content-length");
|
|
79
|
+
const total = contentLength ? parseInt(contentLength, 10) : 0;
|
|
80
|
+
if (!response.body || total === 0) {
|
|
81
|
+
return response.arrayBuffer();
|
|
82
|
+
}
|
|
83
|
+
const reader = response.body.getReader();
|
|
84
|
+
const chunks = [];
|
|
85
|
+
let received = 0;
|
|
86
|
+
while (true) {
|
|
87
|
+
const { done, value } = await reader.read();
|
|
88
|
+
if (done)
|
|
89
|
+
break;
|
|
90
|
+
chunks.push(value);
|
|
91
|
+
received += value.length;
|
|
92
|
+
this.#progressNotifier.notify(received / total);
|
|
93
|
+
}
|
|
94
|
+
const result = new Uint8Array(received);
|
|
95
|
+
let position = 0;
|
|
96
|
+
for (const chunk of chunks) {
|
|
97
|
+
result.set(chunk, position);
|
|
98
|
+
position += chunk.length;
|
|
99
|
+
}
|
|
100
|
+
console.debug("position", position);
|
|
101
|
+
return result.buffer;
|
|
102
|
+
};
|
|
103
|
+
const coreData = await downloadWithProgress(`${baseURL}/ffmpeg-core.js`);
|
|
104
|
+
const wasmData = await downloadWithProgress(`${baseURL}/ffmpeg-core.wasm`);
|
|
105
|
+
console.debug("[FFmpeg] Creating blob URLs...");
|
|
106
|
+
const coreBlob = new Blob([coreData], { type: "text/javascript" });
|
|
107
|
+
const wasmBlob = new Blob([wasmData], { type: "application/wasm" });
|
|
108
|
+
const coreURL = URL.createObjectURL(coreBlob);
|
|
109
|
+
const wasmURL = URL.createObjectURL(wasmBlob);
|
|
110
|
+
console.debug("[FFmpeg] Initializing...");
|
|
111
|
+
await ffmpeg.load({ coreURL, wasmURL });
|
|
112
|
+
console.debug("[FFmpeg] Ready");
|
|
113
|
+
return new FFmpegWorker(ffmpeg);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FFmpegConverter } from "./FFmpegConverter";
|
|
2
|
+
import { int, Progress } from "@opendaw/lib-std";
|
|
3
|
+
import { AcceptedSource, FFmpegWorker } from "./FFmpegWorker";
|
|
4
|
+
export type FlacOptions = {
|
|
5
|
+
compression: int;
|
|
6
|
+
};
|
|
7
|
+
export declare class FlacConverter implements FFmpegConverter<FlacOptions> {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(worker: FFmpegWorker);
|
|
10
|
+
convert(source: AcceptedSource, progress: Progress.Handler, options?: FlacOptions): Promise<ArrayBuffer>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=flac.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flac.d.ts","sourceRoot":"","sources":["../../src/ffmpeg/flac.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,GAAG,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAG3D,MAAM,MAAM,WAAW,GAAG;IAAE,WAAW,EAAE,GAAG,CAAA;CAAE,CAAA;AAE9C,qBAAa,aAAc,YAAW,eAAe,CAAC,WAAW,CAAC;;gBAGlD,MAAM,EAAE,YAAY;IAC1B,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CA0BjH"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class FlacConverter {
|
|
2
|
+
#worker;
|
|
3
|
+
constructor(worker) { this.#worker = worker; }
|
|
4
|
+
async convert(source, progress, options) {
|
|
5
|
+
const subscription = this.#worker.progressNotifier.subscribe(progress);
|
|
6
|
+
try {
|
|
7
|
+
let inputData;
|
|
8
|
+
if (source instanceof File || source instanceof Blob) {
|
|
9
|
+
inputData = new Uint8Array(await source.arrayBuffer());
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
inputData = await this.#worker.fetchFileData(source);
|
|
13
|
+
}
|
|
14
|
+
await this.#worker.ffmpeg.writeFile("input.wav", inputData);
|
|
15
|
+
await this.#worker.ffmpeg.exec([
|
|
16
|
+
"-y",
|
|
17
|
+
"-i", "input.wav",
|
|
18
|
+
"-compression_level", String(options?.compression ?? 8),
|
|
19
|
+
"output.flac"
|
|
20
|
+
]);
|
|
21
|
+
const outputData = await this.#worker.ffmpeg.readFile("output.flac");
|
|
22
|
+
if (typeof outputData === "string") {
|
|
23
|
+
return Promise.reject(outputData);
|
|
24
|
+
}
|
|
25
|
+
return new Blob([new Uint8Array(outputData)], { type: "audio/flac" }).arrayBuffer();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
subscription.terminate();
|
|
29
|
+
await this.#worker.cleanupFiles(["input.wav", "output.flac"]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ffmpeg/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Progress } from "@opendaw/lib-std";
|
|
2
|
+
import { AcceptedSource, FFmpegWorker } from "./FFmpegWorker";
|
|
3
|
+
import { FFmpegConverter } from "./FFmpegConverter";
|
|
4
|
+
export type Mp3Options = {
|
|
5
|
+
bitrate?: string;
|
|
6
|
+
quality?: number;
|
|
7
|
+
};
|
|
8
|
+
export declare class Mp3Converter implements FFmpegConverter<Mp3Options> {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(worker: FFmpegWorker);
|
|
11
|
+
convert(source: AcceptedSource, progress: Progress.Handler, options?: Mp3Options): Promise<ArrayBuffer>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=mp3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mp3.d.ts","sourceRoot":"","sources":["../../src/ffmpeg/mp3.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAEjD,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,qBAAa,YAAa,YAAW,eAAe,CAAC,UAAU,CAAC;;gBAGhD,MAAM,EAAE,YAAY;IAE1B,OAAO,CAAC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,QAAQ,CAAC,OAAO,EAC1B,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,WAAW,CAAC;CA8BhE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class Mp3Converter {
|
|
2
|
+
#worker;
|
|
3
|
+
constructor(worker) { this.#worker = worker; }
|
|
4
|
+
async convert(source, progress, options = {}) {
|
|
5
|
+
const subscription = this.#worker.progressNotifier.subscribe(progress);
|
|
6
|
+
try {
|
|
7
|
+
let inputData;
|
|
8
|
+
if (source instanceof File || source instanceof Blob) {
|
|
9
|
+
inputData = new Uint8Array(await source.arrayBuffer());
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
inputData = await this.#worker.fetchFileData(source);
|
|
13
|
+
}
|
|
14
|
+
await this.#worker.ffmpeg.writeFile("input.wav", inputData);
|
|
15
|
+
const args = ["-y", "-i", "input.wav"];
|
|
16
|
+
if (options.quality !== undefined) {
|
|
17
|
+
// VBR mode: quality 0 (best) to 9 (worst), 2 is recommended
|
|
18
|
+
args.push("-q:a", (options.quality ?? 2).toString());
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
// CBR mode: default to 320k (high quality)
|
|
22
|
+
args.push("-b:a", options.bitrate ?? "320k");
|
|
23
|
+
}
|
|
24
|
+
args.push("output.mp3");
|
|
25
|
+
await this.#worker.ffmpeg.exec(args);
|
|
26
|
+
const outputData = await this.#worker.ffmpeg.readFile("output.mp3");
|
|
27
|
+
if (typeof outputData === "string") {
|
|
28
|
+
return Promise.reject(outputData);
|
|
29
|
+
}
|
|
30
|
+
return new Blob([new Uint8Array(outputData)], { type: "audio/mpeg" }).arrayBuffer();
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
subscription.terminate();
|
|
34
|
+
await this.#worker.cleanupFiles(["input.wav", "output.mp3"]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -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,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;AACvB,cAAc,SAAS,CAAA;AAEvB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,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,yBAAyB,CAAA;AACvC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,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,cAAc,UAAU,CAAA;AACxB,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;AACvB,cAAc,SAAS,CAAA;AAEvB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,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,yBAAyB,CAAA;AACvC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA"}
|