@opendaw/lib-fusion 0.0.6

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 (47) hide show
  1. package/README.md +1 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +17 -0
  5. package/dist/live-stream/Flags.d.ts +6 -0
  6. package/dist/live-stream/Flags.d.ts.map +1 -0
  7. package/dist/live-stream/Flags.js +6 -0
  8. package/dist/live-stream/LiveStreamBroadcaster.d.ts +16 -0
  9. package/dist/live-stream/LiveStreamBroadcaster.d.ts.map +1 -0
  10. package/dist/live-stream/LiveStreamBroadcaster.js +180 -0
  11. package/dist/live-stream/LiveStreamReceiver.d.ts +16 -0
  12. package/dist/live-stream/LiveStreamReceiver.d.ts.map +1 -0
  13. package/dist/live-stream/LiveStreamReceiver.js +191 -0
  14. package/dist/live-stream/Lock.d.ts +5 -0
  15. package/dist/live-stream/Lock.d.ts.map +1 -0
  16. package/dist/live-stream/Lock.js +5 -0
  17. package/dist/live-stream/PackageType.d.ts +8 -0
  18. package/dist/live-stream/PackageType.d.ts.map +1 -0
  19. package/dist/live-stream/PackageType.js +8 -0
  20. package/dist/live-stream/Protocol.d.ts +6 -0
  21. package/dist/live-stream/Protocol.d.ts.map +1 -0
  22. package/dist/live-stream/Protocol.js +1 -0
  23. package/dist/live-stream/Subscribers.d.ts +11 -0
  24. package/dist/live-stream/Subscribers.d.ts.map +1 -0
  25. package/dist/live-stream/Subscribers.js +28 -0
  26. package/dist/opfs/OpfsProtocol.d.ts +12 -0
  27. package/dist/opfs/OpfsProtocol.d.ts.map +1 -0
  28. package/dist/opfs/OpfsProtocol.js +1 -0
  29. package/dist/opfs/OpfsWorker.d.ts +14 -0
  30. package/dist/opfs/OpfsWorker.d.ts.map +1 -0
  31. package/dist/opfs/OpfsWorker.js +70 -0
  32. package/dist/peaks/PeakProtocol.d.ts +5 -0
  33. package/dist/peaks/PeakProtocol.d.ts.map +1 -0
  34. package/dist/peaks/PeakProtocol.js +1 -0
  35. package/dist/peaks/PeakWorker.d.ts +8 -0
  36. package/dist/peaks/PeakWorker.d.ts.map +1 -0
  37. package/dist/peaks/PeakWorker.js +78 -0
  38. package/dist/peaks/Peaks.d.ts +26 -0
  39. package/dist/peaks/Peaks.d.ts.map +1 -0
  40. package/dist/peaks/Peaks.js +107 -0
  41. package/dist/peaks/PeaksPainter.d.ts +16 -0
  42. package/dist/peaks/PeaksPainter.d.ts.map +1 -0
  43. package/dist/peaks/PeaksPainter.js +43 -0
  44. package/dist/types.d.ts +25 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +1 -0
  47. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1 @@
