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
23 * 2020. Derivative by ReframeRT plugin for a more easy use.
24 * It uses percentage value of the speed referred to originl speed (=100%).
25 * Some old ReframeRT parameters (Stretch and denom) have not been deleted,
26 * for future development, if any.
27 * Stretch and denom variables are set to a constant value:
28 * Stretch= 1; denom= 100.00.
29 * Speed_MIN= 1.00%; Speed_MAX= 1000.00%
32 #include "bcdisplayinfo.h"
39 #include "pluginvclient.h"
41 #include "transportque.h"
48 REGISTER_PLUGIN(SpeedPc);
52 SpeedPcConfig::SpeedPcConfig()
54 reset(RESET_DEFAULT_SETTINGS);
57 void SpeedPcConfig::reset(int clear)
66 case RESET_DEFAULT_SETTINGS :
77 int SpeedPcConfig::equivalent(SpeedPcConfig &src)
79 return fabs(num - src.num) < 0.0001 &&
80 fabs(denom - src.denom) < 0.0001 &&
81 stretch == src.stretch &&
85 void SpeedPcConfig::copy_from(SpeedPcConfig &src)
88 this->denom = src.denom;
89 this->stretch = src.stretch;
90 this->interp = src.interp;
93 void SpeedPcConfig::interpolate(SpeedPcConfig &prev,
97 int64_t current_frame)
99 this->interp = prev.interp;
100 this->stretch = prev.stretch;
101 this->denom = prev.denom;
103 if (this->interp && prev_frame != next_frame)
105 double next_weight = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
106 double prev_weight = (double)(next_frame - current_frame) / (next_frame - prev_frame);
107 double prev_slope = prev.num / prev.denom, next_slope = next.num / next.denom;
108 // for interpolation, this is (for now) a simple linear slope to the next keyframe.
109 double scale = prev_slope * prev_weight + next_slope * next_weight;
110 this->num = this->denom * scale;
114 this->num = prev.num;
118 void SpeedPcConfig::boundaries()
120 if(num < 0.0001) num = 0.0001;
121 if(denom < 0.0001) denom = 0.0001;
131 SpeedPcWindow::SpeedPcWindow(SpeedPc *plugin)
132 : PluginClientWindow(plugin, xS(420), yS(150), xS(420), yS(150), 0) // Note: with "Stretch" and "Downsample" gui yS was yS(210)
134 this->plugin = plugin;
137 SpeedPcWindow::~SpeedPcWindow()
141 void SpeedPcWindow::create_objects()
143 int xs10 = xS(10), xs64 = xS(64), xs200 = xS(200);
144 int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
145 int x2 = xS(80), x3 = xS(180);
146 int x = xs10, y = ys10;
147 int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
151 add_subwindow(new BC_Title(x, y, _("Preset:")));
153 add_subwindow(toggle25pc = new SpeedPcToggle(plugin, this,
154 plugin->config.num == 25, x, y, 25, "25%"));
156 add_subwindow(toggle50pc = new SpeedPcToggle(plugin, this,
157 plugin->config.num == 50, x, y, 50, "50%"));
159 add_subwindow(toggle100pc = new SpeedPcToggle(plugin, this,
160 plugin->config.num == 100, x, y, 100, "100%"));
162 add_subwindow(toggle200pc = new SpeedPcToggle(plugin, this,
163 plugin->config.num == 200, x, y, 200, "200%"));
165 add_subwindow(toggle400pc = new SpeedPcToggle(plugin, this,
166 plugin->config.num == 400, x, y, 400, "400%"));
169 add_tool(new BC_Title(x, y, _("Speed:")));
170 add_tool(new BC_Title((x2-x), y, _("%")));
171 speed_pc_text = new SpeedPcText(plugin, this, (x + x2), y);
172 speed_pc_text->create_objects();
173 speed_pc_slider = new SpeedPcSlider(plugin, this, x3, y, xs200);
174 add_subwindow(speed_pc_slider);
175 clr_x = x3 + speed_pc_slider->get_w() + x;
176 add_subwindow(speed_pc_clr = new SpeedPcClr(plugin, this,
177 clr_x, y, RESET_SPEED));
182 add_subwindow(stretch = new SpeedPcStretch(plugin, this, x, y));
184 add_subwindow(downsample = new SpeedPcDownsample(plugin, this, x, y));
188 add_subwindow(interpolate = new SpeedPcInterpolate(plugin, this, x, y));
192 add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
194 add_subwindow(reset = new SpeedPcReset(plugin, this, x, y));
200 void SpeedPcWindow::update(int clear)
204 speed_pc_text->update((float)plugin->config.num);
205 speed_pc_slider->update((float)plugin->config.num);
209 case RESET_DEFAULT_SETTINGS :
211 speed_pc_text->update((float)plugin->config.num);
212 speed_pc_slider->update((float)plugin->config.num);
215 // OLD ReframeRT code
217 stretch->update(plugin->config.stretch);
218 downsample->update(!plugin->config.stretch);
220 interpolate->update(plugin->config.interp);
226 int SpeedPcWindow::update_toggles()
228 toggle25pc->update(EQUIV(plugin->config.num, 25));
229 toggle50pc->update(EQUIV(plugin->config.num, 50));
230 toggle100pc->update(EQUIV(plugin->config.num, 100));
231 toggle200pc->update(EQUIV(plugin->config.num, 200));
232 toggle400pc->update(EQUIV(plugin->config.num, 400));
237 SpeedPcToggle::SpeedPcToggle(SpeedPc *plugin, SpeedPcWindow *gui,
243 : BC_Radial(x, y, init_value, string)
246 this->plugin = plugin;
250 int SpeedPcToggle::handle_event()
252 plugin->config.num = (float)value;
253 gui->update(RESET_SPEED);
254 plugin->send_configure_change();
260 /* *********************************** */
261 /* **** SPEED ******************** */
262 SpeedPcText::SpeedPcText(SpeedPc *plugin, SpeedPcWindow *gui,
265 : BC_TumbleTextBox(gui, (float)plugin->config.num,
266 (float)1.00, (float)1000.00, x, y, xS(60), 2)
268 this->plugin = plugin;
272 SpeedPcText::~SpeedPcText()
276 int SpeedPcText::handle_event()
278 plugin->config.num = atof(get_text());
279 plugin->config.denom = 100.00;
280 plugin->config.stretch = 1;
281 plugin->config.boundaries();
282 gui->update(RESET_SPEED);
283 plugin->send_configure_change();
287 SpeedPcSlider::SpeedPcSlider(SpeedPc *plugin, SpeedPcWindow *gui,
289 : BC_FSlider(x, y, 0, w, w, 1.00, 1000.00, plugin->config.num)
291 this->plugin = plugin;
293 enable_show_value(0); // Hide caption
297 SpeedPcSlider::~SpeedPcSlider()
301 int SpeedPcSlider::handle_event()
303 plugin->config.num = get_value();
304 plugin->config.denom = 100.00;
305 plugin->config.stretch = 1;
306 gui->update(RESET_SPEED);
307 plugin->send_configure_change();
310 /* *********************************** */
313 SpeedPcStretch::SpeedPcStretch(SpeedPc *plugin,
317 : BC_Radial(x, y, plugin->config.stretch, _("Stretch"))
319 this->plugin = plugin;
323 int SpeedPcStretch::handle_event()
325 plugin->config.stretch = get_value();
326 gui->downsample->update(!get_value());
327 plugin->send_configure_change();
332 SpeedPcDownsample::SpeedPcDownsample(SpeedPc *plugin, SpeedPcWindow *gui,
335 : BC_Radial(x, y, !plugin->config.stretch, _("Downsample"))
337 this->plugin = plugin;
341 int SpeedPcDownsample::handle_event()
343 plugin->config.stretch = !get_value();
344 gui->stretch->update(!get_value());
345 plugin->send_configure_change();
349 SpeedPcInterpolate::SpeedPcInterpolate(SpeedPc *plugin, SpeedPcWindow *gui,
352 : BC_CheckBox(x, y, 0, _("Interpolate"))
354 this->plugin = plugin;
356 set_tooltip(_("Interpolate between keyframes"));
359 int SpeedPcInterpolate::handle_event()
361 plugin->config.interp = get_value();
362 gui->interpolate->update(get_value());
363 plugin->send_configure_change();
368 SpeedPcClr::SpeedPcClr(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y, int clear)
369 : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
371 this->plugin = plugin;
375 SpeedPcClr::~SpeedPcClr()
378 int SpeedPcClr::handle_event()
380 plugin->config.reset(clear);
382 plugin->send_configure_change();
387 SpeedPcReset::SpeedPcReset(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y)
388 : BC_GenericButton(x, y, _("Reset"))
390 this->plugin = plugin;
393 SpeedPcReset::~SpeedPcReset()
396 int SpeedPcReset::handle_event()
398 plugin->config.reset(RESET_ALL);
399 gui->update(RESET_ALL);
400 plugin->send_configure_change();
406 SpeedPc::SpeedPc(PluginServer *server)
407 : PluginVClient(server)
416 const char* SpeedPc::plugin_title() { return N_("Speed PerCent"); }
417 int SpeedPc::is_realtime() { return 1; }
418 int SpeedPc::is_synthesis() { return 1; }
421 NEW_WINDOW_MACRO(SpeedPc, SpeedPcWindow)
422 LOAD_CONFIGURATION_MACRO(SpeedPc, SpeedPcConfig)
424 int SpeedPc::process_buffer(VFrame *frame,
425 int64_t start_position,
428 int64_t input_frame = get_source_start();
429 SpeedPcConfig prev_config, next_config;
430 KeyFrame *tmp_keyframe, *next_keyframe = get_prev_keyframe(get_source_start());
431 int64_t tmp_position, next_position;
433 double input_rate = frame_rate;
434 int is_current_keyframe;
436 // if there are no keyframes, the default keyframe is used, and its position is always 0;
437 // if there are keyframes, the first keyframe can be after the effect start (and it controls settings before it)
438 // so let's calculate using a fake keyframe with the same settings but position == effect start
439 KeyFrame *fake_keyframe = new KeyFrame();
440 fake_keyframe->copy_from(next_keyframe);
441 fake_keyframe->position = local_to_edl(get_source_start());
442 next_keyframe = fake_keyframe;
444 // calculate input_frame accounting for all previous keyframes
447 tmp_keyframe = next_keyframe;
448 next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0);
450 tmp_position = edl_to_local(tmp_keyframe->position);
451 next_position = edl_to_local(next_keyframe->position);
453 is_current_keyframe =
454 next_position > start_position // the next keyframe is after the current position
455 || next_keyframe->position == tmp_keyframe->position // there are no more keyframes
456 || !next_keyframe->position; // there are no keyframes at all
458 if (is_current_keyframe)
459 segment_len = start_position - tmp_position;
461 segment_len = next_position - tmp_position;
463 read_data(next_keyframe);
464 next_config.copy_from(config);
465 read_data(tmp_keyframe);
466 prev_config.copy_from(config);
467 config.interpolate(prev_config, next_config, tmp_position, next_position, tmp_position + segment_len);
469 // the area under the curve is the number of frames to advance
470 // as long as interpolate() uses a linear slope we can use geometry to determine this
471 // if interpolate() changes to use a curve then this needs use (possibly) the definite integral
472 double prev_scale = prev_config.num / 100.00;
473 double config_scale = config.num / 100.00;
474 input_frame += (int64_t)(segment_len * ((prev_scale + config_scale) / 2));
475 } while (!is_current_keyframe);
480 input_rate *= config.num / 100.00;
484 // printf("SpeedPc::process_buffer %d %lld %f %lld %f\n",
497 delete fake_keyframe;
504 void SpeedPc::save_data(KeyFrame *keyframe)
508 // cause data to be stored directly in text
509 output.set_shared_output(keyframe->xbuf);
510 output.tag.set_title("SPEED_PC");
511 output.tag.set_property("SPEED", config.num);
512 output.tag.set_property("DENOM", config.denom);
513 output.tag.set_property("STRETCH", config.stretch);
514 output.tag.set_property("INTERPOLATE", config.interp);
516 output.tag.set_title("/SPEED_PC");
518 output.append_newline();
519 output.terminate_string();
522 void SpeedPc::read_data(KeyFrame *keyframe)
526 input.set_shared_input(keyframe->xbuf);
528 while(!input.read_tag())
530 if(input.tag.title_is("SPEED_PC"))
532 config.num = input.tag.get_property("SPEED", config.num);
533 config.denom = input.tag.get_property("DENOM", config.denom);
534 config.stretch = input.tag.get_property("STRETCH", config.stretch);
535 config.interp = input.tag.get_property("INTERPOLATE", config.interp);
540 void SpeedPc::update_gui()
544 int changed = load_configuration();
548 SpeedPcWindow* window = (SpeedPcWindow*)thread->window;
549 window->lock_window("SpeedPc::update_gui");
550 window->update(RESET_ALL);
551 // OLD ReframeRT code
553 window->stretch->update(config.stretch);
554 window->downsample->update(!config.stretch);
556 window->unlock_window();