-
Notifications
You must be signed in to change notification settings - Fork 1
/
au_GoertzelAlgorithm.h
264 lines (187 loc) · 5.46 KB
/
au_GoertzelAlgorithm.h
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
Goertzel Algorithm
Created by Matt Davison on 29/11/2023
For more info: https://en.wikipedia.org/wiki/Goertzel_algorithm
*/
#pragma once
#include "au_config.h"
#include "math.h"
class GoertzelAlgorithm
{
public:
struct SetupParameters
{
int sample_rate;
int window_size_periods = 1;
sample_t target_frequency;
};
struct QValues
{
sample_t q1 = 0;
sample_t q2 = 0;
void reset()
{
q1 = 0;
q2 = 0;
}
};
void setup(const SetupParameters& setup_parameters);
void setTargetFrequencyHz(sample_t target_frequency_hz);
void setWindowSizePeriods(int window_size_periods);
void process(sample_t new_sample, QValues& q_vals);
int getWindowLengthSamples();
sample_t getMagnitudeQuick(QValues q_vals);
struct ComplexPolarForm
{
sample_t magnitude;
sample_t phase;
};
ComplexPolarForm getComplexMagnitudeAndPhase(QValues q_vals);
protected:
int m_window_length_samples;
int m_window_size_periods = 1;
private:
sample_t m_coefficient;
sample_t m_sine_of_omega;
sample_t m_cosine_of_omega;
sample_t m_target_frequency;
int m_sample_rate;
void recalcCoefficients();
};
/*
For use in realtime environments, this class updates the Q values sample by sample, rather than bulk processing at the end of each window
*/
class RealtimeGoertzel : public GoertzelAlgorithm
{
public:
void processSample(sample_t new_sample);
/*
Virtual function called when the end of the window is reached, allowing program to get magnitude/phase without continuously polling for new value
*/
virtual void endOfWindow(){}
/*
Get the magnitude of the last complete window
*/
sample_t getLastMagnitude();
/*
Get the magnitude and phase of the last complete window
*/
ComplexPolarForm getLastComplexMagnitudeAndPhase();
void reset();
/*
* Returns true if a new window val is available since last flag check, false if not
*/
bool checkNewValFlag();
private:
QValues m_current_q_values;
QValues m_last_q_values;
int m_samples_in_window_processed = 0;
bool m_new_val_flag = false;
};
/*
This class takes the whole window as a buffer and processes it in one go.
*/
class WholeWindowGoertzel : public GoertzelAlgorithm
{
public:
double getMagnitude(double* window_buffer)
{
return getMagnitudeQuick(getQValsForBuffer(window_buffer));
}
ComplexPolarForm getMagnitudeAndPhase(double* window_buffer)
{
return getComplexMagnitudeAndPhase(getQValsForBuffer(window_buffer));
}
private:
QValues getQValsForBuffer(double* buffer)
{
QValues q_vals;
for (int i = 0 ; i < m_window_length_samples ; i++)
{
process(buffer[i], q_vals);
}
return q_vals;
}
};
/*
* GoertzelAlgorithm implementation
*/
void GoertzelAlgorithm::setup(const SetupParameters& setup_parameters)
{
m_sample_rate = setup_parameters.sample_rate;
m_window_size_periods = setup_parameters.window_size_periods;
m_target_frequency = setup_parameters.target_frequency;
recalcCoefficients();
}
void GoertzelAlgorithm::setTargetFrequencyHz(sample_t target_frequency_hz)
{
m_target_frequency = target_frequency_hz;
recalcCoefficients();
}
void GoertzelAlgorithm::process(sample_t new_sample, QValues& q_vals)
{
sample_t q0 = new_sample + (m_coefficient * q_vals.q1) - q_vals.q2;
q_vals.q2 = q_vals.q1;
q_vals.q1 = q0;
}
int GoertzelAlgorithm::getWindowLengthSamples()
{
return m_window_length_samples;
}
sample_t GoertzelAlgorithm::getMagnitudeQuick(QValues q_vals)
{
return sqrt( (q_vals.q1 * q_vals.q1) + (q_vals.q2 * q_vals.q2) - (m_coefficient * q_vals.q1 * q_vals.q2));
}
GoertzelAlgorithm::ComplexPolarForm GoertzelAlgorithm::getComplexMagnitudeAndPhase(QValues q_vals)
{
ComplexPolarForm mag_and_phase;
sample_t real = q_vals.q1 - (q_vals.q2 * m_cosine_of_omega);
sample_t imaginary = q_vals.q2 * m_sine_of_omega;
mag_and_phase.magnitude = sqrt( (real * real) + (imaginary * imaginary));
mag_and_phase.phase = atan(imaginary / real);
}
void GoertzelAlgorithm::recalcCoefficients()
{
sample_t omega = (2.0 * M_PI * m_target_frequency) / m_sample_rate;
m_coefficient = 2.0 * cos(omega);
m_sine_of_omega = sin(omega);
m_cosine_of_omega = cos(omega);
m_window_length_samples = (int) m_window_size_periods * (m_sample_rate / m_target_frequency);
}
void GoertzelAlgorithm::setWindowSizePeriods(int window_size_periods)
{
m_window_size_periods = window_size_periods;
recalcCoefficients();
}
/*
* RealtimeGoertzel implementation
*/
void RealtimeGoertzel::processSample(sample_t new_sample)
{
process(new_sample, m_current_q_values);
if (++m_samples_in_window_processed == m_window_length_samples)
{
m_last_q_values = m_current_q_values;
reset();
m_new_val_flag = true;
}
}
sample_t RealtimeGoertzel::getLastMagnitude()
{
return getMagnitudeQuick(m_last_q_values);
}
GoertzelAlgorithm::ComplexPolarForm RealtimeGoertzel::getLastComplexMagnitudeAndPhase()
{
return getComplexMagnitudeAndPhase(m_last_q_values);
}
void RealtimeGoertzel::reset()
{
m_current_q_values.reset();
m_samples_in_window_processed = 0;
}
bool RealtimeGoertzel::checkNewValFlag()
{
bool flag_state = m_new_val_flag;
m_new_val_flag = false;
return flag_state;
}