blob: 86279619d7b4d1d9f5b00ec97a009d91b259df7a [file] [log] [blame]
Éanna Ó Catháinc6ab02a2021-04-07 14:35:25 +01001//
2// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include <algorithm>
7#include <numeric>
8#include <math.h>
9#include <string.h>
10
11#include "MathUtils.hpp"
12#include "Preprocess.hpp"
13
14Preprocess::Preprocess(
15 const uint32_t windowLen,
16 const uint32_t windowStride,
17 const MFCC mfccInst):
18 _m_mfcc(mfccInst),
19 _m_mfccBuf(mfccInst._m_params.m_numMfccFeatures, mfccInst._m_params.m_numMfccVectors),
20 _m_delta1Buf(mfccInst._m_params.m_numMfccFeatures, mfccInst._m_params.m_numMfccVectors),
21 _m_delta2Buf(mfccInst._m_params.m_numMfccFeatures, mfccInst._m_params.m_numMfccVectors),
22 _m_windowLen(windowLen),
23 _m_windowStride(windowStride)
24{
25 if (mfccInst._m_params.m_numMfccFeatures > 0 && windowLen > 0)
26 {
27 this->_m_mfcc.Init();
28 }
29}
30
31Preprocess::~Preprocess()
32{
33}
34
35bool Preprocess::Invoke( const float* audioData, const uint32_t audioDataLen, std::vector<int8_t>& output,
36 int quantOffset, float quantScale)
37{
38 this->_m_window = SlidingWindow<const float>(
39 audioData, audioDataLen,
40 this->_m_windowLen, this->_m_windowStride);
41
42 uint32_t mfccBufIdx = 0;
43
44 // Init buffers with 0
45 std::fill(_m_mfccBuf.begin(), _m_mfccBuf.end(), 0.f);
46 std::fill(_m_delta1Buf.begin(), _m_delta1Buf.end(), 0.f);
47 std::fill(_m_delta2Buf.begin(), _m_delta2Buf.end(), 0.f);
48
49 /* While we can slide over the window */
50 while (this->_m_window.HasNext())
51 {
52 const float* mfccWindow = this->_m_window.Next();
53 auto mfccAudioData = std::vector<float>(
54 mfccWindow,
55 mfccWindow + this->_m_windowLen);
56
57 auto mfcc = this->_m_mfcc.MfccCompute(mfccAudioData);
58 for (size_t i = 0; i < this->_m_mfccBuf.size(0); ++i)
59 {
60 this->_m_mfccBuf(i, mfccBufIdx) = mfcc[i];
61 }
62 ++mfccBufIdx;
63 }
64
65 /* Pad MFCC if needed by repeating last feature vector */
66 while (mfccBufIdx != this->_m_mfcc._m_params.m_numMfccVectors)
67 {
68 memcpy(&this->_m_mfccBuf(0, mfccBufIdx),
69 &this->_m_mfccBuf(0, mfccBufIdx-1), sizeof(float)*this->_m_mfcc._m_params.m_numMfccFeatures);
70 ++mfccBufIdx;
71 }
72
73 /* Compute first and second order deltas from MFCCs */
74 this->_ComputeDeltas(this->_m_mfccBuf,
75 this->_m_delta1Buf,
76 this->_m_delta2Buf);
77
78 /* Normalise */
79 this->_Normalise();
80
81 return this->_Quantise<int8_t>(output.data(), quantOffset, quantScale);
82}
83
84bool Preprocess::_ComputeDeltas(Array2d<float>& mfcc,
85 Array2d<float>& delta1,
86 Array2d<float>& delta2)
87{
88 const std::vector <float> delta1Coeffs =
89 {6.66666667e-02, 5.00000000e-02, 3.33333333e-02,
90 1.66666667e-02, -3.46944695e-18, -1.66666667e-02,
91 -3.33333333e-02, -5.00000000e-02, -6.66666667e-02};
92
93 const std::vector <float> delta2Coeffs =
94 {0.06060606, 0.01515152, -0.01731602,
95 -0.03679654, -0.04329004, -0.03679654,
96 -0.01731602, 0.01515152, 0.06060606};
97
98 if (delta1.size(0) == 0 || delta2.size(0) != delta1.size(0) ||
99 mfcc.size(0) == 0 || mfcc.size(1) == 0)
100 {
101 return false;
102 }
103
104 /* Get the middle index; coeff vec len should always be odd */
105 const size_t coeffLen = delta1Coeffs.size();
106 const size_t fMidIdx = (coeffLen - 1)/2;
107 const size_t numFeatures = mfcc.size(0);
108 const size_t numFeatVectors = mfcc.size(1);
109
110 /* iterate through features in MFCC vector*/
111 for (size_t i = 0; i < numFeatures; ++i)
112 {
113 /* for each feature, iterate through time (t) samples representing feature evolution and
114 * calculate d/dt and d^2/dt^2, using 1d convolution with differential kernels.
115 * Convolution padding = valid, result size is `time length - kernel length + 1`.
116 * The result is padded with 0 from both sides to match the size of initial time samples data.
117 *
118 * For the small filter, conv1d implementation as a simple loop is efficient enough.
119 * Filters of a greater size would need CMSIS-DSP functions to be used, like arm_fir_f32.
120 */
121
122 for (size_t j = fMidIdx; j < numFeatVectors - fMidIdx; ++j)
123 {
124 float d1 = 0;
125 float d2 = 0;
126 const size_t mfccStIdx = j - fMidIdx;
127
128 for (size_t k = 0, m = coeffLen - 1; k < coeffLen; ++k, --m)
129 {
130
131 d1 += mfcc(i,mfccStIdx + k) * delta1Coeffs[m];
132 d2 += mfcc(i,mfccStIdx + k) * delta2Coeffs[m];
133 }
134
135 delta1(i,j) = d1;
136 delta2(i,j) = d2;
137 }
138 }
139
140 return true;
141}
142
143float Preprocess::_GetMean(Array2d<float>& vec)
144{
145 return MathUtils::MeanF32(vec.begin(), vec.totalSize());
146}
147
148float Preprocess::_GetStdDev(Array2d<float>& vec, const float mean)
149{
150 return MathUtils::StdDevF32(vec.begin(), vec.totalSize(), mean);
151}
152
153void Preprocess::_NormaliseVec(Array2d<float>& vec)
154{
155 auto mean = Preprocess::_GetMean(vec);
156 auto stddev = Preprocess::_GetStdDev(vec, mean);
157
158 if (stddev == 0)
159 {
160 std::fill(vec.begin(), vec.end(), 0);
161 }
162 else
163 {
164 const float stddevInv = 1.f/stddev;
165 const float normalisedMean = mean/stddev;
166
167 auto NormalisingFunction = [=](float &value) {
168 value = value * stddevInv - normalisedMean;
169 };
170 std::for_each(vec.begin(), vec.end(), NormalisingFunction);
171 }
172}
173
174void Preprocess::_Normalise()
175{
176 Preprocess::_NormaliseVec(this->_m_mfccBuf);
177 Preprocess::_NormaliseVec(this->_m_delta1Buf);
178 Preprocess::_NormaliseVec(this->_m_delta2Buf);
179}
180
181float Preprocess::_GetQuantElem(
182 const float elem,
183 const float quantScale,
184 const int quantOffset,
185 const float minVal,
186 const float maxVal)
187{
188 float val = std::round((elem/quantScale) + quantOffset);
189 float maxim = std::max<float>(val, minVal);
190 float returnVal = std::min<float>(std::max<float>(val, minVal), maxVal);
191 return returnVal;
192}