1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
use crate::CepFloat;
use crate::fft::CepFft;
use crate::num_complex::{Complex, ComplexFloat};

/// The main struct of this crate; can be used to extract both complex and real cepstrums out of a signal.
///
/// As far as possible, when used multiple times, this struct will try to re-use internal data.
///
/// ## Examples
/// ```rust
/// use cepstrum_extractor::num_complex::Complex;
/// use cepstrum_extractor::num_traits::Zero;
/// use cepstrum_extractor::{CepstrumExtractor, Hann, RealToComplex};
///
/// let extractor = CepstrumExtractor::new(10);
///
/// // Different ways to obtain a vector of complex
/// let signal: Vec<Complex<f32>> = vec![Complex::zero(); 10];
/// let signal: Vec<Complex<f32>> = [0.; 10].to_complex_vec();
/// let signal: Vec<Complex<f32>> = [0.; 10].apply_hann_window_complex();
///
/// // Create new vectors of len `signal.len() / 2`
/// let real_ceps = extractor.rceps_to_vec(&signal);
/// let complex_ceps = extractor.cceps_to_vec(&signal);
///
/// // Use passed slices (useful range will be `0..len/2`)
/// let mut real_ceps = signal.clone();
/// extractor.rceps_mut(&mut real_ceps);
/// real_ceps.truncate(real_ceps.len() / 2);
///
/// let mut complex_ceps = signal.clone();
/// extractor.rceps_mut(&mut complex_ceps);
/// complex_ceps.truncate(complex_ceps.len() / 2);
/// ```
///
/// # Use in a concurrent environment
/// This extractor can be put in an Arc to be shared between different threads.
/// So one could use plain threads or external tools like tokio and rayon to perform parallel
/// computations. It is important to note, however, that each thread must always use the same
/// index, to make sure that the same part of the extractor is not
/// being used by another thread, in a certain moment.
/// `*_with_instance_*` methods require a `usize` parameter, representing the index of the thread
/// which is calling the method.
///
/// As it is built, the extractor has only one instance available, so it can be used only by one thread
/// at a time.
/// Calling [`Self::extend_instances`], one can increase the number of instances.
///
/// So, if one creates an extractor with 10 available instances, threads indices are numbered from 0
/// to 9.
///
/// ## Examples
/// ```rust
///
/// use std::sync::Arc;
/// use std::thread;
/// use cepstrum_extractor::num_complex::Complex;
/// use cepstrum_extractor::num_traits::Zero;
/// use cepstrum_extractor::CepstrumExtractor;
///
/// const THREADS: usize = 2;
/// const CEP_LEN: usize = 10;
///
/// let extractor: Arc<CepstrumExtractor<f32>> = Arc::new(CepstrumExtractor::new(CEP_LEN));
/// extractor.extend_instances(THREADS);
///
/// let signal: Vec<Complex<f32>> = vec![Complex::zero(); 100];
///
/// thread::scope(|s| {
///     for (idx, thread_chunk) in signal.chunks(signal.len() / THREADS).enumerate() {
///         let ex = extractor.clone();
///         s.spawn(move || {
///             for chunk in thread_chunk.chunks(CEP_LEN) {
///                 let complex_ceps = ex.cceps_with_instance_to_vec(chunk, idx);
///                 let real_ceps = ex.rceps_with_instance_to_vec(chunk, idx);
///             }
///         });
///     }
/// });
/// ```
pub struct CepstrumExtractor<T: CepFloat> {
    fft_instance: CepFft<T>,
}

impl<T: CepFloat> CepstrumExtractor<T> {
    fn _ceps_with_instance_mut(&self, mut signal: &mut [Complex<T>], f: fn(&Complex<T>) -> Complex<T>, instance: usize) {
        self.fft_instance.do_fft(&mut signal, instance);

        signal.iter_mut().for_each(|fft_component| {
            *fft_component = f(fft_component);
        });

        self.fft_instance.do_ifft(&mut signal, instance);
    }


    /// Builds a new extractor with a single instance available, i.e. an extractor to be used in a
    /// single-threaded environment.
    pub fn new(win_len: usize) -> CepstrumExtractor<T> {
        Self {
            fft_instance: CepFft::new(win_len)
        }
    }

