feat: add delay tab

This commit is contained in:
2026-08-06 20:51:38 +02:00
parent e802067056
commit 470b95ace3
10 changed files with 662 additions and 47 deletions
+31
View File
@@ -1,3 +1,4 @@
use dsp_thomann::dsp408::types::DelayUnit;
use tauri::State; use tauri::State;
use crate::state::{AppState, DSPDevice}; use crate::state::{AppState, DSPDevice};
use dsp_thomann::dsp408::{DSP408, types}; use dsp_thomann::dsp408::{DSP408, types};
@@ -308,3 +309,33 @@ pub async fn set_limiter(
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
}) })
} }
#[tauri::command]
pub async fn set_delay_unit(
state: State<'_, AppState>,
id: u64,
unit: types::DelayUnit,
) -> Result<bool, String> {
state.with_device_mut(id, |device| {
device
.dsp
.set_delay_unit(unit)
.map_err(|e| e.to_string())
})
}
#[tauri::command]
pub async fn set_delay(
state: State<'_, AppState>,
id: u64,
channel: types::Channel,
delay: f32,
unit: DelayUnit
) -> Result<bool, String> {
state.with_device_mut(id, |device| {
device
.dsp
.set_delay(channel, delay, unit)
.map_err(|e| e.to_string())
})
}
+2
View File
@@ -23,6 +23,8 @@ pub fn run() {
commands::set_gate, commands::set_gate,
commands::set_compressor, commands::set_compressor,
commands::set_limiter, commands::set_limiter,
commands::set_delay_unit,
commands::set_delay,
] ]
) )
.run(tauri::generate_context!()) .run(tauri::generate_context!())
+11
View File
@@ -45,6 +45,12 @@
} }
/* Active toggle states */ /* Active toggle states */
.btn-toggle.active-info {
background: var(--accent-brand);
border-color: var(--accent-brand);
color: var(--bg-void);
}
.btn-toggle.active-danger { .btn-toggle.active-danger {
background: var(--accent-down); background: var(--accent-down);
border-color: var(--accent-down); border-color: var(--accent-down);
@@ -58,6 +64,11 @@
} }
/* Optional: active state hover */ /* Optional: active state hover */
.btn-toggle.active-info:hover:not(:disabled) {
background: var(--accent-brand);
border-color: var(--accent-brand);
}
.btn-toggle.active-danger:hover:not(:disabled) { .btn-toggle.active-danger:hover:not(:disabled) {
background: var(--accent-down); background: var(--accent-down);
border-color: var(--accent-down); border-color: var(--accent-down);
@@ -29,7 +29,6 @@
.compressor-panel { .compressor-panel {
position: relative; position: relative;
height: 10;
aspect-ratio: 0.5; aspect-ratio: 0.5;
flex: 0 0 auto; flex: 0 0 auto;
@@ -200,7 +200,6 @@ function CompressorPanel({
onLiveValue: (channel: OutputChannel, key: CompressorParamKey, value: number | Ratio) => void; onLiveValue: (channel: OutputChannel, key: CompressorParamKey, value: number | Ratio) => void;
}) { }) {
const outputState = dsp.state.current_config.output_states[channel]; const outputState = dsp.state.current_config.output_states[channel];
// ASSUMPTION: compressor settings live at output_states[channel].compressor
const comp = outputState.compressor as CompressorValues; const comp = outputState.compressor as CompressorValues;
const commit = (overrides: Partial<CompressorValues>) => { const commit = (overrides: Partial<CompressorValues>) => {
@@ -0,0 +1,117 @@
.delay-section {
container-type: size;
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: var(--dsp-control-heigth);
display: flex;
align-items: flex-end;
justify-content: center;
box-sizing: border-box;
background: var(--bg-panel);
z-index: 20;
border-top: var(--border-width) solid var(--border-hairline);
}
.delay-panels {
height: 100%;
display: flex;
gap: var(--space-2);
overflow-x: auto;
}
.delay-panel {
position: relative;
height: 100%;
aspect-ratio: 1 / 4;
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
border: var(--border-width) solid var(--border-hairline);
border-radius: var(--radius-md);
box-sizing: border-box;
gap: 2cqh;
background: var(--bg-void);
padding-bottom: 2cqh;
}
.delay-panel__title {
flex: 0 0 auto;
width: 100%;
text-align: center;
font-family: var(--font-ui);
font-size: 4cqh;
font-weight: var(--font-weight-semibold);
padding-top: 2cqh;
padding-bottom: 2cqh;
border-bottom: var(--border-width) solid var(--border-hairline);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.delay-unit-selector {
height: 100%;
width: 20cqh;
display: flex;
flex-direction: column;
gap: 5cqh;
justify-content: center;
box-sizing: border-box;
}
.delay-linear-graph-section {
flex: 0 0 calc(100% - var(--dsp-control-heigth));
min-height: 0;
display: flex;
flex-direction: row;
gap: 1cqh;
padding: 0.5cqh;
box-sizing: border-box;
background: var(--bg-panel);
}
.delay-graph-panel {
flex: 1;
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
border: var(--border-width) solid var(--border-hairline);
border-radius: var(--radius-md);
}
.delay-graph-title {
flex: 0 0 auto;
text-align: center;
padding: 1cqh;
font-family: var(--font-ui);
font-size: 2.5cqh;
font-weight: var(--font-weight-semibold);
}
.delay-graph-panel > .container {
flex: 1;
min-height: 0;
}
@@ -0,0 +1,368 @@
import VerticalSlider from '../Slider/VerticalSlider';
import { InputChannel, Channel, OutputChannel, DelayUnit } from '../../../types/dsp408State';
import { DSP } from '../../../types/types';
import Button from '../../Button/Button';
import ChannelGroup from '../ChannelGroup/ChannelGroup';
import { LinearGraph } from '../LinearGraph/LinearGraph';
import { useMemo, useState } from 'react';
import './DelaySection.css';
const DELAY_RANGES: Record<DelayUnit, [number, number]> = {
[DelayUnit.Millisecond]: [0, 680.0],
[DelayUnit.Meter]: [0, 233.58],
[DelayUnit.Feet]: [0, 766.329],
};
const UNIT_LABEL: Record<DelayUnit, string> = {
[DelayUnit.Millisecond]: 'ms',
[DelayUnit.Meter]: 'm',
[DelayUnit.Feet]: 'ft',
};
const UNIT_ORDER: DelayUnit[] = [DelayUnit.Millisecond, DelayUnit.Meter, DelayUnit.Feet];
const DELAY_STEPS = 255;
const DELAY_STEP: Record<DelayUnit, number> = {
[DelayUnit.Millisecond]: (680.0 - 0) / (DELAY_STEPS - 1),
[DelayUnit.Meter]: (233.58 - 0) / (DELAY_STEPS - 1),
[DelayUnit.Feet]: (766.329 - 0) / (DELAY_STEPS - 1),
};
type SetDelayFn = (channel: Channel, delay: number) => Promise<boolean>;
function channelKey(channel: Channel): string {
return 'Input' in channel ? `Input:${channel.Input}` : `Output:${channel.Output}`;
}
function DelayPanel({
channel,
dsp,
setDelay,
onLiveValue,
}: {
channel: Channel;
dsp: DSP;
setDelay: SetDelayFn;
onLiveValue: (channel: Channel, value: number) => void;
}) {
function getChannelState(channel: Channel) {
if ('Input' in channel) {
return dsp.state.current_config.input_states[channel.Input];
} else {
return dsp.state.current_config.output_states[channel.Output];
}
}
const state = getChannelState(channel);
const unit = dsp.state.current_config.delay_unit;
const [min, max] = DELAY_RANGES[unit];
return (
<div className="delay-panel">
<div className="delay-panel__title">{state.name}</div>
<VerticalSlider
min={min}
max={max}
step={DELAY_STEP[unit]}
unit={' ' + UNIT_LABEL[unit]}
value={state.delay}
onInput={(value) => onLiveValue(channel, value)}
onChange={(value) => setDelay(channel, value)}
/>
</div>
);
}
function DelayGraph({
title,
rows,
unit,
}: {
title: string;
rows: { label: string; delay: number }[];
unit: DelayUnit;
}) {
const [min, max] = DELAY_RANGES[unit];
const labelByX = useMemo(() => {
const map = new Map<number, string>();
rows.forEach((row, i) => map.set(i + 1, row.label));
return map;
}, [rows]);
return (
<div className="delay-graph-panel">
<div className="delay-graph-title">{title}</div>
<LinearGraph
xMin={0.5}
xMax={rows.length + 0.5}
xStep={1}
yMin={min}
yMax={max}
xLabel="Channel"
yLabel={UNIT_LABEL[unit]}
showGrid={true}
formatX={(v) => labelByX.get(Math.round(v)) ?? ''}
formatY={(v) => v.toFixed(0)}
draw={({ ctx, toPx, plot }) => {
const barWidth = (plot.width / rows.length) * 0.55;
rows.forEach((row, i) => {
const x = i + 1;
const [xPx, baseY] = toPx(x, min);
const [, topY] = toPx(x, row.delay);
ctx.fillStyle = '#4ade80';
ctx.fillRect(xPx - barWidth / 2, topY, barWidth, baseY - topY);
});
}}
/>
</div>
);
}
function DelayUnitSelector({
unit,
onSelect,
}: {
unit: DelayUnit;
onSelect: (unit: DelayUnit) => void;
}) {
return (
<div className="delay-unit-selector">
{UNIT_ORDER.map((u) => (
<Button
key={u}
variant="toggle"
className={u === unit ? 'active-info' : ''}
onClick={() => onSelect(u)}
style={{
width: '100%',
fontSize: '3.5cqh',
paddingTop: '2cqh',
paddingBottom: '2cqh',
minHeight: 0,
}}
>
{UNIT_LABEL[u]}
</Button>
))}
</div>
);
}
function DelaySection({
dsp,
setDelay,
setDelayUnit,
setDSPState,
}: {
dsp: DSP;
setDelay: SetDelayFn;
setDelayUnit: (unit: DelayUnit) => Promise<boolean>;
setDSPState: (updater: (dsp: DSP) => DSP) => void;
}) {
const unit = dsp.state?.current_config.delay_unit ?? DelayUnit.Millisecond;
const inputs: InputChannel[] = [
InputChannel.InA,
InputChannel.InB,
InputChannel.InC,
InputChannel.InD,
];
const outputs: { channel: Channel; title: string }[] = [
{ channel: { Output: OutputChannel.Out1 }, title: 'Out 1' },
{ channel: { Output: OutputChannel.Out2 }, title: 'Out 2' },
{ channel: { Output: OutputChannel.Out3 }, title: 'Out 3' },
{ channel: { Output: OutputChannel.Out4 }, title: 'Out 4' },
{ channel: { Output: OutputChannel.Out5 }, title: 'Out 5' },
{ channel: { Output: OutputChannel.Out6 }, title: 'Out 6' },
{ channel: { Output: OutputChannel.Out7 }, title: 'Out 7' },
{ channel: { Output: OutputChannel.Out8 }, title: 'Out 8' },
];
const [liveDelay, setLiveDelay] = useState<Partial<Record<string, number>>>({});
const handleLiveValue = (channel: Channel, value: number) => {
setLiveDelay((prev) => ({
...prev,
[channelKey(channel)]: value,
}));
};
const handleSetDelay: SetDelayFn = async (channel, delay) => {
const success = await setDelay(channel, delay);
if (success) {
const key = channelKey(channel);
setLiveDelay((prev) => {
if (!(key in prev)) return prev;
const next = { ...prev };
delete next[key];
return next;
});
}
return success;
};
const committedDelay = (channel: Channel): number =>
'Input' in channel
? dsp.state.current_config.input_states[channel.Input].delay
: dsp.state.current_config.output_states[channel.Output].delay;
const getDelay = (channel: Channel): number => {
const key = channelKey(channel);
if (key in liveDelay) {
return liveDelay[key]!;
}
return committedDelay(channel);
};
function convertDelayUnit(value: number, from: DelayUnit, to: DelayUnit): number {
const [, fromMax] = DELAY_RANGES[from];
const [, toMax] = DELAY_RANGES[to];
if (fromMax === 0) return 0;
const ratio = value / fromMax;
return ratio * toMax;
}
const handleUnitChange = async (newUnit: DelayUnit) => {
if (newUnit === unit) return;
const success = await setDelayUnit(newUnit);
if (!success) return;
setDSPState((d) => {
if (!d.state) return d;
const currentConfig = d.state.current_config;
return {
...d,
state: {
...d.state,
current_config: {
...currentConfig,
delay_unit: newUnit,
input_states: {
...currentConfig.input_states,
[InputChannel.InA]: {
...currentConfig.input_states[InputChannel.InA],
delay: convertDelayUnit(
currentConfig.input_states[InputChannel.InA].delay,
unit,
newUnit
),
},
[InputChannel.InB]: {
...currentConfig.input_states[InputChannel.InB],
delay: convertDelayUnit(
currentConfig.input_states[InputChannel.InB].delay,
unit,
newUnit
),
},
[InputChannel.InC]: {
...currentConfig.input_states[InputChannel.InC],
delay: convertDelayUnit(
currentConfig.input_states[InputChannel.InC].delay,
unit,
newUnit
),
},
[InputChannel.InD]: {
...currentConfig.input_states[InputChannel.InD],
delay: convertDelayUnit(
currentConfig.input_states[InputChannel.InD].delay,
unit,
newUnit
),
},
},
output_states: {
...currentConfig.output_states,
...Object.fromEntries(
Object.values(OutputChannel).map((channel) => [
channel,
{
...currentConfig.output_states[channel],
delay: convertDelayUnit(
currentConfig.output_states[channel].delay,
unit,
newUnit
),
},
])
),
},
},
},
};
});
};
const inputRows = inputs.map((channel) => ({
label: dsp.state.current_config.input_states[channel].name,
delay: getDelay({ Input: channel }),
}));
const outputRows = outputs.map(({ channel, title }) => ({
label: title,
delay: getDelay(channel),
}));
return (
<>
<div className="delay-linear-graph-section">
<DelayGraph title="Input Delay" rows={inputRows} unit={unit} />
<DelayGraph title="Output Delay" rows={outputRows} unit={unit} />
</div>
<div className="delay-section">
<div className="delay-panels">
<ChannelGroup title="Input">
{inputs.map((channel) => (
<DelayPanel
key={channel}
channel={{ Input: channel }}
dsp={dsp}
setDelay={handleSetDelay}
onLiveValue={handleLiveValue}
/>
))}
</ChannelGroup>
<ChannelGroup title="Output">
{outputs.map(({ channel, title }) => (
<DelayPanel
key={title}
channel={channel}
dsp={dsp}
setDelay={handleSetDelay}
onLiveValue={handleLiveValue}
/>
))}
</ChannelGroup>
<ChannelGroup title="Unit">
<DelayUnitSelector unit={unit} onSelect={handleUnitChange} />
</ChannelGroup>
</div>
</div>
</>
);
}
export default DelaySection;
@@ -91,7 +91,11 @@ export default function VerticalSlider({
} }
const decimals = useMemo( const decimals = useMemo(
() => Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0), () =>
Math.min(
3,
Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0)
),
[step, switchStep] [step, switchStep]
); );
+117 -44
View File
@@ -11,6 +11,7 @@ import {
defaultMeters, defaultMeters,
OutputChannel, OutputChannel,
Ratio, Ratio,
DelayUnit,
} from '../types/dsp408State'; } from '../types/dsp408State';
import { import {
@@ -28,15 +29,13 @@ type NotifyFunction = (message: string, type: 'success' | 'error', dspName?: str
type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>; type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
// ============================================================
// Helpers // Helpers
// ============================================================
function updateDSP(setDsps: SetDsps, dspId: number, updater: (dsp: DSP) => DSP) { function updateDSP(setDsps: SetDsps, dspId: number, updater: (dsp: DSP) => DSP) {
setDsps((prev) => prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp))); setDsps((prev) => prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp)));
} }
export type DSP408Device = { export type DSP408Device = {
setDSPState: (updater: (dsp: DSP) => DSP) => void;
connect: () => Promise<void>; connect: () => Promise<void>;
disconnect: () => Promise<void>; disconnect: () => Promise<void>;
recallPreset: (index: number) => Promise<boolean>; recallPreset: (index: number) => Promise<boolean>;
@@ -65,12 +64,11 @@ export type DSP408Device = {
attack: number, attack: number,
release: number release: number
) => Promise<boolean>; ) => Promise<boolean>;
setDelayUnit: (unit: DelayUnit) => Promise<boolean>;
setDelay: (channel: Channel, delay: number) => Promise<boolean>;
}; };
// ============================================================
// Polling // Polling
// ============================================================
function useDSPPolling( function useDSPPolling(
dsps: DSP[], dsps: DSP[],
selected: number | null, selected: number | null,
@@ -165,10 +163,6 @@ function useDSPPolling(
}, [setDsps]); }, [setDsps]);
} }
// ============================================================
// Main hook
// ============================================================
export function useDSP408( export function useDSP408(
dsps: DSP[], dsps: DSP[],
setDsps: SetDsps, setDsps: SetDsps,
@@ -177,10 +171,7 @@ export function useDSP408(
setPage: React.Dispatch<React.SetStateAction<AppPage>>, setPage: React.Dispatch<React.SetStateAction<AppPage>>,
notify: NotifyFunction notify: NotifyFunction
) { ) {
// ----------------------------------------------------------
// Polling control // Polling control
// ----------------------------------------------------------
const setPollingPaused = (id: number, paused: boolean) => { const setPollingPaused = (id: number, paused: boolean) => {
updateDSP(setDsps, id, (dsp) => ({ updateDSP(setDsps, id, (dsp) => ({
...dsp, ...dsp,
@@ -188,13 +179,14 @@ export function useDSP408(
})); }));
}; };
function setDSPState(dsp: DSP, updater: (dsp: DSP) => DSP) {
updateDSP(setDsps, dsp.id, updater);
}
// Polling is part of the hook. // Polling is part of the hook.
useDSPPolling(dsps, selected, setDsps); useDSPPolling(dsps, selected, setDsps);
// ----------------------------------------------------------
// Generic operation wrapper // 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); setPollingPaused(dsp.id, true);
@@ -205,10 +197,7 @@ export function useDSP408(
} }
} }
// ==========================================================
// Connection // Connection
// ==========================================================
async function connectDSP(dsp: DSP) { async function connectDSP(dsp: DSP) {
setPollingPaused(dsp.id, true); setPollingPaused(dsp.id, true);
@@ -261,10 +250,7 @@ export function useDSP408(
} }
} }
// ==========================================================
// Add / Remove // 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 { try {
const id = await createDSP408(dsp.ip, dsp.port, dsp.deviceId); const id = await createDSP408(dsp.ip, dsp.port, dsp.deviceId);
@@ -312,10 +298,7 @@ export function useDSP408(
} }
} }
// ==========================================================
// Presets // Presets
// ==========================================================
async function recallPreset(dsp: DSP, presetIndex: number) { async function recallPreset(dsp: DSP, presetIndex: number) {
return withPollingPaused(dsp, async () => { return withPollingPaused(dsp, async () => {
try { try {
@@ -398,10 +381,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Gain // Gain
// ==========================================================
async function setGain(dsp: DSP, channel: Channel, gain: number): Promise<boolean> { async function setGain(dsp: DSP, channel: Channel, gain: number): Promise<boolean> {
return withPollingPaused(dsp, async () => { return withPollingPaused(dsp, async () => {
try { try {
@@ -435,10 +415,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Mute // Mute
// ==========================================================
async function setMute(dsp: DSP, channel: Channel, muted: boolean): Promise<boolean> { async function setMute(dsp: DSP, channel: Channel, muted: boolean): Promise<boolean> {
return withPollingPaused(dsp, async () => { return withPollingPaused(dsp, async () => {
try { try {
@@ -511,10 +488,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Inverse // Inverse
// ==========================================================
async function setInverse(dsp: DSP, channel: Channel, inverted: boolean): Promise<boolean> { async function setInverse(dsp: DSP, channel: Channel, inverted: boolean): Promise<boolean> {
return withPollingPaused(dsp, async () => { return withPollingPaused(dsp, async () => {
try { try {
@@ -587,9 +561,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Gate // Gate
// ==========================================================
async function setGate( async function setGate(
dsp: DSP, dsp: DSP,
inputChannel: InputChannel, inputChannel: InputChannel,
@@ -641,9 +613,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Compressor // Compressor
// ==========================================================
async function setCompressor( async function setCompressor(
dsp: DSP, dsp: DSP,
outputChannel: OutputChannel, outputChannel: OutputChannel,
@@ -708,9 +678,7 @@ export function useDSP408(
}); });
} }
// ==========================================================
// Limiter // Limiter
// ==========================================================
async function setLimiter( async function setLimiter(
dsp: DSP, dsp: DSP,
outputChannel: OutputChannel, outputChannel: OutputChannel,
@@ -769,12 +737,115 @@ export function useDSP408(
}); });
} }
// ========================================================== // Delay Unit
// Return API async function setDelayUnit(dsp: DSP, unit: DelayUnit): Promise<boolean> {
// ========================================================== return withPollingPaused(dsp, async () => {
try {
const success = await invoke<boolean>('set_delay_unit', {
id: dsp.id,
unit,
});
if (!success) {
notify('Failed to change delay unit', 'error', dsp.name);
return false;
}
updateDSP(setDsps, dsp.id, (d) => {
if (!d.state) return d;
return {
...d,
state: {
...d.state,
current_config: {
...d.state.current_config,
delay_unit: unit,
},
},
};
});
notify('Delay unit updated', 'success', dsp.name);
return true;
} catch (err) {
notify(`Delay unit update failed: ${String(err)}`, 'error', dsp.name);
return false;
}
});
}
// Channel Delay
async function setDelay(dsp: DSP, channel: Channel, delay: number): Promise<boolean> {
return withPollingPaused(dsp, async () => {
try {
const success = await invoke<boolean>('set_delay', {
id: dsp.id,
channel,
delay,
unit: dsp.state.current_config.delay_unit,
});
if (!success) {
notify('Failed to change channel delay', '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) {
return {
...d,
state: {
...d.state,
current_config: {
...currentConfig,
input_states: {
...currentConfig.input_states,
[channel.Input]: {
...currentConfig.input_states[channel.Input],
delay,
},
},
},
},
};
}
return {
...d,
state: {
...d.state,
current_config: {
...currentConfig,
output_states: {
...currentConfig.output_states,
[channel.Output]: {
...currentConfig.output_states[channel.Output],
delay,
},
},
},
},
};
});
notify('Channel delay updated', 'success', dsp.name);
return true;
} catch (err) {
notify(`Channel delay update failed: ${String(err)}`, 'error', dsp.name);
return false;
}
});
}
// Return API
function forDSP(dsp: DSP): DSP408Device { function forDSP(dsp: DSP): DSP408Device {
return { return {
setDSPState: (updater: (dsp: DSP) => DSP) => setDSPState(dsp, updater),
connect: () => connectDSP(dsp), connect: () => connectDSP(dsp),
disconnect: () => disconnectDSP(dsp), disconnect: () => disconnectDSP(dsp),
recallPreset: (index: number) => recallPreset(dsp, index), recallPreset: (index: number) => recallPreset(dsp, index),
@@ -803,6 +874,8 @@ export function useDSP408(
attack: number, attack: number,
release: number release: number
) => setLimiter(dsp, outputChannel, threshold, attack, release), ) => setLimiter(dsp, outputChannel, threshold, attack, release),
setDelayUnit: (unit: DelayUnit) => setDelayUnit(dsp, unit),
setDelay: (channel: Channel, delay: number) => setDelay(dsp, channel, delay),
}; };
} }
@@ -818,6 +891,6 @@ export function useDSP408(
recallPreset, recallPreset,
setCurrentPresetName, setCurrentPresetName,
setGain, setDSPState,
}; };
} }
+11
View File
@@ -9,6 +9,7 @@ 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 LimiterSection from '../../components/dsp408/LimiterSection/LimiterSection'; import LimiterSection from '../../components/dsp408/LimiterSection/LimiterSection';
import DelaySection from '../../components/dsp408/DelaySection/DelaySection';
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay'; import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import { InputChannel, OutputChannel } from '../../types/dsp408State'; import { InputChannel, OutputChannel } from '../../types/dsp408State';
@@ -187,6 +188,16 @@ function DSPPage({ dsp, dsp408 }: { dsp: DSP; dsp408: DSP408Device }) {
case 'limiter': case 'limiter':
return <LimiterSection dsp={dsp} set_limiter={dsp408.setLimiter} />; return <LimiterSection dsp={dsp} set_limiter={dsp408.setLimiter} />;
case 'delay':
return (
<DelaySection
dsp={dsp}
setDelay={dsp408.setDelay}
setDelayUnit={dsp408.setDelayUnit}
setDSPState={dsp408.setDSPState}
/>
);
default: default:
return null; return null;
} }