Skip to main content

voirs_spatial/
config.rs

1//! Configuration for spatial audio processing
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Configuration for spatial audio processing
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct SpatialConfig {
9    /// HRTF database path
10    pub hrtf_database_path: Option<PathBuf>,
11    /// Sample rate for processing
12    pub sample_rate: u32,
13    /// Buffer size for processing
14    pub buffer_size: usize,
15    /// Room size for acoustics simulation
16    pub room_dimensions: (f32, f32, f32),
17    /// Reverberation time (RT60) in seconds
18    pub reverb_time: f32,
19    /// Distance attenuation enabled
20    pub enable_distance_attenuation: bool,
21    /// Air absorption enabled
22    pub enable_air_absorption: bool,
23    /// Doppler effect enabled
24    pub enable_doppler: bool,
25    /// Maximum processing distance
26    pub max_distance: f32,
27    /// Speed of sound (m/s)
28    pub speed_of_sound: f32,
29    /// Quality level (0.0 = lowest, 1.0 = highest)
30    pub quality_level: f32,
31    /// Maximum concurrent audio sources
32    pub max_sources: usize,
33    /// Enable GPU acceleration if available
34    pub use_gpu: bool,
35}
36
37impl Default for SpatialConfig {
38    fn default() -> Self {
39        Self {
40            hrtf_database_path: None,
41            sample_rate: 44100,
42            buffer_size: 1024,
43            room_dimensions: (10.0, 8.0, 3.0), // meters
44            reverb_time: 1.2,                  // seconds
45            enable_distance_attenuation: true,
46            enable_air_absorption: true,
47            enable_doppler: false,
48            max_distance: 100.0,   // meters
49            speed_of_sound: 343.0, // m/s at 20°C
50            quality_level: 0.8,    // High quality by default
51            max_sources: 16,       // Reasonable default
52            use_gpu: false,        // Conservative default
53        }
54    }
55}
56
57impl SpatialConfig {
58    /// Validate configuration
59    pub fn validate(&self) -> crate::Result<()> {
60        if self.sample_rate == 0 {
61            return Err(crate::Error::LegacyConfig(
62                "Sample rate must be positive".to_string(),
63            ));
64        }
65        if self.buffer_size == 0 {
66            return Err(crate::Error::LegacyConfig(
67                "Buffer size must be positive".to_string(),
68            ));
69        }
70        if self.reverb_time < 0.0 {
71            return Err(crate::Error::LegacyConfig(
72                "Reverb time cannot be negative".to_string(),
73            ));
74        }
75        if self.max_distance <= 0.0 {
76            return Err(crate::Error::LegacyConfig(
77                "Max distance must be positive".to_string(),
78            ));
79        }
80        if self.speed_of_sound <= 0.0 {
81            return Err(crate::Error::LegacyConfig(
82                "Speed of sound must be positive".to_string(),
83            ));
84        }
85        Ok(())
86    }
87}
88
89/// Builder for SpatialConfig
90#[derive(Debug, Default)]
91pub struct SpatialConfigBuilder {
92    config: SpatialConfig,
93}
94
95impl SpatialConfigBuilder {
96    /// Create new config builder
97    pub fn new() -> Self {
98        Self::default()
99    }
100
101    /// Set HRTF database path
102    pub fn hrtf_database_path(mut self, path: PathBuf) -> Self {
103        self.config.hrtf_database_path = Some(path);
104        self
105    }
106
107    /// Set sample rate
108    pub fn sample_rate(mut self, sample_rate: u32) -> Self {
109        self.config.sample_rate = sample_rate;
110        self
111    }
112
113    /// Set buffer size
114    pub fn buffer_size(mut self, buffer_size: usize) -> Self {
115        self.config.buffer_size = buffer_size;
116        self
117    }
118
119    /// Set room dimensions
120    pub fn room_dimensions(mut self, width: f32, height: f32, depth: f32) -> Self {
121        self.config.room_dimensions = (width, height, depth);
122        self
123    }
124
125    /// Set reverb time
126    pub fn reverb_time(mut self, reverb_time: f32) -> Self {
127        self.config.reverb_time = reverb_time;
128        self
129    }
130
131    /// Enable/disable distance attenuation
132    pub fn distance_attenuation(mut self, enabled: bool) -> Self {
133        self.config.enable_distance_attenuation = enabled;
134        self
135    }
136
137    /// Enable/disable air absorption
138    pub fn air_absorption(mut self, enabled: bool) -> Self {
139        self.config.enable_air_absorption = enabled;
140        self
141    }
142
143    /// Enable/disable Doppler effect
144    pub fn doppler(mut self, enabled: bool) -> Self {
145        self.config.enable_doppler = enabled;
146        self
147    }
148
149    /// Set maximum processing distance
150    pub fn max_distance(mut self, max_distance: f32) -> Self {
151        self.config.max_distance = max_distance;
152        self
153    }
154
155    /// Set speed of sound
156    pub fn speed_of_sound(mut self, speed: f32) -> Self {
157        self.config.speed_of_sound = speed;
158        self
159    }
160
161    /// Build the configuration
162    pub fn build(self) -> crate::Result<SpatialConfig> {
163        self.config.validate()?;
164        Ok(self.config)
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn test_default_config() {
174        let config = SpatialConfig::default();
175        assert!(config.validate().is_ok());
176    }
177
178    #[test]
179    fn test_config_builder() {
180        let config = SpatialConfigBuilder::new()
181            .sample_rate(48000)
182            .buffer_size(512)
183            .reverb_time(1.5)
184            .build()
185            .unwrap();
186
187        assert_eq!(config.sample_rate, 48000);
188        assert_eq!(config.buffer_size, 512);
189        assert_eq!(config.reverb_time, 1.5);
190    }
191
192    #[test]
193    fn test_config_validation() {
194        let mut config = SpatialConfig::default();
195        config.sample_rate = 0;
196        assert!(config.validate().is_err());
197
198        config.sample_rate = 44100;
199        config.max_distance = -1.0;
200        assert!(config.validate().is_err());
201    }
202}