    /// Sets the length of the window to `len`.
    pub fn set_len(&mut self, len: usize) {
        self.fft_instance.set_len(len);
    }

    /// Increases the number of instances available for parallel computing to `new_count`.
    pub fn extend_instances(&self, new_count: usize) {
        self.fft_instance.extend_scratches(new_count);
    }


    // ----------------------------------------- REAL ----------------------------------------------


    /// Extract the real cepstrum mutating the provided slice.
    /// <div class="warning">
    ///
    /// As for spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
    /// </div>
    pub fn rceps_mut(&self, mut signal: &mut [Complex<T>]) {
        self.rceps_with_instance_mut(&mut signal, 0);
    }

    /// Extract the real cepstrum placing the result in a new vec.
    /// Such a vec will be already truncated to half `signal.len()`.
    pub fn rceps_to_vec(&self, signal: &[Complex<T>]) -> Vec<Complex<T>> {
        let mut copied = Vec::with_capacity(signal.len());
        copied.extend_from_slice(signal);

        self.rceps_with_instance_mut(&mut copied, 0);
        copied.truncate(copied.len() / 2);

        copied
    }

    #[inline]
    fn r_f(x: &Complex<T>) -> Complex<T> {
        Complex::from(if x.re == T::zero() { x.abs() } else { x.abs().ln() })
    }
    /// As [`Self::rceps_mut`], but uses the passed instance at index `instance`.
    ///
    /// <div class="warning">
    ///
    /// As for spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
    /// </div>
    pub fn rceps_with_instance_mut(&self, signal: &mut [Complex<T>], instance: usize) {
        self._ceps_with_instance_mut(signal, Self::r_f, instance)
    }

    /// As [`Self::rceps_to_vec`], but uses the passed instance at index `instance`.
    pub fn rceps_with_instance_to_vec(&self, signal: &[Complex<T>], instance: usize) -> Vec<Complex<T>> {
        let mut copied = Vec::with_capacity(signal.len());
        copied.extend_from_slice(signal);

        self.rceps_with_instance_mut(&mut copied, instance);
        copied.truncate(copied.len() / 2);

        copied
    }


    // --------------------------------------- COMPLEX ---------------------------------------------


    /// Extract the complex cepstrum mutating the provided slice.
    /// <div class="warning">
    ///
    /// As for spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
    /// </div>
    pub fn cceps_mut(&self, mut signal: &mut [Complex<T>]) {
        self.rceps_with_instance_mut(&mut signal, 0);
    }

    /// Extract the complex cepstrum placing the result in a new vec.
    /// Such a vec will be already truncated to half `signal.len()`.
    pub fn cceps_to_vec(&self, signal: &[Complex<T>]) -> Vec<Complex<T>> {
        let mut copied = Vec::with_capacity(signal.len());
        copied.extend_from_slice(signal);

        self.rceps_with_instance_mut(&mut copied, 0);
        copied.truncate(copied.len() / 2);

        copied
    }

    #[inline]
    fn c_f(x: &Complex<T>) -> Complex<T> {
        if x.re == T::zero() { *x } else { x.ln() }
    }
    /// As [`Self::cceps_mut`], but uses the passed instance at index `instance`.
    ///
    /// <div class="warning">
    ///
    /// As for spectrums, the meaningful area will be `signal[0..signal.len() / 2]`.
    /// </div>
    pub fn cceps_with_instance_mut(&self, signal: &mut [Complex<T>], instance: usize) {
        self._ceps_with_instance_mut(signal, Self::c_f, instance)
    }

    /// As [`Self::cceps_to_vec`], but uses the passed instance at index `instance`.
    pub fn cceps_with_instance_to_vec(&self, signal: &[Complex<T>], instance: usize) -> Vec<Complex<T>> {
        let mut copied = Vec::with_capacity(signal.len());
        copied.extend_from_slice(signal);

        self.rceps_with_instance_mut(&mut copied, instance);
        copied.truncate(copied.len() / 2);

        copied
    }
}