80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import { ENGINE_CONTROLS } from '../config/effects'
|
|
import { getComplexityLabel, getBitDepthLabel, getSampleRateLabel } from '../utils/formatters'
|
|
import type { EffectValues } from '../types/effects'
|
|
import { Knob } from './Knob'
|
|
|
|
interface EngineControlsProps {
|
|
values: EffectValues
|
|
onChange: (parameterId: string, value: number) => void
|
|
onMapClick?: (paramId: string, lfoIndex: number) => void
|
|
getMappedLFOs?: (paramId: string) => number[]
|
|
}
|
|
|
|
const KNOB_PARAMS = ['masterVolume', 'pitch', 'a', 'b', 'c', 'd']
|
|
|
|
export function EngineControls({ values, onChange, onMapClick, getMappedLFOs }: EngineControlsProps) {
|
|
const formatValue = (id: string, value: number): string => {
|
|
switch (id) {
|
|
case 'sampleRate':
|
|
return getSampleRateLabel(value)
|
|
case 'complexity':
|
|
return getComplexityLabel(value)
|
|
case 'bitDepth':
|
|
return getBitDepthLabel(value)
|
|
default: {
|
|
const param = ENGINE_CONTROLS[0].parameters.find(p => p.id === id)
|
|
return `${value}${param?.unit || ''}`
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center gap-6">
|
|
{ENGINE_CONTROLS[0].parameters.map(param => {
|
|
const useKnob = KNOB_PARAMS.includes(param.id)
|
|
|
|
if (useKnob) {
|
|
return (
|
|
<Knob
|
|
key={param.id}
|
|
label={param.label}
|
|
value={(values[param.id] as number) ?? param.default}
|
|
min={param.min as number}
|
|
max={param.max as number}
|
|
step={param.step as number}
|
|
unit={param.unit}
|
|
onChange={(value) => onChange(param.id, value)}
|
|
formatValue={formatValue}
|
|
valueId={param.id}
|
|
paramId={param.id}
|
|
onMapClick={onMapClick}
|
|
mappedLFOs={getMappedLFOs ? getMappedLFOs(param.id) : []}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div key={param.id} className="flex flex-col gap-1 min-w-[100px]">
|
|
<div className="flex justify-between items-baseline">
|
|
<label className="font-mono text-[9px] tracking-[0.15em] text-white">
|
|
{param.label.toUpperCase()}
|
|
</label>
|
|
<span className="font-mono text-[9px] text-white">
|
|
{formatValue(param.id, (values[param.id] as number) ?? param.default)}
|
|
</span>
|
|
</div>
|
|
<input
|
|
type="range"
|
|
min={param.min}
|
|
max={param.max}
|
|
step={param.step}
|
|
value={(values[param.id] as number) ?? param.default}
|
|
onChange={(e) => onChange(param.id, Number(e.target.value))}
|
|
className="w-full h-[2px] bg-white appearance-none cursor-pointer"
|
|
/>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
} |