@editframe/elements 0.14.0-beta.3 → 0.15.0-beta.1
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/elements/EFImage.d.ts +2 -1
- package/dist/elements/EFImage.js +9 -3
- package/dist/elements/EFMedia.d.ts +7 -0
- package/dist/elements/EFMedia.js +133 -5
- package/dist/elements/EFTemporal.d.ts +4 -0
- package/dist/elements/EFTemporal.js +14 -0
- package/dist/elements/EFTimegroup.js +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -5
- package/dist/elements/EFWaveform.js +152 -144
- package/dist/gui/EFWorkbench.js +1 -1
- package/package.json +3 -2
- package/src/elements/EFImage.browsertest.ts +33 -2
- package/src/elements/EFImage.ts +10 -3
- package/src/elements/EFMedia.ts +163 -1
- package/src/elements/EFTemporal.ts +33 -1
- package/src/elements/EFTimegroup.ts +5 -2
- package/src/elements/EFWaveform.ts +188 -185
- package/src/gui/EFWorkbench.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Task } from '@lit/task';
|
|
2
2
|
import { LitElement } from 'lit';
|
|
3
|
-
declare const EFImage_base: (new (...args: any[]) => import('./EFSourceMixin.js').EFSourceMixinInterface) & (new (...args: any[]) => import('./FetchMixin.js').FetchMixinInterface) & typeof LitElement;
|
|
3
|
+
declare const EFImage_base: (new (...args: any[]) => import('./EFTemporal.js').TemporalMixinInterface) & (new (...args: any[]) => import('./EFSourceMixin.js').EFSourceMixinInterface) & (new (...args: any[]) => import('./FetchMixin.js').FetchMixinInterface) & typeof LitElement;
|
|
4
4
|
export declare class EFImage extends EFImage_base {
|
|
5
5
|
#private;
|
|
6
6
|
static styles: import('lit').CSSResult[];
|
|
@@ -10,6 +10,7 @@ export declare class EFImage extends EFImage_base {
|
|
|
10
10
|
get assetId(): string | null;
|
|
11
11
|
render(): import('lit-html').TemplateResult<1>;
|
|
12
12
|
assetPath(): string;
|
|
13
|
+
get hasOwnDuration(): boolean;
|
|
13
14
|
fetchImage: Task<readonly [string, typeof fetch], void>;
|
|
14
15
|
frameTask: Task<readonly [import('@lit/task').TaskStatus], void>;
|
|
15
16
|
}
|
package/dist/elements/EFImage.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createRef, ref } from "lit/directives/ref.js";
|
|
|
5
5
|
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
6
6
|
import { EF_RENDERING } from "../EF_RENDERING.js";
|
|
7
7
|
import { EFSourceMixin } from "./EFSourceMixin.js";
|
|
8
|
+
import { EFTemporal } from "./EFTemporal.js";
|
|
8
9
|
import { FetchMixin } from "./FetchMixin.js";
|
|
9
10
|
var __defProp = Object.defineProperty;
|
|
10
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -24,9 +25,11 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
24
25
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
26
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
26
27
|
var _assetId;
|
|
27
|
-
let EFImage = class extends
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
let EFImage = class extends EFTemporal(
|
|
29
|
+
EFSourceMixin(FetchMixin(LitElement), {
|
|
30
|
+
assetType: "image_files"
|
|
31
|
+
})
|
|
32
|
+
) {
|
|
30
33
|
constructor() {
|
|
31
34
|
super(...arguments);
|
|
32
35
|
this.imageRef = createRef();
|
|
@@ -76,6 +79,9 @@ let EFImage = class extends EFSourceMixin(FetchMixin(LitElement), {
|
|
|
76
79
|
}
|
|
77
80
|
return `/@ef-image/${this.src}`;
|
|
78
81
|
}
|
|
82
|
+
get hasOwnDuration() {
|
|
83
|
+
return this.hasExplicitDuration;
|
|
84
|
+
}
|
|
79
85
|
};
|
|
80
86
|
_assetId = /* @__PURE__ */ new WeakMap();
|
|
81
87
|
EFImage.styles = [
|
|
@@ -64,5 +64,12 @@ export declare class EFMedia extends EFMedia_base {
|
|
|
64
64
|
startMs: number;
|
|
65
65
|
endMs: number;
|
|
66
66
|
} | undefined>;
|
|
67
|
+
fftSize: number;
|
|
68
|
+
fftDecay: number;
|
|
69
|
+
private static readonly MIN_DB;
|
|
70
|
+
private static readonly MAX_DB;
|
|
71
|
+
private static readonly DECAY_WEIGHT;
|
|
72
|
+
get FREQ_WEIGHTS(): Float32Array;
|
|
73
|
+
frequencyDataTask: Task<readonly [import('@lit/task').TaskStatus, number], Uint8Array | null>;
|
|
67
74
|
}
|
|
68
75
|
export {};
|
package/dist/elements/EFMedia.js
CHANGED
|
@@ -21,6 +21,31 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
21
21
|
return result;
|
|
22
22
|
};
|
|
23
23
|
const log = debug("ef:elements:EFMedia");
|
|
24
|
+
const freqWeightsCache = /* @__PURE__ */ new Map();
|
|
25
|
+
class LRUCache {
|
|
26
|
+
constructor(maxSize) {
|
|
27
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
28
|
+
this.maxSize = maxSize;
|
|
29
|
+
}
|
|
30
|
+
get(key) {
|
|
31
|
+
const value = this.cache.get(key);
|
|
32
|
+
if (value) {
|
|
33
|
+
this.cache.delete(key);
|
|
34
|
+
this.cache.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
set(key, value) {
|
|
39
|
+
if (this.cache.has(key)) {
|
|
40
|
+
this.cache.delete(key);
|
|
41
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
42
|
+
const firstKey = this.cache.keys().next().value;
|
|
43
|
+
this.cache.delete(firstKey);
|
|
44
|
+
}
|
|
45
|
+
this.cache.set(key, value);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const frequencyDataCache = new LRUCache(100);
|
|
24
49
|
const deepGetMediaElements = (element, medias = []) => {
|
|
25
50
|
for (const child of Array.from(element.children)) {
|
|
26
51
|
if (child instanceof EFMedia) {
|
|
@@ -31,7 +56,7 @@ const deepGetMediaElements = (element, medias = []) => {
|
|
|
31
56
|
}
|
|
32
57
|
return medias;
|
|
33
58
|
};
|
|
34
|
-
class
|
|
59
|
+
const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
35
60
|
assetType: "isobmff_files"
|
|
36
61
|
}) {
|
|
37
62
|
constructor() {
|
|
@@ -213,6 +238,75 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
213
238
|
};
|
|
214
239
|
}
|
|
215
240
|
});
|
|
241
|
+
this.fftSize = 512;
|
|
242
|
+
this.fftDecay = 8;
|
|
243
|
+
this.frequencyDataTask = new Task(this, {
|
|
244
|
+
autoRun: EF_INTERACTIVE,
|
|
245
|
+
args: () => [this.audioBufferTask.status, this.trimAdjustedOwnCurrentTimeMs],
|
|
246
|
+
task: async () => {
|
|
247
|
+
await this.audioBufferTask.taskComplete;
|
|
248
|
+
if (!this.audioBufferTask.value) return null;
|
|
249
|
+
if (this.trimAdjustedOwnCurrentTimeMs <= 0) return null;
|
|
250
|
+
const currentTimeMs = this.trimAdjustedOwnCurrentTimeMs;
|
|
251
|
+
const startOffsetMs = this.audioBufferTask.value.startOffsetMs;
|
|
252
|
+
const audioBuffer = this.audioBufferTask.value.buffer;
|
|
253
|
+
const framesData = await Promise.all(
|
|
254
|
+
Array.from({ length: this.fftDecay }, async (_, i) => {
|
|
255
|
+
const frameOffset = i * (1e3 / 30);
|
|
256
|
+
const startTime = Math.max(
|
|
257
|
+
0,
|
|
258
|
+
(currentTimeMs - frameOffset - startOffsetMs) / 1e3
|
|
259
|
+
);
|
|
260
|
+
const cacheKey = `${startOffsetMs},${startTime}`;
|
|
261
|
+
const cachedFrame = frequencyDataCache.get(cacheKey);
|
|
262
|
+
if (cachedFrame) {
|
|
263
|
+
return cachedFrame;
|
|
264
|
+
}
|
|
265
|
+
const audioContext = new OfflineAudioContext(
|
|
266
|
+
2,
|
|
267
|
+
48e3 * (1 / 30),
|
|
268
|
+
48e3
|
|
269
|
+
);
|
|
270
|
+
const analyser = audioContext.createAnalyser();
|
|
271
|
+
analyser.fftSize = this.fftSize;
|
|
272
|
+
analyser.minDecibels = _EFMedia2.MIN_DB;
|
|
273
|
+
analyser.maxDecibels = _EFMedia2.MAX_DB;
|
|
274
|
+
const audioBufferSource = audioContext.createBufferSource();
|
|
275
|
+
audioBufferSource.buffer = audioBuffer;
|
|
276
|
+
audioBufferSource.connect(analyser);
|
|
277
|
+
analyser.connect(audioContext.destination);
|
|
278
|
+
audioBufferSource.start(0, startTime, 1 / 30);
|
|
279
|
+
try {
|
|
280
|
+
await audioContext.startRendering();
|
|
281
|
+
const frameData = new Uint8Array(analyser.frequencyBinCount);
|
|
282
|
+
analyser.getByteFrequencyData(frameData);
|
|
283
|
+
frequencyDataCache.set(cacheKey, frameData);
|
|
284
|
+
return frameData;
|
|
285
|
+
} finally {
|
|
286
|
+
audioBufferSource.disconnect();
|
|
287
|
+
analyser.disconnect();
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
);
|
|
291
|
+
const frameLength = framesData[0]?.length ?? 0;
|
|
292
|
+
const smoothedData = new Uint8Array(frameLength);
|
|
293
|
+
for (let i = 0; i < frameLength; i++) {
|
|
294
|
+
let weightedSum = 0;
|
|
295
|
+
let weightSum = 0;
|
|
296
|
+
framesData.forEach((frame, frameIndex) => {
|
|
297
|
+
const decayWeight = _EFMedia2.DECAY_WEIGHT ** frameIndex;
|
|
298
|
+
weightedSum += frame[i] * decayWeight;
|
|
299
|
+
weightSum += decayWeight;
|
|
300
|
+
});
|
|
301
|
+
smoothedData[i] = Math.min(255, Math.round(weightedSum / weightSum));
|
|
302
|
+
}
|
|
303
|
+
smoothedData.forEach((value, i) => {
|
|
304
|
+
const freqWeight = this.FREQ_WEIGHTS[i];
|
|
305
|
+
smoothedData[i] = Math.min(255, Math.round(value * freqWeight));
|
|
306
|
+
});
|
|
307
|
+
return smoothedData;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
216
310
|
}
|
|
217
311
|
static {
|
|
218
312
|
this.styles = [
|
|
@@ -425,16 +519,50 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
425
519
|
endMs: lastFragment.dts / audioTrackIndex.timescale * 1e3 + lastFragment.duration / audioTrackIndex.timescale * 1e3 - this.trimEndMs
|
|
426
520
|
};
|
|
427
521
|
}
|
|
428
|
-
|
|
522
|
+
static {
|
|
523
|
+
this.MIN_DB = -90;
|
|
524
|
+
}
|
|
525
|
+
static {
|
|
526
|
+
this.MAX_DB = -20;
|
|
527
|
+
}
|
|
528
|
+
static {
|
|
529
|
+
this.DECAY_WEIGHT = 0.7;
|
|
530
|
+
}
|
|
531
|
+
// Update FREQ_WEIGHTS to use the instance fftSize instead of a static value
|
|
532
|
+
get FREQ_WEIGHTS() {
|
|
533
|
+
if (freqWeightsCache.has(this.fftSize)) {
|
|
534
|
+
return freqWeightsCache.get(this.fftSize);
|
|
535
|
+
}
|
|
536
|
+
const weights = new Float32Array(this.fftSize / 2).map((_, i) => {
|
|
537
|
+
const frequency = i * 48e3 / this.fftSize;
|
|
538
|
+
if (frequency < 60) return 0.3;
|
|
539
|
+
if (frequency < 250) return 0.4;
|
|
540
|
+
if (frequency < 500) return 0.6;
|
|
541
|
+
if (frequency < 2e3) return 0.8;
|
|
542
|
+
if (frequency < 4e3) return 1.2;
|
|
543
|
+
if (frequency < 8e3) return 1.6;
|
|
544
|
+
return 2;
|
|
545
|
+
});
|
|
546
|
+
freqWeightsCache.set(this.fftSize, weights);
|
|
547
|
+
return weights;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
429
550
|
__decorateClass([
|
|
430
551
|
property({ type: Number })
|
|
431
|
-
],
|
|
552
|
+
], _EFMedia.prototype, "currentTimeMs", 2);
|
|
432
553
|
__decorateClass([
|
|
433
554
|
property({ type: String, attribute: "asset-id", reflect: true })
|
|
434
|
-
],
|
|
555
|
+
], _EFMedia.prototype, "assetId", 1);
|
|
435
556
|
__decorateClass([
|
|
436
557
|
state()
|
|
437
|
-
],
|
|
558
|
+
], _EFMedia.prototype, "desiredSeekTimeMs", 2);
|
|
559
|
+
__decorateClass([
|
|
560
|
+
property({ type: Number })
|
|
561
|
+
], _EFMedia.prototype, "fftSize", 2);
|
|
562
|
+
__decorateClass([
|
|
563
|
+
property({ type: Number })
|
|
564
|
+
], _EFMedia.prototype, "fftDecay", 2);
|
|
565
|
+
let EFMedia = _EFMedia;
|
|
438
566
|
export {
|
|
439
567
|
EFMedia,
|
|
440
568
|
deepGetMediaElements
|
|
@@ -6,6 +6,10 @@ export declare const timegroupContext: {
|
|
|
6
6
|
};
|
|
7
7
|
export declare class TemporalMixinInterface {
|
|
8
8
|
get hasOwnDuration(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the element has a duration set as an attribute.
|
|
11
|
+
*/
|
|
12
|
+
get hasExplicitDuration(): boolean;
|
|
9
13
|
/**
|
|
10
14
|
* Used to trim the start of the media.
|
|
11
15
|
*
|
|
@@ -198,6 +198,9 @@ const EFTemporal = (superClass) => {
|
|
|
198
198
|
}
|
|
199
199
|
return parent;
|
|
200
200
|
}
|
|
201
|
+
get hasExplicitDuration() {
|
|
202
|
+
return this._durationMs !== void 0;
|
|
203
|
+
}
|
|
201
204
|
get hasOwnDuration() {
|
|
202
205
|
return false;
|
|
203
206
|
}
|
|
@@ -309,6 +312,17 @@ const EFTemporal = (superClass) => {
|
|
|
309
312
|
}
|
|
310
313
|
return 0;
|
|
311
314
|
}
|
|
315
|
+
updated(changedProperties) {
|
|
316
|
+
super.updated(changedProperties);
|
|
317
|
+
if (changedProperties.has("currentTime") || changedProperties.has("ownCurrentTimeMs")) {
|
|
318
|
+
const timelineTimeMs = (this.rootTimegroup ?? this).ownCurrentTimeMs;
|
|
319
|
+
if (this.startTimeMs >= timelineTimeMs || this.endTimeMs <= timelineTimeMs) {
|
|
320
|
+
this.style.display = "none";
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
this.style.display = "";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
312
326
|
}
|
|
313
327
|
__decorateClass([
|
|
314
328
|
consume({ context: timegroupContext, subscribe: true }),
|
|
@@ -8,12 +8,14 @@ export declare class EFWaveform extends EFWaveform_base {
|
|
|
8
8
|
static styles: import('lit').CSSResult;
|
|
9
9
|
canvasRef: Ref<HTMLCanvasElement>;
|
|
10
10
|
private ctx;
|
|
11
|
+
private styleObserver;
|
|
11
12
|
private resizeObserver?;
|
|
12
13
|
private mutationObserver?;
|
|
13
14
|
render(): import('lit-html').TemplateResult<1>;
|
|
14
|
-
mode: "roundBars" | "bars" | "bricks" | "
|
|
15
|
+
mode: "roundBars" | "bars" | "bricks" | "line" | "pixel" | "wave";
|
|
15
16
|
color: string;
|
|
16
17
|
targetSelector: string;
|
|
18
|
+
lineWidth: number;
|
|
17
19
|
set target(value: string);
|
|
18
20
|
connectedCallback(): void;
|
|
19
21
|
disconnectedCallback(): void;
|
|
@@ -21,15 +23,14 @@ export declare class EFWaveform extends EFWaveform_base {
|
|
|
21
23
|
protected initCanvas(): CanvasRenderingContext2D | null;
|
|
22
24
|
protected drawBars(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
23
25
|
protected drawBricks(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
24
|
-
protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
25
26
|
protected drawRoundBars(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
26
27
|
protected drawEqualizer(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
27
|
-
protected
|
|
28
|
+
protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
28
29
|
protected drawPixel(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
29
30
|
protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
30
|
-
frameTask: Task<readonly [import('@lit/task').TaskStatus], void>;
|
|
31
|
+
frameTask: Task<readonly [import('@lit/task').TaskStatus | undefined], void>;
|
|
31
32
|
get durationMs(): number;
|
|
32
|
-
get targetElement(): EFAudio | EFVideo;
|
|
33
|
+
get targetElement(): EFAudio | EFVideo | null;
|
|
33
34
|
protected updated(changedProperties: PropertyValueMap<this>): void;
|
|
34
35
|
}
|
|
35
36
|
export {};
|