@editframe/elements 0.15.0-beta.1 → 0.15.0-beta.3

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.
@@ -104,8 +104,6 @@ class EfFramegen {
104
104
  this.audioBufferPromise = firstGroup.renderAudio(
105
105
  renderOptions.encoderOptions.alignedFromUs / 1e3,
106
106
  renderOptions.encoderOptions.alignedToUs / 1e3
107
- // renderOptions.encoderOptions.fromMs,
108
- // renderOptions.encoderOptions.toMs,
109
107
  );
110
108
  console.log("Initialized");
111
109
  }
@@ -2,7 +2,6 @@ import { Task } from '@lit/task';
2
2
  import { EFMedia } from './EFMedia.js';
3
3
  export declare class EFAudio extends EFMedia {
4
4
  audioElementRef: import('lit-html/directives/ref.js').Ref<HTMLAudioElement>;
5
- src: string;
6
5
  render(): import('lit-html').TemplateResult<1>;
7
6
  get audioElement(): HTMLAudioElement | undefined;
8
7
  frameTask: Task<readonly [import('@lit/task').TaskStatus, import('@lit/task').TaskStatus, import('@lit/task').TaskStatus, import('@lit/task').TaskStatus, import('@lit/task').TaskStatus], void>;
@@ -1,6 +1,6 @@
1
1
  import { Task } from "@lit/task";
2
2
  import { html } from "lit";
3
- import { property, customElement } from "lit/decorators.js";
3
+ import { customElement } from "lit/decorators.js";
4
4
  import { createRef, ref } from "lit/directives/ref.js";
5
5
  import { EFMedia } from "./EFMedia.js";
6
6
  var __defProp = Object.defineProperty;
@@ -17,7 +17,6 @@ let EFAudio = class extends EFMedia {
17
17
  constructor() {
18
18
  super(...arguments);
19
19
  this.audioElementRef = createRef();
20
- this.src = "";
21
20
  this.frameTask = new Task(this, {
22
21
  args: () => [
23
22
  this.trackFragmentIndexLoader.status,
@@ -43,9 +42,6 @@ let EFAudio = class extends EFMedia {
43
42
  return this.audioElementRef.value;
44
43
  }
45
44
  };
46
- __decorateClass([
47
- property({ type: String })
48
- ], EFAudio.prototype, "src", 2);
49
45
  EFAudio = __decorateClass([
50
46
  customElement("ef-audio")
51
47
  ], EFAudio);
@@ -335,7 +335,7 @@ let EFCaptions = class extends EFSourceMixin(
335
335
  if (!transcriptionFragment) {
336
336
  return;
337
337
  }
338
- const currentTimeMs = this.targetElement.trimAdjustedOwnCurrentTimeMs;
338
+ const currentTimeMs = this.targetElement.currentSourceTimeMs;
339
339
  const currentTimeSec = currentTimeMs / 1e3;
340
340
  const currentWord = transcriptionFragment.word_segments.find(
341
341
  (word) => currentTimeSec >= word.start && currentTimeSec <= word.end
@@ -70,6 +70,6 @@ export declare class EFMedia extends EFMedia_base {
70
70
  private static readonly MAX_DB;
71
71
  private static readonly DECAY_WEIGHT;
72
72
  get FREQ_WEIGHTS(): Float32Array;
73
- frequencyDataTask: Task<readonly [import('@lit/task').TaskStatus, number], Uint8Array | null>;
73
+ frequencyDataTask: Task<readonly [import('@lit/task').TaskStatus, number, number, number], Uint8Array | null>;
74
74
  }
75
75
  export {};
@@ -10,6 +10,7 @@ import { EF_RENDERING } from "../EF_RENDERING.js";
10
10
  import { EFSourceMixin } from "./EFSourceMixin.js";
11
11
  import { EFTemporal, isEFTemporal } from "./EFTemporal.js";
12
12
  import { FetchMixin } from "./FetchMixin.js";
13
+ import { EFTargetable } from "./TargetController.js";
13
14
  var __defProp = Object.defineProperty;
14
15
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
16
  var __decorateClass = (decorators, target, key, kind) => {
@@ -40,12 +41,13 @@ class LRUCache {
40
41
  this.cache.delete(key);
41
42
  } else if (this.cache.size >= this.maxSize) {
42
43
  const firstKey = this.cache.keys().next().value;
43
- this.cache.delete(firstKey);
44
+ if (firstKey) {
45
+ this.cache.delete(firstKey);
46
+ }
44
47
  }
45
48
  this.cache.set(key, value);
46
49
  }
47
50
  }
48
- const frequencyDataCache = new LRUCache(100);
49
51
  const deepGetMediaElements = (element, medias = []) => {
50
52
  for (const child of Array.from(element.children)) {
51
53
  if (child instanceof EFMedia) {
@@ -56,9 +58,11 @@ const deepGetMediaElements = (element, medias = []) => {
56
58
  }
57
59
  return medias;
58
60
  };
59
- const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
60
- assetType: "isobmff_files"
61
- }) {
61
+ const _EFMedia = class _EFMedia2 extends EFTargetable(
62
+ EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
63
+ assetType: "isobmff_files"
64
+ })
65
+ ) {
62
66
  constructor() {
63
67
  super(...arguments);
64
68
  this.currentTimeMs = 0;
@@ -240,16 +244,29 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
240
244
  });
241
245
  this.fftSize = 512;
242
246
  this.fftDecay = 8;
247
+ this.#frequencyDataCache = new LRUCache(100);
243
248
  this.frequencyDataTask = new Task(this, {
244
249
  autoRun: EF_INTERACTIVE,
245
- args: () => [this.audioBufferTask.status, this.trimAdjustedOwnCurrentTimeMs],
250
+ args: () => [
251
+ this.audioBufferTask.status,
252
+ this.currentSourceTimeMs,
253
+ this.fftSize,
254
+ // Add fftSize to dependency array
255
+ this.fftDecay
256
+ // Add fftDecay to dependency array
257
+ ],
246
258
  task: async () => {
247
259
  await this.audioBufferTask.taskComplete;
248
260
  if (!this.audioBufferTask.value) return null;
249
- if (this.trimAdjustedOwnCurrentTimeMs <= 0) return null;
250
- const currentTimeMs = this.trimAdjustedOwnCurrentTimeMs;
261
+ if (this.currentSourceTimeMs <= 0) return null;
262
+ const currentTimeMs = this.currentSourceTimeMs;
251
263
  const startOffsetMs = this.audioBufferTask.value.startOffsetMs;
252
264
  const audioBuffer = this.audioBufferTask.value.buffer;
265
+ const smoothedKey = `${this.fftSize}:${this.fftDecay}:${startOffsetMs}:${currentTimeMs}`;
266
+ const cachedSmoothedData = this.#frequencyDataCache.get(smoothedKey);
267
+ if (cachedSmoothedData) {
268
+ return cachedSmoothedData;
269
+ }
253
270
  const framesData = await Promise.all(
254
271
  Array.from({ length: this.fftDecay }, async (_, i) => {
255
272
  const frameOffset = i * (1e3 / 30);
@@ -257,8 +274,8 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
257
274
  0,
258
275
  (currentTimeMs - frameOffset - startOffsetMs) / 1e3
259
276
  );
260
- const cacheKey = `${startOffsetMs},${startTime}`;
261
- const cachedFrame = frequencyDataCache.get(cacheKey);
277
+ const cacheKey = `${this.fftSize}:${startOffsetMs}:${startTime}`;
278
+ const cachedFrame = this.#frequencyDataCache.get(cacheKey);
262
279
  if (cachedFrame) {
263
280
  return cachedFrame;
264
281
  }
@@ -278,9 +295,9 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
278
295
  audioBufferSource.start(0, startTime, 1 / 30);
279
296
  try {
280
297
  await audioContext.startRendering();
281
- const frameData = new Uint8Array(analyser.frequencyBinCount);
298
+ const frameData = new Uint8Array(this.fftSize / 2);
282
299
  analyser.getByteFrequencyData(frameData);
283
- frequencyDataCache.set(cacheKey, frameData);
300
+ this.#frequencyDataCache.set(cacheKey, frameData);
284
301
  return frameData;
285
302
  } finally {
286
303
  audioBufferSource.disconnect();
@@ -304,7 +321,12 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
304
321
  const freqWeight = this.FREQ_WEIGHTS[i];
305
322
  smoothedData[i] = Math.min(255, Math.round(value * freqWeight));
306
323
  });
307
- return smoothedData;
324
+ const slicedData = smoothedData.slice(
325
+ 0,
326
+ Math.floor(smoothedData.length / 2)
327
+ );
328
+ this.#frequencyDataCache.set(smoothedKey, slicedData);
329
+ return slicedData;
308
330
  }
309
331
  });
310
332
  }
@@ -359,7 +381,7 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
359
381
  }
360
382
  updated(changedProperties) {
361
383
  if (changedProperties.has("ownCurrentTimeMs")) {
362
- this.executeSeek(this.trimAdjustedOwnCurrentTimeMs);
384
+ this.executeSeek(this.currentSourceTimeMs);
363
385
  }
364
386
  if (changedProperties.has("currentTime") || changedProperties.has("ownCurrentTimeMs")) {
365
387
  const timelineTimeMs = (this.rootTimegroup ?? this).currentTimeMs;
@@ -546,6 +568,7 @@ const _EFMedia = class _EFMedia2 extends EFSourceMixin(EFTemporal(FetchMixin(Lit
546
568
  freqWeightsCache.set(this.fftSize, weights);
547
569
  return weights;
548
570
  }
571
+ #frequencyDataCache;
549
572
  };
550
573
  __decorateClass([
551
574
  property({ type: Number })
@@ -135,18 +135,18 @@ export declare class TemporalMixinInterface {
135
135
  * elements.
136
136
  *
137
137
  * For example, if the media has a `sourcein` value of 10s, when `ownCurrentTimeMs` is 0s,
138
- * `trimAdjustedOwnCurrentTimeMs` will be 10s.
138
+ * `currentSourceTimeMs` will be 10s.
139
139
  *
140
140
  * sourcein=10s sourceout=10s
141
141
  * / / /
142
142
  * |--------|=================|---------|
143
143
  * ^
144
144
  * |_
145
- * trimAdjustedOwnCurrentTimeMs === 10s
145
+ * currentSourceTimeMs === 10s
146
146
  * |_
147
147
  * ownCurrentTimeMs === 0s
148
148
  */
149
- get trimAdjustedOwnCurrentTimeMs(): number;
149
+ get currentSourceTimeMs(): number;
150
150
  set duration(value: string);
151
151
  get duration(): string;
152
152
  /**
@@ -278,6 +278,10 @@ const EFTemporal = (superClass) => {
278
278
  get endTimeMs() {
279
279
  return this.startTimeMs + this.durationMs;
280
280
  }
281
+ /**
282
+ * The current time of the element within itself.
283
+ * Compare with `currentTimeMs` to see the current time with respect to the root timegroup
284
+ */
281
285
  get ownCurrentTimeMs() {
282
286
  if (this.rootTimegroup) {
283
287
  return Math.min(
@@ -291,7 +295,7 @@ const EFTemporal = (superClass) => {
291
295
  * Used to calculate the internal currentTimeMs of the element. This is useful
292
296
  * for mapping to internal media time codes for audio/video elements.
293
297
  */
294
- get trimAdjustedOwnCurrentTimeMs() {
298
+ get currentSourceTimeMs() {
295
299
  if (this.rootTimegroup) {
296
300
  if (this.sourceInMs && this.sourceOutMs) {
297
301
  return Math.min(
@@ -316,7 +320,7 @@ const EFTemporal = (superClass) => {
316
320
  super.updated(changedProperties);
317
321
  if (changedProperties.has("currentTime") || changedProperties.has("ownCurrentTimeMs")) {
318
322
  const timelineTimeMs = (this.rootTimegroup ?? this).ownCurrentTimeMs;
319
- if (this.startTimeMs >= timelineTimeMs || this.endTimeMs <= timelineTimeMs) {
323
+ if (this.startTimeMs > timelineTimeMs || this.endTimeMs < timelineTimeMs) {
320
324
  this.style.display = "none";
321
325
  return;
322
326
  }
@@ -27,11 +27,7 @@ export declare class EFTimegroup extends EFTimegroup_base {
27
27
  * that caused issues with constructing audio data. We had negative durations
28
28
  * in calculations and it was not clear why.
29
29
  */
30
- waitForMediaDurations(): Promise<({
31
- trackId: string;
32
- buffer: import('mp4box').MP4ArrayBuffer;
33
- mp4File: import('../../../assets/src/MP4File.ts').MP4File;
34
- }[] | undefined)[]>;
30
+ waitForMediaDurations(): Promise<Record<number, import('../../../assets/src/index.ts').TrackFragmentIndex>[]>;
35
31
  get childTemporals(): import('./EFTemporal.js').TemporalMixinInterface[];
36
32
  protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
37
33
  private updateAnimations;
@@ -45,7 +45,7 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
45
45
  __privateAdd(this, _EFTimegroup_instances);
46
46
  this._timeGroupContext = this;
47
47
  __privateAdd(this, _currentTime, 0);
48
- this.mode = "sequence";
48
+ this.mode = "contain";
49
49
  this.overlapMs = 0;
50
50
  this.fit = "none";
51
51
  __privateAdd(this, _resizeObserver);
@@ -186,10 +186,9 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
186
186
  * in calculations and it was not clear why.
187
187
  */
188
188
  async waitForMediaDurations() {
189
+ const mediaElements = deepGetMediaElements(this);
189
190
  return await Promise.all(
190
- deepGetMediaElements(this).map(
191
- (media) => media.initSegmentsLoader.taskComplete
192
- )
191
+ mediaElements.map((m) => m.trackFragmentIndexLoader.taskComplete)
193
192
  );
194
193
  }
195
194
  get childTemporals() {
@@ -402,7 +401,7 @@ __decorateClass([
402
401
  property({ type: String })
403
402
  ], EFTimegroup.prototype, "fit", 2);
404
403
  __decorateClass([
405
- property({ type: Number })
404
+ property({ type: Number, attribute: "currenttime" })
406
405
  ], EFTimegroup.prototype, "currentTime", 1);
407
406
  EFTimegroup = __decorateClass([
408
407
  customElement("ef-timegroup")
@@ -1,8 +1,9 @@
1
- import { EFAudio } from './EFAudio.js';
2
1
  import { Task } from '@lit/task';
3
2
  import { LitElement, PropertyValueMap } from 'lit';
4
3
  import { Ref } from 'lit/directives/ref.js';
4
+ import { EFAudio } from './EFAudio.js';
5
5
  import { EFVideo } from './EFVideo.js';
6
+ import { TargetController } from './TargetController.ts';
6
7
  declare const EFWaveform_base: (new (...args: any[]) => import('./EFTemporal.js').TemporalMixinInterface) & typeof LitElement;
7
8
  export declare class EFWaveform extends EFWaveform_base {
8
9
  static styles: import('lit').CSSResult;
@@ -12,11 +13,12 @@ export declare class EFWaveform extends EFWaveform_base {
12
13
  private resizeObserver?;
13
14
  private mutationObserver?;
14
15
  render(): import('lit-html').TemplateResult<1>;
15
- mode: "roundBars" | "bars" | "bricks" | "line" | "pixel" | "wave";
16
+ mode: "roundBars" | "bars" | "bricks" | "line" | "pixel" | "wave" | "spikes";
16
17
  color: string;
17
- targetSelector: string;
18
+ target: string;
19
+ targetElement: EFAudio | EFVideo | null;
18
20
  lineWidth: number;
19
- set target(value: string);
21
+ targetController: TargetController;
20
22
  connectedCallback(): void;
21
23
  disconnectedCallback(): void;
22
24
  private resizeCanvas;
@@ -28,9 +30,14 @@ export declare class EFWaveform extends EFWaveform_base {
28
30
  protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
29
31
  protected drawPixel(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
30
32
  protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
31
- frameTask: Task<readonly [import('@lit/task').TaskStatus | undefined], void>;
33
+ protected drawSpikes(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
34
+ frameTask: Task<readonly [EFAudio | EFVideo | null, Uint8Array | null | undefined], void>;
32
35
  get durationMs(): number;
33
- get targetElement(): EFAudio | EFVideo | null;
34
36
  protected updated(changedProperties: PropertyValueMap<this>): void;
35
37
  }
38
+ declare global {
39
+ interface HTMLElementTagNameMap {
40
+ "ef-waveform": EFWaveform & Element;
41
+ }
42
+ }
36
43
  export {};
@@ -1,15 +1,14 @@
1
- import { EFAudio } from "./EFAudio.js";
2
1
  import { CSSStyleObserver } from "@bramus/style-observer";
3
2
  import { Task } from "@lit/task";
4
3
  import { html, css, LitElement } from "lit";
5
- import { property, customElement } from "lit/decorators.js";
4
+ import { property, state, customElement } from "lit/decorators.js";
6
5
  import { createRef, ref } from "lit/directives/ref.js";
7
6
  import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
8
7
  import { EF_RENDERING } from "../EF_RENDERING.js";
9
8
  import { TWMixin } from "../gui/TWMixin.js";
10
9
  import { CrossUpdateController } from "./CrossUpdateController.js";
11
10
  import { EFTemporal } from "./EFTemporal.js";
12
- import { EFVideo } from "./EFVideo.js";
11
+ import { TargetController } from "./TargetController.js";
13
12
  var __defProp = Object.defineProperty;
14
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
14
  var __decorateClass = (decorators, target, key, kind) => {
@@ -28,11 +27,18 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
28
27
  this.styleObserver = null;
29
28
  this.mode = "bars";
30
29
  this.color = "currentColor";
31
- this.targetSelector = "";
30
+ this.target = "";
31
+ this.targetElement = null;
32
32
  this.lineWidth = 4;
33
+ this.targetController = new TargetController(this);
33
34
  this.frameTask = new Task(this, {
34
35
  autoRun: EF_INTERACTIVE,
35
- args: () => [this.targetElement?.frequencyDataTask.status],
36
+ args: () => {
37
+ return [
38
+ this.targetElement,
39
+ this.targetElement?.frequencyDataTask.value
40
+ ];
41
+ },
36
42
  task: async () => {
37
43
  if (!this.targetElement) return;
38
44
  await this.targetElement.frequencyDataTask.taskComplete;
@@ -41,11 +47,15 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
41
47
  if (!ctx) return;
42
48
  const frequencyData = this.targetElement.frequencyDataTask.value;
43
49
  if (!frequencyData) return;
50
+ ctx.save();
44
51
  if (this.color === "currentColor") {
45
52
  const computedStyle = getComputedStyle(this);
46
53
  const currentColor = computedStyle.color;
47
54
  ctx.strokeStyle = currentColor;
48
55
  ctx.fillStyle = currentColor;
56
+ } else {
57
+ ctx.strokeStyle = this.color;
58
+ ctx.fillStyle = this.color;
49
59
  }
50
60
  switch (this.mode) {
51
61
  case "bars":
@@ -63,19 +73,20 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
63
73
  case "wave":
64
74
  this.drawWave(ctx, frequencyData);
65
75
  break;
76
+ case "spikes":
77
+ this.drawSpikes(ctx, frequencyData);
78
+ break;
66
79
  case "roundBars":
67
80
  this.drawRoundBars(ctx, frequencyData);
68
81
  break;
69
82
  }
83
+ ctx.restore();
70
84
  }
71
85
  });
72
86
  }
73
87
  render() {
74
88
  return html`<canvas ${ref(this.canvasRef)}></canvas>`;
75
89
  }
76
- set target(value) {
77
- this.targetSelector = value;
78
- }
79
90
  connectedCallback() {
80
91
  super.connectedCallback();
81
92
  try {
@@ -130,27 +141,25 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
130
141
  const ctx = canvas.getContext("2d");
131
142
  if (!ctx) return null;
132
143
  ctx.reset();
133
- ctx.scale(dpr, dpr);
134
144
  return ctx;
135
145
  }
136
146
  drawBars(ctx, frequencyData) {
137
147
  const canvas = ctx.canvas;
138
148
  const waveWidth = canvas.width;
139
149
  const waveHeight = canvas.height;
140
- const baseline = waveHeight / 4;
141
150
  const totalBars = frequencyData.length;
142
151
  const paddingInner = 0.5;
143
152
  const paddingOuter = 0.01;
144
- const availableWidth = waveWidth * (1 - 2 * paddingOuter);
153
+ const availableWidth = waveWidth;
145
154
  const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
146
155
  ctx.clearRect(0, 0, waveWidth, waveHeight);
147
156
  const path = new Path2D();
148
157
  frequencyData.forEach((value, i) => {
149
- const normalizedValue = value / 255;
150
- const height = normalizedValue * (waveHeight / 2);
158
+ const normalizedValue = Math.min(value / 255 * 2, 1);
159
+ const barHeight = normalizedValue * waveHeight;
160
+ const y = (waveHeight - barHeight) / 2;
151
161
  const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
152
- const y = baseline - height;
153
- path.rect(x, y, barWidth, height * 2);
162
+ path.rect(x, y, barWidth, barHeight);
154
163
  });
155
164
  ctx.fill(path);
156
165
  }
@@ -162,11 +171,14 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
162
171
  const path = new Path2D();
163
172
  const columnWidth = waveWidth / frequencyData.length;
164
173
  const boxSize = columnWidth * 0.9;
174
+ const verticalGap = boxSize * 0.2;
175
+ const maxBricks = Math.floor(waveHeight / (boxSize + verticalGap));
165
176
  frequencyData.forEach((value, i) => {
166
- const brickHeight = value / 255 * waveHeight;
167
- for (let j = 0; j <= brickHeight; j++) {
177
+ const normalizedValue = Math.min(value / 255 * 2, 1);
178
+ const brickCount = Math.floor(normalizedValue * maxBricks);
179
+ for (let j = 0; j < brickCount; j++) {
168
180
  const x = columnWidth * i;
169
- const y = waveHeight - (j * columnWidth + boxSize);
181
+ const y = waveHeight - (j + 1) * (boxSize + verticalGap);
170
182
  path.rect(x, y, boxSize, boxSize);
171
183
  }
172
184
  });
@@ -176,29 +188,28 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
176
188
  const canvas = ctx.canvas;
177
189
  const waveWidth = canvas.width;
178
190
  const waveHeight = canvas.height;
179
- const baseline = waveHeight / 4;
180
191
  const totalBars = frequencyData.length;
181
192
  const paddingInner = 0.5;
182
193
  const paddingOuter = 0.01;
183
- const availableWidth = waveWidth * (1 - 2 * paddingOuter);
194
+ const availableWidth = waveWidth;
184
195
  const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
185
196
  ctx.clearRect(0, 0, waveWidth, waveHeight);
186
197
  const path = new Path2D();
187
198
  frequencyData.forEach((value, i) => {
188
- const normalizedValue = value / 255;
189
- const height = normalizedValue * (waveHeight / 2);
199
+ const normalizedValue = Math.min(value / 255 * 2, 1);
200
+ const height = normalizedValue * waveHeight;
190
201
  const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
191
- const y = baseline - height;
192
- path.roundRect(x, y, barWidth, height * 2, barWidth / 2);
202
+ const y = (waveHeight - height) / 2;
203
+ path.roundRect(x, y, barWidth, height, barWidth / 2);
193
204
  });
194
205
  ctx.fill(path);
195
206
  }
196
207
  drawEqualizer(ctx, frequencyData) {
197
208
  const canvas = ctx.canvas;
198
209
  const waveWidth = canvas.width;
199
- const waveHeight = canvas.height / 2;
200
- const barWidth = waveWidth / frequencyData.length * 0.8;
210
+ const waveHeight = canvas.height;
201
211
  const baseline = waveHeight / 2;
212
+ const barWidth = waveWidth / frequencyData.length * 0.8;
202
213
  ctx.clearRect(0, 0, waveWidth, waveHeight);
203
214
  const baselinePath = new Path2D();
204
215
  const barsPath = new Path2D();
@@ -217,7 +228,7 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
217
228
  drawLine(ctx, frequencyData) {
218
229
  const canvas = ctx.canvas;
219
230
  const waveWidth = canvas.width;
220
- const waveHeight = canvas.height / 2;
231
+ const waveHeight = canvas.height;
221
232
  ctx.clearRect(0, 0, waveWidth, waveHeight);
222
233
  const path = new Path2D();
223
234
  frequencyData.forEach((value, i) => {
@@ -235,14 +246,15 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
235
246
  drawPixel(ctx, frequencyData) {
236
247
  const canvas = ctx.canvas;
237
248
  const waveWidth = canvas.width;
238
- const waveHeight = canvas.height / 2;
249
+ const waveHeight = canvas.height;
239
250
  const baseline = waveHeight / 2;
240
251
  const barWidth = waveWidth / frequencyData.length;
241
252
  ctx.clearRect(0, 0, waveWidth, waveHeight);
242
253
  const path = new Path2D();
243
254
  frequencyData.forEach((value, i) => {
255
+ const normalizedValue = Math.min(value / 255 * 2, 1);
244
256
  const x = i * (waveWidth / frequencyData.length);
245
- const barHeight = value / 255 * baseline;
257
+ const barHeight = normalizedValue * (waveHeight / 2);
246
258
  const y = baseline - barHeight;
247
259
  path.rect(x, y, barWidth, barHeight * 2);
248
260
  });
@@ -252,53 +264,110 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
252
264
  const canvas = ctx.canvas;
253
265
  const waveWidth = canvas.width;
254
266
  const waveHeight = canvas.height;
255
- const baseline = canvas.height / 4;
267
+ const paddingOuter = 0.01;
268
+ const availableWidth = waveWidth * (1 - 2 * paddingOuter);
269
+ const startX = waveWidth * paddingOuter;
256
270
  ctx.clearRect(0, 0, waveWidth, waveHeight);
257
271
  const path = new Path2D();
258
- path.moveTo(0, baseline);
272
+ const firstValue = Math.min((frequencyData[0] ?? 0) / 255 * 2, 1);
273
+ const firstY = (waveHeight - firstValue * waveHeight) / 2;
274
+ path.moveTo(startX, firstY);
259
275
  frequencyData.forEach((value, i) => {
260
- const normalizedValue = value / 255;
261
- const x = i / (frequencyData.length - 1) * waveWidth;
262
- const y = baseline - normalizedValue * (waveHeight / 2);
276
+ const normalizedValue = Math.min(value / 255 * 2, 1);
277
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
278
+ const barHeight = normalizedValue * waveHeight;
279
+ const y = (waveHeight - barHeight) / 2;
263
280
  if (i === 0) {
264
281
  path.moveTo(x, y);
265
282
  } else {
266
- const prevX = (i - 1) / (frequencyData.length - 1) * waveWidth;
283
+ const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
284
+ const prevValue = Math.min((frequencyData[i - 1] ?? 0) / 255 * 2, 1);
285
+ const prevBarHeight = prevValue * waveHeight;
286
+ const prevY = (waveHeight - prevBarHeight) / 2;
287
+ const xc = (prevX + x) / 2;
288
+ const yc = (prevY + y) / 2;
289
+ path.quadraticCurveTo(prevX, prevY, xc, yc);
290
+ }
291
+ });
292
+ for (let i = frequencyData.length - 1; i >= 0; i--) {
293
+ const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
294
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
295
+ const barHeight = normalizedValue * waveHeight;
296
+ const y = (waveHeight + barHeight) / 2;
297
+ if (i === frequencyData.length - 1) {
298
+ path.lineTo(x, y);
299
+ } else {
300
+ const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
301
+ const nextValue = Math.min((frequencyData[i + 1] ?? 0) / 255 * 2, 1);
302
+ const nextBarHeight = nextValue * waveHeight;
303
+ const nextY = (waveHeight + nextBarHeight) / 2;
304
+ const xc = (nextX + x) / 2;
305
+ const yc = (nextY + y) / 2;
306
+ path.quadraticCurveTo(nextX, nextY, xc, yc);
307
+ }
308
+ }
309
+ const lastY = (waveHeight + firstValue * waveHeight) / 2;
310
+ const controlX = startX;
311
+ const controlY = (lastY + firstY) / 2;
312
+ path.quadraticCurveTo(controlX, controlY, startX, firstY);
313
+ ctx.fill(path);
314
+ }
315
+ drawSpikes(ctx, frequencyData) {
316
+ const canvas = ctx.canvas;
317
+ const waveWidth = canvas.width;
318
+ const waveHeight = canvas.height;
319
+ const paddingOuter = 0.01;
320
+ const availableWidth = waveWidth * (1 - 2 * paddingOuter);
321
+ const startX = waveWidth * paddingOuter;
322
+ ctx.clearRect(0, 0, waveWidth, waveHeight);
323
+ const path = new Path2D();
324
+ const firstValue = (frequencyData[0] ?? 0) / 255;
325
+ const firstY = (waveHeight - firstValue * waveHeight) / 2;
326
+ path.moveTo(startX, firstY);
327
+ frequencyData.forEach((value, i) => {
328
+ const normalizedValue = Math.min(value / 255 * 2, 1);
329
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
330
+ const barHeight = normalizedValue * (waveHeight / 2);
331
+ const y = (waveHeight - barHeight * 2) / 2;
332
+ if (i === 0) {
333
+ path.moveTo(x, y);
334
+ } else {
335
+ const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
267
336
  const prevValue = (frequencyData[i - 1] ?? 0) / 255;
268
- const prevY = baseline - prevValue * (waveHeight / 2);
337
+ const prevBarHeight = prevValue * (waveHeight / 2);
338
+ const prevY = (waveHeight - prevBarHeight * 2) / 2;
269
339
  const xc = (prevX + x) / 2;
270
340
  const yc = (prevY + y) / 2;
271
341
  path.quadraticCurveTo(prevX, prevY, xc, yc);
272
342
  }
273
343
  });
274
344
  for (let i = frequencyData.length - 1; i >= 0; i--) {
275
- const normalizedValue = (frequencyData[i] ?? 0) / 255;
276
- const x = i / (frequencyData.length - 1) * waveWidth;
277
- const y = baseline + normalizedValue * (waveHeight / 2);
345
+ const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
346
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
347
+ const barHeight = normalizedValue * (waveHeight / 2);
348
+ const y = (waveHeight + barHeight * 2) / 2;
278
349
  if (i === frequencyData.length - 1) {
279
350
  path.lineTo(x, y);
280
351
  } else {
281
- const nextX = (i + 1) / (frequencyData.length - 1) * waveWidth;
352
+ const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
282
353
  const nextValue = (frequencyData[i + 1] ?? 0) / 255;
283
- const nextY = baseline + nextValue * (waveHeight / 2);
354
+ const nextBarHeight = nextValue * (waveHeight / 2);
355
+ const nextY = (waveHeight + nextBarHeight * 2) / 2;
284
356
  const xc = (nextX + x) / 2;
285
357
  const yc = (nextY + y) / 2;
286
358
  path.quadraticCurveTo(nextX, nextY, xc, yc);
287
359
  }
288
360
  }
361
+ const lastY = (waveHeight + firstValue * waveHeight) / 2;
362
+ const controlX = startX;
363
+ const controlY = (lastY + firstY) / 2;
364
+ path.quadraticCurveTo(controlX, controlY, startX, firstY);
289
365
  ctx.fill(path);
290
366
  }
291
367
  get durationMs() {
292
368
  if (!this.targetElement) return 0;
293
369
  return this.targetElement.durationMs;
294
370
  }
295
- get targetElement() {
296
- const target = document.getElementById(this.targetSelector ?? "");
297
- if (target instanceof EFAudio || target instanceof EFVideo) {
298
- return target;
299
- }
300
- return null;
301
- }
302
371
  updated(changedProperties) {
303
372
  super.updated(changedProperties);
304
373
  if (changedProperties.size > 0) {
@@ -331,8 +400,11 @@ __decorateClass([
331
400
  property({ type: String })
332
401
  ], EFWaveform.prototype, "color", 2);
333
402
  __decorateClass([
334
- property({ type: String, attribute: "target", reflect: true })
335
- ], EFWaveform.prototype, "targetSelector", 2);
403
+ property({ type: String, reflect: true })
404
+ ], EFWaveform.prototype, "target", 2);
405
+ __decorateClass([
406
+ state()
407
+ ], EFWaveform.prototype, "targetElement", 2);
336
408
  __decorateClass([
337
409
  property({ type: Number, attribute: "line-width" })
338
410
  ], EFWaveform.prototype, "lineWidth", 2);