) {
+ 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 (
void;
+type NotifyFunction = (message: string, type: 'success' | 'error', dspName?: string) => void;
type SetDsps = React.Dispatch>;
@@ -28,14 +25,8 @@ type SetDsps = React.Dispatch>;
// 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;
recallPreset: (index: number) => Promise;
setCurrentPresetName: (name: string) => Promise;
+ setGain: (channel: Channel, gain: number) => Promise;
+ setMute: (channel: Channel, muted: boolean) => Promise;
+ setInverse: (channel: Channel, inverted: boolean) => Promise;
};
// ============================================================
@@ -173,10 +167,7 @@ export function useDSP408(
// Generic operation wrapper
// ----------------------------------------------------------
- async function withPollingPaused(
- dsp: DSP,
- operation: () => Promise
- ): Promise {
+ async function withPollingPaused(dsp: DSP, operation: () => Promise): Promise {
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) {
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 {
return withPollingPaused(dsp, async () => {
try {
- const success = await invoke(
- 'set_gain',
- {
- id: dsp.id,
- channel,
- gain,
- }
- );
+ const success = await invoke('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 {
+ return withPollingPaused(dsp, async () => {
+ try {
+ const success = await invoke('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 {
+ return withPollingPaused(dsp, async () => {
+ try {
+ const success = await invoke('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,
};
-}
\ No newline at end of file
+}
diff --git a/src/pages/DspPage/DspPage.tsx b/src/pages/DspPage/DspPage.tsx
index b2973b4..27096aa 100644
--- a/src/pages/DspPage/DspPage.tsx
+++ b/src/pages/DspPage/DspPage.tsx
@@ -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 ;
+ return (
+
+ );
- // case 'gain':
- // return ;
+ case 'gain':
+ return (
+
+ );
// case 'gate':
// return ;