initial commit
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / reframert / reframert.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2016 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "guicast.h"
27 #include "language.h"
28 #include "pluginvclient.h"
29 #include "theme.h"
30 #include "transportque.h"
31
32 #include <string.h>
33
34 class ReframeRT;
35 class ReframeRTWindow;
36
37 class ReframeRTConfig
38 {
39 public:
40         ReframeRTConfig();
41         void boundaries();
42         int equivalent(ReframeRTConfig &src);
43         void copy_from(ReframeRTConfig &src);
44         void interpolate(ReframeRTConfig &prev,
45                 ReframeRTConfig &next,
46                 int64_t prev_frame,
47                 int64_t next_frame,
48                 int64_t current_frame);
49 // was scale
50         double num;
51         double denom;
52         int stretch;
53         int interp;
54         int optic_flow;
55 };
56
57
58 class ReframeRTNum : public BC_TumbleTextBox
59 {
60 public:
61         ReframeRTNum(ReframeRT *plugin,
62                 ReframeRTWindow *gui,
63                 int x,
64                 int y);
65         int handle_event();
66         ReframeRT *plugin;
67 };
68
69 class ReframeRTDenom : public BC_TumbleTextBox
70 {
71 public:
72         ReframeRTDenom(ReframeRT *plugin,
73                 ReframeRTWindow *gui,
74                 int x,
75                 int y);
76         int handle_event();
77         ReframeRT *plugin;
78 };
79
80 class ReframeRTStretch : public BC_Radial
81 {
82 public:
83         ReframeRTStretch(ReframeRT *plugin,
84                 ReframeRTWindow *gui,
85                 int x,
86                 int y);
87         int handle_event();
88         ReframeRT *plugin;
89         ReframeRTWindow *gui;
90 };
91
92 class ReframeRTDownsample : public BC_Radial
93 {
94 public:
95         ReframeRTDownsample(ReframeRT *plugin,
96                 ReframeRTWindow *gui,
97                 int x,
98                 int y);
99         int handle_event();
100         ReframeRT *plugin;
101         ReframeRTWindow *gui;
102 };
103
104 class ReframeRTInterpolate : public BC_CheckBox
105 {
106 public:
107         ReframeRTInterpolate(ReframeRT *plugin,
108                 ReframeRTWindow *gui,
109                 int x,
110                 int y);
111         int handle_event();
112         ReframeRT *plugin;
113         ReframeRTWindow *gui;
114 };
115
116 class ReframeRTWindow : public PluginClientWindow
117 {
118 public:
119         ReframeRTWindow(ReframeRT *plugin);
120         ~ReframeRTWindow();
121         void create_objects();
122         ReframeRT *plugin;
123         ReframeRTNum *num;
124         ReframeRTDenom *denom;
125         ReframeRTStretch *stretch;
126         ReframeRTDownsample *downsample;
127         ReframeRTInterpolate *interpolate;
128 };
129
130
131 class ReframeRT : public PluginVClient
132 {
133 public:
134         ReframeRT(PluginServer *server);
135         ~ReframeRT();
136
137         PLUGIN_CLASS_MEMBERS(ReframeRTConfig)
138
139         void save_data(KeyFrame *keyframe);
140         void read_data(KeyFrame *keyframe);
141         void update_gui();
142         int is_realtime();
143         int is_synthesis();
144         int process_buffer(VFrame *frame,
145                 int64_t start_position,
146                 double frame_rate);
147 };
148
149
150
151
152
153
154
155 REGISTER_PLUGIN(ReframeRT);
156
157
158
159 ReframeRTConfig::ReframeRTConfig()
160 {
161         num = 1.0;
162         denom = 1.0;
163         stretch = 0;
164         interp = 0;
165         optic_flow = 1;
166 }
167
168 int ReframeRTConfig::equivalent(ReframeRTConfig &src)
169 {
170         return fabs(num - src.num) < 0.0001 &&
171                 fabs(denom - src.denom) < 0.0001 &&
172                 stretch == src.stretch &&
173                 interp == src.interp;
174 }
175
176 void ReframeRTConfig::copy_from(ReframeRTConfig &src)
177 {
178         this->num = src.num;
179         this->denom = src.denom;
180         this->stretch = src.stretch;
181         this->interp = src.interp;
182 }
183
184 void ReframeRTConfig::interpolate(ReframeRTConfig &prev,
185         ReframeRTConfig &next,
186         int64_t prev_frame,
187         int64_t next_frame,
188         int64_t current_frame)
189 {
190         this->interp = prev.interp;
191         this->stretch = prev.stretch;
192         this->denom = prev.denom;
193
194         if (this->interp && prev_frame != next_frame)
195         {
196                 double next_weight = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
197                 double prev_weight = (double)(next_frame - current_frame) / (next_frame - prev_frame);
198                 double prev_slope = prev.num / prev.denom, next_slope = next.num / next.denom;
199                 // for interpolation, this is (for now) a simple linear slope to the next keyframe.
200                 double scale = prev_slope * prev_weight + next_slope * next_weight;
201                 this->num = this->denom * scale;
202         }
203         else
204         {
205                 this->num = prev.num;
206         }
207 }
208
209 void ReframeRTConfig::boundaries()
210 {
211         if(num < 0.0001) num = 0.0001;
212         if(denom < 0.0001) denom = 0.0001;
213 }
214
215
216
217
218
219
220
221
222 ReframeRTWindow::ReframeRTWindow(ReframeRT *plugin)
223  : PluginClientWindow(plugin, 230, 190, 230, 190, 0)
224 {
225         this->plugin = plugin;
226 }
227
228 ReframeRTWindow::~ReframeRTWindow()
229 {
230 }
231
232 void ReframeRTWindow::create_objects()
233 {
234         int x = plugin->get_theme()->window_border;
235         int y = plugin->get_theme()->window_border;
236         BC_Title *title;
237         add_subwindow(title = new BC_Title(x, y, _("Input frames:")));
238         y += title->get_h() + plugin->get_theme()->widget_border;
239         num = new ReframeRTNum(plugin,
240                 this,
241                 x,
242                 y);
243         num->create_objects();
244         num->set_increment(1.0);
245
246         y += num->get_h() + plugin->get_theme()->widget_border;
247         add_subwindow(title = new BC_Title(x, y, _("Output frames:")));
248         y += title->get_h() + plugin->get_theme()->widget_border;
249         denom = new ReframeRTDenom(plugin,
250                 this,
251                 x,
252                 y);
253         denom->create_objects();
254         denom->set_increment(1.0);
255
256
257         y += denom->get_h() + plugin->get_theme()->widget_border;
258         add_subwindow(stretch = new ReframeRTStretch(plugin, this, x, y));
259         y += 30;
260         add_subwindow(downsample = new ReframeRTDownsample(plugin, this, x, y));
261         y += 30;
262         add_subwindow(interpolate = new ReframeRTInterpolate(plugin, this, x, y));
263         y += 30;
264         add_subwindow(stretch = new ReframeRTStretch(plugin, this, x, y));
265         y += stretch->get_h() + plugin->get_theme()->widget_border;
266         add_subwindow(downsample = new ReframeRTDownsample(plugin, this, x, y));
267         show_window();
268 }
269
270
271
272
273
274
275
276
277 ReframeRTNum::ReframeRTNum(ReframeRT *plugin,
278         ReframeRTWindow *gui,
279         int x,
280         int y)
281  : BC_TumbleTextBox(gui,
282         (float)plugin->config.num,
283         (float)0.0001,
284         (float)1000,
285         x,
286         y,
287         gui->get_w() - plugin->get_theme()->window_border * 3)
288 {
289         this->plugin = plugin;
290 }
291
292 int ReframeRTNum::handle_event()
293 {
294         plugin->config.num = atof(get_text());
295         plugin->config.boundaries();
296         plugin->send_configure_change();
297         return 1;
298 }
299
300
301 ReframeRTDenom::ReframeRTDenom(ReframeRT *plugin,
302         ReframeRTWindow *gui,
303         int x,
304         int y)
305  : BC_TumbleTextBox(gui,
306         (float)plugin->config.denom,
307         (float)-1000,
308         (float)1000,
309         x,
310         y,
311         gui->get_w() - plugin->get_theme()->window_border * 3)
312 {
313         this->plugin = plugin;
314 }
315
316 int ReframeRTDenom::handle_event()
317 {
318         plugin->config.denom = atof(get_text());
319         plugin->config.boundaries();
320         plugin->send_configure_change();
321         return 1;
322 }
323
324
325 ReframeRTStretch::ReframeRTStretch(ReframeRT *plugin,
326         ReframeRTWindow *gui,
327         int x,
328         int y)
329  : BC_Radial(x, y, plugin->config.stretch, _("Stretch"))
330 {
331         this->plugin = plugin;
332         this->gui = gui;
333 }
334
335 int ReframeRTStretch::handle_event()
336 {
337         plugin->config.stretch = get_value();
338         gui->downsample->update(!get_value());
339         plugin->send_configure_change();
340         return 1;
341 }
342
343
344 ReframeRTDownsample::ReframeRTDownsample(ReframeRT *plugin,
345         ReframeRTWindow *gui,
346         int x,
347         int y)
348  : BC_Radial(x, y, !plugin->config.stretch, _("Downsample"))
349 {
350         this->plugin = plugin;
351         this->gui = gui;
352 }
353
354 int ReframeRTDownsample::handle_event()
355 {
356         plugin->config.stretch = !get_value();
357         gui->stretch->update(!get_value());
358         plugin->send_configure_change();
359         return 1;
360 }
361
362 ReframeRTInterpolate::ReframeRTInterpolate(ReframeRT *plugin,
363         ReframeRTWindow *gui,
364         int x,
365         int y)
366  : BC_CheckBox(x, y, 0, _("Interpolate"))
367 {
368         this->plugin = plugin;
369         this->gui = gui;
370 }
371
372 int ReframeRTInterpolate::handle_event()
373 {
374         plugin->config.interp = get_value();
375         gui->interpolate->update(get_value());
376         plugin->send_configure_change();
377         return 1;
378 }
379
380 ReframeRT::ReframeRT(PluginServer *server)
381  : PluginVClient(server)
382 {
383 }
384
385 ReframeRT::~ReframeRT()
386 {
387
388 }
389
390 const char* ReframeRT::plugin_title() { return N_("ReframeRT"); }
391 int ReframeRT::is_realtime() { return 1; }
392 int ReframeRT::is_synthesis() { return 1; }
393
394
395 NEW_WINDOW_MACRO(ReframeRT, ReframeRTWindow)
396 LOAD_CONFIGURATION_MACRO(ReframeRT, ReframeRTConfig)
397
398 int ReframeRT::process_buffer(VFrame *frame,
399                 int64_t start_position,
400                 double frame_rate)
401 {
402         int64_t input_frame = get_source_start();
403         ReframeRTConfig prev_config, next_config;
404         KeyFrame *tmp_keyframe, *next_keyframe = get_prev_keyframe(get_source_start());
405         int64_t tmp_position, next_position;
406         int64_t segment_len;
407         double input_rate = frame_rate;
408         int is_current_keyframe;
409
410 // if there are no keyframes, the default keyframe is used, and its position is always 0;
411 // if there are keyframes, the first keyframe can be after the effect start (and it controls settings before it)
412 // so let's calculate using a fake keyframe with the same settings but position == effect start
413         KeyFrame *fake_keyframe = new KeyFrame();
414         fake_keyframe->copy_from(next_keyframe);
415         fake_keyframe->position = local_to_edl(get_source_start());
416         next_keyframe = fake_keyframe;
417
418         // calculate input_frame accounting for all previous keyframes
419         do
420         {
421                 tmp_keyframe = next_keyframe;
422                 next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0);
423
424                 tmp_position = edl_to_local(tmp_keyframe->position);
425                 next_position = edl_to_local(next_keyframe->position);
426
427                 is_current_keyframe =
428                         next_position > start_position // the next keyframe is after the current position
429                         || next_keyframe->position == tmp_keyframe->position // there are no more keyframes
430                         || !next_keyframe->position; // there are no keyframes at all
431
432                 if (is_current_keyframe)
433                         segment_len = start_position - tmp_position;
434                 else
435                         segment_len = next_position - tmp_position;
436
437                 read_data(next_keyframe);
438                 next_config.copy_from(config);
439                 read_data(tmp_keyframe);
440                 prev_config.copy_from(config);
441                 config.interpolate(prev_config, next_config, tmp_position, next_position, tmp_position + segment_len);
442
443                 // the area under the curve is the number of frames to advance
444                 // as long as interpolate() uses a linear slope we can use geometry to determine this
445                 // if interpolate() changes to use a curve then this needs use (possibly) the definite integral
446                 double prev_scale = prev_config.num / prev_config.denom;
447                 double config_scale = config.num / config.denom;
448                 input_frame += (int64_t)(segment_len * ((prev_scale + config_scale) / 2));
449         } while (!is_current_keyframe);
450
451 // Change rate
452         if (!config.stretch)
453         {
454                 input_rate *= config.num / config.denom;
455
456         }
457
458 // printf("ReframeRT::process_buffer %d %lld %f %lld %f\n",
459 // __LINE__,
460 // start_position,
461 // frame_rate,
462 // input_frame,
463 // input_rate);
464
465         read_frame(frame,
466                 0,
467                 input_frame,
468                 input_rate,
469                 0);
470
471         delete fake_keyframe;
472
473         return 0;
474 }
475
476
477
478 void ReframeRT::save_data(KeyFrame *keyframe)
479 {
480         FileXML output;
481
482 // cause data to be stored directly in text
483         output.set_shared_output(keyframe->xbuf);
484         output.tag.set_title("REFRAMERT");
485 // for backwards compatability, we call num scale
486         output.tag.set_property("SCALE", config.num);
487         output.tag.set_property("DENOM", config.denom);
488         output.tag.set_property("STRETCH", config.stretch);
489         output.tag.set_property("INTERPOLATE", config.interp);
490         output.append_tag();
491         output.tag.set_title("/REFRAMERT");
492         output.append_tag();
493         output.append_newline();
494         output.terminate_string();
495 }
496
497 void ReframeRT::read_data(KeyFrame *keyframe)
498 {
499         FileXML input;
500
501         input.set_shared_input(keyframe->xbuf);
502
503         while(!input.read_tag())
504         {
505                 if(input.tag.title_is("REFRAMERT"))
506                 {
507 // for backwards compatability, we call num scale
508                         config.num = input.tag.get_property("SCALE", config.num);
509                         config.denom = input.tag.get_property("DENOM", config.denom);
510                         config.stretch = input.tag.get_property("STRETCH", config.stretch);
511                         config.interp = input.tag.get_property("INTERPOLATE", config.interp);
512                 }
513         }
514 }
515
516 void ReframeRT::update_gui()
517 {
518         if(thread)
519         {
520                 int changed = load_configuration();
521
522                 if(changed)
523                 {
524                         ReframeRTWindow* window = (ReframeRTWindow*)thread->window;
525                         window->lock_window("ReframeRT::update_gui");
526                         window->num->update((float)config.num);
527                         window->denom->update((float)config.denom);
528                         window->stretch->update(config.stretch);
529                         window->downsample->update(!config.stretch);
530                         window->interpolate->update(config.interp);
531                         window->unlock_window();
532                 }
533         }
534 }
535
536
537
538
539