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.append_newline();
77 output.terminate_string();
80 int PitchEffect::load_defaults()
82 char directory[BCTEXTLEN], string[BCTEXTLEN];
83 sprintf(directory, "%spitch.rc", BCASTDIR);
84 defaults = new BC_Hash(directory);
87 config.scale = defaults->get("SCALE", config.scale);
91 int PitchEffect::save_defaults()
93 char string[BCTEXTLEN];
95 defaults->update("SCALE", config.scale);
102 LOAD_CONFIGURATION_MACRO(PitchEffect, PitchConfig)
104 SHOW_GUI_MACRO(PitchEffect, PitchThread)
106 RAISE_WINDOW_MACRO(PitchEffect)
108 SET_STRING_MACRO(PitchEffect)
110 NEW_PICON_MACRO(PitchEffect)
113 void PitchEffect::reset()
118 void PitchEffect::update_gui()
122 load_configuration();
123 thread->window->lock_window("PitchEffect::update_gui");
124 thread->window->update();
125 thread->window->unlock_window();
131 int PitchEffect::process_buffer(int64_t size,
133 int64_t start_position,
136 load_configuration();
141 fft = new PitchFFT(this);
142 fft->initialize(WINDOW_SIZE);
143 fft->set_oversample(OVERSAMPLE);
146 fft->process_buffer_oversample(start_position,
162 PitchFFT::PitchFFT(PitchEffect *plugin)
165 this->plugin = plugin;
166 last_phase = new double[WINDOW_SIZE];
167 new_freq = new double[WINDOW_SIZE];
168 new_magn = new double[WINDOW_SIZE];
169 sum_phase = new double[WINDOW_SIZE];
170 anal_magn = new double[WINDOW_SIZE];
171 anal_freq = new double[WINDOW_SIZE];
175 PitchFFT::~PitchFFT()
177 delete [] last_phase;
186 int PitchFFT::signal_process_oversample(int reset)
188 double scale = plugin->config.scale;
190 memset(new_freq, 0, window_size * sizeof(double));
191 memset(new_magn, 0, window_size * sizeof(double));
195 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
196 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
199 // expected phase difference between windows
200 double expected_phase_diff = 2.0 * M_PI / oversample;
202 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
205 for (int i = 0; i < window_size/2; i++)
207 // Convert to magnitude and phase
208 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
209 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
211 // Remember last phase
212 double temp = phase - last_phase[i];
213 last_phase[i] = phase;
215 // Substract the expected advancement of phase
216 temp -= (double)i * expected_phase_diff;
219 // wrap temp into -/+ PI ... good trick!
220 int qpd = (int)(temp/M_PI);
225 temp -= M_PI*(double)qpd;
227 // Deviation from bin frequency
228 temp = oversample * temp / (2.0 * M_PI);
230 temp = (double)(temp + i) * freq_per_bin;
232 // anal_magn[i] = magn;
233 // anal_freq[i] = temp;
235 // Now temp is the real freq... move it!
236 // int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
237 int new_bin = (int)(i * scale);
238 if (new_bin >= 0 && new_bin < window_size/2)
240 // double tot_magn = new_magn[new_bin] + magn;
242 // new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
243 new_freq[new_bin] = temp*scale;
244 new_magn[new_bin] += magn;
249 /* for (int k = 0; k <= window_size/2; k++) {
251 if (index <= window_size/2) {
252 new_magn[k] += anal_magn[index];
253 new_freq[k] = anal_freq[index] * scale;
262 // Synthesize back the fft window
263 for (int i = 0; i < window_size/2; i++)
265 double magn = new_magn[i];
266 double temp = new_freq[i];
267 // substract the bin frequency
268 temp -= (double)(i) * freq_per_bin;
270 // get bin deviation from freq deviation
271 temp /= freq_per_bin;
274 temp = 2.0 * M_PI *temp / oversample;
276 // add back the expected phase difference (that we substracted in analysis)
277 temp += (double)(i) * expected_phase_diff;
279 // accumulate delta phase, to get bin phase
280 sum_phase[i] += temp;
282 double phase = sum_phase[i];
284 fftw_data[i][0] = magn * cos(phase);
285 fftw_data[i][1] = magn * sin(phase);
288 //symmetry(window_size, freq_real, freq_imag);
289 for (int i = window_size/2; i< window_size; i++)
299 int PitchFFT::read_samples(int64_t output_sample,
303 return plugin->read_samples(buffer,
305 plugin->get_samplerate(),
316 PitchConfig::PitchConfig()
321 int PitchConfig::equivalent(PitchConfig &that)
323 return EQUIV(scale, that.scale);
326 void PitchConfig::copy_from(PitchConfig &that)
331 void PitchConfig::interpolate(PitchConfig &prev,
335 int64_t current_frame)
337 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
338 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
339 scale = prev.scale * prev_scale + next.scale * next_scale;
349 PLUGIN_THREAD_OBJECT(PitchEffect, PitchThread, PitchWindow)
359 PitchWindow::PitchWindow(PitchEffect *plugin, int x, int y)
360 : BC_Window(plugin->gui_string,
371 this->plugin = plugin;
374 void PitchWindow::create_objects()
378 add_subwindow(new BC_Title(x, y, _("Scale:")));
380 add_subwindow(scale = new PitchScale(plugin, x, y));
385 WINDOW_CLOSE_EVENT(PitchWindow)
387 void PitchWindow::update()
389 scale->update(plugin->config.scale);
403 PitchScale::PitchScale(PitchEffect *plugin, int x, int y)
404 : BC_FPot(x, y, (float)plugin->config.scale, .5, 1.5)
406 this->plugin = plugin;
410 int PitchScale::handle_event()
412 plugin->config.scale = get_value();
413 plugin->send_configure_change();