@xsynaptic/sonic-ui 0.16.0 → 0.18.0
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/CHANGELOG.md +20 -0
- package/README.md +112 -19
- package/dist/default/dat.js +1 -0
- package/dist/default/elements/button.js +2 -0
- package/dist/default/elements/dial.js +2 -0
- package/dist/default/elements/display-element.js +9 -7
- package/dist/default/elements/envelope.js +31 -72
- package/dist/default/elements/field-gesture.js +81 -0
- package/dist/default/elements/meter.js +4 -0
- package/dist/default/elements/position-group.js +42 -26
- package/dist/default/elements/radio-group.js +59 -56
- package/dist/default/elements/scale.js +3 -3
- package/dist/default/elements/slider.js +11 -13
- package/dist/default/elements/split.js +14 -13
- package/dist/default/elements/switch.js +31 -46
- package/dist/default/elements/toggle.js +20 -44
- package/dist/default/elements/value-element.js +65 -91
- package/dist/default/elements/value-gestures.js +22 -22
- package/dist/default/elements/wave-element.js +9 -12
- package/dist/default/elements/waveform.js +64 -249
- package/dist/default/elements/wavestrip.js +10 -5
- package/dist/default/elements/xy.js +67 -112
- package/dist/default/lib/canvas-surface.js +25 -0
- package/dist/default/lib/crossfade.js +1 -0
- package/dist/default/lib/envelope-curve.js +1 -0
- package/dist/default/lib/marker-rider.js +37 -14
- package/dist/default/lib/peaks-request.js +44 -0
- package/dist/default/lib/positions.js +58 -0
- package/dist/default/lib/scrub.js +12 -0
- package/dist/default/lib/value-model.js +62 -0
- package/dist/default/lib/waveform-scene.js +108 -0
- package/dist/default/lib/zoom-gesture.js +97 -1
- package/dist/dev/dat.d.ts +1 -0
- package/dist/dev/dat.js +1 -0
- package/dist/dev/dat.js.map +1 -1
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +2 -0
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +2 -0
- package/dist/dev/elements/dial.js +2 -0
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/display-element.d.ts +5 -3
- package/dist/dev/elements/display-element.js +9 -7
- package/dist/dev/elements/display-element.js.map +1 -1
- package/dist/dev/elements/envelope.js +31 -72
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +82 -0
- package/dist/dev/elements/field-gesture.js.map +1 -0
- package/dist/dev/elements/meter.d.ts +10 -5
- package/dist/dev/elements/meter.js +4 -0
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/position-group.d.ts +12 -6
- package/dist/dev/elements/position-group.js +42 -26
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +11 -7
- package/dist/dev/elements/radio-group.js +59 -56
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/scale.js +3 -3
- package/dist/dev/elements/scale.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +11 -13
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/split.d.ts +1 -0
- package/dist/dev/elements/split.js +14 -13
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +2 -2
- package/dist/dev/elements/switch.js +31 -46
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/toggle.js +20 -44
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +5 -5
- package/dist/dev/elements/value-element.js +65 -91
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +22 -22
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +7 -4
- package/dist/dev/elements/wave-element.js +9 -12
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +23 -22
- package/dist/dev/elements/waveform.js +64 -249
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +5 -1
- package/dist/dev/elements/wavestrip.js +10 -5
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +67 -112
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +3 -2
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +25 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/crossfade.d.ts +1 -0
- package/dist/dev/lib/crossfade.js +1 -0
- package/dist/dev/lib/crossfade.js.map +1 -1
- package/dist/dev/lib/envelope-curve.d.ts +1 -0
- package/dist/dev/lib/envelope-curve.js +1 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -1
- package/dist/dev/lib/marker-rider.js +37 -14
- package/dist/dev/lib/marker-rider.js.map +1 -1
- package/dist/dev/lib/peaks-request.d.ts +5 -0
- package/dist/dev/lib/peaks-request.js +45 -0
- package/dist/dev/lib/peaks-request.js.map +1 -0
- package/dist/dev/lib/positions.d.ts +7 -0
- package/dist/dev/lib/positions.js +59 -0
- package/dist/dev/lib/positions.js.map +1 -0
- package/dist/dev/lib/scrub.d.ts +8 -0
- package/dist/dev/lib/scrub.js +13 -0
- package/dist/dev/lib/scrub.js.map +1 -0
- package/dist/dev/lib/time-regions.d.ts +1 -0
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/dev/lib/units.d.ts +8 -0
- package/dist/dev/lib/value-model.js +63 -0
- package/dist/dev/lib/value-model.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +1 -0
- package/dist/dev/lib/waveform-buckets.js.map +1 -1
- package/dist/dev/lib/waveform-scene.d.ts +11 -0
- package/dist/dev/lib/waveform-scene.js +109 -0
- package/dist/dev/lib/waveform-scene.js.map +1 -0
- package/dist/dev/lib/zoom-gesture.js +97 -1
- package/dist/dev/lib/zoom-gesture.js.map +1 -1
- package/dist/styles/button.css +16 -6
- package/dist/styles/waveform.css +12 -4
- package/package.json +1 -1
- package/dist/default/lib/asked-value.js +0 -21
- package/dist/default/lib/fill-watch.js +0 -29
- package/dist/dev/lib/asked-value.js +0 -22
- package/dist/dev/lib/asked-value.js.map +0 -1
- package/dist/dev/lib/fill-watch.js +0 -30
- package/dist/dev/lib/fill-watch.js.map +0 -1
|
@@ -1,36 +1,19 @@
|
|
|
1
1
|
import { clamp, roundTo } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
|
-
import { capturePointer } from "../lib/pointer-drag.js";
|
|
4
3
|
import { dueRegions, readRegions } from "../lib/time-regions.js";
|
|
5
4
|
import { SonicWaveElement } from "./wave-element.js";
|
|
6
5
|
import { frameTimeline } from "../lib/frame-timeline.js";
|
|
7
6
|
import { isKind, writeKind } from "../lib/marker-band.js";
|
|
8
7
|
import { createLabelRider } from "../lib/marker-rider.js";
|
|
8
|
+
import { createPeaksRequest } from "../lib/peaks-request.js";
|
|
9
9
|
import { createTrackingClock } from "../lib/tracking-clock.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { paintWaveform, waveformColours } from "../lib/waveform-scene.js";
|
|
11
|
+
import { bindZoom, bindZoomKeys } from "../lib/zoom-gesture.js";
|
|
12
12
|
|
|
13
13
|
//#region src/elements/waveform.ts
|
|
14
|
-
const colours = {
|
|
15
|
-
ends: "--_sonic-waveform-ends",
|
|
16
|
-
grid: "--_sonic-waveform-grid",
|
|
17
|
-
marker: "--_sonic-waveform-marker",
|
|
18
|
-
placeholder: "--_sonic-waveform-placeholder",
|
|
19
|
-
wave: "--_sonic-waveform-wave",
|
|
20
|
-
waveEdge: "--_sonic-waveform-wave-edge"
|
|
21
|
-
};
|
|
22
14
|
const defaultZoom = 70;
|
|
23
15
|
const defaultZoomMin = 20;
|
|
24
16
|
const defaultZoomMax = 280;
|
|
25
|
-
const zoomKeys = /* @__PURE__ */ new Map([
|
|
26
|
-
["+", 1],
|
|
27
|
-
["-", -1],
|
|
28
|
-
["=", 1]
|
|
29
|
-
]);
|
|
30
|
-
const amplitudeMargin = .94;
|
|
31
|
-
const hatchPitchPx = 8;
|
|
32
|
-
const placeholderWavelengthPx = 48;
|
|
33
|
-
const placeholderAmplitude = .2;
|
|
34
17
|
const renderWaveform = template(`
|
|
35
18
|
<div class="sonic-waveform" role="slider" tabindex="0">
|
|
36
19
|
<canvas class="sonic-waveform-canvas" aria-hidden="true"></canvas>
|
|
@@ -59,66 +42,6 @@ function placeLine(line, at) {
|
|
|
59
42
|
const translate = `${String(at * 100)}cqi`;
|
|
60
43
|
if (line.style.getPropertyValue("translate") !== translate) line.style.setProperty("translate", translate);
|
|
61
44
|
}
|
|
62
|
-
function paintHatch(context, scene, [from, to]) {
|
|
63
|
-
if (to <= from) return;
|
|
64
|
-
const { dpr, height } = scene;
|
|
65
|
-
const pitch = Math.max(2, Math.round(hatchPitchPx * dpr));
|
|
66
|
-
const offset = scene.startSeconds * scene.pixelsPerSecond;
|
|
67
|
-
context.save();
|
|
68
|
-
context.beginPath();
|
|
69
|
-
context.rect(from, 0, to - from, height);
|
|
70
|
-
context.clip();
|
|
71
|
-
context.beginPath();
|
|
72
|
-
for (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {
|
|
73
|
-
context.moveTo(x, height);
|
|
74
|
-
context.lineTo(x + height, 0);
|
|
75
|
-
}
|
|
76
|
-
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
77
|
-
context.strokeStyle = scene.colours.ends;
|
|
78
|
-
context.stroke();
|
|
79
|
-
context.restore();
|
|
80
|
-
}
|
|
81
|
-
function paintPlaceholders(context, scene) {
|
|
82
|
-
if (scene.pending.length === 0) return;
|
|
83
|
-
const { dpr, height, pixelsPerSecond, startSeconds } = scene;
|
|
84
|
-
const wavelength = placeholderWavelengthPx * dpr;
|
|
85
|
-
const center = height / 2;
|
|
86
|
-
const origin = startSeconds * pixelsPerSecond;
|
|
87
|
-
const waveY = (x) => center - Math.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) * center * placeholderAmplitude;
|
|
88
|
-
context.beginPath();
|
|
89
|
-
for (const [from, to] of scene.pending) {
|
|
90
|
-
const opening = (from - startSeconds) * pixelsPerSecond;
|
|
91
|
-
const closing = (to - startSeconds) * pixelsPerSecond;
|
|
92
|
-
context.moveTo(opening, waveY(opening));
|
|
93
|
-
for (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));
|
|
94
|
-
context.lineTo(closing, waveY(closing));
|
|
95
|
-
}
|
|
96
|
-
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
97
|
-
context.strokeStyle = scene.colours.placeholder;
|
|
98
|
-
context.stroke();
|
|
99
|
-
}
|
|
100
|
-
function waveFill(context, scene) {
|
|
101
|
-
const { wave, waveEdge } = scene.colours;
|
|
102
|
-
const gradient = context.createLinearGradient(0, 0, 0, scene.height);
|
|
103
|
-
gradient.addColorStop(0, waveEdge);
|
|
104
|
-
gradient.addColorStop(.5, wave);
|
|
105
|
-
gradient.addColorStop(1, waveEdge);
|
|
106
|
-
return gradient;
|
|
107
|
-
}
|
|
108
|
-
function paintWave(context, scene, peaks) {
|
|
109
|
-
const buckets = waveformBuckets(peaks, scene);
|
|
110
|
-
const [first] = buckets;
|
|
111
|
-
if (!first) return;
|
|
112
|
-
const center = scene.height / 2;
|
|
113
|
-
const amplitudePx = center * amplitudeMargin;
|
|
114
|
-
context.fillStyle = waveFill(context, scene);
|
|
115
|
-
context.beginPath();
|
|
116
|
-
context.moveTo(first.x, center);
|
|
117
|
-
for (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);
|
|
118
|
-
for (const bucket of buckets.toReversed()) context.lineTo(bucket.x, center - bucket.low * amplitudePx);
|
|
119
|
-
context.closePath();
|
|
120
|
-
context.fill();
|
|
121
|
-
}
|
|
122
45
|
var SonicWaveform = class extends SonicWaveElement {
|
|
123
46
|
static observedAttributes = [
|
|
124
47
|
...SonicWaveElement.observedAttributes,
|
|
@@ -137,8 +60,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
137
60
|
set peaks(peaks) {
|
|
138
61
|
this.#peaks = peaks;
|
|
139
62
|
this.renderEmpty();
|
|
140
|
-
this.#
|
|
63
|
+
this.#peaksRequest.changed();
|
|
141
64
|
}
|
|
65
|
+
/** Spans being fetched; drawn as placeholders until cleared */
|
|
142
66
|
get pending() {
|
|
143
67
|
return this.#pending.map(([start, end]) => [start, end]);
|
|
144
68
|
}
|
|
@@ -152,8 +76,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
152
76
|
const key = region.join(":");
|
|
153
77
|
return [key, listedMs.get(key) ?? nowMs];
|
|
154
78
|
}));
|
|
155
|
-
this.#
|
|
79
|
+
this.#peaksRequest.changed();
|
|
156
80
|
}
|
|
81
|
+
/** How long a span stays listed in `pending` before its placeholder draws */
|
|
157
82
|
get pendingDelay() {
|
|
158
83
|
return Math.max(0, this.numberAttribute("pending-delay", 0));
|
|
159
84
|
}
|
|
@@ -173,6 +98,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
173
98
|
set playing(isPlaying) {
|
|
174
99
|
this.reflect("playing", isPlaying);
|
|
175
100
|
}
|
|
101
|
+
/** Read every frame for the playback time, so the waveform scrolls smoother than writes to `value` arrive */
|
|
176
102
|
get readTime() {
|
|
177
103
|
return this.#readTime;
|
|
178
104
|
}
|
|
@@ -186,13 +112,19 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
186
112
|
set reducedMotion(motion) {
|
|
187
113
|
this.reflect("reduced-motion", motion);
|
|
188
114
|
}
|
|
115
|
+
get renderLabel() {
|
|
116
|
+
return this.#renderLabel;
|
|
117
|
+
}
|
|
118
|
+
set renderLabel(render) {
|
|
119
|
+
this.#renderLabel = render;
|
|
120
|
+
if (this.isBound()) this.renderMarkers();
|
|
121
|
+
}
|
|
189
122
|
get requestPeaks() {
|
|
190
123
|
return this.#requestPeaks;
|
|
191
124
|
}
|
|
192
125
|
set requestPeaks(request) {
|
|
193
126
|
this.#requestPeaks = request;
|
|
194
|
-
this.#
|
|
195
|
-
this.surface()?.invalidate();
|
|
127
|
+
this.#peaksRequest.reset();
|
|
196
128
|
}
|
|
197
129
|
get zoom() {
|
|
198
130
|
const zoom = this.numberAttribute("zoom", defaultZoom);
|
|
@@ -223,56 +155,58 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
223
155
|
}
|
|
224
156
|
control = renderWaveform();
|
|
225
157
|
canvas = requireChild(this.control, ".sonic-waveform-canvas", HTMLCanvasElement);
|
|
226
|
-
colours =
|
|
158
|
+
colours = waveformColours;
|
|
227
159
|
lengths = {};
|
|
228
160
|
sheet = "waveform.css";
|
|
229
161
|
sizeProperty = "--_sonic-waveform-size";
|
|
230
|
-
#asked = /* @__PURE__ */ new WeakSet();
|
|
231
|
-
#askedKey;
|
|
232
162
|
#clock = createTrackingClock();
|
|
233
|
-
#connection;
|
|
234
163
|
#drawn;
|
|
235
164
|
#ghost = requireChild(this.control, ".sonic-waveform-ghost", HTMLDivElement);
|
|
236
165
|
#grabbed;
|
|
237
|
-
#isAsking = false;
|
|
238
|
-
#isPinchSpent = false;
|
|
239
166
|
#kindColours = /* @__PURE__ */ new Map();
|
|
240
167
|
#paintedKey = "";
|
|
241
168
|
#peaks;
|
|
169
|
+
#peaksRequest = createPeaksRequest(() => {
|
|
170
|
+
this.surface()?.invalidate();
|
|
171
|
+
});
|
|
242
172
|
#pending = [];
|
|
243
173
|
#pendingListedMs = /* @__PURE__ */ new Map();
|
|
244
|
-
#pinch;
|
|
245
174
|
#playhead = requireChild(this.control, ".sonic-waveform-playhead", HTMLDivElement);
|
|
246
175
|
#readTime;
|
|
176
|
+
#renderLabel;
|
|
247
177
|
#requestPeaks;
|
|
248
178
|
#rider = createLabelRider(this.control, {
|
|
249
179
|
className: "sonic-waveform-label",
|
|
250
180
|
colourProperty: "--_sonic-waveform-marker",
|
|
251
|
-
|
|
181
|
+
fadeProperties: ["--_sonic-waveform-label-fade-start", "--_sonic-waveform-label-fade-end"],
|
|
252
182
|
insetProperty: "--_sonic-waveform-label-inset",
|
|
253
183
|
parkProperty: "--_sonic-waveform-label-park"
|
|
254
184
|
});
|
|
255
|
-
#
|
|
256
|
-
#wheel;
|
|
185
|
+
#zoomGesture;
|
|
257
186
|
attributeChangedCallback(name) {
|
|
258
187
|
super.attributeChangedCallback(name);
|
|
259
|
-
if (name === "zoomable") this.#
|
|
188
|
+
if (name === "zoomable") this.#zoomGesture?.sync();
|
|
260
189
|
}
|
|
261
190
|
connectedCallback() {
|
|
262
|
-
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "requestPeaks", "zoom", "zoomable", "zoomMax", "zoomMin");
|
|
191
|
+
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "renderLabel", "requestPeaks", "zoom", "zoomable", "zoomMax", "zoomMin");
|
|
263
192
|
super.connectedCallback();
|
|
264
193
|
}
|
|
265
194
|
connect(signal) {
|
|
266
195
|
super.connect(signal);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
196
|
+
const zoomTarget = {
|
|
197
|
+
claim: () => {
|
|
198
|
+
this.abandonHold();
|
|
199
|
+
},
|
|
200
|
+
isDisabled: () => this.isDisabled(),
|
|
201
|
+
isZoomable: () => this.zoomable,
|
|
202
|
+
zoom: () => this.zoom,
|
|
203
|
+
zoomTo: (target) => {
|
|
204
|
+
this.#zoomTo(target);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
this.#zoomGesture = bindZoom(this.control, zoomTarget, signal);
|
|
274
208
|
this.bindGestures(this.control, signal, () => this.#grab());
|
|
275
|
-
|
|
209
|
+
bindZoomKeys(this.control, zoomTarget, signal);
|
|
276
210
|
if (!("fonts" in document)) return;
|
|
277
211
|
document.fonts.addEventListener("loadingdone", () => {
|
|
278
212
|
this.renderMarkers();
|
|
@@ -288,7 +222,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
288
222
|
}
|
|
289
223
|
paint(context, { frameMs, isDirty, isRebuilt, look, size }) {
|
|
290
224
|
const held = this.#held();
|
|
225
|
+
const isPaged = look.isReducedMotion && this.reducedMotion === "page";
|
|
291
226
|
const pending = this.#duePending();
|
|
227
|
+
const range = [this.min, Math.max(this.min, this.max)];
|
|
292
228
|
const timeline = frameTimeline({
|
|
293
229
|
clockSeconds: this.#clock.read(frameMs, {
|
|
294
230
|
isPlaying: this.playing,
|
|
@@ -297,11 +233,11 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
297
233
|
}),
|
|
298
234
|
frameMs,
|
|
299
235
|
...held ? { held } : {},
|
|
300
|
-
isPaged
|
|
236
|
+
isPaged,
|
|
301
237
|
isPlaying: this.playing,
|
|
302
238
|
isStill: look.isReducedMotion,
|
|
303
239
|
pending,
|
|
304
|
-
range
|
|
240
|
+
range,
|
|
305
241
|
size,
|
|
306
242
|
zoom: this.zoom
|
|
307
243
|
});
|
|
@@ -315,6 +251,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
315
251
|
this.showCurrentMarker(seconds);
|
|
316
252
|
this.toggleState("pending", view.pending.length > 0);
|
|
317
253
|
this.#rider.place({
|
|
254
|
+
isPaged,
|
|
318
255
|
playheadSeconds: seconds,
|
|
319
256
|
startSeconds: view.startSeconds,
|
|
320
257
|
widthPx: view.width / view.dpr,
|
|
@@ -322,113 +259,41 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
322
259
|
});
|
|
323
260
|
if (isDirty || timeline.paintKey !== this.#paintedKey) {
|
|
324
261
|
this.#paintedKey = timeline.paintKey;
|
|
325
|
-
this.#
|
|
262
|
+
if (isRebuilt) this.#kindColours = this.#readKindColours();
|
|
263
|
+
paintWaveform(context, {
|
|
326
264
|
...view,
|
|
327
265
|
colours: look.colours
|
|
328
|
-
},
|
|
329
|
-
|
|
266
|
+
}, {
|
|
267
|
+
kindColours: this.#kindColours,
|
|
268
|
+
markers: this.markerList(),
|
|
269
|
+
peaks: this.#peaks,
|
|
270
|
+
range
|
|
271
|
+
});
|
|
272
|
+
this.#peaksRequest.ask(this.#requestPeaks, timeline.wanted);
|
|
330
273
|
}
|
|
331
274
|
if (timeline.isMoving) this.surface()?.requestFrame();
|
|
332
275
|
}
|
|
333
276
|
renderMarkers() {
|
|
334
277
|
const bounds = this.mapping().bounds;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
278
|
+
const render = this.#renderLabel;
|
|
279
|
+
this.#rider.measure(this.markers.flatMap((marker) => marker.label === void 0 || marker.label === "" ? [] : [{
|
|
280
|
+
isDimmed: marker.dimmed === true,
|
|
281
|
+
...marker.kind === void 0 ? {} : { kind: marker.kind },
|
|
282
|
+
start: clamp(marker.start, ...bounds),
|
|
283
|
+
text: marker.label,
|
|
284
|
+
...render ? { write: (element) => {
|
|
285
|
+
render(marker, element);
|
|
286
|
+
} } : {}
|
|
340
287
|
}]));
|
|
341
288
|
this.surface()?.rebuild();
|
|
342
289
|
}
|
|
343
|
-
#ask(wanted) {
|
|
344
|
-
const key = wanted.join(":");
|
|
345
|
-
if (key === this.#askedKey) return;
|
|
346
|
-
let asked;
|
|
347
|
-
this.#askedKey = key;
|
|
348
|
-
this.#isAsking = true;
|
|
349
|
-
try {
|
|
350
|
-
asked = this.#requestPeaks?.(...wanted);
|
|
351
|
-
} finally {
|
|
352
|
-
this.#isAsking = false;
|
|
353
|
-
}
|
|
354
|
-
const promises = asked instanceof Promise ? [asked] : asked ?? [];
|
|
355
|
-
for (const promise of promises) {
|
|
356
|
-
if (this.#asked.has(promise)) continue;
|
|
357
|
-
this.#asked.add(promise);
|
|
358
|
-
this.#repaintAfter(promise);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
#bindPinch(signal) {
|
|
362
|
-
const control = this.control;
|
|
363
|
-
this.#touches.clear();
|
|
364
|
-
this.#pinch = void 0;
|
|
365
|
-
this.#isPinchSpent = false;
|
|
366
|
-
control.addEventListener("pointerdown", (event) => {
|
|
367
|
-
if (event.pointerType !== "touch" || !this.zoomable || this.isDisabled()) return;
|
|
368
|
-
this.#touches.set(event.pointerId, event.clientX);
|
|
369
|
-
if (this.#pinch || this.#isPinchSpent) return;
|
|
370
|
-
const pointerIds = this.#touchPair();
|
|
371
|
-
if (!pointerIds) return;
|
|
372
|
-
this.abandonHold();
|
|
373
|
-
capturePointer(control, event.pointerId);
|
|
374
|
-
this.#pinch = {
|
|
375
|
-
pointerIds,
|
|
376
|
-
startSpanPx: this.#spanPx(pointerIds),
|
|
377
|
-
startZoom: this.zoom
|
|
378
|
-
};
|
|
379
|
-
}, { signal });
|
|
380
|
-
control.addEventListener("pointermove", (event) => {
|
|
381
|
-
if (!this.#touches.has(event.pointerId)) return;
|
|
382
|
-
this.#touches.set(event.pointerId, event.clientX);
|
|
383
|
-
const pinch = this.#pinch;
|
|
384
|
-
if (!pinch?.pointerIds.includes(event.pointerId)) return;
|
|
385
|
-
this.#zoomTo(pinch.startZoom * pinchFactor(pinch.startSpanPx, this.#spanPx(pinch.pointerIds)));
|
|
386
|
-
}, { signal });
|
|
387
|
-
for (const type of ["pointerup", "pointercancel"]) control.addEventListener(type, (event) => {
|
|
388
|
-
if (!this.#touches.delete(event.pointerId)) return;
|
|
389
|
-
if (this.#pinch?.pointerIds.includes(event.pointerId)) {
|
|
390
|
-
this.#pinch = void 0;
|
|
391
|
-
this.#isPinchSpent = true;
|
|
392
|
-
}
|
|
393
|
-
if (this.#touches.size === 0) this.#isPinchSpent = false;
|
|
394
|
-
}, { signal });
|
|
395
|
-
}
|
|
396
|
-
#bindWheel() {
|
|
397
|
-
this.#wheel?.abort();
|
|
398
|
-
this.#wheel = void 0;
|
|
399
|
-
if (!this.zoomable || !this.#connection) return;
|
|
400
|
-
this.#wheel = new AbortController();
|
|
401
|
-
this.control.addEventListener("wheel", (event) => {
|
|
402
|
-
if (!event.ctrlKey && !event.metaKey || this.isDisabled()) return;
|
|
403
|
-
event.preventDefault();
|
|
404
|
-
this.#zoomTo(this.zoom * wheelFactor(event.deltaY, event.deltaMode));
|
|
405
|
-
}, {
|
|
406
|
-
passive: false,
|
|
407
|
-
signal: this.#wheel.signal
|
|
408
|
-
});
|
|
409
|
-
let startZoom = this.zoom;
|
|
410
|
-
for (const type of ["gesturestart", "gesturechange"]) this.control.addEventListener(type, (event) => {
|
|
411
|
-
if (this.isDisabled() || this.#touches.size > 0) return;
|
|
412
|
-
event.preventDefault();
|
|
413
|
-
if (type === "gesturestart") startZoom = this.zoom;
|
|
414
|
-
else if ("scale" in event && typeof event.scale === "number") this.#zoomTo(startZoom * event.scale);
|
|
415
|
-
}, { signal: this.#wheel.signal });
|
|
416
|
-
}
|
|
417
|
-
#bindZoomKeys(signal) {
|
|
418
|
-
this.control.addEventListener("keydown", (event) => {
|
|
419
|
-
const direction = this.#zoomKey(event);
|
|
420
|
-
if (direction === void 0) return;
|
|
421
|
-
event.preventDefault();
|
|
422
|
-
this.#zoomTo(this.zoom * zoomKeyFactor ** direction);
|
|
423
|
-
}, { signal });
|
|
424
|
-
}
|
|
425
290
|
#duePending() {
|
|
426
291
|
const due = dueRegions(this.#pending, this.#pendingListedMs, [performance.now(), this.pendingDelay]);
|
|
427
292
|
if (due.length < this.#pending.length) this.surface()?.requestFrame();
|
|
428
293
|
return due;
|
|
429
294
|
}
|
|
430
295
|
#grab() {
|
|
431
|
-
if (this.#
|
|
296
|
+
if (this.#zoomGesture?.isPinching() === true) return void 0;
|
|
432
297
|
const drawn = this.#drawn ?? {
|
|
433
298
|
seconds: this.value,
|
|
434
299
|
startSeconds: this.value
|
|
@@ -445,42 +310,13 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
445
310
|
}
|
|
446
311
|
#held() {
|
|
447
312
|
const grabbed = this.#grabbed;
|
|
448
|
-
const from = this.
|
|
313
|
+
const { from } = this.scrubState();
|
|
449
314
|
if (!grabbed || from === void 0) return void 0;
|
|
450
315
|
return {
|
|
451
316
|
grabbed,
|
|
452
317
|
seconds: this.value === from ? grabbed.seconds : this.value
|
|
453
318
|
};
|
|
454
319
|
}
|
|
455
|
-
#paintMarkers(context, scene, isRebuilt) {
|
|
456
|
-
if (isRebuilt) this.#kindColours = this.#readKindColours();
|
|
457
|
-
const line = Math.max(1, Math.round(scene.dpr));
|
|
458
|
-
for (const { dimmed, kind, start } of this.markers) {
|
|
459
|
-
const x = (start - scene.startSeconds) * scene.pixelsPerSecond;
|
|
460
|
-
context.fillStyle = (isKind(kind) ? this.#kindColours.get(kind) : void 0) ?? scene.colours.marker;
|
|
461
|
-
context.globalAlpha = dimmed === true ? .5 : 1;
|
|
462
|
-
context.fillRect(x - line / 2, 0, line, scene.height);
|
|
463
|
-
}
|
|
464
|
-
context.globalAlpha = 1;
|
|
465
|
-
}
|
|
466
|
-
#paintScene(context, scene, isRebuilt) {
|
|
467
|
-
const { dpr, height, pixelsPerSecond, startSeconds, width } = scene;
|
|
468
|
-
const x = (seconds) => (seconds - startSeconds) * pixelsPerSecond;
|
|
469
|
-
const { ends, grid } = scene.colours;
|
|
470
|
-
const line = Math.max(1, Math.round(dpr));
|
|
471
|
-
const [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];
|
|
472
|
-
const [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];
|
|
473
|
-
context.clearRect(0, 0, width, height);
|
|
474
|
-
paintHatch(context, scene, [0, from]);
|
|
475
|
-
paintHatch(context, scene, [to, width]);
|
|
476
|
-
context.fillStyle = grid;
|
|
477
|
-
if (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);
|
|
478
|
-
paintPlaceholders(context, scene);
|
|
479
|
-
if (this.#peaks) paintWave(context, scene, this.#peaks);
|
|
480
|
-
context.fillStyle = ends;
|
|
481
|
-
for (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);
|
|
482
|
-
this.#paintMarkers(context, scene, isRebuilt);
|
|
483
|
-
}
|
|
484
320
|
#readKindColours() {
|
|
485
321
|
const kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];
|
|
486
322
|
const probes = kinds.map((kind) => {
|
|
@@ -494,29 +330,8 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
494
330
|
for (const probe of probes) probe.remove();
|
|
495
331
|
return new Map(kinds.map((kind, index) => [kind, read[index] ?? ""]));
|
|
496
332
|
}
|
|
497
|
-
#repaint() {
|
|
498
|
-
if (!this.#isAsking) this.#askedKey = void 0;
|
|
499
|
-
this.surface()?.invalidate();
|
|
500
|
-
}
|
|
501
|
-
async #repaintAfter(asked) {
|
|
502
|
-
await Promise.allSettled([asked]);
|
|
503
|
-
this.#repaint();
|
|
504
|
-
}
|
|
505
333
|
#sourceSeconds() {
|
|
506
|
-
return this.#readTime?.() ?? this.
|
|
507
|
-
}
|
|
508
|
-
#spanPx([first, second]) {
|
|
509
|
-
return (this.#touches.get(second) ?? 0) - (this.#touches.get(first) ?? 0);
|
|
510
|
-
}
|
|
511
|
-
#touchPair() {
|
|
512
|
-
const [first, second, third] = this.#touches.keys();
|
|
513
|
-
if (first === void 0 || second === void 0 || third !== void 0) return void 0;
|
|
514
|
-
return [first, second];
|
|
515
|
-
}
|
|
516
|
-
#zoomKey(event) {
|
|
517
|
-
if (!this.zoomable || this.isDisabled() || event.target !== this.control) return void 0;
|
|
518
|
-
if (event.ctrlKey || event.metaKey || event.altKey) return void 0;
|
|
519
|
-
return zoomKeys.get(event.key);
|
|
334
|
+
return this.#readTime?.() ?? this.scrubState().played;
|
|
520
335
|
}
|
|
521
336
|
#zoomTo(target) {
|
|
522
337
|
const next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waveform.js","names":[],"sources":["../../../src/elements/waveform.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { SurfaceFrame, SurfaceLook } from '#lib/canvas-surface.ts';\nimport type { Drawn, View } from '#lib/frame-timeline.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { frameTimeline } from '#lib/frame-timeline.ts';\nimport { isKind, writeKind } from '#lib/marker-band.ts';\nimport { createLabelRider } from '#lib/marker-rider.ts';\nimport { clamp, roundTo } from '#lib/math.ts';\nimport { capturePointer } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { waveformBuckets } from '#lib/waveform-buckets.ts';\nimport { pinchFactor, wheelFactor, zoomKeyFactor } from '#lib/zoom-gesture.ts';\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-zoom': Event;\n\t}\n\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-waveform': SonicWaveform;\n\t}\n}\n\ntype Colour = keyof typeof colours;\n\nexport type PeaksRequest = (\n\tfromSeconds: number,\n\ttoSeconds: number,\n) => Iterable<Promise<unknown>> | Promise<unknown> | undefined;\n\ninterface Pinch {\n\tpointerIds: [number, number];\n\tstartSpanPx: number;\n\tstartZoom: number;\n}\n\ninterface Scene extends View {\n\tcolours: SurfaceLook<Colour>['colours'];\n}\n\nconst colours = {\n\tends: '--_sonic-waveform-ends',\n\tgrid: '--_sonic-waveform-grid',\n\tmarker: '--_sonic-waveform-marker',\n\tplaceholder: '--_sonic-waveform-placeholder',\n\twave: '--_sonic-waveform-wave',\n\twaveEdge: '--_sonic-waveform-wave-edge',\n} as const;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\n\nconst zoomKeys = new Map([\n\t['+', 1],\n\t['-', -1],\n\t['=', 1],\n]);\n\nconst amplitudeMargin = 0.94;\n\nconst hatchPitchPx = 8;\nconst placeholderWavelengthPx = 48;\nconst placeholderAmplitude = 0.2;\n\nconst renderWaveform = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-waveform\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-waveform-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-waveform-scrim\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-ghost\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-playhead\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-waveform-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nfunction placeLine(line: HTMLElement, at: number | undefined): void {\n\tconst isHidden = at === undefined || at < 0 || at > 1;\n\n\tif (line.hidden !== isHidden) line.hidden = isHidden;\n\tif (isHidden) return;\n\n\tconst translate = `${String(at * 100)}cqi`;\n\n\tif (line.style.getPropertyValue('translate') !== translate) {\n\t\tline.style.setProperty('translate', translate);\n\t}\n}\n\nfunction paintHatch(\n\tcontext: CanvasRenderingContext2D,\n\tscene: Scene,\n\t[from, to]: [number, number],\n): void {\n\tif (to <= from) return;\n\n\tconst { dpr, height } = scene;\n\tconst pitch = Math.max(2, Math.round(hatchPitchPx * dpr));\n\tconst offset = scene.startSeconds * scene.pixelsPerSecond;\n\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, height);\n\tcontext.clip();\n\tcontext.beginPath();\n\tfor (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {\n\t\tcontext.moveTo(x, height);\n\t\tcontext.lineTo(x + height, 0);\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.ends;\n\tcontext.stroke();\n\tcontext.restore();\n}\n\nfunction paintPlaceholders(context: CanvasRenderingContext2D, scene: Scene): void {\n\tif (scene.pending.length === 0) return;\n\n\tconst { dpr, height, pixelsPerSecond, startSeconds } = scene;\n\tconst wavelength = placeholderWavelengthPx * dpr;\n\tconst center = height / 2;\n\tconst origin = startSeconds * pixelsPerSecond;\n\tconst waveY = (x: number): number =>\n\t\tcenter -\n\t\tMath.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) *\n\t\t\tcenter *\n\t\t\tplaceholderAmplitude;\n\n\tcontext.beginPath();\n\tfor (const [from, to] of scene.pending) {\n\t\tconst opening = (from - startSeconds) * pixelsPerSecond;\n\t\tconst closing = (to - startSeconds) * pixelsPerSecond;\n\n\t\tcontext.moveTo(opening, waveY(opening));\n\t\tfor (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));\n\t\tcontext.lineTo(closing, waveY(closing));\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.placeholder;\n\tcontext.stroke();\n}\n\nfunction waveFill(context: CanvasRenderingContext2D, scene: Scene): CanvasGradient {\n\tconst { wave, waveEdge } = scene.colours;\n\tconst gradient = context.createLinearGradient(0, 0, 0, scene.height);\n\n\tgradient.addColorStop(0, waveEdge);\n\tgradient.addColorStop(0.5, wave);\n\tgradient.addColorStop(1, waveEdge);\n\n\treturn gradient;\n}\n\nfunction paintWave(context: CanvasRenderingContext2D, scene: Scene, peaks: WaveformPeaks): void {\n\tconst buckets = waveformBuckets(peaks, scene);\n\tconst [first] = buckets;\n\tif (!first) return;\n\n\tconst center = scene.height / 2;\n\tconst amplitudePx = center * amplitudeMargin;\n\n\tcontext.fillStyle = waveFill(context, scene);\n\tcontext.beginPath();\n\tcontext.moveTo(first.x, center);\n\tfor (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);\n\tfor (const bucket of buckets.toReversed())\n\t\tcontext.lineTo(bucket.x, center - bucket.low * amplitudePx);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour> {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicWaveElement.observedAttributes,\n\t\t'pending-delay',\n\t\t'playing',\n\t\t'playback-rate',\n\t\t'reduced-motion',\n\t\t'zoom',\n\t\t'zoom-max',\n\t\t'zoom-min',\n\t\t'zoomable',\n\t];\n\n\tget peaks(): undefined | WaveformPeaks {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: undefined | WaveformPeaks) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.#repaint();\n\t}\n\n\tget pending(): Array<[number, number]> {\n\t\treturn this.#pending.map(([start, end]) => [start, end]);\n\t}\n\n\tset pending(regions: TimeRegions | undefined) {\n\t\tconst pending = regions ? readRegions(regions) : [];\n\t\tif (String(pending) === String(this.#pending)) return;\n\n\t\tconst listedMs = this.#pendingListedMs;\n\t\tconst nowMs = performance.now();\n\n\t\tthis.#pending = pending;\n\t\tthis.#pendingListedMs = new Map(\n\t\t\tthis.#pending.map((region) => {\n\t\t\t\tconst key = region.join(':');\n\n\t\t\t\treturn [key, listedMs.get(key) ?? nowMs];\n\t\t\t}),\n\t\t);\n\t\tthis.#repaint();\n\t}\n\n\tget pendingDelay(): number {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: number | undefined) {\n\t\tthis.reflect('pending-delay', value);\n\t}\n\n\tget playbackRate(): number {\n\t\tconst playbackRate = this.numberAttribute('playback-rate', 1);\n\n\t\treturn playbackRate > 0 ? playbackRate : 1;\n\t}\n\n\tset playbackRate(value: number | undefined) {\n\t\tthis.reflect('playback-rate', value);\n\t}\n\n\tget playing(): boolean {\n\t\treturn this.hasAttribute('playing');\n\t}\n\n\tset playing(isPlaying: boolean) {\n\t\tthis.reflect('playing', isPlaying);\n\t}\n\n\tget readTime(): (() => number | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => number | undefined) | undefined) {\n\t\tthis.#readTime = read;\n\t\tthis.render();\n\t}\n\n\tget reducedMotion(): 'page' | 'scroll' {\n\t\treturn this.getAttribute('reduced-motion') === 'scroll' ? 'scroll' : 'page';\n\t}\n\n\tset reducedMotion(motion: 'page' | 'scroll' | undefined) {\n\t\tthis.reflect('reduced-motion', motion);\n\t}\n\n\tget requestPeaks(): PeaksRequest | undefined {\n\t\treturn this.#requestPeaks;\n\t}\n\n\tset requestPeaks(request: PeaksRequest | undefined) {\n\t\tthis.#requestPeaks = request;\n\t\tthis.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tget zoom(): number {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: number | undefined) {\n\t\tthis.reflect('zoom', value);\n\t}\n\n\tget zoomable(): boolean {\n\t\treturn this.hasAttribute('zoomable');\n\t}\n\n\tset zoomable(isZoomable: boolean) {\n\t\tthis.reflect('zoomable', isZoomable);\n\t}\n\n\tget zoomMax(): number {\n\t\tconst zoomMax = this.numberAttribute('zoom-max', defaultZoomMax);\n\n\t\treturn Math.max(this.zoomMin, zoomMax > 0 ? zoomMax : defaultZoomMax);\n\t}\n\n\tset zoomMax(value: number | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): number {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: number | undefined) {\n\t\tthis.reflect('zoom-min', value);\n\t}\n\n\tprotected readonly control = renderWaveform();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-waveform-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = colours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\treadonly #asked = new WeakSet<Promise<unknown>>();\n\n\t#askedKey: string | undefined;\n\n\treadonly #clock = createTrackingClock();\n\n\t#connection: AbortSignal | undefined;\n\n\t#drawn: Drawn | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#isAsking = false;\n\n\t#isPinchSpent = false;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\t#pinch: Pinch | undefined;\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | undefined;\n\n\t#requestPeaks: PeaksRequest | undefined;\n\n\treadonly #rider = createLabelRider(this.control, {\n\t\tclassName: 'sonic-waveform-label',\n\t\tcolourProperty: '--_sonic-waveform-marker',\n\t\tfadeProperty: '--_sonic-waveform-label-fade',\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\treadonly #touches = new Map<number, number>();\n\n\t#wheel: AbortController | undefined;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#bindWheel();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'peaks',\n\t\t\t'pending',\n\t\t\t'pendingDelay',\n\t\t\t'playing',\n\t\t\t'playbackRate',\n\t\t\t'readTime',\n\t\t\t'reducedMotion',\n\t\t\t'requestPeaks',\n\t\t\t'zoom',\n\t\t\t'zoomable',\n\t\t\t'zoomMax',\n\t\t\t'zoomMin',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\t\tthis.#connection = signal;\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#connection = undefined;\n\t\t\t\tthis.#bindWheel();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t\tthis.#bindWheel();\n\t\tthis.#bindPinch(signal);\n\t\tthis.bindGestures(this.control, signal, () => this.#grab());\n\t\tthis.#bindZoomKeys(signal);\n\t\tif (!('fonts' in document)) return;\n\n\t\tdocument.fonts.addEventListener(\n\t\t\t'loadingdone',\n\t\t\t() => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tsurface?.requestFrame();\n\t\t// No frame follows the position out of view, or before the first one paints\n\t\tif (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.samples.length === 0;\n\t}\n\n\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst pending = this.#duePending();\n\t\tconst timeline = frameTimeline({\n\t\t\tclockSeconds: this.#clock.read(frameMs, {\n\t\t\t\tisPlaying: this.playing,\n\t\t\t\tplaybackRate: this.playbackRate,\n\t\t\t\tseconds: this.#sourceSeconds(),\n\t\t\t}),\n\t\t\tframeMs,\n\t\t\t...(held ? { held } : {}),\n\t\t\tisPaged: look.isReducedMotion && this.reducedMotion === 'page',\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange: [this.min, Math.max(this.min, this.max)],\n\t\t\tsize,\n\t\t\tzoom: this.zoom,\n\t\t});\n\t\tconst { seconds, view } = timeline;\n\n\t\tthis.#drawn = { seconds, startSeconds: view.startSeconds };\n\t\tplaceLine(this.#playhead, timeline.playheadAt);\n\t\tplaceLine(this.#ghost, timeline.ghostAt);\n\t\tthis.showCurrentMarker(seconds);\n\t\tthis.toggleState('pending', view.pending.length > 0);\n\t\tthis.#rider.place({\n\t\t\tplayheadSeconds: seconds,\n\t\t\tstartSeconds: view.startSeconds,\n\t\t\twidthPx: view.width / view.dpr,\n\t\t\twindowSeconds: view.width / view.pixelsPerSecond,\n\t\t});\n\t\tif (isDirty || timeline.paintKey !== this.#paintedKey) {\n\t\t\tthis.#paintedKey = timeline.paintKey;\n\t\t\tthis.#paintScene(context, { ...view, colours: look.colours }, isRebuilt);\n\t\t\tif (timeline.wanted) this.#ask(timeline.wanted);\n\t\t}\n\t\tif (timeline.isMoving) this.surface()?.requestFrame();\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tconst bounds = this.mapping().bounds;\n\n\t\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap(({ dimmed, kind, label, start }) =>\n\t\t\t\tlabel === undefined || label === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tisDimmed: dimmed === true,\n\t\t\t\t\t\t\t\t...(kind === undefined ? {} : { kind }),\n\t\t\t\t\t\t\t\tstart: clamp(start, ...bounds),\n\t\t\t\t\t\t\t\ttext: label,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t),\n\t\t);\n\t\tthis.surface()?.rebuild();\n\t}\n\n\t#ask(wanted: [number, number]): void {\n\t\tconst key = wanted.join(':');\n\t\tif (key === this.#askedKey) return;\n\n\t\tlet asked: ReturnType<PeaksRequest>;\n\n\t\tthis.#askedKey = key;\n\t\tthis.#isAsking = true;\n\t\ttry {\n\t\t\tasked = this.#requestPeaks?.(...wanted);\n\t\t} finally {\n\t\t\tthis.#isAsking = false;\n\t\t}\n\n\t\tconst promises = asked instanceof Promise ? [asked] : (asked ?? []);\n\n\t\tfor (const promise of promises) {\n\t\t\tif (this.#asked.has(promise)) continue;\n\n\t\t\tthis.#asked.add(promise);\n\t\t\tvoid this.#repaintAfter(promise);\n\t\t}\n\t}\n\n\t#bindPinch(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.#touches.clear();\n\t\tthis.#pinch = undefined;\n\t\tthis.#isPinchSpent = false;\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerType !== 'touch' || !this.zoomable || this.isDisabled()) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\t\t\t\tif (this.#pinch || this.#isPinchSpent) return;\n\n\t\t\t\tconst pointerIds = this.#touchPair();\n\t\t\t\tif (!pointerIds) return;\n\n\t\t\t\tthis.abandonHold();\n\t\t\t\tcapturePointer(control, event.pointerId);\n\t\t\t\tthis.#pinch = { pointerIds, startSpanPx: this.#spanPx(pointerIds), startZoom: this.zoom };\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tif (!this.#touches.has(event.pointerId)) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\n\t\t\t\tconst pinch = this.#pinch;\n\t\t\t\tif (!pinch?.pointerIds.includes(event.pointerId)) return;\n\n\t\t\t\tthis.#zoomTo(\n\t\t\t\t\tpinch.startZoom * pinchFactor(pinch.startSpanPx, this.#spanPx(pinch.pointerIds)),\n\t\t\t\t);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tfor (const type of ['pointerup', 'pointercancel'] as const) {\n\t\t\tcontrol.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (!this.#touches.delete(event.pointerId)) return;\n\n\t\t\t\t\tif (this.#pinch?.pointerIds.includes(event.pointerId)) {\n\t\t\t\t\t\tthis.#pinch = undefined;\n\t\t\t\t\t\tthis.#isPinchSpent = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (this.#touches.size === 0) this.#isPinchSpent = false;\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindWheel(): void {\n\t\tthis.#wheel?.abort();\n\t\tthis.#wheel = undefined;\n\t\tif (!this.zoomable || !this.#connection) return;\n\n\t\tthis.#wheel = new AbortController();\n\t\t// Chromium cannot cancel a wheel heard only on the `display: contents` host\n\t\tthis.control.addEventListener(\n\t\t\t'wheel',\n\t\t\t(event) => {\n\t\t\t\tif ((!event.ctrlKey && !event.metaKey) || this.isDisabled()) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * wheelFactor(event.deltaY, event.deltaMode));\n\t\t\t},\n\t\t\t{ passive: false, signal: this.#wheel.signal },\n\t\t);\n\n\t\tlet startZoom = this.zoom;\n\n\t\t// Safari sends a trackpad pinch as gesture events, never as a ctrl wheel\n\t\tfor (const type of ['gesturestart', 'gesturechange']) {\n\t\t\tthis.control.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (this.isDisabled() || this.#touches.size > 0) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (type === 'gesturestart') startZoom = this.zoom;\n\t\t\t\t\telse if ('scale' in event && typeof event.scale === 'number') {\n\t\t\t\t\t\tthis.#zoomTo(startZoom * event.scale);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ signal: this.#wheel.signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindZoomKeys(signal: AbortSignal): void {\n\t\tthis.control.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tconst direction = this.#zoomKey(event);\n\t\t\t\tif (direction === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * zoomKeyFactor ** direction);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#duePending(): Array<[number, number]> {\n\t\tconst due = dueRegions(this.#pending, this.#pendingListedMs, [\n\t\t\tperformance.now(),\n\t\t\tthis.pendingDelay,\n\t\t]);\n\n\t\tif (due.length < this.#pending.length) this.surface()?.requestFrame();\n\n\t\treturn due;\n\t}\n\n\t#grab(): undefined | ValueAxis {\n\t\tif (this.#pinch || this.#isPinchSpent) return undefined;\n\n\t\tconst drawn = this.#drawn ?? { seconds: this.value, startSeconds: this.value };\n\n\t\tthis.#grabbed = drawn;\n\n\t\tconst mapping = this.mapping();\n\t\tconst [low, high] = mapping.bounds;\n\n\t\treturn {\n\t\t\tfromProportion: mapping.proportionOf(drawn.seconds),\n\t\t\tisKeptOnCancel: true,\n\t\t\tposition: (event) => -event.clientX,\n\t\t\ttravelPx: Math.max(1, (high - low) * this.zoom),\n\t\t};\n\t}\n\n\t#held(): undefined | { grabbed: Drawn; seconds: number } {\n\t\tconst grabbed = this.#grabbed;\n\t\tconst from = this.heldFrom();\n\t\tif (!grabbed || from === undefined) return undefined;\n\n\t\treturn { grabbed, seconds: this.value === from ? grabbed.seconds : this.value };\n\t}\n\n\t#paintMarkers(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\n\t\tconst line = Math.max(1, Math.round(scene.dpr));\n\n\t\tfor (const { dimmed, kind, start } of this.markers) {\n\t\t\tconst x = (start - scene.startSeconds) * scene.pixelsPerSecond;\n\n\t\t\tcontext.fillStyle =\n\t\t\t\t(isKind(kind) ? this.#kindColours.get(kind) : undefined) ?? scene.colours.marker;\n\t\t\tcontext.globalAlpha = dimmed === true ? 0.5 : 1;\n\t\t\tcontext.fillRect(x - line / 2, 0, line, scene.height);\n\t\t}\n\t\tcontext.globalAlpha = 1;\n\t}\n\n\t#paintScene(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tconst { dpr, height, pixelsPerSecond, startSeconds, width } = scene;\n\t\tconst x = (seconds: number): number => (seconds - startSeconds) * pixelsPerSecond;\n\t\tconst { ends, grid } = scene.colours;\n\t\tconst line = Math.max(1, Math.round(dpr));\n\t\tconst [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];\n\t\tconst [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];\n\n\t\tcontext.clearRect(0, 0, width, height);\n\t\tpaintHatch(context, scene, [0, from]);\n\t\tpaintHatch(context, scene, [to, width]);\n\t\tcontext.fillStyle = grid;\n\t\tif (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);\n\t\tpaintPlaceholders(context, scene);\n\t\tif (this.#peaks) paintWave(context, scene, this.#peaks);\n\t\t// Scrolling lines sit at fractional x; rounding them judders\n\t\tcontext.fillStyle = ends;\n\t\tfor (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);\n\t\tthis.#paintMarkers(context, scene, isRebuilt);\n\t}\n\n\t#readKindColours(): Map<string, string> {\n\t\tconst kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];\n\t\tconst probes = kinds.map((kind) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.className = 'sonic-waveform-label';\n\t\t\twriteKind(probe, kind, '--_sonic-waveform-marker');\n\n\t\t\treturn probe;\n\t\t});\n\n\t\tthis.control.append(...probes);\n\n\t\tconst read = probes.map((probe) => getComputedStyle(probe).color);\n\n\t\tfor (const probe of probes) probe.remove();\n\n\t\treturn new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t// A write from inside the consumer's call repaints without asking for the same window again\n\t#repaint(): void {\n\t\tif (!this.#isAsking) this.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tasync #repaintAfter(asked: Promise<unknown>): Promise<void> {\n\t\tawait Promise.allSettled([asked]);\n\t\tthis.#repaint();\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.playback();\n\t}\n\n\t#spanPx([first, second]: [number, number]): number {\n\t\treturn (this.#touches.get(second) ?? 0) - (this.#touches.get(first) ?? 0);\n\t}\n\n\t#touchPair(): [number, number] | undefined {\n\t\tconst [first, second, third] = this.#touches.keys();\n\t\tif (first === undefined || second === undefined || third !== undefined) return undefined;\n\n\t\treturn [first, second];\n\t}\n\n\t#zoomKey(event: KeyboardEvent): number | undefined {\n\t\tif (!this.zoomable || this.isDisabled() || event.target !== this.control) return undefined;\n\t\tif (event.ctrlKey || event.metaKey || event.altKey) return undefined;\n\n\t\treturn zoomKeys.get(event.key);\n\t}\n\n\t#zoomTo(target: number): void {\n\t\tconst next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);\n\t\tif (next === this.zoom) return;\n\n\t\tthis.zoom = next;\n\t\tthis.dispatchEvent(new Event('sonic-zoom', { bubbles: true }));\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"waveform.js","names":[],"sources":["../../../src/elements/waveform.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { WaveMarker } from '#elements/wave-element.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Drawn } from '#lib/frame-timeline.ts';\nimport type { PeaksRequest } from '#lib/peaks-request.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { Milliseconds, PixelsPerSecond, Seconds } from '#lib/units.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\nimport type { ZoomGesture } from '#lib/zoom-gesture.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { frameTimeline } from '#lib/frame-timeline.ts';\nimport { isKind, writeKind } from '#lib/marker-band.ts';\nimport { createLabelRider } from '#lib/marker-rider.ts';\nimport { clamp, roundTo } from '#lib/math.ts';\nimport { createPeaksRequest } from '#lib/peaks-request.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { paintWaveform, waveformColours } from '#lib/waveform-scene.ts';\nimport { bindZoom, bindZoomKeys } from '#lib/zoom-gesture.ts';\n\nexport type { PeaksRequest } from '#lib/peaks-request.ts';\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-zoom': Event;\n\t}\n\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-waveform': SonicWaveform;\n\t}\n}\n\ntype Colour = keyof typeof waveformColours;\n\nexport type LabelRender = (marker: WaveMarker, element: HTMLElement) => void;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\n\nconst renderWaveform = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-waveform\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-waveform-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-waveform-scrim\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-ghost\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-playhead\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-waveform-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nfunction placeLine(line: HTMLElement, at: number | undefined): void {\n\tconst isHidden = at === undefined || at < 0 || at > 1;\n\n\tif (line.hidden !== isHidden) line.hidden = isHidden;\n\tif (isHidden) return;\n\n\tconst translate = `${String(at * 100)}cqi`;\n\n\tif (line.style.getPropertyValue('translate') !== translate) {\n\t\tline.style.setProperty('translate', translate);\n\t}\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour> {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicWaveElement.observedAttributes,\n\t\t'pending-delay',\n\t\t'playing',\n\t\t'playback-rate',\n\t\t'reduced-motion',\n\t\t'zoom',\n\t\t'zoom-max',\n\t\t'zoom-min',\n\t\t'zoomable',\n\t];\n\n\tget peaks(): undefined | WaveformPeaks {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: undefined | WaveformPeaks) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** Spans being fetched; drawn as placeholders until cleared */\n\tget pending(): Array<[Seconds, Seconds]> {\n\t\treturn this.#pending.map(([start, end]) => [start, end]);\n\t}\n\n\tset pending(regions: TimeRegions | undefined) {\n\t\tconst pending = regions ? readRegions(regions) : [];\n\t\tif (String(pending) === String(this.#pending)) return;\n\n\t\tconst listedMs = this.#pendingListedMs;\n\t\tconst nowMs = performance.now();\n\n\t\tthis.#pending = pending;\n\t\tthis.#pendingListedMs = new Map(\n\t\t\tthis.#pending.map((region) => {\n\t\t\t\tconst key = region.join(':');\n\n\t\t\t\treturn [key, listedMs.get(key) ?? nowMs];\n\t\t\t}),\n\t\t);\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** How long a span stays listed in `pending` before its placeholder draws */\n\tget pendingDelay(): Milliseconds {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: Milliseconds | undefined) {\n\t\tthis.reflect('pending-delay', value);\n\t}\n\n\tget playbackRate(): number {\n\t\tconst playbackRate = this.numberAttribute('playback-rate', 1);\n\n\t\treturn playbackRate > 0 ? playbackRate : 1;\n\t}\n\n\tset playbackRate(value: number | undefined) {\n\t\tthis.reflect('playback-rate', value);\n\t}\n\n\tget playing(): boolean {\n\t\treturn this.hasAttribute('playing');\n\t}\n\n\tset playing(isPlaying: boolean) {\n\t\tthis.reflect('playing', isPlaying);\n\t}\n\n\t/** Read every frame for the playback time, so the waveform scrolls smoother than writes to `value` arrive */\n\tget readTime(): (() => Seconds | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => Seconds | undefined) | undefined) {\n\t\tthis.#readTime = read;\n\t\tthis.render();\n\t}\n\n\tget reducedMotion(): 'page' | 'scroll' {\n\t\treturn this.getAttribute('reduced-motion') === 'scroll' ? 'scroll' : 'page';\n\t}\n\n\tset reducedMotion(motion: 'page' | 'scroll' | undefined) {\n\t\tthis.reflect('reduced-motion', motion);\n\t}\n\n\tget renderLabel(): LabelRender | undefined {\n\t\treturn this.#renderLabel;\n\t}\n\n\tset renderLabel(render: LabelRender | undefined) {\n\t\tthis.#renderLabel = render;\n\t\tif (this.isBound()) this.renderMarkers();\n\t}\n\n\tget requestPeaks(): PeaksRequest | undefined {\n\t\treturn this.#requestPeaks;\n\t}\n\n\tset requestPeaks(request: PeaksRequest | undefined) {\n\t\tthis.#requestPeaks = request;\n\t\tthis.#peaksRequest.reset();\n\t}\n\n\tget zoom(): PixelsPerSecond {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom', value);\n\t}\n\n\tget zoomable(): boolean {\n\t\treturn this.hasAttribute('zoomable');\n\t}\n\n\tset zoomable(isZoomable: boolean) {\n\t\tthis.reflect('zoomable', isZoomable);\n\t}\n\n\tget zoomMax(): PixelsPerSecond {\n\t\tconst zoomMax = this.numberAttribute('zoom-max', defaultZoomMax);\n\n\t\treturn Math.max(this.zoomMin, zoomMax > 0 ? zoomMax : defaultZoomMax);\n\t}\n\n\tset zoomMax(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): PixelsPerSecond {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-min', value);\n\t}\n\n\tprotected readonly control = renderWaveform();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-waveform-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = waveformColours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\treadonly #clock = createTrackingClock();\n\n\t#drawn: Drawn | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\treadonly #peaksRequest = createPeaksRequest(() => {\n\t\tthis.surface()?.invalidate();\n\t});\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | undefined;\n\n\t#renderLabel: LabelRender | undefined;\n\n\t#requestPeaks: PeaksRequest | undefined;\n\n\treadonly #rider = createLabelRider(this.control, {\n\t\tclassName: 'sonic-waveform-label',\n\t\tcolourProperty: '--_sonic-waveform-marker',\n\t\tfadeProperties: ['--_sonic-waveform-label-fade-start', '--_sonic-waveform-label-fade-end'],\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\t#zoomGesture: undefined | ZoomGesture;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#zoomGesture?.sync();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'peaks',\n\t\t\t'pending',\n\t\t\t'pendingDelay',\n\t\t\t'playing',\n\t\t\t'playbackRate',\n\t\t\t'readTime',\n\t\t\t'reducedMotion',\n\t\t\t'renderLabel',\n\t\t\t'requestPeaks',\n\t\t\t'zoom',\n\t\t\t'zoomable',\n\t\t\t'zoomMax',\n\t\t\t'zoomMin',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\n\t\tconst zoomTarget = {\n\t\t\tclaim: () => {\n\t\t\t\tthis.abandonHold();\n\t\t\t},\n\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\tisZoomable: () => this.zoomable,\n\t\t\tzoom: () => this.zoom,\n\t\t\tzoomTo: (target: number) => {\n\t\t\t\tthis.#zoomTo(target);\n\t\t\t},\n\t\t};\n\n\t\tthis.#zoomGesture = bindZoom(this.control, zoomTarget, signal);\n\t\tthis.bindGestures(this.control, signal, () => this.#grab());\n\t\t// After the value keys, which stand down once a key is taken\n\t\tbindZoomKeys(this.control, zoomTarget, signal);\n\t\tif (!('fonts' in document)) return;\n\n\t\tdocument.fonts.addEventListener(\n\t\t\t'loadingdone',\n\t\t\t() => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tsurface?.requestFrame();\n\t\t// No frame follows the position out of view, or before the first one paints\n\t\tif (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.samples.length === 0;\n\t}\n\n\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst isPaged = look.isReducedMotion && this.reducedMotion === 'page';\n\t\tconst pending = this.#duePending();\n\t\tconst range: [number, number] = [this.min, Math.max(this.min, this.max)];\n\t\tconst timeline = frameTimeline({\n\t\t\tclockSeconds: this.#clock.read(frameMs, {\n\t\t\t\tisPlaying: this.playing,\n\t\t\t\tplaybackRate: this.playbackRate,\n\t\t\t\tseconds: this.#sourceSeconds(),\n\t\t\t}),\n\t\t\tframeMs,\n\t\t\t...(held ? { held } : {}),\n\t\t\tisPaged,\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange,\n\t\t\tsize,\n\t\t\tzoom: this.zoom,\n\t\t});\n\t\tconst { seconds, view } = timeline;\n\n\t\tthis.#drawn = { seconds, startSeconds: view.startSeconds };\n\t\tplaceLine(this.#playhead, timeline.playheadAt);\n\t\tplaceLine(this.#ghost, timeline.ghostAt);\n\t\tthis.showCurrentMarker(seconds);\n\t\tthis.toggleState('pending', view.pending.length > 0);\n\t\tthis.#rider.place({\n\t\t\tisPaged,\n\t\t\tplayheadSeconds: seconds,\n\t\t\tstartSeconds: view.startSeconds,\n\t\t\twidthPx: view.width / view.dpr,\n\t\t\twindowSeconds: view.width / view.pixelsPerSecond,\n\t\t});\n\t\tif (isDirty || timeline.paintKey !== this.#paintedKey) {\n\t\t\tthis.#paintedKey = timeline.paintKey;\n\t\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\t\t\tpaintWaveform(\n\t\t\t\tcontext,\n\t\t\t\t{ ...view, colours: look.colours },\n\t\t\t\t{\n\t\t\t\t\tkindColours: this.#kindColours,\n\t\t\t\t\tmarkers: this.markerList(),\n\t\t\t\t\tpeaks: this.#peaks,\n\t\t\t\t\trange,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.#peaksRequest.ask(this.#requestPeaks, timeline.wanted);\n\t\t}\n\t\tif (timeline.isMoving) this.surface()?.requestFrame();\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tconst bounds = this.mapping().bounds;\n\n\t\tconst render = this.#renderLabel;\n\n\t\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap((marker) =>\n\t\t\t\tmarker.label === undefined || marker.label === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tisDimmed: marker.dimmed === true,\n\t\t\t\t\t\t\t\t...(marker.kind === undefined ? {} : { kind: marker.kind }),\n\t\t\t\t\t\t\t\tstart: clamp(marker.start, ...bounds),\n\t\t\t\t\t\t\t\ttext: marker.label,\n\t\t\t\t\t\t\t\t...(render\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\twrite: (element: HTMLElement) => {\n\t\t\t\t\t\t\t\t\t\t\t\trender(marker, element);\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t),\n\t\t);\n\t\tthis.surface()?.rebuild();\n\t}\n\n\t#duePending(): Array<[number, number]> {\n\t\tconst due = dueRegions(this.#pending, this.#pendingListedMs, [\n\t\t\tperformance.now(),\n\t\t\tthis.pendingDelay,\n\t\t]);\n\n\t\tif (due.length < this.#pending.length) this.surface()?.requestFrame();\n\n\t\treturn due;\n\t}\n\n\t#grab(): undefined | ValueAxis {\n\t\tif (this.#zoomGesture?.isPinching() === true) return undefined;\n\n\t\tconst drawn = this.#drawn ?? { seconds: this.value, startSeconds: this.value };\n\n\t\tthis.#grabbed = drawn;\n\n\t\tconst mapping = this.mapping();\n\t\tconst [low, high] = mapping.bounds;\n\n\t\treturn {\n\t\t\tfromProportion: mapping.proportionOf(drawn.seconds),\n\t\t\tisKeptOnCancel: true,\n\t\t\tposition: (event) => -event.clientX,\n\t\t\ttravelPx: Math.max(1, (high - low) * this.zoom),\n\t\t};\n\t}\n\n\t#held(): undefined | { grabbed: Drawn; seconds: number } {\n\t\tconst grabbed = this.#grabbed;\n\t\tconst { from } = this.scrubState();\n\t\tif (!grabbed || from === undefined) return undefined;\n\n\t\treturn { grabbed, seconds: this.value === from ? grabbed.seconds : this.value };\n\t}\n\n\t#readKindColours(): Map<string, string> {\n\t\tconst kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];\n\t\tconst probes = kinds.map((kind) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.className = 'sonic-waveform-label';\n\t\t\twriteKind(probe, kind, '--_sonic-waveform-marker');\n\n\t\t\treturn probe;\n\t\t});\n\n\t\tthis.control.append(...probes);\n\n\t\tconst read = probes.map((probe) => getComputedStyle(probe).color);\n\n\t\tfor (const probe of probes) probe.remove();\n\n\t\treturn new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.scrubState().played;\n\t}\n\n\t#zoomTo(target: number): void {\n\t\tconst next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);\n\t\tif (next === this.zoom) return;\n\n\t\tthis.zoom = next;\n\t\tthis.dispatchEvent(new Event('sonic-zoom', { bubbles: true }));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Seconds } from "../lib/units.js";
|
|
1
2
|
import { TimeRegions } from "../lib/time-regions.js";
|
|
2
3
|
import { SurfaceFrame } from "../lib/canvas-surface.js";
|
|
3
4
|
import { SonicWaveElement } from "./wave-element.js";
|
|
@@ -24,10 +25,12 @@ declare const lengths: {
|
|
|
24
25
|
};
|
|
25
26
|
export declare class SonicWavestrip extends SonicWaveElement<Colour, Length> {
|
|
26
27
|
#private;
|
|
28
|
+
/** Loaded spans, in seconds */
|
|
27
29
|
get buffered(): Array<[number, number]>;
|
|
28
30
|
set buffered(regions: TimeRegions | undefined);
|
|
29
31
|
get cancellable(): boolean;
|
|
30
32
|
set cancellable(isCancellable: boolean);
|
|
33
|
+
/** One amplitude from 0 to 1 per slice of the whole track, any length; resampled to the bars and never normalised */
|
|
31
34
|
get peaks(): ArrayLike<number> | undefined;
|
|
32
35
|
set peaks(peaks: ArrayLike<number> | undefined);
|
|
33
36
|
protected readonly control: HTMLDivElement;
|
|
@@ -47,7 +50,8 @@ export declare class SonicWavestrip extends SonicWaveElement<Colour, Length> {
|
|
|
47
50
|
};
|
|
48
51
|
protected readonly sheet = "wavestrip.css";
|
|
49
52
|
protected readonly sizeProperty = "--_sonic-wavestrip-size";
|
|
50
|
-
|
|
53
|
+
/** Viewport x of a time, clamped to the strip */
|
|
54
|
+
clientXOf(value: Seconds): number;
|
|
51
55
|
connectedCallback(): void;
|
|
52
56
|
protected connect(signal: AbortSignal): void;
|
|
53
57
|
protected draw(): void;
|