r996: add renderprofiles.C to POTFILES.in
[cinelerra_cv/mob.git] / plugins / timestretch / timestretch.C
blob293ce1cc9b908d84768b5946e3eb464853483aae
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.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;
41         temp = 0;
44 PitchEngine::~PitchEngine()
46         if(input_buffer) delete [] input_buffer;
47         if(temp) delete [] temp;
48         delete [] last_phase;
49         delete [] new_freq;
50         delete [] new_magn;
51         delete [] sum_phase;
52         delete [] anal_magn;
53         delete [] anal_freq;
56 int PitchEngine::read_samples(int64_t output_sample, 
57         int samples, 
58         double *buffer)
61 // FIXME, make sure this is set at the beginning, always
62 // FIXME: we need to do backward play also
63         if (current_output_sample != output_sample)
64         {
65                 input_size = 0;
66                 double input_point = plugin->get_source_start() + (output_sample - plugin->get_source_start()) / plugin->config.scale;
67                 current_input_sample = plugin->local_to_edl((int64_t)input_point);
68                 current_output_sample = output_sample;
70         }
72         while(input_size < samples)
73         {
74                 double scale = plugin->config.scale;
75                 if(!temp) temp = new double[INPUT_SIZE];
77                 plugin->read_samples(temp, 
78                         0, 
79                         plugin->get_samplerate(),
80                         current_input_sample, 
81                         INPUT_SIZE);
82                 current_input_sample +=INPUT_SIZE;
84                 plugin->resample->resample_chunk(temp,
85                         INPUT_SIZE,
86                         1000000,
87                         (int)(1000000 * scale),
88                         0);
90                 int fragment_size = plugin->resample->get_output_size(0);
92                 if(input_size + fragment_size > input_allocated)
93                 {
94                         int new_allocated = input_size + fragment_size;
95                         double *new_buffer = new double[new_allocated];
96                         if(input_buffer)
97                         {
98                                 memcpy(new_buffer, input_buffer, input_size * sizeof(double));
99                                 delete [] input_buffer;
100                         }
101                         input_buffer = new_buffer;
102                         input_allocated = new_allocated;
103                 }
106                 plugin->resample->read_output(input_buffer + input_size,
107                         0,
108                         fragment_size);
109                 input_size += fragment_size;
110         }
111         memcpy(buffer, input_buffer, samples * sizeof(int64_t));
112         memcpy(input_buffer, 
113                 input_buffer + samples, 
114                 sizeof(int64_t) * (input_size - samples));
115         input_size -= samples;
116         current_output_sample += samples;
117         return 0;
120 int PitchEngine::signal_process_oversample(int reset)
122         double scale = plugin->config.scale;
123         
124         memset(new_freq, 0, window_size * sizeof(double));
125         memset(new_magn, 0, window_size * sizeof(double));
126         
127         if (reset)
128         {
129                 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
130                 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
131         }
132         
133         // new or old behaviour
134         if (1)
135         {       
136         // expected phase difference between windows
137                 double expected_phase_diff = 2.0 * M_PI / oversample; 
138         // frequency per bin
139                 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
141         //scale = 1.0;
142                 for (int i = 0; i < window_size/2; i++) 
143                 {
144         // Convert to magnitude and phase
145                         double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
146                         double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
147                         
148         // Remember last phase
149                         double temp = phase - last_phase[i];
150                         last_phase[i] = phase;
151                 
152         // Substract the expected advancement of phase
153                         temp -= (double)i * expected_phase_diff;
154                         
156         // wrap temp into -/+ PI ...  good trick!
157                         int qpd = (int)(temp/M_PI);
158                         if (qpd >= 0) 
159                                 qpd += qpd&1;
160                         else 
161                                 qpd -= qpd&1;
162                         temp -= M_PI*(double)qpd;       
164         // Deviation from bin frequency 
165                         temp = oversample * temp / (2.0 * M_PI);
166                         
167                         temp = (double)(temp + i) * freq_per_bin;
169                         anal_magn[i] = magn;
170                         anal_freq[i] = temp;
172         // Now temp is the real freq... move it!
173         //              int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
174         /*              int new_bin = (int)(i *scale);
175                         if (new_bin >= 0 && new_bin < window_size/2)
176                         {
177         //                      double tot_magn = new_magn[new_bin] + magn;
178                                 
179         //                      new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
180                                 new_freq[new_bin] = temp*scale;
181                                 new_magn[new_bin] += magn;
182                         }
184                 }
185                 
186                 for (int k = 0; k <= window_size/2; k++) {
187                         int index = int(k/scale);
188                         if (index <= window_size/2) {
189                                 new_magn[k] += anal_magn[index];
190                                 new_freq[k] = anal_freq[index] * scale;
191                         } else{
192                         
193                         new_magn[k] = 0;
194                         new_freq[k] = 0;
195                         }
196                 }
198         
199                 // Synthesize back the fft window 
200                 for (int i = 0; i < window_size/2; i++) 
201                 {
202                         double magn = new_magn[i];
203                         double temp = new_freq[i];
204         // substract the bin frequency
205                         temp -= (double)(i) * freq_per_bin;
207         // get bin deviation from freq deviation
208                         temp /= freq_per_bin;
209                         
210         // oversample 
211                         temp = 2.0 * M_PI *temp / oversample;
212                 
213         // add back the expected phase difference (that we substracted in analysis)
214                         temp += (double)(i) * expected_phase_diff;
216         // accumulate delta phase, to get bin phase
217                         sum_phase[i] += temp;
218                         
219                         double phase = sum_phase[i];
221                         fftw_data[i][0] = magn * cos(phase);
222                         fftw_data[i][1] = magn * sin(phase);
223                 }
224         } else
225         {
226                 int min_freq = 
227                         1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate / 
228                                 window_size * 2) + 0.5);
229                 if(plugin->config.scale < 1)
230                 {
231                         for(int i = min_freq; i < window_size / 2; i++)
232                         {
233                                 double destination = i * plugin->config.scale;
234                                 int dest_i = (int)(destination + 0.5);
235                                 if(dest_i != i)
236                                 {
237                                         if(dest_i <= window_size / 2)
238                                         {
239                                                 fftw_data[dest_i][0] = fftw_data[i][0];
240                                                 fftw_data[dest_i][1] = fftw_data[i][1];
241                                         }
242                                         fftw_data[i][0] = 0;
243                                         fftw_data[i][1] = 0;
244                                 }
245                         }
246                 }
247                 else
248                 if(plugin->config.scale > 1)
249                 {
250                         for(int i = window_size / 2 - 1; i >= min_freq; i--)
251                         {
252                                 double destination = i * plugin->config.scale;
253                                 int dest_i = (int)(destination + 0.5);
254                                 if(dest_i != i)
255                                 {
256                                         if(dest_i <= window_size / 2)
257                                         {
258                                                 fftw_data[dest_i][0] = fftw_data[i][0];
259                                                 fftw_data[dest_i][1] = fftw_data[i][1];
260                                         }
261                                         fftw_data[i][0] = 0;
262                                         fftw_data[i][1] = 0;
263                                 }
264                         }
265                 }
266         }
268 //symmetry(window_size, freq_real, freq_imag);
269         for (int i = window_size/2; i< window_size; i++)
270         {
271                 fftw_data[i][0] = 0;
272                 fftw_data[i][1] = 0;
273         }
274         
276         return 0;
292 TimeStretch::TimeStretch(PluginServer *server)
293  : PluginAClient(server)
295         PLUGIN_CONSTRUCTOR_MACRO
296         load_defaults();
297         temp = 0;
298         pitch = 0;
299         resample = 0;
300         stretch = 0;
301         input = 0;
302         input_allocated = 0;
306 TimeStretch::~TimeStretch()
308         PLUGIN_DESTRUCTOR_MACRO
309         if(temp) delete [] temp;
310         if(input) delete [] input;
311         if(pitch) delete pitch;
312         if(resample) delete resample;
313         if(stretch) delete stretch;
316         
317         
318 char* TimeStretch::plugin_title() { return N_("Time stretch"); }
319 int TimeStretch::is_realtime() { return 1; }
321 void TimeStretch::read_data(KeyFrame *keyframe)
323         FileXML input;
324         input.set_shared_string(keyframe->data, strlen(keyframe->data));
326         int result = 0;
327         while(!result)
328         {
329                 result = input.read_tag();
331                 if(!result)
332                 {
333                         if(input.tag.title_is("TIMESTRETCH"))
334                         {
335                                 config.scale = input.tag.get_property("SCALE", config.scale);
336                         }
337                 }
338         }
341 void TimeStretch::save_data(KeyFrame *keyframe)
343         FileXML output;
344         output.set_shared_string(keyframe->data, MESSAGESIZE);
346         output.tag.set_title("TIMESTRETCH");
347         output.tag.set_property("SCALE", config.scale);
348         output.append_tag();
349         output.append_newline();
351         output.terminate_string();
356 int TimeStretch::load_defaults()
358         char directory[BCTEXTLEN];
360 // set the default directory
361         sprintf(directory, "%stimestretch.rc", BCASTDIR);
362 // load the defaults
363         defaults = new BC_Hash(directory);
364         defaults->load();
366         config.scale = defaults->get("SCALE", (double)1);
367         return 0;
370 int TimeStretch::save_defaults()
372         defaults->update("SCALE", config.scale);
373         defaults->save();
374         return 0;
378 TimeStretchConfig::TimeStretchConfig()
380         scale = 1.0;
383 int TimeStretchConfig::equivalent(TimeStretchConfig &that)
385         return EQUIV(scale, that.scale);
388 void TimeStretchConfig::copy_from(TimeStretchConfig &that)
390         scale = that.scale;
393 void TimeStretchConfig::interpolate(TimeStretchConfig &prev, 
394         TimeStretchConfig &next, 
395         int64_t prev_frame, 
396         int64_t next_frame, 
397         int64_t current_frame)
399         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
400         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
401         scale = prev.scale * prev_scale + next.scale * next_scale;
407 LOAD_CONFIGURATION_MACRO(TimeStretch, TimeStretchConfig)
409 SHOW_GUI_MACRO(TimeStretch, TimeStretchThread)
411 RAISE_WINDOW_MACRO(TimeStretch)
413 SET_STRING_MACRO(TimeStretch)
415 NEW_PICON_MACRO(TimeStretch)
418 void TimeStretch::update_gui()
420         if(thread)
421         {
422                 load_configuration();
423                 thread->window->lock_window("TimeStretch::update_gui");
424                 thread->window->update();
425                 thread->window->unlock_window();
426         }
431 int TimeStretch::get_parameters()
433         BC_DisplayInfo info;
434         TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
435         window.create_objects();
436         int result = window.run_window();
437         
438         return result;
442 //int TimeStretch::process_loop(double *buffer, int64_t &write_length)
443 int TimeStretch::process_buffer(int64_t size, 
444                 double *buffer,
445                 int64_t start_position,
446                 int sample_rate)
448         load_configuration();
450         int result = 0;
452         if(!pitch)
453         {
454                 pitch = new PitchEngine(this);
455                 pitch->initialize(WINDOW_SIZE);
456                 pitch->set_oversample(OVERSAMPLE);
457                 resample = new Resample(0, 1);
459         }
461         pitch->process_buffer_oversample(start_position,
462                 size, 
463                 buffer,
464                 get_direction());
467         return result;
472 PLUGIN_THREAD_OBJECT(TimeStretch, TimeStretchThread, TimeStretchWindow) 
475 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
476  : BC_Window(plugin->gui_string, 
477         x, 
478         y, 
479         150, 
480         50, 
481         150, 
482         50,
483         0, 
484         0,
485         1)
487         this->plugin = plugin;
490 void TimeStretchWindow::create_objects()
492         int x = 10, y = 10;
493         
494         add_subwindow(new BC_Title(x, y, _("Scale:")));
495         x += 70;
496         add_subwindow(scale = new TimeStretchScale(plugin, x, y));
497         show_window();
498         flush();
501 WINDOW_CLOSE_EVENT(TimeStretchWindow)
503 void TimeStretchWindow::update()
505         scale->update(plugin->config.scale);
510 TimeStretchScale::TimeStretchScale(TimeStretch *plugin, int x, int y)
511  : BC_FPot(x, y, (float)plugin->config.scale, .3, 2)
513         this->plugin = plugin;
514         set_precision(0.001);
517 int TimeStretchScale::handle_event()
519         plugin->config.scale = get_value();
520         plugin->send_configure_change();
521         return 1;