Skip to main content

rustriff_lib/
lib.rs

1pub mod commands;
2pub mod config;
3pub mod domain;
4pub mod infrastructure;
5pub mod services;
6
7#[cfg(test)]
8pub mod tests;
9
10use crate::commands::analyzer::{
11    get_live_spectrum, get_spectrum_contract, start_live_spectrum_stream,
12    stop_live_spectrum_stream, SpectrumStreamState,
13};
14use crate::commands::channels::{
15    add_channel, get_all_channels, get_channel_id, remove_channel, set_channel_id,
16};
17use crate::commands::default_controls::{
18    get_amp_config, set_bass, set_gain, set_master_volume, set_middle, set_tone_stack, set_treble,
19    set_volume, toggle_on_off,
20};
21use crate::commands::effect_commands::cabinet_ir::{
22    get_all_ir_profiles, remove_ir_profile, upload_ir_profile,
23};
24use crate::commands::effect_commands::delay::{set_delay_delay_time, set_delay_level};
25use crate::commands::effect_commands::hc_distortion::{
26    set_hc_distortion_level, set_hc_distortion_threshold,
27};
28use crate::commands::effect_commands::sc_distortion::{
29    set_sc_distortion_level, set_sc_distortion_smoothing, set_sc_distortion_threshold,
30};
31use crate::commands::latency_testing::{
32    measure_all_dsp_algorithmic_latency, measure_all_dsp_cpu_timings, measure_buffer_latency,
33    measure_round_trip_latency, test_gain_latency,
34};
35use crate::commands::loopback::start_loopback;
36use crate::commands::settings::{
37    get_buffer_size_frames, get_input_device_list, get_output_device_list, set_buffer_size_frames,
38    set_input_device, set_output_device,
39};
40use crate::config::{get_default_ir_file, init_tracing};
41use crate::infrastructure::file_loader::FileLoader;
42use crate::infrastructure::persistence::json_amp_config_repository::JsonFileAmpConfigRepository;
43use crate::services::amp_config_service::AmpConfigPersistenceService;
44use crate::services::audio_service::AudioService;
45use crate::services::device_service::DeviceService;
46use crate::services::file_service::FileService;
47use commands::effect_commands::effects::{
48    add_effect, apply_effect_order_change, remove_effect, toggle_effect,
49};
50use cpal::default_host;
51use cpal::traits::{DeviceTrait, HostTrait};
52use cpal::{BufferSize, StreamConfig};
53use std::sync::Mutex;
54use tauri::Manager;
55use tracing::{error, info};
56
57const AMP_CONFIG_FILE_NAME: &str = "amp-config.json";
58
59#[cfg_attr(mobile, tauri::mobile_entry_point)]
60pub fn run() {
61    init_tracing();
62
63    let host = default_host();
64    let input = host.default_input_device().unwrap();
65    let output = host.default_output_device().unwrap();
66
67    let input_name = input
68        .description()
69        .map(|d| d.name().to_string())
70        .unwrap_or_else(|_| "Unknown".to_string());
71    let output_name = output
72        .description()
73        .map(|d| d.name().to_string())
74        .unwrap_or_else(|_| "Unknown".to_string());
75
76    info!("Input device: {}", input_name);
77    info!("Output device: {}", output_name);
78
79    let input_supported = input
80        .default_input_config()
81        .map_err(|e| format!("Failed to get input device config: {}", e))
82        .unwrap();
83    let output_supported = output
84        .default_output_config()
85        .map_err(|e| format!("Failed to get output device config: {}", e))
86        .unwrap();
87    info!(
88        "Input config - Channels: {}, Sample Rate: {} Hz",
89        input_supported.channels(),
90        input_supported.sample_rate()
91    );
92    info!(
93        "Output config - Channels: {}, Sample Rate: {} Hz",
94        output_supported.channels(),
95        output_supported.sample_rate()
96    );
97
98    let normalize_channels = |channels: u16| -> u16 {
99        match channels {
100            0 => {
101                error!("Device reported 0 channels, defaulting to stereo");
102                2
103            }
104            1 => 1,
105            _ => {
106                if channels > 2 {
107                    info!(
108                        "Device reported {} channels, normalizing to stereo for stability",
109                        channels
110                    );
111                    2
112                } else {
113                    channels
114                }
115            }
116        }
117    };
118
119    let input_channels = normalize_channels(input_supported.channels());
120    let output_channels = normalize_channels(output_supported.channels());
121
122    let input_config = StreamConfig {
123        channels: input_channels,
124        sample_rate: input_supported.sample_rate(),
125        buffer_size: BufferSize::Default,
126    };
127    let output_config = StreamConfig {
128        channels: output_channels,
129        sample_rate: output_supported.sample_rate(),
130        buffer_size: BufferSize::Default,
131    };
132
133    info!(
134        "Configured stream - Input: {} ch @ {} Hz, Output: {} ch @ {} Hz",
135        input_config.channels,
136        input_config.sample_rate,
137        output_config.channels,
138        output_config.sample_rate
139    );
140
141    let audio_service = AudioService::new(input, output, input_config, output_config);
142
143    tauri::Builder::default()
144        .manage(Mutex::new(audio_service))
145        .manage(SpectrumStreamState::default())
146        .manage(DeviceService::new(host))
147        .plugin(tauri_plugin_opener::init())
148        .setup(|app| {
149            let config_dir = app
150                .path()
151                .app_config_dir()
152                .or_else(|_| app.path().app_data_dir())
153                .map_err(|e| format!("Failed to resolve app config/data directory: {e}"))?;
154
155            let config_path = config_dir.join(AMP_CONFIG_FILE_NAME);
156            info!("Using persisted amp config path: {}", config_path.display());
157
158            let amp_config_persistence_service = AmpConfigPersistenceService::new(Box::new(
159                JsonFileAmpConfigRepository::new(config_path),
160            ));
161
162            {
163                let audio_service_state = app.state::<Mutex<AudioService>>();
164                let mut audio_service = audio_service_state
165                    .lock()
166                    .map_err(|_| "Failed to lock audio service during startup")?;
167
168                match amp_config_persistence_service.load_amp_config() {
169                    Ok(Some(config)) => {
170                        info!("Loaded persisted amplifier configuration");
171                        audio_service.apply_amp_config(config);
172                    }
173                    Ok(None) => info!("No persisted amplifier configuration found"),
174                    Err(err) => error!("Failed to load persisted amplifier configuration: {err}"),
175                }
176            }
177
178            let resource_root = app.path().resource_dir().unwrap_or_else(|_| {
179                std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources")
180            });
181            info!("Using resource root: {}", resource_root.display());
182
183            let custom_ir_directory = config_dir.join("default_ir_custom");
184            info!(
185                "Using custom IR directory: {}",
186                custom_ir_directory.display()
187            );
188            std::env::set_var("RUSTRIFF_CUSTOM_IR_DIR", &custom_ir_directory);
189
190            let file_service = FileService::new(
191                Box::new(FileLoader::new()),
192                resource_root,
193                custom_ir_directory,
194            );
195            app.manage(file_service);
196
197            app.manage(Mutex::new(amp_config_persistence_service));
198            Ok(())
199        })
200        .invoke_handler(tauri::generate_handler![
201            get_default_ir_file,
202            start_loopback,
203            set_gain,
204            get_input_device_list,
205            get_output_device_list,
206            set_input_device,
207            set_output_device,
208            set_master_volume,
209            toggle_on_off,
210            get_amp_config,
211            set_bass,
212            set_tone_stack,
213            set_middle,
214            set_treble,
215            set_volume,
216            set_channel_id,
217            get_channel_id,
218            add_channel,
219            get_all_channels,
220            remove_channel,
221            get_buffer_size_frames,
222            set_buffer_size_frames,
223            test_gain_latency,
224            measure_all_dsp_cpu_timings,
225            measure_all_dsp_algorithmic_latency,
226            measure_buffer_latency,
227            measure_round_trip_latency,
228            toggle_effect,
229            set_hc_distortion_threshold,
230            set_hc_distortion_level,
231            add_effect,
232            remove_effect,
233            apply_effect_order_change,
234            get_all_ir_profiles,
235            upload_ir_profile,
236            remove_ir_profile,
237            set_delay_delay_time,
238            set_delay_level,
239            get_live_spectrum,
240            get_spectrum_contract,
241            start_live_spectrum_stream,
242            stop_live_spectrum_stream,
243            set_sc_distortion_threshold,
244            set_sc_distortion_level,
245            set_sc_distortion_smoothing,
246        ])
247        .run(tauri::generate_context!())
248        .expect("error while running tauri application");
249}