First implementation of tuner widget
[calf.git] / src / modules_pitch.cpp
blob817bff10925fcddb79ff4c10a44e20db93b650c1
1 /* Calf DSP plugin pack
2 * Assorted plugins
4 * Copyright (C) 2015 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #include <limits.h>
23 #include <memory.h>
24 #include <math.h>
25 #include <calf/giface.h>
26 #include <calf/modules_pitch.h>
27 #include <calf/utils.h>
29 #if ENABLE_EXPERIMENTAL
31 // http://www.cs.otago.ac.nz/tartini/papers/A_Smarter_Way_to_Find_Pitch.pdf
33 using namespace calf_plugins;
35 pitch_audio_module::pitch_audio_module()
39 pitch_audio_module::~pitch_audio_module()
43 void pitch_audio_module::set_sample_rate(uint32_t sr)
45 srate = sr;
48 void pitch_audio_module::params_changed()
52 void pitch_audio_module::activate()
54 write_ptr = 0;
55 for (size_t i = 0; i < 2 * BufferSize; ++i)
56 waveform[i] = spectrum[i] = autocorr[i] = 0;
57 for (size_t i = 0; i < BufferSize; ++i)
58 inputbuf[i] = 0;
61 void pitch_audio_module::deactivate()
65 void pitch_audio_module::recompute()
67 // second half of the waveform always zero
68 double sumsquares_acc = 0.;
69 for (int i = 0; i < BufferSize; ++i)
71 float val = inputbuf[(i + write_ptr) & (BufferSize - 1)];
72 float win = 0.54 - 0.46 * cos(i * M_PI / BufferSize);
73 val *= win;
74 waveform[i] = val;
75 sumsquares[i] = sumsquares_acc;
76 sumsquares_acc += val * val;
78 sumsquares[BufferSize] = sumsquares_acc;
79 //waveform[i] = inputbuf[(i + write_ptr) & (BufferSize - 1)];
80 transform.calculate(waveform, spectrum, false);
81 pfft::complex temp[2 * BufferSize];
82 for (int i = 0; i < 2 * BufferSize; ++i)
84 float val = std::abs(spectrum[i]);
85 temp[i] = val * val;
87 //temp[i] = spectrum[i] * std::conj(spectrum[i]);
88 transform.calculate(temp, autocorr, true);
89 sumsquares_last = sumsquares_acc;
90 float maxpt = 0;
91 int maxpos = -1;
92 int i;
93 for (i = 2; i < BufferSize / 2; ++i)
95 float mag = 2.0 * autocorr[i].real() / (sumsquares[BufferSize] + sumsquares[BufferSize - i] - sumsquares[i]);
96 magarr[i] = mag;
97 if (mag > maxpt)
99 maxpt = mag;
100 maxpos = i;
103 for (i = 2; i < BufferSize / 2 && magarr[i + 1] < magarr[i]; ++i)
105 float thr = *params[par_pd_threshold];
106 for (; i < BufferSize / 2; ++i)
108 if (magarr[i] >= thr * maxpt)
110 while(i < BufferSize / 2 - 1 && magarr[i + 1] > magarr[i])
111 i++;
112 maxpt = magarr[i];
113 maxpos = i;
114 break;
117 if (maxpt > 0 && maxpt < BufferSize / 2 - 1)
119 float y1 = magarr[maxpos - 1];
120 float y2 = magarr[maxpos];
121 float y3 = magarr[maxpos + 1];
123 float pos2 = maxpos + 0.5 * (y1 - y3) / (y1 - 2 * y2 + y3);
124 float f2 = (srate / pos2) / *params[par_tune];
125 float lf2 = logf(f2);
126 float rf2 = 1200 * lf2 / logf(2.f) - 300;
127 rf2 -= 1200.f * floor(rf2 / 1200.f);
128 int note = round(rf2 / 100.f);
129 rf2 -= note * 100;
130 if (note == 12)
131 note -= 12;
132 static const char notenames[] = "C\0\0C#\0D\0\0D#\0E\0\0F\0\0F#\0G\0\0G#\0A\0\0A#\0B\0\0";
133 int mnote = round(12 * lf2 + 69);
134 //printf("pos %d mag %f freq %f posx %f freqx %f midi %d note %s%+fct\n", maxpos, maxpt, srate * 1.0 / maxpos, pos2, srate / pos2, mnote, notenames + 3 * note, rf2);
135 *params[par_note] = mnote;
136 *params[par_cents] = rf2;
140 bool pitch_audio_module::get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const
142 if (index == par_pd_threshold && subindex == 0)
144 context->set_source_rgba(1, 0, 0);
145 for (int i = 0; i < points; i++)
147 float ac = autocorr[i * (BufferSize / 2 - 1) / (points - 1)].real();
148 if (ac >= 0)
149 data[i] = sqrt(ac / sumsquares_last);
150 else
151 data[i] = -sqrt(-ac / sumsquares_last);
153 return true;
155 if (index == par_pd_threshold && subindex == 1)
157 context->set_source_rgba(0, 0, 1);
158 for (int i = 0; i < points; i++)
160 data[i] = 0.0625 * log(std::abs(spectrum[i * (BufferSize / 4 - 1) / (points - 1)]));
162 return true;
164 if (index == par_pd_threshold && subindex == 2)
166 context->set_source_rgba(0, 0, 0);
167 for (int i = 0; i < points; i++)
169 int j = i * (BufferSize / 2 - 1) / (points - 1);
170 float mag = magarr[j];
171 data[i] = mag;
173 return true;
175 if (index == par_pd_threshold && subindex == 3)
177 context->set_source_rgba(0, 1, 1);
178 for (int i = 0; i < points; i++)
180 int j = i * (BufferSize - 1) / (points - 1);
181 float mag = std::abs(sumsquares[j]);
182 data[i] = 0.25 * log(mag);
184 return true;
186 return false;
189 uint32_t pitch_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
191 uint32_t endpos = offset + numsamples;
192 bool has2nd = ins[1] != NULL;
194 int bperiod = BufferSize;
195 int sd = *params[par_pd_subdivide];
196 if (sd >= 1 && sd <= 8)
197 bperiod /= sd;
199 // XXXKF note: not working, not optimized, just trying stuff out
200 for (uint32_t i = offset; i < endpos; ++i)
202 float val = ins[0][i];
203 inputbuf[write_ptr] = val;
204 write_ptr = (write_ptr + 1) & (BufferSize - 1);
205 if (!(write_ptr % bperiod))
206 recompute();
207 outs[0][i] = ins[0][i];
208 if (has2nd)
209 outs[1][i] = ins[1][i];
211 return outputs_mask;
214 #endif