bpm_analyzer/
config.rs

1//! Configuration types for the BPM analyzer.
2
3use crate::error::{Error, Result};
4
5/// Size of the cross-thread audio queue
6const QUEUE_SIZE: usize = 4096;
7/// Number of levels for discrete wavelet transform decomposition
8pub(crate) const DWT_LEVELS: usize = 4;
9/// Size of the audio buffer for capture
10const AUDIO_BUFFER_SIZE: u32 = 256;
11/// Window size for DWT analysis (must be power of 2)
12const DWT_WINDOW_SIZE: usize = 65536;
13/// Target sampling rate for analysis (Hz)
14pub(crate) const TARGET_SAMPLING_RATE: f64 = 22050.0;
15
16/// Default minimum BPM for detection range
17const MIN_BPM: f32 = 40.0;
18/// Default maximum BPM for detection range
19const MAX_BPM: f32 = 240.0;
20
21/// Configuration for the BPM analyzer.
22///
23/// Use the builder pattern to customize analyzer parameters:
24///
25/// # Example
26///
27/// ```
28/// use bpm_analyzer::AnalyzerConfig;
29///
30/// let config = AnalyzerConfig::builder()
31///     .min_bpm(60.0)
32///     .max_bpm(180.0)
33///     .window_size(32768)
34///     .build();
35/// ```
36#[derive(Clone, Debug, Copy, bon::Builder)]
37pub struct AnalyzerConfig {
38    /// Minimum BPM to detect (default: 40.0)
39    #[builder(default = MIN_BPM)]
40    min_bpm: f32,
41    /// Maximum BPM to detect (default: 240.0)
42    #[builder(default = MAX_BPM)]
43    max_bpm: f32,
44    /// Size of the analysis window in samples (default: 65536)
45    #[builder(default = DWT_WINDOW_SIZE)]
46    window_size: usize,
47    /// Size of the audio queue between threads (default: 4096)
48    #[builder(default = QUEUE_SIZE)]
49    queue_size: usize,
50    /// Size of the audio capture buffer (default: 256)
51    #[builder(default = AUDIO_BUFFER_SIZE)]
52    buffer_size: u32,
53}
54
55impl AnalyzerConfig {
56    /// Creates a configuration preset optimized for electronic music (100-160 BPM).
57    pub fn electronic() -> Self {
58        Self::builder().min_bpm(100.0).max_bpm(160.0).build()
59    }
60
61    /// Creates a configuration preset optimized for hip-hop (80-110 BPM).
62    pub fn hip_hop() -> Self {
63        Self::builder().min_bpm(80.0).max_bpm(110.0).build()
64    }
65
66    /// Creates a configuration preset optimized for classical music (40-100 BPM).
67    pub fn classical() -> Self {
68        Self::builder().min_bpm(40.0).max_bpm(100.0).build()
69    }
70
71    /// Creates a configuration preset optimized for rock/pop (110-140 BPM).
72    pub fn rock_pop() -> Self {
73        Self::builder().min_bpm(110.0).max_bpm(140.0).build()
74    }
75
76    /// Returns the minimum BPM setting.
77    pub fn min_bpm(&self) -> f32 {
78        self.min_bpm
79    }
80
81    /// Returns the maximum BPM setting.
82    pub fn max_bpm(&self) -> f32 {
83        self.max_bpm
84    }
85
86    /// Returns the window size setting.
87    pub fn window_size(&self) -> usize {
88        self.window_size
89    }
90
91    /// Returns the queue size setting.
92    pub fn queue_size(&self) -> usize {
93        self.queue_size
94    }
95
96    /// Returns the buffer size setting.
97    pub fn buffer_size(&self) -> u32 {
98        self.buffer_size
99    }
100
101    /// Validates the configuration and returns an error if invalid.
102    pub fn validate(&self) -> Result<()> {
103        if self.min_bpm <= 0.0 {
104            return Err(Error::InvalidConfig("min_bpm must be positive".to_string()));
105        }
106        if self.max_bpm <= self.min_bpm {
107            return Err(Error::InvalidConfig(
108                "max_bpm must be greater than min_bpm".to_string(),
109            ));
110        }
111        if self.window_size == 0 || !self.window_size.is_power_of_two() {
112            return Err(Error::InvalidConfig(
113                "window_size must be a power of 2".to_string(),
114            ));
115        }
116        if self.queue_size == 0 {
117            return Err(Error::InvalidConfig(
118                "queue_size must be positive".to_string(),
119            ));
120        }
121        if self.buffer_size == 0 {
122            return Err(Error::InvalidConfig(
123                "buffer_size must be positive".to_string(),
124            ));
125        }
126        Ok(())
127    }
128}