feat: dont moove slider cursor if tcp command fails

This commit is contained in:
2026-07-28 13:53:57 +02:00
parent 54fda168cc
commit eab62e151e
6 changed files with 376 additions and 353 deletions

View File

@ -18,14 +18,7 @@ function App() {
const activeDsp = dsps.find((d) => d.id === selected) ?? null;
const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length;
const dsp408 = useDSP408(
dsps,
setDsps,
selected,
setSelected,
setPage,
notify
);
const dsp408 = useDSP408(dsps, setDsps, selected, setSelected, setPage, notify);
return (
<div className="app">
@ -60,10 +53,7 @@ function App() {
) : page === 'add-dsp' ? (
<AddDSPForm onCancel={() => setPage('home')} onAdd={dsp408.addDSP} />
) : activeDsp ? (
<DSPPage
dsp={activeDsp}
dsp408={dsp408.forDSP(activeDsp)}
/>
<DSPPage dsp={activeDsp} dsp408={dsp408.forDSP(activeDsp)} />
) : (
<HomePage
onAddDSP={() => setPage('add-dsp')}

View File

@ -1,8 +1,6 @@
import VerticalSlider from '../Slider/VerticalSlider';
import { invoke } from '@tauri-apps/api/core';
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
import { DSP } from '../../../types/types';
import { NotificationType } from '../../../types/types';
import Button from '../../Button/Button';
import ChannelGroup from '../ChannelGroup/ChannelGroup';
import { LinearGraph } from '../LinearGraph/LinearGraph';
@ -12,109 +10,62 @@ import './GainSection.css';
const PANEL_DESIGN_WIDTH = 90;
const PANEL_DESIGN_HEIGHT = 400;
function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
const wrapperRef = useRef<HTMLDivElement>(null);
const [scale, setScale] = useState(1);
// function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
// const wrapperRef = useRef<HTMLDivElement>(null);
// const [scale, setScale] = useState(1);
useEffect(() => {
const el = wrapperRef.current;
if (!el) return;
const observer = new ResizeObserver((entries) => {
const { height } = entries[0].contentRect;
if (height === 0) return;
setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
});
observer.observe(el);
return () => observer.disconnect();
}, []);
// useEffect(() => {
// const el = wrapperRef.current;
// if (!el) return;
// const observer = new ResizeObserver((entries) => {
// const { height } = entries[0].contentRect;
// if (height === 0) return;
// setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
// });
// observer.observe(el);
// return () => observer.disconnect();
// }, []);
return (
<div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
<ChannelGroup title={title}>{children}</ChannelGroup>
</div>
);
}
// return (
// <div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
// <ChannelGroup title={title}>{children}</ChannelGroup>
// </div>
// );
// }
function GainPanel({
dsp,
notify,
channel,
title,
setGain,
setMute,
setInverse,
}: {
dsp: DSP;
notify: (message: string, type?: NotificationType, dspName?: string) => void;
channel: Channel;
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) => {
try {
const success = await invoke<boolean>('set_channel_gain', {
id: dsp.id,
channel,
db: value,
});
const channelState =
'Input' in channel
? dsp.state?.current_config.input_states[channel.Input]
: dsp.state?.current_config.output_states[channel.Output];
if (success) {
notify(`Input A gain set to ${value} dB`, 'success', dsp.name);
} else {
notify('Failed to set input gain', 'error', dsp.name);
}
} catch (err) {
notify(`Gain update failed: ${String(err)}`, 'error', dsp.name);
}
};
const muted = channelState?.mute ?? false;
const inverted = channelState?.phase_inverted ?? false;
const gain = channelState?.gain ?? 0;
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 [scale, setScale] = useState(1);
const toggleMute = async () => {
const next = !muted;
setMuted(next);
await setMute(next);
await setMute(channel, !muted);
};
const toggleInverse = async () => {
const next = !inverted;
setInverted(next);
await setInverse(next);
await setInverse(channel, !inverted);
};
useEffect(() => {
@ -148,7 +99,8 @@ function GainPanel({
switchStepValue={-10}
switchStep={0.1}
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
/>
@ -169,10 +121,14 @@ function GainPanel({
function GainSection({
dsp,
notify,
setGain,
setMute,
setInverse,
}: {
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 }[] = [
{ channel: { Input: InputChannel.InA }, title: 'In A' },
@ -223,13 +179,29 @@ function GainSection({
<div className="gain-panels">
<ChannelGroup title="Input">
{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 title="Output">
{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>
</div>

View File

@ -1,35 +1,35 @@
import VerticalSlider from '../Slider/VerticalSlider';
import { invoke } from '@tauri-apps/api/core';
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
import { DSP } from '../../../types/types';
import { NotificationType } from '../../../types/types';
// import VerticalSlider from '../Slider/VerticalSlider';
// import { invoke } from '@tauri-apps/api/core';
// import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
// import { DSP } from '../../../types/types';
// import { NotificationType } from '../../../types/types';
import ChannelGroup from '../ChannelGroup/ChannelGroup';
import './GateSection.css';
// import ChannelGroup from '../ChannelGroup/ChannelGroup';
// import './GateSection.css';
function GateSection({
dsp,
notify,
}: {
dsp: DSP;
notify: (message: string, type?: NotificationType, dspName?: string) => void;
}) {
console.log(dsp, notify);
return (
<div className="rerere">
<VerticalSlider
min={-60}
max={12}
step={0.5}
switchStepValue={-10}
switchStep={0.1}
unit=" dB"
onChange={(_f: number) => {
return;
}}
/>
</div>
);
}
// function GateSection({
// dsp,
// notify,
// }: {
// dsp: DSP;
// notify: (message: string, type?: NotificationType, dspName?: string) => void;
// }) {
// console.log(dsp, notify);
// return (
// <div className="rerere">
// <VerticalSlider
// min={-60}
// max={12}
// step={0.5}
// switchStepValue={-10}
// switchStep={0.1}
// unit=" dB"
// onChange={(_f: number) => {
// return;
// }}
// />
// </div>
// );
// }
export default GateSection;
// export default GateSection;

View File

@ -13,8 +13,8 @@ type VerticalSliderProps = {
switchStepValue?: number;
switchStep?: number;
value?: number;
onChange?: (value: number) => void;
value: number;
onChange?: (value: number) => Promise<boolean>;
unit?: string;
@ -34,23 +34,18 @@ export default function VerticalSlider({
step,
switchStepValue,
switchStep,
value: controlledValue,
value,
onChange,
unit = '',
className,
style,
}: VerticalSliderProps) {
const [internalValue, setInternalValue] = useState(min);
const value = controlledValue ?? internalValue;
const [editingText, setEditingText] = useState<string | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const sliderRef = useRef<HTMLDivElement>(null);
const lastValue = useRef(value);
const keyboardValue = useRef<number | null>(null);
const [displayValue, setDisplayValue] = useState(value);
const dragValue = useRef(value);
const rafRef = useRef<number | null>(null);
const dragging = useRef(false);
const thumbRef = useRef<HTMLDivElement>(null);
const keyboardEditing = useRef(false);
@ -74,16 +69,20 @@ export default function VerticalSlider({
}, []);
useEffect(() => {
if (dragging.current) return;
if (keyboardEditing.current) return;
setDisplayValue(value);
dragValue.current = value;
}, [value]);
const currentStep =
switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue
? switchStep
: step;
function getStep(value: number) {
if (switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue) {
return switchStep;
}
return step;
}
const decimals = useMemo(
() => Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0),
@ -92,30 +91,25 @@ export default function VerticalSlider({
function normalizeValue(input: number) {
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) {
const next = normalizeValue(v);
function updateValue(input: number) {
const next = normalizeValue(input);
dragValue.current = next;
lastValue.current = 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;
});
}
setDisplayValue(next);
}
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;
@ -123,33 +117,39 @@ export default function VerticalSlider({
const base = keyboardValue.current ?? value;
const currentStep = getStep(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);
keyboardValue.current = next;
updateValue(next); // updates displayValue only
updateValue(next);
}
function handleKeyUp() {
async function handleKeyUp() {
if (keyboardValue.current == null) return;
keyboardEditing.current = false;
const next = keyboardValue.current;
if (controlledValue === undefined) {
setInternalValue(next);
}
onChange?.(next);
const success = await onChange?.(next);
keyboardEditing.current = false;
keyboardValue.current = null;
if (success === false) {
setDisplayValue(value);
dragValue.current = value;
}
}
function valueFromPointer(clientY: number) {
@ -172,28 +172,47 @@ export default function VerticalSlider({
valueFromPointer(e.clientY);
}
function endDrag(e: React.PointerEvent<HTMLDivElement>) {
dragging.current = false;
async function endDrag(e: React.PointerEvent<HTMLDivElement>) {
if (!dragging.current) return;
const finalValue = dragValue.current;
if (controlledValue === undefined) setInternalValue(finalValue);
setDisplayValue(finalValue);
onChange?.(finalValue);
const success = await onChange?.(finalValue);
dragging.current = false;
if (success === false) {
setDisplayValue(value);
dragValue.current = value;
}
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
e.currentTarget.releasePointerCapture(e.pointerId);
}
}
function commitEditing() {
async function commitEditing() {
if (editingText !== null && editingText !== '' && 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);
}
const percent = (value - min) / (max - min);
const percent = (displayValue - min) / (max - min);
return (
<div

View File

@ -3,7 +3,8 @@
import { useEffect, useRef } from 'react';
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 {
createDSP408,
@ -16,11 +17,7 @@ import {
getDSP408MeterLevels,
} from '../api/dsp408';
type NotifyFunction = (
message: string,
type: 'success' | 'error',
dspName?: string
) => void;
type NotifyFunction = (message: string, type: 'success' | 'error', dspName?: string) => void;
type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
@ -28,14 +25,8 @@ type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
// Helpers
// ============================================================
function updateDSP(
setDsps: SetDsps,
dspId: number,
updater: (dsp: DSP) => DSP
) {
setDsps((prev) =>
prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp))
);
function updateDSP(setDsps: SetDsps, dspId: number, updater: (dsp: DSP) => DSP) {
setDsps((prev) => prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp)));
}
export type DSP408Device = {
@ -43,6 +34,9 @@ export type DSP408Device = {
disconnect: () => Promise<void>;
recallPreset: (index: number) => 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
// ----------------------------------------------------------
async function withPollingPaused<T>(
dsp: DSP,
operation: () => Promise<T>
): Promise<T> {
async function withPollingPaused<T>(dsp: DSP, operation: () => Promise<T>): Promise<T> {
setPollingPaused(dsp.id, true);
try {
@ -216,11 +207,7 @@ export function useDSP408(
status: 'disconnected',
}));
notify(
`Connection failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
} finally {
setPollingPaused(dsp.id, false);
}
@ -242,11 +229,7 @@ export function useDSP408(
// If disconnect failed, it is probably still connected.
setPollingPaused(dsp.id, false);
notify(
`Disconnect failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Disconnect failed: ${String(err)}`, 'error', dsp.name);
}
}
@ -254,18 +237,9 @@ export function useDSP408(
// Add / Remove
// ==========================================================
async function addDSP(
dsp: Omit<
DSP,
'id' | 'status' | 'state' | 'meters' | 'pollingPaused'
>
) {
async function addDSP(dsp: Omit<DSP, 'id' | 'status' | 'state' | 'meters' | 'pollingPaused'>) {
try {
const id = await createDSP408(
dsp.ip,
dsp.port,
dsp.deviceId
);
const id = await createDSP408(dsp.ip, dsp.port, dsp.deviceId);
const newDsp: DSP = {
id,
@ -282,11 +256,7 @@ export function useDSP408(
notify('Added successfully', 'success', dsp.name);
} catch (err) {
notify(
`Connection failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
}
}
@ -297,32 +267,20 @@ export function useDSP408(
await removeDSP408(dsp.id);
setDsps((prev) => {
const next = prev.filter(
(d) => d.id !== dsp.id
);
const next = prev.filter((d) => d.id !== dsp.id);
if (selected === dsp.id) {
setSelected(
next.length ? next[0].id : null
);
setSelected(next.length ? next[0].id : null);
}
return next;
});
notify(
'Removed successfully',
'success',
dsp.name
);
notify('Removed successfully', 'success', dsp.name);
} catch (err) {
setPollingPaused(dsp.id, false);
notify(
`Remove failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Remove failed: ${String(err)}`, 'error', dsp.name);
}
}
@ -330,23 +288,13 @@ export function useDSP408(
// Presets
// ==========================================================
async function recallPreset(
dsp: DSP,
presetIndex: number
) {
async function recallPreset(dsp: DSP, presetIndex: number) {
return withPollingPaused(dsp, async () => {
try {
const success = await recallPresetDSP408(
dsp.id,
presetIndex
);
const success = await recallPresetDSP408(dsp.id, presetIndex);
if (!success) {
notify(
'Failed to recall preset',
'error',
dsp.name
);
notify('Failed to recall preset', 'error', dsp.name);
return false;
}
@ -366,47 +314,26 @@ export function useDSP408(
};
});
notify(
`Preset ${presetIndex} recalled`,
'success',
dsp.name
);
notify(`Preset ${presetIndex} recalled`, 'success', dsp.name);
return true;
} catch (err) {
notify(
`Preset recall failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Preset recall failed: ${String(err)}`, 'error', dsp.name);
return false;
}
});
}
async function setCurrentPresetName(
dsp: DSP,
name: string
) {
async function setCurrentPresetName(dsp: DSP, name: string) {
return withPollingPaused(dsp, async () => {
try {
const paddedName = name
.slice(0, 14)
.padEnd(14, ' ');
const paddedName = name.slice(0, 14).padEnd(14, ' ');
const success =
await DSP408SetCurrentPresetName(
dsp.id,
paddedName
);
const success = await DSP408SetCurrentPresetName(dsp.id, paddedName);
if (!success) {
notify(
'Failed to rename preset',
'error',
dsp.name
);
notify('Failed to rename preset', 'error', dsp.name);
return false;
}
@ -423,36 +350,20 @@ export function useDSP408(
...d.state,
presets: {
...presets,
names: presets.names.map(
(presetName, i) =>
i === index
? paddedName
: presetName
),
modified: presets.modified.map(
(modified, i) =>
i === index
? true
: modified
names: presets.names.map((presetName, i) =>
i === index ? paddedName : presetName
),
modified: presets.modified.map((modified, i) => (i === index ? true : modified)),
},
},
};
});
notify(
'Preset renamed',
'success',
dsp.name
);
notify('Preset renamed', 'success', dsp.name);
return true;
} catch (err) {
notify(
`Preset rename failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Preset rename failed: ${String(err)}`, 'error', dsp.name);
return false;
}
@ -463,28 +374,54 @@ export function useDSP408(
// Gain
// ==========================================================
async function setGain(
dsp: DSP,
channel: number,
gain: number
) {
async function setGain(dsp: DSP, channel: Channel, gain: number): Promise<boolean> {
return withPollingPaused(dsp, async () => {
try {
const success = await invoke<boolean>(
'set_gain',
{
id: dsp.id,
channel,
gain,
}
);
const success = await invoke<boolean>('set_channel_gain', {
id: dsp.id,
channel,
db: gain,
});
if (!success) {
notify(
'Failed to set gain',
'error',
dsp.name
);
notify('Failed to set gain', 'error', dsp.name);
return false;
}
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;
}
@ -492,28 +429,130 @@ export function useDSP408(
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],
mute: muted,
},
},
},
},
};
}
const outputChannel = channel.Output;
return {
...d,
state: {
...d.state,
// Update gain state here
current_config: {
...currentConfig,
output_states: {
...currentConfig.output_states,
[outputChannel]: {
...currentConfig.output_states[outputChannel],
mute: muted,
},
},
},
},
};
});
notify(
'Gain updated',
'success',
dsp.name
);
notify(muted ? 'Muted' : 'Unmuted', 'success', dsp.name);
return true;
} catch (err) {
notify(
`Gain update failed: ${String(err)}`,
'error',
dsp.name
);
notify(`Mute update failed: ${String(err)}`, 'error', dsp.name);
return false;
}
});
}
// ==========================================================
// 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;
}
@ -526,15 +565,15 @@ export function useDSP408(
function forDSP(dsp: DSP): DSP408Device {
return {
connect: () => connectDSP(dsp),
disconnect: () => disconnectDSP(dsp),
recallPreset: (index: number) => recallPreset(dsp, index),
setCurrentPresetName: (name: string) =>
setCurrentPresetName(dsp, name),
// setGain: (channel, gain) =>
// setGain(dsp, channel, gain),
};
}
connect: () => connectDSP(dsp),
disconnect: () => disconnectDSP(dsp),
recallPreset: (index: number) => recallPreset(dsp, index),
setCurrentPresetName: (name: string) => setCurrentPresetName(dsp, name),
setGain: (channel: Channel, gain: number) => setGain(dsp, channel, gain),
setMute: (channel: Channel, muted: boolean) => setMute(dsp, channel, muted),
setInverse: (channel: Channel, inverted: boolean) => setInverse(dsp, channel, inverted),
};
}
return {
forDSP,
@ -550,4 +589,4 @@ export function useDSP408(
setGain,
};
}
}

View File

@ -6,17 +6,11 @@ import ConnectionPanel from '../../components/dsp408/ConnectionSection/Connectio
import GainSection from '../../components/dsp408/GainSection/GainSection';
import { DSP408Device } from '../../hooks/useDSP408';
import './DspPage.css';
import GateSection from '../../components/dsp408/GateSection/GateSection';
// import GateSection from '../../components/dsp408/GateSection/GateSection';
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import { InputChannel, OutputChannel } from '../../types/dsp408State';
function DSPPage({
dsp,
dsp408,
}: {
dsp: DSP;
dsp408: DSP408Device;
}) {
function DSPPage({ dsp, dsp408 }: { dsp: DSP; dsp408: DSP408Device }) {
const [activeSidebar, setActiveSidebar] = useState('overview');
const [sidebarExpanded, setSidebarExpanded] = useState(false);
const [selectedPreset, setSelectedPreset] = useState(dsp.state?.presets.current_index ?? 0);
@ -170,10 +164,19 @@ function DSPPage({
const renderTab = () => {
switch (activeSidebar) {
case 'overview':
return <ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />;
return (
<ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />
);
// case 'gain':
// return <GainSection dsp={dsp} notify={notify} />;
case 'gain':
return (
<GainSection
dsp={dsp}
setGain={dsp408.setGain}
setMute={dsp408.setMute}
setInverse={dsp408.setInverse}
/>
);
// case 'gate':
// return <GateSection dsp={dsp} notify={notify} />;