commit ca9d7fff7d9d41c36a419a2ad363882f090a4b42
parent b939a5926255ccddcfe4157fac3cfc76f54f4354
Author: massi <mdsiboldi@gmail.com>
Date: Tue, 8 Aug 2023 15:41:12 -0700
generic unit control
Diffstat:
12 files changed, 124 insertions(+), 176 deletions(-)
diff --git a/src/lib/ConstUnit.svelte b/src/lib/ConstUnit.svelte
@@ -1,21 +1,22 @@
<script lang="ts">
import Control from '$lib/Control.svelte';
- import { unitStore, unitToConnect } from '$lib/stores';
- import { ensure, getUnit, type UnitId } from '$lib/types';
- import { getControlProps } from './controlUtils';
+ import type { UnitId } from '$lib/types';
export let id: UnitId;
- $: unit = ensure.unit.const(getUnit($unitStore, id));
+ const kind = 'const';
+ const KControl = Control<typeof kind>;
+
+ $: common = { kind, id } as const;
</script>
<div>
- <h1>just a const...</h1>
- <Control {...getControlProps(unit.kind, id, unit.controls.value, 'value', $unitToConnect)} />
+ <h1>const</h1>
+ <KControl {...common} controlName="value" />
</div>
<style>
h1 {
- color: white;
+ color: blue;
}
</style>
diff --git a/src/lib/Control.svelte b/src/lib/Control.svelte
@@ -1,28 +1,61 @@
-<script lang="ts">
- import NumberSelector from '$lib/NumberSelector.svelte';
+<script lang="ts" generics="TKind extends UnitKind">
import { unitStore, unitToConnect } from '$lib/stores';
- import type { NumberRange } from '$lib/types';
+ import {
+ RANGE,
+ getUnit,
+ inp,
+ range,
+ rescale,
+ type ControlName,
+ type Controls,
+ type UnitId,
+ type UnitKind,
+ wrangle
+ } from '$lib/types';
+ import DumbSlider from './DumbSlider.svelte';
import InputDragger from './InputDragger.svelte';
- export let controlName: string;
- export let controlRange: NumberRange;
- export let value: number | null;
- export let connection: { connected: boolean; onConnect: () => void } | null;
- export let updateValue: (n: number) => void;
+ export let id: UnitId;
+ export let kind: TKind;
+ export let controlName: ControlName<TKind>;
+
+ $: unit = getUnit(kind, $unitStore, id);
+
+ const controlRange = RANGE[kind][controlName];
+ $: input = (unit.controls as Controls<TKind>)[controlName];
+ const updateValue = (controlValue: number) => {
+ const n = Math.round(rescale(controlValue, controlRange, range.signal));
+ unitStore.setControl(kind, id, controlName, n);
+ };
+ let connection: { connected: boolean; onConnect: () => void } | null;
+ $: {
+ if ($unitToConnect) {
+ connection = {
+ connected: Array.isArray(input) && inp.connected(input, $unitToConnect),
+ onConnect: () => {
+ if (!$unitToConnect) return;
+ unitStore.setControl(kind, id, controlName, inp.toggle(input, $unitToConnect));
+ }
+ };
+ } else {
+ connection = null;
+ }
+ }
+ let value: number | null;
+ $: value = typeof input === 'number' ? wrangle(input, range.signal, controlRange) : null;
+
+ $: slotProps = { range: controlRange, value: value || 0, update: updateValue };
</script>
<div class="control-wrapper">
<h3>{controlName}</h3>
<InputDragger {connection} />
{#if value !== null}
- <input
- type="range"
- min={controlRange.min}
- max={controlRange.max}
- step={1}
- {value}
- on:input={(e) => updateValue(Number(e.currentTarget?.value))}
- />
+ {#if $$slots.default}
+ <slot props={{ range: controlRange, value: value || 0, update: updateValue }} />
+ {:else}
+ <DumbSlider range={controlRange} value={value || 0} update={updateValue} />
+ {/if}
{/if}
</div>
diff --git a/src/lib/DumbSlider.svelte b/src/lib/DumbSlider.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+ import type { NumberRange } from './types';
+
+ export let range: NumberRange;
+ export let value: number;
+ export let update: (v: number) => void;
+</script>
+
+<input
+ type="range"
+ min={range.min}
+ max={range.max}
+ step={1}
+ {value}
+ on:input={(e) => update(Number(e.currentTarget?.value))}
+/>
diff --git a/src/lib/NoiseUnit.svelte b/src/lib/NoiseUnit.svelte
@@ -1,17 +1,18 @@
<script lang="ts">
import Control from '$lib/Control.svelte';
- import { unitStore, unitToConnect } from '$lib/stores';
- import { ensure, getUnit, type UnitId } from '$lib/types';
- import { getControlProps } from '$lib/controlUtils';
+ import type { UnitId } from '$lib/types';
export let id: UnitId;
- $: unit = ensure.unit.noise(getUnit($unitStore, id));
+ const kind = 'noise';
+ const KControl = Control<typeof kind>;
+
+ $: common = { kind, id } as const;
</script>
<div>
<h1>noiseyboi</h1>
- <Control {...getControlProps(unit.kind, id, unit.controls.amount, 'amount', $unitToConnect)} />
+ <KControl {...common} controlName="amount" />
</div>
<style>
diff --git a/src/lib/NumberSelector.svelte b/src/lib/NumberSelector.svelte
@@ -99,7 +99,6 @@
};
const handleDbl = () => {
- console.log('dbl');
updateValue(0);
};
diff --git a/src/lib/OscUnit.svelte b/src/lib/OscUnit.svelte
@@ -1,20 +1,21 @@
<script lang="ts">
import Control from '$lib/Control.svelte';
- import { getControlProps } from '$lib/controlUtils';
- import { unitStore, unitToConnect } from '$lib/stores';
- import { ensure, getUnit, type UnitId } from '$lib/types';
+ import type { UnitId } from '$lib/types';
export let id: UnitId;
- $: unit = ensure.unit.osc(getUnit($unitStore, id));
+ const kind = 'osc';
+ const KControl = Control<typeof kind>;
+
+ $: common = { kind, id } as const;
</script>
<div>
- <h1>Os-KILL-8r</h1>
- <Control {...getControlProps('osc', id, unit.controls.coarse, 'coarse', $unitToConnect)} />
- <Control {...getControlProps('osc', id, unit.controls.fine, 'fine', $unitToConnect)} />
- <Control {...getControlProps('osc', id, unit.controls.superfine, 'superfine', $unitToConnect)} />
- <Control {...getControlProps('osc', id, unit.controls.amount, 'amount', $unitToConnect)} />
+ <h1>os-kill-8r</h1>
+ <KControl {...common} controlName="coarse" />
+ <KControl {...common} controlName="fine" />
+ <KControl {...common} controlName="superfine" />
+ <KControl {...common} controlName="amount" />
</div>
<style>
diff --git a/src/lib/Slider.svelte b/src/lib/Slider.svelte
@@ -1,53 +0,0 @@
-<script lang="ts">
- import type { WithTarget, UnitId, Units } from '$lib/types';
- import { rescale, range, getUnit as _getUnit } from '$lib/types';
- import NumberSelector from '$lib/NumberSelector.svelte';
-
- export let id: UnitId;
- export let units: Units;
- export let handleInput: (id: string, value: number) => void;
- export let handleChange: () => void;
-
- $: getUnit = _getUnit.bind(null, units);
- $: unit = getUnit(id);
- $: sliderValue =
- unit.kind === 'const' ? Math.round(rescale(unit.value, range.signal, range.slider)) : 0;
-
- const _handleInput = (val: number) => {
- handleInput(id, Math.round(rescale(val, range.slider, range.signal)));
- unit = unit;
- };
-
- const onInput = (e: WithTarget<Event, HTMLInputElement>) => {
- _handleInput(Number(e.currentTarget?.value));
- };
-</script>
-
-<div class="unit-container">
- {#if unit.kind === 'const'}
- <h3>{unit.kind}</h3>
- <NumberSelector value={sliderValue} updateValue={_handleInput} />
- <input
- name={id}
- type="range"
- min={range.slider.min}
- max={range.slider.max}
- step={1}
- value={sliderValue}
- on:input={onInput}
- on:change={handleChange}
- />
- {/if}
-</div>
-
-<style>
- .unit-container {
- background: rgba(255, 255, 255, 0.3);
- padding: 10px;
- display: flexbox;
- }
- h3 {
- padding: 0;
- margin: 0;
- }
-</style>
diff --git a/src/lib/controlUtils.ts b/src/lib/controlUtils.ts
@@ -1,48 +0,0 @@
-import {
- RANGE,
- type ControlName,
- type Controls,
- type UnitKind,
- type UnitToConnect,
- rescale,
- range,
- type UnitId,
- inp,
- wrangle,
- type Input
-} from '$lib/types';
-import { unitStore } from './stores';
-
-export const getControlProps = <K extends UnitKind>(
- kind: K,
- id: UnitId,
- input: Input,
- controlName: ControlName<K>,
- utc: UnitToConnect
-) => {
- const controlRange = RANGE[kind][controlName];
- const updateValue = (controlValue: number) => {
- const n = Math.round(rescale(controlValue, controlRange, range.signal));
- unitStore._setControl(id, controlName, n);
- };
-
- let connection: { connected: boolean; onConnect: () => void } | null = null;
- if (utc) {
- connection = {
- connected: Array.isArray(input) && inp.connected(input, utc),
- onConnect: () => {
- if (!utc) return;
- console.log('connecting', utc, controlName);
- unitStore._setControl(id, controlName, inp.toggle(input, utc));
- }
- };
- }
-
- return {
- controlName,
- controlRange,
- value: typeof input === 'number' ? wrangle(input, range.signal, controlRange) : null,
- connection,
- updateValue
- };
-};
diff --git a/src/lib/engine.worker.ts b/src/lib/engine.worker.ts
@@ -33,7 +33,7 @@ function getUnitState(id: UnitId): UnitState {
if (!config) {
throw new Error("no config, can't get unit state.");
}
- const unit = getUnit(config.units, id);
+ const unit = getUnit(null, config.units, id);
const theState = unitState.get(id);
switch (unit.kind) {
case 'osc': {
@@ -72,7 +72,7 @@ const oscShapes: OscShapes = {
function vUnit(x: { id: UnitId }): number {
const { id } = x;
- const unit: Unit = getUnit(config!.units, id);
+ const unit: Unit = getUnit(null, config!.units, id);
switch (unit.kind) {
case 'osc': {
const position = getUnitState(id);
@@ -124,7 +124,7 @@ function update() {
}
const ids = Object.keys(config.units);
for (let id of ids) {
- const unit = getUnit(config.units, id);
+ const unit = getUnit(null, config.units, id);
switch (unit.kind) {
case 'osc': {
const position = getUnitState(id);
diff --git a/src/lib/stores.ts b/src/lib/stores.ts
@@ -1,16 +1,26 @@
-import type { Controls, Input, Unit, UnitId, UnitKind, UnitToConnect, Units } from '$lib/types';
+import type {
+ ControlName,
+ Controls,
+ Input,
+ Unit,
+ UnitId,
+ UnitKind,
+ UnitToConnect,
+ Units
+} from '$lib/types';
import { writable } from 'svelte/store';
const mkUnitStore = () => {
const { subscribe, set, update } = writable<Units>({});
- const _setControl = (id: UnitId, controlName: string, input: Input) => {
+ const setControl = <K extends UnitKind>(
+ kind: K,
+ id: UnitId,
+ controlName: ControlName<K>,
+ input: Input
+ ) => {
update((units) => {
- // @ts-ignore
- if (units[id].controls[controlName] == null) {
- throw new Error(`invalid control ${controlName} for unit kind ${units[id].kind}`);
- }
- // @ts-ignore
- units[id].controls[controlName] = input;
+ const controls = units[id].controls as Controls<K>;
+ controls[controlName] = input;
return units;
});
};
@@ -20,7 +30,7 @@ const mkUnitStore = () => {
setUnit<K extends UnitKind>(id: UnitId, unit: Unit<K>) {
update((units) => ({ ...units, [id]: unit }));
},
- _setControl
+ setControl
};
};
diff --git a/src/lib/types.ts b/src/lib/types.ts
@@ -92,11 +92,7 @@ export type Input = number | Output[];
export type Units = { [u: UnitId]: Unit };
export type UnitStateMap = Map<UnitId, UnitState>;
export type UnitState = any;
-export type Sinks = {
- l: Input;
- c: Input;
- h: Input;
-};
+export type Sinks = Record<'l' | 'c' | 'h', Input>;
export type NumberRange = {
min: number;
@@ -175,10 +171,17 @@ export function wrangle(n: number, origin: NumberRange, dest: NumberRange) {
return rescale(clamp(n, origin), origin, dest);
}
-export function getUnit(units: Units, id: UnitId): Unit {
- const result = units[id];
- if (!result) throw new Error('invalid id for unit: ' + id);
- return result;
+export function getUnit(kind: null, units: Units, id: UnitId): Unit;
+export function getUnit<K extends UnitKind = UnitKind>(kind: K, units: Units, id: UnitId): Unit<K>;
+export function getUnit<K extends UnitKind = UnitKind>(
+ kind: K | null,
+ units: Units,
+ id: UnitId
+): Unit<UnitKind> {
+ if (kind !== null) {
+ const r = ensure.unit[kind](units[id]);
+ return r as Unit<K>;
+ } else return units[id];
}
const isUnit =
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
@@ -64,7 +64,7 @@
};
});
- const getUnit = (id: UnitId): Unit => _getUnit($unitStore, id);
+ const getUnit = (id: UnitId): Unit => _getUnit(null, $unitStore, id);
let uid = 0;
$: units = $unitStore;
let sinks: Sinks = { l: 0, c: 0, h: 0 };
@@ -72,7 +72,6 @@
// let's update the engine whenever the config changes for now, even though we could ignore pos at least.
$: {
if (engineWorker) {
- console.log('updating the engine');
engineWorker.postMessage({ kind: 'config', content: { units: $unitStore, sinks } });
}
}
@@ -140,14 +139,6 @@
}
};
- const get = {
- constUnit: (id: UnitId): ConstUnit => {
- const unit = getUnit(id);
- if (!is.unit.const(unit)) throw new Error('expecting a const here bro....');
- return unit;
- }
- };
-
let dragging: false | UnitId = false;
const handleMouseMove = (e: MouseEvent) => {
@@ -168,7 +159,6 @@
};
const handleMouseDown = (id: UnitId) => {
- console.log('mouse down', id);
dragging = id;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
@@ -191,14 +181,9 @@
}
sinks[ch] = inp.toggle(sinks[ch], $unitToConnect);
engineWorker && engineWorker.postMessage({ kind: 'config', content: { sinks, units } });
- console.log(sinks);
$unitToConnect = false;
};
- $: {
- console.log(units);
- }
-
$: onSinkConnect = $unitToConnect ? _onSinkConnect : null;
type SinkEntries = [keyof Sinks, Input | null][];