Contributed Speed PerCent video plugin
authorGood Guy <[email protected]>
Sun, 22 Aug 2021 19:00:31 +0000 (13:00 -0600)
committerGood Guy <[email protected]>
Sun, 22 Aug 2021 19:00:31 +0000 (13:00 -0600)
cinelerra-5.1/expanders.txt
cinelerra-5.1/picon/cinfinity/speed_pc.png [new file with mode: 0644]
cinelerra-5.1/picon/cinfinity2/speed_pc.png [new file with mode: 0644]
cinelerra-5.1/plugin_defs
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/speed_pc/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/speed_pc/speed_pc.C [new file with mode: 0644]
cinelerra-5.1/plugins/speed_pc/speed_pc.h [new file with mode: 0644]

index 9bda7186a340d300610d89a1292f2d2838efb7ec..af9157445b172eef76bdf71c39152fd9bb0d8324 100644 (file)
@@ -161,6 +161,7 @@ Video Effects
                Interpolate Video
                ReframeRT
                Reverse video
+               Speed PerCent
                Time Average
                TimeFront
                Timelapse Helper
diff --git a/cinelerra-5.1/picon/cinfinity/speed_pc.png b/cinelerra-5.1/picon/cinfinity/speed_pc.png
new file mode 100644 (file)
index 0000000..a98ec84
Binary files /dev/null and b/cinelerra-5.1/picon/cinfinity/speed_pc.png differ
diff --git a/cinelerra-5.1/picon/cinfinity2/speed_pc.png b/cinelerra-5.1/picon/cinfinity2/speed_pc.png
new file mode 100644 (file)
index 0000000..22d6d1b
Binary files /dev/null and b/cinelerra-5.1/picon/cinfinity2/speed_pc.png differ
index 137c49f468e17711b19dd7e79e9cb59f9831a900..9f7d5396de3cbbd2a30a19b377127a07de9c8bd9 100644 (file)
@@ -106,6 +106,7 @@ video := \
        sharpen \
        shiftinterlace \
        slide \
+       speed_pc \
        spherecam \
        svg \
        swapchannels \
index ff00d7851ba44eecc7bff4038546aa059c99b8da..c4a5fd43fce113d56669af065d02442f9107c081 100644 (file)
@@ -134,6 +134,7 @@ DIRS = $(OPENCV_OBJS) \
        shiftinterlace \
        slide \
        spectrogram \
+       speed_pc \
        spherecam \
        svg \
        swapchannels \
