Compare commits
5
Commits
1792910ca1
...
4f2bce1f5f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f2bce1f5f | ||
|
|
8c0b272ab9 | ||
|
|
772a331f04 | ||
|
|
1643a887af | ||
|
|
8e9a59d026 |
+226
-196
@@ -109,18 +109,6 @@ pub async fn get_dsp_snapshot(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn connect_dsp408(state: State<'_, AppState>, id: u64) -> Result<(), String> {
|
|
||||||
state.with_device_mut(id, |device| device.dsp.connect().map_err(|e| e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn disconnect_dsp408(state: State<'_, AppState>, id: u64) -> Result<(), String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device.dsp.disconnect().map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_dsp_state(
|
pub async fn get_dsp_state(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -133,57 +121,33 @@ pub async fn get_dsp_state(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commands
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_channel_gain(
|
pub async fn connect_dsp408(state: State<'_, AppState>, id: u64) -> Result<(), String> {
|
||||||
state: State<'_, AppState>,
|
state.with_device_mut(id, |device| device.dsp.connect().map_err(|e| e.to_string()))
|
||||||
id: u64,
|
}
|
||||||
channel: dsp_thomann::dsp408::types::Channel,
|
|
||||||
db: f32,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
let r = device
|
|
||||||
.dsp
|
|
||||||
.set_channel_gain(channel, db)
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(r)
|
// Command `0x11`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn disconnect_dsp408(state: State<'_, AppState>, id: u64) -> Result<(), String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device.dsp.disconnect().map_err(|e| e.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x15`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_mute(
|
pub async fn set_delay_unit(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
id: u64,
|
id: u64,
|
||||||
channel: dsp_thomann::dsp408::types::Channel,
|
unit: types::DelayUnit,
|
||||||
muted: bool,
|
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
state.with_device_mut(id, |device| {
|
state.with_device_mut(id, |device| {
|
||||||
let r = device
|
device.dsp.set_delay_unit(unit).map_err(|e| e.to_string())
|
||||||
.dsp
|
|
||||||
.set_mute(channel, muted)
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn set_channel_inverse_gain(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
channel: dsp_thomann::dsp408::types::Channel,
|
|
||||||
inverted: bool,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
let r = device
|
|
||||||
.dsp
|
|
||||||
.set_channel_inverse_gain(channel, inverted)
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(r)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x20`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn recall_preset(
|
pub async fn recall_preset(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -200,16 +164,35 @@ pub async fn recall_preset(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x21` -- TODO
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_meter_levels(
|
pub async fn store_current_configuration(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
id: u64,
|
id: u64,
|
||||||
) -> Result<(types::InputMeters, types::OutputMeters), String> {
|
preset_index: usize,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, String> {
|
||||||
state.with_device_mut(id, |device| {
|
state.with_device_mut(id, |device| {
|
||||||
device.dsp.get_meter_levels().map_err(|e| e.to_string())
|
device
|
||||||
|
.dsp
|
||||||
|
.store_current_configuration(preset_index, force)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x23` -- TODO
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn factory_reset(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device.dsp.factory_reset(force).map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x26`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_current_preset_name(
|
pub async fn set_current_preset_name(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -224,24 +207,26 @@ pub async fn set_current_preset_name(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x2A` -- TODO
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_gate(
|
pub async fn copy_channel(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
id: u64,
|
id: u64,
|
||||||
channel: types::InputChannel,
|
channel_1: types::Channel,
|
||||||
threshold: f32,
|
channel_2: types::Channel,
|
||||||
attack: u16,
|
|
||||||
hold: u16,
|
|
||||||
release: u16,
|
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
state.with_device_mut(id, |device| {
|
state.with_device_mut(id, |device| {
|
||||||
device
|
device
|
||||||
.dsp
|
.dsp
|
||||||
.set_gate(channel, threshold, attack, hold, release)
|
.copy_channel(channel_1, channel_2)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x2D` -- TODO
|
||||||
|
// Command `0x2F` -- TODO
|
||||||
|
|
||||||
|
// Command `0x30`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_compressor(
|
pub async fn set_compressor(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -261,112 +246,7 @@ pub async fn set_compressor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
// Command `0x31`
|
||||||
pub async fn set_limiter(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
channel: types::OutputChannel,
|
|
||||||
threshold: f32,
|
|
||||||
attack: u16,
|
|
||||||
release: u16,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.set_limiter(channel, threshold, attack, release)
|
|
||||||
.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())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn set_matrix_routing(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
output: types::OutputChannel,
|
|
||||||
inputs: Vec<types::InputChannel>,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.set_matrix_routing(output, inputs)
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn set_matrix_gain(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
input_channel: types::InputChannel,
|
|
||||||
output_channel: types::OutputChannel,
|
|
||||||
gain: f32,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.set_matrix_gain(input_channel, output_channel, gain)
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn set_geq_band(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
input_channel: types::InputChannel,
|
|
||||||
frequency_band: types::DiscreteFrequency,
|
|
||||||
gain: f32,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.set_geq_band(input_channel, frequency_band, gain)
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn set_geq_bypass(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
input_channel: types::InputChannel,
|
|
||||||
bypass: bool,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.set_geq_bypass(input_channel, bypass)
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_low_pass(
|
pub async fn set_low_pass(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -383,6 +263,7 @@ pub async fn set_low_pass(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x32`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_high_pass(
|
pub async fn set_high_pass(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -399,6 +280,7 @@ pub async fn set_high_pass(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x33`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_peq_band(
|
pub async fn set_peq_band(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -419,47 +301,78 @@ pub async fn set_peq_band(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x34`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn store_current_configuration(
|
pub async fn set_channel_gain(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
id: u64,
|
id: u64,
|
||||||
preset_index: usize,
|
channel: dsp_thomann::dsp408::types::Channel,
|
||||||
force: bool,
|
db: f32,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
let r = device
|
||||||
|
.dsp
|
||||||
|
.set_channel_gain(channel, db)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x35`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_mute(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
channel: dsp_thomann::dsp408::types::Channel,
|
||||||
|
muted: bool,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
let r = device
|
||||||
|
.dsp
|
||||||
|
.set_mute(channel, muted)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x36`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_channel_inverse_gain(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
channel: dsp_thomann::dsp408::types::Channel,
|
||||||
|
inverted: bool,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
let r = device
|
||||||
|
.dsp
|
||||||
|
.set_channel_inverse_gain(channel, inverted)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x38`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_delay(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
channel: types::Channel,
|
||||||
|
delay: f32,
|
||||||
|
unit: DelayUnit,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
state.with_device_mut(id, |device| {
|
state.with_device_mut(id, |device| {
|
||||||
device
|
device
|
||||||
.dsp
|
.dsp
|
||||||
.store_current_configuration(preset_index, force)
|
.set_delay(channel, delay, unit)
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn factory_reset(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
force: bool,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device.dsp.factory_reset(force).map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn copy_channel(
|
|
||||||
state: State<'_, AppState>,
|
|
||||||
id: u64,
|
|
||||||
channel_1: types::Channel,
|
|
||||||
channel_2: types::Channel,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
state.with_device_mut(id, |device| {
|
|
||||||
device
|
|
||||||
.dsp
|
|
||||||
.copy_channel(channel_1, channel_2)
|
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x39` -- TODO
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_input_source(
|
pub async fn set_input_source(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -474,6 +387,23 @@ pub async fn set_input_source(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x3A`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_matrix_routing(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
output: types::OutputChannel,
|
||||||
|
inputs: Vec<types::InputChannel>,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_matrix_routing(output, inputs)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x3B` -- TODO
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn link_channels(
|
pub async fn link_channels(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -489,6 +419,7 @@ pub async fn link_channels(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x3C`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_bypass_peq(
|
pub async fn set_bypass_peq(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -504,6 +435,7 @@ pub async fn set_bypass_peq(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x3D` -- TODO
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn set_channel_name(
|
pub async fn set_channel_name(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -518,3 +450,101 @@ pub async fn set_channel_name(
|
|||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command `0x3E`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_gate(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
channel: types::InputChannel,
|
||||||
|
threshold: f32,
|
||||||
|
attack: u16,
|
||||||
|
hold: u16,
|
||||||
|
release: u16,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_gate(channel, threshold, attack, hold, release)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x3F`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_limiter(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
channel: types::OutputChannel,
|
||||||
|
threshold: f32,
|
||||||
|
attack: u16,
|
||||||
|
release: u16,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_limiter(channel, threshold, attack, release)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x40`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_meter_levels(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
) -> Result<(types::InputMeters, types::OutputMeters), String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device.dsp.get_meter_levels().map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x41`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_matrix_gain(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
input_channel: types::InputChannel,
|
||||||
|
output_channel: types::OutputChannel,
|
||||||
|
gain: f32,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_matrix_gain(input_channel, output_channel, gain)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x48`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_geq_band(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
input_channel: types::InputChannel,
|
||||||
|
frequency_band: types::DiscreteFrequency,
|
||||||
|
gain: f32,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_geq_band(input_channel, frequency_band, gain)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command `0x49`
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_geq_bypass(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: u64,
|
||||||
|
input_channel: types::InputChannel,
|
||||||
|
bypass: bool,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
state.with_device_mut(id, |device| {
|
||||||
|
device
|
||||||
|
.dsp
|
||||||
|
.set_geq_bypass(input_channel, bypass)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* ChoiceSelect.css */
|
|
||||||
.choice-select {
|
.choice-select {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -11,12 +10,12 @@
|
|||||||
|
|
||||||
.choice-select__input {
|
.choice-select__input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
height: var(--card-control-height);
|
height: var(--card-control-height);
|
||||||
padding: 0 var(--space-6) 0 var(--space-3);
|
padding: 0 var(--space-6) 0 var(--space-3);
|
||||||
|
|
||||||
background: var(--bg-panel);
|
background: transparent;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|
||||||
border: var(--border-width) solid var(--border-hairline);
|
border: var(--border-width) solid var(--border-hairline);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
|
|
||||||
@@ -43,13 +42,24 @@
|
|||||||
|
|
||||||
.choice-select__chevron {
|
.choice-select__chevron {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: var(--space-2);
|
top: 0;
|
||||||
top: 50%;
|
right: 5%;
|
||||||
height: 45%;
|
bottom: 0;
|
||||||
aspect-ratio: 1;
|
|
||||||
transform: translateY(-50%);
|
width: 4cqh;
|
||||||
|
padding-top: 3%;
|
||||||
|
padding-bottom: 3%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: var(--accent-brand);
|
color: var(--accent-brand);
|
||||||
font-size: var(--font-sm);
|
}
|
||||||
|
|
||||||
|
.choice-select__chevron svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.notifications {
|
.notifications {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: var(--space-5);
|
right: var(--space-5);
|
||||||
bottom: var(--space-5);
|
top: var(--space-5);
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ function AppCombobox({
|
|||||||
appOptions: SidebarAppOption[];
|
appOptions: SidebarAppOption[];
|
||||||
selectedAppId?: string;
|
selectedAppId?: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
onAppChange?: (id: string) => void;
|
onAppChange: (id: string) => void;
|
||||||
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 canRename = currentOption?.id !== 'f00';
|
||||||
@@ -95,6 +95,7 @@ function AppCombobox({
|
|||||||
|
|
||||||
if (!canRename) return;
|
if (!canRename) return;
|
||||||
|
|
||||||
|
setInputValue((currentOption?.name ?? appName).trim());
|
||||||
setEditing(true);
|
setEditing(true);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
||||||
@@ -124,13 +125,13 @@ function AppCombobox({
|
|||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
if (matched.id !== selectedAppId) {
|
if (matched.id !== selectedAppId) {
|
||||||
onAppChange?.(matched.id);
|
onAppChange(matched.id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed !== currentOption?.name) {
|
if (trimmed !== currentOption?.name) {
|
||||||
onAppRename?.(trimmed);
|
onAppRename(trimmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +187,7 @@ function AppCombobox({
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
|
title={canRename ? 'Double click to rename' : undefined}
|
||||||
>
|
>
|
||||||
<span className="sidebar-app-trigger-label">{currentOption?.name ?? appName}</span>
|
<span className="sidebar-app-trigger-label">{currentOption?.name ?? appName}</span>
|
||||||
<svg className="sidebar-app-trigger-caret" viewBox="0 0 24 24" width="14" height="14">
|
<svg className="sidebar-app-trigger-caret" viewBox="0 0 24 24" width="14" height="14">
|
||||||
@@ -209,7 +211,8 @@ function AppCombobox({
|
|||||||
onMouseEnter={() => setHighlight(i)}
|
onMouseEnter={() => setHighlight(i)}
|
||||||
onClick={() => pick(opt)}
|
onClick={() => pick(opt)}
|
||||||
>
|
>
|
||||||
{opt.name}
|
<span className="sidebar-app-option-id">{opt.id + '-'}</span>
|
||||||
|
<span>{opt.name}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -236,8 +239,8 @@ export function Sidebar<T extends string>({
|
|||||||
appName: string;
|
appName: string;
|
||||||
appOptions?: SidebarAppOption[];
|
appOptions?: SidebarAppOption[];
|
||||||
selectedAppId?: string;
|
selectedAppId?: string;
|
||||||
onAppChange?: (id: string) => void;
|
onAppChange: (id: string) => void;
|
||||||
onAppRename?: (name: string) => void;
|
onAppRename: (name: string) => void;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import LoadingOverlay from '../../../../components/LoadingOverlay/LoadingOverlay
|
|||||||
import { InputChannel, OutputChannel } from '../../types';
|
import { InputChannel, OutputChannel } from '../../types';
|
||||||
import MatrixSection from '../Matrixsection/Matrixsection';
|
import MatrixSection from '../Matrixsection/Matrixsection';
|
||||||
import GeqSection from '../GeqSection/GeqSection';
|
import GeqSection from '../GeqSection/GeqSection';
|
||||||
import InputSection from '../InputSection/InputSection';
|
import ChannelSection from '../InputSection/ChannelSection';
|
||||||
import { DSP408Api } from '../../dsp408';
|
import { DSP408Api } from '../../dsp408';
|
||||||
import { DSP408 } from '../../../../types';
|
import { DSP408 } from '../../../../types';
|
||||||
import { SidebarId } from '../../constants';
|
import { SidebarId } from '../../constants';
|
||||||
@@ -134,54 +134,6 @@ function DSP408Panel({ dsp, api }: { dsp: DSP408; api: DSP408Api }) {
|
|||||||
case 'delay':
|
case 'delay':
|
||||||
return <DelaySection dsp={dsp} setDelay={api.setDelay} setDelayUnit={api.setDelayUnit} />;
|
return <DelaySection dsp={dsp} setDelay={api.setDelay} setDelayUnit={api.setDelayUnit} />;
|
||||||
|
|
||||||
case 'ina':
|
|
||||||
return (
|
|
||||||
<InputSection
|
|
||||||
dsp={dsp}
|
|
||||||
channel={InputChannel.InA}
|
|
||||||
setLowPass={api.setLowPass}
|
|
||||||
setHighPass={api.setHighPass}
|
|
||||||
setPeq={api.setPeqBand}
|
|
||||||
setPeqBypass={api.setPEQBypass}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'inb':
|
|
||||||
return (
|
|
||||||
<InputSection
|
|
||||||
dsp={dsp}
|
|
||||||
channel={InputChannel.InB}
|
|
||||||
setLowPass={api.setLowPass}
|
|
||||||
setHighPass={api.setHighPass}
|
|
||||||
setPeq={api.setPeqBand}
|
|
||||||
setPeqBypass={api.setPEQBypass}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'inc':
|
|
||||||
return (
|
|
||||||
<InputSection
|
|
||||||
dsp={dsp}
|
|
||||||
channel={InputChannel.InC}
|
|
||||||
setLowPass={api.setLowPass}
|
|
||||||
setHighPass={api.setHighPass}
|
|
||||||
setPeq={api.setPeqBand}
|
|
||||||
setPeqBypass={api.setPEQBypass}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'ind':
|
|
||||||
return (
|
|
||||||
<InputSection
|
|
||||||
dsp={dsp}
|
|
||||||
channel={InputChannel.InD}
|
|
||||||
setLowPass={api.setLowPass}
|
|
||||||
setHighPass={api.setHighPass}
|
|
||||||
setPeq={api.setPeqBand}
|
|
||||||
setPeqBypass={api.setPEQBypass}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'matrix':
|
case 'matrix':
|
||||||
return (
|
return (
|
||||||
<MatrixSection
|
<MatrixSection
|
||||||
@@ -195,6 +147,66 @@ function DSP408Panel({ dsp, api }: { dsp: DSP408; api: DSP408Api }) {
|
|||||||
case 'geq':
|
case 'geq':
|
||||||
return <GeqSection dsp={dsp} setGeqBand={api.setGeqBand} setGeqBypass={api.setGeqBypass} />;
|
return <GeqSection dsp={dsp} setGeqBand={api.setGeqBand} setGeqBypass={api.setGeqBypass} />;
|
||||||
|
|
||||||
|
case 'ina':
|
||||||
|
return (
|
||||||
|
<ChannelSection
|
||||||
|
dsp={dsp}
|
||||||
|
channel={{ Input: InputChannel.InA }}
|
||||||
|
setLowPass={api.setLowPass}
|
||||||
|
setHighPass={api.setHighPass}
|
||||||
|
setPeq={api.setPeqBand}
|
||||||
|
setPeqBypass={api.setPEQBypass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'inb':
|
||||||
|
return (
|
||||||
|
<ChannelSection
|
||||||
|
dsp={dsp}
|
||||||
|
channel={{ Input: InputChannel.InB }}
|
||||||
|
setLowPass={api.setLowPass}
|
||||||
|
setHighPass={api.setHighPass}
|
||||||
|
setPeq={api.setPeqBand}
|
||||||
|
setPeqBypass={api.setPEQBypass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'inc':
|
||||||
|
return (
|
||||||
|
<ChannelSection
|
||||||
|
dsp={dsp}
|
||||||
|
channel={{ Input: InputChannel.InC }}
|
||||||
|
setLowPass={api.setLowPass}
|
||||||
|
setHighPass={api.setHighPass}
|
||||||
|
setPeq={api.setPeqBand}
|
||||||
|
setPeqBypass={api.setPEQBypass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'ind':
|
||||||
|
return (
|
||||||
|
<ChannelSection
|
||||||
|
dsp={dsp}
|
||||||
|
channel={{ Input: InputChannel.InD }}
|
||||||
|
setLowPass={api.setLowPass}
|
||||||
|
setHighPass={api.setHighPass}
|
||||||
|
setPeq={api.setPeqBand}
|
||||||
|
setPeqBypass={api.setPEQBypass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'out1':
|
||||||
|
return (
|
||||||
|
<ChannelSection
|
||||||
|
dsp={dsp}
|
||||||
|
channel={{ Output: OutputChannel.Out1 }}
|
||||||
|
setLowPass={api.setLowPass}
|
||||||
|
setHighPass={api.setHighPass}
|
||||||
|
setPeq={api.setPeqBand}
|
||||||
|
setPeqBypass={api.setPEQBypass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-24
@@ -1,5 +1,5 @@
|
|||||||
/* InputSection.css */
|
/* InputSection.css */
|
||||||
.input-graph-section {
|
.channel-graph-section {
|
||||||
flex: 0 0 calc(100% - var(--dsp-control-heigth));
|
flex: 0 0 calc(100% - var(--dsp-control-heigth));
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-panel {
|
.channel-graph-panel {
|
||||||
|
position: relative;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -27,7 +28,28 @@
|
|||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph__title {
|
.channel-bypass-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-bypass-overlay span {
|
||||||
|
font-size: 8cqh;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
color: #f87171;
|
||||||
|
text-shadow: 0 0 12px rgba(0, 0, 0, 0.6);
|
||||||
|
transform: rotate(-8deg);
|
||||||
|
opacity: 0.85;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-graph__title {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -38,7 +60,7 @@
|
|||||||
font-weight: var(--font-weight-semibold);
|
font-weight: var(--font-weight-semibold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-panel > .container {
|
.channel-graph-panel > .container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
@@ -46,7 +68,7 @@
|
|||||||
/* Bottom control bar — same pattern as .compressor-section: absolute,
|
/* Bottom control bar — same pattern as .compressor-section: absolute,
|
||||||
bottom-anchored, panels flow in a row and scroll horizontally instead
|
bottom-anchored, panels flow in a row and scroll horizontally instead
|
||||||
of squeezing into a fixed max-width. */
|
of squeezing into a fixed max-width. */
|
||||||
.input-control-section {
|
.channel-control-section {
|
||||||
container-type: size;
|
container-type: size;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -71,7 +93,7 @@
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-control-panel {
|
.channel-control-panel {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -84,9 +106,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Peq Sliders */
|
/* Peq Sliders */
|
||||||
.input-panel {
|
.channel-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 10;
|
height: 100%;
|
||||||
|
|
||||||
aspect-ratio: 0.8;
|
aspect-ratio: 0.8;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@@ -102,7 +124,7 @@
|
|||||||
background: var(--bg-void);
|
background: var(--bg-void);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-panel__title {
|
.channel-panel__title {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -118,7 +140,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-panel__grid {
|
.channel-panel__grid {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
||||||
@@ -132,7 +154,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-panel__cell {
|
.channel-panel__cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -142,7 +164,7 @@
|
|||||||
gap: 1cqh;
|
gap: 1cqh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-panel__cell-label {
|
.channel-panel__cell-label {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
font-size: 3.2cqh;
|
font-size: 3.2cqh;
|
||||||
@@ -200,11 +222,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.crossover-panel__select {
|
.crossover-panel__select {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 85%;
|
||||||
|
font-family: var(--font-ui);
|
||||||
font-size: 4cqh;
|
font-size: 4cqh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.crossover-panel__select .choice-select__input {
|
.crossover-panel__select .choice-select__input,
|
||||||
|
.crossover-panel__select .choice-select__input option {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: 2cqh;
|
||||||
font-size: 4cqh;
|
font-size: 4cqh;
|
||||||
|
color: var(--accent-brand);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.peq-band-list {
|
.peq-band-list {
|
||||||
@@ -223,17 +254,16 @@
|
|||||||
padding: 1cqh;
|
padding: 1cqh;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
aspect-ratio: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.peq-band-list__row {
|
.peq-band-list__row {
|
||||||
flex: 1 1 0;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1.2fr 1fr 1fr 1.6fr 1.2fr;
|
grid-template-columns: 0.4fr 0.55fr 0.4fr 0.5fr 1.2fr 0.5fr;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: 1cqh;
|
gap: 1cqh;
|
||||||
|
|
||||||
@@ -307,14 +337,14 @@
|
|||||||
font-size: 4cqh;
|
font-size: 4cqh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-panel__magnitude-wrap {
|
.channel-graph-panel__magnitude-wrap {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph__title-row {
|
.channel-graph__title-row {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -322,11 +352,11 @@
|
|||||||
padding: 1cqh 1.5cqh;
|
padding: 1cqh 1.5cqh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph__title-row .input-graph__title {
|
.channel-graph__title-row .channel-graph__title {
|
||||||
padding: 0; /* was on the standalone title element; row now owns spacing */
|
padding: 0; /* was on the standalone title element; row now owns spacing */
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-view-toggle {
|
.channel-graph-view-toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.4cqh;
|
gap: 0.4cqh;
|
||||||
border: var(--border-width) solid var(--border-hairline);
|
border: var(--border-width) solid var(--border-hairline);
|
||||||
@@ -335,7 +365,7 @@
|
|||||||
background: var(--bg-void);
|
background: var(--bg-void);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-view-toggle__btn {
|
.channel-graph-view-toggle__btn {
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
font-size: 2cqh;
|
font-size: 2cqh;
|
||||||
padding: 0.6cqh 1.2cqh;
|
padding: 0.6cqh 1.2cqh;
|
||||||
@@ -346,8 +376,8 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-graph-view-toggle__btn.active {
|
.channel-graph-view-toggle__btn.active {
|
||||||
background: var(--accent, #4dd0e1);
|
background: var(--accent-brand);
|
||||||
color: var(--bg-void);
|
color: var(--bg-void);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,4 +391,9 @@
|
|||||||
color: var(--accent-brand);
|
color: var(--accent-brand);
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
text-align-last: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
+54
-38
@@ -1,4 +1,4 @@
|
|||||||
// InputSection.tsx
|
// ChannelSection.tsx
|
||||||
import VerticalSlider, {
|
import VerticalSlider, {
|
||||||
makeLogStep,
|
makeLogStep,
|
||||||
makeLogQuantize,
|
makeLogQuantize,
|
||||||
@@ -12,12 +12,12 @@ import {
|
|||||||
PEQ_Q_RANGES,
|
PEQ_Q_RANGES,
|
||||||
PEAK_Q_VALUES,
|
PEAK_Q_VALUES,
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { InputChannel, Channel, PEQBand, PEQFilter, PEQ, Crossover } from '../../types';
|
import { Channel, PEQBand, PEQFilter, PEQ, Crossover } from '../../types';
|
||||||
import { DSP408 as DSP } from '../../../../types';
|
import { DSP408 as DSP } from '../../../../types';
|
||||||
import { CrossoverFilter } from '../../types';
|
import { CrossoverFilter } from '../../types';
|
||||||
import { LinearGraph, LinearGraphHandle } from '../../../../components/LinearGraph/LinearGraph';
|
import { LinearGraph, LinearGraphHandle } from '../../../../components/LinearGraph/LinearGraph';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import './InputSection.css';
|
import './ChannelSection.css';
|
||||||
import ChannelGroup from '../../../../components/ChannelGroup/ChannelGroup';
|
import ChannelGroup from '../../../../components/ChannelGroup/ChannelGroup';
|
||||||
import Button from '../../../../components/Button/Button';
|
import Button from '../../../../components/Button/Button';
|
||||||
import {
|
import {
|
||||||
@@ -50,12 +50,12 @@ function GraphViewToggle({
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="input-graph-view-toggle">
|
<div className="channel-graph-view-toggle">
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<button
|
<button
|
||||||
key={opt.value}
|
key={opt.value}
|
||||||
type="button"
|
type="button"
|
||||||
className={`input-graph-view-toggle__btn${view === opt.value ? ' active' : ''}`}
|
className={`channel-graph-view-toggle__btn${view === opt.value ? ' active' : ''}`}
|
||||||
onClick={() => onChange(opt.value)}
|
onClick={() => onChange(opt.value)}
|
||||||
>
|
>
|
||||||
{opt.label}
|
{opt.label}
|
||||||
@@ -65,7 +65,7 @@ function GraphViewToggle({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function InputActions({
|
function ChannelActions({
|
||||||
bypass,
|
bypass,
|
||||||
resetting,
|
resetting,
|
||||||
onBypass,
|
onBypass,
|
||||||
@@ -135,11 +135,11 @@ function PeqPanelSliders({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="input-panel">
|
<div className="channel-panel">
|
||||||
<div className="input-panel__title">PEQ Parameters</div>
|
<div className="channel-panel__title">PEQ Parameters</div>
|
||||||
|
|
||||||
<div className="input-panel__grid">
|
<div className="channel-panel__grid">
|
||||||
<div className="input-panel__cell input-panel__cell--frequency">
|
<div className="channel-panel__cell channel-panel__cell--frequency">
|
||||||
<VerticalSlider
|
<VerticalSlider
|
||||||
min={FREQUENCY_MIN}
|
min={FREQUENCY_MIN}
|
||||||
max={FREQUENCY_MAX}
|
max={FREQUENCY_MAX}
|
||||||
@@ -156,10 +156,10 @@ function PeqPanelSliders({
|
|||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
style={{ width: '100%', flex: '1 1 auto' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="input-panel__cell-label">Frequency</div>
|
<div className="channel-panel__cell-label">Frequency</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="input-panel__cell input-panel__cell--q">
|
<div className="channel-panel__cell channel-panel__cell--q">
|
||||||
<VerticalSlider
|
<VerticalSlider
|
||||||
min={qMin}
|
min={qMin}
|
||||||
max={qMax}
|
max={qMax}
|
||||||
@@ -171,10 +171,10 @@ function PeqPanelSliders({
|
|||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
style={{ width: '100%', flex: '1 1 auto' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="input-panel__cell-label">Q</div>
|
<div className="channel-panel__cell-label">Q</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="input-panel__cell input-panel__cell--gain">
|
<div className="channel-panel__cell channel-panel__cell--gain">
|
||||||
<VerticalSlider
|
<VerticalSlider
|
||||||
min={-12}
|
min={-12}
|
||||||
max={12}
|
max={12}
|
||||||
@@ -187,7 +187,7 @@ function PeqPanelSliders({
|
|||||||
style={{ width: '100%', flex: '1 1 auto' }}
|
style={{ width: '100%', flex: '1 1 auto' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="input-panel__cell-label">Gain</div>
|
<div className="channel-panel__cell-label">Gain</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -295,6 +295,7 @@ function PeqBandList({
|
|||||||
activeBand,
|
activeBand,
|
||||||
setActiveBand,
|
setActiveBand,
|
||||||
setPEQ,
|
setPEQ,
|
||||||
|
bandsOrder,
|
||||||
}: {
|
}: {
|
||||||
bands: Record<PEQBand, PEQ>;
|
bands: Record<PEQBand, PEQ>;
|
||||||
liveBands: Partial<Record<PEQBand, PEQ>>;
|
liveBands: Partial<Record<PEQBand, PEQ>>;
|
||||||
@@ -308,6 +309,7 @@ function PeqBandList({
|
|||||||
filterType: PEQFilter,
|
filterType: PEQFilter,
|
||||||
bypass: boolean
|
bypass: boolean
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
|
bandsOrder: PEQBand[];
|
||||||
}) {
|
}) {
|
||||||
const commit = (band: PEQBand, overrides: Partial<PEQ>) => {
|
const commit = (band: PEQBand, overrides: Partial<PEQ>) => {
|
||||||
const peq = liveBands[band] ?? bands[band];
|
const peq = liveBands[band] ?? bands[band];
|
||||||
@@ -326,7 +328,7 @@ function PeqBandList({
|
|||||||
<div className="peq-band-list__cell">Bypass</div>
|
<div className="peq-band-list__cell">Bypass</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{PEQ_BANDS_ORDER.map((band, i) => {
|
{bandsOrder.map((band, i) => {
|
||||||
const peq = liveBands[band] ?? bands[band];
|
const peq = liveBands[band] ?? bands[band];
|
||||||
const isActive = band === activeBand;
|
const isActive = band === activeBand;
|
||||||
|
|
||||||
@@ -395,7 +397,7 @@ function PeqBandList({
|
|||||||
variant="toggle"
|
variant="toggle"
|
||||||
className={peq.bypass ? 'active-warning' : ''}
|
className={peq.bypass ? 'active-warning' : ''}
|
||||||
onClick={() => commit(band, { bypass: !peq.bypass })}
|
onClick={() => commit(band, { bypass: !peq.bypass })}
|
||||||
style={{ minHeight: 0 }}
|
style={{ minHeight: 0, fontSize: '4cqh', paddingInline: 0 }}
|
||||||
>
|
>
|
||||||
{peq.bypass ? 'Bypassed' : 'Bypass'}
|
{peq.bypass ? 'Bypassed' : 'Bypass'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -412,7 +414,7 @@ type SetFilterFn = (
|
|||||||
filterType: CrossoverFilter
|
filterType: CrossoverFilter
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
|
|
||||||
function InputSection({
|
function ChannelSection({
|
||||||
dsp,
|
dsp,
|
||||||
channel,
|
channel,
|
||||||
setHighPass,
|
setHighPass,
|
||||||
@@ -421,7 +423,7 @@ function InputSection({
|
|||||||
setPeqBypass,
|
setPeqBypass,
|
||||||
}: {
|
}: {
|
||||||
dsp: DSP;
|
dsp: DSP;
|
||||||
channel: InputChannel;
|
channel: Channel;
|
||||||
setHighPass: SetFilterFn;
|
setHighPass: SetFilterFn;
|
||||||
setLowPass: SetFilterFn;
|
setLowPass: SetFilterFn;
|
||||||
setPeq: (
|
setPeq: (
|
||||||
@@ -448,18 +450,25 @@ function InputSection({
|
|||||||
const magnitudeGraphRef = useRef<LinearGraphHandle>(null);
|
const magnitudeGraphRef = useRef<LinearGraphHandle>(null);
|
||||||
|
|
||||||
// DSP values
|
// DSP values
|
||||||
const activeInputState = dsp.state.current_config.input_states[channel];
|
const isInput = 'Input' in channel;
|
||||||
const ActiveHighPassFilter = activeInputState.crossover.high_pass;
|
|
||||||
const ActiveLowPassFilter = activeInputState.crossover.low_pass;
|
const activeChannelState = isInput
|
||||||
const ActivePeq = activeInputState.peq_chain;
|
? dsp.state.current_config.input_states[channel.Input]
|
||||||
|
: dsp.state.current_config.output_states[channel.Output];
|
||||||
|
const ActiveHighPassFilter = activeChannelState.crossover.high_pass;
|
||||||
|
const ActiveLowPassFilter = activeChannelState.crossover.low_pass;
|
||||||
|
const ActivePeq = activeChannelState.peq_chain;
|
||||||
|
|
||||||
|
const bandsOrder = isInput ? PEQ_BANDS_ORDER.slice(0, -1) : PEQ_BANDS_ORDER;
|
||||||
|
|
||||||
const handleResetPeq = async () => {
|
const handleResetPeq = async () => {
|
||||||
if (resetting) return;
|
if (resetting) return;
|
||||||
setResetting(true);
|
setResetting(true);
|
||||||
|
|
||||||
for (const band of PEQ_BANDS_ORDER) {
|
for (const band of bandsOrder) {
|
||||||
const current = displayPeq[band];
|
const current = displayPeq[band];
|
||||||
if (current.gain === 0 || current.filter_type === PEQFilter.Peak) continue; // skip bands already flat
|
// Skip only if already flat — nothing to change.
|
||||||
|
if (current.gain === 0 && current.filter_type === PEQFilter.Peak) continue;
|
||||||
|
|
||||||
await commitPEQ(band, 0, current.frequency, current.q, PEQFilter.Peak, current.bypass);
|
await commitPEQ(band, 0, current.frequency, current.q, PEQFilter.Peak, current.bypass);
|
||||||
}
|
}
|
||||||
@@ -468,7 +477,7 @@ function InputSection({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBypassClick = () => {
|
const handleBypassClick = () => {
|
||||||
setPeqBypass({ Input: channel }, !ActivePeq.bypass);
|
setPeqBypass(channel, !ActivePeq.bypass);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Live handlers
|
// Live handlers
|
||||||
@@ -489,7 +498,7 @@ function InputSection({
|
|||||||
|
|
||||||
// Real dsp value handlers
|
// Real dsp value handlers
|
||||||
const commitHighPass = async (freq: number, filterType: CrossoverFilter) => {
|
const commitHighPass = async (freq: number, filterType: CrossoverFilter) => {
|
||||||
const success = await setHighPass({ Input: channel }, freq, filterType);
|
const success = await setHighPass(channel, freq, filterType);
|
||||||
if (success) {
|
if (success) {
|
||||||
setLiveHpFreq(freq);
|
setLiveHpFreq(freq);
|
||||||
}
|
}
|
||||||
@@ -497,7 +506,7 @@ function InputSection({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const commitLowPass = async (freq: number, filterType: CrossoverFilter) => {
|
const commitLowPass = async (freq: number, filterType: CrossoverFilter) => {
|
||||||
const success = await setLowPass({ Input: channel }, freq, filterType);
|
const success = await setLowPass(channel, freq, filterType);
|
||||||
if (success) {
|
if (success) {
|
||||||
setLiveLpFreq(freq);
|
setLiveLpFreq(freq);
|
||||||
}
|
}
|
||||||
@@ -512,7 +521,7 @@ function InputSection({
|
|||||||
filter_type: PEQFilter,
|
filter_type: PEQFilter,
|
||||||
bypass: boolean
|
bypass: boolean
|
||||||
) => {
|
) => {
|
||||||
const success = await setPeq({ Input: channel }, band, gain, frequency, q, filter_type, bypass);
|
const success = await setPeq(channel, band, gain, frequency, q, filter_type, bypass);
|
||||||
if (success) {
|
if (success) {
|
||||||
setLivePeq((prev) => {
|
setLivePeq((prev) => {
|
||||||
const next = { ...prev };
|
const next = { ...prev };
|
||||||
@@ -536,15 +545,15 @@ function InputSection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="input-graph-section">
|
<div className="channel-graph-section">
|
||||||
<div className="input-graph-panel">
|
<div className="channel-graph-panel">
|
||||||
<div className="input-graph__title-row">
|
<div className="channel-graph__title-row">
|
||||||
<div className="input-graph__title">{activeInputState.name} - Filters</div>
|
<div className="channel-graph__title">{activeChannelState.name} - Filters</div>
|
||||||
<GraphViewToggle view={graphView} onChange={setGraphView} />
|
<GraphViewToggle view={graphView} onChange={setGraphView} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(graphView === 'both' || graphView === 'magnitude') && (
|
{(graphView === 'both' || graphView === 'magnitude') && (
|
||||||
<div className="input-graph-panel__magnitude-wrap">
|
<div className="channel-graph-panel__magnitude-wrap">
|
||||||
<LinearGraph
|
<LinearGraph
|
||||||
ref={magnitudeGraphRef}
|
ref={magnitudeGraphRef}
|
||||||
xMin={GRAPH_X_MIN}
|
xMin={GRAPH_X_MIN}
|
||||||
@@ -623,11 +632,17 @@ function InputSection({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ActivePeq.bypass && (
|
||||||
|
<div className="channel-bypass-overlay">
|
||||||
|
<span>BYPASSED</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="input-control-section">
|
<div className="channel-control-section">
|
||||||
<div className="input-control-panel">
|
<div className="channel-control-panel">
|
||||||
<ChannelGroup title="PEQ">
|
<ChannelGroup title="PEQ">
|
||||||
<PeqBandList
|
<PeqBandList
|
||||||
bands={ActivePeq.bands}
|
bands={ActivePeq.bands}
|
||||||
@@ -635,6 +650,7 @@ function InputSection({
|
|||||||
activeBand={activeBand}
|
activeBand={activeBand}
|
||||||
setActiveBand={setActiveBand}
|
setActiveBand={setActiveBand}
|
||||||
setPEQ={commitPEQ}
|
setPEQ={commitPEQ}
|
||||||
|
bandsOrder={bandsOrder}
|
||||||
/>
|
/>
|
||||||
<PeqPanelSliders
|
<PeqPanelSliders
|
||||||
peq={ActivePeq.bands[activeBand]}
|
peq={ActivePeq.bands[activeBand]}
|
||||||
@@ -661,7 +677,7 @@ function InputSection({
|
|||||||
</ChannelGroup>
|
</ChannelGroup>
|
||||||
|
|
||||||
<ChannelGroup title="Actions">
|
<ChannelGroup title="Actions">
|
||||||
<InputActions
|
<ChannelActions
|
||||||
bypass={ActivePeq.bypass}
|
bypass={ActivePeq.bypass}
|
||||||
resetting={resetting}
|
resetting={resetting}
|
||||||
onBypass={handleBypassClick}
|
onBypass={handleBypassClick}
|
||||||
@@ -674,4 +690,4 @@ function InputSection({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InputSection;
|
export default ChannelSection;
|
||||||
@@ -4,28 +4,21 @@ import { NotificationType, Notification } from '../types';
|
|||||||
export function useNotifications() {
|
export function useNotifications() {
|
||||||
const [notifications, setNotifications] = useState<Notification[]>([]);
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||||
const hovered = useRef<Set<number>>(new Set());
|
const hovered = useRef<Set<number>>(new Set());
|
||||||
|
const nextId = useRef(0); // monotonically increasing counter
|
||||||
|
|
||||||
const notify = useCallback(
|
const notify = useCallback(
|
||||||
(message: string, type: NotificationType = 'error', dspName?: string) => {
|
(message: string, type: NotificationType = 'error', dspName?: string) => {
|
||||||
const id = Date.now();
|
const id = nextId.current++;
|
||||||
|
|
||||||
const fullMessage = dspName ? `${dspName}: ${message}` : message;
|
const fullMessage = dspName ? `${dspName}: ${message}` : message;
|
||||||
|
|
||||||
setNotifications((prev) => [
|
setNotifications((prev) => [...prev, { id, message: fullMessage, type }]);
|
||||||
...prev,
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
message: fullMessage,
|
|
||||||
type,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const removeLater = () => {
|
const removeLater = () => {
|
||||||
if (hovered.current.has(id)) {
|
if (hovered.current.has(id)) {
|
||||||
setTimeout(removeLater, 1000);
|
setTimeout(removeLater, 1000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,6 +28,7 @@ export function useNotifications() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const removeNotification = useCallback((id: number) => {
|
const removeNotification = useCallback((id: number) => {
|
||||||
|
hovered.current.delete(id); // prevent leaking stale hover state
|
||||||
setNotifications((prev) => prev.map((n) => (n.id === id ? { ...n, removing: true } : n)));
|
setNotifications((prev) => prev.map((n) => (n.id === id ? { ...n, removing: true } : n)));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user