1use crate::error::{Error, Result};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum WindowKind {
7 Rectangular,
8 PeriodicHann,
9 PeriodicHamming,
10}
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum PadMode {
14 Reflect,
15 Zero,
16}
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum SpectrumKind {
20 OneSide,
21}
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum SpectralNormalization {
25 None,
26}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum AudioFeatureLayout {
30 FramesFirst,
31 MelsFirst,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq)]
35pub struct StftConfig {
36 pub n_fft: usize,
37 pub hop_length: usize,
38 pub win_length: usize,
39 pub center: bool,
40 pub pad_mode: PadMode,
41 pub window: WindowKind,
42 pub spectrum: SpectrumKind,
43 pub normalization: SpectralNormalization,
44 pub drop_last_frame: bool,
45}
46
47impl StftConfig {
48 pub const fn frequency_bin_count(self) -> usize {
49 match self.spectrum {
50 SpectrumKind::OneSide => self.n_fft / 2 + 1,
51 }
52 }
53
54 pub fn centered_input_len(self, input_len: usize) -> Result<usize> {
55 if !self.center {
56 return Ok(input_len);
57 }
58 let pad = self.n_fft / 2;
59 input_len
60 .checked_add(pad)
61 .and_then(|len| len.checked_add(pad))
62 .ok_or(Error::SizeOverflow)
63 }
64
65 pub fn frame_count(self, input_len: usize) -> Result<usize> {
66 validate_stft_config(self)?;
67 let padded_len = self.centered_input_len(input_len)?;
68 if padded_len < self.n_fft {
69 return Ok(0);
70 }
71
72 let mut frames = (padded_len - self.n_fft) / self.hop_length + 1;
73 if self.drop_last_frame {
74 frames = frames.saturating_sub(1);
75 }
76 Ok(frames)
77 }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
81pub struct MelFilterConfig {
82 pub sample_rate: usize,
83 pub frequency_bin_count: usize,
84 pub mel_bin_count: usize,
85 pub min_frequency: f32,
86 pub max_frequency: f32,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub struct LogMelConfig {
91 pub floor: f32,
92 pub reference_max: Option<f32>,
93 pub dynamic_range: Option<f32>,
94 pub offset: f32,
95 pub scale: f32,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq)]
99pub struct AudioFrontendConfig {
100 pub stft: StftConfig,
101 pub mel: MelFilterConfig,
102 pub log_mel: LogMelConfig,
103 pub layout: AudioFeatureLayout,
104}
105
106impl AudioFrontendConfig {
107 pub fn validate(self) -> Result<()> {
108 validate_stft_config(self.stft)?;
109 validate_mel_filter_config(self.mel)?;
110 validate_log_mel_config(self.log_mel)?;
111 if self.stft.frequency_bin_count() != self.mel.frequency_bin_count {
112 return Err(Error::LengthMismatch);
113 }
114 Ok(())
115 }
116}
117
118fn validate_stft_config(config: StftConfig) -> Result<()> {
119 if config.n_fft == 0 || config.hop_length == 0 || config.win_length == 0 {
120 return Err(Error::InvalidLength);
121 }
122 if config.win_length > config.n_fft {
123 return Err(Error::InvalidLength);
124 }
125 if !config.n_fft.is_multiple_of(2) {
126 return Err(Error::UnsupportedParameter {
127 op: "stft".into(),
128 parameter: "n_fft".into(),
129 value: config.n_fft,
130 });
131 }
132 Ok(())
133}
134
135fn validate_mel_filter_config(config: MelFilterConfig) -> Result<()> {
136 if config.sample_rate == 0 || config.frequency_bin_count == 0 || config.mel_bin_count == 0 {
137 return Err(Error::InvalidLength);
138 }
139 let nyquist = config.sample_rate as f32 / 2.0;
140 if !config.min_frequency.is_finite()
141 || !config.max_frequency.is_finite()
142 || config.min_frequency < 0.0
143 || config.max_frequency <= config.min_frequency
144 || config.max_frequency > nyquist
145 {
146 return Err(Error::InvalidLength);
147 }
148 Ok(())
149}
150
151fn validate_log_mel_config(config: LogMelConfig) -> Result<()> {
152 if !config.floor.is_finite()
153 || config.floor <= 0.0
154 || !config.offset.is_finite()
155 || !config.scale.is_finite()
156 {
157 return Err(Error::InvalidLength);
158 }
159 if let Some(dynamic_range) = config.dynamic_range
160 && (!dynamic_range.is_finite() || dynamic_range <= 0.0)
161 {
162 return Err(Error::InvalidLength);
163 }
164 if let Some(reference_max) = config.reference_max
165 && !reference_max.is_finite()
166 {
167 return Err(Error::InvalidLength);
168 }
169 Ok(())
170}