1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3 * Copyright (C) 2011 Pete Shorthose
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * --------------------------------------------------------------------------
19 */
20
21 /* ------- This is the guitarix tuner, part of gx_engine_audio.cpp ------- */
22
23 #include "engine.h"
24
25 /****************************************************************
26 ** Pitch Tracker
27 **
28 ** some code and ideas taken from K4Guitune (William Spinelli)
29 ** changed to NSDF method (some code from tartini / Philip McLeod)
30 **
31 */
32
33 namespace gx_engine {
34
35 // downsampling factor
36 static const int DOWNSAMPLE = 2;
37 static const float SIGNAL_THRESHOLD_ON = 0.001;
38 static const float SIGNAL_THRESHOLD_OFF = 0.0009;
39 static const float TRACKER_PERIOD = 0.1;
40 // The size of the read buffer
41 static const int FFT_SIZE = 2048;
42
43
static_run(void * p)44 void *PitchTracker::static_run(void *p) {
45 (reinterpret_cast<PitchTracker *>(p))->run();
46 return NULL;
47 }
48
PitchTracker()49 PitchTracker::PitchTracker()
50 : error(false),
51 busy(false),
52 tick(0),
53 m_pthr(0),
54 resamp(),
55 m_sampleRate(),
56 fixed_sampleRate(41000),
57 m_freq(-1),
58 signal_threshold_on(SIGNAL_THRESHOLD_ON),
59 signal_threshold_off(SIGNAL_THRESHOLD_OFF),
60 tracker_period(TRACKER_PERIOD),
61 m_buffersize(),
62 m_fftSize(),
63 m_buffer(new float[FFT_SIZE]),
64 m_bufferIndex(0),
65 m_input(new float[FFT_SIZE]),
66 m_audioLevel(false),
67 m_fftwPlanFFT(0),
68 m_fftwPlanIFFT(0) {
69 const int size = FFT_SIZE + (FFT_SIZE+1) / 2;
70 m_fftwBufferTime = reinterpret_cast<float*>
71 (fftwf_malloc(size * sizeof(*m_fftwBufferTime)));
72 m_fftwBufferFreq = reinterpret_cast<float*>
73 (fftwf_malloc(size * sizeof(*m_fftwBufferFreq)));
74
75 memset(m_buffer, 0, FFT_SIZE * sizeof(*m_buffer));
76 memset(m_input, 0, FFT_SIZE * sizeof(*m_input));
77 memset(m_fftwBufferTime, 0, size * sizeof(*m_fftwBufferTime));
78 memset(m_fftwBufferFreq, 0, size * sizeof(*m_fftwBufferFreq));
79
80 sem_init(&m_trig, 0, 0);
81
82 if (!m_buffer || !m_input || !m_fftwBufferTime || !m_fftwBufferFreq) {
83 gx_print_error("PitchTracker", "out of memory");
84 error = true;
85 }
86 }
87
88
~PitchTracker()89 PitchTracker::~PitchTracker() {
90 stop_thread();
91 fftwf_destroy_plan(m_fftwPlanFFT);
92 fftwf_destroy_plan(m_fftwPlanIFFT);
93 fftwf_free(m_fftwBufferTime);
94 fftwf_free(m_fftwBufferFreq);
95 delete[] m_input;
96 delete[] m_buffer;
97 }
98
set_fast_note_detection(bool v)99 void PitchTracker::set_fast_note_detection(bool v) {
100 if (v) {
101 signal_threshold_on = SIGNAL_THRESHOLD_ON * 5;
102 signal_threshold_off = SIGNAL_THRESHOLD_OFF * 5;
103 tracker_period = TRACKER_PERIOD / 10;
104 } else {
105 signal_threshold_on = SIGNAL_THRESHOLD_ON;
106 signal_threshold_off = SIGNAL_THRESHOLD_OFF;
107 tracker_period = TRACKER_PERIOD;
108 }
109 }
110
setParameters(int priority,int policy,int sampleRate,int buffersize)111 bool PitchTracker::setParameters(int priority, int policy, int sampleRate, int buffersize) {
112 assert(buffersize <= FFT_SIZE);
113
114 if (error) {
115 return false;
116 }
117
118 m_sampleRate = fixed_sampleRate / DOWNSAMPLE;
119 resamp.setup(sampleRate, m_sampleRate, 1, 16); // 16 == least quality
120
121 if (m_buffersize != buffersize) {
122 m_buffersize = buffersize;
123 m_fftSize = m_buffersize + (m_buffersize+1) / 2;
124 fftwf_destroy_plan(m_fftwPlanFFT);
125 fftwf_destroy_plan(m_fftwPlanIFFT);
126 m_fftwPlanFFT = fftwf_plan_r2r_1d(
127 m_fftSize, m_fftwBufferTime, m_fftwBufferFreq,
128 FFTW_R2HC, FFTW_ESTIMATE);
129 m_fftwPlanIFFT = fftwf_plan_r2r_1d(
130 m_fftSize, m_fftwBufferFreq, m_fftwBufferTime,
131 FFTW_HC2R, FFTW_ESTIMATE);
132 }
133
134 if (!m_fftwPlanFFT || !m_fftwPlanIFFT) {
135 error = true;
136 gx_print_error("PitchTracker", "can't allocate FFTW plan");
137 return false;
138 }
139
140 if (!m_pthr) {
141 start_thread(priority, policy);
142 }
143 return !error;
144 }
145
stop_thread()146 void PitchTracker::stop_thread() {
147 if (m_pthr) {
148 pthread_cancel (m_pthr);
149 pthread_join (m_pthr, NULL);
150 m_pthr = 0;
151 }
152 }
153
start_thread(int priority,int policy)154 void PitchTracker::start_thread(int priority, int policy) {
155 pthread_attr_t attr;
156 struct sched_param spar;
157 spar.sched_priority = priority;
158 pthread_attr_init(&attr);
159 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE );
160 pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
161 pthread_attr_setschedpolicy(&attr, policy);
162 pthread_attr_setschedparam(&attr, &spar);
163 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
164 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
165 // pthread_attr_setstacksize(&attr, 0x10000);
166 if (pthread_create(&m_pthr, &attr, static_run,
167 reinterpret_cast<void*>(this))) {
168 error = true;
169 if (errno == EPERM) {
170 gx_print_error(
171 "PitchTracker",
172 _("no permission to create realtime thread - please check your system configuration - tuner not started"));
173 } else {
174 gx_print_error(
175 "PitchTracker",
176 _("error creating realtime thread - tuner not started"));
177 }
178 }
179 pthread_attr_destroy(&attr);
180 }
181
init(int priority,int policy,unsigned int samplerate)182 void PitchTracker::init(int priority, int policy, unsigned int samplerate) {
183 setParameters(priority, policy, samplerate, FFT_SIZE);
184 }
185
reset()186 void PitchTracker::reset() {
187 tick = 0;
188 m_bufferIndex = 0;
189 resamp.reset();
190 m_freq = -1;
191 }
192
add(int count,float * input)193 void PitchTracker::add(int count, float* input) {
194 if (error) {
195 return;
196 }
197 resamp.inp_count = count;
198 resamp.inp_data = input;
199 for (;;) {
200 resamp.out_data = &m_buffer[m_bufferIndex];
201 int n = FFT_SIZE - m_bufferIndex;
202 resamp.out_count = n;
203 resamp.process();
204 n -= resamp.out_count; // n := number of output samples
205 if (!n) { // all soaked up by filter
206 return;
207 }
208 m_bufferIndex = (m_bufferIndex + n) % FFT_SIZE;
209 if (resamp.inp_count == 0) {
210 break;
211 }
212 }
213 if (++tick * count >= m_sampleRate * DOWNSAMPLE * tracker_period) {
214 if (busy) {
215 return;
216 }
217 busy = true;
218 tick = 0;
219 copy();
220 sem_post(&m_trig);
221 }
222 }
223
copy()224 void PitchTracker::copy() {
225 int start = (FFT_SIZE + m_bufferIndex - m_buffersize) % FFT_SIZE;
226 int end = (FFT_SIZE + m_bufferIndex) % FFT_SIZE;
227 int cnt = 0;
228 if (start >= end) {
229 cnt = FFT_SIZE - start;
230 memcpy(m_input, &m_buffer[start], cnt * sizeof(*m_input));
231 start = 0;
232 }
233 memcpy(&m_input[cnt], &m_buffer[start], (end - start) * sizeof(*m_input));
234 }
235
sq(float x)236 inline float sq(float x) {
237 return x * x;
238 }
239
parabolaTurningPoint(float y_1,float y0,float y1,float xOffset,float * x)240 inline void parabolaTurningPoint(float y_1, float y0, float y1, float xOffset, float *x) {
241 float yTop = y_1 - y1;
242 float yBottom = y1 + y_1 - 2 * y0;
243 if (yBottom != 0.0) {
244 *x = xOffset + yTop / (2 * yBottom);
245 } else {
246 *x = xOffset;
247 }
248 }
249
findMaxima(float * input,int len,int * maxPositions,int * length,int maxLen)250 static int findMaxima(float *input, int len, int *maxPositions, int *length, int maxLen) {
251 int pos = 0;
252 int curMaxPos = 0;
253 int overallMaxIndex = 0;
254
255 while (pos < (len-1)/3 && input[pos] > 0.0) {
256 pos += 1; // find the first negative zero crossing
257 }
258 while (pos < len-1 && input[pos] <= 0.0) {
259 pos += 1; // loop over all the values below zero
260 }
261 if (pos == 0) {
262 pos = 1; // can happen if output[0] is NAN
263 }
264 while (pos < len-1) {
265 if (input[pos] > input[pos-1] && input[pos] >= input[pos+1]) { // a local maxima
266 if (curMaxPos == 0) {
267 curMaxPos = pos; // the first maxima (between zero crossings)
268 } else if (input[pos] > input[curMaxPos]) {
269 curMaxPos = pos; // a higher maxima (between the zero crossings)
270 }
271 }
272 pos += 1;
273 if (pos < len-1 && input[pos] <= 0.0) { // a negative zero crossing
274 if (curMaxPos > 0) { // if there was a maximum
275 maxPositions[*length] = curMaxPos; // add it to the vector of maxima
276 *length += 1;
277 if (overallMaxIndex == 0) {
278 overallMaxIndex = curMaxPos;
279 } else if (input[curMaxPos] > input[overallMaxIndex]) {
280 overallMaxIndex = curMaxPos;
281 }
282 if (*length >= maxLen) {
283 return overallMaxIndex;
284 }
285 curMaxPos = 0; // clear the maximum position, so we start looking for a new ones
286 }
287 while (pos < len-1 && input[pos] <= 0.0) {
288 pos += 1; // loop over all the values below zero
289 }
290 }
291 }
292 if (curMaxPos > 0) { // if there was a maximum in the last part
293 maxPositions[*length] = curMaxPos; // add it to the vector of maxima
294 *length += 1;
295 if (overallMaxIndex == 0) {
296 overallMaxIndex = curMaxPos;
297 } else if (input[curMaxPos] > input[overallMaxIndex]) {
298 overallMaxIndex = curMaxPos;
299 }
300 curMaxPos = 0; // clear the maximum position, so we start looking for a new ones
301 }
302 return overallMaxIndex;
303 }
304
findsubMaximum(float * input,int len,float threshold)305 static int findsubMaximum(float *input, int len, float threshold) {
306 int indices[10];
307 int length = 0;
308 int overallMaxIndex = findMaxima(input, len, indices, &length, 10);
309 if (length == 0) {
310 return -1;
311 }
312 threshold += (1.0 - threshold) * (1.0 - input[overallMaxIndex]);
313 float cutoff = input[overallMaxIndex] * threshold;
314 for (int j = 0; j < length; j++) {
315 if (input[indices[j]] >= cutoff) {
316 return indices[j];
317 }
318 }
319 // should never get here
320 return -1;
321 }
322
run()323 void PitchTracker::run() {
324 for (;;) {
325 busy = false;
326 sem_wait(&m_trig);
327 if (error) {
328 continue;
329 }
330 float sum = 0.0;
331 for (int k = 0; k < m_buffersize; ++k) {
332 sum += fabs(m_input[k]);
333 }
334 float threshold = (m_audioLevel ? signal_threshold_off : signal_threshold_on);
335 m_audioLevel = (sum / m_buffersize >= threshold);
336 if ( m_audioLevel == false ) {
337 if (m_freq != 0) {
338 m_freq = 0;
339 new_freq();
340 }
341 continue;
342 }
343
344 memcpy(m_fftwBufferTime, m_input, m_buffersize * sizeof(*m_fftwBufferTime));
345 memset(m_fftwBufferTime+m_buffersize, 0, (m_fftSize - m_buffersize) * sizeof(*m_fftwBufferTime));
346 fftwf_execute(m_fftwPlanFFT);
347 for (int k = 1; k < m_fftSize/2; k++) {
348 m_fftwBufferFreq[k] = sq(m_fftwBufferFreq[k]) + sq(m_fftwBufferFreq[m_fftSize-k]);
349 m_fftwBufferFreq[m_fftSize-k] = 0.0;
350 }
351 m_fftwBufferFreq[0] = sq(m_fftwBufferFreq[0]);
352 m_fftwBufferFreq[m_fftSize/2] = sq(m_fftwBufferFreq[m_fftSize/2]);
353
354 fftwf_execute(m_fftwPlanIFFT);
355
356 double sumSq = 2.0 * static_cast<double>(m_fftwBufferTime[0]) / static_cast<double>(m_fftSize);
357 for (int k = 0; k < m_fftSize - m_buffersize; k++) {
358 m_fftwBufferTime[k] = m_fftwBufferTime[k+1] / static_cast<float>(m_fftSize);
359 }
360
361 int count = (m_buffersize + 1) / 2;
362 for (int k = 0; k < count; k++) {
363 sumSq -= sq(m_input[m_buffersize-1-k]) + sq(m_input[k]);
364 // dividing by zero is very slow, so deal with it separately
365 if (sumSq > 0.0) {
366 m_fftwBufferTime[k] *= 2.0 / sumSq;
367 } else {
368 m_fftwBufferTime[k] = 0.0;
369 }
370 }
371 const float thres = 0.99; // was 0.6
372 int maxAutocorrIndex = findsubMaximum(m_fftwBufferTime, count, thres);
373
374 float x = 0.0;
375 if (maxAutocorrIndex >= 0) {
376 parabolaTurningPoint(m_fftwBufferTime[maxAutocorrIndex-1],
377 m_fftwBufferTime[maxAutocorrIndex],
378 m_fftwBufferTime[maxAutocorrIndex+1],
379 maxAutocorrIndex+1, &x);
380 x = m_sampleRate / x;
381 if (x > 999.0) { // precision drops above 1000 Hz
382 x = 0.0;
383 }
384 }
385 if (m_freq != x) {
386 m_freq = x;
387 new_freq();
388 }
389 }
390 }
391
get_estimated_note()392 float PitchTracker::get_estimated_note() {
393 return m_freq <= 0.0 ? 1000.0 : 12 * log2f(2.272727e-03f * m_freq);
394 }
395
396 }
397