use crate::state::{AppState, DSPDevice}; use dsp_thomann::dsp408::types::DelayUnit; use dsp_thomann::dsp408::{types, DSP408}; use std::sync::{Arc, Mutex}; use std::{net::IpAddr, str::FromStr}; use tauri::State; impl AppState { pub fn with_device_mut(&self, id: u64, f: F) -> Result where F: FnOnce(&mut DSPDevice) -> Result, { let device = { let devices = self.devices.lock().map_err(|e| e.to_string())?; devices .get(&id) .cloned() .ok_or_else(|| "DSP not found".to_string())? }; let mut device = device.lock().map_err(|e| e.to_string())?; f(&mut device) } pub fn remove_device(&self, id: u64) -> Result<(), String> { let device = { let mut devices = self.devices.lock().map_err(|e| e.to_string())?; devices .remove(&id) .ok_or_else(|| format!("DSP {} not found", id))? }; // Drop happens here. // DSPDevice owns DSP408. drop(device); Ok(()) } } #[derive(serde::Serialize)] pub struct DSP408Snapshot { pub state: types::DSPState, pub input_meters: types::InputMeters, pub output_meters: types::OutputMeters, pub mode: types::DSPMode, } #[tauri::command] pub fn create_dsp408( state: State, ip: String, port: u16, device_id: u8, demo: bool, ) -> Result { let ip = IpAddr::from_str(&ip).map_err(|e| e.to_string())?; let dsp = if demo { DSP408::new_demo() } else { DSP408::new(ip, port, device_id).map_err(|e| e.to_string())? }; let mut next_id = state.next_id.lock().map_err(|e| e.to_string())?; let id = *next_id; *next_id += 1; let device = DSPDevice { dsp }; let mut devices = state.devices.lock().map_err(|e| e.to_string())?; devices.insert(id, Arc::new(Mutex::new(device))); Ok(id) } #[tauri::command] pub async fn remove_dsp408(state: State<'_, AppState>, id: u64) -> Result<(), String> { state.with_device_mut(id, |device| { if device.dsp.connected() { device.dsp.disconnect().map_err(|e| e.to_string())?; } Ok(()) })?; state.remove_device(id)?; Ok(()) } #[tauri::command] pub async fn get_dsp_snapshot( state: State<'_, AppState>, id: u64, ) -> Result { state.with_device_mut(id, |device| { Ok(DSP408Snapshot { state: device.dsp.state().clone(), input_meters: device.dsp.input_meters().clone(), output_meters: device.dsp.output_meters().clone(), mode: device.dsp.mode(), }) }) } #[tauri::command] pub async fn get_dsp_state( state: State<'_, AppState>, id: u64, ) -> Result { state.with_device_mut(id, |device| { let dsp_state = device.dsp.state(); Ok(dsp_state.clone()) }) } // Commands #[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())) } // 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] pub async fn set_delay_unit( state: State<'_, AppState>, id: u64, unit: types::DelayUnit, ) -> Result { state.with_device_mut(id, |device| { device.dsp.set_delay_unit(unit).map_err(|e| e.to_string()) }) } // Command `0x20` #[tauri::command] pub async fn recall_preset( state: State<'_, AppState>, id: u64, preset_index: usize, ) -> Result { state.with_device_mut(id, |device| { let success = device .dsp .recall_preset(preset_index) .map_err(|e| e.to_string())?; Ok(success) }) } // Command `0x21` #[tauri::command] pub async fn store_current_configuration( state: State<'_, AppState>, id: u64, preset_index: usize, force: bool, ) -> Result { state.with_device_mut(id, |device| { device .dsp .store_current_configuration(preset_index, force) .map_err(|e| e.to_string()) }) } // Command `0x23` #[tauri::command] pub async fn factory_reset( state: State<'_, AppState>, id: u64, force: bool, ) -> Result { state.with_device_mut(id, |device| { device.dsp.factory_reset(force).map_err(|e| e.to_string()) }) } // Command `0x26` #[tauri::command] pub async fn set_current_preset_name( state: State<'_, AppState>, id: u64, name: String, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_current_preset_name(&name) .map_err(|e| e.to_string()) }) } // Command `0x2A` #[tauri::command] pub async fn copy_channel( state: State<'_, AppState>, id: u64, channel_1: types::Channel, channel_2: types::Channel, ) -> Result { state.with_device_mut(id, |device| { device .dsp .copy_channel(channel_1, channel_2) .map_err(|e| e.to_string()) }) } // Command `0x2D` // Command `0x2F` // Command `0x30` #[tauri::command] pub async fn set_compressor( state: State<'_, AppState>, id: u64, channel: types::OutputChannel, ratio: types::Ratio, attack: u16, release: u16, knee: u16, threshold: f32, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_compressor(channel, ratio, attack, release, knee, threshold) .map_err(|e| e.to_string()) }) } // Command `0x31` #[tauri::command] pub async fn set_low_pass( state: State<'_, AppState>, id: u64, channel: types::Channel, freq: f32, filter_type: types::CrossoverFilter, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_low_pass(channel, freq, filter_type) .map_err(|e| e.to_string()) }) } // Command `0x32` #[tauri::command] pub async fn set_high_pass( state: State<'_, AppState>, id: u64, channel: types::Channel, freq: f32, filter_type: types::CrossoverFilter, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_high_pass(channel, freq, filter_type) .map_err(|e| e.to_string()) }) } // Command `0x33` #[tauri::command] pub async fn set_peq_band( state: State<'_, AppState>, id: u64, channel: types::Channel, band: types::PEQBand, gain_db: f32, freq_hz: f32, q: f32, filter_type: types::PEQFilter, bypass: bool, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_peq_band(channel, band, gain_db, freq_hz, q, filter_type, bypass) .map_err(|e| e.to_string()) }) } // Command `0x34` #[tauri::command] pub async fn set_channel_gain( state: State<'_, AppState>, id: u64, channel: dsp_thomann::dsp408::types::Channel, db: f32, ) -> Result { 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 { 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 { 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 { state.with_device_mut(id, |device| { device .dsp .set_delay(channel, delay, unit) .map_err(|e| e.to_string()) }) } // Command `0x39` #[tauri::command] pub async fn set_input_source( state: State<'_, AppState>, id: u64, input_source: types::InputSource, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_input_source(input_source) .map_err(|e| e.to_string()) }) } // Command `0x3A` #[tauri::command] pub async fn set_matrix_routing( state: State<'_, AppState>, id: u64, output: types::OutputChannel, inputs: Vec, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_matrix_routing(output, inputs) .map_err(|e| e.to_string()) }) } // Command `0x3B` #[tauri::command] pub async fn link_channels( state: State<'_, AppState>, id: u64, source: types::Channel, destinations: Vec, ) -> Result { state.with_device_mut(id, |device| { device .dsp .link_channels(source, destinations) .map_err(|e| e.to_string()) }) } // Command `0x3C` #[tauri::command] pub async fn set_bypass_peq( state: State<'_, AppState>, id: u64, channel: types::Channel, bypass: bool, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_bypass_peq(channel, bypass) .map_err(|e| e.to_string()) }) } // Command `0x3D` #[tauri::command] pub async fn set_channel_name( state: State<'_, AppState>, id: u64, channel: types::Channel, name: String, ) -> Result { state.with_device_mut(id, |device| { device .dsp .set_channel_name(channel, &name) .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 { 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 { 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 { 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 { 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 { state.with_device_mut(id, |device| { device .dsp .set_geq_bypass(input_channel, bypass) .map_err(|e| e.to_string()) }) }