diff --git a/cinelerra-5.1/plugins/speed_pc/Makefile b/cinelerra-5.1/plugins/speed_pc/Makefile
new file mode 100644 (file)
index 0000000..d4c5eb0
--- /dev/null
@@ -0,0 +1,13 @@
+include ../../plugin_defs
+
+OBJS = $(OBJDIR)/speed_pc.o
+
+PLUGIN = speed_pc
+
+include ../../plugin_config
+
+
+$(OBJDIR)/speed_pc.o: speed_pc.C
+
+
+
diff --git a/cinelerra-5.1/plugins/speed_pc/speed_pc.C b/cinelerra-5.1/plugins/speed_pc/speed_pc.C
new file mode 100644 (file)
index 0000000..de8d1e1
--- /dev/null
@@ -0,0 +1,564 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2016 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * 2020. Derivative by ReframeRT plugin for a more easy use.
+ * It uses percentage value of the speed referred to originl speed (=100%).
+ * Some old ReframeRT parameters (Stretch and denom) have not been deleted,
+ * for future development, if any.
+ * Stretch and denom variables are set to a constant value:
+ * Stretch= 1; denom= 100.00.
+ * Speed_MIN= 1.00%; Speed_MAX= 1000.00% 
+ */
+
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "speed_pc.h"
+#include "guicast.h"
+#include "language.h"
+#include "pluginvclient.h"
+#include "theme.h"
+#include "transportque.h"
+
+#include <string.h>
+
+
+
+
+REGISTER_PLUGIN(SpeedPc);
+
+
+
+SpeedPcConfig::SpeedPcConfig()
+{
+       reset(RESET_DEFAULT_SETTINGS);
+}
+
+void SpeedPcConfig::reset(int clear)
+{
+       switch(clear) {
+               case RESET_SPEED :
+                       num = 100.00;
+                       denom = 100.0;
+                       stretch = 1;
+                       break;
+               case RESET_ALL :
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       num = 100.00;
+                       denom = 100.0;
+                       stretch = 1;
+                       interp = 0;
+                       optic_flow = 1;
+                       break;
+       }
+}
+
+int SpeedPcConfig::equivalent(SpeedPcConfig &src)
+{
+       return fabs(num - src.num) < 0.0001 &&
+               fabs(denom - src.denom) < 0.0001 &&
+               stretch == src.stretch &&
+               interp == src.interp;
+}
+
+void SpeedPcConfig::copy_from(SpeedPcConfig &src)
+{
+       this->num = src.num;
+       this->denom = src.denom;
+       this->stretch = src.stretch;
+       this->interp = src.interp;
+}
+
+void SpeedPcConfig::interpolate(SpeedPcConfig &prev,
+       SpeedPcConfig &next,
+       int64_t prev_frame,
+       int64_t next_frame,
+       int64_t current_frame)
+{
+       this->interp = prev.interp;
+       this->stretch = prev.stretch;
+       this->denom = prev.denom;
+
+       if (this->interp && prev_frame != next_frame)
+       {
+               double next_weight = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+               double prev_weight = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+               double prev_slope = prev.num / prev.denom, next_slope = next.num / next.denom;
+               // for interpolation, this is (for now) a simple linear slope to the next keyframe.
+               double scale = prev_slope * prev_weight + next_slope * next_weight;
+               this->num = this->denom * scale;
+       }
+       else
+       {
+               this->num = prev.num;
+       }
+}
+
+void SpeedPcConfig::boundaries()
+{
+       if(num < 0.0001) num = 0.0001;
+       if(denom < 0.0001) denom = 0.0001;
+}
+
+
+
+
+
+
+
+
+SpeedPcWindow::SpeedPcWindow(SpeedPc *plugin)
+ : PluginClientWindow(plugin, xS(420), yS(150), xS(420), yS(150), 0)  // Note: with "Stretch" and "Downsample" gui yS was yS(210)
+{
+       this->plugin = plugin;
+}
+
+SpeedPcWindow::~SpeedPcWindow()
+{
+}
+
+void SpeedPcWindow::create_objects()
+{
+       int xs10 = xS(10), xs64 = xS(64), xs200 = xS(200);
+       int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
+       int x2 = xS(80), x3 = xS(180);
+       int x = xs10, y = ys10;
+       int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
+
+       BC_Bar *bar;
+
+       add_subwindow(new BC_Title(x, y, _("Preset:")));
+       x = x + x2; 
+       add_subwindow(toggle25pc = new SpeedPcToggle(plugin, this,
+               plugin->config.num == 25, x, y, 25, "25%"));
+       x += xs64;
+       add_subwindow(toggle50pc = new SpeedPcToggle(plugin, this,
+               plugin->config.num == 50, x, y, 50, "50%"));
+       x += xs64;
+       add_subwindow(toggle100pc = new SpeedPcToggle(plugin, this,
+               plugin->config.num == 100, x, y, 100, "100%"));
+       x += xs64;
+       add_subwindow(toggle200pc = new SpeedPcToggle(plugin, this,
+               plugin->config.num == 200, x, y, 200, "200%"));
+       x += xs64;
+       add_subwindow(toggle400pc = new SpeedPcToggle(plugin, this,
+               plugin->config.num == 400, x, y, 400, "400%"));
+       x = xs10;  y += ys30;
+
+       add_tool(new BC_Title(x, y, _("Speed:")));
+       add_tool(new BC_Title((x2-x), y, _("%")));
+       speed_pc_text = new SpeedPcText(plugin, this, (x + x2), y);
+       speed_pc_text->create_objects();
+       speed_pc_slider = new SpeedPcSlider(plugin, this, x3, y, xs200);
+       add_subwindow(speed_pc_slider);
+       clr_x = x3 + speed_pc_slider->get_w() + x;
+       add_subwindow(speed_pc_clr = new SpeedPcClr(plugin, this,
+               clr_x, y, RESET_SPEED));
+       y += ys30;
+
+// REM 2020-06-23
+/*
+       add_subwindow(stretch = new SpeedPcStretch(plugin, this, x, y));
+       y += yS(30);
+       add_subwindow(downsample = new SpeedPcDownsample(plugin, this, x, y));
+       y += yS(30);
+*/
+
+       add_subwindow(interpolate = new SpeedPcInterpolate(plugin, this, x, y));
+       y += ys40;
+
+// Reset section
+       add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
+       y += ys10;
+       add_subwindow(reset = new SpeedPcReset(plugin, this, x, y));
+
+       update(RESET_ALL);
+       show_window();
+}
+
+void SpeedPcWindow::update(int clear)
+{
+       switch(clear) {
+               case RESET_SPEED :
+                       speed_pc_text->update((float)plugin->config.num);
+                       speed_pc_slider->update((float)plugin->config.num);
+                       update_toggles();
+                       break;
+               case RESET_ALL :
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       speed_pc_text->update((float)plugin->config.num);
+                       speed_pc_slider->update((float)plugin->config.num);
+                       update_toggles();
+
+// OLD ReframeRT code
+/*
+                       stretch->update(plugin->config.stretch);
+                       downsample->update(!plugin->config.stretch);
+*/
+                       interpolate->update(plugin->config.interp);
+                       break;
+       }
+}
+
+
+int SpeedPcWindow::update_toggles()
+{
+       toggle25pc->update(EQUIV(plugin->config.num, 25));
+       toggle50pc->update(EQUIV(plugin->config.num, 50));
+       toggle100pc->update(EQUIV(plugin->config.num, 100));
+       toggle200pc->update(EQUIV(plugin->config.num, 200));
+       toggle400pc->update(EQUIV(plugin->config.num, 400));
+       return 0;
+}
+
+
+SpeedPcToggle::SpeedPcToggle(SpeedPc *plugin, SpeedPcWindow *gui,
+       int init_value,
+       int x,
+       int y,
+       int value,
+       const char *string)
+ : BC_Radial(x, y, init_value, string)
+{
+       this->value = value;
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int SpeedPcToggle::handle_event()
+{
+       plugin->config.num = (float)value;
+       gui->update(RESET_SPEED);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+/* *********************************** */
+/* **** SPEED     ******************** */
+SpeedPcText::SpeedPcText(SpeedPc *plugin, SpeedPcWindow *gui,
+       int x,
+       int y)
+ : BC_TumbleTextBox(gui, (float)plugin->config.num,
+       (float)1.00, (float)1000.00, x, y, xS(60), 2)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+SpeedPcText::~SpeedPcText()
+{
+}
+
+int SpeedPcText::handle_event()
+{
+       plugin->config.num = atof(get_text());
+       plugin->config.denom = 100.00;
+       plugin->config.stretch = 1;
+       plugin->config.boundaries();
+       gui->update(RESET_SPEED);
+       plugin->send_configure_change();
+       return 1;
+}
+
+SpeedPcSlider::SpeedPcSlider(SpeedPc *plugin, SpeedPcWindow *gui,
+       int x, int y, int w)
+ : BC_FSlider(x, y, 0, w, w, 1.00, 1000.00, plugin->config.num)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       enable_show_value(0); // Hide caption
+       set_precision(1.00);
+}
+
+SpeedPcSlider::~SpeedPcSlider()
+{
+}
+
+int SpeedPcSlider::handle_event()
+{
+       plugin->config.num = get_value();
+       plugin->config.denom = 100.00;
+       plugin->config.stretch = 1;
+       gui->update(RESET_SPEED);
+       plugin->send_configure_change();
+       return 1;
+}
+/* *********************************** */
+
+
+SpeedPcStretch::SpeedPcStretch(SpeedPc *plugin,
+       SpeedPcWindow *gui,
+       int x,
+       int y)
+ : BC_Radial(x, y, plugin->config.stretch, _("Stretch"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int SpeedPcStretch::handle_event()
+{
+       plugin->config.stretch = get_value();
+       gui->downsample->update(!get_value());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+SpeedPcDownsample::SpeedPcDownsample(SpeedPc *plugin, SpeedPcWindow *gui,
+       int x,
+       int y)
+ : BC_Radial(x, y, !plugin->config.stretch, _("Downsample"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int SpeedPcDownsample::handle_event()
+{
+       plugin->config.stretch = !get_value();
+       gui->stretch->update(!get_value());
+       plugin->send_configure_change();
+       return 1;
+}
+
+SpeedPcInterpolate::SpeedPcInterpolate(SpeedPc *plugin,        SpeedPcWindow *gui,
+       int x,
+       int y)
+ : BC_CheckBox(x, y, 0, _("Interpolate"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       set_tooltip(_("Interpolate between keyframes"));
+}
+
+int SpeedPcInterpolate::handle_event()
+{
+       plugin->config.interp = get_value();
+       gui->interpolate->update(get_value());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+SpeedPcClr::SpeedPcClr(SpeedPc *plugin,        SpeedPcWindow *gui, int x, int y, int clear)
+ : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       this->clear = clear;
+}
+SpeedPcClr::~SpeedPcClr()
+{
+}
+int SpeedPcClr::handle_event()
+{
+       plugin->config.reset(clear);
+       gui->update(clear);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+SpeedPcReset::SpeedPcReset(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+SpeedPcReset::~SpeedPcReset()
+{
+}
+int SpeedPcReset::handle_event()
+{
+       plugin->config.reset(RESET_ALL);
+       gui->update(RESET_ALL);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+SpeedPc::SpeedPc(PluginServer *server)
+ : PluginVClient(server)
+{
+}
+
+SpeedPc::~SpeedPc()
+{
+
+}
+
+const char* SpeedPc::plugin_title() { return N_("Speed PerCent"); }
+int SpeedPc::is_realtime() { return 1; }
+int SpeedPc::is_synthesis() { return 1; }
+
+
+NEW_WINDOW_MACRO(SpeedPc, SpeedPcWindow)
+LOAD_CONFIGURATION_MACRO(SpeedPc, SpeedPcConfig)
+
+int SpeedPc::process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate)
+{
+       int64_t input_frame = get_source_start();
+       SpeedPcConfig prev_config, next_config;
+       KeyFrame *tmp_keyframe, *next_keyframe = get_prev_keyframe(get_source_start());
+       int64_t tmp_position, next_position;
+       int64_t segment_len;
+       double input_rate = frame_rate;
+       int is_current_keyframe;
+
+// if there are no keyframes, the default keyframe is used, and its position is always 0;
+// if there are keyframes, the first keyframe can be after the effect start (and it controls settings before it)
+// so let's calculate using a fake keyframe with the same settings but position == effect start
+       KeyFrame *fake_keyframe = new KeyFrame();
+       fake_keyframe->copy_from(next_keyframe);
+       fake_keyframe->position = local_to_edl(get_source_start());
+       next_keyframe = fake_keyframe;
+
+       // calculate input_frame accounting for all previous keyframes
+       do
+       {
+               tmp_keyframe = next_keyframe;
+               next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0);
+
+               tmp_position = edl_to_local(tmp_keyframe->position);
+               next_position = edl_to_local(next_keyframe->position);
+
+               is_current_keyframe =
+                       next_position > start_position // the next keyframe is after the current position
+                       || next_keyframe->position == tmp_keyframe->position // there are no more keyframes
+                       || !next_keyframe->position; // there are no keyframes at all
+
+               if (is_current_keyframe)
+                       segment_len = start_position - tmp_position;
+               else
+                       segment_len = next_position - tmp_position;
+
+               read_data(next_keyframe);
+               next_config.copy_from(config);
+               read_data(tmp_keyframe);
+               prev_config.copy_from(config);
+               config.interpolate(prev_config, next_config, tmp_position, next_position, tmp_position + segment_len);
+
+               // the area under the curve is the number of frames to advance
+               // as long as interpolate() uses a linear slope we can use geometry to determine this
+               // if interpolate() changes to use a curve then this needs use (possibly) the definite integral
+               double prev_scale = prev_config.num / 100.00;
+               double config_scale = config.num / 100.00;
+               input_frame += (int64_t)(segment_len * ((prev_scale + config_scale) / 2));
+       } while (!is_current_keyframe);
+
+// Change rate
+       if (!config.stretch)
+       {
+               input_rate *= config.num / 100.00;
+
+       }
+
+// printf("SpeedPc::process_buffer %d %lld %f %lld %f\n",
+// __LINE__,
+// start_position,
+// frame_rate,
+// input_frame,
+// input_rate);
+
+       read_frame(frame,
+               0,
+               input_frame,
+               input_rate,
+               0);
+
+       delete fake_keyframe;
+
+       return 0;
+}
+
+
+
+void SpeedPc::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->xbuf);
+       output.tag.set_title("SPEED_PC");
+       output.tag.set_property("SPEED", config.num);
+       output.tag.set_property("DENOM", config.denom);
+       output.tag.set_property("STRETCH", config.stretch);
+       output.tag.set_property("INTERPOLATE", config.interp);
+       output.append_tag();
+       output.tag.set_title("/SPEED_PC");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void SpeedPc::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->xbuf);
+
+       while(!input.read_tag())
+       {
+               if(input.tag.title_is("SPEED_PC"))
+               {
+                       config.num = input.tag.get_property("SPEED", config.num);
+                       config.denom = input.tag.get_property("DENOM", config.denom);
+                       config.stretch = input.tag.get_property("STRETCH", config.stretch);
+                       config.interp = input.tag.get_property("INTERPOLATE", config.interp);
+               }
+       }
+}
+
+void SpeedPc::update_gui()
+{
+       if(thread)
+       {
+               int changed = load_configuration();
+
+               if(changed)
+               {
+                       SpeedPcWindow* window = (SpeedPcWindow*)thread->window;
+                       window->lock_window("SpeedPc::update_gui");
+                       window->update(RESET_ALL);
+// OLD ReframeRT code
+/*
+                       window->stretch->update(config.stretch);
+                       window->downsample->update(!config.stretch);
+*/
+                       window->unlock_window();
+               }
+       }
+}
+
+
+
+
+
diff --git a/cinelerra-5.1/plugins/speed_pc/speed_pc.h b/cinelerra-5.1/plugins/speed_pc/speed_pc.h
new file mode 100644 (file)
index 0000000..58fb693
--- /dev/null
@@ -0,0 +1,234 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2016 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * 2020. Derivative by ReframeRT plugin for a more easy use.
+ * It uses percentage value of the speed referred to originl speed (=100%).
+ * Some old ReframeRT parameters (Stretch and denom) have not been deleted,
+ * for future development, if any.
+ * Stretch and denom variables are set to a constant value:
+ * Stretch= 1; denom= 100.00.
+ * Speed_MIN= 1.00%; Speed_MAX= 1000.00% 
+ */
+
+#ifndef SPEED_PC_H
+#define SPEED_PC_H
+
+
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "language.h"
+#include "pluginvclient.h"
+#include "theme.h"
+#include "transportque.h"
+
+
+#define RESET_DEFAULT_SETTINGS 10
+#define RESET_ALL    0
+#define RESET_SPEED  1
+
+class SpeedPcConfig;
+class SpeedPcText;
+class SpeedPcSlider;
+class SpeedPcStretch;
+class SpeedPcDownsample;
+class SpeedPcInterpolate;
+class SpeedPcClr;
+class SpeedPc;
+class SpeedPcWindow;
+class SpeedPcReset;
+
+
+
+class SpeedPcConfig
+{
+public:
+       SpeedPcConfig();
+       void boundaries();
+       int equivalent(SpeedPcConfig &src);
+       void reset(int clear);
+       void copy_from(SpeedPcConfig &src);
+       void interpolate(SpeedPcConfig &prev,
+               SpeedPcConfig &next,
+               int64_t prev_frame,
+               int64_t next_frame,
+               int64_t current_frame);
+
+       double num;
+       double denom;
+       int stretch;
+       int interp;
+       int optic_flow;
+};
+
+
+
+class SpeedPcToggle : public BC_Radial
+{
+public:
+       SpeedPcToggle(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int init_value,
+               int x,
+               int y,
+               int value,
+               const char *string);
+       int handle_event();
+
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+       int value;
+};
+
+class SpeedPcText : public BC_TumbleTextBox
+{
+public:
+       SpeedPcText(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int x,
+               int y);
+       ~SpeedPcText();
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcSlider : public BC_FSlider
+{
+public:
+       SpeedPcSlider(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int x, int y, int w);
+       ~SpeedPcSlider();
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcClr : public BC_Button
+{
+public:
+       SpeedPcClr(SpeedPc *plugin, SpeedPcWindow *gui,
+               int x, int y, int clear);
+       ~SpeedPcClr();
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+       int clear;
+};
+
+class SpeedPcStretch : public BC_Radial
+{
+public:
+       SpeedPcStretch(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int x,
+               int y);
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcDownsample : public BC_Radial
+{
+public:
+       SpeedPcDownsample(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int x,
+               int y);
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcInterpolate : public BC_CheckBox
+{
+public:
+       SpeedPcInterpolate(SpeedPc *plugin,
+               SpeedPcWindow *gui,
+               int x,
+               int y);
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcReset : public BC_GenericButton
+{
+public:
+       SpeedPcReset(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y);
+       ~SpeedPcReset();
+       int handle_event();
+       SpeedPc *plugin;
+       SpeedPcWindow *gui;
+};
+
+class SpeedPcWindow : public PluginClientWindow
+{
+public:
+       SpeedPcWindow(SpeedPc *plugin);
+       ~SpeedPcWindow();
+       void create_objects();
+       void update(int clear);
+
+       int update_toggles();
+
+       SpeedPc *plugin;
+
+       SpeedPcToggle *toggle25pc;
+       SpeedPcToggle *toggle50pc;
+       SpeedPcToggle *toggle100pc;
+       SpeedPcToggle *toggle200pc;
+       SpeedPcToggle *toggle400pc;
+
+       SpeedPcText *speed_pc_text;
+       SpeedPcSlider *speed_pc_slider;
+       SpeedPcClr *speed_pc_clr;
+       SpeedPcStretch *stretch;
+       SpeedPcDownsample *downsample;
+       SpeedPcInterpolate *interpolate;
+
+       SpeedPcReset *reset;
+};
+
+
+class SpeedPc : public PluginVClient
+{
+public:
+       SpeedPc(PluginServer *server);
+       ~SpeedPc();
+
+       PLUGIN_CLASS_MEMBERS(SpeedPcConfig)
+
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+       int is_realtime();
+       int is_synthesis();
+       int process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate);
+};
+
+#endif
\ No newline at end of file