1
+ This package is part of the openDAW SDK
@@ -0,0 +1,10 @@
1
+ import './types';
2
+ export * from "./live-stream/LiveStreamReceiver";
3
+ export * from "./live-stream/LiveStreamBroadcaster";
4
+ export * from "./peaks/Peaks";
5
+ export * from "./peaks/PeakWorker";
6
+ export * from "./peaks/PeakProtocol";
7
+ export * from "./peaks/PeaksPainter";
8
+ export * from "./opfs/OpfsWorker";
9
+ export * from "./opfs/OpfsProtocol";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,SAAS,CAAA;AAEhB,cAAc,kCAAkC,CAAA;AAChD,cAAc,qCAAqC,CAAA;AACnD,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ const key = Symbol.for("@openDAW/lib-fusion");
2
+ if (globalThis[key]) {
3
+ console.debug(`%c${key.description}%c is already available in ${globalThis.constructor.name}.`, "color: hsl(10, 83%, 60%)", "color: inherit");
4
+ }
5
+ else {
6
+ globalThis[key] = true;
7
+ console.debug(`%c${key.description}%c is now available in ${globalThis.constructor.name}.`, "color: hsl(200, 83%, 60%)", "color: inherit");
8
+ }
9
+ import './types';
10
+ export * from "./live-stream/LiveStreamReceiver";
11
+ export * from "./live-stream/LiveStreamBroadcaster";
12
+ export * from "./peaks/Peaks";
13
+ export * from "./peaks/PeakWorker";
14
+ export * from "./peaks/PeakProtocol";
15
+ export * from "./peaks/PeaksPainter";
16
+ export * from "./opfs/OpfsWorker";
17
+ export * from "./opfs/OpfsProtocol";
@@ -0,0 +1,6 @@
1
+ export declare const enum Flags {
2
+ ID = 15793935,
3
+ START = 15790320,
4
+ END = 986895
5
+ }
6
+ //# sourceMappingURL=Flags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Flags.d.ts","sourceRoot":"","sources":["../../src/live-stream/Flags.ts"],"names":[],"mappings":"AAAA,0BAAkB,KAAK;IACnB,EAAE,WAAW;IAAE,KAAK,WAAW;IAAE,GAAG,SAAW;CAClD"}
@@ -0,0 +1,6 @@
1
+ export var Flags;
2
+ (function (Flags) {
3
+ Flags[Flags["ID"] = 15793935] = "ID";
4
+ Flags[Flags["START"] = 15790320] = "START";
5
+ Flags[Flags["END"] = 986895] = "END";
6
+ })(Flags || (Flags = {}));
@@ -0,0 +1,16 @@
1
+ import { Exec, float, int, Provider, Terminable } from "@opendaw/lib-std";
2
+ import { Address } from "@opendaw/lib-box";
3
+ import { Messenger } from "@opendaw/lib-runtime";
4
+ export declare class LiveStreamBroadcaster {
5
+ #private;
6
+ static create(messenger: Messenger, name: string): LiveStreamBroadcaster;
7
+ private constructor();
8
+ flush(): void;
9
+ broadcastFloat(address: Address, provider: Provider<float>): Terminable;
10
+ broadcastInteger(address: Address, provider: Provider<int>): Terminable;
11
+ broadcastFloats(address: Address, values: Float32Array, update: Exec): Terminable;
12
+ broadcastIntegers(address: Address, values: Int32Array, update: Exec): Terminable;
13
+ broadcastByteArray(address: Address, values: Int8Array, update: Exec): Terminable;
14
+ terminate(): void;
15
+ }
16
+ //# sourceMappingURL=LiveStreamBroadcaster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveStreamBroadcaster.d.ts","sourceRoot":"","sources":["../../src/live-stream/LiveStreamBroadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,IAAI,EACJ,KAAK,EACL,GAAG,EAGH,QAAQ,EACR,UAAU,EACb,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAa5D,qBAAa,qBAAqB;;IAC9B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB;IAkBxE,OAAO;IASP,KAAK,IAAI,IAAI;IA2Bb,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,UAAU;IASvE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,UAAU;IASvE,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,GAAG,UAAU;IAajF,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAG,UAAU;IAajF,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,GAAG,UAAU;IAmCjF,SAAS,IAAI,IAAI;CAgDpB"}
@@ -0,0 +1,180 @@
1
+ import { Arrays, assert, ByteArrayOutput, nextPowOf2, Option } from "@opendaw/lib-std";
2
+ import { Communicator } from "@opendaw/lib-runtime";
3
+ import { Lock } from "./Lock";
4
+ import { PackageType } from "./PackageType";
5
+ import { Flags } from "./Flags";
6
+ export class LiveStreamBroadcaster {
7
+ static create(messenger, name) {
8
+ return new LiveStreamBroadcaster(messenger.channel(name));
9
+ }
10
+ #packages = [];
11
+ #lock = new SharedArrayBuffer(1);
12
+ #lockArray = new Int8Array(this.#lock);
13
+ #sender;
14
+ #output = ByteArrayOutput.create(0);
15
+ #sabOption = Option.None;
16
+ #availableUpdate = Option.None;
17
+ #version = -1;
18
+ #capacity = -1;
19
+ #invalid = false;
20
+ #lockShared = false;
21
+ constructor(messenger) {
22
+ this.#sender = Communicator.sender(messenger, ({ dispatchAndForget }) => new class {
23
+ sendShareLock(lock) { dispatchAndForget(this.sendShareLock, lock); }
24
+ sendUpdateData(buffer) { dispatchAndForget(this.sendUpdateData, buffer); }
25
+ sendUpdateStructure(buffer) { dispatchAndForget(this.sendUpdateStructure, buffer); }
26
+ });
27
+ }
28
+ flush() {
29
+ const update = this.#updateAvailable();
30
+ if (update.nonEmpty()) {
31
+ if (!this.#lockShared) {
32
+ this.#sender.sendShareLock(this.#lock);
33
+ this.#lockShared = true;
34
+ }
35
+ this.#sender.sendUpdateStructure(update.unwrap());
36
+ let capacity = this.#computeCapacity();
37
+ if (this.#output.remaining < capacity) {
38
+ const size = nextPowOf2(capacity);
39
+ const data = new SharedArrayBuffer(size);
40
+ this.#output = ByteArrayOutput.use(data);
41
+ this.#sabOption = Option.wrap(data);
42
+ this.#sender.sendUpdateData(data);
43
+ }
44
+ }
45
+ if (this.#sabOption.isEmpty()) {
46
+ return;
47
+ }
48
+ // If main-thread is not interested, no data will ever be sent again, since it will not set the lock to CAN_WRITE.
49
+ // No lock is necessary since the other side skips reading until we set the lock to CAN_READ.
50
+ if (Atomics.load(this.#lockArray, 0) === Lock.WRITE) {
51
+ this.#flushData(this.#output);
52
+ this.#output.position = 0;
53
+ Atomics.store(this.#lockArray, 0, Lock.READ);
54
+ }
55
+ }
56
+ broadcastFloat(address, provider) {
57
+ return this.#storeChunk(new class {
58
+ type = PackageType.Float;
59
+ address = address;
60
+ capacity = 8;
61
+ put(output) { output.writeFloat(provider()); }
62
+ });
63
+ }
64
+ broadcastInteger(address, provider) {
65
+ return this.#storeChunk(new class {
66
+ type = PackageType.Integer;
67
+ address = address;
68
+ capacity = 8;
69
+ put(output) { output.writeInt(provider()); }
70
+ });
71
+ }
72
+ broadcastFloats(address, values, update) {
73
+ return this.#storeChunk(new class {
74
+ type = PackageType.FloatArray;
75
+ address = address;
76
+ capacity = 4 + (values.byteLength << 2);
77
+ put(output) {
78
+ update();
79
+ output.writeInt(values.length);
80
+ for (const value of values) {
81
+ output.writeFloat(value);
82
+ }
83
+ }
84
+ });
85
+ }
86
+ broadcastIntegers(address, values, update) {
87
+ return this.#storeChunk(new class {
88
+ type = PackageType.IntegerArray;
89
+ address = address;
90
+ capacity = 4 + (values.byteLength << 2);
91
+ put(output) {
92
+ update();
93
+ output.writeInt(values.length);
94
+ for (const value of values) {
95
+ output.writeInt(value);
96
+ }
97
+ }
98
+ });
99
+ }
100
+ broadcastByteArray(address, values, update) {
101
+ return this.#storeChunk(new class {
102
+ type = PackageType.ByteArray;
103
+ address = address;
104
+ capacity = 4 + values.byteLength;
105
+ put(output) {
106
+ update();
107
+ output.writeInt(values.byteLength);
108
+ output.writeBytes(values);
109
+ }
110
+ });
111
+ }
112
+ #updateAvailable() {
113
+ if (this.#invalid) {
114
+ try {
115
+ this.#availableUpdate = Option.wrap(this.#compileStructure());
116
+ }
117
+ catch (reason) {
118
+ throw reason;
119
+ }
120
+ this.#invalid = false;
121
+ }
122
+ if (this.#availableUpdate.nonEmpty()) {
123
+ const option = this.#availableUpdate;
124
+ this.#availableUpdate = Option.None;
125
+ return option;
126
+ }
127
+ return Option.None;
128
+ }
129
+ #computeCapacity() {
130
+ if (-1 === this.#capacity) {
131
+ this.#capacity = this.#sumRequiredCapacity() + 12;
132
+ }
133
+ return this.#capacity;
134
+ }
135
+ terminate() {
136
+ Arrays.clear(this.#packages);
137
+ this.#availableUpdate = Option.None;
138
+ this.#invalid = false;
139
+ this.#capacity = 0;
140
+ }
141
+ #flushData(output) {
142
+ assert(!this.#invalid && this.#availableUpdate.isEmpty(), "Cannot flush while update is available");
143
+ let requiredCapacity = this.#computeCapacity();
144
+ assert(output.remaining >= requiredCapacity, "Insufficient data");
145
+ output.writeInt(this.#version);
146
+ output.writeInt(Flags.START);
147
+ for (const pack of this.#packages) {
148
+ pack.put(output);
149
+ }
150
+ output.writeInt(Flags.END);
151
+ }
152
+ #sumRequiredCapacity() {
153
+ return this.#packages.reduce((sum, pack) => sum + pack.capacity, 0);
154
+ }
155
+ #storeChunk(pack) {
156
+ this.#packages.push(pack);
157
+ this.#invalidate();
158
+ return {
159
+ terminate: () => {
160
+ Arrays.removeOpt(this.#packages, pack);
161
+ this.#invalidate();
162
+ }
163
+ };
164
+ }
165
+ #invalidate() {
166
+ this.#capacity = -1;
167
+ this.#invalid = true;
168
+ }
169
+ #compileStructure() {
170
+ const output = ByteArrayOutput.create();
171
+ output.writeInt(Flags.ID);
172
+ output.writeInt(++this.#version);
173
+ output.writeInt(this.#packages.length);
174
+ for (const { address, type } of this.#packages) {
175
+ address.write(output);
176
+ output.writeByte(type);
177
+ }
178
+ return output.toArrayBuffer();
179
+ }
180
+ }
@@ -0,0 +1,16 @@
1
+ import { float, int, Procedure, Subscription, Terminable } from "@opendaw/lib-std";
2
+ import { Address } from "@opendaw/lib-box";
3
+ import { Messenger } from "@opendaw/lib-runtime";
4
+ export declare class LiveStreamReceiver implements Terminable {
5
+ #private;
6
+ static ID: int;
7
+ constructor();
8
+ connect(messenger: Messenger): Terminable;
9
+ subscribeFloat(address: Address, procedure: Procedure<int>): Subscription;
10
+ subscribeInteger(address: Address, procedure: Procedure<float>): Subscription;
11
+ subscribeFloats(address: Address, procedure: Procedure<Float32Array>): Subscription;
12
+ subscribeIntegers(address: Address, procedure: Procedure<Int32Array>): Subscription;
13
+ subscribeByteArray(address: Address, procedure: Procedure<Int8Array>): Subscription;
14
+ terminate(): void;
15
+ }
16
+ //# sourceMappingURL=LiveStreamReceiver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveStreamReceiver.d.ts","sourceRoot":"","sources":["../../src/live-stream/LiveStreamReceiver.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,EACL,GAAG,EAIH,SAAS,EAET,YAAY,EACZ,UAAU,EACb,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAA;AAExC,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAmG5D,qBAAa,kBAAmB,YAAW,UAAU;;IACjD,MAAM,CAAC,EAAE,EAAE,GAAG,CAAQ;;IA0BtB,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU;IA2BzC,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,YAAY;IAIzE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,YAAY;IAI7E,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,YAAY,CAAC,GAAG,YAAY;IAInF,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,YAAY;IAInF,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,YAAY;IAInF,SAAS,IAAI,IAAI;CA4CpB"}
@@ -0,0 +1,191 @@
1
+ import { Arrays, assert, ByteArrayInput, isDefined, Option, panic, Terminable } from "@opendaw/lib-std";
2
+ import { Address } from "@opendaw/lib-box";
3
+ import { AnimationFrame } from "@opendaw/lib-dom";
4
+ import { Communicator } from "@opendaw/lib-runtime";
5
+ import { PackageType } from "./PackageType";
6
+ import { Subscribers } from "./Subscribers";
7
+ import { Lock } from "./Lock";
8
+ import { Flags } from "./Flags";
9
+ class FloatPackage {
10
+ subscribers = new Subscribers();
11
+ dispatch(address, input) {
12
+ const value = input.readFloat();
13
+ this.subscribers.getOrNull(address)?.forEach(procedure => procedure(value));
14
+ }
15
+ subscribe(address, procedure) {
16
+ return this.subscribers.subscribe(address, procedure);
17
+ }
18
+ terminate() { this.subscribers.terminate(); }
19
+ }
20
+ class IntegerPackage {
21
+ subscribers = new Subscribers();
22
+ dispatch(address, input) {
23
+ const value = input.readInt();
24
+ this.subscribers.getOrNull(address)?.forEach(procedure => procedure(value));
25
+ }
26
+ subscribe(address, procedure) {
27
+ return this.subscribers.subscribe(address, procedure);
28
+ }
29
+ terminate() { this.subscribers.terminate(); }
30
+ }
31
+ class ArrayPackage {
32
+ subscribers = new Subscribers();
33
+ #arrays = Address.newSet(entry => entry.address);
34
+ dispatch(address, input) {
35
+ const length = input.readInt();
36
+ const entry = this.#arrays.getOrNull(address);
37
+ let array;
38
+ if (isDefined(entry)) {
39
+ array = entry.array;
40
+ }
41
+ else {
42
+ array = this.create(length);
43
+ this.#arrays.add({ address, array });
44
+ }
45
+ this.read(input, array, length);
46
+ this.subscribers.getOrNull(address)?.forEach(procedure => procedure(array));
47
+ }
48
+ subscribe(address, procedure) {
49
+ const subscription = this.subscribers.subscribe(address, procedure);
50
+ return {
51
+ terminate: () => {
52
+ subscription.terminate();
53
+ if (this.subscribers.isEmpty(address) && this.#arrays.hasKey(address)) {
54
+ this.#arrays.removeByKey(address);
55
+ }
56
+ }
57
+ };
58
+ }
59
+ terminate() { this.subscribers.terminate(); }
60
+ }
61
+ class FloatArrayPackage extends ArrayPackage {
62
+ create(length) { return new Float32Array(length); }
63
+ read(input, array, length) {
64
+ for (let i = 0; i < length; i++) {
65
+ array[i] = input.readFloat();
66
+ }
67
+ }
68
+ }
69
+ class IntegerArrayPackage extends ArrayPackage {
70
+ create(length) { return new Int32Array(length); }
71
+ read(input, array, length) {
72
+ for (let i = 0; i < length; i++) {
73
+ array[i] = input.readInt();
74
+ }
75
+ }
76
+ }
77
+ class ByteArrayPackage extends ArrayPackage {
78
+ create(length) { return new Int8Array(length); }
79
+ read(input, array, _length) {
80
+ input.readBytes(array);
81
+ }
82
+ }
83
+ export class LiveStreamReceiver {
84
+ static ID = 0 | 0;
85
+ #float = new FloatPackage();
86
+ #integer = new IntegerPackage();
87
+ #floats = new FloatArrayPackage();
88
+ #integers = new IntegerArrayPackage();
89
+ #bytes = new ByteArrayPackage();
90
+ #packages = [];
91
+ #procedures = [];
92
+ #id;
93
+ #optLock = Option.None;
94
+ #memory = Option.None;
95
+ #structureVersion = -1;
96
+ #connected = false;
97
+ constructor() {
98
+ this.#id = LiveStreamReceiver.ID++;
99
+ this.#packages[PackageType.Float] = this.#float;
100
+ this.#packages[PackageType.FloatArray] = this.#floats;
101
+ this.#packages[PackageType.Integer] = this.#integer;
102
+ this.#packages[PackageType.IntegerArray] = this.#integers;
103
+ this.#packages[PackageType.ByteArray] = this.#bytes;
104
+ }
105
+ connect(messenger) {
106
+ assert(!this.#connected, "Already connected");
107
+ this.#connected = true;
108
+ return Terminable.many({ terminate: () => { this.#disconnect(); } }, Communicator.executor(messenger, {
109
+ sendShareLock: (lock) => this.#optLock = Option.wrap(new Int8Array(lock)),
110
+ sendUpdateData: (data) => this.#memory = Option.wrap(new ByteArrayInput(data)),
111
+ sendUpdateStructure: (structure) => this.#updateStructure(new ByteArrayInput(structure))
112
+ }), AnimationFrame.add(() => this.#dispatch()));
113
+ }
114
+ #disconnect() {
115
+ this.#memory = Option.None;
116
+ this.#optLock = Option.None;
117
+ this.#structureVersion = -1;
118
+ this.#connected = false;
119
+ Arrays.clear(this.#procedures);
120
+ this.#float.terminate();
121
+ this.#floats.terminate();
122
+ this.#integer.terminate();
123
+ this.#integers.terminate();
124
+ this.#bytes.terminate();
125
+ }
126
+ subscribeFloat(address, procedure) {
127
+ return this.#float.subscribe(address, procedure);
128
+ }
129
+ subscribeInteger(address, procedure) {
130
+ return this.#integer.subscribe(address, procedure);
131
+ }
132
+ subscribeFloats(address, procedure) {
133
+ return this.#floats.subscribe(address, procedure);
134
+ }
135
+ subscribeIntegers(address, procedure) {
136
+ return this.#integers.subscribe(address, procedure);
137
+ }
138
+ subscribeByteArray(address, procedure) {
139
+ return this.#bytes.subscribe(address, procedure);
140
+ }
141
+ terminate() { this.#disconnect(); }
142
+ #dispatch() {
143
+ if (this.#optLock.isEmpty() || this.#memory.isEmpty()) {
144
+ return;
145
+ }
146
+ const lock = this.#optLock.unwrap();
147
+ if (Atomics.load(lock, 0) === Lock.READ) {
148
+ const byteArrayInput = this.#memory.unwrap();
149
+ this.#dispatchData(byteArrayInput);
150
+ byteArrayInput.position = 0;
151
+ Atomics.store(lock, 0, Lock.WRITE);
152
+ }
153
+ }
154
+ #updateStructure(input) {
155
+ Arrays.clear(this.#procedures);
156
+ this.#parseStructure(input);
157
+ }
158
+ #dispatchData(input) {
159
+ let version = input.readInt();
160
+ if (version !== this.#structureVersion) {
161
+ // we simply skip and await the latest version soon enough
162
+ return false;
163
+ }
164
+ if (input.readInt() !== Flags.START) {
165
+ throw new Error("stream is broken (no start flag)");
166
+ }
167
+ for (const procedure of this.#procedures) {
168
+ procedure(input);
169
+ }
170
+ if (input.readInt() !== Flags.END) {
171
+ throw new Error("stream is broken (no end flag)");
172
+ }
173
+ return true;
174
+ }
175
+ #parseStructure(input) {
176
+ if (input.readInt() !== Flags.ID) {
177
+ throw new Error("no valid id");
178
+ }
179
+ const version = input.readInt();
180
+ if (version <= this.#structureVersion) {
181
+ return panic("Invalid version. new: " + version + `, was: ${this.#structureVersion}, id: ${this.#id}`);
182
+ }
183
+ this.#structureVersion = version;
184
+ const n = input.readInt();
185
+ for (let i = 0; i < n; i++) {
186
+ const address = Address.read(input);
187
+ const chunk = this.#packages[input.readByte()];
188
+ this.#procedures.push(input => chunk.dispatch(address, input));
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,5 @@
1
+ export declare enum Lock {
2
+ WRITE = 0,
3
+ READ = 1
4
+ }
5
+ //# sourceMappingURL=Lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Lock.d.ts","sourceRoot":"","sources":["../../src/live-stream/Lock.ts"],"names":[],"mappings":"AAAA,oBAAY,IAAI;IAAE,KAAK,IAAI;IAAE,IAAI,IAAI;CAAC"}
@@ -0,0 +1,5 @@
1
+ export var Lock;
2
+ (function (Lock) {
3
+ Lock[Lock["WRITE"] = 0] = "WRITE";
4
+ Lock[Lock["READ"] = 1] = "READ";
5
+ })(Lock || (Lock = {}));
@@ -0,0 +1,8 @@
1
+ export declare enum PackageType {
2
+ Float = 0,
3
+ FloatArray = 1,
4
+ Integer = 2,
5
+ IntegerArray = 3,
6
+ ByteArray = 4
7
+ }
8
+ //# sourceMappingURL=PackageType.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PackageType.d.ts","sourceRoot":"","sources":["../../src/live-stream/PackageType.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IAAE,KAAK,IAAA;IAAE,UAAU,IAAA;IAAE,OAAO,IAAA;IAAE,YAAY,IAAA;IAAE,SAAS,IAAA;CAAC"}
@@ -0,0 +1,8 @@
1
+ export var PackageType;
2
+ (function (PackageType) {
3
+ PackageType[PackageType["Float"] = 0] = "Float";
4
+ PackageType[PackageType["FloatArray"] = 1] = "FloatArray";
5
+ PackageType[PackageType["Integer"] = 2] = "Integer";
6
+ PackageType[PackageType["IntegerArray"] = 3] = "IntegerArray";
7
+ PackageType[PackageType["ByteArray"] = 4] = "ByteArray";
8
+ })(PackageType || (PackageType = {}));
@@ -0,0 +1,6 @@
1
+ export interface Protocol {
2
+ sendShareLock(lock: SharedArrayBuffer): void;
3
+ sendUpdateData(data: ArrayBufferLike): void;
4
+ sendUpdateStructure(structure: ArrayBufferLike): void;
5
+ }
6
+ //# sourceMappingURL=Protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../src/live-stream/Protocol.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACrB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC5C,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAA;IAC3C,mBAAmB,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAA;CACxD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { Nullish, Subscription, Terminable } from "@opendaw/lib-std";
2
+ import { Address } from "@opendaw/lib-box";
3
+ export declare class Subscribers<T> implements Terminable {
4
+ #private;
5
+ constructor();
6
+ getOrNull(address: Address): Nullish<ReadonlyArray<T>>;
7
+ isEmpty(address: Address): boolean;
8
+ subscribe(address: Address, listener: T): Subscription;
9
+ terminate(): void;
10
+ }
11
+ //# sourceMappingURL=Subscribers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Subscribers.d.ts","sourceRoot":"","sources":["../../src/live-stream/Subscribers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,OAAO,EAAa,YAAY,EAAE,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAChG,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAA;AAIxC,qBAAa,WAAW,CAAC,CAAC,CAAE,YAAW,UAAU;;;IAK7C,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAEtD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAElC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,YAAY;IAmBtD,SAAS,IAAI,IAAI;CACpB"}
@@ -0,0 +1,28 @@
1
+ import { Arrays, isDefined } from "@opendaw/lib-std";
2
+ import { Address } from "@opendaw/lib-box";
3
+ export class Subscribers {
4
+ #subscribers;
5
+ constructor() { this.#subscribers = Address.newSet(entry => entry.address); }
6
+ getOrNull(address) { return this.#subscribers.getOrNull(address)?.listeners; }
7
+ isEmpty(address) { return !this.#subscribers.hasKey(address); }
8
+ subscribe(address, listener) {
9
+ const entry = this.#subscribers.getOrNull(address);
10
+ if (isDefined(entry)) {
11
+ entry.listeners.push(listener);
12
+ }
13
+ else {
14
+ this.#subscribers.add({ address, listeners: [listener] });
15
+ }
16
+ return {
17
+ terminate: () => {
18
+ this.#subscribers.opt(address).ifSome(entry => {
19
+ Arrays.remove(entry.listeners, listener);
20
+ if (entry.listeners.length === 0) {
21
+ this.#subscribers.removeByKey(address);
22
+ }
23
+ });
24
+ }
25
+ };
26
+ }
27
+ terminate() { this.#subscribers.clear(); }
28
+ }
@@ -0,0 +1,12 @@
1
+ export type Kind = "file" | "directory";
2
+ export type Entry = {
3
+ name: string;
4
+ kind: Kind;
5
+ };
6
+ export interface OpfsProtocol {
7
+ write(path: string, data: Uint8Array): Promise<void>;
8
+ read(path: string): Promise<Uint8Array>;
9
+ delete(path: string): Promise<void>;
10
+ list(path: string): Promise<ReadonlyArray<Entry>>;
11
+ }
12
+ //# sourceMappingURL=OpfsProtocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpfsProtocol.d.ts","sourceRoot":"","sources":["../../src/opfs/OpfsProtocol.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,CAAA;AACvC,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAA;AAEhD,MAAM,WAAW,YAAY;IACzB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACvC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;CACpD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Communicator, Messenger } from "@opendaw/lib-runtime";
2
+ import { Entry } from "./OpfsProtocol";
3
+ import "../types";
4
+ export declare namespace OpfsWorker {
5
+ const init: (messenger: Messenger) => Communicator.Executor<{
6
+ write(path: string, data: Uint8Array): Promise<void>;
7
+ read(path: string): Promise<Uint8Array>;
8
+ delete(path: string): Promise<void>;
9
+ list(path: string): Promise<ReadonlyArray<Entry>>;
10
+ "__#49@#resolveFile"(path: string, options?: FileSystemGetDirectoryOptions): Promise<FileSystemSyncAccessHandle>;
11
+ "__#49@#resolveFolder"(segments: ReadonlyArray<string>, options?: FileSystemGetDirectoryOptions): Promise<FileSystemDirectoryHandle>;
12
+ }>;
13
+ }
14
+ //# sourceMappingURL=OpfsWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpfsWorker.d.ts","sourceRoot":"","sources":["../../src/opfs/OpfsWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAE,SAAS,EAAW,MAAM,sBAAsB,CAAA;AACtE,OAAO,EAAC,KAAK,EAAe,MAAM,gBAAgB,CAAA;AAClD,OAAO,UAAU,CAAA;AAEjB,yBAAiB,UAAU,CAAC;IAKjB,MAAM,IAAI,GAAI,WAAW,SAAS;oBAEf,MAAM,QAAQ,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;mBAWzC,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;qBAY1B,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;mBAMxB,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;mCAW9B,MAAM,YAAY,6BAA6B,GAAG,OAAO,CAAC,0BAA0B,CAAC;yCAO/E,aAAa,CAAC,MAAM,CAAC,YACrB,6BAA6B,GAAG,OAAO,CAAC,yBAAyB,CAAC;MAKnG,CAAA;CAMT"}
@@ -0,0 +1,70 @@
1
+ import { Arrays, asDefined } from "@opendaw/lib-std";
2
+ import { Communicator, Promises } from "@opendaw/lib-runtime";
3
+ import "../types";
4
+ export var OpfsWorker;
5
+ (function (OpfsWorker) {
6
+ const DEBUG = false;
7
+ const readLimiter = new Promises.Limit(1);
8
+ const writeLimiter = new Promises.Limit(1);
9
+ OpfsWorker.init = (messenger) => Communicator.executor(messenger.channel("opfs"), new class {
10
+ async write(path, data) {
11
+ if (DEBUG) {
12
+ console.debug(`write ${data.length}b to ${path}`);
13
+ }
14
+ return writeLimiter.add(() => this.#resolveFile(path, { create: true })
15
+ .then(handle => {
16
+ handle.truncate(data.length);
17
+ handle.write(data, { at: 0 });
18
+ handle.flush();
19
+ handle.close();
20
+ }));
21
+ }
22
+ async read(path) {
23
+ if (DEBUG) {
24
+ console.debug(`read ${path}`);
25
+ }
26
+ return readLimiter.add(() => this.#resolveFile(path)
27
+ .then(handle => {
28
+ const size = handle.getSize();
29
+ const buffer = new Uint8Array(size);
30
+ handle.read(buffer);
31
+ handle.close();
32
+ return buffer;
33
+ }));
34
+ }
35
+ async delete(path) {
36
+ const segments = pathToSegments(path);
37
+ return this.#resolveFolder(segments.slice(0, -1))
38
+ .then(folder => folder.removeEntry(asDefined(segments.at(-1)), { recursive: true }));
39
+ }
40
+ async list(path) {
41
+ const segments = pathToSegments(path);
42
+ const { status, value: folder } = await Promises.tryCatch(this.#resolveFolder(segments));
43
+ if (status === "rejected") {
44
+ return Arrays.empty();
45
+ }
46
+ const result = [];
47
+ for await (const { name, kind } of folder.values()) {
48
+ result.push({ name, kind });
49
+ }
50
+ return result;
51
+ }
52
+ async #resolveFile(path, options) {
53
+ const segments = pathToSegments(path);
54
+ return this.#resolveFolder(segments.slice(0, -1), options)
55
+ .then((folder) => folder.getFileHandle(asDefined(segments.at(-1)), options)
56
+ .then(handle => handle.createSyncAccessHandle()));
57
+ }
58
+ async #resolveFolder(segments, options) {
59
+ let folder = await navigator.storage.getDirectory();
60
+ for (const segment of segments) {
61
+ folder = await folder.getDirectoryHandle(segment, options);
62
+ }
63
+ return folder;
64
+ }
65
+ });
66
+ const pathToSegments = (path) => {
67
+ const noSlashes = path.replace(/^\/+|\/+$/g, "");
68
+ return noSlashes === "" ? [] : noSlashes.split("/");
69
+ };
70
+ })(OpfsWorker || (OpfsWorker = {}));
@@ -0,0 +1,5 @@
1
+ import { FloatArray, int, Procedure } from "@opendaw/lib-std";
2
+ export interface PeakProtocol {
3
+ generateAsync(progress: Procedure<number>, shifts: Uint8Array, frames: ReadonlyArray<FloatArray>, numFrames: int, numChannels: int): Promise<ArrayBufferLike>;
4
+ }
5
+ //# sourceMappingURL=PeakProtocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PeakProtocol.d.ts","sourceRoot":"","sources":["../../src/peaks/PeakProtocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,GAAG,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAE3D,MAAM,WAAW,YAAY;IACzB,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,EAC3B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,EACjC,SAAS,EAAE,GAAG,EACd,WAAW,EAAE,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAC5D"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { FloatArray, int, Procedure } from "@opendaw/lib-std";
2
+ import { Communicator, Messenger } from "@opendaw/lib-runtime";
3
+ export declare namespace PeakWorker {
4
+ const install: (messenger: Messenger) => Communicator.Executor<{
5
+ generateAsync(progress: Procedure<number>, shifts: Uint8Array, frames: FloatArray[], numFrames: int, numChannels: int): Promise<ArrayBufferLike>;
6
+ }>;
7
+ }
8
+ //# sourceMappingURL=PeakWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PeakWorker.d.ts","sourceRoot":"","sources":["../../src/peaks/PeakWorker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,UAAU,EAAE,GAAG,EAAS,SAAS,EAAC,MAAM,kBAAkB,CAAA;AAC1F,OAAO,EAAC,YAAY,EAAE,SAAS,EAAY,MAAM,sBAAsB,CAAA;AAIvE,yBAAiB,UAAU,CAAC;IACjB,MAAM,OAAO,GAAI,WAAW,SAAS;gCAEN,SAAS,CAAC,MAAM,CAAC,UACnB,UAAU,UACV,UAAU,EAAE,aACT,GAAG,eACD,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC;MAGjE,CAAA;CA6ET"}
@@ -0,0 +1,78 @@
1
+ import { Arrays, Float16, panic } from "@opendaw/lib-std";
2
+ import { Communicator, stopwatch } from "@opendaw/lib-runtime";
3
+ import { Peaks } from "./Peaks";
4
+ export var PeakWorker;
5
+ (function (PeakWorker) {
6
+ PeakWorker.install = (messenger) => Communicator.executor(messenger.channel("peaks"), new class {
7
+ async generateAsync(progress, shifts, frames, numFrames, numChannels) {
8
+ return generatePeaks(progress, shifts, frames, numFrames, numChannels).toArrayBuffer();
9
+ }
10
+ });
11
+ const generatePeaks = (progress, shifts, frames, numFrames, numChannels) => {
12
+ if (frames.length !== numChannels) {
13
+ return panic(`Invalid numberOfChannels. Expected: ${numChannels}. Got ${frames.length}`);
14
+ }
15
+ class State {
16
+ min = Number.POSITIVE_INFINITY;
17
+ max = Number.NEGATIVE_INFINITY;
18
+ index = 0;
19
+ }
20
+ const time = stopwatch();
21
+ const numShifts = shifts.length;
22
+ const [stages, dataOffset] = initStages(shifts, numFrames);
23
+ const data = Arrays.create(() => new Int32Array(dataOffset), numChannels);
24
+ const minMask = stages[0].mask;
25
+ const total = numChannels * numFrames;
26
+ let count = 0;
27
+ for (let channel = 0; channel < numChannels; ++channel) {
28
+ const channelData = data[channel];
29
+ const channelFrames = frames[channel];
30
+ const states = Arrays.create(() => new State(), numShifts);
31
+ let min = Number.POSITIVE_INFINITY;
32
+ let max = Number.NEGATIVE_INFINITY;
33
+ let position = 0;
34
+ for (let i = 0; i < numFrames; ++i) {
35
+ const frame = channelFrames[i];
36
+ min = Math.min(frame, min);
37
+ max = Math.max(frame, max);
38
+ if ((++position & minMask) === 0) {
39
+ for (let j = 0; j < numShifts; ++j) {
40
+ const stage = stages[j];
41
+ const state = states[j];
42
+ state.min = Math.min(state.min, min);
43
+ state.max = Math.max(state.max, max);
44
+ if ((stage.mask & position) === 0) {
45
+ channelData[stage.dataOffset + state.index++] = pack(state.min, state.max);
46
+ state.min = Number.POSITIVE_INFINITY;
47
+ state.max = Number.NEGATIVE_INFINITY;
48
+ }
49
+ }
50
+ min = Number.POSITIVE_INFINITY;
51
+ max = Number.NEGATIVE_INFINITY;
52
+ }
53
+ if ((++count & 0xFFFF) === 0) {
54
+ progress(count / total);
55
+ }
56
+ }
57
+ }
58
+ progress(1.0);
59
+ time.lab(`Peak-Gen '${self.constructor.name}'`);
60
+ return new Peaks(stages, data, numFrames, numChannels);
61
+ };
62
+ const initStages = (shifts, numFrames) => {
63
+ let dataOffset = 0;
64
+ const stages = Arrays.create((index) => {
65
+ const shift = shifts[index];
66
+ const numPeaks = Math.ceil(numFrames / (1 << shift));
67
+ const stage = new Peaks.Stage((1 << shift) - 1, shift, numPeaks, dataOffset);
68
+ dataOffset += numPeaks;
69
+ return stage;
70
+ }, shifts.length);
71
+ return [stages, dataOffset];
72
+ };
73
+ const pack = (f0, f1) => {
74
+ const bits0 = Float16.floatToIntBits(f0);
75
+ const bits1 = Float16.floatToIntBits(f1);
76
+ return bits0 | (bits1 << 16);
77
+ };
78
+ })(PeakWorker || (PeakWorker = {}));
@@ -0,0 +1,26 @@
1
+ import { ByteArrayInput, float, int } from "@opendaw/lib-std";
2
+ export declare class Peaks {
3
+ readonly stages: ReadonlyArray<Peaks.Stage>;
4
+ readonly data: ReadonlyArray<Int32Array>;
5
+ readonly numFrames: int;
6
+ readonly numChannels: int;
7
+ static from(input: ByteArrayInput): Peaks;
8
+ static readonly None: Peaks;
9
+ constructor(stages: ReadonlyArray<Peaks.Stage>, data: ReadonlyArray<Int32Array>, numFrames: int, numChannels: int);
10
+ nearest(unitsPerPixel: number): Peaks.Stage | null;
11
+ toArrayBuffer(): ArrayBufferLike;
12
+ toString(): string;
13
+ }
14
+ export declare namespace Peaks {
15
+ const findBestFit: (numFrames: int, width?: int) => Uint8Array;
16
+ class Stage {
17
+ readonly mask: int;
18
+ readonly shift: int;
19
+ readonly numPeaks: int;
20
+ readonly dataOffset: int;
21
+ constructor(mask: int, shift: int, numPeaks: int, dataOffset: int);
22
+ unitsEachPeak(): int;
23
+ }
24
+ const unpack: (bits: int, index: 0 | 1) => float;
25
+ }
26
+ //# sourceMappingURL=Peaks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Peaks.d.ts","sourceRoot":"","sources":["../../src/peaks/Peaks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAmB,KAAK,EAAW,GAAG,EAAY,MAAM,kBAAkB,CAAA;AAEhH,qBAAa,KAAK;IA0BF,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,GAAG;IACvB,QAAQ,CAAC,WAAW,EAAE,GAAG;IA5BrC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK;IAuBzC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAA0B;gBAEzB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAClC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,EAC/B,SAAS,EAAE,GAAG,EACd,WAAW,EAAE,GAAG;IAErC,OAAO,CAAC,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,GAAG,IAAI;IAYlD,aAAa,IAAI,eAAe;IAsBhC,QAAQ,IAAI,MAAM;CACrB;AAED,yBAAiB,KAAK,CAAC;IACZ,MAAM,WAAW,GAAI,WAAW,GAAG,EAAE,QAAO,GAAU,KAAG,UAS/D,CAAA;IAED,MAAa,KAAK;QACF,QAAQ,CAAC,IAAI,EAAE,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,GAAG;QAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,GAAG;oBAAhF,IAAI,EAAE,GAAG,EAAW,KAAK,EAAE,GAAG,EAAW,QAAQ,EAAE,GAAG,EAAW,UAAU,EAAE,GAAG;QACrG,aAAa,IAAI,GAAG;KACvB;IAEM,MAAM,MAAM,GAAI,MAAM,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAG,KAShD,CAAA;CACJ"}
@@ -0,0 +1,107 @@
1
+ import { Arrays, assert, ByteArrayOutput, Float16, Unhandled } from "@opendaw/lib-std";
2
+ export class Peaks {
3
+ stages;
4
+ data;
5
+ numFrames;
6
+ numChannels;
7
+ static from(input) {
8
+ assert(input.readString() === "PEAKS", "Wrong header");
9
+ const numStages = input.readInt();
10
+ const stages = [];
11
+ for (let i = 0; i < numStages; i++) {
12
+ const dataOffset = input.readInt();
13
+ const numPeaks = input.readInt();
14
+ const shift = input.readInt();
15
+ const mask = input.readInt();
16
+ stages[i] = new Peaks.Stage(mask, shift, numPeaks, dataOffset);
17
+ }
18
+ const numData = input.readInt();
19
+ const data = [];
20
+ for (let i = 0; i < numData; i++) {
21
+ const array = new Int8Array(input.readInt());
22
+ input.readBytes(array);
23
+ data[i] = new Int32Array(array.buffer);
24
+ }
25
+ const numFrames = input.readInt();
26
+ const numChannels = input.readInt();
27
+ return new Peaks(stages, data, numFrames, numChannels);
28
+ }
29
+ static None = new Peaks([], [], 0, 0);
30
+ constructor(stages, data, numFrames, numChannels) {
31
+ this.stages = stages;
32
+ this.data = data;
33
+ this.numFrames = numFrames;
34
+ this.numChannels = numChannels;
35
+ }
36
+ nearest(unitsPerPixel) {
37
+ if (this.stages.length === 0) {
38
+ return null;
39
+ }
40
+ const shift = Math.floor(Math.log(Math.abs(unitsPerPixel)) / Math.LN2);
41
+ let i = this.stages.length;
42
+ while (--i > -1) {
43
+ if (shift >= this.stages[i].shift) {
44
+ return this.stages[i];
45
+ }
46
+ }
47
+ return this.stages[0];
48
+ }
49
+ toArrayBuffer() {
50
+ const output = ByteArrayOutput.create();
51
+ output.writeString("PEAKS");
52
+ output.writeInt(this.stages.length);
53
+ for (let i = 0; i < this.stages.length; i++) {
54
+ const { dataOffset, numPeaks, shift, mask } = this.stages[i];
55
+ output.writeInt(dataOffset);
56
+ output.writeInt(numPeaks);
57
+ output.writeInt(shift);
58
+ output.writeInt(mask);
59
+ }
60
+ output.writeInt(this.data.length);
61
+ for (let i = 0; i < this.data.length; i++) {
62
+ const array = new Int8Array(this.data[i].buffer);
63
+ output.writeInt(array.length);
64
+ output.writeBytes(array);
65
+ }
66
+ output.writeInt(this.numFrames);
67
+ output.writeInt(this.numChannels);
68
+ return output.toArrayBuffer();
69
+ }
70
+ toString() { return `{Peaks num-stages: ${this.stages.length}}`; }
71
+ }
72
+ (function (Peaks) {
73
+ Peaks.findBestFit = (numFrames, width = 1200) => {
74
+ const ratio = numFrames / width;
75
+ if (ratio <= 1.0) {
76
+ return new Uint8Array(0);
77
+ }
78
+ const ShiftPadding = 3;
79
+ const maxShift = Math.floor(Math.log(ratio) / Math.LN2);
80
+ const numStages = Math.max(1, Math.floor(maxShift / ShiftPadding));
81
+ return new Uint8Array(Arrays.create(index => ShiftPadding * (index + 1), numStages));
82
+ };
83
+ class Stage {
84
+ mask;
85
+ shift;
86
+ numPeaks;
87
+ dataOffset;
88
+ constructor(mask, shift, numPeaks, dataOffset) {
89
+ this.mask = mask;
90
+ this.shift = shift;
91
+ this.numPeaks = numPeaks;
92
+ this.dataOffset = dataOffset;
93
+ }
94
+ unitsEachPeak() { return 1 << this.shift; }
95
+ }
96
+ Peaks.Stage = Stage;
97
+ Peaks.unpack = (bits, index) => {
98
+ switch (index) {
99
+ case 0:
100
+ return Float16.intBitsToFloat(bits);
101
+ case 1:
102
+ return Float16.intBitsToFloat(bits >> 16);
103
+ default:
104
+ return Unhandled(index);
105
+ }
106
+ };
107
+ })(Peaks || (Peaks = {}));
@@ -0,0 +1,16 @@
1
+ import { int } from "@opendaw/lib-std";
2
+ import { Peaks } from "./Peaks";
3
+ export declare namespace PeaksPainter {
4
+ interface Layout {
5
+ x0: number;
6
+ x1: number;
7
+ u0: number;
8
+ u1: number;
9
+ y0: number;
10
+ y1: number;
11
+ v0: number;
12
+ v1: number;
13
+ }
14
+ const renderBlocks: (path: CanvasRenderingContext2D, peaks: Peaks, channelIndex: int, { u0, u1, v0, v1, x0, x1, y0, y1 }: Layout) => void;
15
+ }
16
+ //# sourceMappingURL=PeaksPainter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PeaksPainter.d.ts","sourceRoot":"","sources":["../../src/peaks/PeaksPainter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,kBAAkB,CAAA;AACpC,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAA;AAE7B,yBAAiB,YAAY,CAAC;IAC1B,UAAiB,MAAM;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAA;KACb;IAEM,MAAM,YAAY,GAAI,MAAM,wBAAwB,EAC9B,OAAO,KAAK,EACZ,cAAc,GAAG,EACjB,oCAAkC,MAAM,KAAG,IAoCvE,CAAA;CACJ"}
@@ -0,0 +1,43 @@
1
+ import { Peaks } from "./Peaks";
2
+ export var PeaksPainter;
3
+ (function (PeaksPainter) {
4
+ PeaksPainter.renderBlocks = (path, peaks, channelIndex, { u0, u1, v0, v1, x0, x1, y0, y1 }) => {
5
+ const unitsEachPixel = (u1 - u0) / (x1 - x0);
6
+ const stage = peaks.nearest(unitsEachPixel);
7
+ if (stage === null) {
8
+ return;
9
+ }
10
+ const scale = (y1 - y0 - 1.0) / (v1 - v0);
11
+ const unitsEachPeak = stage.unitsEachPeak();
12
+ const pixelOverFlow = x0 - Math.floor(x0);
13
+ const peaksEachPixel = unitsEachPixel / unitsEachPeak;
14
+ let from = (u0 - pixelOverFlow * unitsEachPixel) / unitsEachPixel * peaksEachPixel;
15
+ let indexFrom = Math.floor(from);
16
+ let min = 0.0;
17
+ let max = 0.0;
18
+ const data = peaks.data[channelIndex];
19
+ for (let x = Math.floor(x0); x < Math.floor(x1); x++) {
20
+ const to = from + peaksEachPixel;
21
+ const indexTo = Math.floor(to);
22
+ let swap = false;
23
+ while (indexFrom < indexTo) {
24
+ const bits = data[stage.dataOffset + indexFrom++];
25
+ min = Math.min(Peaks.unpack(bits, 0), min);
26
+ max = Math.max(Peaks.unpack(bits, 1), max);
27
+ swap = true;
28
+ }
29
+ const yMin = y0 + Math.floor((min - v0) * scale);
30
+ const yMax = y0 + Math.floor((max - v0) * scale);
31
+ const ry0 = Math.max(y0, Math.min(yMin, yMax));
32
+ const ry1 = Math.min(y1, Math.max(yMin, yMax));
33
+ path.fillRect(x, ry0, 1, ry1 === ry0 ? 1 : ry1 - ry0);
34
+ if (swap) {
35
+ const tmp = max;
36
+ max = min;
37
+ min = tmp;
38
+ }
39
+ from = to;
40
+ indexFrom = indexTo;
41
+ }
42
+ };
43
+ })(PeaksPainter || (PeaksPainter = {}));
@@ -0,0 +1,25 @@
1
+ declare global {
2
+ interface FileSystemFileHandle {
3
+ createSyncAccessHandle(): Promise<FileSystemSyncAccessHandle>;
4
+ }
5
+ interface FileSystemSyncAccessHandle {
6
+ write(buffer: BufferSource, options?: {
7
+ at?: number;
8
+ }): number;
9
+ read(buffer: BufferSource, options?: {
10
+ at?: number;
11
+ }): number;
12
+ getSize(): number;
13
+ truncate(newSize: number): void;
14
+ flush(): void;
15
+ close(): void;
16
+ }
17
+ interface Navigator {
18
+ readonly storage: StorageManager;
19
+ }
20
+ interface StorageManager {
21
+ getDirectory(): Promise<FileSystemDirectoryHandle>;
22
+ }
23
+ }
24
+ export {};
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,oBAAoB;QAC1B,sBAAsB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAA;KAChE;IAED,UAAU,0BAA0B;QAChC,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,MAAM,CAAA;QAC9D,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,MAAM,CAAA;QAC7D,OAAO,IAAI,MAAM,CAAA;QACjB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QAC/B,KAAK,IAAI,IAAI,CAAA;QACb,KAAK,IAAI,IAAI,CAAA;KAChB;IAED,UAAU,SAAS;QACf,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;KACnC;IAED,UAAU,cAAc;QACpB,YAAY,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAAA;KACrD;CACJ;AAED,OAAO,EAAE,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@opendaw/lib-fusion",
3
+ "version": "0.0.6",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "license": "LGPL-3.0-or-later",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist/**/*"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "lint": "eslint \"**/*.ts\"",
22
+ "test": "echo \"No tests to run\""
23
+ },
24
+ "dependencies": {
25
+ "@opendaw/lib-box": "^0.0.6",
26
+ "@opendaw/lib-runtime": "^0.0.6",
27
+ "@opendaw/lib-std": "^0.0.6"
28
+ },
29
+ "devDependencies": {
30
+ "@opendaw/eslint-config": "^0.0.6",
31
+ "@opendaw/typescript-config": "^0.0.6"
32
+ },
33
+ "gitHead": "04e5363a9851c7e116a306c2e933c5f410980fbe"
34
+ }