@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,10 +1,12 @@
|
|
|
1
1
|
import { EFAudio } from "./EFAudio.js";
|
|
2
2
|
|
|
3
|
+
import { CSSStyleObserver } from "@bramus/style-observer";
|
|
3
4
|
import { Task } from "@lit/task";
|
|
4
5
|
import { LitElement, type PropertyValueMap, css, html } from "lit";
|
|
5
6
|
import { customElement, property } from "lit/decorators.js";
|
|
6
7
|
import { type Ref, createRef, ref } from "lit/directives/ref.js";
|
|
7
8
|
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
9
|
+
import { EF_RENDERING } from "../EF_RENDERING.js";
|
|
8
10
|
import { TWMixin } from "../gui/TWMixin.js";
|
|
9
11
|
import { CrossUpdateController } from "./CrossUpdateController.js";
|
|
10
12
|
import { EFTemporal } from "./EFTemporal.js";
|
|
@@ -30,6 +32,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
30
32
|
|
|
31
33
|
canvasRef: Ref<HTMLCanvasElement> = createRef();
|
|
32
34
|
private ctx: CanvasRenderingContext2D | null = null;
|
|
35
|
+
private styleObserver: CSSStyleObserver | null = null;
|
|
33
36
|
|
|
34
37
|
private resizeObserver?: ResizeObserver;
|
|
35
38
|
private mutationObserver?: MutationObserver;
|
|
@@ -42,15 +45,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
42
45
|
type: String,
|
|
43
46
|
attribute: "mode",
|
|
44
47
|
})
|
|
45
|
-
mode:
|
|
46
|
-
| "roundBars"
|
|
47
|
-
| "bars"
|
|
48
|
-
| "bricks"
|
|
49
|
-
| "equalizer"
|
|
50
|
-
| "curve"
|
|
51
|
-
| "line"
|
|
52
|
-
| "pixel"
|
|
53
|
-
| "wave" = "bars";
|
|
48
|
+
mode: "roundBars" | "bars" | "bricks" | "line" | "pixel" | "wave" = "bars";
|
|
54
49
|
|
|
55
50
|
@property({ type: String })
|
|
56
51
|
color = "currentColor";
|
|
@@ -58,14 +53,23 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
58
53
|
@property({ type: String, attribute: "target", reflect: true })
|
|
59
54
|
targetSelector = "";
|
|
60
55
|
|
|
56
|
+
@property({ type: Number, attribute: "line-width" })
|
|
57
|
+
lineWidth = 4;
|
|
58
|
+
|
|
61
59
|
set target(value: string) {
|
|
62
60
|
this.targetSelector = value;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
connectedCallback() {
|
|
66
64
|
super.connectedCallback();
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (this.targetElement) {
|
|
67
|
+
new CrossUpdateController(this.targetElement, this);
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// TODO: determine if this is a critical error, or if we should just ignore it
|
|
71
|
+
// currenty evidence suggests everything still works
|
|
72
|
+
// no target element, no cross update controller
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
// Initialize ResizeObserver
|
|
@@ -87,6 +91,13 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
87
91
|
|
|
88
92
|
// Observe attribute changes on the element
|
|
89
93
|
this.mutationObserver.observe(this, { attributes: true });
|
|
94
|
+
|
|
95
|
+
if (!EF_RENDERING()) {
|
|
96
|
+
this.styleObserver = new CSSStyleObserver(["color"], () => {
|
|
97
|
+
this.frameTask.run();
|
|
98
|
+
});
|
|
99
|
+
this.styleObserver.attach(this);
|
|
100
|
+
}
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
disconnectedCallback() {
|
|
@@ -94,6 +105,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
94
105
|
// Disconnect the observers when the element is removed from the DOM
|
|
95
106
|
this.resizeObserver?.disconnect();
|
|
96
107
|
this.mutationObserver?.disconnect();
|
|
108
|
+
this.styleObserver?.detach();
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
private resizeCanvas() {
|
|
@@ -107,7 +119,10 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
107
119
|
const canvas = this.canvasRef.value;
|
|
108
120
|
if (!canvas) return null;
|
|
109
121
|
|
|
110
|
-
const rect =
|
|
122
|
+
const rect = {
|
|
123
|
+
width: this.offsetWidth,
|
|
124
|
+
height: this.offsetHeight,
|
|
125
|
+
};
|
|
111
126
|
const dpr = window.devicePixelRatio;
|
|
112
127
|
|
|
113
128
|
canvas.style.width = `${rect.width}px`;
|
|
@@ -128,20 +143,34 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
128
143
|
|
|
129
144
|
protected drawBars(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {
|
|
130
145
|
const canvas = ctx.canvas;
|
|
131
|
-
const waveWidth = canvas.width
|
|
132
|
-
const waveHeight = canvas.height
|
|
133
|
-
const
|
|
134
|
-
|
|
146
|
+
const waveWidth = canvas.width;
|
|
147
|
+
const waveHeight = canvas.height;
|
|
148
|
+
const baseline = waveHeight / 4;
|
|
149
|
+
|
|
150
|
+
// Calculate bar width with padding
|
|
151
|
+
const totalBars = frequencyData.length;
|
|
152
|
+
const paddingInner = 0.5; // 50% padding between bars
|
|
153
|
+
const paddingOuter = 0.01; // 1% padding on edges
|
|
154
|
+
const availableWidth = waveWidth * (1 - 2 * paddingOuter);
|
|
155
|
+
const barWidth =
|
|
156
|
+
availableWidth / (totalBars + (totalBars - 1) * paddingInner);
|
|
135
157
|
|
|
136
158
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
137
159
|
|
|
160
|
+
// Create a single Path2D object for all bars
|
|
161
|
+
const path = new Path2D();
|
|
162
|
+
|
|
138
163
|
frequencyData.forEach((value, i) => {
|
|
139
|
-
const
|
|
140
|
-
const
|
|
164
|
+
const normalizedValue = value / 255;
|
|
165
|
+
const height = normalizedValue * (waveHeight / 2);
|
|
166
|
+
const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
|
|
141
167
|
const y = baseline - height;
|
|
142
168
|
|
|
143
|
-
|
|
169
|
+
path.rect(x, y, barWidth, height * 2);
|
|
144
170
|
});
|
|
171
|
+
|
|
172
|
+
// Single fill operation for all bars
|
|
173
|
+
ctx.fill(path);
|
|
145
174
|
}
|
|
146
175
|
|
|
147
176
|
protected drawBricks(
|
|
@@ -149,55 +178,23 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
149
178
|
frequencyData: Uint8Array,
|
|
150
179
|
) {
|
|
151
180
|
const canvas = ctx.canvas;
|
|
152
|
-
const waveWidth = canvas.width
|
|
153
|
-
const waveHeight = canvas.height
|
|
154
|
-
const brickWidth = waveWidth / frequencyData.length;
|
|
155
|
-
const brickHeightFactor = waveHeight / 255 / 2;
|
|
156
|
-
const brickPadding = 2;
|
|
157
|
-
const midHeight = waveHeight / 2;
|
|
158
|
-
|
|
181
|
+
const waveWidth = canvas.width;
|
|
182
|
+
const waveHeight = canvas.height;
|
|
159
183
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
184
|
+
const path = new Path2D();
|
|
160
185
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
ctx.setLineDash([2]);
|
|
164
|
-
ctx.lineWidth = 4;
|
|
165
|
-
ctx.moveTo(0, midHeight);
|
|
166
|
-
ctx.lineTo(waveWidth, midHeight);
|
|
167
|
-
ctx.stroke();
|
|
168
|
-
ctx.setLineDash([]); // Reset dash
|
|
169
|
-
|
|
170
|
-
// Draw bricks
|
|
186
|
+
const columnWidth = waveWidth / frequencyData.length;
|
|
187
|
+
const boxSize = columnWidth * 0.9;
|
|
171
188
|
frequencyData.forEach((value, i) => {
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {
|
|
181
|
-
const canvas = ctx.canvas;
|
|
182
|
-
const waveWidth = canvas.width / devicePixelRatio;
|
|
183
|
-
const waveHeight = canvas.height / devicePixelRatio;
|
|
184
|
-
|
|
185
|
-
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
186
|
-
ctx.beginPath();
|
|
187
|
-
|
|
188
|
-
frequencyData.forEach((value, i) => {
|
|
189
|
-
const x = (i / frequencyData.length) * waveWidth;
|
|
190
|
-
const y = (1 - value / 255) * waveHeight;
|
|
191
|
-
|
|
192
|
-
if (i === 0) {
|
|
193
|
-
ctx.moveTo(x, y);
|
|
194
|
-
} else {
|
|
195
|
-
ctx.lineTo(x, y);
|
|
189
|
+
const brickHeight = (value / 255) * waveHeight;
|
|
190
|
+
for (let j = 0; j <= brickHeight; j++) {
|
|
191
|
+
const x = columnWidth * i;
|
|
192
|
+
const y = waveHeight - (j * columnWidth + boxSize);
|
|
193
|
+
path.rect(x, y, boxSize, boxSize);
|
|
196
194
|
}
|
|
197
195
|
});
|
|
198
196
|
|
|
199
|
-
ctx.
|
|
200
|
-
ctx.stroke();
|
|
197
|
+
ctx.fill(path);
|
|
201
198
|
}
|
|
202
199
|
|
|
203
200
|
protected drawRoundBars(
|
|
@@ -205,25 +202,35 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
205
202
|
frequencyData: Uint8Array,
|
|
206
203
|
) {
|
|
207
204
|
const canvas = ctx.canvas;
|
|
208
|
-
const waveWidth = canvas.width
|
|
209
|
-
const waveHeight = canvas.height
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
const waveWidth = canvas.width;
|
|
206
|
+
const waveHeight = canvas.height;
|
|
207
|
+
const baseline = waveHeight / 4;
|
|
208
|
+
|
|
209
|
+
// Similar padding calculation as drawBars
|
|
210
|
+
const totalBars = frequencyData.length;
|
|
211
|
+
const paddingInner = 0.5;
|
|
212
|
+
const paddingOuter = 0.01;
|
|
213
|
+
const availableWidth = waveWidth * (1 - 2 * paddingOuter);
|
|
214
|
+
const barWidth =
|
|
215
|
+
availableWidth / (totalBars + (totalBars - 1) * paddingInner);
|
|
213
216
|
|
|
214
217
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
215
218
|
|
|
219
|
+
// Create a single Path2D object for all rounded bars
|
|
220
|
+
const path = new Path2D();
|
|
221
|
+
|
|
216
222
|
frequencyData.forEach((value, i) => {
|
|
217
|
-
const
|
|
218
|
-
const
|
|
223
|
+
const normalizedValue = value / 255;
|
|
224
|
+
const height = normalizedValue * (waveHeight / 2);
|
|
225
|
+
const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
|
|
219
226
|
const y = baseline - height;
|
|
220
227
|
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
ctx.beginPath();
|
|
224
|
-
ctx.roundRect(x, y, barWidth, Math.max(height * 2, 2), radius);
|
|
225
|
-
ctx.fill();
|
|
228
|
+
// Add rounded rectangle to path
|
|
229
|
+
path.roundRect(x, y, barWidth, height * 2, barWidth / 2);
|
|
226
230
|
});
|
|
231
|
+
|
|
232
|
+
// Single fill operation for all bars
|
|
233
|
+
ctx.fill(path);
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
protected drawEqualizer(
|
|
@@ -231,54 +238,60 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
231
238
|
frequencyData: Uint8Array,
|
|
232
239
|
) {
|
|
233
240
|
const canvas = ctx.canvas;
|
|
234
|
-
const waveWidth = canvas.width
|
|
235
|
-
const waveHeight = canvas.height /
|
|
241
|
+
const waveWidth = canvas.width;
|
|
242
|
+
const waveHeight = canvas.height / 2;
|
|
236
243
|
const barWidth = (waveWidth / frequencyData.length) * 0.8;
|
|
237
244
|
const baseline = waveHeight / 2;
|
|
238
245
|
|
|
239
246
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
240
247
|
|
|
248
|
+
// Create paths for baseline and bars
|
|
249
|
+
const baselinePath = new Path2D();
|
|
250
|
+
const barsPath = new Path2D();
|
|
251
|
+
|
|
241
252
|
// Draw baseline
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
ctx.moveTo(0, baseline);
|
|
245
|
-
ctx.lineTo(waveWidth, baseline);
|
|
246
|
-
ctx.stroke();
|
|
253
|
+
baselinePath.moveTo(0, baseline);
|
|
254
|
+
baselinePath.lineTo(waveWidth, baseline);
|
|
247
255
|
|
|
248
256
|
// Draw bars
|
|
249
257
|
frequencyData.forEach((value, i) => {
|
|
250
258
|
const height = (value / 255) * (waveHeight / 2);
|
|
251
259
|
const x = i * (waveWidth / frequencyData.length);
|
|
252
260
|
const y = baseline - height;
|
|
253
|
-
|
|
254
|
-
ctx.fillRect(x, y, barWidth, Math.max(height * 2, 1));
|
|
261
|
+
barsPath.rect(x, y, barWidth, Math.max(height * 2, 1));
|
|
255
262
|
});
|
|
263
|
+
|
|
264
|
+
// Render baseline
|
|
265
|
+
ctx.lineWidth = 2;
|
|
266
|
+
ctx.stroke(baselinePath);
|
|
267
|
+
|
|
268
|
+
// Render bars
|
|
269
|
+
ctx.fill(barsPath);
|
|
256
270
|
}
|
|
257
271
|
|
|
258
|
-
protected
|
|
259
|
-
ctx: CanvasRenderingContext2D,
|
|
260
|
-
frequencyData: Uint8Array,
|
|
261
|
-
) {
|
|
272
|
+
protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {
|
|
262
273
|
const canvas = ctx.canvas;
|
|
263
|
-
const waveWidth = canvas.width
|
|
264
|
-
const waveHeight = canvas.height /
|
|
274
|
+
const waveWidth = canvas.width;
|
|
275
|
+
const waveHeight = canvas.height / 2;
|
|
265
276
|
|
|
266
277
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
267
|
-
|
|
278
|
+
|
|
279
|
+
// Create a single Path2D object for the curve
|
|
280
|
+
const path = new Path2D();
|
|
268
281
|
|
|
269
282
|
frequencyData.forEach((value, i) => {
|
|
270
283
|
const x = (i / frequencyData.length) * waveWidth;
|
|
271
284
|
const y = (1 - value / 255) * waveHeight;
|
|
272
285
|
|
|
273
286
|
if (i === 0) {
|
|
274
|
-
|
|
287
|
+
path.moveTo(x, y);
|
|
275
288
|
} else {
|
|
276
|
-
|
|
289
|
+
path.lineTo(x, y);
|
|
277
290
|
}
|
|
278
291
|
});
|
|
279
292
|
|
|
280
|
-
ctx.lineWidth =
|
|
281
|
-
ctx.stroke();
|
|
293
|
+
ctx.lineWidth = this.lineWidth;
|
|
294
|
+
ctx.stroke(path);
|
|
282
295
|
}
|
|
283
296
|
|
|
284
297
|
protected drawPixel(
|
|
@@ -286,132 +299,122 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
286
299
|
frequencyData: Uint8Array,
|
|
287
300
|
) {
|
|
288
301
|
const canvas = ctx.canvas;
|
|
289
|
-
const waveWidth = canvas.width
|
|
290
|
-
const waveHeight = canvas.height /
|
|
302
|
+
const waveWidth = canvas.width;
|
|
303
|
+
const waveHeight = canvas.height / 2;
|
|
291
304
|
const baseline = waveHeight / 2;
|
|
292
|
-
const barWidth =
|
|
305
|
+
const barWidth = waveWidth / frequencyData.length;
|
|
293
306
|
|
|
294
307
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
295
308
|
|
|
309
|
+
// Create a single Path2D object for all pixels
|
|
310
|
+
const path = new Path2D();
|
|
311
|
+
|
|
296
312
|
frequencyData.forEach((value, i) => {
|
|
297
313
|
const x = i * (waveWidth / frequencyData.length);
|
|
298
314
|
const barHeight = (value / 255) * baseline;
|
|
299
315
|
const y = baseline - barHeight;
|
|
300
316
|
|
|
301
|
-
|
|
317
|
+
path.rect(x, y, barWidth, barHeight * 2);
|
|
302
318
|
});
|
|
319
|
+
|
|
320
|
+
// Single fill operation for all pixels
|
|
321
|
+
ctx.fill(path);
|
|
303
322
|
}
|
|
304
323
|
|
|
305
324
|
protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {
|
|
306
325
|
const canvas = ctx.canvas;
|
|
307
|
-
const waveWidth = canvas.width
|
|
308
|
-
const waveHeight = canvas.height
|
|
309
|
-
const baseline =
|
|
310
|
-
const barWidth = (waveWidth / frequencyData.length) * 0.97; // Account for padding
|
|
326
|
+
const waveWidth = canvas.width;
|
|
327
|
+
const waveHeight = canvas.height;
|
|
328
|
+
const baseline = canvas.height / 4;
|
|
311
329
|
|
|
312
330
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
331
|
+
const path = new Path2D();
|
|
332
|
+
path.moveTo(0, baseline);
|
|
313
333
|
|
|
314
|
-
// Draw
|
|
315
|
-
ctx.beginPath();
|
|
316
|
-
ctx.moveTo(0, baseline);
|
|
317
|
-
ctx.lineTo(waveWidth, baseline);
|
|
318
|
-
ctx.strokeStyle = this.color;
|
|
319
|
-
ctx.lineWidth = 1;
|
|
320
|
-
ctx.stroke();
|
|
321
|
-
|
|
322
|
-
// Draw bars
|
|
334
|
+
// Draw top curve
|
|
323
335
|
frequencyData.forEach((value, i) => {
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const y = baseline -
|
|
336
|
+
const normalizedValue = value / 255;
|
|
337
|
+
const x = (i / (frequencyData.length - 1)) * waveWidth;
|
|
338
|
+
const y = baseline - normalizedValue * (waveHeight / 2);
|
|
327
339
|
|
|
328
|
-
|
|
340
|
+
if (i === 0) {
|
|
341
|
+
path.moveTo(x, y);
|
|
342
|
+
} else {
|
|
343
|
+
const prevX = ((i - 1) / (frequencyData.length - 1)) * waveWidth;
|
|
344
|
+
const prevValue = (frequencyData[i - 1] ?? 0) / 255;
|
|
345
|
+
const prevY = baseline - prevValue * (waveHeight / 2);
|
|
346
|
+
const xc = (prevX + x) / 2;
|
|
347
|
+
const yc = (prevY + y) / 2;
|
|
348
|
+
path.quadraticCurveTo(prevX, prevY, xc, yc);
|
|
349
|
+
}
|
|
329
350
|
});
|
|
351
|
+
|
|
352
|
+
// Draw bottom curve
|
|
353
|
+
for (let i = frequencyData.length - 1; i >= 0; i--) {
|
|
354
|
+
const normalizedValue = (frequencyData[i] ?? 0) / 255;
|
|
355
|
+
const x = (i / (frequencyData.length - 1)) * waveWidth;
|
|
356
|
+
const y = baseline + normalizedValue * (waveHeight / 2);
|
|
357
|
+
|
|
358
|
+
if (i === frequencyData.length - 1) {
|
|
359
|
+
path.lineTo(x, y);
|
|
360
|
+
} else {
|
|
361
|
+
const nextX = ((i + 1) / (frequencyData.length - 1)) * waveWidth;
|
|
362
|
+
const nextValue = (frequencyData[i + 1] ?? 0) / 255;
|
|
363
|
+
const nextY = baseline + nextValue * (waveHeight / 2);
|
|
364
|
+
const xc = (nextX + x) / 2;
|
|
365
|
+
const yc = (nextY + y) / 2;
|
|
366
|
+
path.quadraticCurveTo(nextX, nextY, xc, yc);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
ctx.fill(path);
|
|
330
371
|
}
|
|
331
372
|
|
|
332
373
|
frameTask = new Task(this, {
|
|
333
374
|
autoRun: EF_INTERACTIVE,
|
|
334
|
-
args: () => [this.targetElement.
|
|
375
|
+
args: () => [this.targetElement?.frequencyDataTask.status] as const,
|
|
335
376
|
task: async () => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// every frame, which blanks the canvas, causing flicker.
|
|
377
|
+
if (!this.targetElement) return;
|
|
378
|
+
await this.targetElement.frequencyDataTask.taskComplete;
|
|
339
379
|
this.ctx ||= this.initCanvas();
|
|
340
380
|
const ctx = this.ctx;
|
|
341
381
|
if (!ctx) return;
|
|
342
382
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (this.targetElement.trimAdjustedOwnCurrentTimeMs > 0) {
|
|
346
|
-
const FRAME_DURATION_MS = 48000 / 1000;
|
|
347
|
-
const FRAME_SMEAR_MS = FRAME_DURATION_MS * 1;
|
|
348
|
-
const FRAME_SMEAR_S = FRAME_SMEAR_MS / 1000;
|
|
349
|
-
|
|
350
|
-
const audioContext = new OfflineAudioContext(2, 48000 / 25, 48000);
|
|
351
|
-
const audioBufferSource = audioContext.createBufferSource();
|
|
352
|
-
audioBufferSource.buffer =
|
|
353
|
-
this.targetElement.audioBufferTask.value.buffer;
|
|
354
|
-
const analyser = audioContext.createAnalyser();
|
|
355
|
-
|
|
356
|
-
// Adjust FFT size for better frequency resolution
|
|
357
|
-
analyser.fftSize = 128 * 8;
|
|
383
|
+
const frequencyData = this.targetElement.frequencyDataTask.value;
|
|
384
|
+
if (!frequencyData) return;
|
|
358
385
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
1000,
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
audioBufferSource.start(0, startTime, FRAME_SMEAR_S);
|
|
369
|
-
await audioContext.startRendering();
|
|
370
|
-
|
|
371
|
-
const frameData = new Uint8Array(analyser.frequencyBinCount);
|
|
372
|
-
analyser.getByteFrequencyData(frameData);
|
|
373
|
-
|
|
374
|
-
const smoothedData = frameData.slice(0, frameData.length / 2);
|
|
375
|
-
|
|
376
|
-
if (this.color === "currentColor") {
|
|
377
|
-
const computedStyle = getComputedStyle(this);
|
|
378
|
-
const currentColor = computedStyle.color;
|
|
379
|
-
ctx.strokeStyle = currentColor;
|
|
380
|
-
ctx.fillStyle = currentColor;
|
|
381
|
-
} else {
|
|
382
|
-
}
|
|
386
|
+
if (this.color === "currentColor") {
|
|
387
|
+
const computedStyle = getComputedStyle(this);
|
|
388
|
+
const currentColor = computedStyle.color;
|
|
389
|
+
ctx.strokeStyle = currentColor;
|
|
390
|
+
ctx.fillStyle = currentColor;
|
|
391
|
+
}
|
|
383
392
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
case "roundBars":
|
|
404
|
-
this.drawRoundBars(ctx, smoothedData);
|
|
405
|
-
break;
|
|
406
|
-
case "equalizer":
|
|
407
|
-
this.drawEqualizer(ctx, smoothedData);
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
393
|
+
switch (this.mode) {
|
|
394
|
+
case "bars":
|
|
395
|
+
this.drawBars(ctx, frequencyData);
|
|
396
|
+
break;
|
|
397
|
+
case "bricks":
|
|
398
|
+
this.drawBricks(ctx, frequencyData);
|
|
399
|
+
break;
|
|
400
|
+
case "line":
|
|
401
|
+
this.drawLine(ctx, frequencyData);
|
|
402
|
+
break;
|
|
403
|
+
case "pixel":
|
|
404
|
+
this.drawPixel(ctx, frequencyData);
|
|
405
|
+
break;
|
|
406
|
+
case "wave":
|
|
407
|
+
this.drawWave(ctx, frequencyData);
|
|
408
|
+
break;
|
|
409
|
+
case "roundBars":
|
|
410
|
+
this.drawRoundBars(ctx, frequencyData);
|
|
411
|
+
break;
|
|
410
412
|
}
|
|
411
413
|
},
|
|
412
414
|
});
|
|
413
415
|
|
|
414
416
|
get durationMs() {
|
|
417
|
+
if (!this.targetElement) return 0;
|
|
415
418
|
return this.targetElement.durationMs;
|
|
416
419
|
}
|
|
417
420
|
|
|
@@ -420,7 +423,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
420
423
|
if (target instanceof EFAudio || target instanceof EFVideo) {
|
|
421
424
|
return target;
|
|
422
425
|
}
|
|
423
|
-
|
|
426
|
+
return null;
|
|
424
427
|
}
|
|
425
428
|
|
|
426
429
|
protected updated(changedProperties: PropertyValueMap<this>): void {
|
package/src/gui/EFWorkbench.ts
CHANGED
|
@@ -107,7 +107,7 @@ export class EFWorkbench extends ContextMixin(TWMixin(LitElement)) {
|
|
|
107
107
|
focusOverlay.style.display = "block";
|
|
108
108
|
const rect = this.focusedElement.getBoundingClientRect();
|
|
109
109
|
Object.assign(focusOverlay.style, {
|
|
110
|
-
position: "
|
|
110
|
+
position: "fixed",
|
|
111
111
|
top: `${rect.top}px`,
|
|
112
112
|
left: `${rect.left}px`,
|
|
113
113
|
width: `${rect.width}px`,
|