@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.
- package/dist/EF_FRAMEGEN.js +0 -2
- package/dist/elements/EFAudio.d.ts +0 -1
- package/dist/elements/EFAudio.js +1 -5
- package/dist/elements/EFCaptions.js +1 -1
- package/dist/elements/EFMedia.d.ts +1 -1
- package/dist/elements/EFMedia.js +37 -14
- package/dist/elements/EFTemporal.d.ts +3 -3
- package/dist/elements/EFTemporal.js +6 -2
- package/dist/elements/EFTimegroup.d.ts +1 -5
- package/dist/elements/EFTimegroup.js +4 -5
- package/dist/elements/EFWaveform.d.ts +13 -6
- package/dist/elements/EFWaveform.js +122 -50
- package/dist/elements/TargetController.d.ts +25 -0
- package/dist/elements/TargetController.js +164 -0
- package/dist/elements/TargetController.test.d.ts +19 -0
- package/dist/gui/EFPreview.d.ts +1 -1
- package/dist/gui/EFPreview.js +1 -0
- package/package.json +2 -2
- package/src/elements/EFAudio.ts +1 -4
- package/src/elements/EFCaptions.ts +1 -1
- package/src/elements/EFMedia.ts +41 -22
- package/src/elements/EFTemporal.ts +10 -10
- package/src/elements/EFTimegroup.ts +4 -9
- package/src/elements/EFWaveform.ts +161 -66
- package/src/elements/TargetController.test.ts +229 -0
- package/src/elements/TargetController.ts +219 -0
- package/src/gui/EFPreview.ts +10 -9
package/dist/EF_FRAMEGEN.js
CHANGED
|
@@ -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>;
|
package/dist/elements/EFAudio.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
2
|
import { html } from "lit";
|
|
3
|
-
import {
|
|
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.
|
|
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 {};
|
package/dist/elements/EFMedia.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
60
|
-
|
|
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: () => [
|
|
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.
|
|
250
|
-
const currentTimeMs = this.
|
|
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}
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
-
* `
|
|
138
|
+
* `currentSourceTimeMs` will be 10s.
|
|
139
139
|
*
|
|
140
140
|
* sourcein=10s sourceout=10s
|
|
141
141
|
* / / /
|
|
142
142
|
* |--------|=================|---------|
|
|
143
143
|
* ^
|
|
144
144
|
* |_
|
|
145
|
-
*
|
|
145
|
+
* currentSourceTimeMs === 10s
|
|
146
146
|
* |_
|
|
147
147
|
* ownCurrentTimeMs === 0s
|
|
148
148
|
*/
|
|
149
|
-
get
|
|
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
|
|
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
|
|
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 = "
|
|
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
|
-
|
|
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
|
-
|
|
18
|
+
target: string;
|
|
19
|
+
targetElement: EFAudio | EFVideo | null;
|
|
18
20
|
lineWidth: number;
|
|
19
|
-
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
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: () =>
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
167
|
-
|
|
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 *
|
|
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
|
|
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 *
|
|
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 =
|
|
192
|
-
path.roundRect(x, y, barWidth, height
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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) *
|
|
262
|
-
const
|
|
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) *
|
|
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
|
|
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) *
|
|
277
|
-
const
|
|
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) *
|
|
352
|
+
const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
|
|
282
353
|
const nextValue = (frequencyData[i + 1] ?? 0) / 255;
|
|
283
|
-
const
|
|
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,
|
|
335
|
-
], EFWaveform.prototype, "
|
|
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);
|