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}