r1008: pt_BR translation update
[cinelerra_cv/mob.git] / plugins / pitch / pitch.C
blob554f29e9f0ccece57772bd1b0307419665d0312f
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.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);
85         defaults->load();
86         
87         config.scale = defaults->get("SCALE", config.scale);
88         return 0;
91 int PitchEffect::save_defaults()
93         char string[BCTEXTLEN];
95         defaults->update("SCALE", config.scale);
96         defaults->save();
98         return 0;
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()
115         fft = 0;
118 void PitchEffect::update_gui()
120         if(thread)
121         {
122                 load_configuration();
123                 thread->window->lock_window("PitchEffect::update_gui");
124                 thread->window->update();
125                 thread->window->unlock_window();
126         }
131 int PitchEffect::process_buffer(int64_t size, 
132                 double *buffer,
133                 int64_t start_position,
134                 int sample_rate)
136         load_configuration();
139         if(!fft)
140         {
141                 fft = new PitchFFT(this);
142                 fft->initialize(WINDOW_SIZE);
143                 fft->set_oversample(OVERSAMPLE);
144         }
146         fft->process_buffer_oversample(start_position,
147                 size, 
148                 buffer,
149                 get_direction());
151         return 0;
162 PitchFFT::PitchFFT(PitchEffect *plugin)
163  : CrossfadeFFT()
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;
178         delete [] new_freq;
179         delete [] new_magn;
180         delete [] sum_phase;
181         delete [] anal_magn;
182         delete [] anal_freq;
186 int PitchFFT::signal_process_oversample(int reset)
188         double scale = plugin->config.scale;
189         
190         memset(new_freq, 0, window_size * sizeof(double));
191         memset(new_magn, 0, window_size * sizeof(double));
192         
193         if (reset)
194         {
195                 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
196                 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
197         }
198         
199 // expected phase difference between windows
200         double expected_phase_diff = 2.0 * M_PI / oversample; 
201 // frequency per bin
202         double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
204 //scale = 1.0;
205         for (int i = 0; i < window_size/2; i++) 
206         {
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);
221                 if (qpd >= 0) 
222                         qpd += qpd&1;
223                 else 
224                         qpd -= qpd&1;
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)
239                 {
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;
245                 }
247         }
249 /*      for (int k = 0; k <= window_size/2; k++) {
250                 int index = k/scale;
251                 if (index <= window_size/2) {
252                         new_magn[k] += anal_magn[index];
253                         new_freq[k] = anal_freq[index] * scale;
254                 } else{
256                 new_magn[k] = 0;
257                 new_freq[k] = 0;
258                 }
259         }
262         // Synthesize back the fft window 
263         for (int i = 0; i < window_size/2; i++) 
264         {
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;
273 // oversample 
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);
286         }
288 //symmetry(window_size, freq_real, freq_imag);
289         for (int i = window_size/2; i< window_size; i++)
290         {
291                 fftw_data[i][0] = 0;
292                 fftw_data[i][1] = 0;
293         }
294         
296         return 0;
299 int PitchFFT::read_samples(int64_t output_sample, 
300         int samples, 
301         double *buffer)
303         return plugin->read_samples(buffer,
304                 0,
305                 plugin->get_samplerate(),
306                 output_sample,
307                 samples);
316 PitchConfig::PitchConfig()
318         scale = 1.0;
321 int PitchConfig::equivalent(PitchConfig &that)
323         return EQUIV(scale, that.scale);
326 void PitchConfig::copy_from(PitchConfig &that)
328         scale = that.scale;
331 void PitchConfig::interpolate(PitchConfig &prev, 
332         PitchConfig &next, 
333         int64_t prev_frame, 
334         int64_t next_frame, 
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, 
361         x, 
362         y, 
363         150, 
364         50, 
365         150, 
366         50,
367         0, 
368         0,
369         1)
371         this->plugin = plugin;
374 void PitchWindow::create_objects()
376         int x = 10, y = 10;
377         
378         add_subwindow(new BC_Title(x, y, _("Scale:")));
379         x += 70;
380         add_subwindow(scale = new PitchScale(plugin, x, y));
381         show_window();
382         flush();
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;
407         set_precision(0.01);
410 int PitchScale::handle_event()
412         plugin->config.scale = get_value();
413         plugin->send_configure_change();
414         return 1;