@xsynaptic/sonic-ui 0.4.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 -20
- package/dist/default/dat.js +51 -0
- package/dist/default/define/button.js +7 -0
- package/dist/default/define/envelope.js +7 -0
- package/dist/default/define/split.js +7 -0
- package/dist/default/define/switch.js +7 -0
- package/dist/default/define/waveform.js +7 -0
- package/dist/default/define/wavestrip.js +7 -0
- package/dist/default/define/xy.js +7 -0
- package/dist/default/define.js +7 -1
- package/dist/default/elements/button.js +165 -0
- package/dist/default/elements/dial.js +16 -22
- package/dist/default/elements/envelope.js +383 -0
- package/dist/default/elements/meter.js +155 -63
- package/dist/default/elements/number.js +22 -9
- package/dist/default/elements/radio-group.js +183 -0
- package/dist/default/elements/readout-claim.js +81 -0
- package/dist/default/elements/readout.js +35 -0
- package/dist/default/elements/segmented.js +9 -159
- package/dist/default/elements/slider.js +42 -51
- package/dist/default/elements/sonic-element.js +69 -31
- package/dist/default/elements/split.js +249 -0
- package/dist/default/elements/switch.js +273 -0
- package/dist/default/elements/value-element.js +694 -0
- package/dist/default/elements/{range-entry.js → value-entry.js} +3 -3
- package/dist/default/elements/wave-element.js +80 -0
- package/dist/default/elements/waveform.js +328 -0
- package/dist/default/elements/wavestrip.js +185 -0
- package/dist/default/elements/xy.js +487 -0
- package/dist/default/index.js +10 -2
- package/dist/default/lib/adsr-shape.js +113 -0
- package/dist/default/lib/ballistics.js +55 -0
- package/dist/default/lib/canvas-surface.js +203 -0
- package/dist/default/lib/copy-node.js +32 -2
- package/dist/default/lib/crossfade.js +22 -0
- package/dist/default/lib/distribute.js +215 -0
- package/dist/default/lib/drag-step.js +99 -0
- package/dist/default/lib/envelope-curve.js +9 -0
- package/dist/default/lib/field.js +94 -0
- package/dist/default/lib/focus-by-pointer.js +28 -0
- package/dist/default/lib/frame-timeline.js +63 -0
- package/dist/default/lib/hold.js +38 -0
- package/dist/default/lib/marker-band.js +80 -0
- package/dist/default/lib/marker-rider.js +120 -0
- package/dist/default/lib/math.js +17 -3
- package/dist/default/lib/modifier-press.js +11 -0
- package/dist/default/lib/number-list.js +6 -6
- package/dist/default/lib/pointer-drag.js +46 -0
- package/dist/default/lib/render.js +20 -5
- package/dist/default/lib/resample-peaks.js +46 -0
- package/dist/default/lib/strip-scene.js +48 -0
- package/dist/default/lib/taper.js +23 -17
- package/dist/default/lib/time-regions.js +8 -0
- package/dist/default/lib/tracking-clock.js +35 -0
- package/dist/default/lib/value-mapping.js +115 -0
- package/dist/default/lib/waveform-buckets.js +41 -0
- package/dist/default/lib/write-attribute.js +1 -0
- package/dist/dev/dat.d.ts +19 -0
- package/dist/dev/dat.js +52 -0
- package/dist/dev/dat.js.map +1 -0
- package/dist/dev/define/button.d.ts +2 -0
- package/dist/dev/define/button.js +8 -0
- package/dist/dev/define/button.js.map +1 -0
- package/dist/dev/define/dial.js.map +1 -1
- package/dist/dev/define/envelope.d.ts +2 -0
- package/dist/dev/define/envelope.js +8 -0
- package/dist/dev/define/envelope.js.map +1 -0
- package/dist/dev/define/meter.js.map +1 -1
- package/dist/dev/define/number.js.map +1 -1
- package/dist/dev/define/segmented.js.map +1 -1
- package/dist/dev/define/slider.js.map +1 -1
- package/dist/dev/define/split.d.ts +2 -0
- package/dist/dev/define/split.js +8 -0
- package/dist/dev/define/split.js.map +1 -0
- package/dist/dev/define/switch.d.ts +2 -0
- package/dist/dev/define/switch.js +8 -0
- package/dist/dev/define/switch.js.map +1 -0
- package/dist/dev/define/waveform.d.ts +2 -0
- package/dist/dev/define/waveform.js +8 -0
- package/dist/dev/define/waveform.js.map +1 -0
- package/dist/dev/define/wavestrip.d.ts +2 -0
- package/dist/dev/define/wavestrip.js +8 -0
- package/dist/dev/define/wavestrip.js.map +1 -0
- package/dist/dev/define/xy.d.ts +2 -0
- package/dist/dev/define/xy.js +8 -0
- package/dist/dev/define/xy.js.map +1 -0
- package/dist/dev/define.d.ts +15 -3
- package/dist/dev/define.js +7 -1
- package/dist/dev/elements/{key.d.ts → button.d.ts} +8 -6
- package/dist/dev/elements/button.js +166 -0
- package/dist/dev/elements/button.js.map +1 -0
- package/dist/dev/elements/dial.d.ts +6 -4
- package/dist/dev/elements/dial.js +16 -22
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.d.ts +36 -0
- package/dist/dev/elements/envelope.js +388 -0
- package/dist/dev/elements/envelope.js.map +1 -0
- package/dist/dev/elements/form-element.d.ts +1 -1
- package/dist/dev/elements/form-element.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +11 -6
- package/dist/dev/elements/meter.js +155 -63
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +7 -3
- package/dist/dev/elements/number.js +22 -9
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +25 -0
- package/dist/dev/elements/radio-group.js +184 -0
- package/dist/dev/elements/radio-group.js.map +1 -0
- package/dist/dev/elements/readout-claim.js +82 -0
- package/dist/dev/elements/readout-claim.js.map +1 -0
- package/dist/dev/elements/readout.js +36 -0
- package/dist/dev/elements/readout.js.map +1 -0
- package/dist/dev/elements/segmented.d.ts +4 -10
- package/dist/dev/elements/segmented.js +9 -159
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +6 -6
- package/dist/dev/elements/slider.js +42 -51
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +6 -3
- package/dist/dev/elements/sonic-element.js +119 -40
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.d.ts +21 -0
- package/dist/dev/elements/split.js +258 -0
- package/dist/dev/elements/split.js.map +1 -0
- package/dist/dev/elements/switch.d.ts +35 -0
- package/dist/dev/elements/switch.js +274 -0
- package/dist/dev/elements/switch.js.map +1 -0
- package/dist/dev/elements/value-element.d.ts +84 -0
- package/dist/dev/elements/value-element.js +695 -0
- package/dist/dev/elements/value-element.js.map +1 -0
- package/dist/dev/elements/{range-entry.js → value-entry.js} +4 -4
- package/dist/dev/elements/value-entry.js.map +1 -0
- package/dist/dev/elements/wave-element.d.ts +37 -0
- package/dist/dev/elements/wave-element.js +81 -0
- package/dist/dev/elements/wave-element.js.map +1 -0
- package/dist/dev/elements/waveform.d.ts +55 -0
- package/dist/dev/elements/waveform.js +329 -0
- package/dist/dev/elements/waveform.js.map +1 -0
- package/dist/dev/elements/wavestrip.d.ts +51 -0
- package/dist/dev/elements/wavestrip.js +186 -0
- package/dist/dev/elements/wavestrip.js.map +1 -0
- package/dist/dev/elements/xy.d.ts +62 -0
- package/dist/dev/elements/xy.js +488 -0
- package/dist/dev/elements/xy.js.map +1 -0
- package/dist/dev/index.d.ts +11 -2
- package/dist/dev/index.js +10 -2
- package/dist/dev/lib/adsr-shape.js +114 -0
- package/dist/dev/lib/adsr-shape.js.map +1 -0
- package/dist/dev/lib/ballistics.js +56 -0
- package/dist/dev/lib/ballistics.js.map +1 -0
- package/dist/dev/lib/canvas-surface.d.ts +26 -0
- package/dist/dev/lib/canvas-surface.js +204 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -0
- package/dist/dev/lib/copy-node.js +32 -2
- package/dist/dev/lib/copy-node.js.map +1 -1
- package/dist/dev/lib/crossfade.d.ts +12 -0
- package/dist/dev/lib/crossfade.js +23 -0
- package/dist/dev/lib/crossfade.js.map +1 -0
- package/dist/dev/lib/distribute.d.ts +4 -0
- package/dist/dev/lib/distribute.js +216 -0
- package/dist/dev/lib/distribute.js.map +1 -0
- package/dist/dev/lib/drag-step.js +100 -0
- package/dist/dev/lib/drag-step.js.map +1 -0
- package/dist/dev/lib/envelope-curve.d.ts +4 -0
- package/dist/dev/lib/envelope-curve.js +10 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -0
- package/dist/dev/lib/field.d.ts +4 -0
- package/dist/dev/lib/field.js +95 -0
- package/dist/dev/lib/field.js.map +1 -0
- package/dist/dev/lib/focus-by-pointer.js +29 -0
- package/dist/dev/lib/focus-by-pointer.js.map +1 -0
- package/dist/dev/lib/frame-timeline.js +64 -0
- package/dist/dev/lib/frame-timeline.js.map +1 -0
- package/dist/dev/lib/hold.js +39 -0
- package/dist/dev/lib/hold.js.map +1 -0
- package/dist/dev/lib/marker-band.js +81 -0
- package/dist/dev/lib/marker-band.js.map +1 -0
- package/dist/dev/lib/marker-rider.js +121 -0
- package/dist/dev/lib/marker-rider.js.map +1 -0
- package/dist/dev/lib/math.js +17 -3
- package/dist/dev/lib/math.js.map +1 -1
- package/dist/dev/lib/modifier-press.js +12 -0
- package/dist/dev/lib/modifier-press.js.map +1 -0
- package/dist/dev/lib/number-list.js +6 -6
- package/dist/dev/lib/number-list.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +47 -0
- package/dist/dev/lib/pointer-drag.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -1
- package/dist/dev/lib/render.js +20 -5
- package/dist/dev/lib/render.js.map +1 -1
- package/dist/dev/lib/resample-peaks.js +47 -0
- package/dist/dev/lib/resample-peaks.js.map +1 -0
- package/dist/dev/lib/strip-scene.js +49 -0
- package/dist/dev/lib/strip-scene.js.map +1 -0
- package/dist/dev/lib/taper.js +23 -17
- package/dist/dev/lib/taper.js.map +1 -1
- package/dist/dev/lib/time-regions.d.ts +4 -0
- package/dist/dev/lib/time-regions.js +9 -0
- package/dist/dev/lib/time-regions.js.map +1 -0
- package/dist/dev/lib/tracking-clock.js +36 -0
- package/dist/dev/lib/tracking-clock.js.map +1 -0
- package/dist/dev/lib/value-mapping.d.ts +23 -0
- package/dist/dev/lib/value-mapping.js +116 -0
- package/dist/dev/lib/value-mapping.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +9 -0
- package/dist/dev/lib/waveform-buckets.js +42 -0
- package/dist/dev/lib/waveform-buckets.js.map +1 -0
- 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 +2 -2
- package/dist/skins/lime.css +2 -0
- package/dist/skins/slate.css +2 -0
- package/dist/styles/button.css +240 -0
- package/dist/styles/controls.css +8 -1
- package/dist/styles/dial.css +110 -56
- package/dist/styles/envelope.css +204 -0
- package/dist/styles/led.css +39 -8
- package/dist/styles/material.css +343 -100
- package/dist/styles/meter.css +41 -52
- package/dist/styles/number.css +5 -1
- package/dist/styles/panel.css +61 -0
- package/dist/styles/ring.css +74 -0
- package/dist/styles/segmented.css +46 -51
- package/dist/styles/slider.css +177 -40
- package/dist/styles/switch.css +386 -0
- package/dist/styles/waveform.css +196 -0
- package/dist/styles/wavestrip.css +232 -0
- package/dist/styles/xy.css +241 -0
- package/package.json +15 -6
- package/dist/default/define/key.js +0 -7
- package/dist/default/elements/key.js +0 -169
- package/dist/default/elements/range-element.js +0 -546
- package/dist/default/lib/detent.js +0 -17
- package/dist/dev/define/key.d.ts +0 -2
- package/dist/dev/define/key.js +0 -8
- package/dist/dev/define/key.js.map +0 -1
- package/dist/dev/elements/key.js +0 -170
- package/dist/dev/elements/key.js.map +0 -1
- package/dist/dev/elements/range-element.d.ts +0 -64
- package/dist/dev/elements/range-element.js +0 -547
- package/dist/dev/elements/range-element.js.map +0 -1
- package/dist/dev/elements/range-entry.js.map +0 -1
- package/dist/dev/lib/detent.js +0 -18
- package/dist/dev/lib/detent.js.map +0 -1
- package/dist/styles/key.css +0 -242
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taper.js","names":[],"sources":["../../../src/lib/taper.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"taper.js","names":[],"sources":["../../../src/lib/taper.ts"],"sourcesContent":["import { clampProportion } from '#lib/math.ts';\n\nexport interface Taper {\n\tproportionOf: (value: number) => number;\n\tvalueAt: (proportion: number) => number;\n}\n\nexport function linearTaper(min: number, max: number): Taper {\n\tconst range = max - min;\n\n\treturn {\n\t\tproportionOf: (value) => (range > 0 ? clampProportion((value - min) / range) : 0),\n\t\tvalueAt: (proportion) => min + clampProportion(proportion) * range,\n\t};\n}\n\nexport function logTaper(min: number, max: number): Taper | undefined {\n\tif (min <= 0 || max <= min) return undefined;\n\n\tconst span = Math.log(max / min);\n\n\treturn {\n\t\tproportionOf: (value) => clampProportion(Math.log(Math.max(min, value) / min) / span),\n\t\tvalueAt: (proportion) => {\n\t\t\tconst clamped = clampProportion(proportion);\n\t\t\tif (clamped === 0) return min;\n\t\t\tif (clamped === 1) return max;\n\n\t\t\treturn min * Math.exp(clamped * span);\n\t\t},\n\t};\n}\n\n// JUCE's skew: a power curve through `midpoint` at half the travel\nexport function skewTaper(min: number, max: number, midpoint: number): Taper | undefined {\n\tconst range = max - min;\n\tconst midpointProportion = (midpoint - min) / range;\n\n\tif (midpointProportion === 0.5 || !(midpointProportion > 0 && midpointProportion < 1))\n\t\treturn undefined;\n\n\tconst skew = Math.log(0.5) / Math.log(midpointProportion);\n\n\treturn {\n\t\tproportionOf: (value) => clampProportion((value - min) / range) ** skew,\n\t\tvalueAt: (proportion) => min + range * clampProportion(proportion) ** (1 / skew),\n\t};\n}\n\nexport function listTaper(positions: ReadonlyArray<number>): Taper {\n\tconst last = positions.length - 1;\n\n\treturn {\n\t\tproportionOf: (value) => {\n\t\t\tif (Number.isNaN(value)) return NaN;\n\n\t\t\tconst above = positions.findIndex((position) => position >= value);\n\t\t\tif (above === -1) return 1;\n\n\t\t\tconst high = positions[above];\n\t\t\tconst low = positions[above - 1];\n\t\t\tif (high === undefined || low === undefined) return 0;\n\n\t\t\treturn (above - 1 + (value - low) / (high - low)) / last;\n\t\t},\n\t\tvalueAt: (proportion) => positions[Math.round(clampProportion(proportion) * last)] ?? NaN,\n\t};\n}\n"],"mappings":";;;AAOA,SAAgB,YAAY,KAAa,KAAoB;CAC5D,MAAM,QAAQ,MAAM;CAEpB,OAAO;EACN,eAAe,UAAW,QAAQ,IAAI,iBAAiB,QAAQ,OAAO,KAAK,IAAI;EAC/E,UAAU,eAAe,MAAM,gBAAgB,UAAU,IAAI;CAC9D;AACD;AAEA,SAAgB,SAAS,KAAa,KAAgC;CACrE,IAAI,OAAO,KAAK,OAAO,KAAK,OAAO;CAEnC,MAAM,OAAO,KAAK,IAAI,MAAM,GAAG;CAE/B,OAAO;EACN,eAAe,UAAU,gBAAgB,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI;EACpF,UAAU,eAAe;GACxB,MAAM,UAAU,gBAAgB,UAAU;GAC1C,IAAI,YAAY,GAAG,OAAO;GAC1B,IAAI,YAAY,GAAG,OAAO;GAE1B,OAAO,MAAM,KAAK,IAAI,UAAU,IAAI;EACrC;CACD;AACD;AAGA,SAAgB,UAAU,KAAa,KAAa,UAAqC;CACxF,MAAM,QAAQ,MAAM;CACpB,MAAM,sBAAsB,WAAW,OAAO;CAE9C,IAAI,uBAAuB,MAAO,EAAE,qBAAqB,KAAK,qBAAqB,IAClF,OAAO;CAER,MAAM,OAAO,KAAK,IAAI,EAAG,IAAI,KAAK,IAAI,kBAAkB;CAExD,OAAO;EACN,eAAe,UAAU,iBAAiB,QAAQ,OAAO,KAAK,KAAK;EACnE,UAAU,eAAe,MAAM,QAAQ,gBAAgB,UAAU,MAAM,IAAI;CAC5E;AACD;AAEA,SAAgB,UAAU,WAAyC;CAClE,MAAM,OAAO,UAAU,SAAS;CAEhC,OAAO;EACN,eAAe,UAAU;GACxB,IAAI,OAAO,MAAM,KAAK,GAAG,OAAO;GAEhC,MAAM,QAAQ,UAAU,WAAW,aAAa,YAAY,KAAK;GACjE,IAAI,UAAU,IAAI,OAAO;GAEzB,MAAM,OAAO,UAAU;GACvB,MAAM,MAAM,UAAU,QAAQ;GAC9B,IAAI,SAAS,UAAa,QAAQ,QAAW,OAAO;GAEpD,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,OAAO,QAAQ;EACrD;EACA,UAAU,eAAe,UAAU,KAAK,MAAM,gBAAgB,UAAU,IAAI,IAAI,MAAM;CACvF;AACD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/lib/time-regions.ts
|
|
2
|
+
function readRegions(regions) {
|
|
3
|
+
if ("start" in regions) return Array.from({ length: regions.length }, (_entry, index) => [regions.start(index), regions.end(index)]);
|
|
4
|
+
return [...regions].map(([start, end]) => [start, end]);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { readRegions };
|
|
9
|
+
//# sourceMappingURL=time-regions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-regions.js","names":[],"sources":["../../../src/lib/time-regions.ts"],"sourcesContent":["export type TimeRegions = Iterable<readonly [number, number]> | TimeRanges;\n\nexport function readRegions(regions: TimeRegions): Array<[number, number]> {\n\tif ('start' in regions) {\n\t\treturn Array.from({ length: regions.length }, (_entry, index) => [\n\t\t\tregions.start(index),\n\t\t\tregions.end(index),\n\t\t]);\n\t}\n\n\treturn [...regions].map(([start, end]) => [start, end]);\n}\n"],"mappings":";AAEA,SAAgB,YAAY,SAA+C;CAC1E,IAAI,WAAW,SACd,OAAO,MAAM,KAAK,EAAE,QAAQ,QAAQ,OAAO,IAAI,QAAQ,UAAU,CAChE,QAAQ,MAAM,KAAK,GACnB,QAAQ,IAAI,KAAK,CAClB,CAAC;CAGF,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,OAAO,GAAG,CAAC;AACvD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/lib/tracking-clock.ts
|
|
2
|
+
const seekThresholdSeconds = .5;
|
|
3
|
+
const jitterSeconds = .05;
|
|
4
|
+
function isLandingOnSource({ isPlaying, seconds }, carried, lastSourceSeconds) {
|
|
5
|
+
const hasSteppedBack = lastSourceSeconds !== void 0 && lastSourceSeconds - seconds > jitterSeconds;
|
|
6
|
+
return !isPlaying || hasSteppedBack || Math.abs(seconds - carried) > seekThresholdSeconds;
|
|
7
|
+
}
|
|
8
|
+
function createTrackingClock() {
|
|
9
|
+
let positionSeconds;
|
|
10
|
+
let lastFrameMs;
|
|
11
|
+
let lastSourceSeconds;
|
|
12
|
+
let wasPlaying = false;
|
|
13
|
+
return { read: (frameMs, source) => {
|
|
14
|
+
const { isPlaying, playbackRate, seconds } = source;
|
|
15
|
+
const elapsedSeconds = wasPlaying ? Math.max(0, frameMs - (lastFrameMs ?? frameMs)) / 1e3 : 0;
|
|
16
|
+
const carried = (positionSeconds ?? seconds) + elapsedSeconds * playbackRate;
|
|
17
|
+
lastFrameMs = frameMs;
|
|
18
|
+
wasPlaying = isPlaying;
|
|
19
|
+
if (!Number.isFinite(seconds)) {
|
|
20
|
+
if (positionSeconds !== void 0) positionSeconds = carried;
|
|
21
|
+
return carried;
|
|
22
|
+
}
|
|
23
|
+
const isLanding = isLandingOnSource(source, carried, lastSourceSeconds);
|
|
24
|
+
lastSourceSeconds = seconds;
|
|
25
|
+
if (positionSeconds === void 0 || isLanding) {
|
|
26
|
+
positionSeconds = seconds;
|
|
27
|
+
return positionSeconds;
|
|
28
|
+
}
|
|
29
|
+
positionSeconds = carried + (seconds - carried) * (1 - .94 ** (elapsedSeconds * 60));
|
|
30
|
+
return positionSeconds;
|
|
31
|
+
} };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { createTrackingClock };
|
|
36
|
+
//# sourceMappingURL=tracking-clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking-clock.js","names":[],"sources":["../../../src/lib/tracking-clock.ts"],"sourcesContent":["const seekThresholdSeconds = 0.5;\nconst jitterSeconds = 0.05;\n\n// The share of the error closed in a 60 Hz frame; scaled by elapsed time so every refresh rate converges alike\nconst catchUpPerFrame = 0.06;\n\ninterface ClockSource {\n\tisPlaying: boolean;\n\tplaybackRate: number;\n\tseconds: number;\n}\n\ninterface TrackingClock {\n\t// `frameMs` is the frame's timestamp, never `performance.now()` in the callback\n\tread: (frameMs: number, source: ClockSource) => number;\n}\n\nfunction isLandingOnSource(\n\t{ isPlaying, seconds }: ClockSource,\n\tcarried: number,\n\tlastSourceSeconds: number | undefined,\n): boolean {\n\tconst hasSteppedBack =\n\t\tlastSourceSeconds !== undefined && lastSourceSeconds - seconds > jitterSeconds;\n\n\treturn !isPlaying || hasSteppedBack || Math.abs(seconds - carried) > seekThresholdSeconds;\n}\n\nexport function createTrackingClock(): TrackingClock {\n\tlet positionSeconds: number | undefined;\n\tlet lastFrameMs: number | undefined;\n\tlet lastSourceSeconds: number | undefined;\n\tlet wasPlaying = false;\n\n\treturn {\n\t\tread: (frameMs, source) => {\n\t\t\tconst { isPlaying, playbackRate, seconds } = source;\n\t\t\tconst elapsedSeconds = wasPlaying\n\t\t\t\t? Math.max(0, frameMs - (lastFrameMs ?? frameMs)) / 1000\n\t\t\t\t: 0;\n\t\t\tconst carried = (positionSeconds ?? seconds) + elapsedSeconds * playbackRate;\n\n\t\t\tlastFrameMs = frameMs;\n\t\t\twasPlaying = isPlaying;\n\n\t\t\tif (!Number.isFinite(seconds)) {\n\t\t\t\tif (positionSeconds !== undefined) positionSeconds = carried;\n\t\t\t\treturn carried;\n\t\t\t}\n\n\t\t\tconst isLanding = isLandingOnSource(source, carried, lastSourceSeconds);\n\n\t\t\tlastSourceSeconds = seconds;\n\n\t\t\tif (positionSeconds === undefined || isLanding) {\n\t\t\t\tpositionSeconds = seconds;\n\t\t\t\treturn positionSeconds;\n\t\t\t}\n\n\t\t\tpositionSeconds =\n\t\t\t\tcarried + (seconds - carried) * (1 - (1 - catchUpPerFrame) ** (elapsedSeconds * 60));\n\n\t\t\treturn positionSeconds;\n\t\t},\n\t};\n}\n"],"mappings":";AAAA,MAAM,uBAAuB;AAC7B,MAAM,gBAAgB;AAgBtB,SAAS,kBACR,EAAE,WAAW,WACb,SACA,mBACU;CACV,MAAM,iBACL,sBAAsB,UAAa,oBAAoB,UAAU;CAElE,OAAO,CAAC,aAAa,kBAAkB,KAAK,IAAI,UAAU,OAAO,IAAI;AACtE;AAEA,SAAgB,sBAAqC;CACpD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,aAAa;CAEjB,OAAO,EACN,OAAO,SAAS,WAAW;EAC1B,MAAM,EAAE,WAAW,cAAc,YAAY;EAC7C,MAAM,iBAAiB,aACpB,KAAK,IAAI,GAAG,WAAW,eAAe,QAAQ,IAAI,MAClD;EACH,MAAM,WAAW,mBAAmB,WAAW,iBAAiB;EAEhE,cAAc;EACd,aAAa;EAEb,IAAI,CAAC,OAAO,SAAS,OAAO,GAAG;GAC9B,IAAI,oBAAoB,QAAW,kBAAkB;GACrD,OAAO;EACR;EAEA,MAAM,YAAY,kBAAkB,QAAQ,SAAS,iBAAiB;EAEtE,oBAAoB;EAEpB,IAAI,oBAAoB,UAAa,WAAW;GAC/C,kBAAkB;GAClB,OAAO;EACR;EAEA,kBACC,WAAW,UAAU,YAAY,IAAK,QAAyB,iBAAiB;EAEjF,OAAO;CACR,EACD;AACD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/lib/value-mapping.d.ts
|
|
2
|
+
export interface ValueSpec {
|
|
3
|
+
detent?: number;
|
|
4
|
+
isNotched: boolean;
|
|
5
|
+
isWrapping: boolean;
|
|
6
|
+
max: number;
|
|
7
|
+
midpoint?: number;
|
|
8
|
+
min: number;
|
|
9
|
+
positions?: ReadonlyArray<number>;
|
|
10
|
+
step: number;
|
|
11
|
+
taper: 'linear' | 'log';
|
|
12
|
+
}
|
|
13
|
+
export interface ValueMapping {
|
|
14
|
+
readonly bounds: [number, number];
|
|
15
|
+
readonly isWrapping: boolean;
|
|
16
|
+
readonly keyTarget: (key: string, from: number, keyStep?: number) => number | undefined;
|
|
17
|
+
readonly positionCount: number | undefined;
|
|
18
|
+
readonly proportionOf: (value: number) => number;
|
|
19
|
+
readonly snap: (value: number) => number;
|
|
20
|
+
readonly valueAt: (proportion: number) => number;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=value-mapping.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { clamp, decimalPlaces, roundTo, trimFloat } from "./math.js";
|
|
2
|
+
import { nearestPosition } from "./number-list.js";
|
|
3
|
+
import { linearTaper, listTaper, logTaper, skewTaper } from "./taper.js";
|
|
4
|
+
|
|
5
|
+
//#region src/lib/value-mapping.ts
|
|
6
|
+
const resetKeys = /* @__PURE__ */ new Set(["Backspace", "Delete"]);
|
|
7
|
+
const keySteps = /* @__PURE__ */ new Map([
|
|
8
|
+
["ArrowDown", -1],
|
|
9
|
+
["ArrowLeft", -1],
|
|
10
|
+
["ArrowRight", 1],
|
|
11
|
+
["ArrowUp", 1],
|
|
12
|
+
["PageDown", -10],
|
|
13
|
+
["PageUp", 10]
|
|
14
|
+
]);
|
|
15
|
+
function curveOf(spec, max) {
|
|
16
|
+
const { min, positions } = spec;
|
|
17
|
+
if (positions) return listTaper(positions);
|
|
18
|
+
if (spec.isNotched || spec.isWrapping) return void 0;
|
|
19
|
+
return (spec.taper === "log" ? logTaper(min, max) : void 0) ?? skewTaper(min, max, spec.midpoint ?? NaN);
|
|
20
|
+
}
|
|
21
|
+
function countPositions(spec, count) {
|
|
22
|
+
if (!spec.isNotched || !Number.isFinite(count) || count < 2) return;
|
|
23
|
+
return count;
|
|
24
|
+
}
|
|
25
|
+
function countSteps(spec, range) {
|
|
26
|
+
if (spec.step <= 0) return NaN;
|
|
27
|
+
const fit = trimFloat(range / spec.step);
|
|
28
|
+
return spec.isWrapping ? Math.max(1, Math.ceil(fit)) : Math.floor(fit) + 1;
|
|
29
|
+
}
|
|
30
|
+
function valueMapping(spec) {
|
|
31
|
+
const { min, positions, step } = spec;
|
|
32
|
+
const max = Math.max(min, spec.max);
|
|
33
|
+
const range = max - min;
|
|
34
|
+
const count = countSteps(spec, range);
|
|
35
|
+
const isStepped = step > 0 && !positions;
|
|
36
|
+
const curve = curveOf(spec, max);
|
|
37
|
+
const taper = curve ?? linearTaper(min, max);
|
|
38
|
+
function round(value) {
|
|
39
|
+
if (step <= 0) return trimFloat(value);
|
|
40
|
+
return roundTo(value, Math.max(decimalPlaces(step), decimalPlaces(min)));
|
|
41
|
+
}
|
|
42
|
+
const top = round((count - 1) * step);
|
|
43
|
+
function limits() {
|
|
44
|
+
if (positions) return [positions[0] ?? min, positions.at(-1) ?? max];
|
|
45
|
+
return [min, isStepped && !spec.isWrapping ? round(min + top) : max];
|
|
46
|
+
}
|
|
47
|
+
const bounds = limits();
|
|
48
|
+
function wrap(value, span) {
|
|
49
|
+
const offset = round(((value - min) % span + span) % span);
|
|
50
|
+
return offset >= span ? 0 : offset;
|
|
51
|
+
}
|
|
52
|
+
function bound(value) {
|
|
53
|
+
if (!spec.isWrapping) return clamp(value, ...bounds);
|
|
54
|
+
if (range <= 0) return min;
|
|
55
|
+
return round(min + wrap(value, range));
|
|
56
|
+
}
|
|
57
|
+
function ring(value, span) {
|
|
58
|
+
if (range <= 0) return min;
|
|
59
|
+
const offset = wrap(value, span);
|
|
60
|
+
if (offset > top) return span - offset <= offset - top ? min : round(min + top);
|
|
61
|
+
return round(min + Math.round(offset / step) * step);
|
|
62
|
+
}
|
|
63
|
+
function snap(value) {
|
|
64
|
+
if (positions) return nearestPosition(positions, value);
|
|
65
|
+
if (step <= 0) return bound(round(value));
|
|
66
|
+
if (spec.isWrapping) return ring(value, range);
|
|
67
|
+
return bound(round(min + Math.round((value - min) / step) * step));
|
|
68
|
+
}
|
|
69
|
+
function stepBy(from, steps, size) {
|
|
70
|
+
if (positions) {
|
|
71
|
+
const index = clamp(positions.indexOf(from) + steps, 0, positions.length - 1);
|
|
72
|
+
return positions[index] ?? from;
|
|
73
|
+
}
|
|
74
|
+
if (!curve) return from + steps * size;
|
|
75
|
+
const next = snap(curve.valueAt(curve.proportionOf(from) + steps / 100));
|
|
76
|
+
return next === from ? from + Math.sign(steps) * size : next;
|
|
77
|
+
}
|
|
78
|
+
function stopAtDetent(from, next) {
|
|
79
|
+
if (spec.detent === void 0) return next;
|
|
80
|
+
const detent = snap(spec.detent);
|
|
81
|
+
const toward = Math.sign(next - from) * (detent - from);
|
|
82
|
+
const ahead = spec.isWrapping ? wrap(min + toward, isStepped ? count * step : range) : toward;
|
|
83
|
+
return ahead > 0 && ahead < Math.abs(next - from) ? detent : next;
|
|
84
|
+
}
|
|
85
|
+
function keyStride(keyStep) {
|
|
86
|
+
if (positions || !(keyStep > 0)) return void 0;
|
|
87
|
+
return step > 0 ? Math.max(1, Math.round(trimFloat(keyStep / step))) * step : keyStep;
|
|
88
|
+
}
|
|
89
|
+
function end(size) {
|
|
90
|
+
if (!spec.isWrapping) return bounds[1];
|
|
91
|
+
return min + (isStepped ? top : (Math.ceil(range / size) - 1) * size);
|
|
92
|
+
}
|
|
93
|
+
function target(key, from, keyStep) {
|
|
94
|
+
if (key === "Home") return bounds[0];
|
|
95
|
+
const size = step > 0 ? step : range / 100;
|
|
96
|
+
if (key === "End") return snap(end(size));
|
|
97
|
+
const steps = keySteps.get(key);
|
|
98
|
+
if (steps === void 0) return void 0;
|
|
99
|
+
const stride = keyStride(keyStep);
|
|
100
|
+
const next = stopAtDetent(from, stride === void 0 ? stepBy(from, steps, size) : from + steps * stride);
|
|
101
|
+
return isStepped && spec.isWrapping ? ring(next, count * step) : snap(next);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
bounds,
|
|
105
|
+
isWrapping: spec.isWrapping,
|
|
106
|
+
keyTarget: (key, from, keyStep = 0) => target(key, from, keyStep),
|
|
107
|
+
positionCount: countPositions(spec, positions?.length ?? count),
|
|
108
|
+
proportionOf: (value) => taper.proportionOf(bound(value)),
|
|
109
|
+
snap,
|
|
110
|
+
valueAt: (proportion) => taper.valueAt(proportion)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
export { resetKeys, valueMapping };
|
|
116
|
+
//# sourceMappingURL=value-mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-mapping.js","names":[],"sources":["../../../src/lib/value-mapping.ts"],"sourcesContent":["import type { Taper } from '#lib/taper.ts';\n\nimport { clamp, decimalPlaces, roundTo, trimFloat } from '#lib/math.ts';\nimport { nearestPosition } from '#lib/number-list.ts';\nimport { linearTaper, listTaper, logTaper, skewTaper } from '#lib/taper.ts';\n\nexport interface ValueSpec {\n\tdetent?: number;\n\tisNotched: boolean;\n\tisWrapping: boolean;\n\tmax: number;\n\tmidpoint?: number;\n\tmin: number;\n\tpositions?: ReadonlyArray<number>;\n\tstep: number;\n\ttaper: 'linear' | 'log';\n}\n\nexport interface ValueMapping {\n\treadonly bounds: [number, number];\n\treadonly isWrapping: boolean;\n\treadonly keyTarget: (key: string, from: number, keyStep?: number) => number | undefined;\n\treadonly positionCount: number | undefined;\n\treadonly proportionOf: (value: number) => number;\n\treadonly snap: (value: number) => number;\n\treadonly valueAt: (proportion: number) => number;\n}\n\n// A Mac keyboard's Delete sends Backspace\nexport const resetKeys = new Set(['Backspace', 'Delete']);\n\nconst pageSteps = 10;\n\nconst keySteps = new Map([\n\t['ArrowDown', -1],\n\t['ArrowLeft', -1],\n\t['ArrowRight', 1],\n\t['ArrowUp', 1],\n\t['PageDown', -pageSteps],\n\t['PageUp', pageSteps],\n]);\n\nfunction curveOf(spec: ValueSpec, max: number): Taper | undefined {\n\tconst { min, positions } = spec;\n\tif (positions) return listTaper(positions);\n\tif (spec.isNotched || spec.isWrapping) return undefined;\n\n\tconst log = spec.taper === 'log' ? logTaper(min, max) : undefined;\n\n\treturn log ?? skewTaper(min, max, spec.midpoint ?? NaN);\n}\n\nfunction countPositions(spec: ValueSpec, count: number): number | undefined {\n\tif (!spec.isNotched || !Number.isFinite(count) || count < 2) return;\n\n\treturn count;\n}\n\n// A wrapping mapping stops a step short of max, which is min again\nfunction countSteps(spec: ValueSpec, range: number): number {\n\tif (spec.step <= 0) return NaN;\n\n\tconst fit = trimFloat(range / spec.step);\n\n\treturn spec.isWrapping ? Math.max(1, Math.ceil(fit)) : Math.floor(fit) + 1;\n}\n\nexport function valueMapping(spec: ValueSpec): ValueMapping {\n\tconst { min, positions, step } = spec;\n\tconst max = Math.max(min, spec.max);\n\tconst range = max - min;\n\tconst count = countSteps(spec, range);\n\tconst isStepped = step > 0 && !positions;\n\tconst curve = curveOf(spec, max);\n\tconst taper = curve ?? linearTaper(min, max);\n\n\t// Decimal places, since significant digits keep the float residue beside 0\n\tfunction round(value: number): number {\n\t\tif (step <= 0) return trimFloat(value);\n\n\t\treturn roundTo(value, Math.max(decimalPlaces(step), decimalPlaces(min)));\n\t}\n\n\tconst top = round((count - 1) * step);\n\n\tfunction limits(): [number, number] {\n\t\tif (positions) return [positions[0] ?? min, positions.at(-1) ?? max];\n\n\t\treturn [min, isStepped && !spec.isWrapping ? round(min + top) : max];\n\t}\n\n\tconst bounds = limits();\n\n\tfunction wrap(value: number, span: number): number {\n\t\tconst offset = round((((value - min) % span) + span) % span);\n\n\t\treturn offset >= span ? 0 : offset;\n\t}\n\n\tfunction bound(value: number): number {\n\t\tif (!spec.isWrapping) return clamp(value, ...bounds);\n\t\tif (range <= 0) return min;\n\n\t\treturn round(min + wrap(value, range));\n\t}\n\n\t// A step that does not divide the range leaves a short seam, where a value goes to the nearer side\n\tfunction ring(value: number, span: number): number {\n\t\tif (range <= 0) return min;\n\n\t\tconst offset = wrap(value, span);\n\t\tif (offset > top) return span - offset <= offset - top ? min : round(min + top);\n\n\t\treturn round(min + Math.round(offset / step) * step);\n\t}\n\n\tfunction snap(value: number): number {\n\t\tif (positions) return nearestPosition(positions, value);\n\t\tif (step <= 0) return bound(round(value));\n\t\tif (spec.isWrapping) return ring(value, range);\n\n\t\treturn bound(round(min + Math.round((value - min) / step) * step));\n\t}\n\n\tfunction stepBy(from: number, steps: number, size: number): number {\n\t\tif (positions) {\n\t\t\tconst index = clamp(positions.indexOf(from) + steps, 0, positions.length - 1);\n\n\t\t\treturn positions[index] ?? from;\n\t\t}\n\t\tif (!curve) return from + steps * size;\n\n\t\tconst next = snap(curve.valueAt(curve.proportionOf(from) + steps / 100));\n\n\t\treturn next === from ? from + Math.sign(steps) * size : next;\n\t}\n\n\tfunction stopAtDetent(from: number, next: number): number {\n\t\tif (spec.detent === undefined) return next;\n\n\t\tconst detent = snap(spec.detent);\n\t\tconst toward = Math.sign(next - from) * (detent - from);\n\t\tconst ahead = spec.isWrapping ? wrap(min + toward, isStepped ? count * step : range) : toward;\n\n\t\treturn ahead > 0 && ahead < Math.abs(next - from) ? detent : next;\n\t}\n\n\tfunction keyStride(keyStep: number): number | undefined {\n\t\tif (positions || !(keyStep > 0)) return undefined;\n\n\t\treturn step > 0 ? Math.max(1, Math.round(trimFloat(keyStep / step))) * step : keyStep;\n\t}\n\n\tfunction end(size: number): number {\n\t\tif (!spec.isWrapping) return bounds[1];\n\n\t\treturn min + (isStepped ? top : (Math.ceil(range / size) - 1) * size);\n\t}\n\n\tfunction target(key: string, from: number, keyStep: number): number | undefined {\n\t\tif (key === 'Home') return bounds[0];\n\n\t\tconst size = step > 0 ? step : range / 100;\n\t\tif (key === 'End') return snap(end(size));\n\n\t\tconst steps = keySteps.get(key);\n\t\tif (steps === undefined) return undefined;\n\n\t\tconst stride = keyStride(keyStep);\n\t\tconst moved = stride === undefined ? stepBy(from, steps, size) : from + steps * stride;\n\t\tconst next = stopAtDetent(from, moved);\n\n\t\treturn isStepped && spec.isWrapping ? ring(next, count * step) : snap(next);\n\t}\n\n\treturn {\n\t\tbounds,\n\t\tisWrapping: spec.isWrapping,\n\t\tkeyTarget: (key, from, keyStep = 0) => target(key, from, keyStep),\n\t\tpositionCount: countPositions(spec, positions?.length ?? count),\n\t\tproportionOf: (value) => taper.proportionOf(bound(value)),\n\t\tsnap,\n\t\tvalueAt: (proportion) => taper.valueAt(proportion),\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/lib/waveform-buckets.ts
|
|
2
|
+
function foldPairs(peaks, fromPair, toPair) {
|
|
3
|
+
const { samples } = peaks;
|
|
4
|
+
const channels = Math.max(1, peaks.channels ?? 1);
|
|
5
|
+
const stride = channels * 2;
|
|
6
|
+
const pairsTotal = Math.floor(samples.length / stride);
|
|
7
|
+
let low = 0;
|
|
8
|
+
let high = 0;
|
|
9
|
+
for (let pair = Math.max(0, fromPair); pair < Math.min(pairsTotal, toPair); pair += 1) for (let channel = 0; channel < channels; channel += 1) {
|
|
10
|
+
const at = pair * stride + channel * 2;
|
|
11
|
+
low = Math.min(low, samples[at] ?? 0);
|
|
12
|
+
high = Math.max(high, samples[at + 1] ?? 0);
|
|
13
|
+
}
|
|
14
|
+
return [low / peaks.fullScale, high / peaks.fullScale];
|
|
15
|
+
}
|
|
16
|
+
function waveformBuckets(peaks, view) {
|
|
17
|
+
const { pairsPerSecond } = peaks;
|
|
18
|
+
if (pairsPerSecond <= 0 || view.pixelsPerSecond <= 0) return [];
|
|
19
|
+
const pxPerPair = view.pixelsPerSecond / pairsPerSecond;
|
|
20
|
+
const pairs = Math.max(1, Math.round(1 / pxPerPair));
|
|
21
|
+
const openingPair = view.startSeconds * pairsPerSecond;
|
|
22
|
+
const first = Math.floor(openingPair / pairs) - 1;
|
|
23
|
+
const count = Math.ceil(view.width / (pairs * pxPerPair)) + 3;
|
|
24
|
+
const buckets = [];
|
|
25
|
+
for (let index = 0; index < count; index += 1) {
|
|
26
|
+
const fromPair = (first + index) * pairs;
|
|
27
|
+
const toPair = fromPair + pairs;
|
|
28
|
+
const [low, high] = foldPairs(peaks, fromPair, toPair);
|
|
29
|
+
buckets.push({
|
|
30
|
+
fromPair,
|
|
31
|
+
high,
|
|
32
|
+
low,
|
|
33
|
+
toPair,
|
|
34
|
+
x: (fromPair - openingPair) * pxPerPair
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return buckets;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { waveformBuckets };
|
|
42
|
+
//# sourceMappingURL=waveform-buckets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waveform-buckets.js","names":[],"sources":["../../../src/lib/waveform-buckets.ts"],"sourcesContent":["// Interleaved min, max per channel per pair; `fullScale` is 128, 32768 or 1\nexport interface WaveformPeaks {\n\tchannels?: number;\n\tfullScale: number;\n\tpairsPerSecond: number;\n\tsamples: Float32Array | Int8Array | Int16Array;\n}\n\ninterface WaveformBucket {\n\tfromPair: number;\n\thigh: number;\n\tlow: number;\n\ttoPair: number;\n\tx: number;\n}\n\ninterface BucketView {\n\tpixelsPerSecond: number;\n\tstartSeconds: number;\n\twidth: number;\n}\n\nfunction foldPairs(peaks: WaveformPeaks, fromPair: number, toPair: number): [number, number] {\n\tconst { samples } = peaks;\n\tconst channels = Math.max(1, peaks.channels ?? 1);\n\tconst stride = channels * 2;\n\tconst pairsTotal = Math.floor(samples.length / stride);\n\tlet low = 0;\n\tlet high = 0;\n\n\tfor (let pair = Math.max(0, fromPair); pair < Math.min(pairsTotal, toPair); pair += 1) {\n\t\tfor (let channel = 0; channel < channels; channel += 1) {\n\t\t\tconst at = pair * stride + channel * 2;\n\n\t\t\tlow = Math.min(low, samples[at] ?? 0);\n\t\t\thigh = Math.max(high, samples[at + 1] ?? 0);\n\t\t}\n\t}\n\n\treturn [low / peaks.fullScale, high / peaks.fullScale];\n}\n\n// Keyed to absolute pairs; keyed to the window, the same samples re-bucket every frame and judder\nexport function waveformBuckets(peaks: WaveformPeaks, view: BucketView): Array<WaveformBucket> {\n\tconst { pairsPerSecond } = peaks;\n\tif (pairsPerSecond <= 0 || view.pixelsPerSecond <= 0) return [];\n\n\tconst pxPerPair = view.pixelsPerSecond / pairsPerSecond;\n\tconst pairs = Math.max(1, Math.round(1 / pxPerPair));\n\tconst openingPair = view.startSeconds * pairsPerSecond;\n\tconst first = Math.floor(openingPair / pairs) - 1;\n\tconst count = Math.ceil(view.width / (pairs * pxPerPair)) + 3;\n\tconst buckets: Array<WaveformBucket> = [];\n\n\tfor (let index = 0; index < count; index += 1) {\n\t\tconst fromPair = (first + index) * pairs;\n\t\tconst toPair = fromPair + pairs;\n\t\tconst [low, high] = foldPairs(peaks, fromPair, toPair);\n\n\t\tbuckets.push({ fromPair, high, low, toPair, x: (fromPair - openingPair) * pxPerPair });\n\t}\n\n\treturn buckets;\n}\n"],"mappings":";AAsBA,SAAS,UAAU,OAAsB,UAAkB,QAAkC;CAC5F,MAAM,EAAE,YAAY;CACpB,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,YAAY,CAAC;CAChD,MAAM,SAAS,WAAW;CAC1B,MAAM,aAAa,KAAK,MAAM,QAAQ,SAAS,MAAM;CACrD,IAAI,MAAM;CACV,IAAI,OAAO;CAEX,KAAK,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,GAAG,OAAO,KAAK,IAAI,YAAY,MAAM,GAAG,QAAQ,GACnF,KAAK,IAAI,UAAU,GAAG,UAAU,UAAU,WAAW,GAAG;EACvD,MAAM,KAAK,OAAO,SAAS,UAAU;EAErC,MAAM,KAAK,IAAI,KAAK,QAAQ,OAAO,CAAC;EACpC,OAAO,KAAK,IAAI,MAAM,QAAQ,KAAK,MAAM,CAAC;CAC3C;CAGD,OAAO,CAAC,MAAM,MAAM,WAAW,OAAO,MAAM,SAAS;AACtD;AAGA,SAAgB,gBAAgB,OAAsB,MAAyC;CAC9F,MAAM,EAAE,mBAAmB;CAC3B,IAAI,kBAAkB,KAAK,KAAK,mBAAmB,GAAG,OAAO,CAAC;CAE9D,MAAM,YAAY,KAAK,kBAAkB;CACzC,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,SAAS,CAAC;CACnD,MAAM,cAAc,KAAK,eAAe;CACxC,MAAM,QAAQ,KAAK,MAAM,cAAc,KAAK,IAAI;CAChD,MAAM,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,UAAU,IAAI;CAC5D,MAAM,UAAiC,CAAC;CAExC,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAAG;EAC9C,MAAM,YAAY,QAAQ,SAAS;EACnC,MAAM,SAAS,WAAW;EAC1B,MAAM,CAAC,KAAK,QAAQ,UAAU,OAAO,UAAU,MAAM;EAErD,QAAQ,KAAK;GAAE;GAAU;GAAM;GAAK;GAAQ,IAAI,WAAW,eAAe;EAAU,CAAC;CACtF;CAEA,OAAO;AACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-attribute.js","names":[],"sources":["../../../src/lib/write-attribute.ts"],"sourcesContent":["export function writeAttribute(element: Element, name: string, value: string | undefined): void {\n\tif (value === undefined) {\n\t\telement.removeAttribute(name);\n\t\treturn;\n\t}\n\n\telement.setAttribute(name, value);\n}\n"],"mappings":";AAAA,SAAgB,eAAe,SAAkB,MAAc,OAAiC;CAC/F,IAAI,UAAU,QAAW;EACxB,QAAQ,gBAAgB,IAAI;EAC5B;CACD;
|
|
1
|
+
{"version":3,"file":"write-attribute.js","names":[],"sources":["../../../src/lib/write-attribute.ts"],"sourcesContent":["export function writeAttribute(element: Element, name: string, value: string | undefined): void {\n\tif (value === undefined) {\n\t\telement.removeAttribute(name);\n\t\treturn;\n\t}\n\tif (element.getAttribute(name) === value) return;\n\n\telement.setAttribute(name, value);\n}\n"],"mappings":";AAAA,SAAgB,eAAe,SAAkB,MAAc,OAAiC;CAC/F,IAAI,UAAU,QAAW;EACxB,QAAQ,gBAAgB,IAAI;EAC5B;CACD;CACA,IAAI,QAAQ,aAAa,IAAI,MAAM,OAAO;CAE1C,QAAQ,aAAa,MAAM,KAAK;AACjC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@layer sonic {
|
|
2
|
+
.sonic-skin-flat {
|
|
3
|
+
--sonic-relief: 0;
|
|
4
|
+
--sonic-lit: oklch(58% 0.19 255deg);
|
|
5
|
+
--sonic-unlit: oklch(84% 0.006 255deg);
|
|
6
|
+
--sonic-cap: oklch(93% 0.004 255deg);
|
|
7
|
+
--sonic-cap-hover: oklch(97% 0.004 255deg);
|
|
8
|
+
--sonic-cap-edge: oklch(72% 0.01 255deg);
|
|
9
|
+
--sonic-well: var(--sonic-unlit);
|
|
10
|
+
--sonic-well-lit: var(--sonic-lit);
|
|
11
|
+
--sonic-bezel: oklch(90% 0.004 255deg);
|
|
12
|
+
--sonic-lens: var(--sonic-unlit);
|
|
13
|
+
--sonic-led-lens-ratio: 0.8;
|
|
14
|
+
--sonic-waveform-edge: var(--sonic-lit);
|
|
15
|
+
--sonic-ink: oklch(27% 0.012 255deg);
|
|
16
|
+
--sonic-focus: oklch(52% 0.21 255deg);
|
|
17
|
+
--sonic-glass: oklch(97% 0.003 255deg);
|
|
18
|
+
--sonic-glass-text: oklch(27% 0.012 255deg);
|
|
19
|
+
--sonic-glass-edge: oklch(84% 0.006 255deg);
|
|
20
|
+
--sonic-glass-texture: 0;
|
|
21
|
+
--sonic-panel: oklch(98.5% 0.002 255deg);
|
|
22
|
+
--sonic-button-depth-ratio: 0;
|
|
23
|
+
--sonic-segmented-depth-ratio: 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/skins/ivory.css
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/* Ink derived from a pale cap would vanish against it */
|
|
2
1
|
@layer sonic {
|
|
3
2
|
.sonic-skin-ivory {
|
|
3
|
+
--sonic-relief: 1;
|
|
4
4
|
--sonic-cap: oklch(85.5% 0.024 85deg);
|
|
5
5
|
--sonic-cap-hover: oklch(90.5% 0.02 85deg);
|
|
6
|
-
--sonic-
|
|
6
|
+
--sonic-ink: oklch(18.5% 0.012 60deg);
|
|
7
7
|
}
|
|
8
8
|
}
|
package/dist/skins/lime.css
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
@layer sonic {
|
|
2
2
|
.sonic-skin-lime {
|
|
3
|
+
--sonic-relief: 1;
|
|
3
4
|
--sonic-lit: oklch(83.8% 0.205 118deg);
|
|
4
5
|
--sonic-unlit: oklch(28.6% 0.0203 237deg);
|
|
5
6
|
--sonic-cap: oklch(32.4% 0.0229 237deg);
|
|
6
7
|
--sonic-cap-hover: oklch(37.2% 0.025 237deg);
|
|
8
|
+
--sonic-ink: color-mix(in oklab, var(--sonic-cap) 30%, #fff);
|
|
7
9
|
--sonic-dial-ring-ratio: 0.05;
|
|
8
10
|
--sonic-dial-gap-ratio: 0.1;
|
|
9
11
|
}
|
package/dist/skins/slate.css
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
@layer sonic {
|
|
2
2
|
.sonic-skin-slate {
|
|
3
|
+
--sonic-relief: 1;
|
|
3
4
|
--sonic-lit: oklch(79.7% 0.134 212deg);
|
|
4
5
|
--sonic-unlit: oklch(27.9% 0.037 260deg);
|
|
5
6
|
--sonic-cap: oklch(37.2% 0.039 257deg);
|
|
6
7
|
--sonic-cap-hover: oklch(44.6% 0.037 257deg);
|
|
8
|
+
--sonic-ink: color-mix(in oklab, var(--sonic-cap) 30%, #fff);
|
|
7
9
|
--sonic-dial-ring-ratio: 0.12;
|
|
8
10
|
--sonic-dial-gap-ratio: 0.07;
|
|
9
11
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
@property --_sonic-button-size {
|
|
2
|
+
syntax: '<length>';
|
|
3
|
+
inherits: true;
|
|
4
|
+
initial-value: 32px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@keyframes sonic-button-busy {
|
|
8
|
+
from {
|
|
9
|
+
opacity: 0.35;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@layer sonic {
|
|
14
|
+
:is(sonic-button, .sonic-button) {
|
|
15
|
+
--_sonic-button-size: var(--sonic-button-size, 2rem);
|
|
16
|
+
--_sonic-button-aspect-ratio: var(--sonic-button-aspect-ratio, 1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:has(> .sonic-button):not([hidden]) {
|
|
20
|
+
display: contents;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
sonic-button:not(:defined, [hidden]) {
|
|
24
|
+
display: block;
|
|
25
|
+
visibility: hidden;
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-aspect-ratio));
|
|
28
|
+
block-size: var(--_sonic-button-size);
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sonic-button {
|
|
33
|
+
--_sonic-button-gap-ratio: var(--sonic-button-gap-ratio, 0);
|
|
34
|
+
--_sonic-button-radius-ratio: var(--sonic-button-radius-ratio, 0.2);
|
|
35
|
+
--_sonic-button-bevel-ratio: var(--sonic-button-bevel-ratio, 0.075);
|
|
36
|
+
--_sonic-button-face-radius-ratio: var(--sonic-button-face-radius-ratio, 0.15);
|
|
37
|
+
--_sonic-button-icon-ratio: var(--sonic-button-icon-ratio, 0.5);
|
|
38
|
+
--_sonic-button-led-ratio: var(--sonic-button-led-ratio, 0.2);
|
|
39
|
+
--_sonic-button-led-icon-ratio: var(--sonic-button-led-icon-ratio, 0.35);
|
|
40
|
+
--_sonic-button-depth-ratio: var(--sonic-button-depth-ratio, calc(0.03 * var(--_sonic-relief)));
|
|
41
|
+
--_sonic-button-press-scale: var(--sonic-button-press-scale, 0.985);
|
|
42
|
+
--_sonic-unit: calc(var(--_sonic-button-size) / 32);
|
|
43
|
+
--_sonic-button-corner: calc(var(--_sonic-button-size) * var(--_sonic-button-radius-ratio));
|
|
44
|
+
--_sonic-button-gap: calc(var(--_sonic-button-size) * var(--_sonic-button-gap-ratio));
|
|
45
|
+
--_sonic-keycap-depth: calc(var(--_sonic-button-size) * var(--_sonic-button-depth-ratio));
|
|
46
|
+
|
|
47
|
+
position: relative;
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-aspect-ratio));
|
|
50
|
+
block-size: var(--_sonic-button-size);
|
|
51
|
+
padding: 0;
|
|
52
|
+
font: inherit;
|
|
53
|
+
color: var(--_sonic-ink);
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
background: var(--_sonic-well-floor);
|
|
56
|
+
border: 0;
|
|
57
|
+
border-radius: var(--_sonic-button-corner);
|
|
58
|
+
box-shadow: var(--_sonic-well);
|
|
59
|
+
transition: color 120ms ease-out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:where([momentary]) > .sonic-button {
|
|
63
|
+
touch-action: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Before the latched rule, which must win unchanged */
|
|
67
|
+
:where([armed]) > .sonic-button:not(:disabled, [aria-disabled='true']) {
|
|
68
|
+
--_sonic-keycap-glow: color-mix(
|
|
69
|
+
in oklab,
|
|
70
|
+
var(--_sonic-lit) calc(8% * var(--_sonic-relief)),
|
|
71
|
+
transparent
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
color: color-mix(in oklab, var(--_sonic-lit) 55%, var(--_sonic-ink));
|
|
75
|
+
|
|
76
|
+
@media (forced-colors: active) {
|
|
77
|
+
--_sonic-keycap-glow: transparent;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Before the latched rule too; a button with an LED blinks the LED and keeps its legend */
|
|
82
|
+
:where([busy]) > .sonic-button:not(:disabled, [aria-disabled='true'], :has(.sonic-led)) {
|
|
83
|
+
color: color-mix(in oklab, var(--_sonic-lit) 55%, var(--_sonic-ink));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.sonic-button:active:where(:not(:disabled, [aria-disabled='true'])),
|
|
87
|
+
.sonic-button:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
|
|
88
|
+
--_sonic-keycap-shift: calc(var(--_sonic-keycap-depth) / 2);
|
|
89
|
+
--_sonic-keycap-scale: var(--_sonic-button-press-scale);
|
|
90
|
+
--_sonic-keycap-drop: var(--_sonic-press-drop);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.sonic-button:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
|
|
94
|
+
--_sonic-keycap-glow: var(--_sonic-keycap-lit-glow);
|
|
95
|
+
|
|
96
|
+
color: var(--_sonic-lit);
|
|
97
|
+
background: var(
|
|
98
|
+
--sonic-well-lit,
|
|
99
|
+
linear-gradient(
|
|
100
|
+
var(--_sonic-light-fall),
|
|
101
|
+
color-mix(
|
|
102
|
+
in oklab,
|
|
103
|
+
color-mix(in oklab, var(--_sonic-lit) 25%, #000) calc(100% * var(--_sonic-relief)),
|
|
104
|
+
transparent
|
|
105
|
+
),
|
|
106
|
+
color-mix(
|
|
107
|
+
in oklab,
|
|
108
|
+
color-mix(in oklab, var(--_sonic-lit) 60%, #000) calc(100% * var(--_sonic-relief)),
|
|
109
|
+
transparent
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.sonic-button:is(:disabled, [aria-disabled='true']) {
|
|
116
|
+
--_sonic-lit: var(--_sonic-lit-disabled);
|
|
117
|
+
|
|
118
|
+
color: var(--_sonic-etch);
|
|
119
|
+
cursor: default;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.sonic-button:not(
|
|
123
|
+
[aria-pressed='true'],
|
|
124
|
+
:disabled,
|
|
125
|
+
[aria-disabled='true'],
|
|
126
|
+
:where([momentary]:state(pressed), [armed]) > *,
|
|
127
|
+
:where([busy]) > :not(:has(.sonic-led))
|
|
128
|
+
):is(:focus-visible, :active) {
|
|
129
|
+
color: var(--_sonic-ink-lift);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (hover: hover) {
|
|
133
|
+
/* fallow-ignore-next-line code-duplication -- `:hover` and `:focus-visible` split by `(hover: hover)` */
|
|
134
|
+
.sonic-button:not(
|
|
135
|
+
[aria-pressed='true'],
|
|
136
|
+
:disabled,
|
|
137
|
+
[aria-disabled='true'],
|
|
138
|
+
:where([momentary]:state(pressed), [armed]) > *,
|
|
139
|
+
:where([busy]) > :not(:has(.sonic-led))
|
|
140
|
+
):hover {
|
|
141
|
+
color: var(--_sonic-ink-lift);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.sonic-button[aria-pressed='true']:not(:disabled, [aria-disabled='true']):focus-visible {
|
|
146
|
+
color: var(--sonic-lit-hover, var(--_sonic-lit));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@media (hover: hover) {
|
|
150
|
+
.sonic-button[aria-pressed='true']:not(:disabled, [aria-disabled='true']):hover {
|
|
151
|
+
color: var(--sonic-lit-hover, var(--_sonic-lit));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.sonic-button-cap {
|
|
156
|
+
--_sonic-keycap-corner: max(0px, var(--_sonic-button-corner) - var(--_sonic-button-gap));
|
|
157
|
+
--_sonic-keycap-bevel: calc(var(--_sonic-button-size) * var(--_sonic-button-bevel-ratio));
|
|
158
|
+
--_sonic-keycap-face-corner: calc(
|
|
159
|
+
var(--_sonic-button-size) * var(--_sonic-button-face-radius-ratio)
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
position: absolute;
|
|
163
|
+
inset: var(--_sonic-button-gap);
|
|
164
|
+
gap: calc(var(--_sonic-button-size) * var(--_sonic-button-led-ratio) / 2);
|
|
165
|
+
place-content: center;
|
|
166
|
+
|
|
167
|
+
& > .sonic-led {
|
|
168
|
+
--_sonic-led-size: var(
|
|
169
|
+
--sonic-led-size,
|
|
170
|
+
calc(var(--_sonic-button-size) * var(--_sonic-button-led-ratio))
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
& > svg {
|
|
175
|
+
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-icon-ratio));
|
|
176
|
+
block-size: calc(var(--_sonic-button-size) * var(--_sonic-button-icon-ratio));
|
|
177
|
+
filter: var(--_sonic-engraving);
|
|
178
|
+
mask-image: linear-gradient(
|
|
179
|
+
var(--_sonic-light-fall),
|
|
180
|
+
#000 15%,
|
|
181
|
+
rgb(0 0 0 / calc(1 - 0.4 * var(--_sonic-relief)))
|
|
182
|
+
);
|
|
183
|
+
mask-repeat: no-repeat;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.sonic-button-cap:has(> .sonic-led) > svg {
|
|
188
|
+
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-led-icon-ratio));
|
|
189
|
+
block-size: calc(var(--_sonic-button-size) * var(--_sonic-button-led-icon-ratio));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.sonic-button:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
|
|
193
|
+
.sonic-button-cap
|
|
194
|
+
> svg {
|
|
195
|
+
filter: var(--_sonic-engraving) var(--_sonic-legend-glow);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
199
|
+
.sonic-button:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
|
|
200
|
+
.sonic-button-cap
|
|
201
|
+
> [data-sonic-when='released'],
|
|
202
|
+
.sonic-button:not([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
|
|
203
|
+
.sonic-button-cap
|
|
204
|
+
> [data-sonic-when='pressed'] {
|
|
205
|
+
display: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
[busy]
|
|
209
|
+
> .sonic-button:not(:disabled, [aria-disabled='true'], :has(.sonic-led))
|
|
210
|
+
> .sonic-button-cap
|
|
211
|
+
> *,
|
|
212
|
+
[busy] > .sonic-button:not(:disabled, [aria-disabled='true']) .sonic-led::after {
|
|
213
|
+
animation: sonic-button-busy 500ms ease-in-out infinite alternate;
|
|
214
|
+
|
|
215
|
+
@media (prefers-reduced-motion: reduce) {
|
|
216
|
+
animation: none;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@media (forced-colors: active) {
|
|
221
|
+
.sonic-button-cap > svg {
|
|
222
|
+
filter: none;
|
|
223
|
+
mask: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.sonic-button:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
|
|
227
|
+
--_sonic-skirt: var(--_sonic-lit);
|
|
228
|
+
--_sonic-face: var(--_sonic-lit);
|
|
229
|
+
--_sonic-ink: HighlightText;
|
|
230
|
+
|
|
231
|
+
&:is(:disabled, [aria-disabled='true']) {
|
|
232
|
+
--_sonic-ink: ButtonFace;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
& .sonic-button-cap > svg {
|
|
236
|
+
filter: none;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
package/dist/styles/controls.css
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
@import './material.css';
|
|
2
2
|
@import './dial.css';
|
|
3
|
-
@import './
|
|
3
|
+
@import './envelope.css';
|
|
4
|
+
@import './button.css';
|
|
4
5
|
@import './led.css';
|
|
6
|
+
@import './switch.css';
|
|
5
7
|
@import './meter.css';
|
|
6
8
|
@import './number.css';
|
|
9
|
+
@import './panel.css';
|
|
10
|
+
@import './ring.css';
|
|
7
11
|
@import './screen.css';
|
|
8
12
|
@import './segmented.css';
|
|
9
13
|
@import './slider.css';
|
|
14
|
+
@import './waveform.css';
|
|
15
|
+
@import './wavestrip.css';
|
|
16
|
+
@import './xy.css';
|