feat: sidebar allows recall, rename and display custom names
This commit is contained in:
109
src/App.tsx
109
src/App.tsx
@ -1,8 +1,6 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { DSP, DSPStatus, AppPage } from './types/types';
|
import { DSP, AppPage } from './types/types';
|
||||||
import TopBar from './components/Topbar/Topbar';
|
import TopBar from './components/Topbar/Topbar';
|
||||||
import DSPPage from './pages/DspPage/DspPage';
|
import DSPPage from './pages/DspPage/DspPage';
|
||||||
import AddDSPForm from './pages/AddDspPage/AddDspPage';
|
import AddDSPForm from './pages/AddDspPage/AddDspPage';
|
||||||
@ -10,8 +8,7 @@ import { useNotifications } from './hooks/useNotifications';
|
|||||||
import Notifications from './components/Notifications/Notifications';
|
import Notifications from './components/Notifications/Notifications';
|
||||||
import CreditsPage from './pages/CreditsPage/CreditsPage';
|
import CreditsPage from './pages/CreditsPage/CreditsPage';
|
||||||
import HomePage from './pages/HomePage/HomePage';
|
import HomePage from './pages/HomePage/HomePage';
|
||||||
import { getDSP408State, connectDSP408, disconnectDSP408 } from './api/dsp408';
|
import { useDSP408 } from './hooks/useDSP408';
|
||||||
import { useDSPPolling } from './hooks/useDSPPolling';
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [dsps, setDsps] = useState<DSP[]>([]);
|
const [dsps, setDsps] = useState<DSP[]>([]);
|
||||||
@ -21,92 +18,14 @@ 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;
|
||||||
|
|
||||||
useDSPPolling(dsps, selected, setDsps);
|
const dsp408 = useDSP408(
|
||||||
|
dsps,
|
||||||
function setStatus(id: number, status: DSPStatus) {
|
setDsps,
|
||||||
setDsps((prev) => prev.map((d) => (d.id === id ? { ...d, status } : d)));
|
selected,
|
||||||
}
|
setSelected,
|
||||||
|
setPage,
|
||||||
async function addDSP(dsp: Omit<DSP, 'id' | 'status' | 'state' | 'meters'>) {
|
notify
|
||||||
try {
|
);
|
||||||
const id = await invoke<number>('create_dsp408', {
|
|
||||||
ip: dsp.ip,
|
|
||||||
port: dsp.port,
|
|
||||||
deviceId: Number(dsp.deviceId),
|
|
||||||
});
|
|
||||||
|
|
||||||
const newDsp: DSP = {
|
|
||||||
id,
|
|
||||||
...dsp,
|
|
||||||
status: 'disconnected',
|
|
||||||
state: null,
|
|
||||||
meters: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
setDsps((prev) => [...prev, newDsp]);
|
|
||||||
setSelected(id);
|
|
||||||
setPage('view-dsp');
|
|
||||||
|
|
||||||
notify('Added successfully', 'success', dsp.name);
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeDSP(dsp: DSP) {
|
|
||||||
try {
|
|
||||||
let id = dsp.id;
|
|
||||||
await invoke('remove_dsp408', {
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
setDsps((prev) => {
|
|
||||||
const next = prev.filter((d) => d.id !== id);
|
|
||||||
|
|
||||||
if (selected === id) {
|
|
||||||
setSelected(next.length ? next[0].id : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
|
|
||||||
notify('Removed successfully', 'success', dsp?.name);
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Remove failed: ${String(err)}`, 'error', dsp?.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function connectDSP(dsp: DSP) {
|
|
||||||
setStatus(dsp.id, 'connecting');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await connectDSP408(dsp.id);
|
|
||||||
|
|
||||||
setStatus(dsp.id, 'connected');
|
|
||||||
|
|
||||||
const state = await getDSP408State(dsp.id);
|
|
||||||
|
|
||||||
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, state } : d)));
|
|
||||||
|
|
||||||
notify('Connected', 'success', dsp.name);
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
|
|
||||||
setStatus(dsp.id, 'disconnected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function disconnectDSP(dsp: DSP) {
|
|
||||||
try {
|
|
||||||
await disconnectDSP408(dsp.id);
|
|
||||||
|
|
||||||
setStatus(dsp.id, 'disconnected');
|
|
||||||
|
|
||||||
notify('Disconnected', 'success', dsp.name);
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Disconnect failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
@ -128,7 +47,7 @@ function App() {
|
|||||||
setPage('view-dsp');
|
setPage('view-dsp');
|
||||||
}}
|
}}
|
||||||
|
|
||||||
onRemove={removeDSP}
|
onRemove={dsp408.removeDSP}
|
||||||
|
|
||||||
onAddClick={() => {
|
onAddClick={() => {
|
||||||
setPage('add-dsp');
|
setPage('add-dsp');
|
||||||
@ -139,13 +58,11 @@ function App() {
|
|||||||
{page === 'credits' ? (
|
{page === 'credits' ? (
|
||||||
<CreditsPage />
|
<CreditsPage />
|
||||||
) : page === 'add-dsp' ? (
|
) : page === 'add-dsp' ? (
|
||||||
<AddDSPForm onCancel={() => setPage('home')} onAdd={addDSP} />
|
<AddDSPForm onCancel={() => setPage('home')} onAdd={dsp408.addDSP} />
|
||||||
) : activeDsp ? (
|
) : activeDsp ? (
|
||||||
<DSPPage
|
<DSPPage
|
||||||
dsp={activeDsp}
|
dsp={activeDsp}
|
||||||
onConnect={() => connectDSP(activeDsp)}
|
dsp408={dsp408.forDSP(activeDsp)}
|
||||||
onDisconnect={() => disconnectDSP(activeDsp)}
|
|
||||||
notify={notify}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<HomePage
|
<HomePage
|
||||||
|
|||||||
@ -16,10 +16,16 @@ export const disconnectDSP408 = (id: number) => invoke('disconnect_dsp408', { id
|
|||||||
|
|
||||||
export const getDSP408State = (id: number) => invoke<DSPState>('get_dsp_state', { id });
|
export const getDSP408State = (id: number) => invoke<DSPState>('get_dsp_state', { id });
|
||||||
|
|
||||||
export const recallPreset = (id: number, presetIndex: number) =>
|
export const recallPresetDSP408 = (id: number, presetIndex: number) =>
|
||||||
invoke<boolean>('recall_preset', {
|
invoke<boolean>('recall_preset', {
|
||||||
id,
|
id,
|
||||||
presetIndex,
|
presetIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const DSP408SetCurrentPresetName = (id: number, name: string) =>
|
||||||
|
invoke<boolean>('set_current_preset_name', {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
|
||||||
export const getDSP408MeterLevels = (id: number) => invoke<Meters>('get_meter_levels', { id });
|
export const getDSP408MeterLevels = (id: number) => invoke<Meters>('get_meter_levels', { id });
|
||||||
|
|||||||
@ -50,6 +50,7 @@ function AppCombobox({
|
|||||||
onAppRename?: (name: string) => void;
|
onAppRename?: (name: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const currentOption = appOptions.find((o) => o.id === selectedAppId) ?? appOptions[0];
|
const currentOption = appOptions.find((o) => o.id === selectedAppId) ?? appOptions[0];
|
||||||
|
const canRename = currentOption?.id !== 'f00';
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
@ -91,8 +92,12 @@ function AppCombobox({
|
|||||||
|
|
||||||
function startEditing(e: React.MouseEvent) {
|
function startEditing(e: React.MouseEvent) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!canRename) return;
|
||||||
|
|
||||||
setEditing(true);
|
setEditing(true);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
inputRef.current?.select();
|
inputRef.current?.select();
|
||||||
@ -100,17 +105,30 @@ function AppCombobox({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function commitRename() {
|
function commitRename() {
|
||||||
const trimmed = inputValue.trim();
|
if (!canRename) {
|
||||||
|
setInputValue(currentOption?.name ?? appName);
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = inputValue.trim();
|
||||||
|
|
||||||
|
setEditing(false);
|
||||||
|
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setInputValue(currentOption?.name ?? appName);
|
setInputValue(currentOption?.name ?? appName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const matched = appOptions.find((o) => o.name === trimmed);
|
const matched = appOptions.find((o) => o.name === trimmed);
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
if (matched.id !== selectedAppId) onAppChange?.(matched.id);
|
if (matched.id !== selectedAppId) {
|
||||||
|
onAppChange?.(matched.id);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed !== currentOption?.name) {
|
if (trimmed !== currentOption?.name) {
|
||||||
onAppRename?.(trimmed);
|
onAppRename?.(trimmed);
|
||||||
}
|
}
|
||||||
@ -153,7 +171,8 @@ function AppCombobox({
|
|||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className="sidebar-app-input"
|
className="sidebar-app-input"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => setInputValue(e.target.value)}
|
maxLength={14}
|
||||||
|
onChange={(e) => setInputValue(e.target.value.slice(0, 14))}
|
||||||
onBlur={commitRename}
|
onBlur={commitRename}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
aria-label="Rename app"
|
aria-label="Rename app"
|
||||||
|
|||||||
@ -57,7 +57,7 @@ function ConnectionPanel({
|
|||||||
|
|
||||||
<div className="connection-panel__spec">
|
<div className="connection-panel__spec">
|
||||||
<dt>Device ID</dt>
|
<dt>Device ID</dt>
|
||||||
<dd className="connection-panel__mono" title={dsp.deviceId || undefined}>
|
<dd className="connection-panel__mono" title={String(dsp.deviceId) || undefined}>
|
||||||
{dsp.deviceId || '—'}
|
{dsp.deviceId || '—'}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
553
src/hooks/useDSP408.ts
Normal file
553
src/hooks/useDSP408.ts
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
// hooks/useDSP408.ts
|
||||||
|
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
|
import { DSP, DSPStatus, AppPage } from '../types/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createDSP408,
|
||||||
|
removeDSP408,
|
||||||
|
connectDSP408,
|
||||||
|
disconnectDSP408,
|
||||||
|
getDSP408State,
|
||||||
|
recallPresetDSP408,
|
||||||
|
DSP408SetCurrentPresetName,
|
||||||
|
getDSP408MeterLevels,
|
||||||
|
} from '../api/dsp408';
|
||||||
|
|
||||||
|
type NotifyFunction = (
|
||||||
|
message: string,
|
||||||
|
type: 'success' | 'error',
|
||||||
|
dspName?: string
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
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))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DSP408Device = {
|
||||||
|
connect: () => Promise<void>;
|
||||||
|
disconnect: () => Promise<void>;
|
||||||
|
recallPreset: (index: number) => Promise<boolean>;
|
||||||
|
setCurrentPresetName: (name: string) => Promise<boolean>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Polling
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
function useDSPPolling(
|
||||||
|
dsps: DSP[],
|
||||||
|
selected: number | null,
|
||||||
|
setDsps: React.Dispatch<React.SetStateAction<DSP[]>>
|
||||||
|
) {
|
||||||
|
const dspsRef = useRef(dsps);
|
||||||
|
const selectedRef = useRef(selected);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dspsRef.current = dsps;
|
||||||
|
}, [dsps]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
selectedRef.current = selected;
|
||||||
|
}, [selected]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let stopped = false;
|
||||||
|
let activeTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
let inactiveTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
|
||||||
|
const pollDSP = async (dsp: DSP) => {
|
||||||
|
// Don't poll paused DSPs
|
||||||
|
if (dsp.pollingPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const meters = await getDSP408MeterLevels(dsp.id);
|
||||||
|
|
||||||
|
if (stopped) return;
|
||||||
|
|
||||||
|
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, meters } : d)));
|
||||||
|
} catch (err) {
|
||||||
|
if (!stopped) {
|
||||||
|
console.error(`Meter polling failed for ${dsp.name}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeLoop = async () => {
|
||||||
|
if (stopped) return;
|
||||||
|
|
||||||
|
const activeId = selectedRef.current;
|
||||||
|
|
||||||
|
if (activeId !== null) {
|
||||||
|
const dsp = dspsRef.current.find(
|
||||||
|
(d) => d.id === activeId && d.status === 'connected' && !d.pollingPaused
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dsp) {
|
||||||
|
await pollDSP(dsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stopped) {
|
||||||
|
activeTimeout = setTimeout(activeLoop, 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const inactiveLoop = async () => {
|
||||||
|
if (stopped) return;
|
||||||
|
|
||||||
|
const activeId = selectedRef.current;
|
||||||
|
|
||||||
|
const inactiveDsps = dspsRef.current.filter(
|
||||||
|
(dsp) => dsp.status === 'connected' && dsp.id !== activeId && !dsp.pollingPaused
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(inactiveDsps.map((dsp) => pollDSP(dsp)));
|
||||||
|
|
||||||
|
if (!stopped) {
|
||||||
|
inactiveTimeout = setTimeout(inactiveLoop, 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start polling
|
||||||
|
activeLoop();
|
||||||
|
inactiveLoop();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
stopped = true;
|
||||||
|
|
||||||
|
if (activeTimeout !== undefined) {
|
||||||
|
clearTimeout(activeTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inactiveTimeout !== undefined) {
|
||||||
|
clearTimeout(inactiveTimeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [setDsps]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Main hook
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export function useDSP408(
|
||||||
|
dsps: DSP[],
|
||||||
|
setDsps: SetDsps,
|
||||||
|
selected: number | null,
|
||||||
|
setSelected: React.Dispatch<React.SetStateAction<number | null>>,
|
||||||
|
setPage: React.Dispatch<React.SetStateAction<AppPage>>,
|
||||||
|
notify: NotifyFunction
|
||||||
|
) {
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// Polling control
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
const setPollingPaused = (id: number, paused: boolean) => {
|
||||||
|
updateDSP(setDsps, id, (dsp) => ({
|
||||||
|
...dsp,
|
||||||
|
pollingPaused: paused,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Polling is part of the hook.
|
||||||
|
useDSPPolling(dsps, selected, setDsps);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// Generic operation wrapper
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
async function withPollingPaused<T>(
|
||||||
|
dsp: DSP,
|
||||||
|
operation: () => Promise<T>
|
||||||
|
): Promise<T> {
|
||||||
|
setPollingPaused(dsp.id, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await operation();
|
||||||
|
} finally {
|
||||||
|
setPollingPaused(dsp.id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Connection
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function connectDSP(dsp: DSP) {
|
||||||
|
setPollingPaused(dsp.id, true);
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => ({
|
||||||
|
...d,
|
||||||
|
status: 'connecting',
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connectDSP408(dsp.id);
|
||||||
|
|
||||||
|
const state = await getDSP408State(dsp.id);
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => ({
|
||||||
|
...d,
|
||||||
|
status: 'connected',
|
||||||
|
state,
|
||||||
|
}));
|
||||||
|
|
||||||
|
notify('Connected', 'success', dsp.name);
|
||||||
|
} catch (err) {
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => ({
|
||||||
|
...d,
|
||||||
|
status: 'disconnected',
|
||||||
|
}));
|
||||||
|
|
||||||
|
notify(
|
||||||
|
`Connection failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setPollingPaused(dsp.id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function disconnectDSP(dsp: DSP) {
|
||||||
|
setPollingPaused(dsp.id, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await disconnectDSP408(dsp.id);
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => ({
|
||||||
|
...d,
|
||||||
|
status: 'disconnected',
|
||||||
|
}));
|
||||||
|
|
||||||
|
notify('Disconnected', 'success', dsp.name);
|
||||||
|
} catch (err) {
|
||||||
|
// If disconnect failed, it is probably still connected.
|
||||||
|
setPollingPaused(dsp.id, false);
|
||||||
|
|
||||||
|
notify(
|
||||||
|
`Disconnect failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Add / Remove
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function addDSP(
|
||||||
|
dsp: Omit<
|
||||||
|
DSP,
|
||||||
|
'id' | 'status' | 'state' | 'meters' | 'pollingPaused'
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const id = await createDSP408(
|
||||||
|
dsp.ip,
|
||||||
|
dsp.port,
|
||||||
|
dsp.deviceId
|
||||||
|
);
|
||||||
|
|
||||||
|
const newDsp: DSP = {
|
||||||
|
id,
|
||||||
|
...dsp,
|
||||||
|
status: 'disconnected',
|
||||||
|
state: null,
|
||||||
|
meters: null,
|
||||||
|
pollingPaused: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
setDsps((prev) => [...prev, newDsp]);
|
||||||
|
setSelected(id);
|
||||||
|
setPage('view-dsp');
|
||||||
|
|
||||||
|
notify('Added successfully', 'success', dsp.name);
|
||||||
|
} catch (err) {
|
||||||
|
notify(
|
||||||
|
`Connection failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeDSP(dsp: DSP) {
|
||||||
|
setPollingPaused(dsp.id, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await removeDSP408(dsp.id);
|
||||||
|
|
||||||
|
setDsps((prev) => {
|
||||||
|
const next = prev.filter(
|
||||||
|
(d) => d.id !== dsp.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected === dsp.id) {
|
||||||
|
setSelected(
|
||||||
|
next.length ? next[0].id : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
|
||||||
|
notify(
|
||||||
|
'Removed successfully',
|
||||||
|
'success',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
setPollingPaused(dsp.id, false);
|
||||||
|
|
||||||
|
notify(
|
||||||
|
`Remove failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Presets
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function recallPreset(
|
||||||
|
dsp: DSP,
|
||||||
|
presetIndex: number
|
||||||
|
) {
|
||||||
|
return withPollingPaused(dsp, async () => {
|
||||||
|
try {
|
||||||
|
const success = await recallPresetDSP408(
|
||||||
|
dsp.id,
|
||||||
|
presetIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
notify(
|
||||||
|
'Failed to recall preset',
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => {
|
||||||
|
if (!d.state) return d;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
presets: {
|
||||||
|
...d.state.presets,
|
||||||
|
current_index: presetIndex - 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
notify(
|
||||||
|
`Preset ${presetIndex} recalled`,
|
||||||
|
'success',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
notify(
|
||||||
|
`Preset recall failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setCurrentPresetName(
|
||||||
|
dsp: DSP,
|
||||||
|
name: string
|
||||||
|
) {
|
||||||
|
return withPollingPaused(dsp, async () => {
|
||||||
|
try {
|
||||||
|
const paddedName = name
|
||||||
|
.slice(0, 14)
|
||||||
|
.padEnd(14, ' ');
|
||||||
|
|
||||||
|
const success =
|
||||||
|
await DSP408SetCurrentPresetName(
|
||||||
|
dsp.id,
|
||||||
|
paddedName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
notify(
|
||||||
|
'Failed to rename preset',
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => {
|
||||||
|
if (!d.state) return d;
|
||||||
|
|
||||||
|
const presets = d.state.presets;
|
||||||
|
const index = presets.current_index;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
presets: {
|
||||||
|
...presets,
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
notify(
|
||||||
|
`Preset rename failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Gain
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
async function setGain(
|
||||||
|
dsp: DSP,
|
||||||
|
channel: number,
|
||||||
|
gain: number
|
||||||
|
) {
|
||||||
|
return withPollingPaused(dsp, async () => {
|
||||||
|
try {
|
||||||
|
const success = await invoke<boolean>(
|
||||||
|
'set_gain',
|
||||||
|
{
|
||||||
|
id: dsp.id,
|
||||||
|
channel,
|
||||||
|
gain,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
notify(
|
||||||
|
'Failed to set gain',
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDSP(setDsps, dsp.id, (d) => {
|
||||||
|
if (!d.state) return d;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
state: {
|
||||||
|
...d.state,
|
||||||
|
// Update gain state here
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
notify(
|
||||||
|
'Gain updated',
|
||||||
|
'success',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
notify(
|
||||||
|
`Gain update failed: ${String(err)}`,
|
||||||
|
'error',
|
||||||
|
dsp.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Return API
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
forDSP,
|
||||||
|
|
||||||
|
addDSP,
|
||||||
|
removeDSP,
|
||||||
|
|
||||||
|
connectDSP,
|
||||||
|
disconnectDSP,
|
||||||
|
|
||||||
|
recallPreset,
|
||||||
|
setCurrentPresetName,
|
||||||
|
|
||||||
|
setGain,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,82 +0,0 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
|
||||||
import { DSP } from '../types/types';
|
|
||||||
import { getDSP408MeterLevels } from '../api/dsp408';
|
|
||||||
|
|
||||||
export function useDSPPolling(
|
|
||||||
dsps: DSP[],
|
|
||||||
selected: number | null,
|
|
||||||
setDsps: React.Dispatch<React.SetStateAction<DSP[]>>
|
|
||||||
) {
|
|
||||||
const dspsRef = useRef(dsps);
|
|
||||||
const selectedRef = useRef(selected);
|
|
||||||
|
|
||||||
// Keep refs up to date without restarting the polling loops
|
|
||||||
useEffect(() => {
|
|
||||||
dspsRef.current = dsps;
|
|
||||||
}, [dsps]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
selectedRef.current = selected;
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let stopped = false;
|
|
||||||
|
|
||||||
const pollDSP = async (dsp: DSP) => {
|
|
||||||
try {
|
|
||||||
const meters = await getDSP408MeterLevels(dsp.id);
|
|
||||||
|
|
||||||
if (stopped) return;
|
|
||||||
|
|
||||||
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, meters } : d)));
|
|
||||||
} catch (err) {
|
|
||||||
if (!stopped) {
|
|
||||||
console.error(`Meter polling failed for ${dsp.name}:`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeLoop = async () => {
|
|
||||||
if (stopped) return;
|
|
||||||
|
|
||||||
const activeId = selectedRef.current;
|
|
||||||
|
|
||||||
if (activeId !== null) {
|
|
||||||
const dsp = dspsRef.current.find((d) => d.id === activeId && d.status === 'connected');
|
|
||||||
|
|
||||||
if (dsp) {
|
|
||||||
await pollDSP(dsp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stopped) {
|
|
||||||
setTimeout(activeLoop, 500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const inactiveLoop = async () => {
|
|
||||||
if (stopped) return;
|
|
||||||
|
|
||||||
const activeId = selectedRef.current;
|
|
||||||
|
|
||||||
const inactiveDsps = dspsRef.current.filter(
|
|
||||||
(dsp) => dsp.status === 'connected' && dsp.id !== activeId
|
|
||||||
);
|
|
||||||
|
|
||||||
// Poll inactive connected DSPs
|
|
||||||
await Promise.all(inactiveDsps.map((dsp) => pollDSP(dsp)));
|
|
||||||
|
|
||||||
if (!stopped) {
|
|
||||||
setTimeout(inactiveLoop, 5000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start both loops
|
|
||||||
activeLoop();
|
|
||||||
inactiveLoop();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
stopped = true;
|
|
||||||
};
|
|
||||||
}, [setDsps]);
|
|
||||||
}
|
|
||||||
@ -10,18 +10,22 @@ function AddDSPForm({
|
|||||||
onAdd,
|
onAdd,
|
||||||
}: {
|
}: {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onAdd: (d: Omit<DSP, 'status' | 'id' | 'state' | 'meters'>) => void;
|
onAdd: (d: Omit<DSP, 'status' | 'id' | 'state' | 'meters' | 'pollingPaused'>) => void;
|
||||||
}) {
|
}) {
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
type: DSP_TYPES[0].name,
|
type: DSP_TYPES[0].name,
|
||||||
ip: '192.168.1.100',
|
ip: '192.168.1.100',
|
||||||
port: DSP_TYPES[0].defaultPort,
|
port: DSP_TYPES[0].defaultPort,
|
||||||
deviceId: '1',
|
deviceId: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
form.name.trim().length > 0 && form.ip.trim().length > 0 && form.deviceId.length > 0;
|
form.name.trim().length > 0 &&
|
||||||
|
form.ip.trim().length > 0 &&
|
||||||
|
Number.isInteger(form.deviceId) &&
|
||||||
|
form.deviceId >= 1 &&
|
||||||
|
form.deviceId <= 255;
|
||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
if (!canSubmit) return;
|
if (!canSubmit) return;
|
||||||
@ -82,7 +86,7 @@ function AddDSPForm({
|
|||||||
step={1}
|
step={1}
|
||||||
placeholder="1"
|
placeholder="1"
|
||||||
value={form.deviceId}
|
value={form.deviceId}
|
||||||
onChange={(e) => setForm({ ...form, deviceId: e.target.value })}
|
onChange={(e) => setForm({ ...form, deviceId: Number(e.target.value) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,27 +4,25 @@ import Sidebar from '../../components/Sidebar/Sidebar';
|
|||||||
import { InfoIcon, KnobGainIcon } from '../../assets/icons/AudioIcons';
|
import { InfoIcon, KnobGainIcon } from '../../assets/icons/AudioIcons';
|
||||||
import ConnectionPanel from '../../components/dsp408/ConnectionSection/ConnectionSection';
|
import ConnectionPanel from '../../components/dsp408/ConnectionSection/ConnectionSection';
|
||||||
import GainSection from '../../components/dsp408/GainSection/GainSection';
|
import GainSection from '../../components/dsp408/GainSection/GainSection';
|
||||||
import { NotificationType } from '../../types/types';
|
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 { invoke } from '@tauri-apps/api/core';
|
|
||||||
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
|
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
|
||||||
|
import { InputChannel, OutputChannel } from '../../types/dsp408State';
|
||||||
|
|
||||||
function DSPPage({
|
function DSPPage({
|
||||||
dsp,
|
dsp,
|
||||||
onConnect,
|
dsp408,
|
||||||
onDisconnect,
|
|
||||||
notify,
|
|
||||||
}: {
|
}: {
|
||||||
dsp: DSP;
|
dsp: DSP;
|
||||||
onConnect: () => void;
|
dsp408: DSP408Device;
|
||||||
onDisconnect: () => void;
|
|
||||||
notify: (message: string, type?: NotificationType, dspName?: string) => void;
|
|
||||||
}) {
|
}) {
|
||||||
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);
|
||||||
const [presetLoading, setPresetLoading] = useState(false);
|
const [presetLoading, setPresetLoading] = useState(false);
|
||||||
|
const inputStates = dsp.state?.current_config.input_states;
|
||||||
|
const outputStates = dsp.state?.current_config.output_states;
|
||||||
const sidebarItems = [
|
const sidebarItems = [
|
||||||
{
|
{
|
||||||
id: 'overview',
|
id: 'overview',
|
||||||
@ -68,62 +66,63 @@ function DSPPage({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ina',
|
id: 'ina',
|
||||||
label: 'In A',
|
label: `In A (${inputStates?.[InputChannel.InA]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'inb',
|
id: 'inb',
|
||||||
label: 'In B',
|
label: `In B (${inputStates?.[InputChannel.InB]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'inc',
|
id: 'inc',
|
||||||
label: 'In C',
|
label: `In C (${inputStates?.[InputChannel.InC]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ind',
|
id: 'ind',
|
||||||
label: 'In D (TESTTEST)',
|
label: `In D (${inputStates?.[InputChannel.InD]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'out1',
|
id: 'out1',
|
||||||
label: 'Out 1',
|
label: `Out 1 (${outputStates?.[OutputChannel.Out1]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out2',
|
id: 'out2',
|
||||||
label: 'Out 2',
|
label: `Out 2 (${outputStates?.[OutputChannel.Out2]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out3',
|
id: 'out3',
|
||||||
label: 'Out 3',
|
label: `Out 3 (${outputStates?.[OutputChannel.Out3]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out4',
|
id: 'out4',
|
||||||
label: 'Out 4',
|
label: `Out 4 (${outputStates?.[OutputChannel.Out4]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out5',
|
id: 'out5',
|
||||||
label: 'Out 5',
|
label: `Out 5 (${outputStates?.[OutputChannel.Out5]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out6',
|
id: 'out6',
|
||||||
label: 'Out 6',
|
label: `Out 6 (${outputStates?.[OutputChannel.Out6]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out7',
|
id: 'out7',
|
||||||
label: 'Out 7',
|
label: `Out 7 (${outputStates?.[OutputChannel.Out7]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'out8',
|
id: 'out8',
|
||||||
label: 'Out 8',
|
label: `Out 8 (${outputStates?.[OutputChannel.Out8]?.name})`,
|
||||||
icon: <KnobGainIcon />,
|
icon: <KnobGainIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -154,52 +153,14 @@ function DSPPage({
|
|||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
const recallPreset = async (presetIndex: number) => {
|
// const { recallPreset, setCurrentPresetName } = useDSPPresets(dsp, setDsps, notify);
|
||||||
try {
|
|
||||||
const success = await invoke<boolean>('recall_preset', {
|
|
||||||
id: dsp.id,
|
|
||||||
presetIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
notify(`Preset ${presetIndex} recalled`, 'success', dsp.name);
|
|
||||||
} else {
|
|
||||||
notify('Failed to recall preset', 'error', dsp.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Preset recall failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCurrentPresetName = async (name: string) => {
|
|
||||||
try {
|
|
||||||
const success = await invoke<boolean>('set_current_preset_name', {
|
|
||||||
id: dsp.id,
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
notify(`Preset renamed`, 'success', dsp.name);
|
|
||||||
} else {
|
|
||||||
notify('Failed to rename preset', 'error', dsp.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
} catch (err) {
|
|
||||||
notify(`Preset rename failed: ${String(err)}`, 'error', dsp.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAppChange = async (id: string) => {
|
const onAppChange = async (id: string) => {
|
||||||
const presetIndex = presets.findIndex((preset) => preset.id === id);
|
const presetIndex = presets.findIndex((preset) => preset.id === id);
|
||||||
if (presetIndex === -1) return;
|
if (presetIndex === -1) return;
|
||||||
|
|
||||||
setPresetLoading(true);
|
setPresetLoading(true);
|
||||||
const success = await recallPreset(presetIndex);
|
const success = await dsp408.recallPreset(presetIndex);
|
||||||
if (success) {
|
if (success) {
|
||||||
setSelectedPreset(presetIndex);
|
setSelectedPreset(presetIndex);
|
||||||
}
|
}
|
||||||
@ -209,13 +170,13 @@ function DSPPage({
|
|||||||
const renderTab = () => {
|
const renderTab = () => {
|
||||||
switch (activeSidebar) {
|
switch (activeSidebar) {
|
||||||
case 'overview':
|
case 'overview':
|
||||||
return <ConnectionPanel dsp={dsp} onConnect={onConnect} onDisconnect={onDisconnect} />;
|
return <ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />;
|
||||||
|
|
||||||
case 'gain':
|
// case 'gain':
|
||||||
return <GainSection dsp={dsp} notify={notify} />;
|
// return <GainSection dsp={dsp} notify={notify} />;
|
||||||
|
|
||||||
case 'gate':
|
// case 'gate':
|
||||||
return <GateSection dsp={dsp} notify={notify} />;
|
// return <GateSection dsp={dsp} notify={notify} />;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@ -232,7 +193,7 @@ function DSPPage({
|
|||||||
appOptions={presets}
|
appOptions={presets}
|
||||||
selectedAppId={presets[selectedPreset]?.id}
|
selectedAppId={presets[selectedPreset]?.id}
|
||||||
onAppChange={onAppChange}
|
onAppChange={onAppChange}
|
||||||
onAppRename={setCurrentPresetName}
|
onAppRename={dsp408.setCurrentPresetName}
|
||||||
expanded={sidebarExpanded}
|
expanded={sidebarExpanded}
|
||||||
onToggle={() => setSidebarExpanded((v) => !v)}
|
onToggle={() => setSidebarExpanded((v) => !v)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -8,10 +8,11 @@ export type DSP = {
|
|||||||
type: string;
|
type: string;
|
||||||
ip: string;
|
ip: string;
|
||||||
port: number;
|
port: number;
|
||||||
deviceId: string;
|
deviceId: number;
|
||||||
status: DSPStatus;
|
status: DSPStatus;
|
||||||
state: DSPState | null;
|
state: DSPState | null;
|
||||||
meters: Meters | null;
|
meters: Meters | null;
|
||||||
|
pollingPaused: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DSP_TYPES = [
|
export const DSP_TYPES = [
|
||||||
|
|||||||
Reference in New Issue
Block a user