Skip to main content

rustriff_lib/commands/effect_commands/
effects.rs

1use crate::commands::helpers::persist_amp_config;
2use crate::domain::dto::effect::effect_dto::EffectDto;
3use crate::domain::effect::Effect;
4use crate::services::amp_config_service::AmpConfigPersistenceService;
5use crate::services::audio_service::AudioService;
6use std::sync::Mutex;
7use tracing::info;
8
9#[tauri::command]
10pub(crate) fn add_effect(
11    audio_service: tauri::State<Mutex<AudioService>>,
12    persistence_service: tauri::State<Mutex<AmpConfigPersistenceService>>,
13    effect_dto: EffectDto,
14) -> Result<(), String> {
15    let mut service = audio_service.inner().lock().unwrap();
16    let target_channel_id = *service.current_channel_id();
17    let dsp_sample_rate = service.dsp_chain_sample_rate();
18
19    if let Some(channel) = service
20        .channels_mut()
21        .iter_mut()
22        .find(|c| c.id() == target_channel_id)
23    {
24        let effect = effect_dto.add_to_domain(channel.next_effect_id(), dsp_sample_rate);
25        channel.add_effect_to_chain(effect);
26        persist_amp_config(&service, &persistence_service);
27        Ok(())
28    } else {
29        Err("Channel not found".into())
30    }
31}
32
33#[tauri::command]
34pub(crate) fn remove_effect(
35    audio_service: tauri::State<Mutex<AudioService>>,
36    persistence_service: tauri::State<Mutex<AmpConfigPersistenceService>>,
37    effect_id: u32,
38) {
39    let mut service = audio_service.inner().lock().unwrap();
40    let channel_id = *service.current_channel_id();
41    let current_channel = service
42        .channels_mut()
43        .iter_mut()
44        .find(|c| c.id() == channel_id)
45        .unwrap();
46    current_channel.remove_effect_from_chain(effect_id);
47    persist_amp_config(&service, &persistence_service);
48}
49
50#[tauri::command]
51pub(crate) fn apply_effect_order_change(
52    audio_service: tauri::State<Mutex<AudioService>>,
53    effects: Vec<EffectDto>,
54) {
55    let mut service = audio_service.inner().lock().unwrap();
56    let dsp_sample_rate = service.dsp_chain_sample_rate();
57    let channel_id = *service.current_channel_id();
58    let current_channel = service
59        .channels_mut()
60        .iter_mut()
61        .find(|c| c.id() == channel_id)
62        .unwrap();
63    let boxed_effects: Vec<Box<dyn Effect>> = effects
64        .into_iter()
65        .map(|dto| dto.to_domain(dsp_sample_rate))
66        .collect();
67    current_channel.restore_effect_chain(boxed_effects);
68}
69
70/// Toggles an effect's active state on the current channel.
71/// Enables or disables audio processing for a specific effect. The change takes effect
72/// on the very next audio sample — no loopback restart needed.
73///
74/// This is a generic command that works with any effect type.
75///
76/// # Arguments
77/// * `effect_id` — Unique ID of the effect to toggle
78///
79/// # Returns
80/// * `Ok(bool)` — The new active state (`true` = processing, `false` = bypassed)
81/// * `Err(String)` — Error message if effect ID is invalid or channel not found
82///
83/// # Implementation Details
84///
85/// - Updates the effect's [`Arc<AtomicBool>`] active flag
86/// - Changes apply immediately to audio processing thread
87#[tauri::command]
88pub fn toggle_effect(
89    audio_service: tauri::State<Mutex<AudioService>>,
90    persistence_service: tauri::State<Mutex<AmpConfigPersistenceService>>,
91    effect_id: u32,
92) -> Result<bool, String> {
93    let service = audio_service
94        .lock()
95        .map_err(|_| "Failed to lock audio service".to_string())?;
96    let channel = service
97        .channels()
98        .iter()
99        .find(|c| c.id() == *service.current_channel_id())
100        .ok_or("No active channel")?;
101    let new_state = channel.toggle_effect(effect_id)?;
102    info!(
103        channel_id = *service.current_channel_id(),
104        effect_id,
105        is_active = new_state,
106        "Effect toggled"
107    );
108    persist_amp_config(&service, &persistence_service);
109    Ok(new_state)
110}