feat: dont moove slider cursor if tcp command fails
This commit is contained in:
14
src/App.tsx
14
src/App.tsx
@ -18,14 +18,7 @@ function App() {
|
|||||||
const activeDsp = dsps.find((d) => d.id === selected) ?? null;
|
const activeDsp = dsps.find((d) => d.id === selected) ?? null;
|
||||||
const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length;
|
const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length;
|
||||||
|
|
||||||
const dsp408 = useDSP408(
|
const dsp408 = useDSP408(dsps, setDsps, selected, setSelected, setPage, notify);
|
||||||
dsps,
|
|
||||||
setDsps,
|
|
||||||
selected,
|
|
||||||
setSelected,
|
|
||||||
setPage,
|
|
||||||
notify
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
@ -60,10 +53,7 @@ function App() {
|
|||||||
) : page === 'add-dsp' ? (
|
) : page === 'add-dsp' ? (
|
||||||
<AddDSPForm onCancel={() => setPage('home')} onAdd={dsp408.addDSP} />
|
<AddDSPForm onCancel={() => setPage('home')} onAdd={dsp408.addDSP} />
|
||||||
) : activeDsp ? (
|
) : activeDsp ? (
|
||||||
<DSPPage
|
<DSPPage dsp={activeDsp} dsp408={dsp408.forDSP(activeDsp)} />
|
||||||
dsp={activeDsp}
|
|
||||||
dsp408={dsp408.forDSP(activeDsp)}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<HomePage
|
<HomePage
|
||||||
onAddDSP={() => setPage('add-dsp')}
|
onAddDSP={() => setPage('add-dsp')}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import VerticalSlider from '../Slider/VerticalSlider';
|
import VerticalSlider from '../Slider/VerticalSlider';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
|
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
|
||||||
import { DSP } from '../../../types/types';
|
import { DSP } from '../../../types/types';
|
||||||
import { NotificationType } from '../../../types/types';
|
|
||||||
import Button from '../../Button/Button';
|
import Button from '../../Button/Button';
|
||||||
import ChannelGroup from '../ChannelGroup/ChannelGroup';
|
import ChannelGroup from '../ChannelGroup/ChannelGroup';
|
||||||
import { LinearGraph } from '../LinearGraph/LinearGraph';
|
import { LinearGraph } from '../LinearGraph/LinearGraph';
|
||||||
@ -12,109 +10,62 @@ import './GainSection.css';
|
|||||||
const PANEL_DESIGN_WIDTH = 90;
|
const PANEL_DESIGN_WIDTH = 90;
|
||||||
const PANEL_DESIGN_HEIGHT = 400;
|
const PANEL_DESIGN_HEIGHT = 400;
|
||||||
|
|
||||||
function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
|
// function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
// const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const [scale, setScale] = useState(1);
|
// const [scale, setScale] = useState(1);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const el = wrapperRef.current;
|
// const el = wrapperRef.current;
|
||||||
if (!el) return;
|
// if (!el) return;
|
||||||
const observer = new ResizeObserver((entries) => {
|
// const observer = new ResizeObserver((entries) => {
|
||||||
const { height } = entries[0].contentRect;
|
// const { height } = entries[0].contentRect;
|
||||||
if (height === 0) return;
|
// if (height === 0) return;
|
||||||
setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
|
// setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
|
||||||
});
|
// });
|
||||||
observer.observe(el);
|
// observer.observe(el);
|
||||||
return () => observer.disconnect();
|
// return () => observer.disconnect();
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
|
// <div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
|
||||||
<ChannelGroup title={title}>{children}</ChannelGroup>
|
// <ChannelGroup title={title}>{children}</ChannelGroup>
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
function GainPanel({
|
function GainPanel({
|
||||||
dsp,
|
dsp,
|
||||||
notify,
|
|
||||||
channel,
|
channel,
|
||||||
title,
|
title,
|
||||||
|
setGain,
|
||||||
|
setMute,
|
||||||
|
setInverse,
|
||||||
}: {
|
}: {
|
||||||
dsp: DSP;
|
dsp: DSP;
|
||||||
notify: (message: string, type?: NotificationType, dspName?: string) => void;
|
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
title: string;
|
title: string;
|
||||||
|
setGain: (channel: Channel, gain: number) => Promise<boolean>;
|
||||||
|
setMute: (channel: Channel, muted: boolean) => Promise<boolean>;
|
||||||
|
setInverse: (channel: Channel, inverted: boolean) => Promise<boolean>;
|
||||||
}) {
|
}) {
|
||||||
const setGain = async (value: number) => {
|
const channelState =
|
||||||
try {
|
'Input' in channel
|
||||||
const success = await invoke<boolean>('set_channel_gain', {
|
? dsp.state?.current_config.input_states[channel.Input]
|
||||||
id: dsp.id,
|
: dsp.state?.current_config.output_states[channel.Output];
|
||||||
channel,
|
|
||||||
db: value,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
const muted = channelState?.mute ?? false;
|
||||||
notify(`Input A gain set to ${value} dB`, 'success', dsp.name);
|
const inverted = channelState?.phase_inverted ?? false;
|
||||||
} else {
|
const gain = channelState?.gain ?? 0;
|
||||||
notify('Failed to set input gain', 'error', dsp.name);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Gain update failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setMute = async (muted: boolean) => {
|
|
||||||
try {
|
|
||||||
const success = await invoke<boolean>('set_mute', {
|
|
||||||
id: dsp.id,
|
|
||||||
channel,
|
|
||||||
muted,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
notify(`Input A ${muted ? 'muted' : 'unmuted'}`, 'success', dsp.name);
|
|
||||||
} else {
|
|
||||||
notify('Failed to change mute state', 'error', dsp.name);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Mute update failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setInverse = async (inverted: boolean) => {
|
|
||||||
try {
|
|
||||||
const success = await invoke<boolean>('set_channel_inverse_gain', {
|
|
||||||
id: dsp.id,
|
|
||||||
channel,
|
|
||||||
inverted,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
notify(`Input A phase ${inverted ? 'inverted' : 'normal'}`, 'success', dsp.name);
|
|
||||||
} else {
|
|
||||||
notify('Failed to change phase', 'error', dsp.name);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Phase update failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const [muted, setMuted] = useState(false);
|
|
||||||
const [inverted, setInverted] = useState(false);
|
|
||||||
const viewportRef = useRef<HTMLDivElement>(null);
|
const viewportRef = useRef<HTMLDivElement>(null);
|
||||||
const [scale, setScale] = useState(1);
|
const [scale, setScale] = useState(1);
|
||||||
|
|
||||||
const toggleMute = async () => {
|
const toggleMute = async () => {
|
||||||
const next = !muted;
|
await setMute(channel, !muted);
|
||||||
setMuted(next);
|
|
||||||
await setMute(next);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleInverse = async () => {
|
const toggleInverse = async () => {
|
||||||
const next = !inverted;
|
await setInverse(channel, !inverted);
|
||||||
setInverted(next);
|
|
||||||
await setInverse(next);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -148,7 +99,8 @@ function GainPanel({
|
|||||||
switchStepValue={-10}
|
switchStepValue={-10}
|
||||||
switchStep={0.1}
|
switchStep={0.1}
|
||||||
unit=" dB"
|
unit=" dB"
|
||||||
onChange={setGain}
|
value={gain}
|
||||||
|
onChange={(value) => setGain(channel, value)}
|
||||||
style={{ width: 70, height: 260, flex: '0 0 auto' }} // pin to design size, disable its own auto-shrink
|
style={{ width: 70, height: 260, flex: '0 0 auto' }} // pin to design size, disable its own auto-shrink
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -169,10 +121,14 @@ function GainPanel({
|
|||||||
|
|
||||||
function GainSection({
|
function GainSection({
|
||||||
dsp,
|
dsp,
|
||||||
notify,
|
setGain,
|
||||||
|
setMute,
|
||||||
|
setInverse,
|
||||||
}: {
|
}: {
|
||||||
dsp: DSP;
|
dsp: DSP;
|
||||||
notify: (message: string, type?: NotificationType, dspName?: string) => void;
|
setGain: (channel: Channel, gain: number) => Promise<boolean>;
|
||||||
|
setMute: (channel: Channel, muted: boolean) => Promise<boolean>;
|
||||||
|
setInverse: (channel: Channel, inverted: boolean) => Promise<boolean>;
|
||||||
}) {
|
}) {
|
||||||
const inputs: { channel: Channel; title: string }[] = [
|
const inputs: { channel: Channel; title: string }[] = [
|
||||||
{ channel: { Input: InputChannel.InA }, title: 'In A' },
|
{ channel: { Input: InputChannel.InA }, title: 'In A' },
|
||||||
@ -223,13 +179,29 @@ function GainSection({
|
|||||||
<div className="gain-panels">
|
<div className="gain-panels">
|
||||||
<ChannelGroup title="Input">
|
<ChannelGroup title="Input">
|
||||||
{inputs.map(({ channel, title }) => (
|
{inputs.map(({ channel, title }) => (
|
||||||
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
|
<GainPanel
|
||||||
|
key={title}
|
||||||
|
dsp={dsp}
|
||||||
|
channel={channel}
|
||||||
|
title={title}
|
||||||
|
setGain={setGain}
|
||||||
|
setMute={setMute}
|
||||||
|
setInverse={setInverse}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ChannelGroup>
|
</ChannelGroup>
|
||||||
|
|
||||||
<ChannelGroup title="Output">
|
<ChannelGroup title="Output">
|
||||||
{outputs.map(({ channel, title }) => (
|
{outputs.map(({ channel, title }) => (
|
||||||
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
|
<GainPanel
|
||||||
|
key={title}
|
||||||
|
dsp={dsp}
|
||||||
|
channel={channel}
|
||||||
|
title={title}
|
||||||
|
setGain={setGain}
|
||||||
|
setMute={setMute}
|
||||||
|
setInverse={setInverse}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ChannelGroup>
|
</ChannelGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
import VerticalSlider from '../Slider/VerticalSlider';
|
// import VerticalSlider from '../Slider/VerticalSlider';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
// import { invoke } from '@tauri-apps/api/core';
|
||||||
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
|
// import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
|
||||||
import { DSP } from '../../../types/types';
|
// import { DSP } from '../../../types/types';
|
||||||
import { NotificationType } from '../../../types/types';
|
// import { NotificationType } from '../../../types/types';
|
||||||
|
|
||||||
import ChannelGroup from '../ChannelGroup/ChannelGroup';
|
// import ChannelGroup from '../ChannelGroup/ChannelGroup';
|
||||||
import './GateSection.css';
|
// import './GateSection.css';
|
||||||
|
|
||||||
function GateSection({
|
// function GateSection({
|
||||||
dsp,
|
// dsp,
|
||||||
notify,
|
// notify,
|
||||||
}: {
|
// }: {
|
||||||
dsp: DSP;
|
// dsp: DSP;
|
||||||
notify: (message: string, type?: NotificationType, dspName?: string) => void;
|
// notify: (message: string, type?: NotificationType, dspName?: string) => void;
|
||||||
}) {
|
// }) {
|
||||||
console.log(dsp, notify);
|
// console.log(dsp, notify);
|
||||||
return (
|
// return (
|
||||||
<div className="rerere">
|
// <div className="rerere">
|
||||||
<VerticalSlider
|
// <VerticalSlider
|
||||||
min={-60}
|
// min={-60}
|
||||||
max={12}
|
// max={12}
|
||||||
step={0.5}
|
// step={0.5}
|
||||||
switchStepValue={-10}
|
// switchStepValue={-10}
|
||||||
switchStep={0.1}
|
// switchStep={0.1}
|
||||||
unit=" dB"
|
// unit=" dB"
|
||||||
onChange={(_f: number) => {
|
// onChange={(_f: number) => {
|
||||||
return;
|
// return;
|
||||||
}}
|
// }}
|
||||||
/>
|
// />
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default GateSection;
|
// export default GateSection;
|
||||||
|
|||||||
@ -13,8 +13,8 @@ type VerticalSliderProps = {
|
|||||||
switchStepValue?: number;
|
switchStepValue?: number;
|
||||||
switchStep?: number;
|
switchStep?: number;
|
||||||
|
|
||||||
value?: number;
|
value: number;
|
||||||
onChange?: (value: number) => void;
|
onChange?: (value: number) => Promise<boolean>;
|
||||||
|
|
||||||
unit?: string;
|
unit?: string;
|
||||||
|
|
||||||
@ -34,23 +34,18 @@ export default function VerticalSlider({
|
|||||||
step,
|
step,
|
||||||
switchStepValue,
|
switchStepValue,
|
||||||
switchStep,
|
switchStep,
|
||||||
value: controlledValue,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
unit = '',
|
unit = '',
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
}: VerticalSliderProps) {
|
}: VerticalSliderProps) {
|
||||||
const [internalValue, setInternalValue] = useState(min);
|
|
||||||
const value = controlledValue ?? internalValue;
|
|
||||||
|
|
||||||
const [editingText, setEditingText] = useState<string | null>(null);
|
const [editingText, setEditingText] = useState<string | null>(null);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const sliderRef = useRef<HTMLDivElement>(null);
|
const sliderRef = useRef<HTMLDivElement>(null);
|
||||||
const lastValue = useRef(value);
|
|
||||||
const keyboardValue = useRef<number | null>(null);
|
const keyboardValue = useRef<number | null>(null);
|
||||||
const [displayValue, setDisplayValue] = useState(value);
|
const [displayValue, setDisplayValue] = useState(value);
|
||||||
const dragValue = useRef(value);
|
const dragValue = useRef(value);
|
||||||
const rafRef = useRef<number | null>(null);
|
|
||||||
const dragging = useRef(false);
|
const dragging = useRef(false);
|
||||||
const thumbRef = useRef<HTMLDivElement>(null);
|
const thumbRef = useRef<HTMLDivElement>(null);
|
||||||
const keyboardEditing = useRef(false);
|
const keyboardEditing = useRef(false);
|
||||||
@ -74,16 +69,20 @@ export default function VerticalSlider({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (dragging.current) return;
|
||||||
if (keyboardEditing.current) return;
|
if (keyboardEditing.current) return;
|
||||||
|
|
||||||
setDisplayValue(value);
|
setDisplayValue(value);
|
||||||
dragValue.current = value;
|
dragValue.current = value;
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
const currentStep =
|
function getStep(value: number) {
|
||||||
switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue
|
if (switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue) {
|
||||||
? switchStep
|
return switchStep;
|
||||||
: step;
|
}
|
||||||
|
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
const decimals = useMemo(
|
const decimals = useMemo(
|
||||||
() => Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0),
|
() => Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0),
|
||||||
@ -92,30 +91,25 @@ export default function VerticalSlider({
|
|||||||
|
|
||||||
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));
|
||||||
const snapped = Math.round(clamped / currentStep) * currentStep;
|
|
||||||
return Number(snapped.toFixed(6));
|
const currentStep = getStep(clamped);
|
||||||
|
|
||||||
|
const snapped = min + Math.round((clamped - min) / currentStep) * currentStep;
|
||||||
|
|
||||||
|
return Number(Math.min(max, Math.max(min, snapped)).toFixed(6));
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateValue(v: number) {
|
function updateValue(input: number) {
|
||||||
const next = normalizeValue(v);
|
const next = normalizeValue(input);
|
||||||
|
|
||||||
dragValue.current = next;
|
dragValue.current = next;
|
||||||
lastValue.current = next;
|
setDisplayValue(next);
|
||||||
|
|
||||||
if (thumbRef.current) {
|
|
||||||
const percent = (next - min) / (max - min);
|
|
||||||
thumbRef.current.style.bottom = `${percent * 100}%`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rafRef.current === null) {
|
|
||||||
rafRef.current = requestAnimationFrame(() => {
|
|
||||||
setDisplayValue(dragValue.current);
|
|
||||||
rafRef.current = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
|
function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) return;
|
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
keyboardEditing.current = true;
|
keyboardEditing.current = true;
|
||||||
|
|
||||||
@ -123,33 +117,39 @@ export default function VerticalSlider({
|
|||||||
|
|
||||||
const base = keyboardValue.current ?? value;
|
const base = keyboardValue.current ?? value;
|
||||||
|
|
||||||
|
const currentStep = getStep(base);
|
||||||
|
|
||||||
let next = base;
|
let next = base;
|
||||||
|
|
||||||
if (e.key === 'ArrowUp' || e.key === 'ArrowRight') next += currentStep;
|
if (e.key === 'ArrowUp' || e.key === 'ArrowRight') {
|
||||||
|
next += currentStep;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') next -= currentStep;
|
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {
|
||||||
|
next -= currentStep;
|
||||||
|
}
|
||||||
|
|
||||||
next = normalizeValue(next);
|
next = normalizeValue(next);
|
||||||
|
|
||||||
keyboardValue.current = next;
|
keyboardValue.current = next;
|
||||||
|
|
||||||
updateValue(next); // updates displayValue only
|
updateValue(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp() {
|
async function handleKeyUp() {
|
||||||
if (keyboardValue.current == null) return;
|
if (keyboardValue.current == null) return;
|
||||||
|
|
||||||
keyboardEditing.current = false;
|
|
||||||
|
|
||||||
const next = keyboardValue.current;
|
const next = keyboardValue.current;
|
||||||
|
|
||||||
if (controlledValue === undefined) {
|
const success = await onChange?.(next);
|
||||||
setInternalValue(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange?.(next);
|
|
||||||
|
|
||||||
|
keyboardEditing.current = false;
|
||||||
keyboardValue.current = null;
|
keyboardValue.current = null;
|
||||||
|
|
||||||
|
if (success === false) {
|
||||||
|
setDisplayValue(value);
|
||||||
|
dragValue.current = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function valueFromPointer(clientY: number) {
|
function valueFromPointer(clientY: number) {
|
||||||
@ -172,28 +172,47 @@ export default function VerticalSlider({
|
|||||||
valueFromPointer(e.clientY);
|
valueFromPointer(e.clientY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function endDrag(e: React.PointerEvent<HTMLDivElement>) {
|
async function endDrag(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
dragging.current = false;
|
if (!dragging.current) return;
|
||||||
|
|
||||||
const finalValue = dragValue.current;
|
const finalValue = dragValue.current;
|
||||||
|
|
||||||
if (controlledValue === undefined) setInternalValue(finalValue);
|
const success = await onChange?.(finalValue);
|
||||||
setDisplayValue(finalValue);
|
|
||||||
onChange?.(finalValue);
|
dragging.current = false;
|
||||||
|
|
||||||
|
if (success === false) {
|
||||||
|
setDisplayValue(value);
|
||||||
|
dragValue.current = value;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitEditing() {
|
async function commitEditing() {
|
||||||
if (editingText !== null && editingText !== '' && editingText !== '-') {
|
if (editingText !== null && editingText !== '' && editingText !== '-') {
|
||||||
const parsed = Number(editingText);
|
const parsed = Number(editingText);
|
||||||
if (!Number.isNaN(parsed)) updateValue(parsed);
|
|
||||||
|
if (!Number.isNaN(parsed)) {
|
||||||
|
const next = normalizeValue(parsed);
|
||||||
|
|
||||||
|
setDisplayValue(next);
|
||||||
|
|
||||||
|
const success = await onChange?.(next);
|
||||||
|
|
||||||
|
if (success === false) {
|
||||||
|
setDisplayValue(value);
|
||||||
|
dragValue.current = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setEditingText(null);
|
setEditingText(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const percent = (value - min) / (max - min);
|
const percent = (displayValue - min) / (max - min);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
import { DSP, DSPStatus, AppPage } from '../types/types';
|
import { DSP, AppPage } from '../types/types';
|
||||||
|
import { Channel } from '../types/dsp408State';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createDSP408,
|
createDSP408,
|
||||||
@ -16,11 +17,7 @@ import {
|
|||||||
getDSP408MeterLevels,
|
getDSP408MeterLevels,
|
||||||
} from '../api/dsp408';
|
} from '../api/dsp408';
|
||||||
|
|
||||||
type NotifyFunction = (
|
type NotifyFunction = (message: string, type: 'success' | 'error', dspName?: string) => void;
|
||||||
message: string,
|
|
||||||
type: 'success' | 'error',
|
|
||||||
dspName?: string
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
|
type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
|
||||||
|
|
||||||
@ -28,14 +25,8 @@ type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
|
|||||||
// Helpers
|
// Helpers
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
function updateDSP(
|
function updateDSP(setDsps: SetDsps, dspId: number, updater: (dsp: DSP) => DSP) {
|
||||||
setDsps: SetDsps,
|
setDsps((prev) => prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp)));
|
||||||
dspId: number,
|
|
||||||
updater: (dsp: DSP) => DSP
|
|
||||||
) {
|
|
||||||
setDsps((prev) =>
|
|
||||||
prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DSP408Device = {
|
export type DSP408Device = {
|
||||||
@ -43,6 +34,9 @@ export type DSP408Device = {
|
|||||||
disconnect: () => Promise<void>;
|
disconnect: () => Promise<void>;
|
||||||
recallPreset: (index: number) => Promise<boolean>;
|
recallPreset: (index: number) => Promise<boolean>;
|
||||||
setCurrentPresetName: (name: string) => Promise<boolean>;
|
setCurrentPresetName: (name: string) => Promise<boolean>;
|
||||||
|
setGain: (channel: Channel, gain: number) => Promise<boolean>;
|
||||||
|
setMute: (channel: Channel, muted: boolean) => Promise<boolean>;
|
||||||
|
setInverse: (channel: Channel, inverted: boolean) => Promise<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@ -173,10 +167,7 @@ export function useDSP408(
|
|||||||
// Generic operation wrapper
|
// Generic operation wrapper
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
async function withPollingPaused<T>(
|
async function withPollingPaused<T>(dsp: DSP, operation: () => Promise<T>): Promise<T> {
|
||||||
dsp: DSP,
|
|
||||||
operation: () => Promise<T>
|
|
||||||
): Promise<T> {
|
|
||||||
setPollingPaused(dsp.id, true);
|
setPollingPaused(dsp.id, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -216,11 +207,7 @@ export function useDSP408(
|
|||||||
status: 'disconnected',
|
status: 'disconnected',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
notify(
|
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Connection failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
setPollingPaused(dsp.id, false);
|
setPollingPaused(dsp.id, false);
|
||||||
}
|
}
|
||||||
@ -242,11 +229,7 @@ export function useDSP408(
|
|||||||
// If disconnect failed, it is probably still connected.
|
// If disconnect failed, it is probably still connected.
|
||||||
setPollingPaused(dsp.id, false);
|
setPollingPaused(dsp.id, false);
|
||||||
|
|
||||||
notify(
|
notify(`Disconnect failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Disconnect failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,18 +237,9 @@ export function useDSP408(
|
|||||||
// Add / Remove
|
// Add / Remove
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
|
|
||||||
async function addDSP(
|
async function addDSP(dsp: Omit<DSP, 'id' | 'status' | 'state' | 'meters' | 'pollingPaused'>) {
|
||||||
dsp: Omit<
|
|
||||||
DSP,
|
|
||||||
'id' | 'status' | 'state' | 'meters' | 'pollingPaused'
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const id = await createDSP408(
|
const id = await createDSP408(dsp.ip, dsp.port, dsp.deviceId);
|
||||||
dsp.ip,
|
|
||||||
dsp.port,
|
|
||||||
dsp.deviceId
|
|
||||||
);
|
|
||||||
|
|
||||||
const newDsp: DSP = {
|
const newDsp: DSP = {
|
||||||
id,
|
id,
|
||||||
@ -282,11 +256,7 @@ export function useDSP408(
|
|||||||
|
|
||||||
notify('Added successfully', 'success', dsp.name);
|
notify('Added successfully', 'success', dsp.name);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify(
|
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Connection failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,32 +267,20 @@ export function useDSP408(
|
|||||||
await removeDSP408(dsp.id);
|
await removeDSP408(dsp.id);
|
||||||
|
|
||||||
setDsps((prev) => {
|
setDsps((prev) => {
|
||||||
const next = prev.filter(
|
const next = prev.filter((d) => d.id !== dsp.id);
|
||||||
(d) => d.id !== dsp.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selected === dsp.id) {
|
if (selected === dsp.id) {
|
||||||
setSelected(
|
setSelected(next.length ? next[0].id : null);
|
||||||
next.length ? next[0].id : null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
|
||||||
notify(
|
notify('Removed successfully', 'success', dsp.name);
|
||||||
'Removed successfully',
|
|
||||||
'success',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setPollingPaused(dsp.id, false);
|
setPollingPaused(dsp.id, false);
|
||||||
|
|
||||||
notify(
|
notify(`Remove failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Remove failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,23 +288,13 @@ export function useDSP408(
|
|||||||
// Presets
|
// Presets
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
|
|
||||||
async function recallPreset(
|
async function recallPreset(dsp: DSP, presetIndex: number) {
|
||||||
dsp: DSP,
|
|
||||||
presetIndex: number
|
|
||||||
) {
|
|
||||||
return withPollingPaused(dsp, async () => {
|
return withPollingPaused(dsp, async () => {
|
||||||
try {
|
try {
|
||||||
const success = await recallPresetDSP408(
|
const success = await recallPresetDSP408(dsp.id, presetIndex);
|
||||||
dsp.id,
|
|
||||||
presetIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
notify(
|
notify('Failed to recall preset', 'error', dsp.name);
|
||||||
'Failed to recall preset',
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -366,47 +314,26 @@ export function useDSP408(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
notify(
|
notify(`Preset ${presetIndex} recalled`, 'success', dsp.name);
|
||||||
`Preset ${presetIndex} recalled`,
|
|
||||||
'success',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify(
|
notify(`Preset recall failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Preset recall failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setCurrentPresetName(
|
async function setCurrentPresetName(dsp: DSP, name: string) {
|
||||||
dsp: DSP,
|
|
||||||
name: string
|
|
||||||
) {
|
|
||||||
return withPollingPaused(dsp, async () => {
|
return withPollingPaused(dsp, async () => {
|
||||||
try {
|
try {
|
||||||
const paddedName = name
|
const paddedName = name.slice(0, 14).padEnd(14, ' ');
|
||||||
.slice(0, 14)
|
|
||||||
.padEnd(14, ' ');
|
|
||||||
|
|
||||||
const success =
|
const success = await DSP408SetCurrentPresetName(dsp.id, paddedName);
|
||||||
await DSP408SetCurrentPresetName(
|
|
||||||
dsp.id,
|
|
||||||
paddedName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
notify(
|
notify('Failed to rename preset', 'error', dsp.name);
|
||||||
'Failed to rename preset',
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -423,36 +350,20 @@ export function useDSP408(
|
|||||||
...d.state,
|
...d.state,
|
||||||
presets: {
|
presets: {
|
||||||
...presets,
|
...presets,
|
||||||
names: presets.names.map(
|
names: presets.names.map((presetName, i) =>
|
||||||
(presetName, i) =>
|
i === index ? paddedName : presetName
|
||||||
i === index
|
|
||||||
? paddedName
|
|
||||||
: presetName
|
|
||||||
),
|
|
||||||
modified: presets.modified.map(
|
|
||||||
(modified, i) =>
|
|
||||||
i === index
|
|
||||||
? true
|
|
||||||
: modified
|
|
||||||
),
|
),
|
||||||
|
modified: presets.modified.map((modified, i) => (i === index ? true : modified)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
notify(
|
notify('Preset renamed', 'success', dsp.name);
|
||||||
'Preset renamed',
|
|
||||||
'success',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify(
|
notify(`Preset rename failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Preset rename failed: ${String(err)}`,
|
|
||||||
'error',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -463,28 +374,54 @@ export function useDSP408(
|
|||||||
// Gain
|
// Gain
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
|
|
||||||
async function setGain(
|
async function setGain(dsp: DSP, channel: Channel, gain: number): Promise<boolean> {
|
||||||
dsp: DSP,
|
|
||||||
channel: number,
|
|
||||||
gain: number
|
|
||||||
) {
|
|
||||||
return withPollingPaused(dsp, async () => {
|
return withPollingPaused(dsp, async () => {
|
||||||
try {
|
try {
|
||||||
const success = await invoke<boolean>(
|
const success = await invoke<boolean>('set_channel_gain', {
|
||||||
'set_gain',
|
|
||||||
{
|
|
||||||
id: dsp.id,
|
id: dsp.id,
|
||||||
channel,
|
channel,
|
||||||
gain,
|
db: gain,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
notify(
|
notify('Failed to set gain', 'error', dsp.name);
|
||||||
'Failed to set gain',
|
|
||||||
'error',
|
return false;
|
||||||
dsp.name
|
}
|
||||||
);
|
|
||||||
|
const state = await getDSP408State(dsp.id);
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => ({
|
||||||
|
...d,
|
||||||
|
state,
|
||||||
|
}));
|
||||||
|
|
||||||
|
notify('Gain updated', 'success', dsp.name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
notify(`Gain update failed: ${String(err)}`, 'error', dsp.name);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Mute
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function setMute(dsp: DSP, channel: Channel, muted: boolean): Promise<boolean> {
|
||||||
|
return withPollingPaused(dsp, async () => {
|
||||||
|
try {
|
||||||
|
const success = await invoke<boolean>('set_mute', {
|
||||||
|
id: dsp.id,
|
||||||
|
channel,
|
||||||
|
muted,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
notify('Failed to change mute state', 'error', dsp.name);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -492,28 +429,130 @@ export function useDSP408(
|
|||||||
updateDSP(setDsps, dsp.id, (d) => {
|
updateDSP(setDsps, dsp.id, (d) => {
|
||||||
if (!d.state) return d;
|
if (!d.state) return d;
|
||||||
|
|
||||||
|
const currentConfig = d.state.current_config;
|
||||||
|
|
||||||
|
if ('Input' in channel) {
|
||||||
|
const inputChannel = channel.Input;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...d,
|
...d,
|
||||||
state: {
|
state: {
|
||||||
...d.state,
|
...d.state,
|
||||||
// Update gain state here
|
current_config: {
|
||||||
|
...currentConfig,
|
||||||
|
input_states: {
|
||||||
|
...currentConfig.input_states,
|
||||||
|
[inputChannel]: {
|
||||||
|
...currentConfig.input_states[inputChannel],
|
||||||
|
mute: muted,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputChannel = channel.Output;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
current_config: {
|
||||||
|
...currentConfig,
|
||||||
|
output_states: {
|
||||||
|
...currentConfig.output_states,
|
||||||
|
[outputChannel]: {
|
||||||
|
...currentConfig.output_states[outputChannel],
|
||||||
|
mute: muted,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
notify(
|
notify(muted ? 'Muted' : 'Unmuted', 'success', dsp.name);
|
||||||
'Gain updated',
|
|
||||||
'success',
|
|
||||||
dsp.name
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify(
|
notify(`Mute update failed: ${String(err)}`, 'error', dsp.name);
|
||||||
`Gain update failed: ${String(err)}`,
|
|
||||||
'error',
|
return false;
|
||||||
dsp.name
|
}
|
||||||
);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Inverse
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function setInverse(dsp: DSP, channel: Channel, inverted: boolean): Promise<boolean> {
|
||||||
|
return withPollingPaused(dsp, async () => {
|
||||||
|
try {
|
||||||
|
const success = await invoke<boolean>('set_channel_inverse_gain', {
|
||||||
|
id: dsp.id,
|
||||||
|
channel,
|
||||||
|
inverted,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
notify('Failed to change phase', 'error', dsp.name);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => {
|
||||||
|
if (!d.state) return d;
|
||||||
|
|
||||||
|
const currentConfig = d.state.current_config;
|
||||||
|
|
||||||
|
if ('Input' in channel) {
|
||||||
|
const inputChannel = channel.Input;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
current_config: {
|
||||||
|
...currentConfig,
|
||||||
|
input_states: {
|
||||||
|
...currentConfig.input_states,
|
||||||
|
[inputChannel]: {
|
||||||
|
...currentConfig.input_states[inputChannel],
|
||||||
|
phase_inverted: inverted,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputChannel = channel.Output;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
current_config: {
|
||||||
|
...currentConfig,
|
||||||
|
output_states: {
|
||||||
|
...currentConfig.output_states,
|
||||||
|
[outputChannel]: {
|
||||||
|
...currentConfig.output_states[outputChannel],
|
||||||
|
phase_inverted: inverted,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
notify(inverted ? 'Phase inverted' : 'Phase normal', 'success', dsp.name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
notify(`Phase update failed: ${String(err)}`, 'error', dsp.name);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -529,10 +568,10 @@ export function useDSP408(
|
|||||||
connect: () => connectDSP(dsp),
|
connect: () => connectDSP(dsp),
|
||||||
disconnect: () => disconnectDSP(dsp),
|
disconnect: () => disconnectDSP(dsp),
|
||||||
recallPreset: (index: number) => recallPreset(dsp, index),
|
recallPreset: (index: number) => recallPreset(dsp, index),
|
||||||
setCurrentPresetName: (name: string) =>
|
setCurrentPresetName: (name: string) => setCurrentPresetName(dsp, name),
|
||||||
setCurrentPresetName(dsp, name),
|
setGain: (channel: Channel, gain: number) => setGain(dsp, channel, gain),
|
||||||
// setGain: (channel, gain) =>
|
setMute: (channel: Channel, muted: boolean) => setMute(dsp, channel, muted),
|
||||||
// setGain(dsp, channel, gain),
|
setInverse: (channel: Channel, inverted: boolean) => setInverse(dsp, channel, inverted),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,17 +6,11 @@ import ConnectionPanel from '../../components/dsp408/ConnectionSection/Connectio
|
|||||||
import GainSection from '../../components/dsp408/GainSection/GainSection';
|
import GainSection from '../../components/dsp408/GainSection/GainSection';
|
||||||
import { DSP408Device } from '../../hooks/useDSP408';
|
import { DSP408Device } from '../../hooks/useDSP408';
|
||||||
import './DspPage.css';
|
import './DspPage.css';
|
||||||
import GateSection from '../../components/dsp408/GateSection/GateSection';
|
// import GateSection from '../../components/dsp408/GateSection/GateSection';
|
||||||
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
|
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
|
||||||
import { InputChannel, OutputChannel } from '../../types/dsp408State';
|
import { InputChannel, OutputChannel } from '../../types/dsp408State';
|
||||||
|
|
||||||
function DSPPage({
|
function DSPPage({ dsp, dsp408 }: { dsp: DSP; dsp408: DSP408Device }) {
|
||||||
dsp,
|
|
||||||
dsp408,
|
|
||||||
}: {
|
|
||||||
dsp: DSP;
|
|
||||||
dsp408: DSP408Device;
|
|
||||||
}) {
|
|
||||||
const [activeSidebar, setActiveSidebar] = useState('overview');
|
const [activeSidebar, setActiveSidebar] = useState('overview');
|
||||||
const [sidebarExpanded, setSidebarExpanded] = useState(false);
|
const [sidebarExpanded, setSidebarExpanded] = useState(false);
|
||||||
const [selectedPreset, setSelectedPreset] = useState(dsp.state?.presets.current_index ?? 0);
|
const [selectedPreset, setSelectedPreset] = useState(dsp.state?.presets.current_index ?? 0);
|
||||||
@ -170,10 +164,19 @@ function DSPPage({
|
|||||||
const renderTab = () => {
|
const renderTab = () => {
|
||||||
switch (activeSidebar) {
|
switch (activeSidebar) {
|
||||||
case 'overview':
|
case 'overview':
|
||||||
return <ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />;
|
return (
|
||||||
|
<ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />
|
||||||
|
);
|
||||||
|
|
||||||
// case 'gain':
|
case 'gain':
|
||||||
// return <GainSection dsp={dsp} notify={notify} />;
|
return (
|
||||||
|
<GainSection
|
||||||
|
dsp={dsp}
|
||||||
|
setGain={dsp408.setGain}
|
||||||
|
setMute={dsp408.setMute}
|
||||||
|
setInverse={dsp408.setInverse}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
// case 'gate':
|
// case 'gate':
|
||||||
// return <GateSection dsp={dsp} notify={notify} />;
|
// return <GateSection dsp={dsp} notify={notify} />;
|
||||||
|
|||||||
Reference in New Issue
Block a user