@xsynaptic/sonic-ui 0.5.0 → 0.6.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/README.md +3 -1
- package/dist/default/dat.js +5 -4
- package/dist/default/define/button.js +1 -1
- package/dist/default/define.js +2 -2
- package/dist/default/elements/button.js +3 -3
- package/dist/default/elements/envelope.js +32 -16
- package/dist/default/elements/meter.js +11 -7
- package/dist/default/elements/number.js +1 -1
- package/dist/default/elements/radio-group.js +43 -45
- package/dist/default/elements/readout.js +1 -1
- package/dist/default/elements/sonic-element.js +56 -29
- package/dist/default/elements/switch.js +32 -38
- package/dist/default/elements/value-element.js +111 -108
- package/dist/default/elements/wave-element.js +5 -2
- package/dist/default/elements/waveform.js +35 -12
- package/dist/default/elements/wavestrip.js +18 -15
- package/dist/default/elements/xy.js +32 -18
- package/dist/default/index.js +2 -2
- package/dist/default/lib/adsr-shape.js +5 -0
- package/dist/default/lib/ballistics.js +2 -2
- package/dist/default/lib/canvas-surface.js +13 -2
- package/dist/default/lib/distribute.js +1 -1
- package/dist/default/lib/drag-step.js +4 -4
- package/dist/default/lib/frame-timeline.js +2 -1
- package/dist/default/lib/marker-band.js +54 -10
- package/dist/default/lib/marker-rider.js +10 -4
- package/dist/default/lib/math.js +6 -1
- package/dist/default/lib/pointer-drag.js +8 -5
- package/dist/default/lib/resample-peaks.js +11 -1
- package/dist/default/lib/write-attribute.js +1 -0
- package/dist/dev/dat.d.ts +2 -2
- package/dist/dev/dat.js +5 -4
- package/dist/dev/dat.js.map +1 -1
- package/dist/dev/define/button.js +1 -1
- package/dist/dev/define.d.ts +4 -4
- package/dist/dev/define.js +2 -2
- package/dist/dev/elements/button.js +3 -3
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +1 -1
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.js +32 -16
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +1 -1
- package/dist/dev/elements/meter.js +11 -7
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.js +1 -1
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/radio-group.js +43 -45
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/readout.js +1 -1
- package/dist/dev/elements/readout.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +1 -1
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +4 -2
- package/dist/dev/elements/sonic-element.js +79 -29
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/switch.js +32 -38
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +3 -3
- package/dist/dev/elements/value-element.js +111 -108
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +1 -0
- package/dist/dev/elements/wave-element.js +5 -2
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +1 -1
- package/dist/dev/elements/waveform.js +35 -12
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.js +18 -15
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.d.ts +3 -3
- package/dist/dev/elements/xy.js +32 -18
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +2 -2
- package/dist/dev/index.js +2 -2
- package/dist/dev/lib/adsr-shape.js +5 -0
- package/dist/dev/lib/adsr-shape.js.map +1 -1
- package/dist/dev/lib/ballistics.js +2 -2
- package/dist/dev/lib/ballistics.js.map +1 -1
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +13 -2
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/distribute.js +1 -1
- package/dist/dev/lib/distribute.js.map +1 -1
- package/dist/dev/lib/drag-step.js +4 -4
- package/dist/dev/lib/drag-step.js.map +1 -1
- package/dist/dev/lib/frame-timeline.js +2 -1
- package/dist/dev/lib/frame-timeline.js.map +1 -1
- package/dist/dev/lib/marker-band.js +54 -10
- package/dist/dev/lib/marker-band.js.map +1 -1
- package/dist/dev/lib/marker-rider.js +10 -4
- package/dist/dev/lib/marker-rider.js.map +1 -1
- package/dist/dev/lib/math.js +6 -1
- package/dist/dev/lib/math.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +8 -5
- package/dist/dev/lib/pointer-drag.js.map +1 -1
- package/dist/dev/lib/resample-peaks.js +11 -1
- package/dist/dev/lib/resample-peaks.js.map +1 -1
- package/dist/dev/lib/write-attribute.js +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -1
- package/dist/skins/amber.css +8 -0
- package/dist/skins/flat.css +25 -0
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +2 -0
- package/dist/skins/slate.css +2 -0
- package/dist/styles/button.css +38 -10
- package/dist/styles/dial.css +24 -16
- package/dist/styles/envelope.css +56 -11
- package/dist/styles/led.css +3 -3
- package/dist/styles/material.css +83 -41
- package/dist/styles/meter.css +8 -4
- package/dist/styles/panel.css +5 -3
- package/dist/styles/segmented.css +27 -5
- package/dist/styles/slider.css +2 -1
- package/dist/styles/switch.css +59 -20
- package/dist/styles/waveform.css +28 -5
- package/dist/styles/wavestrip.css +23 -6
- package/dist/styles/xy.css +61 -24
- package/package.json +2 -5
|
@@ -2,7 +2,7 @@ import { clampProportion } from "../lib/math.js";
|
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
3
|
import { readPxProperty } from "../lib/read-px-property.js";
|
|
4
4
|
import { SonicWaveElement } from "./wave-element.js";
|
|
5
|
-
import { createMarkerBand
|
|
5
|
+
import { createMarkerBand } from "../lib/marker-band.js";
|
|
6
6
|
import { stripBars, stripRegions } from "../lib/strip-scene.js";
|
|
7
7
|
|
|
8
8
|
//#region src/elements/wavestrip.ts
|
|
@@ -101,7 +101,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
101
101
|
]);
|
|
102
102
|
}
|
|
103
103
|
renderMarkers() {
|
|
104
|
-
this.#markerBand.render(this.
|
|
104
|
+
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
105
105
|
}
|
|
106
106
|
scrubsKeyRepeat() {
|
|
107
107
|
return true;
|
|
@@ -111,6 +111,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
111
111
|
return {
|
|
112
112
|
position: (event) => event.clientX,
|
|
113
113
|
startPx: box.left,
|
|
114
|
+
topPx: box.top,
|
|
114
115
|
travelPx: Math.max(1, box.width)
|
|
115
116
|
};
|
|
116
117
|
}
|
|
@@ -126,10 +127,11 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
126
127
|
#bindHover(strip, signal) {
|
|
127
128
|
strip.addEventListener("pointermove", (event) => {
|
|
128
129
|
if (event.pointerType === "touch" || event.buttons !== 0) return;
|
|
129
|
-
if (!this.readout
|
|
130
|
+
if (!this.readout) return;
|
|
130
131
|
const axis = this.#axis();
|
|
131
132
|
const at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);
|
|
132
|
-
|
|
133
|
+
const mapping = this.mapping();
|
|
134
|
+
this.hoverReadout(this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at)));
|
|
133
135
|
this.#placeReadout();
|
|
134
136
|
}, { signal });
|
|
135
137
|
for (const type of ["pointerdown", "pointerleave"]) strip.addEventListener(type, () => {
|
|
@@ -137,25 +139,26 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
137
139
|
}, { signal });
|
|
138
140
|
}
|
|
139
141
|
#grab(event) {
|
|
140
|
-
const
|
|
141
|
-
const at = (event.clientX - startPx) / axis.travelPx;
|
|
142
|
+
const axis = this.#axis();
|
|
143
|
+
const at = (event.clientX - axis.startPx) / axis.travelPx;
|
|
142
144
|
const outside = this.#outside();
|
|
143
145
|
const grabbed = {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
fromProportion: clampProportion(at),
|
|
147
|
+
position: axis.position,
|
|
148
|
+
travelPx: axis.travelPx
|
|
146
149
|
};
|
|
147
|
-
this.input(this.#markerAt(
|
|
150
|
+
this.input(this.#markerAt(event, axis) ?? this.mapping().valueAt(at));
|
|
148
151
|
return outside ? {
|
|
149
152
|
...grabbed,
|
|
150
153
|
outside
|
|
151
154
|
} : grabbed;
|
|
152
155
|
}
|
|
153
|
-
#markerAt(
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return index === void 0 ? void 0 :
|
|
156
|
+
#markerAt(event, { startPx, topPx, travelPx }) {
|
|
157
|
+
const index = this.#markerBand.at({
|
|
158
|
+
x: event.clientX - startPx,
|
|
159
|
+
y: event.clientY - topPx
|
|
160
|
+
}, travelPx);
|
|
161
|
+
return index === void 0 ? void 0 : this.markerList()[index]?.start;
|
|
159
162
|
}
|
|
160
163
|
#outside() {
|
|
161
164
|
if (!this.cancellable) return void 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clampProportion } from "../lib/math.js";
|
|
1
|
+
import { clampProportion, toNumber } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
3
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
4
4
|
import { SonicFormElement } from "./form-element.js";
|
|
@@ -7,9 +7,9 @@ import { Readout } from "./readout.js";
|
|
|
7
7
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
8
8
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
9
9
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
10
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
10
11
|
import { resetKeys, valueMapping } from "../lib/value-mapping.js";
|
|
11
12
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
12
|
-
import { bindDrag } from "../lib/pointer-drag.js";
|
|
13
13
|
|
|
14
14
|
//#region src/elements/xy.ts
|
|
15
15
|
const axes = ["x", "y"];
|
|
@@ -193,6 +193,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
193
193
|
#xy = renderXy();
|
|
194
194
|
#field = requireChild(this.#xy, ".sonic-xy-field", HTMLDivElement);
|
|
195
195
|
#formatValue;
|
|
196
|
+
#mapped;
|
|
196
197
|
#parts = {
|
|
197
198
|
x: requireChild(this.#xy, "[data-sonic-axis=\"x\"]", HTMLDivElement),
|
|
198
199
|
y: requireChild(this.#xy, "[data-sonic-axis=\"y\"]", HTMLDivElement)
|
|
@@ -202,18 +203,19 @@ var SonicXy = class extends SonicFormElement {
|
|
|
202
203
|
#readout = new Readout(requireChild(this.#xy, ".sonic-xy-readout", HTMLDivElement));
|
|
203
204
|
#values = {
|
|
204
205
|
x: {
|
|
205
|
-
|
|
206
|
+
asked: void 0,
|
|
206
207
|
value: 0
|
|
207
208
|
},
|
|
208
209
|
y: {
|
|
209
|
-
|
|
210
|
+
asked: void 0,
|
|
210
211
|
value: 0
|
|
211
212
|
}
|
|
212
213
|
};
|
|
213
214
|
attributeChangedCallback(name) {
|
|
215
|
+
this.#mapped = void 0;
|
|
214
216
|
if (name === "x" || name === "y") {
|
|
215
217
|
if (this.#pointerDrag?.current()) return;
|
|
216
|
-
this.#values[name].
|
|
218
|
+
this.#values[name].asked = void 0;
|
|
217
219
|
}
|
|
218
220
|
if (name === "disabled" && this.isDisabled()) this.#pointerDrag?.end();
|
|
219
221
|
this.#refresh();
|
|
@@ -223,12 +225,12 @@ var SonicXy = class extends SonicFormElement {
|
|
|
223
225
|
super.connectedCallback();
|
|
224
226
|
}
|
|
225
227
|
formResetCallback() {
|
|
226
|
-
for (const axis of axes) this.#values[axis].
|
|
228
|
+
for (const axis of axes) this.#values[axis].asked = void 0;
|
|
227
229
|
this.#refresh();
|
|
228
230
|
}
|
|
229
231
|
connect(signal) {
|
|
230
232
|
const xy = this.#xy;
|
|
231
|
-
this.
|
|
233
|
+
this.keepControl(xy, signal);
|
|
232
234
|
this.render();
|
|
233
235
|
this.checkStyles(xy, "xy.css");
|
|
234
236
|
this.#bindPointer(xy, signal);
|
|
@@ -238,6 +240,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
238
240
|
return this.#parts[this.#currentAxis];
|
|
239
241
|
}
|
|
240
242
|
render() {
|
|
243
|
+
if (!this.isBound()) return;
|
|
241
244
|
const { style } = this.#xy;
|
|
242
245
|
const mappings = this.#mappings();
|
|
243
246
|
for (const axis of axes) {
|
|
@@ -365,7 +368,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
365
368
|
return true;
|
|
366
369
|
}
|
|
367
370
|
#keyTo(axis, key) {
|
|
368
|
-
const next = this.#
|
|
371
|
+
const next = this.#mappings()[axis].keyTarget(key, this.#values[axis].value);
|
|
369
372
|
const hasMoved = this.#set(axis, next ?? NaN);
|
|
370
373
|
this.#currentAxis = axis;
|
|
371
374
|
this.render();
|
|
@@ -390,17 +393,19 @@ var SonicXy = class extends SonicFormElement {
|
|
|
390
393
|
});
|
|
391
394
|
}
|
|
392
395
|
#mappings() {
|
|
393
|
-
return
|
|
396
|
+
if (this.#mapped) return this.#mapped;
|
|
397
|
+
this.#mapped = {
|
|
394
398
|
x: this.#mapping("x"),
|
|
395
399
|
y: this.#mapping("y")
|
|
396
400
|
};
|
|
401
|
+
return this.#mapped;
|
|
397
402
|
}
|
|
398
403
|
#refresh() {
|
|
399
404
|
const mappings = this.#mappings();
|
|
400
405
|
for (const axis of axes) {
|
|
401
406
|
const axisValue = this.#values[axis];
|
|
402
407
|
const mapping = mappings[axis];
|
|
403
|
-
axisValue.value = mapping.snap(axisValue.
|
|
408
|
+
axisValue.value = mapping.snap(axisValue.asked ?? this.numberAttribute(axis, mapping.bounds[0]));
|
|
404
409
|
}
|
|
405
410
|
this.render();
|
|
406
411
|
}
|
|
@@ -412,11 +417,11 @@ var SonicXy = class extends SonicFormElement {
|
|
|
412
417
|
const [low, high] = mappings[axis].bounds;
|
|
413
418
|
const isCurrent = axis === this.#currentAxis;
|
|
414
419
|
const stop = isCurrent ? "0" : "-1";
|
|
415
|
-
part
|
|
416
|
-
part
|
|
417
|
-
part
|
|
418
|
-
part
|
|
419
|
-
part
|
|
420
|
+
writeAttribute(part, "aria-orientation", orientations[axis]);
|
|
421
|
+
writeAttribute(part, "aria-valuemin", String(low));
|
|
422
|
+
writeAttribute(part, "aria-valuemax", String(high));
|
|
423
|
+
writeAttribute(part, "aria-valuenow", String(this.#values[axis].value));
|
|
424
|
+
writeAttribute(part, "aria-valuetext", this.#valueText(axis));
|
|
420
425
|
this.forwardNaming(part, true);
|
|
421
426
|
writeAttribute(part, "aria-hidden", isCurrent ? void 0 : "true");
|
|
422
427
|
writeAttribute(part, "aria-disabled", isDisabled ? "true" : void 0);
|
|
@@ -451,8 +456,8 @@ var SonicXy = class extends SonicFormElement {
|
|
|
451
456
|
#set(axis, next) {
|
|
452
457
|
if (!Number.isFinite(next)) return false;
|
|
453
458
|
const axisValue = this.#values[axis];
|
|
454
|
-
const snapped = this.#
|
|
455
|
-
axisValue.
|
|
459
|
+
const snapped = this.#mappings()[axis].snap(next);
|
|
460
|
+
axisValue.asked = snapped;
|
|
456
461
|
if (snapped === axisValue.value) return false;
|
|
457
462
|
axisValue.value = snapped;
|
|
458
463
|
return true;
|
|
@@ -465,7 +470,16 @@ var SonicXy = class extends SonicFormElement {
|
|
|
465
470
|
}
|
|
466
471
|
#write(axis, next) {
|
|
467
472
|
if (this.#pointerDrag?.current()) return;
|
|
468
|
-
|
|
473
|
+
const axisValue = this.#values[axis];
|
|
474
|
+
if (next === void 0 || next === null) {
|
|
475
|
+
axisValue.asked = void 0;
|
|
476
|
+
this.#refresh();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const asked = toNumber(next);
|
|
480
|
+
const hasMoved = this.#set(axis, asked);
|
|
481
|
+
if (Number.isFinite(asked)) axisValue.asked = asked;
|
|
482
|
+
if (hasMoved) this.render();
|
|
469
483
|
}
|
|
470
484
|
};
|
|
471
485
|
|
package/dist/default/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { SonicButton } from "./elements/button.js";
|
|
1
2
|
import { SonicDial } from "./elements/dial.js";
|
|
2
3
|
import { envelopeCurve } from "./lib/envelope-curve.js";
|
|
3
4
|
import { SonicEnvelope } from "./elements/envelope.js";
|
|
4
|
-
import { SonicButton } from "./elements/button.js";
|
|
5
|
-
import { SonicSwitch } from "./elements/switch.js";
|
|
6
5
|
import { SonicMeter } from "./elements/meter.js";
|
|
7
6
|
import { SonicNumber } from "./elements/number.js";
|
|
8
7
|
import { SonicSegmented } from "./elements/segmented.js";
|
|
9
8
|
import { SonicSlider } from "./elements/slider.js";
|
|
10
9
|
import { SonicSplit } from "./elements/split.js";
|
|
10
|
+
import { SonicSwitch } from "./elements/switch.js";
|
|
11
11
|
import { SonicWaveform } from "./elements/waveform.js";
|
|
12
12
|
import { SonicWavestrip } from "./elements/wavestrip.js";
|
|
13
13
|
import { SonicXy } from "./elements/xy.js";
|
|
@@ -98,8 +98,13 @@ function adsrShape(proportions, curves = {}) {
|
|
|
98
98
|
}
|
|
99
99
|
function adsrPath({ points }) {
|
|
100
100
|
const stroke = points.map(({ x, y }, index) => `${index === 0 ? "M" : "L"}${String(roundTo(x, 4))} ${String(roundTo(1 - y, 4))}`).join("");
|
|
101
|
+
const floor = {
|
|
102
|
+
from: roundTo(points[0]?.x ?? 0, 4),
|
|
103
|
+
to: roundTo(points.at(-1)?.x ?? 0, 4)
|
|
104
|
+
};
|
|
101
105
|
return {
|
|
102
106
|
fill: `${stroke}Z`,
|
|
107
|
+
floor,
|
|
103
108
|
stroke
|
|
104
109
|
};
|
|
105
110
|
}
|
|
@@ -3,13 +3,13 @@ const burstTimeConstants = -Math.log(1 - 10 ** (-2 / 20));
|
|
|
3
3
|
function ppmRate({ burstMs, fallDecibels, fallSeconds }) {
|
|
4
4
|
return {
|
|
5
5
|
fallDecibelsPerSecond: fallDecibels / fallSeconds,
|
|
6
|
-
|
|
6
|
+
riseTimeConstantMs: burstMs / burstTimeConstants
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
9
|
const ballisticsRates = {
|
|
10
10
|
peak: {
|
|
11
11
|
fallDecibelsPerSecond: 20,
|
|
12
|
-
|
|
12
|
+
riseTimeConstantMs: 0
|
|
13
13
|
},
|
|
14
14
|
"ppm-1": ppmRate({
|
|
15
15
|
burstMs: 5,
|
|
@@ -69,12 +69,22 @@ var CanvasSurface = class {
|
|
|
69
69
|
style.setProperty("transition-timing-function", "step-start");
|
|
70
70
|
style.setProperty("transition-behavior", "allow-discrete");
|
|
71
71
|
}
|
|
72
|
+
#current() {
|
|
73
|
+
if (this.#look && !this.#isRebuildWanted) return {
|
|
74
|
+
isRebuilt: false,
|
|
75
|
+
look: this.#look
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
isRebuilt: true,
|
|
79
|
+
look: this.#read()
|
|
80
|
+
};
|
|
81
|
+
}
|
|
72
82
|
#draw(frameMs) {
|
|
73
83
|
this.#frame = void 0;
|
|
74
84
|
const size = this.#size;
|
|
75
85
|
if (size.width === 0 || size.height === 0) return;
|
|
76
86
|
const { canvas, paint } = this.#options;
|
|
77
|
-
const
|
|
87
|
+
const { isRebuilt, look } = this.#current();
|
|
78
88
|
const isDirty = this.#isDirty;
|
|
79
89
|
this.#look = look;
|
|
80
90
|
this.#isDirty = false;
|
|
@@ -84,6 +94,7 @@ var CanvasSurface = class {
|
|
|
84
94
|
if (this.#context) paint(this.#context, {
|
|
85
95
|
frameMs,
|
|
86
96
|
isDirty,
|
|
97
|
+
isRebuilt,
|
|
87
98
|
look,
|
|
88
99
|
size
|
|
89
100
|
});
|
|
@@ -140,7 +151,7 @@ var CanvasSurface = class {
|
|
|
140
151
|
const { canvas, colours, lengths = {} } = this.#options;
|
|
141
152
|
const styles = getComputedStyle(canvas);
|
|
142
153
|
return {
|
|
143
|
-
colours: readEach(colours, (property) => styles.getPropertyValue(property).trim()),
|
|
154
|
+
colours: readEach(colours, (property) => styles.getPropertyValue(property).trim().replaceAll("currentcolor", () => styles.color)),
|
|
144
155
|
isReducedMotion: this.#reducedMotion.matches,
|
|
145
156
|
lengths: readEach(lengths, (property) => readPxProperty(styles, property, 0))
|
|
146
157
|
};
|
|
@@ -77,7 +77,7 @@ function byShortfall(values, { exact, order }, direction) {
|
|
|
77
77
|
const value = values[index] ?? NaN;
|
|
78
78
|
return trimFloat(direction * ((exact.get(index) ?? value) - value));
|
|
79
79
|
};
|
|
80
|
-
return order.toReversed().
|
|
80
|
+
return order.toReversed().toSorted((first, second) => shortfall(second) - shortfall(first));
|
|
81
81
|
}
|
|
82
82
|
function stepEach(members, values, pass) {
|
|
83
83
|
let remainder = pass.owed;
|
|
@@ -10,18 +10,18 @@ function dragThresholdPx(pointerType) {
|
|
|
10
10
|
function isAcross(from, to, proportion) {
|
|
11
11
|
return from !== proportion && (from - proportion) * (to - proportion) <= 0;
|
|
12
12
|
}
|
|
13
|
-
function passDetent(hold, from, to) {
|
|
13
|
+
function passDetent(hold, [from, to], pace) {
|
|
14
14
|
if (hold.slack === void 0 && !isAcross(from, to, hold.proportion)) return {
|
|
15
15
|
proportion: to,
|
|
16
16
|
slack: void 0
|
|
17
17
|
};
|
|
18
|
-
const slack = (hold.slack ?? 0) + to - hold.proportion;
|
|
18
|
+
const slack = (hold.slack ?? 0) + (to - hold.proportion) / pace;
|
|
19
19
|
if (Math.abs(slack) <= hold.zone) return {
|
|
20
20
|
proportion: void 0,
|
|
21
21
|
slack
|
|
22
22
|
};
|
|
23
23
|
return {
|
|
24
|
-
proportion: hold.proportion + slack - Math.sign(slack) * hold.zone,
|
|
24
|
+
proportion: hold.proportion + (slack - Math.sign(slack) * hold.zone) * pace,
|
|
25
25
|
slack: void 0
|
|
26
26
|
};
|
|
27
27
|
}
|
|
@@ -77,7 +77,7 @@ function stepDrag(mapping, state, move) {
|
|
|
77
77
|
};
|
|
78
78
|
const { detent } = state;
|
|
79
79
|
if (!detent) return land(mapping, moved, to);
|
|
80
|
-
const passed = passDetent(mapping.isWrapping ? nearestTurn(detent, state.rawProportion) : detent, state.rawProportion, to);
|
|
80
|
+
const passed = passDetent(mapping.isWrapping ? nearestTurn(detent, state.rawProportion) : detent, [state.rawProportion, to], pace);
|
|
81
81
|
const held = {
|
|
82
82
|
...moved,
|
|
83
83
|
detent: {
|
|
@@ -2,6 +2,7 @@ import { trimFloat } from "./math.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/lib/frame-timeline.ts
|
|
4
4
|
const placeholderPeriodMs = 2500;
|
|
5
|
+
const lookAheadSeconds = 30;
|
|
5
6
|
function windowStart(input, seconds, windowSeconds) {
|
|
6
7
|
if (!input.isPaged) return seconds - windowSeconds / 2;
|
|
7
8
|
if (input.held) return input.held.grabbed.startSeconds + seconds - input.held.grabbed.seconds;
|
|
@@ -31,7 +32,7 @@ function frameTimeline(input) {
|
|
|
31
32
|
const pending = pendingIn(input, [startSeconds, startSeconds + windowSeconds]);
|
|
32
33
|
const placeholder = placeholderPhase(input, pending);
|
|
33
34
|
const phase = placeholder ?? 0;
|
|
34
|
-
const wanted = overlap([startSeconds - windowSeconds, startSeconds +
|
|
35
|
+
const wanted = overlap([startSeconds - windowSeconds, startSeconds + windowSeconds + Math.max(windowSeconds, lookAheadSeconds)], range);
|
|
35
36
|
const at = (time) => (time - startSeconds) / windowSeconds;
|
|
36
37
|
return {
|
|
37
38
|
...held ? { ghostAt: at(clockSeconds) } : {},
|
|
@@ -3,28 +3,72 @@ import { readPxProperty } from "./read-px-property.js";
|
|
|
3
3
|
//#region src/lib/marker-band.ts
|
|
4
4
|
const kindPattern = /^[a-z][a-z0-9-]*$/;
|
|
5
5
|
const snapSlopPx = 4;
|
|
6
|
-
function
|
|
6
|
+
function isKind(kind) {
|
|
7
|
+
return kind !== void 0 && kindPattern.test(kind);
|
|
8
|
+
}
|
|
9
|
+
function writeKind(part, kind, fallback) {
|
|
10
|
+
if (!isKind(kind)) {
|
|
11
|
+
part.style.removeProperty("--_sonic-marker");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
part.style.setProperty("--_sonic-marker", `var(--sonic-marker-${kind}, var(${fallback}))`);
|
|
15
|
+
}
|
|
16
|
+
function renderMarker(marker, [from, to], lane) {
|
|
7
17
|
const part = document.createElement("div");
|
|
8
|
-
part.className =
|
|
18
|
+
part.className = lane === void 0 ? "sonic-wavestrip-region" : "sonic-wavestrip-marker";
|
|
19
|
+
if (lane !== void 0) part.style.setProperty("--_sonic-marker-lane", String(lane));
|
|
9
20
|
part.style.setProperty("--_sonic-marker-from", String(from));
|
|
10
21
|
part.style.setProperty("--_sonic-marker-to", String(to));
|
|
11
|
-
|
|
22
|
+
writeKind(part, marker.kind, "--_sonic-marker-default");
|
|
12
23
|
if (marker.dimmed === true) part.dataset.sonicDimmed = "";
|
|
13
24
|
return part;
|
|
14
25
|
}
|
|
15
26
|
function createMarkerBand(band) {
|
|
27
|
+
let dots = [];
|
|
28
|
+
const px = (property) => readPxProperty(getComputedStyle(band), property, 0);
|
|
16
29
|
return {
|
|
17
|
-
|
|
30
|
+
at: ({ x, y }, widthPx) => {
|
|
31
|
+
const size = px("--_sonic-marker-size");
|
|
32
|
+
const step = px("--_sonic-marker-step");
|
|
33
|
+
const nearest = nearestDot({
|
|
34
|
+
x,
|
|
35
|
+
y
|
|
36
|
+
}, dots.map(({ at, lane }) => ({
|
|
37
|
+
x: at * widthPx,
|
|
38
|
+
y: size / 2 + lane * step
|
|
39
|
+
})), size / 2 + snapSlopPx);
|
|
40
|
+
return nearest === void 0 ? void 0 : dots[nearest]?.index;
|
|
41
|
+
},
|
|
18
42
|
render: (markers, proportions) => {
|
|
19
|
-
|
|
43
|
+
const widthPx = band.getBoundingClientRect().width;
|
|
44
|
+
const points = markers.flatMap((marker, index) => marker.end === void 0 ? [{
|
|
45
|
+
at: proportions[index]?.[0] ?? 0,
|
|
46
|
+
index
|
|
47
|
+
}] : []);
|
|
48
|
+
const lanes = markerLanes(points.map(({ at }) => at * widthPx), px("--_sonic-marker-step"));
|
|
49
|
+
dots = points.map((point, order) => ({
|
|
50
|
+
...point,
|
|
51
|
+
lane: lanes[order] ?? 0
|
|
52
|
+
}));
|
|
53
|
+
const laneOf = new Map(dots.map(({ index, lane }) => [index, lane]));
|
|
54
|
+
band.replaceChildren(...markers.map((marker, index) => renderMarker(marker, proportions[index] ?? [0, 0], laneOf.get(index))));
|
|
20
55
|
}
|
|
21
56
|
};
|
|
22
57
|
}
|
|
23
|
-
function
|
|
58
|
+
function markerLanes(positions, minDistance) {
|
|
59
|
+
const lastInLane = [];
|
|
60
|
+
return positions.map((position) => {
|
|
61
|
+
const free = lastInLane.findIndex((last) => position - last >= minDistance);
|
|
62
|
+
const lane = free === -1 ? lastInLane.length : free;
|
|
63
|
+
lastInLane[lane] = position;
|
|
64
|
+
return lane;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function nearestDot(point, dots, reach) {
|
|
24
68
|
let nearest;
|
|
25
|
-
let nearestDistance = reach;
|
|
26
|
-
for (const [index,
|
|
27
|
-
const distance =
|
|
69
|
+
let nearestDistance = reach * reach;
|
|
70
|
+
for (const [index, dot] of dots.entries()) {
|
|
71
|
+
const distance = (dot.x - point.x) ** 2 + (dot.y - point.y) ** 2;
|
|
28
72
|
if (distance > nearestDistance) continue;
|
|
29
73
|
nearest = index;
|
|
30
74
|
nearestDistance = distance;
|
|
@@ -33,4 +77,4 @@ function nearestMarker(proportion, markerProportions, reach) {
|
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
//#endregion
|
|
36
|
-
export { createMarkerBand,
|
|
80
|
+
export { createMarkerBand, isKind, markerLanes, nearestDot, writeKind };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { clamp } from "./math.js";
|
|
2
2
|
import { requireChild } from "./render.js";
|
|
3
3
|
import { readPxProperty } from "./read-px-property.js";
|
|
4
|
+
import { writeKind } from "./marker-band.js";
|
|
4
5
|
|
|
5
6
|
//#region src/lib/marker-rider.ts
|
|
6
7
|
const fadeShare = .25;
|
|
@@ -43,6 +44,7 @@ function layoutRider(labels, view) {
|
|
|
43
44
|
function labelSlot(element) {
|
|
44
45
|
return {
|
|
45
46
|
element,
|
|
47
|
+
kind: void 0,
|
|
46
48
|
opacity: NaN,
|
|
47
49
|
shownPx: NaN,
|
|
48
50
|
x: NaN
|
|
@@ -63,13 +65,17 @@ function writePlacement(slot, { opacity, shownPx, x }, widthPx) {
|
|
|
63
65
|
style.setProperty("opacity", opacity < 1 ? opacity.toFixed(3) : "");
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
|
-
function writeLabel(slot, labels, placement) {
|
|
68
|
+
function writeLabel(slot, [labels, colourProperty], placement) {
|
|
67
69
|
const { element } = slot;
|
|
68
70
|
const label = placement && placement.shownPx > 0 ? labels[placement.index] : void 0;
|
|
69
71
|
if (element.hidden !== !label) element.hidden = !label;
|
|
70
72
|
if (!label || !placement) return;
|
|
71
73
|
if (element.textContent !== label.text) element.textContent = label.text;
|
|
72
74
|
element.toggleAttribute("data-sonic-dimmed", label.isDimmed);
|
|
75
|
+
if (slot.kind !== label.kind) {
|
|
76
|
+
slot.kind = label.kind;
|
|
77
|
+
writeKind(element, label.kind, colourProperty);
|
|
78
|
+
}
|
|
73
79
|
writePlacement(slot, placement, label.widthPx);
|
|
74
80
|
}
|
|
75
81
|
function measureWidths(control, className, texts) {
|
|
@@ -85,7 +91,7 @@ function measureWidths(control, className, texts) {
|
|
|
85
91
|
return widths;
|
|
86
92
|
}
|
|
87
93
|
function createLabelRider(control, options) {
|
|
88
|
-
const { className, insetProperty } = options;
|
|
94
|
+
const { className, colourProperty, insetProperty } = options;
|
|
89
95
|
const parked = labelSlot(requireChild(control, `.${className}`, HTMLElement));
|
|
90
96
|
const arriving = labelSlot(requireChild(control, `.${className} + .${className}`, HTMLElement));
|
|
91
97
|
let insetPx = 0;
|
|
@@ -104,8 +110,8 @@ function createLabelRider(control, options) {
|
|
|
104
110
|
...window,
|
|
105
111
|
insetPx
|
|
106
112
|
});
|
|
107
|
-
writeLabel(parked, measured, layout.parked);
|
|
108
|
-
writeLabel(arriving, measured, layout.arriving);
|
|
113
|
+
writeLabel(parked, [measured, colourProperty], layout.parked);
|
|
114
|
+
writeLabel(arriving, [measured, colourProperty], layout.arriving);
|
|
109
115
|
}
|
|
110
116
|
};
|
|
111
117
|
}
|
package/dist/default/lib/math.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
function clamp(value, min, max) {
|
|
3
3
|
return Math.min(max, Math.max(min, value));
|
|
4
4
|
}
|
|
5
|
+
function toNumber(value) {
|
|
6
|
+
if (typeof value === "number") return value;
|
|
7
|
+
if (typeof value !== "string" || value.trim() === "") return NaN;
|
|
8
|
+
return Number(value);
|
|
9
|
+
}
|
|
5
10
|
function clampProportion(fraction) {
|
|
6
11
|
return clamp(fraction, 0, 1);
|
|
7
12
|
}
|
|
@@ -22,4 +27,4 @@ function roundTo(value, places) {
|
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
//#endregion
|
|
25
|
-
export { clamp, clampProportion, decimalPlaces, roundTo, trimFloat, wrapProportion };
|
|
30
|
+
export { clamp, clampProportion, decimalPlaces, roundTo, toNumber, trimFloat, wrapProportion };
|
|
@@ -5,8 +5,8 @@ function bindDrag(target, handlers, signal) {
|
|
|
5
5
|
if (!held) return;
|
|
6
6
|
const { drag } = held;
|
|
7
7
|
held = void 0;
|
|
8
|
-
handlers.toggle(false);
|
|
9
|
-
handlers.release(drag);
|
|
8
|
+
handlers.toggle?.(false);
|
|
9
|
+
handlers.release?.(drag);
|
|
10
10
|
};
|
|
11
11
|
const whileHeld = (type, handle) => {
|
|
12
12
|
target.addEventListener(type, (event) => {
|
|
@@ -23,15 +23,18 @@ function bindDrag(target, handlers, signal) {
|
|
|
23
23
|
pointerId: event.pointerId
|
|
24
24
|
};
|
|
25
25
|
target.setPointerCapture(event.pointerId);
|
|
26
|
-
handlers.toggle(true);
|
|
26
|
+
handlers.toggle?.(true);
|
|
27
27
|
}, { signal });
|
|
28
28
|
whileHeld("pointermove", handlers.move);
|
|
29
29
|
whileHeld("pointercancel", (drag) => {
|
|
30
|
-
handlers.cancel(drag);
|
|
30
|
+
handlers.cancel?.(drag);
|
|
31
31
|
end();
|
|
32
32
|
});
|
|
33
33
|
whileHeld("lostpointercapture", end);
|
|
34
|
-
whileHeld("pointerup",
|
|
34
|
+
whileHeld("pointerup", (drag, event) => {
|
|
35
|
+
end();
|
|
36
|
+
handlers.lift?.(drag, event);
|
|
37
|
+
});
|
|
35
38
|
signal.addEventListener("abort", end, { once: true });
|
|
36
39
|
return {
|
|
37
40
|
current: () => held?.drag,
|
|
@@ -11,7 +11,17 @@ function downsample(peaks, count) {
|
|
|
11
11
|
for (let source = start; source < end; source += 1) total += peaks[source] ?? 0;
|
|
12
12
|
resampled.push(total / (end - start));
|
|
13
13
|
}
|
|
14
|
-
return resampled;
|
|
14
|
+
return rescale(resampled, peakOf(peaks));
|
|
15
|
+
}
|
|
16
|
+
function peakOf(peaks) {
|
|
17
|
+
let loudest = 0;
|
|
18
|
+
for (const peak of peaks) loudest = Math.max(loudest, peak);
|
|
19
|
+
return loudest;
|
|
20
|
+
}
|
|
21
|
+
function rescale(bars, loudest) {
|
|
22
|
+
const tallest = peakOf(bars);
|
|
23
|
+
if (tallest <= 0) return bars;
|
|
24
|
+
return bars.map((bar) => bar * loudest / tallest);
|
|
15
25
|
}
|
|
16
26
|
function upsample(peaks, count) {
|
|
17
27
|
const resampled = [];
|
package/dist/dev/dat.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export interface DatHeader {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function readDatHeader(buffer: ArrayBuffer): DatHeader;
|
|
14
14
|
export declare function datByteRange(header: DatHeader, fromPair: number, toPair: number): [number, number] | undefined;
|
|
15
|
-
export declare function readDatSamples(header: DatHeader, body: ArrayBuffer): Int8Array | Int16Array;
|
|
16
|
-
export declare function readDatPeaks(header: DatHeader, body: ArrayBuffer): WaveformPeaks;
|
|
15
|
+
export declare function readDatSamples(header: DatHeader, body: ArrayBuffer, byteOffset?: number): Int8Array | Int16Array;
|
|
16
|
+
export declare function readDatPeaks(header: DatHeader, body: ArrayBuffer, byteOffset?: number): WaveformPeaks;
|
|
17
17
|
//#endregion
|
|
18
18
|
export type { WaveformPeaks };
|
|
19
19
|
//# sourceMappingURL=dat.d.ts.map
|
package/dist/dev/dat.js
CHANGED
|
@@ -16,6 +16,7 @@ function readDatHeader(buffer) {
|
|
|
16
16
|
if (channels < 1) throw new Error("A .dat header names no channels");
|
|
17
17
|
const sampleRate = view.getInt32(8, true);
|
|
18
18
|
const samplesPerPixel = view.getInt32(12, true);
|
|
19
|
+
if (samplesPerPixel < 1) throw new Error("A .dat header names no samples per pixel");
|
|
19
20
|
return {
|
|
20
21
|
bits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,
|
|
21
22
|
channels,
|
|
@@ -34,15 +35,15 @@ function datByteRange(header, fromPair, toPair) {
|
|
|
34
35
|
const pairBytes = header.channels * 2 * header.bits / 8;
|
|
35
36
|
return [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];
|
|
36
37
|
}
|
|
37
|
-
function readDatSamples(header, body) {
|
|
38
|
-
return header.bits === 8 ? new Int8Array(body) : new Int16Array(body,
|
|
38
|
+
function readDatSamples(header, body, byteOffset = 0) {
|
|
39
|
+
return header.bits === 8 ? new Int8Array(body, byteOffset) : new Int16Array(body, byteOffset, Math.floor((body.byteLength - byteOffset) / 2));
|
|
39
40
|
}
|
|
40
|
-
function readDatPeaks(header, body) {
|
|
41
|
+
function readDatPeaks(header, body, byteOffset = 0) {
|
|
41
42
|
return {
|
|
42
43
|
channels: header.channels,
|
|
43
44
|
fullScale: header.bits === 8 ? 128 : 32768,
|
|
44
45
|
pairsPerSecond: header.pairsPerSecond,
|
|
45
|
-
samples: readDatSamples(header, body)
|
|
46
|
+
samples: readDatSamples(header, body, byteOffset)
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
package/dist/dev/dat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dat.js","names":[],"sources":["../../src/dat.ts"],"sourcesContent":["import type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { clamp } from '#lib/math.ts';\n\nexport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nexport interface DatHeader {\n\tbits: 8 | 16;\n\tchannels: number;\n\tdataOffset: number;\n\tpairs: number;\n\tpairsPerSecond: number;\n\tsampleRate: number;\n\tsamplesPerPixel: number;\n\tversion: 1 | 2;\n}\n\nfunction readVersion(view: DataView): 1 | 2 {\n\tconst version = view.getInt32(0, true);\n\tif (version === 1 || version === 2) return version;\n\n\tthrow new Error(`A .dat file of version ${String(version)} is not one this reads`);\n}\n\nexport function readDatHeader(buffer: ArrayBuffer): DatHeader {\n\tif (buffer.byteLength < 20) throw new Error('A .dat header takes at least 20 bytes');\n\n\tconst view = new DataView(buffer);\n\tconst version = readVersion(view);\n\tconst dataOffset = version === 1 ? 20 : 24;\n\tif (buffer.byteLength < dataOffset) {\n\t\tthrow new Error(`A version ${String(version)} .dat header takes ${String(dataOffset)} bytes`);\n\t}\n\n\tconst channels = version === 1 ? 1 : view.getInt32(20, true);\n\tif (channels < 1) throw new Error('A .dat header names no channels');\n\n\tconst sampleRate = view.getInt32(8, true);\n\tconst samplesPerPixel = view.getInt32(12, true);\n\n\treturn {\n\t\tbits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,\n\t\tchannels,\n\t\tdataOffset,\n\t\tpairs: view.getUint32(16, true),\n\t\tpairsPerSecond: sampleRate / samplesPerPixel,\n\t\tsampleRate,\n\t\tsamplesPerPixel,\n\t\tversion,\n\t};\n}\n\nexport function datByteRange(\n\theader: DatHeader,\n\tfromPair: number,\n\ttoPair: number,\n): [number, number] | undefined {\n\tconst from = clamp(Math.floor(fromPair), 0, header.pairs);\n\tconst to = clamp(Math.ceil(toPair), from, header.pairs);\n\t// A server ignores an inverted `Range` and sends the whole file\n\tif (!(to > from)) return undefined;\n\n\tconst pairBytes = (header.channels * 2 * header.bits) / 8;\n\n\treturn [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];\n}\n\n// Typed arrays read the platform's byte order; every browser platform is little-endian, as the format is\n// A range response can end mid-sample; the odd byte is dropped\nexport function readDatSamples(
|
|
1
|
+
{"version":3,"file":"dat.js","names":[],"sources":["../../src/dat.ts"],"sourcesContent":["import type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { clamp } from '#lib/math.ts';\n\nexport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nexport interface DatHeader {\n\tbits: 8 | 16;\n\tchannels: number;\n\tdataOffset: number;\n\tpairs: number;\n\tpairsPerSecond: number;\n\tsampleRate: number;\n\tsamplesPerPixel: number;\n\tversion: 1 | 2;\n}\n\nfunction readVersion(view: DataView): 1 | 2 {\n\tconst version = view.getInt32(0, true);\n\tif (version === 1 || version === 2) return version;\n\n\tthrow new Error(`A .dat file of version ${String(version)} is not one this reads`);\n}\n\nexport function readDatHeader(buffer: ArrayBuffer): DatHeader {\n\tif (buffer.byteLength < 20) throw new Error('A .dat header takes at least 20 bytes');\n\n\tconst view = new DataView(buffer);\n\tconst version = readVersion(view);\n\tconst dataOffset = version === 1 ? 20 : 24;\n\tif (buffer.byteLength < dataOffset) {\n\t\tthrow new Error(`A version ${String(version)} .dat header takes ${String(dataOffset)} bytes`);\n\t}\n\n\tconst channels = version === 1 ? 1 : view.getInt32(20, true);\n\tif (channels < 1) throw new Error('A .dat header names no channels');\n\n\tconst sampleRate = view.getInt32(8, true);\n\tconst samplesPerPixel = view.getInt32(12, true);\n\tif (samplesPerPixel < 1) throw new Error('A .dat header names no samples per pixel');\n\n\treturn {\n\t\tbits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,\n\t\tchannels,\n\t\tdataOffset,\n\t\tpairs: view.getUint32(16, true),\n\t\tpairsPerSecond: sampleRate / samplesPerPixel,\n\t\tsampleRate,\n\t\tsamplesPerPixel,\n\t\tversion,\n\t};\n}\n\nexport function datByteRange(\n\theader: DatHeader,\n\tfromPair: number,\n\ttoPair: number,\n): [number, number] | undefined {\n\tconst from = clamp(Math.floor(fromPair), 0, header.pairs);\n\tconst to = clamp(Math.ceil(toPair), from, header.pairs);\n\t// A server ignores an inverted `Range` and sends the whole file\n\tif (!(to > from)) return undefined;\n\n\tconst pairBytes = (header.channels * 2 * header.bits) / 8;\n\n\treturn [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];\n}\n\n// Typed arrays read the platform's byte order; every browser platform is little-endian, as the format is\n// A range response can end mid-sample; the odd byte is dropped\nexport function readDatSamples(\n\theader: DatHeader,\n\tbody: ArrayBuffer,\n\tbyteOffset = 0,\n): Int8Array | Int16Array {\n\treturn header.bits === 8\n\t\t? new Int8Array(body, byteOffset)\n\t\t: new Int16Array(body, byteOffset, Math.floor((body.byteLength - byteOffset) / 2));\n}\n\nexport function readDatPeaks(header: DatHeader, body: ArrayBuffer, byteOffset = 0): WaveformPeaks {\n\treturn {\n\t\tchannels: header.channels,\n\t\tfullScale: header.bits === 8 ? 128 : 32_768,\n\t\tpairsPerSecond: header.pairsPerSecond,\n\t\tsamples: readDatSamples(header, body, byteOffset),\n\t};\n}\n"],"mappings":";;;AAiBA,SAAS,YAAY,MAAuB;CAC3C,MAAM,UAAU,KAAK,SAAS,GAAG,IAAI;CACrC,IAAI,YAAY,KAAK,YAAY,GAAG,OAAO;CAE3C,MAAM,IAAI,MAAM,0BAA0B,OAAO,OAAO,EAAE,uBAAuB;AAClF;AAEA,SAAgB,cAAc,QAAgC;CAC7D,IAAI,OAAO,aAAa,IAAI,MAAM,IAAI,MAAM,uCAAuC;CAEnF,MAAM,OAAO,IAAI,SAAS,MAAM;CAChC,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,aAAa,YAAY,IAAI,KAAK;CACxC,IAAI,OAAO,aAAa,YACvB,MAAM,IAAI,MAAM,aAAa,OAAO,OAAO,EAAE,qBAAqB,OAAO,UAAU,EAAE,OAAO;CAG7F,MAAM,WAAW,YAAY,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI;CAC3D,IAAI,WAAW,GAAG,MAAM,IAAI,MAAM,iCAAiC;CAEnE,MAAM,aAAa,KAAK,SAAS,GAAG,IAAI;CACxC,MAAM,kBAAkB,KAAK,SAAS,IAAI,IAAI;CAC9C,IAAI,kBAAkB,GAAG,MAAM,IAAI,MAAM,0CAA0C;CAEnF,OAAO;EACN,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI,OAAO,IAAI,IAAI;EAChD;EACA;EACA,OAAO,KAAK,UAAU,IAAI,IAAI;EAC9B,gBAAgB,aAAa;EAC7B;EACA;EACA;CACD;AACD;AAEA,SAAgB,aACf,QACA,UACA,QAC+B;CAC/B,MAAM,OAAO,MAAM,KAAK,MAAM,QAAQ,GAAG,GAAG,OAAO,KAAK;CACxD,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,GAAG,MAAM,OAAO,KAAK;CAEtD,IAAI,EAAE,KAAK,OAAO,OAAO;CAEzB,MAAM,YAAa,OAAO,WAAW,IAAI,OAAO,OAAQ;CAExD,OAAO,CAAC,OAAO,aAAa,OAAO,WAAW,OAAO,aAAa,KAAK,YAAY,CAAC;AACrF;AAIA,SAAgB,eACf,QACA,MACA,aAAa,GACY;CACzB,OAAO,OAAO,SAAS,IACpB,IAAI,UAAU,MAAM,UAAU,IAC9B,IAAI,WAAW,MAAM,YAAY,KAAK,OAAO,KAAK,aAAa,cAAc,CAAC,CAAC;AACnF;AAEA,SAAgB,aAAa,QAAmB,MAAmB,aAAa,GAAkB;CACjG,OAAO;EACN,UAAU,OAAO;EACjB,WAAW,OAAO,SAAS,IAAI,MAAM;EACrC,gBAAgB,OAAO;EACvB,SAAS,eAAe,QAAQ,MAAM,UAAU;CACjD;AACD"}
|