xref: /aosp_15_r20/external/armnn/samples/common/include/Audio/MFCC.hpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 
8 #include <vector>
9 #include <cstdint>
10 #include <cmath>
11 #include <limits>
12 #include <string>
13 
14 /* MFCC's consolidated parameters */
15 class MfccParams
16 {
17 public:
18     float       m_samplingFreq;
19     int         m_numFbankBins;
20     float       m_melLoFreq;
21     float       m_melHiFreq;
22     int         m_numMfccFeatures;
23     int         m_frameLen;
24     int         m_frameLenPadded;
25     bool        m_useHtkMethod;
26     int         m_numMfccVectors;
27     /** @brief  Constructor */
28     MfccParams(const float samplingFreq, const int numFbankBins,
29                const float melLoFreq, const float melHiFreq,
30                const int numMfccFeats, const int frameLen,
31                const bool useHtkMethod, const int numMfccVectors);
32     /* Delete the default constructor */
33     MfccParams()  = delete;
34     /* Default destructor */
35     ~MfccParams() = default;
36     /** @brief  String representation of parameters */
37     std::string Str();
38 };
39 
40 /**
41  * @brief   Class for MFCC feature extraction.
42  *          Based on https://github.com/ARM-software/ML-KWS-for-MCU/blob/master/Deployment/Source/MFCC/mfcc.cpp
43  *          This class is designed to be generic and self-sufficient but
44  *          certain calculation routines can be overridden to accommodate
45  *          use-case specific requirements.
46  */
47 class MFCC {
48 public:
49     /**
50      * @brief       Constructor
51      * @param[in]   params   MFCC parameters
52     */
53     explicit MFCC(const MfccParams& params);
54 
55     MFCC() = delete;
56 
57     ~MFCC() = default;
58 
59     /**
60     * @brief        Extract MFCC  features for one single small frame of
61     *               audio data e.g. 640 samples.
62     * @param[in]    audioData   Vector of audio samples to calculate
63     *                           features for.
64     * @return       Vector of extracted MFCC features.
65     **/
66     std::vector<float> MfccCompute(const std::vector<float>& audioData);
67 
68     /** @brief  Initialise. */
69     void Init();
70 
71    /**
72     * @brief        Extract MFCC features and quantise for one single small
73     *               frame of audio data e.g. 640 samples.
74     * @param[in]    audioData     Vector of audio samples to calculate
75     *                             features for.
76     * @param[in]    quantScale    Quantisation scale.
77     * @param[in]    quantOffset   Quantisation offset.
78     * @return       Vector of extracted quantised MFCC features.
79     **/
80     template<typename T>
MfccComputeQuant(const std::vector<float> & audioData,const float quantScale,const int quantOffset)81     std::vector<T> MfccComputeQuant(const std::vector<float>& audioData,
82                                     const float quantScale,
83                                     const int quantOffset)
84     {
85         this->MfccComputePreFeature(audioData);
86         float minVal = std::numeric_limits<T>::min();
87         float maxVal = std::numeric_limits<T>::max();
88 
89         std::vector<T> mfccOut(this->m_params.m_numMfccFeatures);
90         const size_t numFbankBins = this->m_params.m_numFbankBins;
91 
92         /* Take DCT. Uses matrix mul. */
93         for (size_t i = 0, j = 0; i < mfccOut.size(); ++i, j += numFbankBins)
94         {
95             float sum = 0;
96             for (size_t k = 0; k < numFbankBins; ++k)
97             {
98                 sum += this->m_dctMatrix[j + k] * this->m_melEnergies[k];
99             }
100             /* Quantize to T. */
101             sum = std::round((sum / quantScale) + quantOffset);
102             mfccOut[i] = static_cast<T>(std::min<float>(std::max<float>(sum, minVal), maxVal));
103         }
104 
105         return mfccOut;
106     }
107 
108     MfccParams m_params;
109 
110     /* Constants */
111     static constexpr float ms_logStep = /*logf(6.4)*/ 1.8562979903656 / 27.0;
112     static constexpr float ms_freqStep = 200.0 / 3;
113     static constexpr float ms_minLogHz = 1000.0;
114     static constexpr float ms_minLogMel = ms_minLogHz / ms_freqStep;
115 
116 protected:
117     /**
118      * @brief       Project input frequency to Mel Scale.
119      * @param[in]   freq           Input frequency in floating point.
120      * @param[in]   useHTKMethod   bool to signal if HTK method is to be
121      *                             used for calculation.
122      * @return      Mel transformed frequency in floating point.
123      **/
124     static float MelScale(float freq,
125                           bool  useHTKMethod = true);
126 
127     /**
128      * @brief       Inverse Mel transform - convert MEL warped frequency
129      *              back to normal frequency.
130      * @param[in]   melFreq        Mel frequency in floating point.
131      * @param[in]   useHTKMethod   bool to signal if HTK method is to be
132      *                             used for calculation.
133      * @return      Real world frequency in floating point.
134      **/
135     static float InverseMelScale(float melFreq,
136                                  bool  useHTKMethod = true);
137 
138     /**
139      * @brief       Populates MEL energies after applying the MEL filter
140      *              bank weights and adding them up to be placed into
141      *              bins, according to the filter bank's first and last
142      *              indices (pre-computed for each filter bank element
143      *              by CreateMelFilterBank function).
144      * @param[in]   fftVec                  Vector populated with FFT magnitudes.
145      * @param[in]   melFilterBank           2D Vector with filter bank weights.
146      * @param[in]   filterBankFilterFirst   Vector containing the first indices of filter bank
147      *                                      to be used for each bin.
148      * @param[in]   filterBankFilterLast    Vector containing the last indices of filter bank
149      *                                      to be used for each bin.
150      * @param[out]  melEnergies             Pre-allocated vector of MEL energies to be
151      *                                      populated.
152      * @return      true if successful, false otherwise.
153      */
154     virtual bool ApplyMelFilterBank(
155         std::vector<float>&                 fftVec,
156         std::vector<std::vector<float>>&    melFilterBank,
157         std::vector<uint32_t>&              filterBankFilterFirst,
158         std::vector<uint32_t>&              filterBankFilterLast,
159         std::vector<float>&                 melEnergies);
160 
161     /**
162      * @brief           Converts the Mel energies for logarithmic scale.
163      * @param[in,out]   melEnergies   1D vector of Mel energies.
164      **/
165     virtual void ConvertToLogarithmicScale(std::vector<float>& melEnergies);
166 
167     /**
168      * @brief       Create a matrix used to calculate Discrete Cosine
169      *              Transform.
170      * @param[in]   inputLength        Input length of the buffer on which
171      *                                 DCT will be performed.
172      * @param[in]   coefficientCount   Total coefficients per input length.
173      * @return      1D vector with inputLength x coefficientCount elements
174      *              populated with DCT coefficients.
175      */
176     virtual std::vector<float> CreateDCTMatrix(
177                                 int32_t inputLength,
178                                 int32_t coefficientCount);
179 
180     /**
181      * @brief       Given the low and high Mel values, get the normaliser
182      *              for weights to be applied when populating the filter
183      *              bank.
184      * @param[in]   leftMel        Low Mel frequency value.
185      * @param[in]   rightMel       High Mel frequency value.
186      * @param[in]   useHTKMethod   bool to signal if HTK method is to be
187      *                             used for calculation.
188      * @return      Value to use for normalizing.
189      */
190     virtual float GetMelFilterBankNormaliser(
191                     const float&   leftMel,
192                     const float&   rightMel,
193                     bool     useHTKMethod);
194 
195 private:
196 
197     std::vector<float>              m_frame;
198     std::vector<float>              m_buffer;
199     std::vector<float>              m_melEnergies;
200     std::vector<float>              m_windowFunc;
201     std::vector<std::vector<float>> m_melFilterBank;
202     std::vector<float>              m_dctMatrix;
203     std::vector<uint32_t>           m_filterBankFilterFirst;
204     std::vector<uint32_t>           m_filterBankFilterLast;
205     bool                            m_filterBankInitialised;
206 
207     /**
208      * @brief       Initialises the filter banks and the DCT matrix. **/
209     void InitMelFilterBank();
210 
211     /**
212      * @brief       Signals whether the instance of MFCC has had its
213      *              required buffers initialised.
214      * @return      true if initialised, false otherwise.
215      **/
216     bool IsMelFilterBankInited() const;
217 
218     /**
219      * @brief       Create mel filter banks for MFCC calculation.
220      * @return      2D vector of floats.
221      **/
222     std::vector<std::vector<float>> CreateMelFilterBank();
223 
224     /**
225      * @brief       Computes and populates internal memeber buffers used
226      *              in MFCC feature calculation
227      * @param[in]   audioData   1D vector of 16-bit audio data.
228      */
229     void MfccComputePreFeature(const std::vector<float>& audioData);
230 
231     /** @brief       Computes the magnitude from an interleaved complex array. */
232     void ConvertToPowerSpectrum();
233 
234 };
235