1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef CONVERSION_FFT_H
17 #define CONVERSION_FFT_H
18 
19 #include "fft.h"
20 
21 namespace OHOS {
22 namespace Sensors {
23 /**
24  * @brief The parameters required for FFT
25  */
26 struct FFTInputPara {
27     /** sampling rate of audio file. */
28     int32_t sampleRate { 0 };
29     /** the FFT size. This must be a power of 2. */
30     int32_t fftSize { 0 };
31     /** the hop size. */
32     int32_t hopSize { 0 };
33     /** the window size. */
34     int32_t windowSize { 0 };
35 };
36 
37 struct FFTOutputResult {
38     std::vector<float> magnitudes;
39     std::vector<float> phases;
40     std::vector<float> magnitudesDB;
41     std::vector<float> buffer;
42     std::vector<float> window;
43 };
44 
45 /**
46  * Fast fourier transform. For spectral audio process and machine listening.
47  */
48 class ConversionFFT {
49 public:
50     /**
51      * @brief How to run the FFT - with or without polar conversion
52      */
53     enum FFTModes {
54         NO_POLAR_CONVERSION = 0,
55         WITH_POLAR_CONVERSION = 1
56     };
57 
58     ConversionFFT() = default;
59     ~ConversionFFT() = default;
60 
61     /**
62      * @brief the FFT.
63      *
64      * @param fftSize the FFT size. This must be a power of 2
65      * @param hopSize the hop size
66      * @param windowSize the window size
67      *
68      * @return Returns <b>0</b> if the operation is successful; returns a negative value otherwise.
69      */
70     int32_t Init(const FFTInputPara &fftPara);
71 
72     /**
73      * @brief The sampled data is processed frame by frame
74      * @param values A set of signal data
75      * @return Return 0 when the analysis has run (every [hopsize] samples)
76      */
77     int32_t Process(const std::vector<double> &values, int32_t &frameCount, std::vector<float> &frameMagsArr);
78 
79     /**
80      * @brief Process the sampled data one by one
81      * @param value A sampling value.
82      * @param mode see FFTModes enumeration
83      * @return Return true when the analysis has run (every [hopsize] samples)
84      */
85     bool ProcessSingle(float value, FFTModes mode = ConversionFFT::WITH_POLAR_CONVERSION);
86 
87     /**
88      * @brief Get the complex real object
89      *
90      * @return Return a pointer to an array of values containing the real components of the fft analysis.
91      */
GetReal()92     std::vector<float> GetReal()
93     {
94         return fft_.GetReal();
95     };
96 
97     /**
98      * @brief Get the complex imaginary part
99      *
100      * @return Return a pointer to an array of values containing the imaginary components of the fft analysis
101      */
GetImag()102     std::vector<float> GetImag()
103     {
104         return fft_.GetImg();
105     };
106 
107     /**
108      * @brief Get the magnitudes object
109      *
110      * @return a vector of magnitudes (assuming the FFT was calcuated with polar conversion)
111      */
GetMagnitudes()112     std::vector<float> GetMagnitudes()
113     {
114         return fftResult_.magnitudes;
115     }
116 
117     /**
118      * @brief Get the magnitudes DB object
119      *
120      * @return Return a vector of magnitudes in decibels (assuming the FFT was calcuated with polar conversion).
121      */
GetMagnitudesDB()122     std::vector<float> GetMagnitudesDB()
123     {
124         return ConvertDB();
125     }
126 
127     /**
128      * @brief Get the phases object
129      *
130      * @return Return a vector of phases (assuming the FFT was calcuated with polar conversion)
131      */
GetPhases()132     std::vector<float> GetPhases()
133     {
134         return fftResult_.phases;
135     }
136 
137     /**
138      * @brief Get the num bins object
139      *
140      * @return Return the number of bins in the FFT analysis
141      */
GetNumBins()142     int32_t GetNumBins() const
143     {
144         return bins_;
145     }
146 
147     /**
148      * @brief Get the FFT size
149      *
150      * @return Return the FFT size
151      */
GetFFTSize()152     int32_t GetFFTSize()
153     {
154         return para_.fftSize;
155     }
156 
157     /**
158      * @brief Get the hop size
159      *
160      * @return Return the hop size
161      */
GetHopSize()162     int32_t GetHopSize()
163     {
164         return para_.hopSize;
165     }
166 
167     /**
168      * @brief Get the window size
169      *
170      * @return Return the window size
171      */
GetWindowSize()172     int32_t GetWindowSize()
173     {
174         return para_.windowSize;
175     }
176 
177     /**
178      * @brief Calculate the spectral flatness of the most recent FFT
179      *
180      * @return Return the spectral flatness of the most recent FFT calculation
181      */
182     float GetSpectralFlatness() const;
183 
184     /**
185      * @brief Calculate the spectral centroid of the most recent FFT.
186      *
187      * @return Return the spectral centroid of the most recent FFT calculation
188      */
189     float GetSpectralCentroid() const;
190 
191 private:
192     std::vector<float> &ConvertDB();
193 
194 private:
195     FFTInputPara para_;
196     FFTOutputResult fftResult_;
197     int32_t pos_ { 0 };
198     Fft fft_;
199     bool isFrameFull_ { false };
200     int32_t bins_ { 0 };
201     bool isFftCalcFinish_ { false };
202 };
203 
204 /**
205  * @brief Inverse FFT transform
206  */
207 class ConversionIFFT {
208 public:
209     /**
210      * @brief Configure what kind of data is being given to the inverse FFT
211      *
212      */
213     enum FFTModes {
214          /** Magnitudes and phases */
215         SPECTRUM = 0,
216         /** Real and imaginary components */
217         COMPLEX = 1
218     };
219 
220     ConversionIFFT() = default;
221     ~ConversionIFFT() = default;
222 
223     /**
224      * @brief the inverse FFT.
225      *
226      * @param fftSize the FFT size. This must be a power of 2
227      * @param hopSize the hop size
228      * @param windowSize the window size
229      * @return Returns <b>0</b> if the operation is successful; returns a negative value otherwise.
230      */
231     int32_t Init(int32_t fftSize, int32_t hopSize, int32_t windowSize);
232 
233     /**
234      * @brief the inverse transform. Call this function at audio rate, but update the FFT information at FFT rate.
235      *
236      * @param data1 either magnitudes or real values
237      * @param data2 either phases or imaginary values
238      * @param mode see FFTModes
239      * @return Return the most recent sample an audio signal, creates from the inverse transform of the FFT data
240      */
241     float Process(const std::vector<float> &mags, const std::vector<float> &phases,
242 	    const FFTModes mode = ConversionIFFT::SPECTRUM);
243 
244     /**
245      * @brief Get the num bins
246      *
247      * @return Return the number of fft bins
248      */
GetNumBins()249     int32_t GetNumBins() const
250     {
251         return bins_;
252     }
253 
254 private:
255     FFTInputPara para_;
256     /** output buffer and window. */
257     FFTOutputResult fftResult_;
258     std::vector<float> ifftOut_;
259     int32_t bins_ { 0 };
260     int32_t pos_ { 0 };
261     float nextValue_ { 0.0F };
262     Fft fft_;
263 };
264 
265 /**
266  * @brief octave analyser. It takes FFT magnitudes and remaps them into pitches
267  */
268 class ConversionOctave {
269 public:
270     ConversionOctave() = default;
271     ~ConversionOctave() = default;
272 
273     /**
274      * @brief Initializes the octave note analyzer
275      *
276      * @param samplingRate the sample rate
277      * @param nBandsInTheFFT the number of bins in the FFT
278      * @param nAveragesPerOctave how many frequency bands to split each octave into
279      *
280      * @return Returns <b>0</b> if the operation is successful; returns a negative value otherwise.
281      */
282     int32_t Init(float samplingRate, int32_t nBandsInTheFFT, int32_t nAveragesPerOctave);
283 
284     /**
285      * @brief Calculate the octave note related parameters
286      *
287      * @param fftData a pointer to an array of fft magnitudes
288      *
289      * @return Returns <b>0</b> if the operation is successful; returns a negative value otherwise.
290      */
291     int32_t Calculate(const std::vector<float> &fftData);
292 
293 private:
294     float samplingRate_ { 0.0F }; // sampling rate in Hz (needed to calculate frequency spans)
295     int32_t nSpectrum_ { 0 };  // number of spectrum bins in the fft
296     /** the number of averages, after analysis */
297     int32_t nAverages_ { 0 };               // number of averaging bins here
298     float spectrumFrequencySpan_ { 0.0F };     // the "width" of an fft spectrum bin in Hz
299     float firstOctaveFrequency_ { 0.0F };      // the "top" of the first averaging bin here in Hz
300     float averageFrequencyIncrement_ { 0.0F }; // the root-of-two multiplier between averaging bin frequencies
301     /** An array of averages - the energy across the pitch spectrum */
302     std::shared_ptr<float> averages_ { nullptr };        // the actual averages
303     std::shared_ptr<float> peaks_ { nullptr };           // peaks of the averages, aka "maxAverages" in other implementations
304     std::shared_ptr<int32_t> peakHoldTimes_ { nullptr }; // how long to hold THIS peak meter?  decay if == 0
305     int32_t peakHoldTime_ { 0 };   // how long do we hold peaks? (in fft frames)
306     float peakDecayRate_ { 0.0F };    // how quickly the peaks decay:  0f=instantly .. 1f=not at all
307     std::shared_ptr<int32_t> spe2avg_ { nullptr };       // the mapping between spectrum[] indices and averages[] indices
308     // the fft's log equalizer() is no longer of any use (it would be nonsense to log scale
309     // the spectrum values into log-sized average bins) so here's a quick-and-dirty linear
310     // equalizer instead:
311     float linearEQSlope_ { 0.0F };     // the rate of linear eq
312     float linearEQIntercept_ { 0.0F }; // the base linear scaling used at the first averaging bin
313     // the formula is:  spectrum[i] * (linearEQIntercept + i * linearEQSlope)
314     // so.. note that clever use of it can also provide a "gain" control of sorts
315     // (fe: set intercept to 2f and slope to 0f to double gain)
316 };
317 } // namespace Sensors
318 } // namespace OHOS
319 #endif // CONVERSION_FFT_H
320