rustriff_lib/commands/effect_commands/hc_distortion.rs
1use crate::commands::helpers::persist_amp_config;
2use crate::services::amp_config_service::AmpConfigPersistenceService;
3use crate::services::audio_service::AudioService;
4use std::sync::Mutex;
5use tracing::info;
6
7/// # Sets the Clipping Threshold on an HC Distortion Effect
8///
9/// Adjusts the Drive parameter: lower thresholds produce heavier distortion.
10///
11/// # Arguments
12/// * `effect_id` — ID of the HCDistortion effect to modify
13/// * `threshold` — Clipping level in range `(0.0, 1.0]`
14/// * Values < 0.001 are clamped to 0.001
15/// * Values > 1.0 are clamped to 1.0
16///
17/// # Returns
18/// * `Ok(())` — Threshold updated successfully
19/// * `Err(String)` — Error if:
20/// - Effect not found or parameter update fails
21/// - Threshold is NaN or infinite (audio thread safety)
22///
23/// # Validation
24///
25/// This command validates the threshold before forwarding to the audio thread:
26/// - Rejects NaN and infinite values (would panic in audio processor)
27/// - Clamps finite values to safe range `[0.001, 1.0]`
28/// - Prevents audio thread crashes from invalid clamp operations
29
30#[tauri::command]
31pub fn set_hc_distortion_threshold(
32 audio_service: tauri::State<Mutex<AudioService>>,
33 persistence_service: tauri::State<Mutex<AmpConfigPersistenceService>>,
34 effect_id: u32,
35 threshold: f32,
36) -> Result<(), String> {
37 if !threshold.is_finite() {
38 return Err(format!(
39 "Invalid threshold: {} (must be finite, not NaN or infinite)",
40 threshold
41 ));
42 }
43
44 let safe_threshold = threshold.clamp(0.001, 1.0);
45
46 let service = audio_service
47 .lock()
48 .map_err(|_| "Failed to lock audio service".to_string())?;
49 let channel = service
50 .channels()
51 .iter()
52 .find(|c| c.id() == *service.current_channel_id())
53 .ok_or("No active channel")?;
54 channel.set_effect_param(effect_id, "threshold", safe_threshold)?;
55 info!(
56 channel_id = *service.current_channel_id(),
57 effect_id,
58 threshold = safe_threshold,
59 "HCDistortion threshold updated"
60 );
61 persist_amp_config(&service, &persistence_service);
62 Ok(())
63}
64
65/// # Sets the Output Level (Boost) on an HC Distortion Effect
66///
67/// Adjusts the Level parameter: controls output amplitude after clipping.
68///
69/// # Arguments
70/// * `effect_id` — ID of the HCDistortion effect to modify
71/// * `level` — Normalised level in range `[0.0, 1.0]`
72/// * `0.0` = unity gain (no boost)
73/// * `1.0` = ×2.0 boost
74/// * Values outside range are clamped
75///
76/// # Returns
77/// * `Ok(())` — Level updated successfully
78/// * `Err(String)` — Error if:
79/// - Effect not found or parameter update fails
80/// - Level is NaN or infinite (audio thread safety)
81///
82/// # Validation
83///
84/// This command validates the level before forwarding to the audio thread:
85/// - Rejects NaN and infinite values (would create invalid gain multiplier)
86/// - Clamps finite values to range `[0.0, 1.0]`
87/// - Maps to internal gain `[1.0, 2.0]` after validation
88/// - Prevents audio thread crashes from invalid gain operations
89
90#[tauri::command]
91pub fn set_hc_distortion_level(
92 audio_service: tauri::State<Mutex<AudioService>>,
93 persistence_service: tauri::State<Mutex<AmpConfigPersistenceService>>,
94 effect_id: u32,
95 level: f32,
96) -> Result<(), String> {
97 // Validate level before forwarding to audio thread
98 if !level.is_finite() {
99 return Err(format!(
100 "Invalid level: {} (must be finite, not NaN or infinite)",
101 level
102 ));
103 }
104
105 // Clamp to safe range [0.0, 1.0] then map to internal gain [1.0, 2.0]
106 let safe_level = level.clamp(0.0, 1.0);
107 let gain = 1.0 + safe_level;
108
109 let service = audio_service
110 .lock()
111 .map_err(|_| "Failed to lock audio service".to_string())?;
112 let channel = service
113 .channels()
114 .iter()
115 .find(|c| c.id() == *service.current_channel_id())
116 .ok_or("No active channel")?;
117 channel.set_effect_param(effect_id, "level", gain)?;
118 info!(
119 channel_id = *service.current_channel_id(),
120 effect_id,
121 level = safe_level,
122 gain,
123 "HCDistortion level updated"
124 );
125 persist_amp_config(&service, &persistence_service);
126 Ok(())
127}