Merge branch 'design'
[calf.git] / src / modules_pitch.cpp
blobf8c9e27e65b456adca7e5d9b10f008ba15885db1
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 && maxpos < BufferSize / 2 - 1)
119 float y1 = magarr[maxpos - 1];
120 float y2 = magarr[maxpos];
121 float y3 = magarr[maxpos + 1];
122 float pos2 = maxpos + 0.5 * (y1 - y3) / (y1 - 2 * y2 + y3);
123 dsp::note_desc desc = dsp::hz_to_note(srate / pos2, *params[par_tune]);
124 *params[par_note] = desc.note;
125 *params[par_cents] = desc.cents;
126 *params[par_freq] = desc.freq;
127 *params[par_clarity] = maxpt;
129 *params[par_clarity] = maxpt;
132 bool pitch_audio_module::get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const
134 if (index == par_pd_threshold && subindex == 0)
136 context->set_source_rgba(1, 0, 0);
137 for (int i = 0; i < points; i++)
139 float ac = autocorr[i * (BufferSize / 2 - 1) / (points - 1)].real();
140 if (ac >= 0)
141 data[i] = sqrt(ac / sumsquares_last);
142 else
143 data[i] = -sqrt(-ac / sumsquares_last);
145 return true;
147 if (index == par_pd_threshold && subindex == 1)
149 context->set_source_rgba(0, 0, 1);
150 for (int i = 0; i < points; i++)
152 data[i] = 0.0625 * log(std::abs(spectrum[i * (BufferSize / 4 - 1) / (points - 1)]));
154 return true;
156 if (index == par_pd_threshold && subindex == 2)
158 context->set_source_rgba(0, 0, 0);
159 for (int i = 0; i < points; i++)
161 int j = i * (BufferSize / 2 - 1) / (points - 1);
162 float mag = magarr[j];
163 data[i] = mag;
165 return true;
167 if (index == par_pd_threshold && subindex == 3)
169 context->set_source_rgba(0, 1, 1);
170 for (int i = 0; i < points; i++)
172 int j = i * (BufferSize - 1) / (points - 1);
173 float mag = std::abs(sumsquares[j]);
174 data[i] = 0.25 * log(mag);
176 return true;
178 return false;
181 uint32_t pitch_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
183 uint32_t endpos = offset + numsamples;
184 bool has2nd = ins[1] != NULL;
186 int bperiod = BufferSize;
187 int sd = *params[par_pd_subdivide];
188 if (sd >= 1 && sd <= 8)
189 bperiod /= sd;
191 // XXXKF note: not working, not optimized, just trying stuff out
192 for (uint32_t i = offset; i < endpos; ++i)
194 float val = ins[0][i];
195 inputbuf[write_ptr] = val;
196 write_ptr = (write_ptr + 1) & (BufferSize - 1);
197 if (!(write_ptr % bperiod))
198 recompute();
199 outs[0][i] = ins[0][i];
200 if (has2nd)
201 outs[1][i] = ins[1][i];
203 return outputs_mask;
206 #endif