4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
37 // It needs to be at least 40Hz yet high enough to have enough precision
38 #define WINDOW_SIZE 2048
41 //#define WINDOW_SIZE 131072
43 REGISTER_PLUGIN(PitchEffect);
49 PitchEffect::PitchEffect(PluginServer *server)
50 : PluginAClient(server)
56 PitchEffect::~PitchEffect()
63 const char* PitchEffect::plugin_title() { return N_("Pitch shift"); }
64 int PitchEffect::is_realtime() { return 1; }
68 void PitchEffect::read_data(KeyFrame *keyframe)
71 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
76 result = input.read_tag();
80 if(input.tag.title_is("PITCH"))
82 config.scale = input.tag.get_property("SCALE", config.scale);
83 config.size = input.tag.get_property("SIZE", config.size);
89 void PitchEffect::save_data(KeyFrame *keyframe)
92 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
94 output.tag.set_title("PITCH");
95 output.tag.set_property("SCALE", config.scale);
96 output.tag.set_property("SIZE", config.size);
98 output.tag.set_title("/PITCH");
100 output.append_newline();
101 output.terminate_string();
106 LOAD_CONFIGURATION_MACRO(PitchEffect, PitchConfig)
108 NEW_WINDOW_MACRO(PitchEffect, PitchWindow)
112 void PitchEffect::reset()
117 void PitchEffect::update_gui()
121 if(load_configuration())
123 thread->window->lock_window("PitchEffect::update_gui");
124 ((PitchWindow*)thread->window)->update();
125 thread->window->unlock_window();
132 int PitchEffect::process_buffer(int64_t size,
134 int64_t start_position,
137 //printf("PitchEffect::process_buffer %d\n", __LINE__);
138 load_configuration();
140 if(fft && config.size != fft->window_size)
148 fft = new PitchFFT(this);
149 fft->initialize(config.size);
152 fft->process_buffer(start_position,
168 PitchFFT::PitchFFT(PitchEffect *plugin)
171 this->plugin = plugin;
180 PitchFFT::~PitchFFT()
182 delete [] last_phase;
191 int PitchFFT::signal_process()
194 double scale = plugin->config.scale;
195 double oversample = 1.0;
199 last_phase = new double[window_size];
200 new_freq = new double[window_size];
201 new_magn = new double[window_size];
202 sum_phase = new double[window_size];
203 anal_magn = new double[window_size];
204 anal_freq = new double[window_size];
205 memset (last_phase, 0, window_size * sizeof(double));
206 memset (sum_phase, 0, window_size * sizeof(double));
209 memset(new_freq, 0, window_size * sizeof(double));
210 memset(new_magn, 0, window_size * sizeof(double));
212 // expected phase difference between windows
213 double expected_phase_diff = 2.0 * M_PI / oversample;
215 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
217 for (int i = 0; i < window_size / 2; i++)
219 // Convert to magnitude and phase
220 double magn = sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
221 double phase = atan2(freq_imag[i], freq_real[i]);
223 // Remember last phase
224 double temp = phase - last_phase[i];
225 last_phase[i] = phase;
227 // Substract the expected advancement of phase
228 temp -= (double)i * expected_phase_diff;
231 // wrap temp into -/+ PI ... good trick!
232 int qpd = (int)(temp/M_PI);
237 temp -= M_PI * (double)qpd;
239 // Deviation from bin frequency
240 temp = oversample * temp / (2.0 * M_PI);
242 temp = (double)(temp + i) * freq_per_bin;
244 // Now temp is the real freq... move it!
245 int new_bin = (int)(i * scale);
246 if (new_bin >= 0 && new_bin < window_size / 2)
248 new_freq[new_bin] = temp*scale;
249 new_magn[new_bin] += magn;
257 // Synthesize back the fft window
258 for (int i = 0; i < window_size / 2; i++)
260 double magn = new_magn[i];
261 double temp = new_freq[i];
262 // substract the bin frequency
263 temp -= (double)(i) * freq_per_bin;
265 // get bin deviation from freq deviation
266 temp /= freq_per_bin;
269 temp = 2.0 * M_PI * temp / oversample;
271 // add back the expected phase difference (that we substracted in analysis)
272 temp += (double)(i) * expected_phase_diff;
274 // accumulate delta phase, to get bin phase
275 sum_phase[i] += temp;
277 double phase = sum_phase[i];
279 freq_real[i] = magn * cos(phase);
280 freq_imag[i] = magn * sin(phase);
283 for (int i = window_size / 2; i < window_size; i++)
293 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate /
294 window_size * 2) + 0.5);
295 if(plugin->config.scale < 1)
297 for(int i = min_freq; i < window_size / 2; i++)
299 double destination = i * plugin->config.scale;
300 int dest_i = (int)(destination + 0.5);
303 if(dest_i <= window_size / 2)
305 freq_real[dest_i] = freq_real[i];
306 freq_imag[dest_i] = freq_imag[i];
314 if(plugin->config.scale > 1)
316 for(int i = window_size / 2 - 1; i >= min_freq; i--)
318 double destination = i * plugin->config.scale;
319 int dest_i = (int)(destination + 0.5);
322 if(dest_i <= window_size / 2)
324 freq_real[dest_i] = freq_real[i];
325 freq_imag[dest_i] = freq_imag[i];
333 symmetry(window_size, freq_real, freq_imag);
340 int PitchFFT::read_samples(int64_t output_sample,
344 return plugin->read_samples(buffer,
346 plugin->get_samplerate(),
357 PitchConfig::PitchConfig()
363 int PitchConfig::equivalent(PitchConfig &that)
365 return EQUIV(scale, that.scale) && size == that.size;
368 void PitchConfig::copy_from(PitchConfig &that)
374 void PitchConfig::interpolate(PitchConfig &prev,
378 int64_t current_frame)
380 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
381 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
382 scale = prev.scale * prev_scale + next.scale * next_scale;
402 PitchWindow::PitchWindow(PitchEffect *plugin)
403 : PluginClientWindow(plugin,
410 this->plugin = plugin;
413 void PitchWindow::create_objects()
415 int x1 = 10, x = 10, y = 10;
418 add_subwindow(title = new BC_Title(x, y, _("Scale:")));
419 x += title->get_w() + plugin->get_theme()->widget_border;
420 add_subwindow(scale = new PitchScale(plugin, x, y));
422 y += scale->get_h() + plugin->get_theme()->widget_border;
423 add_subwindow(title = new BC_Title(x, y, _("Window Size:")));
424 y += title->get_h() + plugin->get_theme()->widget_border;
425 add_subwindow(size = new PitchSize(this, plugin, x, y));
426 size->create_objects();
427 size->update(plugin->config.size);
434 void PitchWindow::update()
436 scale->update(plugin->config.scale);
437 size->update(plugin->config.size);
451 PitchScale::PitchScale(PitchEffect *plugin, int x, int y)
452 : BC_FPot(x, y, (float)plugin->config.scale, .5, 1.5)
454 this->plugin = plugin;
458 int PitchScale::handle_event()
460 plugin->config.scale = get_value();
461 plugin->send_configure_change();
467 PitchSize::PitchSize(PitchWindow *window, PitchEffect *plugin, int x, int y)
468 : BC_PopupMenu(x, y, 100, "4096", 1)
470 this->plugin = plugin;
473 int PitchSize::handle_event()
475 plugin->config.size = atoi(get_text());
476 plugin->send_configure_change();
480 void PitchSize::create_objects()
482 add_item(new BC_MenuItem("2048"));
483 add_item(new BC_MenuItem("4096"));
484 add_item(new BC_MenuItem("8192"));
485 add_item(new BC_MenuItem("16384"));
486 add_item(new BC_MenuItem("32768"));
487 add_item(new BC_MenuItem("65536"));
488 add_item(new BC_MenuItem("131072"));
489 add_item(new BC_MenuItem("262144"));
492 void PitchSize::update(int size)
494 char string[BCTEXTLEN];
495 sprintf(string, "%d", size);