r784: Fix to bug 250 by Dan Streetman
[cinelerra_cv/mob.git] / plugins / timestretch / timestretch.C
blobf030697c8f1555c6fdf2b7de1cc7d2c96e1c760c
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "defaults.h"
4 #include "language.h"
5 #include "mainprogress.h"
6 #include "picon_png.h"
7 #include "resample.h"
8 #include "timestretch.h"
9 #include "timestretchengine.h"
10 #include "transportque.inc"
11 #include "vframe.h"
12 #include "filexml.h"
14 #include <string.h>
17 #define WINDOW_SIZE 4096
18 #define INPUT_SIZE 65536
19 #define OVERSAMPLE 8
22 REGISTER_PLUGIN(TimeStretch)
26 PitchEngine::PitchEngine(TimeStretch *plugin)
27  : CrossfadeFFT()
29         this->plugin = plugin;
30         last_phase = new double[WINDOW_SIZE];
31         new_freq = new double[WINDOW_SIZE];
32         new_magn = new double[WINDOW_SIZE];
33         sum_phase = new double[WINDOW_SIZE];
34         anal_magn = new double[WINDOW_SIZE];
35         anal_freq = new double[WINDOW_SIZE];
37         input_buffer = 0;
38         input_size = 0;
39         input_allocated = 0;
40         current_output_sample = -100000000000LL;
42         
43         temp = 0;
47 PitchEngine::~PitchEngine()
49         if(input_buffer) delete [] input_buffer;
50         if(temp) delete [] temp;
51         delete [] last_phase;
52         delete [] new_freq;
53         delete [] new_magn;
54         delete [] sum_phase;
55         delete [] anal_magn;
56         delete [] anal_freq;
62 int PitchEngine::read_samples(int64_t output_sample, 
63         int samples, 
64         double *buffer)
67 // FIXME, make sure this is set at the beginning, always
68 // FIXME: we need to do backward play also
69         if (current_output_sample != output_sample)
70         {
71                 input_size = 0;
72                 double input_point = plugin->get_source_start() + (output_sample - plugin->get_source_start()) / plugin->config.scale;
73                 current_input_sample = plugin->local_to_edl((int64_t)input_point);
74                 current_output_sample = output_sample;
76         }
78         while(input_size < samples)
79         {
80                 double scale = plugin->config.scale;
81                 if(!temp) temp = new double[INPUT_SIZE];
83                 plugin->read_samples(temp, 
84                         0, 
85                         plugin->get_samplerate(),
86                         current_input_sample, 
87                         INPUT_SIZE);
88                 current_input_sample +=INPUT_SIZE;
90                 plugin->resample->resample_chunk(temp,
91                         INPUT_SIZE,
92                         1000000,
93                         (int)(1000000 * scale),
94                         0);
96                 int fragment_size = plugin->resample->get_output_size(0);
98                 if(input_size + fragment_size > input_allocated)
99                 {
100                         int new_allocated = input_size + fragment_size;
101                         double *new_buffer = new double[new_allocated];
102                         if(input_buffer)
103                         {
104                                 memcpy(new_buffer, input_buffer, input_size * sizeof(double));
105                                 delete [] input_buffer;
106                         }
107                         input_buffer = new_buffer;
108                         input_allocated = new_allocated;
109                 }
112                 plugin->resample->read_output(input_buffer + input_size,
113                         0,
114                         fragment_size);
115                 input_size += fragment_size;
116         }
117         memcpy(buffer, input_buffer, samples * sizeof(int64_t));
118         memcpy(input_buffer, 
119                 input_buffer + samples, 
120                 sizeof(int64_t) * (input_size - samples));
121         input_size -= samples;
122         current_output_sample += samples;
123         return 0;
126 int PitchEngine::signal_process_oversample(int reset)
128         double scale = plugin->config.scale;
129         
130         memset(new_freq, 0, window_size * sizeof(double));
131         memset(new_magn, 0, window_size * sizeof(double));
132         
133         if (reset)
134         {
135                 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
136                 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
137         }
138         
139         // new or old behaviour
140         if (1)
141         {       
142         // expected phase difference between windows
143                 double expected_phase_diff = 2.0 * M_PI / oversample; 
144         // frequency per bin
145                 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
147         //scale = 1.0;
148                 for (int i = 0; i < window_size/2; i++) 
149                 {
150         // Convert to magnitude and phase
151                         double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
152                         double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
153                         
154         // Remember last phase
155                         double temp = phase - last_phase[i];
156                         last_phase[i] = phase;
157                 
158         // Substract the expected advancement of phase
159                         temp -= (double)i * expected_phase_diff;
160                         
162         // wrap temp into -/+ PI ...  good trick!
163                         int qpd = (int)(temp/M_PI);
164                         if (qpd >= 0) 
165                                 qpd += qpd&1;
166                         else 
167                                 qpd -= qpd&1;
168                         temp -= M_PI*(double)qpd;       
170         // Deviation from bin frequency 
171                         temp = oversample * temp / (2.0 * M_PI);
172                         
173                         temp = (double)(temp + i) * freq_per_bin;
175                         anal_magn[i] = magn;
176                         anal_freq[i] = temp;
178         // Now temp is the real freq... move it!
179         //              int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
180         /*              int new_bin = (int)(i *scale);
181                         if (new_bin >= 0 && new_bin < window_size/2)
182                         {
183         //                      double tot_magn = new_magn[new_bin] + magn;
184                                 
185         //                      new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
186                                 new_freq[new_bin] = temp*scale;
187                                 new_magn[new_bin] += magn;
188                         }
190                 }
191                 
192                 for (int k = 0; k <= window_size/2; k++) {
193                         int index = int(k/scale);
194                         if (index <= window_size/2) {
195                                 new_magn[k] += anal_magn[index];
196                                 new_freq[k] = anal_freq[index] * scale;
197                         } else{
198                         
199                         new_magn[k] = 0;
200                         new_freq[k] = 0;
201                         }
202                 }
204         
205                 // Synthesize back the fft window 
206                 for (int i = 0; i < window_size/2; i++) 
207                 {
208                         double magn = new_magn[i];
209                         double temp = new_freq[i];
210         // substract the bin frequency
211                         temp -= (double)(i) * freq_per_bin;
213         // get bin deviation from freq deviation
214                         temp /= freq_per_bin;
215                         
216         // oversample 
217                         temp = 2.0 * M_PI *temp / oversample;
218                 
219         // add back the expected phase difference (that we substracted in analysis)
220                         temp += (double)(i) * expected_phase_diff;
222         // accumulate delta phase, to get bin phase
223                         sum_phase[i] += temp;
224                         
225                         double phase = sum_phase[i];
227                         fftw_data[i][0] = magn * cos(phase);
228                         fftw_data[i][1] = magn * sin(phase);
229                 }
230         } else
231         {
232                 int min_freq = 
233                         1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate / 
234                                 window_size * 2) + 0.5);
235                 if(plugin->config.scale < 1)
236                 {
237                         for(int i = min_freq; i < window_size / 2; i++)
238                         {
239                                 double destination = i * plugin->config.scale;
240                                 int dest_i = (int)(destination + 0.5);
241                                 if(dest_i != i)
242                                 {
243                                         if(dest_i <= window_size / 2)
244                                         {
245                                                 fftw_data[dest_i][0] = fftw_data[i][0];
246                                                 fftw_data[dest_i][1] = fftw_data[i][1];
247                                         }
248                                         fftw_data[i][0] = 0;
249                                         fftw_data[i][1] = 0;
250                                 }
251                         }
252                 }
253                 else
254                 if(plugin->config.scale > 1)
255                 {
256                         for(int i = window_size / 2 - 1; i >= min_freq; i--)
257                         {
258                                 double destination = i * plugin->config.scale;
259                                 int dest_i = (int)(destination + 0.5);
260                                 if(dest_i != i)
261                                 {
262                                         if(dest_i <= window_size / 2)
263                                         {
264                                                 fftw_data[dest_i][0] = fftw_data[i][0];
265                                                 fftw_data[dest_i][1] = fftw_data[i][1];
266                                         }
267                                         fftw_data[i][0] = 0;
268                                         fftw_data[i][1] = 0;
269                                 }
270                         }
271                 }
272         }
274 //symmetry(window_size, freq_real, freq_imag);
275         for (int i = window_size/2; i< window_size; i++)
276         {
277                 fftw_data[i][0] = 0;
278                 fftw_data[i][1] = 0;
279         }
280         
282         return 0;
298 TimeStretch::TimeStretch(PluginServer *server)
299  : PluginAClient(server)
301         PLUGIN_CONSTRUCTOR_MACRO
302         load_defaults();
303         temp = 0;
304         pitch = 0;
305         resample = 0;
306         stretch = 0;
307         input = 0;
308         input_allocated = 0;
312 TimeStretch::~TimeStretch()
314         PLUGIN_DESTRUCTOR_MACRO
315         if(temp) delete [] temp;
316         if(input) delete [] input;
317         if(pitch) delete pitch;
318         if(resample) delete resample;
319         if(stretch) delete stretch;
322         
323         
324 char* TimeStretch::plugin_title() { return N_("Time stretch"); }
325 int TimeStretch::is_realtime() { return 1; }
327 void TimeStretch::read_data(KeyFrame *keyframe)
329         FileXML input;
330         input.set_shared_string(keyframe->data, strlen(keyframe->data));
332         int result = 0;
333         while(!result)
334         {
335                 result = input.read_tag();
337                 if(!result)
338                 {
339                         if(input.tag.title_is("TIMESTRETCH"))
340                         {
341                                 config.scale = input.tag.get_property("SCALE", config.scale);
342                         }
343                 }
344         }
347 void TimeStretch::save_data(KeyFrame *keyframe)
349         FileXML output;
350         output.set_shared_string(keyframe->data, MESSAGESIZE);
352         output.tag.set_title("TIMESTRETCH");
353         output.tag.set_property("SCALE", config.scale);
354         output.append_tag();
355         output.append_newline();
357         output.terminate_string();
362 int TimeStretch::load_defaults()
364         char directory[BCTEXTLEN];
366 // set the default directory
367         sprintf(directory, "%stimestretch.rc", BCASTDIR);
368 // load the defaults
369         defaults = new Defaults(directory);
370         defaults->load();
372         config.scale = defaults->get("SCALE", (double)1);
373         return 0;
376 int TimeStretch::save_defaults()
378         defaults->update("SCALE", config.scale);
379         defaults->save();
380         return 0;
384 TimeStretchConfig::TimeStretchConfig()
386         scale = 1.0;
389 int TimeStretchConfig::equivalent(TimeStretchConfig &that)
391         return EQUIV(scale, that.scale);
394 void TimeStretchConfig::copy_from(TimeStretchConfig &that)
396         scale = that.scale;
399 void TimeStretchConfig::interpolate(TimeStretchConfig &prev, 
400         TimeStretchConfig &next, 
401         int64_t prev_frame, 
402         int64_t next_frame, 
403         int64_t current_frame)
405         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
406         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
407         scale = prev.scale * prev_scale + next.scale * next_scale;
413 LOAD_CONFIGURATION_MACRO(TimeStretch, TimeStretchConfig)
415 SHOW_GUI_MACRO(TimeStretch, TimeStretchThread)
417 RAISE_WINDOW_MACRO(TimeStretch)
419 SET_STRING_MACRO(TimeStretch)
421 NEW_PICON_MACRO(TimeStretch)
424 void TimeStretch::update_gui()
426         if(thread)
427         {
428                 load_configuration();
429                 thread->window->lock_window("TimeStretch::update_gui");
430                 thread->window->update();
431                 thread->window->unlock_window();
432         }
437 int TimeStretch::get_parameters()
439         BC_DisplayInfo info;
440         TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
441         window.create_objects();
442         int result = window.run_window();
443         
444         return result;
448 //int TimeStretch::process_loop(double *buffer, int64_t &write_length)
449 int TimeStretch::process_buffer(int64_t size, 
450                 double *buffer,
451                 int64_t start_position,
452                 int sample_rate)
454         load_configuration();
456         int result = 0;
458         if(!pitch)
459         {
460                 pitch = new PitchEngine(this);
461                 pitch->initialize(WINDOW_SIZE);
462                 pitch->set_oversample(OVERSAMPLE);
463                 resample = new Resample(0, 1);
465         }
467         pitch->process_buffer_oversample(start_position,
468                 size, 
469                 buffer,
470                 get_direction());
473         return result;
478 PLUGIN_THREAD_OBJECT(TimeStretch, TimeStretchThread, TimeStretchWindow) 
481 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
482  : BC_Window(plugin->gui_string, 
483         x, 
484         y, 
485         150, 
486         50, 
487         150, 
488         50,
489         0, 
490         0,
491         1)
493         this->plugin = plugin;
496 void TimeStretchWindow::create_objects()
498         int x = 10, y = 10;
499         
500         add_subwindow(new BC_Title(x, y, _("Scale:")));
501         x += 70;
502         add_subwindow(scale = new TimeStretchScale(plugin, x, y));
503         show_window();
504         flush();
507 WINDOW_CLOSE_EVENT(TimeStretchWindow)
509 void TimeStretchWindow::update()
511         scale->update(plugin->config.scale);
516 TimeStretchScale::TimeStretchScale(TimeStretch *plugin, int x, int y)
517  : BC_FPot(x, y, (float)plugin->config.scale, .3, 2)
519         this->plugin = plugin;
520         set_precision(0.001);
523 int TimeStretchScale::handle_event()
525         plugin->config.scale = get_value();
526         plugin->send_configure_change();
527         return 1;