cepstrum_extractor/
cepstrum.rs

1//! Module used to extract cepstrums.
2
3use crate::fft::CepFft;
4use crate::num_complex::{Complex, ComplexFloat};
5use crate::CepFloat;
6
7/// The main struct of this crate; can be used to extract both complex and real cepstrums from a signal.
8///
9/// As far as possible, when used multiple times, this struct will try to re-use internal data.
10///
11/// ## Examples
12/// ```rust
13/// use cepstrum_extractor::num_complex::Complex;
14/// use cepstrum_extractor::num_traits::Zero;
15/// use cepstrum_extractor::{CepstrumExtractor, RealToComplex};
16/// use cepstrum_extractor::windows::hann::Hann;
17///
18/// let extractor = CepstrumExtractor::new(10);
19///
20/// // Different ways to obtain a vector of complex
21/// let signal: Vec<Complex<f32>> = vec![Complex::zero(); 10];
22/// let signal: Vec<Complex<f32>> = [0.; 10].to_complex_vec();
23/// let signal: Vec<Complex<f32>> = [0.; 10].hann_complex();
24///
25/// // Create new vectors of len `signal.len() / 2`
26/// let real_ceps = extractor.rceps_to_vec(&signal);
27/// let complex_ceps = extractor.cceps_to_vec(&signal);
28///
29/// // Use passed slices (useful range will be `0..len/2`)
30/// let mut real_ceps = signal.clone();
31/// extractor.rceps_mut(&mut real_ceps);
32/// real_ceps.truncate(real_ceps.len() / 2);
33///
34/// let mut complex_ceps = signal.clone();
35/// extractor.cceps_mut(&mut complex_ceps);
36/// complex_ceps.truncate(complex_ceps.len() / 2);
37/// ```
38///
39/// # Use in a concurrent environment
40/// This extractor can be placed in an `Arc` to be shared between different threads.
41/// You can use plain threads or external tools like Tokio and Rayon to perform parallel
42/// computations. It is important to note, however, that each thread must always use the same
43/// index to ensure that the same part of the extractor is not being used by another thread
44/// at the same time. The `*_with_instance_*` methods require a `usize` parameter, representing
45/// the index of the thread that is calling the method.
46///
47/// As it is built, the extractor has only one instance available, so it can be used only by one thread
48/// at a time. By calling [`Self::extend_instances`], you can increase the number of instances.
49///
50/// If you create an extractor with 10 available instances, thread indices are numbered from 0
51/// to 9.
52///
53/// ## Examples
54/// ```rust
55///
56/// use std::sync::Arc;
57/// use std::thread;
58/// use cepstrum_extractor::num_complex::Complex;
59/// use cepstrum_extractor::num_traits::Zero;
60/// use cepstrum_extractor::CepstrumExtractor;
61///
62/// const THREADS: usize = 2;
63/// const CEP_LEN: usize = 10;
64///
65/// let extractor: Arc<CepstrumExtractor<f32>> = Arc::new(CepstrumExtractor::new(CEP_LEN));
66/// extractor.extend_instances(THREADS);
67///
68/// let signal: Vec<Complex<f32>> = vec![Complex::zero(); 100];
69///
70/// thread::scope(|s| {
71///     for (idx, thread_chunk) in signal.chunks(signal.len() / THREADS).enumerate() {
72///         let ex = extractor.clone();
73///         s.spawn(move || {
74///             for chunk in thread_chunk.chunks(CEP_LEN) {
75///                 let complex_ceps = ex.cceps_with_instance_to_vec(chunk, idx);
76///                 let real_ceps = ex.rceps_with_instance_to_vec(chunk, idx);
77///             }
78///         });
79///     }
80/// });
81/// ```
82pub struct CepstrumExtractor<T: CepFloat> {
83    fft_instance: CepFft<T>,
84}
85
86impl<T: CepFloat> CepstrumExtractor<T> {
87    fn _ceps_with_instance_mut(
88        &self,
89        mut signal: &mut [Complex<T>],
90        f: fn(&Complex<T>) -> Complex<T>,
91        instance: usize,
92    ) {
93        self.fft_instance.do_fft(&mut signal, instance);
94
95        signal.iter_mut().for_each(|fft_component| {
96            *fft_component = f(fft_component);
97        });
98
99        self.fft_instance.do_ifft(&mut signal, instance);
100    }
101
102    /// Builds a new extractor with a single instance available, i.e. an extractor to be used in a
103    /// single-threaded environment.
104    pub fn new(win_len: usize) -> CepstrumExtractor<T> {
105        Self {
106            fft_instance: CepFft::new(win_len),
107        }
108    }
109
110    /// Sets the length of the window to `len`.
111    pub fn set_len(&mut self, len: usize) {
112        self.fft_instance.set_len(len);
113    }
114
115    /// Increases the number of instances available for parallel computing to `new_count`.
116    pub fn extend_instances(&self, new_count: usize) {
117        self.fft_instance.extend_scratches(new_count);
118    }
119
120    // ----------------------------------------- REAL ----------------------------------------------
121
122    /// Extract the real cepstrum mutating the provided slice.
123    /// <div class="warning">
124    ///
125    /// As with spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
126    /// </div>
127    pub fn rceps_mut(&self, mut signal: &mut [Complex<T>]) {
128        self.rceps_with_instance_mut(&mut signal, 0);
129    }
130
131    /// Extract the real cepstrum placing the result in a new vector.
132    /// Such a vector will be already truncated to half `signal.len()`.
133    pub fn rceps_to_vec(&self, signal: &[Complex<T>]) -> Vec<Complex<T>> {
134        let mut copied = signal.to_vec();
135
136        self.rceps_with_instance_mut(&mut copied, 0);
137        copied.truncate(copied.len() / 2);
138
139        copied
140    }
141
142    #[inline]
143    fn r_f(x: &Complex<T>) -> Complex<T> {
144        Complex::from(if x.re == T::zero() {
145            x.abs()
146        } else {
147            x.abs().ln()
148        })
149    }
150    /// As [`Self::rceps_mut`], but uses the passed instance at index `instance`.
151    ///
152    /// <div class="warning">
153    ///
154    /// As with spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
155    /// </div>
156    pub fn rceps_with_instance_mut(&self, signal: &mut [Complex<T>], instance: usize) {
157        self._ceps_with_instance_mut(signal, Self::r_f, instance)
158    }
159
160    /// As [`Self::rceps_to_vec`], but uses the passed instance at index `instance`.
161    pub fn rceps_with_instance_to_vec(
162        &self,
163        signal: &[Complex<T>],
164        instance: usize,
165    ) -> Vec<Complex<T>> {
166        let mut copied = signal.to_vec();
167
168        self.rceps_with_instance_mut(&mut copied, instance);
169        copied.truncate(copied.len() / 2);
170
171        copied
172    }
173
174    // --------------------------------------- COMPLEX ---------------------------------------------
175
176    /// Extract the complex cepstrum mutating the provided slice.
177    /// <div class="warning">
178    ///
179    /// As with spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
180    /// </div>
181    pub fn cceps_mut(&self, mut signal: &mut [Complex<T>]) {
182        self.rceps_with_instance_mut(&mut signal, 0);
183    }
184
185    /// Extract the complex cepstrum placing the result in a new vector.
186    /// Such a vector will be already truncated to half `signal.len()`.
187    pub fn cceps_to_vec(&self, signal: &[Complex<T>]) -> Vec<Complex<T>> {
188        let mut copied = signal.to_vec();
189
190        self.rceps_with_instance_mut(&mut copied, 0);
191        copied.truncate(copied.len() / 2);
192
193        copied
194    }
195
196    #[inline]
197    fn c_f(x: &Complex<T>) -> Complex<T> {
198        if x.re == T::zero() {
199            *x
200        } else {
201            x.ln()
202        }
203    }
204    /// As [`Self::cceps_mut`], but uses the passed instance at index `instance`.
205    ///
206    /// <div class="warning">
207    ///
208    /// As with spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
209    /// </div>
210    pub fn cceps_with_instance_mut(&self, signal: &mut [Complex<T>], instance: usize) {
211        self._ceps_with_instance_mut(signal, Self::c_f, instance)
212    }
213
214    /// As [`Self::cceps_to_vec`], but uses the passed instance at index `instance`.
215    pub fn cceps_with_instance_to_vec(
216        &self,
217        signal: &[Complex<T>],
218        instance: usize,
219    ) -> Vec<Complex<T>> {
220        let mut copied = signal.to_vec();
221
222        self.rceps_with_instance_mut(&mut copied, instance);
223        copied.truncate(copied.len() / 2);
224
225        copied
226    }
227}