feat: add behaviour to hp/lp filters
This commit is contained in:
@@ -13,8 +13,8 @@
|
|||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "thomann-dsp-app",
|
"title": "thomann-dsp-app",
|
||||||
"width": 1200,
|
"width": 1600,
|
||||||
"height": 800,
|
"height": 950,
|
||||||
"minWidth": 1200,
|
"minWidth": 1200,
|
||||||
"minHeight": 600
|
"minHeight": 600
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type VerticalSliderProps = {
|
|||||||
onChange?: (value: number) => Promise<boolean>;
|
onChange?: (value: number) => Promise<boolean>;
|
||||||
onInput?: (value: number) => void;
|
onInput?: (value: number) => void;
|
||||||
|
|
||||||
unit?: string;
|
unit?: string | ((value: number) => string);
|
||||||
formatValue?: (value: number, decimals: number) => string;
|
formatValue?: (value: number, decimals: number) => string;
|
||||||
|
|
||||||
showTicks?: boolean;
|
showTicks?: boolean;
|
||||||
@@ -198,6 +198,8 @@ export default function VerticalSlider({
|
|||||||
|
|
||||||
const formattedValue = (formatValue ?? formatValueDefault)(displayValue, decimals);
|
const formattedValue = (formatValue ?? formatValueDefault)(displayValue, decimals);
|
||||||
|
|
||||||
|
const displayUnit = typeof unit === 'function' ? unit(displayValue) : unit;
|
||||||
|
|
||||||
function normalizeValue(input: number) {
|
function normalizeValue(input: number) {
|
||||||
const clamped = Math.min(max, Math.max(min, input));
|
const clamped = Math.min(max, Math.max(min, input));
|
||||||
|
|
||||||
@@ -434,7 +436,7 @@ export default function VerticalSlider({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{unit && <span className="value-unit">{unit}</span>}
|
{displayUnit && <span className="value-unit">{' ' + displayUnit}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ function GeqSection({
|
|||||||
}) {
|
}) {
|
||||||
const [activeChannel, setActiveChannel] = useState<InputChannel>(INPUT_ORDER[0]);
|
const [activeChannel, setActiveChannel] = useState<InputChannel>(INPUT_ORDER[0]);
|
||||||
const [liveGeq, setLiveGeq] = useState<Partial<Record<InputChannel, LiveBands>>>({});
|
const [liveGeq, setLiveGeq] = useState<Partial<Record<InputChannel, LiveBands>>>({});
|
||||||
const [resetting, setResetting] = useState(false); // NEW
|
const [resetting, setResetting] = useState(false);
|
||||||
|
|
||||||
const handleLiveValue = (channel: InputChannel, band: DiscreteFrequency, value: number) => {
|
const handleLiveValue = (channel: InputChannel, band: DiscreteFrequency, value: number) => {
|
||||||
setLiveGeq((prev) => ({
|
setLiveGeq((prev) => ({
|
||||||
@@ -231,7 +231,11 @@ function GeqSection({
|
|||||||
return success;
|
return success;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NEW: sequential per-band reset, since hardware has no bulk reset command.
|
const handleBypassClick = () => {
|
||||||
|
setGeqBypass(activeChannel, !activeGeq.bypass);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sequential per-band reset, since hardware has no bulk reset command.
|
||||||
const handleReset = async (channel: InputChannel) => {
|
const handleReset = async (channel: InputChannel) => {
|
||||||
if (resetting) return;
|
if (resetting) return;
|
||||||
setResetting(true);
|
setResetting(true);
|
||||||
@@ -251,10 +255,6 @@ function GeqSection({
|
|||||||
const activeGeq = activeInputState.geq;
|
const activeGeq = activeInputState.geq;
|
||||||
const activeLiveBands = liveGeq[activeChannel];
|
const activeLiveBands = liveGeq[activeChannel];
|
||||||
|
|
||||||
const handleBypassClick = () => {
|
|
||||||
setGeqBypass(activeChannel, !activeGeq.bypass);
|
|
||||||
};
|
|
||||||
|
|
||||||
const channelOptions = INPUT_ORDER.map((channel) => ({
|
const channelOptions = INPUT_ORDER.map((channel) => ({
|
||||||
channel,
|
channel,
|
||||||
label: dsp.state.current_config.input_states[channel].name,
|
label: dsp.state.current_config.input_states[channel].name,
|
||||||
|
|||||||
@@ -228,7 +228,7 @@
|
|||||||
.crossover-panel {
|
.crossover-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
aspect-ratio: 0.28;
|
aspect-ratio: 0.3;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import VerticalSlider, {
|
|||||||
// ASSUMPTION: an InputChannel enum exists alongside OutputChannel, mapping
|
// ASSUMPTION: an InputChannel enum exists alongside OutputChannel, mapping
|
||||||
// input pages 1-8 similarly (In1 -> 0, In2 -> 1, ...). Adjust import/values
|
// input pages 1-8 similarly (In1 -> 0, In2 -> 1, ...). Adjust import/values
|
||||||
// if the actual enum differs.
|
// if the actual enum differs.
|
||||||
import { InputChannel, Channel, PEQBand, PEQFilter, PEQ, Meter } from '../../types';
|
import { InputChannel, Channel, PEQBand, PEQFilter, PEQ, Meter, Crossover } from '../../types';
|
||||||
import { DSP408 as DSP } from '../../../../types';
|
import { DSP408 as DSP } from '../../../../types';
|
||||||
import { CrossoverFilter } from '../../types';
|
import { CrossoverFilter } from '../../types';
|
||||||
import { LinearGraph } from '../../../../components/LinearGraph/LinearGraph';
|
import { LinearGraph } from '../../../../components/LinearGraph/LinearGraph';
|
||||||
@@ -15,6 +15,15 @@ import './InputSection.css';
|
|||||||
import ChannelGroup from '../../../../components/ChannelGroup/ChannelGroup';
|
import ChannelGroup from '../../../../components/ChannelGroup/ChannelGroup';
|
||||||
import Button from '../../../../components/Button/Button';
|
import Button from '../../../../components/Button/Button';
|
||||||
import MeterBar from '../../../../components/MeterBar/MeterBar';
|
import MeterBar from '../../../../components/MeterBar/MeterBar';
|
||||||
|
import {
|
||||||
|
FREQUENCY_MAX,
|
||||||
|
FREQUENCY_MIN,
|
||||||
|
FREQUENCY_QUANTIZATION,
|
||||||
|
GRAPH_X_MAX,
|
||||||
|
GRAPH_X_MIN,
|
||||||
|
GRAPH_Y_MAX,
|
||||||
|
GRAPH_Y_MIN,
|
||||||
|
} from '../../constants';
|
||||||
|
|
||||||
const PEQ_BANDS: PEQBand[] = [
|
const PEQ_BANDS: PEQBand[] = [
|
||||||
PEQBand.B1,
|
PEQBand.B1,
|
||||||
@@ -67,7 +76,7 @@ type SetPEQFn = (
|
|||||||
|
|
||||||
type PeqParamKey = 'frequency' | 'q' | 'gain';
|
type PeqParamKey = 'frequency' | 'q' | 'gain';
|
||||||
|
|
||||||
type PEQValues = {
|
type PEQValue = {
|
||||||
frequency: number;
|
frequency: number;
|
||||||
q: number;
|
q: number;
|
||||||
gain: number;
|
gain: number;
|
||||||
@@ -91,7 +100,7 @@ function PeqPanelSliders({
|
|||||||
const inputState = dsp.state.current_config.input_states[channel];
|
const inputState = dsp.state.current_config.input_states[channel];
|
||||||
const peq = inputState.peq_chain.bands[band] as PEQ;
|
const peq = inputState.peq_chain.bands[band] as PEQ;
|
||||||
|
|
||||||
const commit = (overrides: Partial<PEQValues>) => {
|
const commit = (overrides: Partial<PEQValue>) => {
|
||||||
const next = { ...peq, ...overrides };
|
const next = { ...peq, ...overrides };
|
||||||
return setPEQ(
|
return setPEQ(
|
||||||
{ Input: channel },
|
{ Input: channel },
|
||||||
@@ -118,7 +127,7 @@ function PeqPanelSliders({
|
|||||||
unit={param.unit}
|
unit={param.unit}
|
||||||
value={peq[param.key]}
|
value={peq[param.key]}
|
||||||
// onInput={(value) => onLiveValue(channel, param.key, value)}
|
// onInput={(value) => onLiveValue(channel, param.key, value)}
|
||||||
onChange={(value) => commit({ [param.key]: value } as Partial<PEQValues>)}
|
onChange={(value) => commit({ [param.key]: value } as Partial<PEQValue>)}
|
||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
style={{ width: '100%', flex: '1 1 auto' }}
|
||||||
/>
|
/>
|
||||||
<div className="input-panel__cell-label">{param.label}</div>
|
<div className="input-panel__cell-label">{param.label}</div>
|
||||||
@@ -129,45 +138,49 @@ function PeqPanelSliders({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FREQ_MIN = 19.7;
|
|
||||||
const FREQ_MAX = 20160;
|
|
||||||
const FREQ_POINTS = 300;
|
|
||||||
const FREQ_STEP = makeLogStep(FREQ_MIN, FREQ_MAX, FREQ_POINTS);
|
|
||||||
const FREQ_QUANTIZE = makeLogQuantize(FREQ_MIN, FREQ_MAX, FREQ_POINTS);
|
|
||||||
|
|
||||||
function CrossoverPanel({
|
function CrossoverPanel({
|
||||||
channel,
|
crossover,
|
||||||
dsp,
|
|
||||||
title,
|
title,
|
||||||
setCrossover,
|
setCrossover,
|
||||||
|
onLiveValue,
|
||||||
}: {
|
}: {
|
||||||
channel: Channel;
|
crossover: Crossover;
|
||||||
dsp: DSP;
|
|
||||||
title: string;
|
title: string;
|
||||||
setCrossover: (
|
setCrossover: (frequency: number, filter_type: CrossoverFilter) => Promise<boolean>;
|
||||||
channel: Channel,
|
onLiveValue: (value: number) => void;
|
||||||
frequency: number,
|
|
||||||
filter_type: CrossoverFilter
|
|
||||||
) => Promise<boolean>;
|
|
||||||
}) {
|
}) {
|
||||||
const channelState =
|
const frequency = crossover.frequency;
|
||||||
'Input' in channel
|
const filter_type = crossover.slope;
|
||||||
? dsp.state?.current_config.input_states[channel.Input]
|
const bypass = filter_type === CrossoverFilter.Bypass;
|
||||||
: dsp.state?.current_config.output_states[channel.Output];
|
|
||||||
|
|
||||||
const crossover = channelState?.crossover;
|
const lastFilterType = useRef<CrossoverFilter>(
|
||||||
|
filter_type !== CrossoverFilter.Bypass ? filter_type : CrossoverFilter.Lk24
|
||||||
|
);
|
||||||
|
|
||||||
const frequency = crossover.high_pass.frequency;
|
const commit = async (overrides: {
|
||||||
const filter_type = crossover.high_pass.slope;
|
|
||||||
const bypass = false;
|
|
||||||
|
|
||||||
const commit = (overrides: {
|
|
||||||
frequency?: number;
|
frequency?: number;
|
||||||
filter_type?: CrossoverFilter;
|
filter_type?: CrossoverFilter;
|
||||||
bypass?: boolean;
|
bypass?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const next = { frequency, filter_type, bypass, ...overrides };
|
const nextFrequency = overrides.frequency ?? frequency;
|
||||||
return setCrossover(channel, next.frequency, next.filter_type);
|
|
||||||
|
const selectedFilterType =
|
||||||
|
overrides.filter_type !== undefined
|
||||||
|
? overrides.filter_type
|
||||||
|
: bypass
|
||||||
|
? lastFilterType.current
|
||||||
|
: filter_type;
|
||||||
|
|
||||||
|
// Always remember the latest non-bypass selection.
|
||||||
|
if (selectedFilterType !== CrossoverFilter.Bypass) {
|
||||||
|
lastFilterType.current = selectedFilterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextBypass = overrides.bypass ?? bypass;
|
||||||
|
|
||||||
|
const nextFilterType = nextBypass ? CrossoverFilter.Bypass : selectedFilterType;
|
||||||
|
|
||||||
|
return setCrossover(nextFrequency, nextFilterType);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -175,20 +188,24 @@ function CrossoverPanel({
|
|||||||
<div className="crossover-panel__title">{title}</div>
|
<div className="crossover-panel__title">{title}</div>
|
||||||
|
|
||||||
<VerticalSlider
|
<VerticalSlider
|
||||||
min={FREQ_MIN}
|
min={FREQUENCY_MIN}
|
||||||
max={FREQ_MAX}
|
max={FREQUENCY_MAX}
|
||||||
scale="log"
|
scale="log"
|
||||||
step={FREQ_STEP}
|
step={makeLogStep(FREQUENCY_MIN, FREQUENCY_MAX, FREQUENCY_QUANTIZATION)}
|
||||||
quantize={FREQ_QUANTIZE}
|
quantize={makeLogQuantize(FREQUENCY_MIN, FREQUENCY_MAX, FREQUENCY_QUANTIZATION)}
|
||||||
unit=" Hz"
|
unit={(value) => (value >= 1000 ? 'kHz' : 'Hz')}
|
||||||
value={frequency}
|
value={frequency}
|
||||||
|
formatValue={(value) =>
|
||||||
|
value >= 1000 ? `${(value / 1000).toFixed(2)}` : `${Math.round(value)}`
|
||||||
|
}
|
||||||
onChange={(value) => commit({ frequency: value })}
|
onChange={(value) => commit({ frequency: value })}
|
||||||
|
onInput={(value) => onLiveValue(value)}
|
||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
style={{ width: '100%', flex: '1 1 auto' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
className="crossover-panel__select"
|
className="crossover-panel__select"
|
||||||
value={filter_type}
|
value={bypass ? lastFilterType.current : filter_type}
|
||||||
onChange={(e) => commit({ filter_type: e.target.value as CrossoverFilter })}
|
onChange={(e) => commit({ filter_type: e.target.value as CrossoverFilter })}
|
||||||
>
|
>
|
||||||
{FILTER_TYPE_OPTIONS.map((opt) => (
|
{FILTER_TYPE_OPTIONS.map((opt) => (
|
||||||
@@ -233,7 +250,7 @@ function PeqBandList({
|
|||||||
}) {
|
}) {
|
||||||
const inputState = dsp.state.current_config.input_states[channel];
|
const inputState = dsp.state.current_config.input_states[channel];
|
||||||
|
|
||||||
const commit = (band: PEQBand, overrides: Partial<PEQValues>) => {
|
const commit = (band: PEQBand, overrides: Partial<PEQValue>) => {
|
||||||
const peq = inputState.peq_chain.bands[band] as PEQ;
|
const peq = inputState.peq_chain.bands[band] as PEQ;
|
||||||
const next = { ...peq, ...overrides };
|
const next = { ...peq, ...overrides };
|
||||||
return setPEQ(
|
return setPEQ(
|
||||||
@@ -364,76 +381,88 @@ function InputSection({
|
|||||||
setPeq: setPeqBandFn;
|
setPeq: setPeqBandFn;
|
||||||
setPeqBypass: (channel: Channel, bypass: boolean) => Promise<boolean>;
|
setPeqBypass: (channel: Channel, bypass: boolean) => Promise<boolean>;
|
||||||
}) {
|
}) {
|
||||||
|
// UI variables
|
||||||
const [activeBand, setActiveBand] = useState<PEQBand>(PEQBand.B1);
|
const [activeBand, setActiveBand] = useState<PEQBand>(PEQBand.B1);
|
||||||
|
|
||||||
const [liveHpFreq, setLiveHpFreq] = useState<number>(19.7);
|
// Live values
|
||||||
const [liveLpFreq, setLiveLpFreq] = useState<number>(20.16);
|
const [liveHpFreq, setLiveHpFreq] = useState<number>(FREQUENCY_MIN);
|
||||||
const [livePeq, setLivePeq] = useState<Partial<Record<PEQBand, PEQValues>>>({});
|
const [liveLpFreq, setLiveLpFreq] = useState<number>(FREQUENCY_MAX);
|
||||||
|
const [livePeq, setLivePeq] = useState<Partial<Record<PEQBand, PEQValue>>>({});
|
||||||
|
|
||||||
const inputState = dsp.state.current_config.input_states[channel];
|
// DSP values
|
||||||
|
const activeInputState = dsp.state.current_config.input_states[channel];
|
||||||
|
const ActiveHighPassFilter = activeInputState.crossover.high_pass;
|
||||||
|
const ActiveLowPassFilter = activeInputState.crossover.low_pass;
|
||||||
|
const ActivePeq = activeInputState.peq_chain;
|
||||||
|
|
||||||
const hp = inputState.crossover.high_pass;
|
// Live handlers
|
||||||
const lp = inputState.crossover.low_pass;
|
const handleLiveHighPassValue = (value: number) => {
|
||||||
const activePeq = livePeq[activeBand] ?? (inputState.peq_chain.bands[activeBand] as PEQValues);
|
setLiveHpFreq(value);
|
||||||
|
};
|
||||||
|
|
||||||
const bypassed = hp.slope === CrossoverFilter.Bypass;
|
const handleLiveLowPassValue = (value: number) => {
|
||||||
const activeFilterType = bypassed && lp.slope !== CrossoverFilter.Bypass ? lp.slope : hp.slope;
|
setLiveLpFreq(value);
|
||||||
|
};
|
||||||
|
|
||||||
// Remembers the last non-bypass slope so the Bypass button can restore it.
|
const handleLivePEQValue = (band: PEQBand, value: PEQValue) => {
|
||||||
// const lastNonBypassType = useRef<CrossoverFilter>(
|
setLivePeq((prev) => ({
|
||||||
// activeFilterType === CrossoverFilter.Bypass ? DEFAULT_FILTER_TYPE : activeFilterType
|
...prev,
|
||||||
// );
|
[band]: value,
|
||||||
// if (activeFilterType !== CrossoverFilter.Bypass) {
|
}));
|
||||||
// lastNonBypassType.current = activeFilterType;
|
};
|
||||||
// }
|
|
||||||
|
|
||||||
const hpFreq = liveHpFreq ?? hp.frequency;
|
// Real dsp value handlers
|
||||||
const lpFreq = liveLpFreq ?? lp.frequency;
|
const commitHighPass = async (freq: number, filterType: CrossoverFilter) => {
|
||||||
|
const success = await setHighPass({ Input: channel }, freq, filterType);
|
||||||
|
if (success) {
|
||||||
|
setLiveHpFreq(freq);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
};
|
||||||
|
|
||||||
// const commitHighPass = async (freq: number, filterType: CrossoverFilter) => {
|
const commitLowPass = async (freq: number, filterType: CrossoverFilter) => {
|
||||||
// const success = await set_high_pass({ Input: channel }, freq, filterType);
|
const success = await setLowPass({ Input: channel }, freq, filterType);
|
||||||
// if (success) setLiveHpFreq(null);
|
if (success) {
|
||||||
// return success;
|
setLiveLpFreq(freq);
|
||||||
// };
|
}
|
||||||
|
return success;
|
||||||
|
};
|
||||||
|
|
||||||
// const commitLowPass = async (freq: number, filterType: CrossoverFilter) => {
|
const commitPEQ = async (
|
||||||
// const success = await set_low_pass({ Input: channel }, freq, filterType);
|
band: PEQBand,
|
||||||
// if (success) setLiveLpFreq(null);
|
gain: number,
|
||||||
// return success;
|
frequency: number,
|
||||||
// };
|
q: number,
|
||||||
|
filter_type: PEQFilter,
|
||||||
// const handleFilterTypeChange = (filterType: CrossoverFilter) => {
|
bypass: boolean
|
||||||
// // Applying a slope change always takes the crossover out of bypass.
|
) => {
|
||||||
// void commitHighPass(hpFreq, filterType);
|
const success = await setPeq({ Input: channel }, band, gain, frequency, q, filter_type, bypass);
|
||||||
// void commitLowPass(lpFreq, filterType);
|
if (success) {
|
||||||
// };
|
setLivePeq((prev) => {
|
||||||
|
const channelLive = prev[band];
|
||||||
// const handleBypassToggle = () => {
|
if (!channelLive || !(band in channelLive)) return prev;
|
||||||
// if (bypassed) {
|
const next = { ...channelLive };
|
||||||
// const restore = lastNonBypassType.current;
|
return { ...prev, [channel]: next };
|
||||||
// void commitHighPass(hpFreq, restore);
|
});
|
||||||
// void commitLowPass(lpFreq, restore);
|
}
|
||||||
// } else {
|
return success;
|
||||||
// void commitHighPass(hpFreq, CrossoverFilter.Bypass);
|
};
|
||||||
// void commitLowPass(lpFreq, CrossoverFilter.Bypass);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="input-graph-section">
|
<div className="input-graph-section">
|
||||||
<div className="input-graph-panel">
|
<div className="input-graph-panel">
|
||||||
<div className="input-graph__title">{inputState.name} - Filters</div>
|
<div className="input-graph__title">{activeInputState.name} - Filters</div>
|
||||||
|
|
||||||
{/* TODO: draw HP/LP transfer curves here once filter math is defined,
|
|
||||||
same pattern as LimiterSection's `draw` prop. */}
|
|
||||||
<LinearGraph
|
<LinearGraph
|
||||||
xMin={20}
|
xMin={GRAPH_X_MIN}
|
||||||
xMax={20000}
|
xMax={GRAPH_X_MAX}
|
||||||
yMin={-30}
|
xScale="log"
|
||||||
yMax={6}
|
yMin={GRAPH_Y_MIN}
|
||||||
|
yMax={GRAPH_Y_MAX}
|
||||||
|
yStep={3}
|
||||||
xLabel="Hz"
|
xLabel="Hz"
|
||||||
yLabel="dB"
|
yLabel="dB"
|
||||||
|
showGrid={true}
|
||||||
formatX={(v) => Math.round(v).toString()}
|
formatX={(v) => Math.round(v).toString()}
|
||||||
formatY={(v) => Math.round(v).toString()}
|
formatY={(v) => Math.round(v).toString()}
|
||||||
draw={() => {}}
|
draw={() => {}}
|
||||||
@@ -456,76 +485,18 @@ function InputSection({
|
|||||||
|
|
||||||
<ChannelGroup title="CrossOver">
|
<ChannelGroup title="CrossOver">
|
||||||
<CrossoverPanel
|
<CrossoverPanel
|
||||||
channel={{ Input: channel }}
|
crossover={ActiveLowPassFilter}
|
||||||
dsp={dsp}
|
|
||||||
title="LowPass"
|
title="LowPass"
|
||||||
setCrossover={setHighPass}
|
setCrossover={commitLowPass}
|
||||||
|
onLiveValue={handleLiveLowPassValue}
|
||||||
/>
|
/>
|
||||||
<CrossoverPanel
|
<CrossoverPanel
|
||||||
channel={{ Input: channel }}
|
crossover={ActiveHighPassFilter}
|
||||||
dsp={dsp}
|
|
||||||
title="HighPass"
|
title="HighPass"
|
||||||
setCrossover={setHighPass}
|
setCrossover={commitHighPass}
|
||||||
|
onLiveValue={handleLiveHighPassValue}
|
||||||
/>
|
/>
|
||||||
</ChannelGroup>
|
</ChannelGroup>
|
||||||
{/* <div className="input-control-panel__sliders">
|
|
||||||
<div className="input-control-panel__cell input-control-panel__cell--high-pass">
|
|
||||||
<VerticalSlider
|
|
||||||
min={FREQ_MIN}
|
|
||||||
max={FREQ_MAX}
|
|
||||||
step={FREQ_STEP}
|
|
||||||
unit=" Hz"
|
|
||||||
value={hpFreq}
|
|
||||||
// disabled={bypassed}
|
|
||||||
onInput={(value) => setLiveHpFreq(value)}
|
|
||||||
onChange={(value) => commitHighPass(value, activeFilterType)}
|
|
||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
|
||||||
/>
|
|
||||||
<div className="input-control-panel__cell-label">High Pass</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="input-control-panel__cell input-control-panel__cell--low-pass">
|
|
||||||
<VerticalSlider
|
|
||||||
min={FREQ_MIN}
|
|
||||||
max={FREQ_MAX}
|
|
||||||
step={FREQ_STEP}
|
|
||||||
unit=" Hz"
|
|
||||||
value={lpFreq}
|
|
||||||
// disabled={bypassed}
|
|
||||||
onInput={(value) => setLiveLpFreq(value)}
|
|
||||||
onChange={(value) => commitLowPass(value, activeFilterType)}
|
|
||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
|
||||||
/>
|
|
||||||
<div className="input-control-panel__cell-label">Low Pass</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="input-control-panel__side">
|
|
||||||
<label className="input-control-panel__select">
|
|
||||||
<span className="input-control-panel__select-label">Filter Type</span>
|
|
||||||
<select
|
|
||||||
value={activeFilterType}
|
|
||||||
disabled={bypassed}
|
|
||||||
onChange={(e) => handleFilterTypeChange(e.target.value as CrossoverFilter)}
|
|
||||||
>
|
|
||||||
{FILTER_TYPE_OPTIONS.map((opt) => (
|
|
||||||
<option key={opt.value} value={opt.value}>
|
|
||||||
{opt.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`input-control-panel__bypass-btn${
|
|
||||||
bypassed ? ' input-control-panel__bypass-btn--active' : ''
|
|
||||||
}`}
|
|
||||||
onClick={handleBypassToggle}
|
|
||||||
>
|
|
||||||
{bypassed ? 'Bypassed' : 'Bypass'}
|
|
||||||
</button>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ export const GEQ_GAIN_MIN = -12;
|
|||||||
export const GEQ_GAIN_MAX = 12;
|
export const GEQ_GAIN_MAX = 12;
|
||||||
export const GEQ_GAIN_STEP = 0.1;
|
export const GEQ_GAIN_STEP = 0.1;
|
||||||
|
|
||||||
|
// ------ Filter constants ------
|
||||||
|
export const FREQUENCY_MIN = 19.7;
|
||||||
|
export const FREQUENCY_MAX = 20160;
|
||||||
|
export const FREQUENCY_QUANTIZATION = 300;
|
||||||
|
|
||||||
// ------ Matrix constants ------
|
// ------ Matrix constants ------
|
||||||
export const MATRIX_GAIN_MIN = -60;
|
export const MATRIX_GAIN_MIN = -60;
|
||||||
export const MATRIX_GAIN_MAX = 0;
|
export const MATRIX_GAIN_MAX = 0;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
PEQFilter,
|
PEQFilter,
|
||||||
PEQBand,
|
PEQBand,
|
||||||
InputSource,
|
InputSource,
|
||||||
|
Crossover,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { SetDsps, NotifyFn, updateDSP, setPollingPaused } from '../../dsp/dspShared';
|
import { SetDsps, NotifyFn, updateDSP, setPollingPaused } from '../../dsp/dspShared';
|
||||||
import { registerPollStopper } from '../../dsp/pollRegistry';
|
import { registerPollStopper } from '../../dsp/pollRegistry';
|
||||||
@@ -1144,9 +1145,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
if (!d.state) return d;
|
if (!d.state) return d;
|
||||||
|
|
||||||
const cfg = d.state.current_config;
|
const cfg = d.state.current_config;
|
||||||
const lowPass = {
|
|
||||||
|
const lowPass: Crossover = {
|
||||||
frequency: freq,
|
frequency: freq,
|
||||||
filter_type: filterType,
|
slope: filterType,
|
||||||
};
|
};
|
||||||
|
|
||||||
if ('Input' in channel) {
|
if ('Input' in channel) {
|
||||||
@@ -1160,7 +1162,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
...cfg.input_states,
|
...cfg.input_states,
|
||||||
[channel.Input]: {
|
[channel.Input]: {
|
||||||
...cfg.input_states[channel.Input],
|
...cfg.input_states[channel.Input],
|
||||||
low_pass: lowPass,
|
crossover: {
|
||||||
|
...cfg.input_states[channel.Input].crossover,
|
||||||
|
low_pass: lowPass,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1178,7 +1183,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
...cfg.output_states,
|
...cfg.output_states,
|
||||||
[channel.Output]: {
|
[channel.Output]: {
|
||||||
...cfg.output_states[channel.Output],
|
...cfg.output_states[channel.Output],
|
||||||
low_pass: lowPass,
|
crossover: {
|
||||||
|
...cfg.output_states[channel.Output].crossover,
|
||||||
|
low_pass: lowPass,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1227,9 +1235,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
if (!d.state) return d;
|
if (!d.state) return d;
|
||||||
|
|
||||||
const cfg = d.state.current_config;
|
const cfg = d.state.current_config;
|
||||||
const highPass = {
|
|
||||||
|
const highPass: Crossover = {
|
||||||
frequency: freq,
|
frequency: freq,
|
||||||
filter_type: filterType,
|
slope: filterType,
|
||||||
};
|
};
|
||||||
|
|
||||||
if ('Input' in channel) {
|
if ('Input' in channel) {
|
||||||
@@ -1243,7 +1252,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
...cfg.input_states,
|
...cfg.input_states,
|
||||||
[channel.Input]: {
|
[channel.Input]: {
|
||||||
...cfg.input_states[channel.Input],
|
...cfg.input_states[channel.Input],
|
||||||
high_pass: highPass,
|
crossover: {
|
||||||
|
...cfg.input_states[channel.Input].crossover,
|
||||||
|
high_pass: highPass,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1261,7 +1273,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
|
|||||||
...cfg.output_states,
|
...cfg.output_states,
|
||||||
[channel.Output]: {
|
[channel.Output]: {
|
||||||
...cfg.output_states[channel.Output],
|
...cfg.output_states[channel.Output],
|
||||||
high_pass: highPass,
|
crossover: {
|
||||||
|
...cfg.output_states[channel.Output].crossover,
|
||||||
|
high_pass: highPass,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user