1 #include "bcdisplayinfo.h"
16 #define WINDOW_SIZE 8192
20 //#define WINDOW_SIZE 131072
22 REGISTER_PLUGIN(PitchEffect);
28 PitchEffect::PitchEffect(PluginServer *server)
29 : PluginAClient(server)
31 PLUGIN_CONSTRUCTOR_MACRO
35 PitchEffect::~PitchEffect()
37 PLUGIN_DESTRUCTOR_MACRO
42 char* PitchEffect::plugin_title() { return N_("Pitch shift"); }
43 int PitchEffect::is_realtime() { return 1; }
47 void PitchEffect::read_data(KeyFrame *keyframe)
50 input.set_shared_string(keyframe->data, strlen(keyframe->data));
55 result = input.read_tag();
59 if(input.tag.title_is("PITCH"))
61 config.scale = input.tag.get_property("SCALE", config.scale);
67 void PitchEffect::save_data(KeyFrame *keyframe)
70 output.set_shared_string(keyframe->data, MESSAGESIZE);
72 output.tag.set_title("PITCH");
73 output.tag.set_property("SCALE", config.scale);
75 output.tag.set_title("/PITCH");
77 output.append_newline();
79 output.terminate_string();
82 int PitchEffect::load_defaults()
84 char directory[BCTEXTLEN], string[BCTEXTLEN];
85 sprintf(directory, "%spitch.rc", BCASTDIR);
86 defaults = new BC_Hash(directory);
89 config.scale = defaults->get("SCALE", config.scale);
93 int PitchEffect::save_defaults()
95 char string[BCTEXTLEN];
97 defaults->update("SCALE", config.scale);
104 LOAD_CONFIGURATION_MACRO(PitchEffect, PitchConfig)
106 SHOW_GUI_MACRO(PitchEffect, PitchThread)
108 RAISE_WINDOW_MACRO(PitchEffect)
110 SET_STRING_MACRO(PitchEffect)
112 NEW_PICON_MACRO(PitchEffect)
115 void PitchEffect::reset()
120 void PitchEffect::update_gui()
124 load_configuration();
125 thread->window->lock_window("PitchEffect::update_gui");
126 thread->window->update();
127 thread->window->unlock_window();
133 int PitchEffect::process_buffer(int64_t size,
135 int64_t start_position,
138 load_configuration();
143 fft = new PitchFFT(this);
144 fft->initialize(WINDOW_SIZE);
145 fft->set_oversample(OVERSAMPLE);
148 fft->process_buffer_oversample(start_position,
164 PitchFFT::PitchFFT(PitchEffect *plugin)
167 this->plugin = plugin;
168 last_phase = new double[WINDOW_SIZE];
169 new_freq = new double[WINDOW_SIZE];
170 new_magn = new double[WINDOW_SIZE];
171 sum_phase = new double[WINDOW_SIZE];
172 anal_magn = new double[WINDOW_SIZE];
173 anal_freq = new double[WINDOW_SIZE];
177 PitchFFT::~PitchFFT()
179 delete [] last_phase;
188 int PitchFFT::signal_process_oversample(int reset)
190 double scale = plugin->config.scale;
192 memset(new_freq, 0, window_size * sizeof(double));
193 memset(new_magn, 0, window_size * sizeof(double));
197 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
198 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
201 // expected phase difference between windows
202 double expected_phase_diff = 2.0 * M_PI / oversample;
204 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
207 for (int i = 0; i < window_size/2; i++)
209 // Convert to magnitude and phase
210 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
211 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
213 // Remember last phase
214 double temp = phase - last_phase[i];
215 last_phase[i] = phase;
217 // Substract the expected advancement of phase
218 temp -= (double)i * expected_phase_diff;
221 // wrap temp into -/+ PI ... good trick!
222 int qpd = (int)(temp/M_PI);
227 temp -= M_PI*(double)qpd;
229 // Deviation from bin frequency
230 temp = oversample * temp / (2.0 * M_PI);
232 temp = (double)(temp + i) * freq_per_bin;
234 // anal_magn[i] = magn;
235 // anal_freq[i] = temp;
237 // Now temp is the real freq... move it!
238 // int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
239 int new_bin = (int)(i * scale);
240 if (new_bin >= 0 && new_bin < window_size/2)
242 // double tot_magn = new_magn[new_bin] + magn;
244 // new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
245 new_freq[new_bin] = temp*scale;
246 new_magn[new_bin] += magn;
251 /* for (int k = 0; k <= window_size/2; k++) {
253 if (index <= window_size/2) {
254 new_magn[k] += anal_magn[index];
255 new_freq[k] = anal_freq[index] * scale;
264 // Synthesize back the fft window
265 for (int i = 0; i < window_size/2; i++)
267 double magn = new_magn[i];
268 double temp = new_freq[i];
269 // substract the bin frequency
270 temp -= (double)(i) * freq_per_bin;
272 // get bin deviation from freq deviation
273 temp /= freq_per_bin;
276 temp = 2.0 * M_PI *temp / oversample;
278 // add back the expected phase difference (that we substracted in analysis)
279 temp += (double)(i) * expected_phase_diff;
281 // accumulate delta phase, to get bin phase
282 sum_phase[i] += temp;
284 double phase = sum_phase[i];
286 fftw_data[i][0] = magn * cos(phase);
287 fftw_data[i][1] = magn * sin(phase);
290 //symmetry(window_size, freq_real, freq_imag);
291 for (int i = window_size/2; i< window_size; i++)
301 int PitchFFT::read_samples(int64_t output_sample,
305 return plugin->read_samples(buffer,
307 plugin->get_samplerate(),
318 PitchConfig::PitchConfig()
323 int PitchConfig::equivalent(PitchConfig &that)
325 return EQUIV(scale, that.scale);
328 void PitchConfig::copy_from(PitchConfig &that)
333 void PitchConfig::interpolate(PitchConfig &prev,
337 int64_t current_frame)
339 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
340 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
341 scale = prev.scale * prev_scale + next.scale * next_scale;
351 PLUGIN_THREAD_OBJECT(PitchEffect, PitchThread, PitchWindow)
361 PitchWindow::PitchWindow(PitchEffect *plugin, int x, int y)
362 : BC_Window(plugin->gui_string,
373 this->plugin = plugin;
376 void PitchWindow::create_objects()
380 add_subwindow(new BC_Title(x, y, _("Scale:")));
382 add_subwindow(scale = new PitchScale(plugin, x, y));
387 WINDOW_CLOSE_EVENT(PitchWindow)
389 void PitchWindow::update()
391 scale->update(plugin->config.scale);
405 PitchScale::PitchScale(PitchEffect *plugin, int x, int y)
406 : BC_FPot(x, y, (float)plugin->config.scale, .5, 1.5)
408 this->plugin = plugin;
412 int PitchScale::handle_event()
414 plugin->config.scale = get_value();
415 plugin->send_configure_change();