@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.
@@ -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
  }
@@ -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 EFSourceMixin(FetchMixin(LitElement), {
28
- assetType: "image_files"
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 {};
@@ -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 EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
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
- ], EFMedia.prototype, "currentTimeMs", 2);
552
+ ], _EFMedia.prototype, "currentTimeMs", 2);
432
553
  __decorateClass([
433
554
  property({ type: String, attribute: "asset-id", reflect: true })
434
- ], EFMedia.prototype, "assetId", 1);
555
+ ], _EFMedia.prototype, "assetId", 1);
435
556
  __decorateClass([
436
557
  state()
437
- ], EFMedia.prototype, "desiredSeekTimeMs", 2);
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 }),
@@ -378,7 +378,7 @@ EFTimegroup.styles = css`
378
378
  display: block;
379
379
  width: 100%;
380
380
  height: 100%;
381
- position: relative;
381
+ position: absolute;
382
382
  transform-origin: center center;
383
383
  }
384
384
  `;
@@ -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" | "equalizer" | "curve" | "line" | "pixel" | "wave";
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 drawCurve(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
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 {};