Condense the xml-cleanup into a short feature-branch
[cinelerra_cv/mob.git] / plugins / pitch / pitch.C
blobfc065eb0f099b3316bdd90e7ddbb04f4ce20e091
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "language.h"
6 #include "pitch.h"
7 #include "picon_png.h"
8 #include "units.h"
9 #include "vframe.h"
11 #include <math.h>
12 #include <string.h>
16 #define WINDOW_SIZE 8192
17 #define OVERSAMPLE 8
20 //#define WINDOW_SIZE 131072
22 REGISTER_PLUGIN(PitchEffect);
28 PitchEffect::PitchEffect(PluginServer *server)
29  : PluginAClient(server)
31         PLUGIN_CONSTRUCTOR_MACRO
32         reset();
35 PitchEffect::~PitchEffect()
37         PLUGIN_DESTRUCTOR_MACRO
39         if(fft) delete fft;
42 char* PitchEffect::plugin_title() { return N_("Pitch shift"); }
43 int PitchEffect::is_realtime() { return 1; }
47 void PitchEffect::read_data(KeyFrame *keyframe)
49         FileXML input;
50         input.set_shared_string(keyframe->data, strlen(keyframe->data));
52         int result = 0;
53         while(!result)
54         {
55                 result = input.read_tag();
57                 if(!result)
58                 {
59                         if(input.tag.title_is("PITCH"))
60                         {
61                                 config.scale = input.tag.get_property("SCALE", config.scale);
62                         }
63                 }
64         }
67 void PitchEffect::save_data(KeyFrame *keyframe)
69         FileXML output;
70         output.set_shared_string(keyframe->data, MESSAGESIZE);
72         output.tag.set_title("PITCH");
73         output.tag.set_property("SCALE", config.scale);
74         output.append_tag();
75         output.tag.set_title("/PITCH");
76         output.append_tag();
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);
87         defaults->load();
88         
89         config.scale = defaults->get("SCALE", config.scale);
90         return 0;
93 int PitchEffect::save_defaults()
95         char string[BCTEXTLEN];
97         defaults->update("SCALE", config.scale);
98         defaults->save();
100         return 0;
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()
117         fft = 0;
120 void PitchEffect::update_gui()
122         if(thread)
123         {
124                 load_configuration();
125                 thread->window->lock_window("PitchEffect::update_gui");
126                 thread->window->update();
127                 thread->window->unlock_window();
128         }
133 int PitchEffect::process_buffer(int64_t size, 
134                 double *buffer,
135                 int64_t start_position,
136                 int sample_rate)
138         load_configuration();
141         if(!fft)
142         {
143                 fft = new PitchFFT(this);
144                 fft->initialize(WINDOW_SIZE);
145                 fft->set_oversample(OVERSAMPLE);
146         }
148         fft->process_buffer_oversample(start_position,
149                 size, 
150                 buffer,
151                 get_direction());
153         return 0;
164 PitchFFT::PitchFFT(PitchEffect *plugin)
165  : CrossfadeFFT()
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;
180         delete [] new_freq;
181         delete [] new_magn;
182         delete [] sum_phase;
183         delete [] anal_magn;
184         delete [] anal_freq;
188 int PitchFFT::signal_process_oversample(int reset)
190         double scale = plugin->config.scale;
191         
192         memset(new_freq, 0, window_size * sizeof(double));
193         memset(new_magn, 0, window_size * sizeof(double));
194         
195         if (reset)
196         {
197                 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
198                 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
199         }
200         
201 // expected phase difference between windows
202         double expected_phase_diff = 2.0 * M_PI / oversample; 
203 // frequency per bin
204         double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
206 //scale = 1.0;
207         for (int i = 0; i < window_size/2; i++) 
208         {
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);
223                 if (qpd >= 0) 
224                         qpd += qpd&1;
225                 else 
226                         qpd -= qpd&1;
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)
241                 {
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;
247                 }
249         }
251 /*      for (int k = 0; k <= window_size/2; k++) {
252                 int index = k/scale;
253                 if (index <= window_size/2) {
254                         new_magn[k] += anal_magn[index];
255                         new_freq[k] = anal_freq[index] * scale;
256                 } else{
258                 new_magn[k] = 0;
259                 new_freq[k] = 0;
260                 }
261         }
264         // Synthesize back the fft window 
265         for (int i = 0; i < window_size/2; i++) 
266         {
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;
275 // oversample 
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);
288         }
290 //symmetry(window_size, freq_real, freq_imag);
291         for (int i = window_size/2; i< window_size; i++)
292         {
293                 fftw_data[i][0] = 0;
294                 fftw_data[i][1] = 0;
295         }
296         
298         return 0;
301 int PitchFFT::read_samples(int64_t output_sample, 
302         int samples, 
303         double *buffer)
305         return plugin->read_samples(buffer,
306                 0,
307                 plugin->get_samplerate(),
308                 output_sample,
309                 samples);
318 PitchConfig::PitchConfig()
320         scale = 1.0;
323 int PitchConfig::equivalent(PitchConfig &that)
325         return EQUIV(scale, that.scale);
328 void PitchConfig::copy_from(PitchConfig &that)
330         scale = that.scale;
333 void PitchConfig::interpolate(PitchConfig &prev, 
334         PitchConfig &next, 
335         int64_t prev_frame, 
336         int64_t next_frame, 
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, 
363         x, 
364         y, 
365         150, 
366         50, 
367         150, 
368         50,
369         0, 
370         0,
371         1)
373         this->plugin = plugin;
376 void PitchWindow::create_objects()
378         int x = 10, y = 10;
379         
380         add_subwindow(new BC_Title(x, y, _("Scale:")));
381         x += 70;
382         add_subwindow(scale = new PitchScale(plugin, x, y));
383         show_window();
384         flush();
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;
409         set_precision(0.01);
412 int PitchScale::handle_event()
414         plugin->config.scale = get_value();
415         plugin->send_configure_change();
416         return 1;