4 * Copyright (C) 2008-2016 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcdisplayinfo.h"
28 #include "pluginvclient.h"
30 #include "transportque.h"
35 class ReframeRTWindow;
42 int equivalent(ReframeRTConfig &src);
43 void copy_from(ReframeRTConfig &src);
44 void interpolate(ReframeRTConfig &prev,
45 ReframeRTConfig &next,
48 int64_t current_frame);
58 class ReframeRTNum : public BC_TumbleTextBox
61 ReframeRTNum(ReframeRT *plugin,
69 class ReframeRTDenom : public BC_TumbleTextBox
72 ReframeRTDenom(ReframeRT *plugin,
80 class ReframeRTStretch : public BC_Radial
83 ReframeRTStretch(ReframeRT *plugin,
92 class ReframeRTDownsample : public BC_Radial
95 ReframeRTDownsample(ReframeRT *plugin,
101 ReframeRTWindow *gui;
104 class ReframeRTInterpolate : public BC_CheckBox
107 ReframeRTInterpolate(ReframeRT *plugin,
108 ReframeRTWindow *gui,
113 ReframeRTWindow *gui;
116 class ReframeRTWindow : public PluginClientWindow
119 ReframeRTWindow(ReframeRT *plugin);
121 void create_objects();
124 ReframeRTDenom *denom;
125 ReframeRTStretch *stretch;
126 ReframeRTDownsample *downsample;
127 ReframeRTInterpolate *interpolate;
131 class ReframeRT : public PluginVClient
134 ReframeRT(PluginServer *server);
137 PLUGIN_CLASS_MEMBERS(ReframeRTConfig)
139 void save_data(KeyFrame *keyframe);
140 void read_data(KeyFrame *keyframe);
144 int process_buffer(VFrame *frame,
145 int64_t start_position,
155 REGISTER_PLUGIN(ReframeRT);
159 ReframeRTConfig::ReframeRTConfig()
168 int ReframeRTConfig::equivalent(ReframeRTConfig &src)
170 return fabs(num - src.num) < 0.0001 &&
171 fabs(denom - src.denom) < 0.0001 &&
172 stretch == src.stretch &&
173 interp == src.interp;
176 void ReframeRTConfig::copy_from(ReframeRTConfig &src)
179 this->denom = src.denom;
180 this->stretch = src.stretch;
181 this->interp = src.interp;
184 void ReframeRTConfig::interpolate(ReframeRTConfig &prev,
185 ReframeRTConfig &next,
188 int64_t current_frame)
190 this->interp = prev.interp;
191 this->stretch = prev.stretch;
192 this->denom = prev.denom;
194 if (this->interp && prev_frame != next_frame)
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;
205 this->num = prev.num;
209 void ReframeRTConfig::boundaries()
211 if(num < 0.0001) num = 0.0001;
212 if(denom < 0.0001) denom = 0.0001;
222 ReframeRTWindow::ReframeRTWindow(ReframeRT *plugin)
223 : PluginClientWindow(plugin, 230, 190, 230, 190, 0)
225 this->plugin = plugin;
228 ReframeRTWindow::~ReframeRTWindow()
232 void ReframeRTWindow::create_objects()
234 int x = plugin->get_theme()->window_border;
235 int y = plugin->get_theme()->window_border;
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,
243 num->create_objects();
244 num->set_increment(1.0);
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,
253 denom->create_objects();
254 denom->set_increment(1.0);
257 y += denom->get_h() + plugin->get_theme()->widget_border;
258 add_subwindow(stretch = new ReframeRTStretch(plugin, this, x, y));
260 add_subwindow(downsample = new ReframeRTDownsample(plugin, this, x, y));
262 add_subwindow(interpolate = new ReframeRTInterpolate(plugin, this, x, y));
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));
277 ReframeRTNum::ReframeRTNum(ReframeRT *plugin,
278 ReframeRTWindow *gui,
281 : BC_TumbleTextBox(gui,
282 (float)plugin->config.num,
287 gui->get_w() - plugin->get_theme()->window_border * 3)
289 this->plugin = plugin;
292 int ReframeRTNum::handle_event()
294 plugin->config.num = atof(get_text());
295 plugin->config.boundaries();
296 plugin->send_configure_change();
301 ReframeRTDenom::ReframeRTDenom(ReframeRT *plugin,
302 ReframeRTWindow *gui,
305 : BC_TumbleTextBox(gui,
306 (float)plugin->config.denom,
311 gui->get_w() - plugin->get_theme()->window_border * 3)
313 this->plugin = plugin;
316 int ReframeRTDenom::handle_event()
318 plugin->config.denom = atof(get_text());
319 plugin->config.boundaries();
320 plugin->send_configure_change();
325 ReframeRTStretch::ReframeRTStretch(ReframeRT *plugin,
326 ReframeRTWindow *gui,
329 : BC_Radial(x, y, plugin->config.stretch, _("Stretch"))
331 this->plugin = plugin;
335 int ReframeRTStretch::handle_event()
337 plugin->config.stretch = get_value();
338 gui->downsample->update(!get_value());
339 plugin->send_configure_change();
344 ReframeRTDownsample::ReframeRTDownsample(ReframeRT *plugin,
345 ReframeRTWindow *gui,
348 : BC_Radial(x, y, !plugin->config.stretch, _("Downsample"))
350 this->plugin = plugin;
354 int ReframeRTDownsample::handle_event()
356 plugin->config.stretch = !get_value();
357 gui->stretch->update(!get_value());
358 plugin->send_configure_change();
362 ReframeRTInterpolate::ReframeRTInterpolate(ReframeRT *plugin,
363 ReframeRTWindow *gui,
366 : BC_CheckBox(x, y, 0, _("Interpolate"))
368 this->plugin = plugin;
372 int ReframeRTInterpolate::handle_event()
374 plugin->config.interp = get_value();
375 gui->interpolate->update(get_value());
376 plugin->send_configure_change();
380 ReframeRT::ReframeRT(PluginServer *server)
381 : PluginVClient(server)
385 ReframeRT::~ReframeRT()
390 const char* ReframeRT::plugin_title() { return N_("ReframeRT"); }
391 int ReframeRT::is_realtime() { return 1; }
392 int ReframeRT::is_synthesis() { return 1; }
395 NEW_WINDOW_MACRO(ReframeRT, ReframeRTWindow)
396 LOAD_CONFIGURATION_MACRO(ReframeRT, ReframeRTConfig)
398 int ReframeRT::process_buffer(VFrame *frame,
399 int64_t start_position,
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;
407 double input_rate = frame_rate;
408 int is_current_keyframe;
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;
418 // calculate input_frame accounting for all previous keyframes
421 tmp_keyframe = next_keyframe;
422 next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0);
424 tmp_position = edl_to_local(tmp_keyframe->position);
425 next_position = edl_to_local(next_keyframe->position);
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
432 if (is_current_keyframe)
433 segment_len = start_position - tmp_position;
435 segment_len = next_position - tmp_position;
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);
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);
454 input_rate *= config.num / config.denom;
458 // printf("ReframeRT::process_buffer %d %lld %f %lld %f\n",
471 delete fake_keyframe;
478 void ReframeRT::save_data(KeyFrame *keyframe)
482 // cause data to be stored directly in text
483 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
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);
491 output.tag.set_title("/REFRAMERT");
493 output.append_newline();
494 output.terminate_string();
497 void ReframeRT::read_data(KeyFrame *keyframe)
501 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
503 while(!input.read_tag())
505 if(input.tag.title_is("REFRAMERT"))
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);
516 void ReframeRT::update_gui()
520 int changed = load_configuration();
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();