Krotos Modules 3
Loading...
Searching...
No Matches
EarFilter.cpp
Go to the documentation of this file.
1#include "EarFilter.h"
2
3//=====================================================================================
5
6//=====================================================================================
7void krotos::EarFilter::configure(float sampleRate)
8{
9 m_sampleRate = sampleRate;
10
11 // extend to sampleRate / 2
12 if (m_sampleRate / 2.0f > m_maxFreq)
13 {
14 m_freqs.push_back(m_sampleRate / 2.0f); // extend to sr / 2
15 m_gains.push_back(m_gains.back());
16 }
17
18 // second order low pass coefficients
19 m_a = getLowPassFilterCoefficients(m_fc / m_sampleRate, m_q);
20
21 // convert to highpass
22 m_b = convertToHighPass(m_a);
23
24 // create the filter object and filter the signal
25 // create the filter object and filter the signal
26 // the only way it does not crash
27 ReferenceCountedObjectPtr<CoefficientsIIR> filterCoeff =
28 new CoefficientsIIR(m_b[0], m_b[1], m_b[2], m_a[0], m_a[1], m_a[2]);
29 m_filter.coefficients = filterCoeff;
30
31 // transfer function of filter applied so far
32 calculateTransferFunction(m_b, m_a, 512);
33
34 // square every element of mag response
35 std::transform(m_magResponse.begin(), m_magResponse.end(), m_magResponse.begin(),
36 [](float value) { return value * value; });
37 // transfer function that remains to apply
38 std::vector<float> gainsInterp = LinearInterpolator::interpolate(m_freqs, m_gains, m_normalisedFreqs);
39 // divide by transfer function of first filter
40 m_gain = elementWiseDivision(gainsInterp, m_magResponse);
41
42 // apply gain condition for elements below m_freqs(2)
43 float conditionValue = m_freqs[1];
44
45 for (size_t i = 0; i < m_gain.size(); ++i)
46 {
47 if (m_normalisedFreqs[i] < conditionValue)
48 {
49 m_gain[i] = 1.0f;
50 }
51 }
52
53 m_freqResponse = linspace(0.0f, 1.0f, static_cast<int>(m_gain.size()));
54
55 // numerator
56 m_firCoeffb = fir2(N, m_freqResponse, m_gain);
57}
58
59//=====================================================================================
60std::vector<float> krotos::EarFilter::processSignal(std::vector<float>& inputSignal)
61{
62 jassert(m_sampleRate != -1.0f);
63
64 const int numSamples = static_cast<int>(inputSignal.size());
65
66 // filter the signal twice in cascade
67 std::vector<float> filteredSignal1;
68 std::vector<float> filteredSignal2;
69 applyIIRFilter(inputSignal, m_filter, filteredSignal1);
70 applyIIRFilter(filteredSignal1, m_filter, filteredSignal2);
71
72 // Apply to signal
73 std::vector<float> filteredSignal3;
74 applyFIRFilter(filteredSignal2, m_firCoeffb, filteredSignal3);
75
76 return filteredSignal3;
77}
78
79std::vector<float> krotos::EarFilter::getLowPassFilterCoefficients(float normalizedFrequency, float qualityFactor)
80{
81 jassert(qualityFactor != 0.0f);
82
83 float rho = std::exp(-float(M_PI) * normalizedFrequency / qualityFactor);
84 float theta =
85 2.0f * float(M_PI) * normalizedFrequency * std::sqrt(1.0f - 1.0f / (4.0f * std::pow(qualityFactor, 2.0f)));
86
87 std::vector<float> coeff(static_cast<int>(rho), 1.0f);
88
89 float a1 = -2.0f * rho * cos(theta);
90 float a2 = std::pow(rho, 2.0f);
91
92 coeff.push_back(1.0f);
93 coeff.push_back(a1);
94 coeff.push_back(a2);
95
96 return coeff;
97}
98
99std::vector<float> krotos::EarFilter::convertToHighPass(const std::vector<float>& lowPassCoefficients)
100{
101 float sum = 0.0f;
102 for (int i = 0; i < lowPassCoefficients.size(); i++)
103 {
104 sum += lowPassCoefficients[i];
105 }
106
107 float b0 = sum - 1.0f;
108 float b1 = -lowPassCoefficients[1];
109 float b2 = -lowPassCoefficients[2];
110
111 std::vector<float> coeff;
112 coeff.push_back(b0);
113 coeff.push_back(b1);
114 coeff.push_back(b2);
115
116 return coeff;
117}
118
119void krotos::EarFilter::applyIIRFilter(const std::vector<float>& inputSignal, dsp::IIR::Filter<float>& filter,
120 std::vector<float>& filteredSignal)
121{
122 for (const auto& sample : inputSignal)
123 {
124 float filteredSample = filter.processSample(sample);
125 filteredSignal.push_back(filteredSample);
126 }
127}
128
129void krotos::EarFilter::applyFIRFilter(const std::vector<float>& inputSignal, const std::vector<float>& coefficients,
130 std::vector<float>& filteredSignal)
131{
132 int numSamples = static_cast<int>(inputSignal.size());
133 int numCoefficients = static_cast<int>(coefficients.size());
134
135 for (int sample = 0; sample < numSamples; ++sample)
136 {
137 float filteredSample = 0.0f;
138
139 for (int i = 0; i < numCoefficients; ++i)
140 {
141 int index = sample - i;
142 if (index >= 0)
143 filteredSample += coefficients[i] * inputSignal[index];
144 }
145
146 filteredSignal.push_back(filteredSample);
147 }
148}
149
150void krotos::EarFilter::calculateTransferFunction(const std::vector<float> b, const std::vector<float> a, int numPoints)
151{
152 CoefficientsIIR coeff(b[0], b[1], b[2], a[0], a[1], a[2]);
153
154 // evaluate the transfer function
155 m_magResponse.resize(numPoints);
156 m_normalisedFreqs.resize(numPoints);
157
158 for (int i = 0; i < numPoints; ++i)
159 {
160 float normalizedFrequency = static_cast<float>(i) / numPoints;
161 float magnitude = static_cast<float>(coeff.getMagnitudeForFrequency(
162 static_cast<double>(normalizedFrequency * 0.5f * m_sampleRate), static_cast<double>(m_sampleRate)));
163
164 // square magnitude values
165 m_magResponse[i] = magnitude;
166 // 0...pi to 0...Fs/2
167 m_normalisedFreqs[i] = normalizedFrequency * m_sampleRate / 2.0f;
168 }
169}
170
171std::vector<float> krotos::EarFilter::elementWiseDivision(const std::vector<float>& vector1,
172 const std::vector<float>& vector2)
173{
174 // check if input vectors are of equal size
175 jassert(vector1.size() == vector2.size());
176
177 std::vector<float> result(vector1.size()); // reserve memory for the result
178
179 for (int i = 0; i < vector1.size(); ++i)
180 {
181 // perform element-wise division
182 result[i] = (vector2[i] != 0.0f) ? (vector1[i] / vector2[i] + std::numeric_limits<float>::epsilon()) : 0.0f;
183 }
184
185 return result;
186}
187
188std::vector<float> krotos::EarFilter::fir2(int number, std::vector<float>& freqResponse, const std::vector<float>& gain)
189{
190 // check freq and gain should have same size
191 jassert(freqResponse.size() == gain.size());
192 // work with filter length
193 number += 1;
194 float npt = 512;
195
196 if (number < 1024)
197 npt = 512;
198 else
199 npt = std::pow(2.0f, std::ceilf(std::log2(static_cast<float>(number))));
200
201 std::vector<float> wind(number);
202 // create Window
203 for (int n = 0; n < number; ++n)
204 {
205 wind[n] = 0.54f - 0.46f * std::cos((2.0f * float(M_PI) * n) / float(number - 1.0f));
206 }
207
208 // Ensure the window is symmetric
209 for (int i = 0; i < number / 2; ++i)
210 {
211 wind[number - i - 1] = wind[i];
212 }
213
214 int lap = static_cast<int>(std::floor(npt / 25.0f));
215
216 int nbrk = static_cast<int>(gain.size());
217
218 const float eps = std::numeric_limits<float>::epsilon();
219
220 jassert(std::abs(freqResponse[0]) <= eps || std::abs(freqResponse[nbrk - 1] - 1) <= eps);
221
222 freqResponse[0] = 0.0f;
223 freqResponse[nbrk - 1] = 1.0f;
224
225 // interpolate breakpoints onto large grid
226
227 int nint = nbrk - 1;
228 std::vector<double> df(freqResponse.size());
229 adjacent_difference(freqResponse.begin(), freqResponse.end(), df.begin());
230 df.erase(df.begin());
231
232 // jassert(std::any_of(df.begin(), df.end(), [](double val) { return val < 0;
233 // }));
234
235 if (number > 2 * static_cast<int>(npt))
236 {
237 jassertfalse;
238 }
239
240 // length of[dc 1 2 ... nyquist] frequencies.
241 npt += 1.0f;
242 std::vector<float> H;
243 int nb = 1;
244 int ne;
245
246 for (int i = 0; i < nint; ++i)
247 {
248 if (df[i] == 0)
249 {
250 nb = static_cast<int>(std::ceil(nb - lap / 2.0f));
251 ne = nb + lap;
252 }
253 else
254 {
255 ne = static_cast<int>(std::floor(freqResponse[i + 1] * npt));
256 }
257
258 if (nb < 0 || ne > npt)
259 {
260 jassertfalse;
261 }
262
263 for (int j = nb; j <= ne; ++j)
264 {
265 float inc = (j - nb) / static_cast<float>(ne - nb);
266
267 if (nb == ne)
268 inc = 0;
269
270 H.push_back(inc * gain[i + 1] + (1.0f - inc) * gain[i]);
271 }
272
273 nb = ne + 1;
274 }
275
276 // Fourier time shift
277 float dt = 0.5f * (number - 1);
278
279 std::vector<std::complex<float>> rad(static_cast<int>(npt));
280
281 for (int i = 0; i < npt; ++i)
282 {
283 float realPart = 0.0f;
284 float imagPart = -dt * float(M_PI) * static_cast<float>(i) / static_cast<float>(npt - 1);
285 ; // Since the imaginary part is 0 in this case
286 rad[i] = std::complex<float>(realPart, imagPart);
287 }
288
289 std::vector<std::complex<float>> Hexp(H.size());
290
291 for (int i = 0; i < H.size(); ++i)
292 {
293 std::complex<float> expValue = std::exp(rad[i]);
294 Hexp[i] = H[i] * expValue;
295 }
296
297 // force last element to real
298 Hexp.back().imag(0.0f);
299
300 // Fourier transform of real series
301 // reverse H excluding the last element
302 std::vector<std::complex<float>> H_reversed(Hexp.rbegin() + 1, Hexp.rend());
303 std::vector<std::complex<float>> H_conj;
304 for (const auto& h : H_reversed)
305 {
306 // calculate the conjugate of each element
307 H_conj.push_back(std::conj(h));
308 }
309
310 // concatenate H with its reversed conjugate
311 Hexp.insert(Hexp.end(), H_conj.begin(), H_conj.end() - 1);
312
313 // initialise FFT object
314 juce::dsp::FFT fft(static_cast<int>(std::log2(static_cast<float>(Hexp.size()))));
315
316 // perform the inverse FFT
317 std::vector<std::complex<float>> Hexp1(Hexp.size());
318 fft.perform(Hexp.data(), Hexp1.data(), true);
319
320 // extract the real part of the result
321 std::vector<float> ht(Hexp1.size());
322
323 for (int i = 0; i < Hexp1.size(); ++i)
324 {
325 ht[i] = Hexp1[i].real();
326 }
327
328 // raw numerator
329 std::vector<float> b(ht.begin(), ht.begin() + number);
330
331 // apply Window
332 for (int i = 0; i < number; i++)
333 {
334 b[i] *= wind[i];
335 }
336
337 return b;
338}
juce::dsp::IIR::Coefficients< float > CoefficientsIIR
Definition ERB_FFTSpectrogram.h:10
void applyIIRFilter(const std::vector< float > &inputSignal, dsp::IIR::Filter< float > &filter, std::vector< float > &filteredSignal)
Definition EarFilter.cpp:119
void applyFIRFilter(const std::vector< float > &inputSignal, const std::vector< float > &coefficients, std::vector< float > &filteredSignal)
Definition EarFilter.cpp:129
std::vector< float > getLowPassFilterCoefficients(float normalizedFrequency, float qualityFactor)
Definition EarFilter.cpp:79
void calculateTransferFunction(const std::vector< float > b, const std::vector< float > a, int numPoints)
Definition EarFilter.cpp:150
std::vector< float > processSignal(std::vector< float > &inputSignal)
Definition EarFilter.cpp:60
EarFilter()
Definition EarFilter.cpp:4
std::vector< float > convertToHighPass(const std::vector< float > &lowPassCoefficients)
Definition EarFilter.cpp:99
std::vector< float > elementWiseDivision(const std::vector< float > &vector1, const std::vector< float > &vector2)
Definition EarFilter.cpp:171
void configure(float sampleRate)
Definition EarFilter.cpp:7
std::vector< float > fir2(int N, std::vector< float > &freqResponse, const std::vector< float > &gain)
Definition EarFilter.cpp:188
static std::vector< float > interpolate(const std::vector< float > &xValues, const std::vector< float > &yValues, const std::vector< float > &vals)
Definition KrotosIntrepolators.cpp:283
#define M_PI
Definition windowing.h:9