@xsynaptic/sonic-ui 0.21.0 → 0.23.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 +16 -0
- package/README.md +1 -0
- package/dist/default/elements/envelope.js +3 -6
- package/dist/default/elements/split.js +4 -7
- package/dist/default/elements/value-element.js +3 -0
- package/dist/default/elements/wave-element.js +1 -0
- package/dist/default/elements/waveform.js +48 -4
- package/dist/default/elements/wavestrip.js +1 -0
- package/dist/default/lib/waveform-bands.js +85 -0
- package/dist/default/lib/waveform-scene.js +52 -16
- package/dist/dev/elements/envelope.js +3 -6
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/split.js +4 -7
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/value-element.js +3 -0
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +3 -2
- package/dist/dev/elements/wave-element.js +1 -0
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +13 -4
- package/dist/dev/elements/waveform.js +48 -4
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +1 -0
- package/dist/dev/elements/wavestrip.js +1 -0
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/index.d.ts +2 -1
- package/dist/dev/lib/waveform-bands.d.ts +13 -0
- package/dist/dev/lib/waveform-bands.js +86 -0
- package/dist/dev/lib/waveform-bands.js.map +1 -0
- package/dist/dev/lib/waveform-scene.d.ts +6 -1
- package/dist/dev/lib/waveform-scene.js +52 -16
- package/dist/dev/lib/waveform-scene.js.map +1 -1
- package/dist/skins/amber.css +1 -0
- package/dist/skins/flat.css +0 -1
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +1 -0
- package/dist/skins/slate.css +1 -0
- package/dist/styles/material/readout.css +4 -2
- package/dist/styles/material/value.css +1 -1
- package/dist/styles/slider.css +29 -11
- package/dist/styles/waveform.css +29 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @xsynaptic/sonic-ui
|
|
2
2
|
|
|
3
|
+
## 0.23.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- - Waveform: `bands` (`WaveformBands`) tints the wave from levels per frequency band, coloured by `--sonic-waveform-band-1` upward and scaled by `--sonic-waveform-band-opacity`. Call `repaint()` after changing a band token.
|
|
8
|
+
- Breaking: `--sonic-waveform-edge` is replaced by `--sonic-waveform-edge-shade`, 0 to 1, how far the wave's edges mix towards `--sonic-shade`. Unset, the wave is flat; the skins with relief set 0.55.
|
|
9
|
+
|
|
10
|
+
## 0.22.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- - `--sonic-entry-selection` colours the selected text of a typed entry, which opens with its value selected. Unset, it stays the control's lit colour.
|
|
15
|
+
- `--sonic-slider-groove-hover-delay` is how long a slider's groove waits under a pointer before it thickens to `--sonic-slider-groove-hover-ratio`. A press or a keyboard focus thickens it at once and leaving thins it at once. Unset, there is no wait.
|
|
16
|
+
- `--sonic-readout-depth` scales a readout's cast shadow, inner shine and lighter centre from 0 to 1. Unset, it follows the relief.
|
|
17
|
+
- Behaviour change: a readout no longer takes `--sonic-glass-depth` from a screen around it. Inside a screen flattened with `--sonic-glass-depth: 0` its centre follows the relief again, as its shadow always did; set `--sonic-readout-depth: 0` to flatten it.
|
|
18
|
+
|
|
3
19
|
## 0.21.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ import '@xsynaptic/sonic-ui/define';
|
|
|
48
48
|
- Events bubble and carry no detail: read the property when one fires. A control knows nothing about playback, so wire its events to your own state and write the property back.
|
|
49
49
|
- A value control fires `input` as its value moves and `change` once when a gesture ends. A drag owns the value: a write to `value` during one is dropped.
|
|
50
50
|
- `formatValue` and `parseValue` are properties holding functions. Once `parseValue` is set it has to read what `formatValue` writes, or typed entry opens on text it cannot take.
|
|
51
|
+
- To show a control without taking input, set `inert` on the host. It leaves the tab order and the accessibility tree and ignores presses, and it keeps its colours and keeps drawing; `disabled` dims it and is announced as disabled.
|
|
51
52
|
- A display takes its signal as data, and nothing in the library fetches, decodes or opens an `AudioContext`.
|
|
52
53
|
- States a control sets itself are custom states on the host, selected with `:state()`.
|
|
53
54
|
- The LED, the panel, the ring and the screen have no behaviour, so they are classes on your own elements (`sonic-led`, `sonic-panel`, `sonic-ring`, `sonic-screen`). A control draws no glass of its own: wrap one in a screen for the encased look.
|
|
@@ -139,11 +139,10 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
139
139
|
#line = requireChild(this.#envelope, ".sonic-envelope-line", SVGElement);
|
|
140
140
|
#readout = new Readout(requireChild(this.#envelope, ".sonic-envelope-readout", HTMLDivElement));
|
|
141
141
|
#shape = adsrShape({});
|
|
142
|
-
#signal;
|
|
143
142
|
#waiting = /* @__PURE__ */ new Set();
|
|
144
143
|
#warned = /* @__PURE__ */ new Set();
|
|
145
144
|
attributeChangedCallback(name) {
|
|
146
|
-
if (!this
|
|
145
|
+
if (!this.isBound()) return;
|
|
147
146
|
if (name === "disabled") {
|
|
148
147
|
this.#gesture?.end();
|
|
149
148
|
this.#draw();
|
|
@@ -158,12 +157,10 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
158
157
|
}
|
|
159
158
|
connect(signal) {
|
|
160
159
|
const envelope = this.#envelope;
|
|
161
|
-
this.#signal = signal;
|
|
162
160
|
this.keepControl(envelope, signal);
|
|
163
161
|
this.#bind();
|
|
164
162
|
this.#bindPointer(envelope, signal);
|
|
165
163
|
signal.addEventListener("abort", () => {
|
|
166
|
-
this.#signal = void 0;
|
|
167
164
|
this.#unbind();
|
|
168
165
|
}, { once: true });
|
|
169
166
|
}
|
|
@@ -201,7 +198,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
201
198
|
release: ({ drives }, moved) => {
|
|
202
199
|
for (const { axis, binding } of drives) {
|
|
203
200
|
if (!moved.includes(axis)) continue;
|
|
204
|
-
binding.
|
|
201
|
+
binding.link.change();
|
|
205
202
|
}
|
|
206
203
|
},
|
|
207
204
|
toggle: (isDragging) => {
|
|
@@ -308,7 +305,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
308
305
|
this.#waiting.add(key);
|
|
309
306
|
arrives(() => {
|
|
310
307
|
this.#waiting.delete(key);
|
|
311
|
-
if (this
|
|
308
|
+
if (this.isBound()) this.#bind();
|
|
312
309
|
});
|
|
313
310
|
}
|
|
314
311
|
#renderReadout() {
|
|
@@ -58,13 +58,12 @@ var SonicSplit = class extends SonicElement {
|
|
|
58
58
|
#isWarned = false;
|
|
59
59
|
#isWriting = false;
|
|
60
60
|
#members = [];
|
|
61
|
-
#signal;
|
|
62
61
|
#snapshot;
|
|
63
62
|
#total = 0;
|
|
64
63
|
#waiting = /* @__PURE__ */ new Set();
|
|
65
64
|
attributeChangedCallback(name) {
|
|
66
|
-
if ((name === "mode" || name === "total") && this
|
|
67
|
-
if (name === "disabled" && this
|
|
65
|
+
if ((name === "mode" || name === "total") && this.isBound()) this.#bind();
|
|
66
|
+
if (name === "disabled" && this.isBound()) this.#limit();
|
|
68
67
|
}
|
|
69
68
|
connectedCallback() {
|
|
70
69
|
this.upgradeProperties("mode", "total");
|
|
@@ -74,7 +73,6 @@ var SonicSplit = class extends SonicElement {
|
|
|
74
73
|
const observer = new MutationObserver((records) => {
|
|
75
74
|
if (records.some((record) => record.attributeName !== null && lockAttributes.includes(record.attributeName)) || !this.#isSameMembers(this.#find())) this.#bind();
|
|
76
75
|
});
|
|
77
|
-
this.#signal = signal;
|
|
78
76
|
if (this.internals) this.internals.role = "group";
|
|
79
77
|
observer.observe(this, {
|
|
80
78
|
attributeFilter: lockAttributes,
|
|
@@ -87,7 +85,6 @@ var SonicSplit = class extends SonicElement {
|
|
|
87
85
|
}, { signal });
|
|
88
86
|
signal.addEventListener("abort", () => {
|
|
89
87
|
observer.disconnect();
|
|
90
|
-
this.#signal = void 0;
|
|
91
88
|
this.#isTotalKept = false;
|
|
92
89
|
this.#unbind();
|
|
93
90
|
}, { once: true });
|
|
@@ -194,7 +191,7 @@ var SonicSplit = class extends SonicElement {
|
|
|
194
191
|
queueMicrotask(() => {
|
|
195
192
|
this.#isWriting = true;
|
|
196
193
|
try {
|
|
197
|
-
for (const {
|
|
194
|
+
for (const { link } of changed) link.change();
|
|
198
195
|
} finally {
|
|
199
196
|
this.#isWriting = false;
|
|
200
197
|
}
|
|
@@ -228,7 +225,7 @@ var SonicSplit = class extends SonicElement {
|
|
|
228
225
|
this.#waiting.add(tag);
|
|
229
226
|
customElements.whenDefined(tag).then(() => {
|
|
230
227
|
this.#waiting.delete(tag);
|
|
231
|
-
if (this
|
|
228
|
+
if (this.isBound()) this.#bind();
|
|
232
229
|
});
|
|
233
230
|
}
|
|
234
231
|
#warnReach() {}
|
|
@@ -52,6 +52,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
52
52
|
];
|
|
53
53
|
static {
|
|
54
54
|
link = (element) => ({
|
|
55
|
+
change: () => {
|
|
56
|
+
element.dispatchEvent(new Event("change", { bubbles: true }));
|
|
57
|
+
},
|
|
55
58
|
input: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),
|
|
56
59
|
isDisabled: () => element.isDisabled(),
|
|
57
60
|
isHeld: () => element.#isHeld(),
|
|
@@ -7,7 +7,8 @@ import { isKind, writeKind } from "../lib/marker-band.js";
|
|
|
7
7
|
import { createLabelRider } from "../lib/marker-rider.js";
|
|
8
8
|
import { createPeaksRequest } from "../lib/peaks-request.js";
|
|
9
9
|
import { createTrackingClock } from "../lib/tracking-clock.js";
|
|
10
|
-
import {
|
|
10
|
+
import { bandTable } from "../lib/waveform-bands.js";
|
|
11
|
+
import { bandChannels, paintWaveform, waveformColours, waveformNumbers } from "../lib/waveform-scene.js";
|
|
11
12
|
import { bindZoom, bindZoomKeys } from "../lib/zoom-gesture.js";
|
|
12
13
|
|
|
13
14
|
//#region src/elements/waveform.ts
|
|
@@ -54,6 +55,14 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
54
55
|
"zoom-min",
|
|
55
56
|
"zoomable"
|
|
56
57
|
];
|
|
58
|
+
get bands() {
|
|
59
|
+
return this.#bands;
|
|
60
|
+
}
|
|
61
|
+
set bands(bands) {
|
|
62
|
+
this.#bands = bands;
|
|
63
|
+
this.#bandTable = bandTable(bands);
|
|
64
|
+
this.#peaksRequest.changed();
|
|
65
|
+
}
|
|
57
66
|
get peaks() {
|
|
58
67
|
return this.#peaks;
|
|
59
68
|
}
|
|
@@ -157,8 +166,12 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
157
166
|
canvas = requireChild(this.control, ".sonic-waveform-canvas", HTMLCanvasElement);
|
|
158
167
|
colours = waveformColours;
|
|
159
168
|
lengths = {};
|
|
169
|
+
numbers = waveformNumbers;
|
|
160
170
|
sheet = "waveform.css";
|
|
161
171
|
sizeProperty = "--_sonic-waveform-size";
|
|
172
|
+
#bandColours = [];
|
|
173
|
+
#bands;
|
|
174
|
+
#bandTable;
|
|
162
175
|
#clock = createTrackingClock();
|
|
163
176
|
#drawn;
|
|
164
177
|
#ghost = requireChild(this.control, ".sonic-waveform-ghost", HTMLDivElement);
|
|
@@ -182,13 +195,14 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
182
195
|
insetProperty: "--_sonic-waveform-label-inset",
|
|
183
196
|
parkProperty: "--_sonic-waveform-label-park"
|
|
184
197
|
});
|
|
198
|
+
#strip;
|
|
185
199
|
#zoomGesture;
|
|
186
200
|
attributeChangedCallback(name) {
|
|
187
201
|
super.attributeChangedCallback(name);
|
|
188
202
|
if (name === "zoomable") this.#zoomGesture?.sync();
|
|
189
203
|
}
|
|
190
204
|
connectedCallback() {
|
|
191
|
-
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "renderLabel", "requestPeaks", "zoom", "zoomable", "zoomMax", "zoomMin");
|
|
205
|
+
this.upgradeProperties("bands", "peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "renderLabel", "requestPeaks", "zoom", "zoomable", "zoomMax", "zoomMin");
|
|
192
206
|
super.connectedCallback();
|
|
193
207
|
}
|
|
194
208
|
connect(signal) {
|
|
@@ -262,8 +276,10 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
262
276
|
if (isRebuilt) this.#kindColours = this.#readKindColours();
|
|
263
277
|
paintWaveform(context, {
|
|
264
278
|
...view,
|
|
265
|
-
colours: look.colours
|
|
279
|
+
colours: look.colours,
|
|
280
|
+
numbers: look.numbers
|
|
266
281
|
}, {
|
|
282
|
+
bands: this.#tint(isRebuilt),
|
|
267
283
|
kindColours: this.#kindColours,
|
|
268
284
|
markers: this.markerList(),
|
|
269
285
|
peaks: this.#peaks,
|
|
@@ -328,6 +344,14 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
328
344
|
startSeconds: this.value
|
|
329
345
|
};
|
|
330
346
|
}
|
|
347
|
+
#readBandColours(strip, bandCount) {
|
|
348
|
+
const probes = Array.from({ length: bandCount }, (_probe, band) => {
|
|
349
|
+
const probe = document.createElement("div");
|
|
350
|
+
probe.style.setProperty("color", `var(--sonic-waveform-band-${String(band + 1)}, transparent)`);
|
|
351
|
+
return probe;
|
|
352
|
+
});
|
|
353
|
+
return this.#readProbes(probes).map((colour) => bandChannels(strip, colour));
|
|
354
|
+
}
|
|
331
355
|
#readKindColours() {
|
|
332
356
|
const kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];
|
|
333
357
|
const probes = kinds.map((kind) => {
|
|
@@ -336,14 +360,34 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
336
360
|
writeKind(probe, kind, "--_sonic-waveform-marker");
|
|
337
361
|
return probe;
|
|
338
362
|
});
|
|
363
|
+
const read = this.#readProbes(probes);
|
|
364
|
+
return new Map(kinds.map((kind, index) => [kind, read[index] ?? ""]));
|
|
365
|
+
}
|
|
366
|
+
#readProbes(probes) {
|
|
339
367
|
this.control.append(...probes);
|
|
340
368
|
const read = probes.map((probe) => getComputedStyle(probe).color);
|
|
341
369
|
for (const probe of probes) probe.remove();
|
|
342
|
-
return
|
|
370
|
+
return read;
|
|
343
371
|
}
|
|
344
372
|
#sourceSeconds() {
|
|
345
373
|
return this.#readTime?.() ?? this.scrubState().played;
|
|
346
374
|
}
|
|
375
|
+
#tint(isRebuilt) {
|
|
376
|
+
const table = this.#bandTable;
|
|
377
|
+
if (!table) return void 0;
|
|
378
|
+
const strip = this.#strip ?? document.createElement("canvas").getContext("2d", { willReadFrequently: true }) ?? void 0;
|
|
379
|
+
if (!strip) return void 0;
|
|
380
|
+
this.#strip = strip;
|
|
381
|
+
if (isRebuilt || this.#bandColours.length !== table.bandCount) {
|
|
382
|
+
strip.canvas.height = 1;
|
|
383
|
+
this.#bandColours = this.#readBandColours(strip, table.bandCount);
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
colours: this.#bandColours,
|
|
387
|
+
strip,
|
|
388
|
+
table
|
|
389
|
+
};
|
|
390
|
+
}
|
|
347
391
|
#zoomTo(target) {
|
|
348
392
|
const next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);
|
|
349
393
|
if (next === this.zoom) return;
|
|
@@ -76,6 +76,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
76
76
|
canvas = requireChild(this.control, ".sonic-wavestrip-canvas", HTMLCanvasElement);
|
|
77
77
|
colours = colours;
|
|
78
78
|
lengths = lengths;
|
|
79
|
+
numbers = {};
|
|
79
80
|
sheet = "wavestrip.css";
|
|
80
81
|
sizeProperty = "--_sonic-wavestrip-size";
|
|
81
82
|
#bars;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//#region src/lib/waveform-bands.ts
|
|
2
|
+
function fullScaleOf({ fullScale, levels }) {
|
|
3
|
+
if (fullScale !== void 0) return fullScale;
|
|
4
|
+
if (levels instanceof Uint8Array) return 255;
|
|
5
|
+
return levels instanceof Uint16Array ? 65535 : 1;
|
|
6
|
+
}
|
|
7
|
+
function amplitudeReader(bands) {
|
|
8
|
+
const fullScale = fullScaleOf(bands);
|
|
9
|
+
const minDecibels = Math.min(0, bands.minDecibels ?? 0);
|
|
10
|
+
return (level) => {
|
|
11
|
+
const proportion = Math.min(1, level / fullScale);
|
|
12
|
+
if (!(proportion > 0)) return 0;
|
|
13
|
+
return minDecibels < 0 ? 10 ** (minDecibels * (1 - proportion) / 20) : proportion;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function bandTable(bands) {
|
|
17
|
+
if (!bands) return void 0;
|
|
18
|
+
const { bandCount, framesPerSecond, levels } = bands;
|
|
19
|
+
if (!Number.isSafeInteger(bandCount) || bandCount < 1 || !(framesPerSecond > 0)) return void 0;
|
|
20
|
+
const amplitudeOf = amplitudeReader(bands);
|
|
21
|
+
if (levels instanceof Float32Array) return {
|
|
22
|
+
bandCount,
|
|
23
|
+
framesPerSecond,
|
|
24
|
+
levels,
|
|
25
|
+
weightOf: amplitudeOf
|
|
26
|
+
};
|
|
27
|
+
const weights = Float32Array.from({ length: levels instanceof Uint8Array ? 256 : 65536 }, (_weight, level) => amplitudeOf(level));
|
|
28
|
+
return {
|
|
29
|
+
bandCount,
|
|
30
|
+
framesPerSecond,
|
|
31
|
+
levels,
|
|
32
|
+
weightOf: (level) => weights[level] ?? 0
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function mixGroup({ bandCount, levels, weightOf }, colours, [fromFrame, toFrame]) {
|
|
36
|
+
let red = 0;
|
|
37
|
+
let green = 0;
|
|
38
|
+
let blue = 0;
|
|
39
|
+
let total = 0;
|
|
40
|
+
for (let band = 0; band < bandCount; band += 1) {
|
|
41
|
+
const colour = colours[band];
|
|
42
|
+
if (!colour) continue;
|
|
43
|
+
let weight = 0;
|
|
44
|
+
for (let bandFrame = fromFrame; bandFrame < toFrame; bandFrame += 1) weight += weightOf(levels[bandFrame * bandCount + band] ?? 0);
|
|
45
|
+
red += weight * colour[0];
|
|
46
|
+
green += weight * colour[1];
|
|
47
|
+
blue += weight * colour[2];
|
|
48
|
+
total += weight;
|
|
49
|
+
}
|
|
50
|
+
if (total === 0) return void 0;
|
|
51
|
+
const scale = 255 / Math.max(red, green, blue, Number.MIN_VALUE);
|
|
52
|
+
return [
|
|
53
|
+
red * scale,
|
|
54
|
+
green * scale,
|
|
55
|
+
blue * scale,
|
|
56
|
+
255
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
function bandStrip(table, colours, view) {
|
|
60
|
+
if (view.pixelsPerSecond <= 0 || !colours.some(Boolean)) return void 0;
|
|
61
|
+
const { bandCount, framesPerSecond, levels } = table;
|
|
62
|
+
const pxPerFrame = view.pixelsPerSecond / framesPerSecond;
|
|
63
|
+
const frames = Math.max(1, Math.round(1 / pxPerFrame));
|
|
64
|
+
const groupWidth = frames * pxPerFrame;
|
|
65
|
+
const openingFrame = view.startSeconds * framesPerSecond;
|
|
66
|
+
const groupsTotal = Math.ceil(levels.length / bandCount / frames);
|
|
67
|
+
const first = Math.max(0, Math.floor(openingFrame / frames) - 1);
|
|
68
|
+
const count = Math.min(groupsTotal, Math.ceil((openingFrame + view.width / pxPerFrame) / frames) + 1) - first;
|
|
69
|
+
if (count < 1) return void 0;
|
|
70
|
+
const pixels = new Uint8ClampedArray(count * 4);
|
|
71
|
+
for (let index = 0; index < count; index += 1) {
|
|
72
|
+
const fromFrame = (first + index) * frames;
|
|
73
|
+
const mixed = mixGroup(table, colours, [fromFrame, fromFrame + frames]);
|
|
74
|
+
if (mixed) pixels.set(mixed, index * 4);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
count,
|
|
78
|
+
groupWidth,
|
|
79
|
+
pixels,
|
|
80
|
+
x: (first * frames - openingFrame) * pxPerFrame
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
export { bandStrip, bandTable };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { clamp } from "./math.js";
|
|
2
2
|
import { isKind } from "./marker-band.js";
|
|
3
|
+
import { bandStrip } from "./waveform-bands.js";
|
|
3
4
|
import { waveformBuckets } from "./waveform-buckets.js";
|
|
4
5
|
|
|
5
6
|
//#region src/lib/waveform-scene.ts
|
|
@@ -8,8 +9,13 @@ const waveformColours = {
|
|
|
8
9
|
grid: "--_sonic-waveform-grid",
|
|
9
10
|
marker: "--_sonic-waveform-marker",
|
|
10
11
|
placeholder: "--_sonic-waveform-placeholder",
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
shade: "--_sonic-waveform-shade",
|
|
13
|
+
shadeClear: "--_sonic-waveform-shade-clear",
|
|
14
|
+
wave: "--_sonic-waveform-wave"
|
|
15
|
+
};
|
|
16
|
+
const waveformNumbers = {
|
|
17
|
+
bandOpacity: "--_sonic-waveform-band-opacity",
|
|
18
|
+
edgeShade: "--_sonic-waveform-edge-shade"
|
|
13
19
|
};
|
|
14
20
|
const amplitudeMargin = .94;
|
|
15
21
|
const hatchPitchPx = 8;
|
|
@@ -53,27 +59,55 @@ function paintPlaceholders(context, scene) {
|
|
|
53
59
|
context.strokeStyle = scene.colours.placeholder;
|
|
54
60
|
context.stroke();
|
|
55
61
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
62
|
+
function bandChannels(strip, colour) {
|
|
63
|
+
strip.clearRect(0, 0, 1, 1);
|
|
64
|
+
strip.fillStyle = colour;
|
|
65
|
+
strip.fillRect(0, 0, 1, 1);
|
|
66
|
+
const [red = 0, green = 0, blue = 0, alpha = 0] = strip.getImageData(0, 0, 1, 1).data;
|
|
67
|
+
return alpha === 0 ? void 0 : [
|
|
68
|
+
red,
|
|
69
|
+
green,
|
|
70
|
+
blue
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
function paintTint(context, scene, tint) {
|
|
74
|
+
const strip = bandStrip(tint.table, tint.colours, scene);
|
|
75
|
+
if (!strip) return;
|
|
76
|
+
const { canvas } = tint.strip;
|
|
77
|
+
const { count } = strip;
|
|
78
|
+
if (canvas.width < count) canvas.width = count;
|
|
79
|
+
tint.strip.putImageData(new ImageData(strip.pixels, count, 1), 0, 0);
|
|
80
|
+
context.globalAlpha = scene.numbers.bandOpacity;
|
|
81
|
+
context.drawImage(canvas, 0, 0, count, 1, strip.x, 0, count * strip.groupWidth, scene.height);
|
|
82
|
+
}
|
|
83
|
+
function paintEdgeShade(context, scene) {
|
|
84
|
+
const { shade, shadeClear } = scene.colours;
|
|
58
85
|
const gradient = context.createLinearGradient(0, 0, 0, scene.height);
|
|
59
|
-
gradient.addColorStop(0,
|
|
60
|
-
gradient.addColorStop(.5,
|
|
61
|
-
gradient.addColorStop(1,
|
|
62
|
-
|
|
86
|
+
gradient.addColorStop(0, shade);
|
|
87
|
+
gradient.addColorStop(.5, shadeClear);
|
|
88
|
+
gradient.addColorStop(1, shade);
|
|
89
|
+
context.globalAlpha = scene.numbers.edgeShade;
|
|
90
|
+
context.fillStyle = gradient;
|
|
91
|
+
context.fillRect(0, 0, scene.width, scene.height);
|
|
63
92
|
}
|
|
64
|
-
function paintWave(context, scene,
|
|
65
|
-
const buckets = waveformBuckets(peaks, scene);
|
|
93
|
+
function paintWave(context, scene, drawn) {
|
|
94
|
+
const buckets = drawn.peaks ? waveformBuckets(drawn.peaks, scene) : [];
|
|
66
95
|
const [first] = buckets;
|
|
67
96
|
if (!first) return;
|
|
68
97
|
const center = scene.height / 2;
|
|
69
98
|
const amplitudePx = center * amplitudeMargin;
|
|
70
|
-
|
|
99
|
+
const { bandOpacity, edgeShade } = scene.numbers;
|
|
100
|
+
context.fillStyle = scene.colours.wave;
|
|
71
101
|
context.beginPath();
|
|
72
102
|
context.moveTo(first.x, center);
|
|
73
103
|
for (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);
|
|
74
104
|
for (const bucket of buckets.toReversed()) context.lineTo(bucket.x, center - bucket.low * amplitudePx);
|
|
75
105
|
context.closePath();
|
|
76
106
|
context.fill();
|
|
107
|
+
context.globalCompositeOperation = "source-atop";
|
|
108
|
+
if (drawn.bands && bandOpacity > 0) paintTint(context, scene, drawn.bands);
|
|
109
|
+
if (edgeShade > 0) paintEdgeShade(context, scene);
|
|
110
|
+
context.globalAlpha = 1;
|
|
77
111
|
}
|
|
78
112
|
function paintMarkers(context, scene, drawn) {
|
|
79
113
|
const line = Math.max(1, Math.round(scene.dpr));
|
|
@@ -93,16 +127,18 @@ function paintWaveform(context, scene, drawn) {
|
|
|
93
127
|
const [opening, closing] = [x(drawn.range[0]), x(drawn.range[1])];
|
|
94
128
|
const [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];
|
|
95
129
|
context.clearRect(0, 0, width, height);
|
|
96
|
-
|
|
97
|
-
|
|
130
|
+
paintWave(context, scene, drawn);
|
|
131
|
+
context.globalCompositeOperation = "destination-over";
|
|
132
|
+
paintPlaceholders(context, scene);
|
|
98
133
|
context.fillStyle = grid;
|
|
99
134
|
if (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);
|
|
100
|
-
|
|
101
|
-
|
|
135
|
+
paintHatch(context, scene, [0, from]);
|
|
136
|
+
paintHatch(context, scene, [to, width]);
|
|
137
|
+
context.globalCompositeOperation = "source-over";
|
|
102
138
|
context.fillStyle = ends;
|
|
103
139
|
for (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);
|
|
104
140
|
paintMarkers(context, scene, drawn);
|
|
105
141
|
}
|
|
106
142
|
|
|
107
143
|
//#endregion
|
|
108
|
-
export { paintWaveform, waveformColours };
|
|
144
|
+
export { bandChannels, paintWaveform, waveformColours, waveformNumbers };
|
|
@@ -139,11 +139,10 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
139
139
|
#line = requireChild(this.#envelope, ".sonic-envelope-line", SVGElement);
|
|
140
140
|
#readout = new Readout(requireChild(this.#envelope, ".sonic-envelope-readout", HTMLDivElement));
|
|
141
141
|
#shape = adsrShape({});
|
|
142
|
-
#signal;
|
|
143
142
|
#waiting = /* @__PURE__ */ new Set();
|
|
144
143
|
#warned = /* @__PURE__ */ new Set();
|
|
145
144
|
attributeChangedCallback(name) {
|
|
146
|
-
if (!this
|
|
145
|
+
if (!this.isBound()) return;
|
|
147
146
|
if (name === "disabled") {
|
|
148
147
|
this.#gesture?.end();
|
|
149
148
|
this.#draw();
|
|
@@ -158,7 +157,6 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
158
157
|
}
|
|
159
158
|
connect(signal) {
|
|
160
159
|
const envelope = this.#envelope;
|
|
161
|
-
this.#signal = signal;
|
|
162
160
|
this.keepControl(envelope, signal);
|
|
163
161
|
this.#bind();
|
|
164
162
|
this.checkStyles(envelope, "envelope.css", {
|
|
@@ -167,7 +165,6 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
167
165
|
});
|
|
168
166
|
this.#bindPointer(envelope, signal);
|
|
169
167
|
signal.addEventListener("abort", () => {
|
|
170
|
-
this.#signal = void 0;
|
|
171
168
|
this.#unbind();
|
|
172
169
|
}, { once: true });
|
|
173
170
|
}
|
|
@@ -205,7 +202,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
205
202
|
release: ({ drives }, moved) => {
|
|
206
203
|
for (const { axis, binding } of drives) {
|
|
207
204
|
if (!moved.includes(axis)) continue;
|
|
208
|
-
binding.
|
|
205
|
+
binding.link.change();
|
|
209
206
|
}
|
|
210
207
|
},
|
|
211
208
|
toggle: (isDragging) => {
|
|
@@ -312,7 +309,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
312
309
|
this.#waiting.add(key);
|
|
313
310
|
arrives(() => {
|
|
314
311
|
this.#waiting.delete(key);
|
|
315
|
-
if (this
|
|
312
|
+
if (this.isBound()) this.#bind();
|
|
316
313
|
});
|
|
317
314
|
}
|
|
318
315
|
#renderReadout() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { FieldGesture, FieldHold } from '#elements/field-gesture.ts';\nimport type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldPoint } from '#lib/field.ts';\n\nimport { bindFieldGesture } from '#elements/field-gesture.ts';\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleHold extends FieldHold {\n\tdrives: Array<Drive>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-bracket\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget pointerType(): string | undefined {\n\t\treturn this.#gesture?.pointerType();\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #bracket = requireChild(this.#envelope, '.sonic-envelope-bracket', HTMLDivElement);\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\t#gesture: FieldGesture<HandleHold> | undefined;\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\t#signal: AbortSignal | undefined;\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.#signal) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#gesture?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.#signal = signal;\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(envelope, 'envelope.css', {\n\t\t\t\tproperty: 'margin-bottom',\n\t\t\t\tselector: '[popover]',\n\t\t\t});\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#signal = undefined;\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bind(): void {\n\t\tthis.#gesture?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.model.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#gesture = bindFieldGesture(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tclaim: this.#claim,\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tinput: ({ drives }, next) => {\n\t\t\t\t\tthis.#isDriving = true;\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tconst value = next[axis];\n\n\t\t\t\t\t\tif (value !== undefined) binding.link.input(value);\n\t\t\t\t\t}\n\t\t\t\t\tthis.#isDriving = false;\n\t\t\t\t\tthis.#draw();\n\t\t\t\t},\n\t\t\t\trelease: ({ drives }, moved) => {\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tif (!moved.includes(axis)) continue;\n\n\t\t\t\t\t\tbinding.element.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawBracket();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawBracket(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#bracket.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#bracket,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleHold | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tthis.#held = pressed.part;\n\t\tthis.#drawBracket();\n\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst axes: HandleHold['axes'] = {};\n\n\t\tfor (const { axis, binding } of pressed.drives) {\n\t\t\taxes[axis] = { model: binding.link.model, travelPx: travel[axis] };\n\t\t}\n\n\t\treturn { ...pressed, axes };\n\t}\n\n\t#pressed(event: PointerEvent): Omit<HandleHold, 'axes'> | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst model = this.#bindings.get(stage)?.link.model;\n\n\t\t\tproportions[stage] = model?.mapping().proportionOf(model.value);\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.model.value;\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.#signal) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#gesture?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { FieldGesture, FieldHold } from '#elements/field-gesture.ts';\nimport type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldPoint } from '#lib/field.ts';\n\nimport { bindFieldGesture } from '#elements/field-gesture.ts';\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleHold extends FieldHold {\n\tdrives: Array<Drive>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-bracket\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget pointerType(): string | undefined {\n\t\treturn this.#gesture?.pointerType();\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #bracket = requireChild(this.#envelope, '.sonic-envelope-bracket', HTMLDivElement);\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\t#gesture: FieldGesture<HandleHold> | undefined;\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.isBound()) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#gesture?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(envelope, 'envelope.css', {\n\t\t\t\tproperty: 'margin-bottom',\n\t\t\t\tselector: '[popover]',\n\t\t\t});\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bind(): void {\n\t\tthis.#gesture?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.model.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#gesture = bindFieldGesture(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tclaim: this.#claim,\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tinput: ({ drives }, next) => {\n\t\t\t\t\tthis.#isDriving = true;\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tconst value = next[axis];\n\n\t\t\t\t\t\tif (value !== undefined) binding.link.input(value);\n\t\t\t\t\t}\n\t\t\t\t\tthis.#isDriving = false;\n\t\t\t\t\tthis.#draw();\n\t\t\t\t},\n\t\t\t\trelease: ({ drives }, moved) => {\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tif (!moved.includes(axis)) continue;\n\n\t\t\t\t\t\tbinding.link.change();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawBracket();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawBracket(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#bracket.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#bracket,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleHold | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tthis.#held = pressed.part;\n\t\tthis.#drawBracket();\n\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst axes: HandleHold['axes'] = {};\n\n\t\tfor (const { axis, binding } of pressed.drives) {\n\t\t\taxes[axis] = { model: binding.link.model, travelPx: travel[axis] };\n\t\t}\n\n\t\treturn { ...pressed, axes };\n\t}\n\n\t#pressed(event: PointerEvent): Omit<HandleHold, 'axes'> | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst model = this.#bindings.get(stage)?.link.model;\n\n\t\t\tproportions[stage] = model?.mapping().proportionOf(model.value);\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.model.value;\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.isBound()) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#gesture?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
|