2 FAMILY_LABELS,
3 SIGMA_STOPS,
4 TAP_STOPS,
5 WIDTH_STOPS,
6 frequencyStops,
7 maxCutoffHz,
8 nearestStop,
9 type FilterFamily,
10 type FilterSpec,
11} from '../model/filters'
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 12
13export interface ControlsProps {
14 sigma: number
15 setSigma: (v: number) => void
16 sampleRateHz: number
17 setSampleRateHz: (v: number) => void
18 spec: FilterSpec
19 setSpec: (v: FilterSpec) => void
20 dither: boolean
21 setDither: (v: boolean) => void
22}
24function formatHz(hz: number): string {
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 25 return hz >= 1000 ? `${+(hz / 1000).toFixed(1)} kHz` : `${hz} Hz`
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 26}
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 28/** A slider that moves between the given values rather than a continuum. */
29function StopSlider(props: {
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 30 label: string
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 32 value: number
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 33 format: (v: number) => string
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 34 onChange: (v: number) => void
35}) {
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 36 const { stops } = props
37 const snapped = nearestStop(stops, props.value)
38 const index = stops.indexOf(snapped)
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 39 return (
40 <div className="control">
41 <label>
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 42 {props.label} <span className="value">{props.format(snapped)}</span>
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 43 </label>
44 <input
45 type="range"
47 max={stops.length - 1}
48 step={1}
49 value={index}
50 onChange={e => props.onChange(stops[Number(e.target.value)])}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 51 />
52 </div>
53 )
54}
56export default function Controls(p: ControlsProps) {
57 const { spec } = p
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 58 const freqStops = frequencyStops(maxCutoffHz(p.sampleRateHz))
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 59 const sinc = spec.family === 'lowpass' || spec.family === 'bandpass'
60 return (
61 <div className="controls">
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 63 label="σ (quantization steps)"
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 65 value={p.sigma}
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 66 format={v => String(v)}
67 onChange={p.setSigma}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 68 />
69 <div className="control">
70 <label>filter</label>
71 <select
72 value={spec.family}
73 onChange={e => p.setSpec({ ...spec, family: e.target.value as FilterFamily })}
74 >
75 {(Object.keys(FAMILY_LABELS) as FilterFamily[]).map(f => (
76 <option key={f} value={f}>
77 {FAMILY_LABELS[f]}
78 </option>
79 ))}
80 </select>
81 </div>
82 {spec.family === 'bandpass' && (
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 84 label="low cutoff"
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 85 stops={freqStops.filter(f => f < spec.highHz)}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 86 value={spec.lowHz}
88 onChange={v => p.setSpec({ ...spec, lowHz: v })}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 89 />
90 )}
91 {sinc && (
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 93 label={spec.family === 'bandpass' ? 'high cutoff' : 'cutoff'}
fb1a12fSnap every parameter slider to a ladder of round valuesJeremy Magland 94 stops={spec.family === 'bandpass' ? freqStops.filter(f => f > spec.lowHz) : freqStops}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 95 value={spec.highHz}
97 onChange={v => p.setSpec({ ...spec, highHz: v })}
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 98 />
99 )}
100 {sinc && (
102 label="kernel length"
103 stops={TAP_STOPS}
104 value={spec.taps}
105 format={v => `${v} taps`}
106 onChange={v => p.setSpec({ ...spec, taps: v })}
107 />
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 108 )}
109 {spec.family === 'movingAverage' && (
111 label="width"
112 stops={WIDTH_STOPS}
113 value={spec.width}
114 format={v => `${v} samples`}
115 onChange={v => p.setSpec({ ...spec, width: v })}
116 />
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 117 )}
118 <div className="control">
119 <label>sample rate</label>
120 <select value={p.sampleRateHz} onChange={e => p.setSampleRateHz(Number(e.target.value))}>
121 {[1000, 8000, 16000, 30000, 44100, 96000].map(r => (
122 <option key={r} value={r}>
123 {formatHz(r)}
124 </option>
125 ))}
126 </select>
127 </div>
128 <div className="control control-toggle">
129 <input
130 id="dither"
131 type="checkbox"
132 checked={p.dither}
133 onChange={e => p.setDither(e.target.checked)}
134 />
135 <label htmlFor="dither">dither (uniform ±½ before rounding)</label>
136 </div>
137 </div>
138 )
139}