new/reworked audio plugins ported from hv72 compressor/multi/reverb, glyph workaround...
authorGood Guy <good1.2guy@gmail.com>
Wed, 11 Dec 2019 16:17:30 +0000 (09:17 -0700)
committerGood Guy <good1.2guy@gmail.com>
Wed, 11 Dec 2019 16:17:30 +0000 (09:17 -0700)
61 files changed:
cinelerra-5.1/Cinelerra_factory
cinelerra-5.1/cinelerra/Makefile
cinelerra-5.1/cinelerra/attachmentpoint.C
cinelerra-5.1/cinelerra/attachmentpoint.h
cinelerra-5.1/cinelerra/compressortools.C [new file with mode: 0644]
cinelerra-5.1/cinelerra/compressortools.h [new file with mode: 0644]
cinelerra-5.1/cinelerra/compressortools.inc [new file with mode: 0644]
cinelerra-5.1/cinelerra/cwindowgui.h
cinelerra-5.1/cinelerra/eqcanvas.C [new file with mode: 0644]
cinelerra-5.1/cinelerra/eqcanvas.h [new file with mode: 0644]
cinelerra-5.1/cinelerra/eqcanvas.inc [new file with mode: 0644]
cinelerra-5.1/cinelerra/formattools.C
cinelerra-5.1/cinelerra/fourier.C
cinelerra-5.1/cinelerra/fourier.h
cinelerra-5.1/cinelerra/mbuttons.h
cinelerra-5.1/cinelerra/mwindow.C
cinelerra-5.1/cinelerra/mwindow.h
cinelerra-5.1/cinelerra/playbackengine.C
cinelerra-5.1/cinelerra/playbackengine.h
cinelerra-5.1/cinelerra/playtransport.C
cinelerra-5.1/cinelerra/playtransport.h
cinelerra-5.1/cinelerra/pluginaclient.C
cinelerra-5.1/cinelerra/pluginaclient.h
cinelerra-5.1/cinelerra/pluginclient.C
cinelerra-5.1/cinelerra/pluginclient.h
cinelerra-5.1/cinelerra/pluginclient.inc
cinelerra-5.1/cinelerra/pluginserver.C
cinelerra-5.1/cinelerra/pluginserver.h
cinelerra-5.1/cinelerra/resourcepixmap.C
cinelerra-5.1/cinelerra/resourcepixmap.h
cinelerra-5.1/cinelerra/theme.C
cinelerra-5.1/cinelerra/theme.h
cinelerra-5.1/guicast/arraylist.h
cinelerra-5.1/guicast/bcmeter.C
cinelerra-5.1/guicast/bcmeter.h
cinelerra-5.1/guicast/bcpot.C
cinelerra-5.1/guicast/bcwindowbase.C
cinelerra-5.1/guicast/bcwindowbase.h
cinelerra-5.1/guicast/linklist.h
cinelerra-5.1/guicast/units.C
cinelerra-5.1/guicast/units.h
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/compressor/Makefile
cinelerra-5.1/plugins/compressor/compressor.C
cinelerra-5.1/plugins/compressor/compressor.h
cinelerra-5.1/plugins/compressormulti/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/compressormulti/comprmulti.C [new file with mode: 0644]
cinelerra-5.1/plugins/compressormulti/comprmulti.h [new file with mode: 0644]
cinelerra-5.1/plugins/compressormulti/comprmultigui.C [new file with mode: 0644]
cinelerra-5.1/plugins/compressormulti/comprmultigui.h [new file with mode: 0644]
cinelerra-5.1/plugins/denoisefft/denoisefft.C
cinelerra-5.1/plugins/graphic/graphic.C
cinelerra-5.1/plugins/graphic/graphic.h
cinelerra-5.1/plugins/parametric/parametric.C
cinelerra-5.1/plugins/parametric/parametric.h
cinelerra-5.1/plugins/reverb/reverb.C
cinelerra-5.1/plugins/reverb/reverb.h
cinelerra-5.1/plugins/reverb/reverb.inc
cinelerra-5.1/plugins/reverb/reverbwindow.C
cinelerra-5.1/plugins/reverb/reverbwindow.h
cinelerra-5.1/plugins/reverb/reverbwindow.inc

index 05acba1..4a4c0e5 100644 (file)
@@ -1,17 +1,29 @@
 <?xml version="1.0"?>
 <PLUGIN TITLE=Compressor>
-<KEYFRAME TITLE=loud><COMPRESSOR TRIGGER=0 REACTION_LEN=-1.0000000000000014e-01 DECAY_LEN=9.9999999999999367e-02 SMOOTHING_ONLY=0 INPUT=2>
-<LEVEL X=-4.9852631578947367e+01 Y=0>
+<KEYFRAME TITLE=loud>
+    <COMPRESSOR TRIGGER=0 ATTACK_LEN=1e-01 RELEASE_LEN=2e-01 SMOOTHING_ONLY=0 INPUT=2>
+    <COMPRESSORBAND>
+    <LEVEL X=-42 Y=0>
+    <LEVEL X=6 Y=0>
+    </COMPRESSORBAND>
+    </COMPRESSOR>
 </KEYFRAME>
-<KEYFRAME TITLE=soft><COMPRESSOR TRIGGER=0 REACTION_LEN=-1.0000000000000014e-01 DECAY_LEN=4.9999999999999982e+00 SMOOTHING_ONLY=0 INPUT=2>
-<LEVEL X=-4.9852631578947367e+01 Y=0>
+<KEYFRAME TITLE=soft>
+    <COMPRESSOR TRIGGER=0 ATTACK_LEN=1e-01 RELEASE_LEN=1 SMOOTHING_ONLY=0 INPUT=2>
+    <COMPRESSORBAND>
+    <LEVEL X=-42 Y=0>
+    <LEVEL X=6 Y=0>
+    </COMPRESSORBAND>
+    </COMPRESSOR>
 </KEYFRAME>
-<KEYFRAME TITLE=voice><COMPRESSOR TRIGGER=0 REACTION_LEN=-1.0000000000000001e-01 DECAY_LEN=1.0000000000000001e-01 SMOOTHING_ONLY=0 INPUT=2>
-<LEVEL X=-3.0484210526315792e+01 Y=-80>
-<LEVEL X=-1.9873684210526321e+01 Y=0>
-</KEYFRAME>
-<KEYFRAME TITLE="live audio"><COMPRESSOR TRIGGER=0 REACTION_LEN=1.0000000000000001e-01 DECAY_LEN=1.0000000000000001e-01 SMOOTHING_ONLY=0 INPUT=2>
-<LEVEL X=-4.9852631578947367e+01 Y=0>
+<KEYFRAME TITLE=voice>
+    <COMPRESSOR TRIGGER=0 ATTACK_LEN=1e-01 RELEASE_LEN=2e-01 SMOOTHING_ONLY=0 INPUT=2>
+    <COMPRESSORBAND>
+    <LEVEL X=-30 Y=-78>
+    <LEVEL X=-20 Y=0>
+    <LEVEL X=6 Y=0>
+    </COMPRESSORBAND>
+    </COMPRESSOR>
 </KEYFRAME>
 </PLUGIN>
 <PLUGIN TITLE=Lens>
index f8c0bc1..e4f782d 100644 (file)
@@ -96,6 +96,7 @@ OBJS := $(OVERLAYS) \
        $(OBJDIR)/clippopup.o \
        $(OBJDIR)/colorpicker.o \
        $(OBJDIR)/commonrender.o \
+       $(OBJDIR)/compressortools.o \
        $(OBJDIR)/confirmquit.o \
        $(OBJDIR)/confirmsave.o \
        $(OBJDIR)/convert.o \
@@ -126,6 +127,7 @@ OBJS := $(OVERLAYS) \
        $(OBJDIR)/edl.o \
        $(OBJDIR)/edlsession.o \
        $(OBJDIR)/effectlist.o \
+       $(OBJDIR)/eqcanvas.o \
        $(OBJDIR)/exportedl.o \
        $(OBJDIR)/fadeengine.o \
        $(OBJDIR)/ffmpeg.o \
index 362a5e1..adc1c4a 100644 (file)
@@ -211,13 +211,32 @@ int AttachmentPoint::singlechannel()
        return 0;
 }
 
+void AttachmentPoint::reset_gui_frames(PluginServer *server)
+{
+       if( server != plugin_servers.get(0) ) return;
+       if( renderengine && renderengine->mwindow )
+               renderengine->mwindow->reset_plugin_gui_frames(plugin);
+}
 
-void AttachmentPoint::render_gui(void *data, PluginServer *server)
+void AttachmentPoint::render_gui_frames(PluginClientFrames *frames, PluginServer *server)
 {
 //printf("AttachmentPoint::render_gui 1 %p %p\n", server, plugin_servers.get(0));
        void *This = this;
        if(!This) printf("AttachmentPoint::render_gui 1 NULL\n");
 
+// Discard if not 1st plugin server, so single channel plugins don't get double GUI updates
+       if(server != plugin_servers.get(0)) return;
+
+       if(renderengine && renderengine->mwindow)
+               renderengine->mwindow->render_plugin_gui_frames(frames, plugin);
+}
+
+
+void AttachmentPoint::render_gui(void *data, PluginServer *server)
+{
+       void *This = this;
+       if(!This) printf("AttachmentPoint::render_gui 2 NULL\n");
+
 // Discard if not 1st plugin server, so single channel plugins don't get double GUI updates
        if(server != plugin_servers.get(0)) return;
 
@@ -228,7 +247,7 @@ void AttachmentPoint::render_gui(void *data, PluginServer *server)
 void AttachmentPoint::render_gui(void *data, int size, PluginServer *server)
 {
        void *This = this;
-       if(!This) printf("AttachmentPoint::render_gui 2 NULL\n");
+       if(!This) printf("AttachmentPoint::render_gui 3 NULL\n");
 
 // Discard if not 1st plugin server, so single channel plugins don't get double GUI updates
        if(server != plugin_servers.get(0)) return;
@@ -237,6 +256,7 @@ void AttachmentPoint::render_gui(void *data, int size, PluginServer *server)
                renderengine->mwindow->render_plugin_gui(data, size, plugin);
 }
 
+
 int AttachmentPoint::gui_open()
 {
        if(renderengine && renderengine->mwindow)
index 6a7dc0f..ebcfdc1 100644 (file)
@@ -32,6 +32,7 @@
 #include "mwindow.inc"
 #include "messages.inc"
 #include "plugin.inc"
+#include "pluginclient.inc"
 #include "pluginserver.inc"
 #include "renderengine.inc"
 #include "sharedlocation.h"
@@ -67,11 +68,9 @@ public:
        int attach_virtual_plugin(VirtualNode *virtual_plugin);
        virtual void delete_buffer_vector() {};
 
-// return 0 if ready to render
-// check all the virtual plugins for waiting status
-// all virtual plugins attached to this must be waiting for a render
-//     int sort(VirtualNode *virtual_plugin);
 // Called by plugin server to render GUI with data.
+       void reset_gui_frames(PluginServer *server);
+       void render_gui_frames(PluginClientFrames *frames, PluginServer *server);
        void render_gui(void *data, PluginServer *server);
        void render_gui(void *data, int size, PluginServer *server);
        int gui_open();
diff --git a/cinelerra-5.1/cinelerra/compressortools.C b/cinelerra-5.1/cinelerra/compressortools.C
new file mode 100644 (file)
index 0000000..fee76d3
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2019 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
+ * 
+ */
+
+
+// Objects for compressors
+#include "clip.h"
+#include "compressortools.h"
+#include "cursors.h"
+#include "filexml.h"
+#include "language.h"
+#include "pluginclient.h"
+#include "samples.h"
+#include "theme.h"
+#include <string.h>
+
+
+BandConfig::BandConfig()
+{
+       freq = 0;
+       solo = 0;
+       bypass = 0;
+//     readahead_len = 1.0;
+       attack_len = 1.0;
+       release_len = 1.0;
+}
+
+BandConfig::~BandConfig()
+{
+       
+}
+
+void BandConfig::save_data(FileXML *xml, int number, int do_multiband)
+{
+       xml->tag.set_title("COMPRESSORBAND");
+       if( do_multiband ) {
+               xml->tag.set_property("NUMBER", number);
+               xml->tag.set_property("FREQ", freq);
+               xml->tag.set_property("BYPASS", bypass);
+               xml->tag.set_property("SOLO", solo);
+               xml->tag.set_property("ATTACK_LEN", attack_len);
+               xml->tag.set_property("RELEASE_LEN", release_len);
+       }
+       xml->append_tag();
+       xml->append_newline();
+
+       for( int i = 0; i < levels.total; i++ ) {
+               xml->tag.set_title("LEVEL");
+               xml->tag.set_property("X", levels.values[i].x);
+               xml->tag.set_property("Y", levels.values[i].y);
+               xml->append_tag();
+               xml->append_newline();
+       }
+
+       xml->tag.set_title("/COMPRESSORBAND");
+       xml->append_tag();
+       xml->append_newline();
+}
+
+void BandConfig::read_data(FileXML *xml, int do_multiband)
+{
+       if( do_multiband ) {
+               freq = xml->tag.get_property("FREQ", freq);
+               bypass = xml->tag.get_property("BYPASS", bypass);
+               solo = xml->tag.get_property("SOLO", solo);
+               attack_len = xml->tag.get_property("ATTACK_LEN", attack_len);
+               release_len = xml->tag.get_property("RELEASE_LEN", release_len);
+       }
+
+       levels.remove_all();
+       int result = 0;
+       while( !result ) {
+               result = xml->read_tag();
+               if( !result ) {
+                       if( xml->tag.title_is("LEVEL") ) {
+                               double x = xml->tag.get_property("X", (double)0);
+                               double y = xml->tag.get_property("Y", (double)0);
+                               compressor_point_t point = { x, y };
+
+                               levels.append(point);
+                       }
+                       else
+                       if( xml->tag.title_is("/COMPRESSORBAND") ) {
+                               break;
+                       }
+               }
+       }
+}
+
+void BandConfig::copy_from(BandConfig *src)
+{
+       levels.remove_all();
+       for( int i = 0; i < src->levels.total; i++ ) {
+               levels.append(src->levels.values[i]);
+       }
+
+//     readahead_len = src->readahead_len;
+       attack_len = src->attack_len;
+       release_len = src->release_len;
+       freq = src->freq;
+       solo = src->solo;
+       bypass = src->bypass;
+}
+
+int BandConfig::equiv(BandConfig *src)
+{
+       if( levels.total != src->levels.total ||
+               solo != src->solo ||
+               bypass != src->bypass ||
+               freq != src->freq ||
+//             !EQUIV(readahead_len, src->readahead_len) ||
+               !EQUIV(attack_len, src->attack_len) ||
+               !EQUIV(release_len, src->release_len) ) {
+               return 0;
+       }
+
+       for( int i = 0; i < levels.total && i < src->levels.total; i++ ) {
+               compressor_point_t *this_level = &levels.values[i];
+               compressor_point_t *that_level = &src->levels.values[i];
+               if( !EQUIV(this_level->x, that_level->x) ||
+                       !EQUIV(this_level->y, that_level->y) ) {
+                       return 0;
+               }
+       }
+       
+       return 1;
+}
+
+void BandConfig::boundaries(CompressorConfigBase *base)
+{
+       for( int i = 0; i < levels.size(); i++ ) {
+               compressor_point_t *level = &levels.values[i];
+               if( level->x < base->min_db ) level->x = base->min_db;
+               if( level->y < base->min_db ) level->y = base->min_db;
+               if( level->x > base->max_db ) level->x = base->max_db;
+               if( level->y > base->max_db ) level->y = base->max_db;
+       }
+}
+
+
+CompressorConfigBase::CompressorConfigBase(int total_bands)
+{
+       this->total_bands = total_bands;
+       bands = new BandConfig[total_bands];
+       min_db = -78.0;
+       max_db = 6.0;
+       min_value = DB::fromdb(min_db) + 0.001;
+//     min_x = min_db;  max_x = 0;
+//     min_y = min_db;  max_y = 0;
+       smoothing_only = 0;
+       trigger = 0;
+       input = CompressorConfigBase::TRIGGER;
+       for( int i=0; i<total_bands; ++i ) {
+               bands[i].freq = Freq::tofreq((i+1) * TOTALFREQS / total_bands);
+       }
+       current_band = 0;
+}
+
+
+CompressorConfigBase::~CompressorConfigBase()
+{
+       delete [] bands;
+}
+
+void CompressorConfigBase::boundaries()
+{
+       for( int i=0; i<total_bands; ++i ) {
+               bands[i].boundaries(this);
+       }
+}
+
+
+void CompressorConfigBase::copy_from(CompressorConfigBase &that)
+{
+//     min_x = that.min_x;  max_x = that.max_x;
+//     min_y = that.min_y;  max_y = that.max_y;
+       trigger = that.trigger;
+       input = that.input;
+       smoothing_only = that.smoothing_only;
+
+       for( int i=0; i<total_bands; ++i ) {
+               BandConfig *dst = &bands[i];
+               BandConfig *src = &that.bands[i];
+               dst->copy_from(src);
+       }
+}
+
+
+int CompressorConfigBase::equivalent(CompressorConfigBase &that)
+{
+       for( int i=0; i<total_bands; ++i ) {
+               if( !bands[i].equiv(&that.bands[i]) )
+                       return 0;
+       }
+
+       return trigger == that.trigger &&
+               input == that.input &&
+               smoothing_only == that.smoothing_only;
+}
+
+double CompressorConfigBase::get_y(int band, int i)
+{
+       ArrayList<compressor_point_t> &levels = bands[band].levels;
+       int sz = levels.size();
+       if( !sz ) return 1.;
+       if( i >= sz ) i = sz-1;
+       return levels.values[i].y;
+}
+
+double CompressorConfigBase::get_x(int band, int i)
+{
+       ArrayList<compressor_point_t> &levels = bands[band].levels;
+       int sz = levels.size();
+       if( !sz ) return 0.;
+       if( i >= sz ) i = sz-1;
+       return levels.values[i].x;
+}
+
+double CompressorConfigBase::calculate_db(int band, double x)
+{
+       ArrayList<compressor_point_t> &levels = bands[band].levels;
+       int sz = levels.size();
+       if( !sz ) return x;
+       compressor_point_t &point0 = levels[0];
+       double px0 = point0.x, py0 = point0.y, dx0 = x - px0;
+// the only point.  Use slope from min_db
+       if( sz == 1 )
+               return py0 + dx0 * (py0 - min_db) / (px0 - min_db);
+// before 1st point, use 1:1 gain
+       double ret = py0 + dx0;
+// find point <= x
+       int k = sz;
+       while( --k >= 0 && levels[k].x > x );
+       if( k >= 0 ) {
+               compressor_point_t &curr = levels[k];
+               double cx = curr.x, cy = curr.y, dx = x - cx;
+// between 2 points.  Use slope between 2 points
+// the last point.  Use slope of last 2 points
+               if( k >= sz-1 ) --k;
+               compressor_point_t &prev = levels[k+0];
+               compressor_point_t &next = levels[k+1];
+               double px = prev.x, py = prev.y;
+               double nx = next.x, ny = next.y;
+               ret = cy + dx * (ny - py) / (nx - px);
+       }
+
+       return ret;
+}
+
+
+int CompressorConfigBase::set_point(int band, double x, double y)
+{
+       ArrayList<compressor_point_t> &levels = bands[band].levels;
+       int k = levels.size(), ret = k;
+       while( --k >= 0 && levels[k].x >= x ) ret = k;
+       compressor_point_t new_point = { x, y };
+       levels.insert(new_point, ret);
+       return ret;
+}
+
+void CompressorConfigBase::remove_point(int band, int i)
+{
+       ArrayList<compressor_point_t> &levels = bands[band].levels;
+       levels.remove_number(i);
+}
+
+
+double CompressorConfigBase::calculate_output(int band, double x)
+{
+       double x_db = DB::todb(x);
+       return DB::fromdb(calculate_db(band, x_db));
+}
+
+
+double CompressorConfigBase::calculate_gain(int band, double input_linear)
+{
+       double output_linear = calculate_output(band, input_linear);
+// output is below minimum.  Mute it
+       return output_linear < min_value ? 0. :
+// input is below minimum.  Don't change it.
+               fabs(input_linear - 0.0) < min_value ? 1. :
+// gain
+               output_linear / input_linear;
+}
+
+
+CompressorCanvasBase::CompressorCanvasBase(CompressorConfigBase *config, 
+               PluginClient *plugin, PluginClientWindow *window, 
+               int x, int y, int w, int h)
+ : BC_SubWindow(x, y, w, h, BLACK)
+{
+       this->config = config;
+       this->plugin = plugin;
+       this->window = window;
+       current_operation = NONE;
+
+       graph_x = 0;
+       graph_y = 0;
+       graph_w = w - graph_x;
+       graph_h = h - graph_y;
+       subdivisions = 6;
+       divisions = (int)(config->max_db - config->min_db) / subdivisions;
+}
+
+CompressorCanvasBase::~CompressorCanvasBase()
+{
+}
+
+
+void CompressorCanvasBase::create_objects()
+{
+       add_subwindow(menu = new CompressorPopup(this));
+       menu->create_objects();
+
+       set_cursor(CROSS_CURSOR, 0, 0);
+       draw_scales();
+       update();
+}
+
+void CompressorCanvasBase::draw_scales()
+{
+       int yfudge = yS(10);
+       window->set_font(SMALLFONT);
+       window->set_color(get_resources()->default_text_color);
+
+// output divisions
+       for( int i=0; i<=divisions; ++i ) {
+               int y = get_y() + yfudge + graph_y + graph_h * i / divisions;
+               int x = get_x();
+               char string[BCTEXTLEN];
+               sprintf(string, "%.0f", config->max_db - 
+                       (float)i / divisions * (config->max_db - config->min_db));
+               int text_w = get_text_width(SMALLFONT, string);
+               if( i >= divisions ) y -= yfudge;
+               window->draw_text(x-xS(10) - text_w, y, string);
+               if( i >= divisions ) break;
+               
+               int y1 = get_y() + graph_y + graph_h * i / divisions;
+               int y2 = get_y() + graph_y + graph_h * (i + 1) / divisions;
+               int x1 = get_x() - xS(10), x2 = get_x() - xS(5);
+               for( int j=0; j<subdivisions; ++j,x1=x2 ) {
+                       y = y1 + (y2 - y1) * j / subdivisions;
+                       window->draw_line(x, y, x1, y);
+               }
+       }
+
+// input divisions
+       for( int i=0; i<=divisions; ++i ) {
+               int y = get_y() + get_h();
+               int x = get_x() + graph_x + graph_w * i / divisions;
+               int y0 = y + window->get_text_ascent(SMALLFONT);
+               char string[BCTEXTLEN];
+               sprintf(string, "%.0f", (float)i / divisions * 
+                               (config->max_db - config->min_db) + config->min_db);
+               int text_w = get_text_width(SMALLFONT, string);
+               window->draw_text(x - text_w, y0 + yS(10), string);
+               if( i >= divisions ) break;
+
+               int x1 = get_x() + graph_x + graph_w * i / divisions;
+               int x2 = get_x() + graph_x + graph_w * (i + 1) / divisions;
+               int y1 = y + yS(10), y2 = y + yS(5);
+               for( int j=0; j<subdivisions; ++j,y1=y2 ) {
+                       x = x1 + (x2 - x1) * j / subdivisions;
+                       window->draw_line(x, y, x, y1);
+               }
+       }
+
+
+}
+
+#define POINT_W xS(10)
+
+// get Y from X
+int CompressorCanvasBase::x_to_y(int band, int x)
+{
+       double min_db = config->min_db, max_db = config->max_db;
+       double rng_db = max_db - min_db;
+       double x_db = min_db + (double)x / graph_w * rng_db;
+       double y_db = config->calculate_db(band, x_db);
+       int y = graph_y + graph_h - (int)((y_db - min_db) * graph_h / rng_db);
+       return y;
+}
+
+// get X from DB
+int CompressorCanvasBase::db_to_x(double db)
+{
+       double min_db = config->min_db, max_db = config->max_db;
+       double rng_db = max_db - min_db;
+       int x = graph_x + (double)(db - min_db) * graph_w / rng_db;
+       return x;
+}
+
+// get Y from DB
+int CompressorCanvasBase::db_to_y(double db)
+{
+       double min_db = config->min_db, max_db = config->max_db;
+       double rng_db = max_db - min_db;
+       int y = graph_y + graph_h - (int)((db - min_db) * graph_h / rng_db); 
+       return y;
+}
+
+
+double CompressorCanvasBase::x_to_db(int x)
+{
+       CLAMP(x, 0, get_w());
+       double min_db = config->min_db, max_db = config->max_db;
+       double rng_db = max_db - min_db;
+       double x_db = (double)(x - graph_x) * rng_db / graph_w + min_db;
+       CLAMP(x_db, min_db, max_db);
+       return x_db;
+}
+
+double CompressorCanvasBase::y_to_db(int y)
+{
+       CLAMP(y, 0, get_h());
+       double min_db = config->min_db, max_db = config->max_db;
+       double rng_db = max_db - min_db;
+       double y_db = (double)(graph_y - y) * rng_db / graph_h + max_db;
+       CLAMP(y_db, min_db, max_db);
+       return y_db;
+}
+
+
+
+void CompressorCanvasBase::update()
+{
+// headroom boxes
+       set_color(window->get_bg_color());
+       draw_box(graph_x, 0, get_w(), graph_y);
+       draw_box(graph_w, graph_y, get_w() - graph_w, get_h() - graph_y);
+//      const int checker_w = DP(10);
+//      const int checker_h = DP(10);
+//      set_color(MDGREY);
+//      for( int i = 0; i < get_h(); i += checker_h )
+//      {
+//              for( int j = (i % 2) * checker_w; j < get_w(); j += checker_w * 2 )
+//              {
+//                      if( !(i >= graph_y && 
+//                              i + checker_h < graph_y + graph_h &&
+//                              j >= graph_x &&
+//                              j + checker_w < graph_x + graph_w) )
+//                      {
+//                              draw_box(j, i, checker_w, checker_h);
+//                      }
+//              }
+//      }
+
+// canvas boxes
+       set_color(plugin->get_theme()->graph_bg_color);
+       draw_box(graph_x, graph_y, graph_w, graph_h);
+
+// graph border
+       draw_3d_border(0, 0, get_w(), get_h(), window->get_bg_color(),
+               plugin->get_theme()->graph_border1_color,
+               plugin->get_theme()->graph_border2_color, 
+               window->get_bg_color());
+
+       set_line_dashes(1);
+       set_color(plugin->get_theme()->graph_grid_color);
+       
+       for( int i = 1; i < divisions; i++ ) {
+               int y = graph_y + graph_h * i / divisions;
+               draw_line(graph_x, y, graph_x + graph_w, y);
+// 0db 
+               if( i == 1 ) {
+                       draw_line(graph_x, y + 1, graph_x + graph_w, y + 1);
+               }
+               
+               int x = graph_x + graph_w * i / divisions;
+               draw_line(x, graph_y, x, graph_y + graph_h);
+// 0db 
+               if( i == divisions - 1 ) {
+                       draw_line(x + 1, graph_y, x + 1, graph_y + graph_h);
+               }
+       }
+       set_line_dashes(0);
+
+
+       set_font(MEDIUMFONT);
+       int border = plugin->get_theme()->widget_border; 
+       draw_text(border, get_h() / 2, _("Output"));
+       int tx = get_w() / 2 - get_text_width(MEDIUMFONT, _("Input")) / 2;
+       int ty = get_h() - get_text_height(MEDIUMFONT, _("Input")) - border;
+       draw_text(tx, ty, _("Input"));
+
+       for( int pass = 0; pass < 2; pass++ ) {
+               for( int band = 0; band < config->total_bands; band++ ) {
+// draw the active band on top of the others
+                       if( band == config->current_band && pass == 0 ||
+                               band != config->current_band && pass == 1 ) {
+                               continue;
+                       }
+
+                       if( band == config->current_band ) {
+                               set_color(plugin->get_theme()->graph_active_color);
+                               set_line_width(2);
+                       }
+                       else {
+                               set_color(plugin->get_theme()->graph_inactive_color);
+                               set_line_width(1);
+                       }
+
+// draw the line
+                       int x1 = graph_x, y1 = x_to_y(band, x1);
+                       for( int i=0; ++i <= graph_w; ) {
+                               int x2 = x1+1, y2 = x_to_y(band, x2);
+                               draw_line(x1,y1, x2,y2);
+                               x1 = x2;  y1 = y2;
+                       }
+
+                       set_line_width(1);
+
+// draw the points
+                       if( band == config->current_band ) {
+                               ArrayList<compressor_point_t> &levels = config->bands[band].levels;
+                               for( int i = 0; i < levels.size(); i++ ) {
+                                       double x_db = config->get_x(band, i);
+                                       double y_db = config->get_y(band, i);
+
+                                       int x = db_to_x(x_db);
+                                       int y = db_to_y(y_db);
+
+                                       if( i == current_point ) {
+                                               draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
+                                       }
+                                       else {
+                                               draw_rectangle(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       flash();
+}
+
+int CompressorCanvasBase::button_press_event()
+{
+// Check existing points
+       if( is_event_win() && 
+               cursor_inside() ) {
+               if( get_buttonpress() == 3 ) {
+                       menu->activate_menu();
+                       return 1;
+               }
+               int band = config->current_band;
+               ArrayList<compressor_point_t> &levels = config->bands[band].levels;
+               for( int i=0; i<levels.size(); ++i ) {
+                       double x_db = config->get_x(config->current_band, i);
+                       double y_db = config->get_y(config->current_band, i);
+
+                       int x = db_to_x(x_db);
+                       int y = db_to_y(y_db);
+
+                       if( get_cursor_x() <= x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
+                           get_cursor_y() <= y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2 ) {
+                               current_operation = DRAG;
+                               current_point = i;
+                               return 1;
+                       }
+               }
+
+               if( get_cursor_x() >= graph_x &&
+                   get_cursor_x() < graph_x + graph_w &&
+                   get_cursor_y() >= graph_y &&
+                   get_cursor_y() < graph_y + graph_h ) {
+// Create new point
+                       double x_db = x_to_db(get_cursor_x());
+                       double y_db = y_to_db(get_cursor_y());
+
+                       current_point = config->set_point(config->current_band, x_db, y_db);
+                       current_operation = DRAG;
+                       update_window();
+                       plugin->send_configure_change();
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int CompressorCanvasBase::button_release_event()
+{
+       int band = config->current_band;
+       ArrayList<compressor_point_t> &levels = config->bands[band].levels;
+
+       if( current_operation == DRAG ) {
+               if( current_point > 0 ) {
+                       if( levels[current_point].x < levels[current_point-1].x ) {
+                               config->remove_point(config->current_band, current_point);
+                       }
+               }
+
+               if( current_point < levels.size()-1 ) {
+                       if( levels[current_point].x >= levels[current_point + 1].x ) {
+                               config->remove_point(config->current_band, current_point);
+                       }
+               }
+
+               update_window();
+               plugin->send_configure_change();
+               current_operation = NONE;
+               return 1;
+       }
+
+       return 0;
+}
+
+int CompressorCanvasBase::cursor_motion_event()
+{
+       int band = config->current_band;
+       ArrayList<compressor_point_t> &levels = config->bands[band].levels;
+
+       if( current_operation == DRAG ) {
+               int x = get_cursor_x();
+               int y = get_cursor_y();
+               double x_db = x_to_db(x);
+               double y_db = y_to_db(y);
+               
+               if( shift_down() ) {
+                       const int grid_precision = 6;
+                       x_db = config->max_db + (double)(grid_precision * (int)((x_db - config->max_db) / grid_precision - 0.5));
+                       y_db = config->max_db + (double)(grid_precision * (int)((y_db - config->max_db) / grid_precision - 0.5));
+               }
+               
+               
+//printf("CompressorCanvasBase::cursor_motion_event %d x=%d y=%d x_db=%f y_db=%f\n", 
+//__LINE__, x, y, x_db, y_db);
+               levels[current_point].x = x_db;
+               levels[current_point].y = y_db;
+               update_window();
+               plugin->send_configure_change();
+               return 1;
+       }
+       else
+// Change cursor over points
+       if( is_event_win() && cursor_inside() ) {
+               int new_cursor = CROSS_CURSOR;
+
+               for( int i = 0; i < levels.size(); i++ ) {
+                       double x_db = config->get_x(config->current_band, i);
+                       double y_db = config->get_y(config->current_band, i);
+
+                       int x = db_to_x(x_db);
+                       int y = db_to_y(y_db);
+
+                       if( get_cursor_x() <= x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
+                               get_cursor_y() <= y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2 ) {
+                               new_cursor = UPRIGHT_ARROW_CURSOR;
+                               break;
+                       }
+               }
+
+// out of active area
+               if( get_cursor_x() >= graph_x + graph_w ||
+                       get_cursor_y() < graph_y ) {
+                       new_cursor = ARROW_CURSOR;
+               }
+
+               if( new_cursor != get_cursor() ) {
+                       set_cursor(new_cursor, 0, 1);
+               }
+       }
+       return 0;
+}
+
+
+void CompressorCanvasBase::update_window()
+{
+       printf("CompressorCanvasBase::update_window %d empty\n", __LINE__);
+}
+
+
+int CompressorCanvasBase::is_dragging()
+{
+       return current_operation == DRAG;
+}
+
+
+CompressorClientFrame::CompressorClientFrame()
+{
+       type = -1;
+       band = 0;
+}
+CompressorClientFrame::~CompressorClientFrame()
+{
+}
+
+CompressorFreqFrame::CompressorFreqFrame()
+{
+       type = FREQ_COMPRESSORFRAME;
+       data = 0;      data_size = 0;
+       freq_max = 0;  time_max = 0;
+       nyquist = 0;
+}
+CompressorFreqFrame::~CompressorFreqFrame()
+{
+       delete [] data;
+}
+
+CompressorGainFrame::CompressorGainFrame()
+{
+       type = GAIN_COMPRESSORFRAME;
+       gain = 0;
+       level = 0;
+}
+CompressorGainFrame::~CompressorGainFrame()
+{
+}
+
+CompressorEngine::CompressorEngine(CompressorConfigBase *config,
+       int band)
+{
+       this->config = config;
+       this->band = band;
+       reset();
+}
+
+CompressorEngine::~CompressorEngine()
+{
+}
+
+
+void CompressorEngine::reset()
+{
+       slope_samples = 0;
+       slope_current_sample = 0;
+       peak_samples = 0;
+       slope_value2 = 1.0;
+       slope_value1 = 1.0;
+       slope_samples = 0;
+       slope_current_sample = 0;
+       current_value = 1.0;
+       gui_frame_samples = 2048;
+       gui_max_gain = 1.0;
+       gui_frame_counter = 0;
+}
+
+
+void CompressorEngine::calculate_ranges(int *attack_samples,
+       int *release_samples,
+       int *preview_samples,
+       int sample_rate)
+{
+       BandConfig *band_config = &config->bands[band];
+       *attack_samples = labs(Units::round(band_config->attack_len * sample_rate));
+       *release_samples = Units::round(band_config->release_len * sample_rate);
+       CLAMP(*attack_samples, 1, 1000000);
+       CLAMP(*release_samples, 1, 1000000);
+       *preview_samples = MAX(*attack_samples, *release_samples);
+}
+
+
+void CompressorEngine::process(Samples **output_buffer,
+       Samples **input_buffer,
+       int size,
+       int sample_rate,
+       int channels,
+       int64_t start_position)
+{
+       BandConfig *band_config = &config->bands[band];
+       int attack_samples;
+       int release_samples;
+       int preview_samples;
+       int trigger = CLIP(config->trigger, 0, channels - 1);
+
+       gui_gains.remove_all();
+       gui_levels.remove_all();
+       gui_offsets.remove_all();
+       
+       calculate_ranges(&attack_samples,
+               &release_samples,
+               &preview_samples,
+               sample_rate);
+       if( slope_current_sample < 0 ) slope_current_sample = slope_samples;
+
+       double *trigger_buffer = input_buffer[trigger]->get_data();
+
+       for( int i = 0; i < size; i++ ) {
+               double current_slope = (slope_value2 - slope_value1) /
+                       slope_samples;
+
+// maximums in the 2 time ranges
+               double attack_slope = -0x7fffffff;
+               double attack_sample = -1;
+               int attack_offset = -1;
+               int have_attack_sample = 0;
+               double release_slope = -0x7fffffff;
+               double release_sample = -1;
+               int release_offset = -1;
+               int have_release_sample = 0;
+               if( slope_current_sample >= slope_samples ) {
+// start new line segment
+                       for( int j = 1; j < preview_samples; j++ ) {
+                               GET_TRIGGER(input_buffer[channel]->get_data(), i + j)
+                               double new_slope = (sample - current_value) / j;
+                               if( j < attack_samples && new_slope >= attack_slope ) {
+                                       attack_slope = new_slope;
+                                       attack_sample = sample;
+                                       attack_offset = j;
+                                       have_attack_sample = 1;
+                               }
+                               
+                               if( j < release_samples && 
+                                       new_slope <= 0 && 
+                                       new_slope > release_slope ) {
+                                       release_slope = new_slope;
+                                       release_sample = sample;
+                                       release_offset = j;
+                                       have_release_sample = 1;
+                               }
+                       }
+
+                       slope_current_sample = 0;
+                       if( have_attack_sample && attack_slope >= 0 ) {
+// attack
+                               peak_samples = attack_offset;
+                               slope_samples = attack_offset;
+                               slope_value1 = current_value;
+                               slope_value2 = attack_sample;
+                               current_slope = attack_slope;
+//printf("CompressorEngine::process %d position=%ld slope=%f samples=%d\n", 
+//__LINE__, start_position + i, current_slope, slope_samples);
+                       }
+                       else
+                       if( have_release_sample ) {
+// release
+                               slope_samples = release_offset;
+//                             slope_samples = release_samples;
+                               peak_samples = release_offset;
+                               slope_value1 = current_value;
+                               slope_value2 = release_sample;
+                               current_slope = release_slope;
+//printf("CompressorEngine::process %d position=%ld slope=%f\n", 
+//__LINE__, start_position + i, current_slope);
+                       }
+                       else {
+static int bug = 0;
+if( !bug ) {
+printf("CompressorEngine::process %d have neither attack nor release position=%ld attack=%f release=%f current_value=%f\n",
+__LINE__, start_position + i, attack_slope, release_slope, current_value); bug = 1;
+}
+                       }
+               }
+               else {
+// check for new peak after the line segment
+                       GET_TRIGGER(input_buffer[channel]->get_data(), i + attack_samples)
+                       double new_slope = (sample - current_value) /
+                               attack_samples;
+                       if( current_slope >= 0 ) {
+                               if( new_slope > current_slope ) {
+                                       peak_samples = attack_samples;
+                                       slope_samples = attack_samples;
+                                       slope_current_sample = 0;
+                                       slope_value1 = current_value;
+                                       slope_value2 = sample;
+                                       current_slope = new_slope;
+//printf("CompressorEngine::process %d position=%ld slope=%f\n", 
+//__LINE__, start_position + i, current_slope);
+                               }
+                       }
+                       else
+// this strings together multiple release periods instead of 
+// approaching but never reaching the ending gain
+                       if( current_slope < 0 ) {
+                               if( sample > slope_value2 ) {
+                                       peak_samples = attack_samples;
+                                       slope_samples = release_samples;
+                                       slope_current_sample = 0;
+                                       slope_value1 = current_value;
+                                       slope_value2 = sample;
+                                       new_slope = (sample - current_value) /
+                                               release_samples;
+                                       current_slope = new_slope;
+//printf("CompressorEngine::process %d position=%ld slope=%f\n", 
+//__LINE__, start_position + i, current_slope);
+                               }
+//                             else
+//                              {
+//                                      GET_TRIGGER(input_buffer[channel]->get_data(), i + release_samples)
+//                                      new_slope = (sample - current_value) /
+//                                             release_samples;
+//                                      if( new_slope < current_slope &&
+//                                              slope_current_sample >= peak_samples )
+//                                      {
+//                                              peak_samples = release_samples;
+//                                              slope_samples = release_samples;
+//                                              slope_current_sample = 0;
+//                                              slope_value1 = current_value;
+//                                              slope_value2 = sample;
+//                                             current_slope = new_slope;
+// printf("CompressorEngine::process %d position=%ld slope=%f\n", 
+// __LINE__, start_position + i, current_slope);
+//                                      }
+//                              }
+                       }
+               }
+
+// Update current value and multiply gain
+               slope_current_sample++;
+               current_value = slope_value1 +
+                       (slope_value2 - slope_value1) * 
+                       slope_current_sample / 
+                       slope_samples;
+
+               if( config->smoothing_only ) {
+                       for( int j = 0; j < channels; j++ ) {
+                               output_buffer[j]->get_data()[i] = current_value * 2 - 1;
+                       }
+               }
+               else {
+                       double gain = 1.0;
+
+                       if( band_config->bypass ) {
+                               gain = 1.0;
+                       }
+                       else {
+                               gain = config->calculate_gain(band, current_value);
+                       }
+
+// update the GUI frames
+                       if( fabs(gain - 1.0) > fabs(gui_max_gain - 1.0) ) {
+                               gui_max_gain = gain;
+                       }
+//if( !EQUIV(gain, 1.0) ) printf("CompressorEngine::process %d gain=%f\n", __LINE__, gain);
+
+// calculate the input level to draw.  Should it be the trigger or a channel?
+                       GET_TRIGGER(input_buffer[channel]->get_data(), i);
+                       if( sample > gui_max_level ) {
+                               gui_max_level = sample;
+                       }
+
+                       gui_frame_counter++;
+                       if( gui_frame_counter > gui_frame_samples ) {
+//if( !EQUIV(gui_frame_max, 1.0) ) printf("CompressorEngine::process %d offset=%d gui_frame_max=%f\n", __LINE__, i, gui_frame_max);
+                               gui_gains.append(gui_max_gain);
+                               gui_levels.append(gui_max_level);
+                               gui_offsets.append((double)i / sample_rate);
+                               gui_max_gain = 1.0;
+                               gui_max_level = 0.0;
+                               gui_frame_counter = 0;
+                       }
+
+                       for( int j = 0; j < channels; j++ ) {
+                               output_buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
+                       }
+               }
+       }
+}
+
+
+CompressorPopup::CompressorPopup(CompressorCanvasBase *canvas)
+ : BC_PopupMenu(0, 0, 0, "", 0)
+{
+       this->canvas = canvas;
+}
+
+CompressorPopup::~CompressorPopup()
+{
+}
+
+       
+void CompressorPopup::create_objects()
+{
+       add_item(new CompressorCopy(this));
+       add_item(new CompressorPaste(this));
+       add_item(new CompressorClearGraph(this));
+}
+
+
+CompressorCopy::CompressorCopy(CompressorPopup *popup)
+ : BC_MenuItem(_("Copy graph"))
+{
+       this->popup = popup;
+}
+
+
+CompressorCopy::~CompressorCopy()
+{
+}
+
+int CompressorCopy::handle_event()
+{
+       FileXML output;
+       CompressorConfigBase *config = popup->canvas->config;
+       config->bands[config->current_band].save_data(&output, 0, 0);
+       output.terminate_string();
+       char *cp = output.string(); 
+       popup->to_clipboard(cp, strlen(cp), SECONDARY_SELECTION);
+       return 1;
+}
+
+
+CompressorPaste::CompressorPaste(CompressorPopup *popup)
+ : BC_MenuItem(_("Paste graph"))
+{
+       this->popup = popup;
+}
+
+
+CompressorPaste::~CompressorPaste()
+{
+}
+
+int CompressorPaste::handle_event()
+{
+       int len = popup->get_clipboard()->clipboard_len(SECONDARY_SELECTION);
+       if( len ) {
+               CompressorConfigBase *config = popup->canvas->config;
+               char *string = new char[len + 1];
+               popup->get_clipboard()->from_clipboard(string, len, SECONDARY_SELECTION);
+               
+               FileXML xml;
+               xml.read_from_string(string);
+               delete [] string;
+               int result = 0, got_it = 0;
+               while( !(result = xml.read_tag()) ) {
+                       if( xml.tag.title_is("COMPRESSORBAND") ) {
+                               int band = config->current_band;
+                               BandConfig *band_config = &config->bands[band];
+                               band_config->read_data(&xml, 0);
+                               got_it = 1;
+                               break;
+                       }
+               }
+               
+               if( got_it ) {
+                       popup->canvas->update();
+                       PluginClient *plugin = popup->canvas->plugin;
+                       plugin->send_configure_change();
+               }
+       }
+       return 1;
+}
+
+
+CompressorClearGraph::CompressorClearGraph(CompressorPopup *popup)
+ : BC_MenuItem(_("Clear graph"))
+{
+       this->popup = popup;
+}
+
+
+CompressorClearGraph::~CompressorClearGraph()
+{
+}
+
+int CompressorClearGraph::handle_event()
+{
+       CompressorConfigBase *config = popup->canvas->config;
+       config->bands[config->current_band].levels.remove_all();
+       popup->canvas->update();
+       PluginClient *plugin = popup->canvas->plugin;
+       plugin->send_configure_change();
+       return 1;
+}
+
diff --git a/cinelerra-5.1/cinelerra/compressortools.h b/cinelerra-5.1/cinelerra/compressortools.h
new file mode 100644 (file)
index 0000000..abfb098
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2019 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
+ * 
+ */
+
+
+// Base classes for compressors
+
+#ifndef COMPRESSORTOOLS_H
+#define COMPRESSORTOOLS_H
+
+
+#include "guicast.h"
+#include "pluginaclient.h"
+#include "samples.inc"
+
+
+#define MIN_ATTACK -10
+#define MAX_ATTACK 10
+#define MIN_DECAY 0
+#define MAX_DECAY 255
+#define MIN_TRIGGER 0
+#define MAX_TRIGGER 255
+// range of the meter
+#define MIN_GAIN_CHANGE -20
+#define MAX_GAIN_CHANGE 20
+
+
+class CompressorConfigBase;
+class CompressorPopup;
+
+
+// get sample from trigger buffer
+#define GET_TRIGGER(buffer, offset) \
+double sample = 0; \
+switch(config->input) \
+{ \
+       case CompressorConfigBase::MAX: \
+       { \
+               double max = 0; \
+               for(int channel = 0; channel < channels; channel++) \
+               { \
+                       sample = fabs((buffer)[offset]); \
+                       if(sample > max) max = sample; \
+               } \
+               sample = max; \
+               break; \
+       } \
+ \
+       case CompressorConfigBase::TRIGGER: \
+               sample = fabs(trigger_buffer[offset]); \
+               break; \
+ \
+       case CompressorConfigBase::SUM: \
+       { \
+               double max = 0; \
+               for(int channel = 0; channel < channels; channel++) \
+               { \
+                       sample = fabs((buffer)[offset]); \
+                       max += sample; \
+               } \
+               sample = max; \
+               break; \
+       } \
+}
+
+
+
+
+
+typedef struct
+{
+// DB from min_db - 0
+       double x, y;
+} compressor_point_t;
+
+class BandConfig
+{
+public:
+       BandConfig();
+       virtual ~BandConfig();
+
+       void copy_from(BandConfig *src);
+       int equiv(BandConfig *src);
+       void boundaries(CompressorConfigBase *base);
+       void save_data(FileXML *xml, int number, int do_multiband);
+       void read_data(FileXML *xml, int do_multiband);
+
+       ArrayList<compressor_point_t> levels;
+       int solo;
+       int bypass;
+// units of seconds
+//     double readahead_len;
+       double attack_len;
+       double release_len;
+
+// upper frequency in Hz
+       int freq;
+};
+
+class CompressorConfigBase
+{
+public:
+       CompressorConfigBase(int total_bands);
+       virtual ~CompressorConfigBase();
+
+       virtual void copy_from(CompressorConfigBase &that);
+       virtual int equivalent(CompressorConfigBase &that);
+       void boundaries();
+       
+       void remove_point(int band, int number);
+       int set_point(int band, double x, double y);
+       double calculate_db(int band, double x);
+       double get_x(int band, int number);
+       double get_y(int band, int number);
+       double calculate_gain(int band, double input);
+
+// Calculate linear output from linear input
+       double calculate_output(int band, double x);
+
+// min DB of the graph
+       double min_db;
+// max DB of the graph
+       double max_db;
+
+       BandConfig *bands;
+       int total_bands;
+       int current_band;
+       int trigger;
+       int input;
+       enum
+       {
+               TRIGGER,
+               MAX,
+               SUM
+       };
+       
+       double min_value;
+//     double min_x, min_y;
+//     double max_x, max_y;
+       int smoothing_only;
+};
+
+
+class CompressorCanvasBase : public BC_SubWindow
+{
+public:
+       CompressorCanvasBase(CompressorConfigBase *config, PluginClient *plugin,
+               PluginClientWindow *window, int x, int y, int w, int h);
+       virtual ~CompressorCanvasBase();
+       int is_dragging();
+       int button_press_event();
+       int button_release_event();
+       int cursor_motion_event();
+       void create_objects();
+       void draw_scales();
+       void update();
+       int x_to_y(int band, int x);
+       int db_to_x(double db);
+       int db_to_y(double db);
+       double x_to_db(int x);
+       double y_to_db(int y);
+
+       virtual void update_window();
+
+       enum { NONE, DRAG };
+
+// clickable area of canvas
+       int graph_x, graph_y;
+       int graph_w, graph_h;
+       int current_point;
+       int current_operation;
+       int divisions;
+       int subdivisions;
+       CompressorConfigBase *config;
+       CompressorPopup *menu;
+       PluginClient *plugin;
+       PluginClientWindow *window;
+};
+
+
+#define FREQ_COMPRESSORFRAME 0
+#define GAIN_COMPRESSORFRAME 1
+// used in eqcanvas, compressortools,
+//  plugins: compressor, compressormulti
+class CompressorClientFrame : public PluginClientFrame
+{
+public:
+       CompressorClientFrame();
+       ~CompressorClientFrame();
+       int type, band;
+};
+
+class CompressorFreqFrame : public CompressorClientFrame
+{
+public:
+       CompressorFreqFrame();
+       ~CompressorFreqFrame();
+
+       double freq_max, time_max;
+       double *data;
+       int data_size;
+       int nyquist;
+};
+
+class CompressorGainFrame : public CompressorClientFrame
+{
+public:
+       CompressorGainFrame();
+       ~CompressorGainFrame();
+
+       double gain, level;
+};
+
+class CompressorEngine
+{
+public:
+       CompressorEngine(CompressorConfigBase *config,
+               int band);
+       ~CompressorEngine();
+       
+       void reset();
+       void calculate_ranges(int *attack_samples,
+               int *release_samples,
+               int *preview_samples,
+               int sample_rate);
+       void process(Samples **output_buffer,
+               Samples **input_buffer,
+               int size,
+               int sample_rate,
+               int channels,
+               int64_t start_position);
+
+       CompressorConfigBase *config;
+       int band;
+// the current line segment defining the smooth signal level
+// starting input value of line segment
+       double slope_value1;
+// ending input value of line segment
+       double slope_value2;
+// samples comprising the line segment
+       int slope_samples;
+// samples from the start of the line to the peak that determined the slope
+       int peak_samples;
+// current sample from 0 to slope_samples
+       int slope_current_sample;
+// current value in the line segment
+       double current_value;
+// gain change values to draw on the GUI
+       ArrayList<double> gui_gains;
+// input levels to draw on the GUI
+       ArrayList<double> gui_levels;
+// which second in process() the gui_values came from
+       ArrayList<double> gui_offsets;
+// samples between gui_values.  Set by the user.
+       int gui_frame_samples;
+// temporaries
+       int gui_frame_counter;
+       double gui_max_gain;
+       double gui_max_level;
+};
+
+
+class CompressorCopy : public BC_MenuItem
+{
+public:
+       CompressorCopy(CompressorPopup *popup);
+       ~CompressorCopy();
+       int handle_event();
+       CompressorPopup *popup;
+};
+
+class CompressorPaste : public BC_MenuItem
+{
+public:
+       CompressorPaste(CompressorPopup *popup);
+       ~CompressorPaste();
+       int handle_event();
+       CompressorPopup *popup;
+};
+
+class CompressorClearGraph : public BC_MenuItem
+{
+public:
+       CompressorClearGraph(CompressorPopup *popup);
+       ~CompressorClearGraph();
+       int handle_event();
+       CompressorPopup *popup;
+};
+
+class CompressorPopup : public BC_PopupMenu
+{
+public:
+       CompressorPopup(CompressorCanvasBase *canvas);
+       ~CompressorPopup();
+       
+       void create_objects();
+       
+       
+       CompressorCanvasBase *canvas;
+};
+
+#define GRAPH_BG_COLOR         0x559977
+#define GRAPH_BORDER1_COLOR    0xeeaa44
+#define GRAPH_BORDER2_COLOR    0xeeaaff
+#define GRAPH_GRID_COLOR       0xeeffcc
+#define GRAPH_ACTIVE_COLOR     0x99cc77
+#define GRAPH_INACTIVE_COLOR   0x666688
+
+#endif
+
+
+
+
diff --git a/cinelerra-5.1/cinelerra/compressortools.inc b/cinelerra-5.1/cinelerra/compressortools.inc
new file mode 100644 (file)
index 0000000..58810d9
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __COMPRESSORTOOLS_INC__
+#define __COMPRESSORTOOLS_INC__
+
+class BandConfig;
+class CompressorConfigBase;
+class CompressorClientFrame;
+class CompressorFreqFrame;
+class CompressorGainFrame;
+class CompressorEngine;
+class CompressorCopy;
+class CompressorPaste;
+class CompressorClearGraph;
+class CompressorPopup;
+
+#define GRAPH_BG_COLOR         0x559977
+#define GRAPH_BORDER1_COLOR    0xeeaa44
+#define GRAPH_BORDER2_COLOR    0xeeaaff
+#define GRAPH_GRID_COLOR       0xeeffcc
+#define GRAPH_ACTIVE_COLOR     0x99cc77
+#define GRAPH_INACTIVE_COLOR   0x666688
+
+#endif
index 7dbfde8..14e0ebf 100644 (file)
@@ -263,9 +263,8 @@ class CWindowTransport : public PlayTransport
 {
 public:
        CWindowTransport(MWindow *mwindow,
-               CWindowGUI *gui,
-               int x,
-               int y);
+               CWindowGUI *gui, int x, int y);
+       bool use_mixers() { return true; }
        EDL* get_edl();
        void goto_start();
        void goto_end();
diff --git a/cinelerra-5.1/cinelerra/eqcanvas.C b/cinelerra-5.1/cinelerra/eqcanvas.C
new file mode 100644 (file)
index 0000000..2402888
--- /dev/null
@@ -0,0 +1,248 @@
+#include "clip.h"
+#include "eqcanvas.h"
+#include "mwindow.h"
+#include "pluginaclient.h"
+#include "theme.h"
+
+#define graph_bg_color 0x559977
+#define graph_border1_color 0xeeaa44
+#define graph_border2_color 0xeeaaff
+#define graph_grid_color 0xeeffcc
+#define graph_active_color 0x99cc77
+#define graph_inactive_color 0x666688
+
+EQCanvas::EQCanvas(BC_WindowBase *parent,
+               int x, int y, int w, int h,
+               float min_db, float max_db)
+{
+       this->parent = parent;
+       this->x = x;  this->y = y;
+       this->w = w;  this->h = h;
+       this->min_db = min_db;
+       this->max_db = max_db;
+       canvas = 0;
+
+       minor_divisions = 5;
+       freq_divisions = 5;
+}
+
+EQCanvas::~EQCanvas()
+{
+       delete canvas;
+}
+
+void EQCanvas::initialize()
+{
+       int big_xtick = xS(10), big_ytick = yS(10);
+       int small_xtick = xS(5), small_ytick = yS(5);
+       int tiny_xtick = xS(2);
+       int db_width = parent->get_text_width(SMALLFONT, "-00", -1) + big_xtick;
+       int freq_height = parent->get_text_ascent(SMALLFONT);
+
+       canvas_x = x + db_width;
+       canvas_y = y;
+       canvas_w = w - db_width;
+       canvas_h = h - freq_height - big_ytick;
+       parent->add_subwindow(canvas = new BC_SubWindow(
+               canvas_x, canvas_y, canvas_w, canvas_h, BLACK));
+
+// Draw canvas titles
+// DB
+       parent->set_font(SMALLFONT);
+       int ascent = parent->get_text_ascent(SMALLFONT);
+// DB per minor division
+       db_per_division = 1;
+       pixels_per_division = (float)canvas_h / (max_db - min_db) * db_per_division;
+// increase the DB per minor division until they fit
+//printf("EQCanvas::initialize %d pixels_per_division=%f\n", __LINE__, pixels_per_division);
+       while( pixels_per_division < 5 ) {
+               db_per_division *= 2;
+               pixels_per_division = (float)canvas_h / (max_db - min_db) * db_per_division;
+       }
+       total_divisions = (int)((max_db - min_db) / db_per_division);
+
+       char string[BCTEXTLEN];
+       for( int i = 0; i <= total_divisions; i++ ) {
+               int y1 = canvas_y + (int)(i * pixels_per_division);
+               int y2 = y1 + ascent;
+               int x2 = canvas_x - big_xtick;
+               int x3 = canvas_x - tiny_xtick;
+               int x4 = x3 - small_xtick;
+
+               if( !(i % minor_divisions) ||
+                       i == total_divisions ) {
+                       if( i == total_divisions ) {
+                               sprintf(string, "oo");
+                       }
+                       else {
+                               sprintf(string, "%d", (int)(max_db - i * db_per_division));
+                       }
+
+                       parent->set_color(BLACK);
+                       int text_w = parent->get_text_width(SMALLFONT, string, -1);
+                       int x1 = canvas_x - big_xtick - text_w;
+                       parent->set_color(parent->get_resources()->default_text_color);
+                       parent->draw_text(x1, y2, string);
+                       parent->draw_line(x2, y1, x3, y1);
+               }
+               else {
+                       parent->draw_line(x4, y1, x3, y1);
+               }
+       }
+
+// freq
+       for( int i = 0; i <= freq_divisions; i++ ) {
+               int freq = Freq::tofreq(i * TOTALFREQS / freq_divisions);
+               sprintf(string, "%d", freq);
+               int x1 = canvas_x + i * canvas_w / freq_divisions;
+               int x2 = x1 - parent->get_text_width(SMALLFONT, string);
+               int y1 = canvas_y + canvas_h;
+               int y2 = y1 + big_ytick;
+               int y3 = y1 + small_ytick;
+               int y4 = y2 + parent->get_text_ascent(SMALLFONT);
+
+//             parent->set_color(BLACK);
+//             parent->draw_text(x2 + 1, y4 + 1, string);
+//             parent->draw_line(x1 + 1, y1 + 1, x1 + 1, y2 + 1);
+//             parent->set_color(RED);
+               parent->set_color(parent->get_resources()->default_text_color);
+               parent->draw_text(x2, y4, string);
+               parent->draw_line(x1, y1, x1, y2);
+
+               if( i < freq_divisions ) {
+                       for( int j = 0; j < minor_divisions; j++ ) {
+                               int x3 = (int)(x1 +
+                                       (canvas_w / freq_divisions) -
+                                       exp(-(double)j * 0.7) *
+                                       (canvas_w / freq_divisions));
+//                             parent->set_color(BLACK);
+//                             parent->draw_line(x3 + 1, y1 + 1, x3 + 1, y3 + 1);
+//                             parent->set_color(RED);
+                               parent->set_color(parent->get_resources()->default_text_color);
+                               parent->draw_line(x3, y1, x3, y3);
+                       }
+               }
+       }
+
+       draw_grid();
+}
+
+
+void EQCanvas::draw_grid()
+{
+       canvas->set_line_dashes(1);
+       canvas->set_color(graph_grid_color);
+       for( int i = minor_divisions; i < total_divisions; i += minor_divisions ) {
+               int y = (int)(i * pixels_per_division);
+               canvas->draw_line(0, y, canvas_w, y);
+       }
+//     for( int i = 1; i < major_divisions; i++ ) {
+//             int y = canvas_h - i * canvas_h / major_divisions;
+//             canvas->draw_line(0, y, canvas_w, y);
+//     }
+       for( int i = 1; i < freq_divisions; i++ ) {
+               int x = i * canvas_w / freq_divisions;
+               canvas->draw_line(x, 0, x, canvas_h);
+       }
+       canvas->set_line_dashes(0);
+}
+
+void EQCanvas::update_spectrogram(CompressorFreqFrame *frame,
+               int offset, int size, int window_size)
+{
+//if( frame ) printf("EQCanvas::update_spectrogram %d frame->freq_max=%f frame->data=%p\n",
+// __LINE__, frame->freq_max, frame->data);
+       canvas->set_color(graph_bg_color);
+       canvas->draw_box(0, 0, canvas->get_w(), canvas->get_h());
+       draw_grid();
+
+// Draw it
+       if( frame && !EQUIV(frame->freq_max, 0.0) && frame->data ) {
+               int y1 = 0, y2 = 0;
+               if( offset < 0 ) {
+                       offset = 0;
+                       size = frame->data_size;
+                       window_size = frame->data_size * 2;
+               }
+
+               canvas->set_color(graph_inactive_color);
+               if( !EQUIV(frame->freq_max, 0) ) {
+                       for( int i = 0; i < canvas->get_w(); i++ ) {
+                               int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
+
+                               if( freq < frame->nyquist ) {
+                                       int index = offset +
+                                               (int64_t)freq * (int64_t)window_size / 2 /
+                                               frame->nyquist;
+
+                                       if( index < frame->data_size ) {
+                                               double magnitude = frame->data[index] /
+                                                       frame->freq_max * frame->time_max;
+
+                                               y2 = (int)(canvas->get_h() -
+                                                       (DB::todb(magnitude) - INFINITYGAIN) *
+                                                       canvas->get_h() /
+                                                       -INFINITYGAIN);
+                                               CLAMP(y2, 0, canvas->get_h() - 1);
+                                               if( i > 0 ) {
+                                                       canvas->draw_line(i - 1, y1, i, y2);
+                                               }
+                                               y1 = y2;
+                                       }
+                               }
+                               else {
+//                                      printf("EQCanvas::update_spectrogram %d i=%d freq=%d nyquist=%d\n",
+//                                              __LINE__, i, freq, frame->nyquist);
+                               }
+                       }
+               }
+       }
+
+}
+
+
+void EQCanvas::draw_envelope(double *envelope,
+               int samplerate, int window_size, int is_top, int flash_it)
+{
+       int niquist = samplerate / 2;
+
+       if( is_top ) {
+               canvas->set_color(graph_active_color);
+               canvas->set_line_width(2);
+       }
+       else {
+               canvas->set_color(graph_inactive_color);
+               canvas->set_line_width(1);
+       }
+
+       int y1;
+       for( int i = 0; i < canvas->get_w(); i++ ) {
+               int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
+               int index = (int64_t)freq * (int64_t)window_size / 2 / niquist;
+               if( freq < niquist && index < window_size / 2 ) {
+                       double mag = envelope[index];
+                       int y2 = (int)(DB::todb(mag) * canvas->get_h() / INFINITYGAIN);
+
+                       if( y2 >= canvas->get_h() ) {
+                               y2 = canvas->get_h() - 1;
+                       }
+
+                       if( i > 0 ) {
+                               canvas->draw_line(i - 1, y1, i, y2);
+                       }
+                       y1 = y2;
+               }
+               else
+               if( i > 0 ) {
+                       int y2 = canvas->get_h() - 1;
+                       canvas->draw_line(i - 1, y1, i, y2);
+                       y1 = y2;
+               }
+       }
+
+       canvas->set_line_width(1);
+       if( flash_it ) {
+               canvas->flash(1);
+       }
+}
+
diff --git a/cinelerra-5.1/cinelerra/eqcanvas.h b/cinelerra-5.1/cinelerra/eqcanvas.h
new file mode 100644 (file)
index 0000000..318c99a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2019 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
+ * 
+ */
+
+
+// A canvas for drawing a spectrogram on top of a filter envelope
+
+#ifndef EQCANVAS_H
+#define EQCANVAS_H
+
+#include "compressortools.h"
+#include "guicast.h"
+#include "pluginclient.inc"
+#include "pluginaclient.h"
+
+class EQCanvas
+{
+public:
+       EQCanvas(BC_WindowBase *parent, 
+               int x, int y, int w, int h, 
+               float min_db, float max_db);
+       virtual ~EQCanvas();
+       
+       void initialize();
+       void draw_grid();
+       void update_spectrogram(CompressorFreqFrame *frame, 
+               int offset, int size, int window_size);
+       void draw_envelope(double *envelope, 
+               int samplerate, int window_size, int is_top, int flash_it);
+       
+       BC_WindowBase *parent;
+       BC_SubWindow *canvas;
+       int x, y, w, h;
+       int canvas_x, canvas_y;
+       int canvas_w, canvas_h;
+       float min_db, max_db;
+// divisions for the frequency
+       int freq_divisions;
+// divisions for the DB
+       int db_per_division;
+       float pixels_per_division;
+       int minor_divisions;
+       int total_divisions;
+};
+
+#endif // EQCANVAS_H
diff --git a/cinelerra-5.1/cinelerra/eqcanvas.inc b/cinelerra-5.1/cinelerra/eqcanvas.inc
new file mode 100644 (file)
index 0000000..10ecc7a
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef EQCANVAS_INC
+#define EQCANVAS_INC
+
+
+
+class EQCanvas;
+
+
+
+#endif
+
+
index 2bf178a..03d8e83 100644 (file)
@@ -388,7 +388,8 @@ void FormatTools::update_extension()
                        char *ptr1 = ptr;
 // change "qt" to "mov" since ffmpeg does not know qt
                        extension_ptr = asset->format != FILE_FFMPEG ? extensions.get(0) :
-                               !strcmp(asset->fformat, "qt") ? "mov" : asset->fformat ;
+                               !strcmp(asset->fformat, "qt" ) ||
+                               !strcmp(asset->fformat, "pro" ) ? "mov" : asset->fformat ;
                        while(*extension_ptr != 0 && *extension_ptr != '/')
                                *ptr1++ = *extension_ptr++;
                        *ptr1 = 0;
index 401a5fa..f78a45b 100644 (file)
@@ -109,7 +109,7 @@ int FFT::do_fft(int samples,        // must be a power of 2
                        int j = i, k = i+1; // cos(0)=1, sin(0)=0
                        rj = real_out[j];  ij = imag_out[j];
                        rk = real_out[k];  ik = imag_out[k];
-                       real_out[j] += rk;      imag_out[j] += ik;
+                       real_out[j] += rk;        imag_out[j] += ik;
                        real_out[k] = rj - rk;  imag_out[k] = ij - ik;
                }
        }
@@ -119,12 +119,12 @@ int FFT::do_fft(int samples,      // must be a power of 2
                                int j = i, k = j+2; // cos(0)=1,sin(0)=0
                                rj = real_out[j];  ij = imag_out[j];
                                rk = real_out[k];  ik = imag_out[k];
-                               real_out[j] += rk;      imag_out[j] += ik;
+                               real_out[j] += rk;        imag_out[j] += ik;
                                real_out[k] = rj - rk;  imag_out[k] = ij - ik;
                                j = i+1;  k = j+2; // cos(-pi/2)=0, sin(-pi/2)=-1
                                rj = real_out[j];  ij = imag_out[j];
                                rk = real_out[k];  ik = imag_out[k];
-                               real_out[j] += ik;      imag_out[j] -= rk;
+                               real_out[j] += ik;        imag_out[j] -= rk;
                                real_out[k] = rj - ik;  imag_out[k] = ij + rk;
                        }
                }
@@ -133,12 +133,12 @@ int FFT::do_fft(int samples,      // must be a power of 2
                                int j = i, k = j+2; // cos(0)=1,sin(0)=0
                                rj = real_out[j];  ij = imag_out[j];
                                rk = real_out[k];  ik = imag_out[k];
-                               real_out[j] += rk;      imag_out[j] += ik;
+                               real_out[j] += rk;        imag_out[j] += ik;
                                real_out[k] = rj - rk;  imag_out[k] = ij - ik;
                                j = i+1;  k = j+2; // cos(pi/2)=0, sin(pi/2)=1
                                rj = real_out[j];  ij = imag_out[j];
                                rk = real_out[k];  ik = imag_out[k];
-                               real_out[j] -= ik;      imag_out[j] += rk;
+                               real_out[j] -= ik;        imag_out[j] += rk;
                                real_out[k] = rj + ik;  imag_out[k] = ij - rk;
                        }
                }
@@ -167,7 +167,7 @@ int FFT::do_fft(int samples,        // must be a power of 2
                                double rk = *rkp, ik = *ikp;
                                double tr = ar[0] * rk - ai[0] * ik;
                                double ti = ar[0] * ik + ai[0] * rk;
-                               *rjp++ += tr;      *ijp++ += ti;
+                               *rjp++ += tr;     *ijp++ += ti;
                                *rkp++ = rj - tr;  *ikp++ = ij - ti;
                        }
                }
@@ -204,7 +204,7 @@ unsigned int FFT::reverse_bits(unsigned int index, unsigned int bits)
 {
        unsigned int i, rev;
 
-       for(i = rev = 0; i < bits; i++) {
+       for( i = rev = 0; i < bits; i++ ) {
                rev = (rev << 1) | (index & 1);
                index >>= 1;
        }
@@ -259,8 +259,7 @@ unsigned int FFT::reverse_bits(unsigned int index, unsigned int bits)
 int FFT::symmetry(int size, double *freq_real, double *freq_imag)
 {
        int h = size / 2;
-       for(int i = h + 1; i < size; i++)
-       {
+       for( int i = h + 1; i < size; i++ ) {
                freq_real[i] = freq_real[size - i];
                freq_imag[i] = -freq_imag[size - i];
        }
@@ -273,6 +272,7 @@ int FFT::symmetry(int size, double *freq_real, double *freq_imag)
 
 CrossfadeFFT::CrossfadeFFT() : FFT()
 {
+       bands = 1;
        reset();
        window_size = 4096;
 }
@@ -288,6 +288,8 @@ int CrossfadeFFT::reset()
        output_buffer = 0;
        freq_real = 0;
        freq_imag = 0;
+       freq_real2 = 0;
+       freq_imag2 = 0;
        output_real = 0;
        output_imag = 0;
        first_window = 1;
@@ -303,12 +305,17 @@ int CrossfadeFFT::reset()
 
 int CrossfadeFFT::delete_fft()
 {
-       if(input_buffer) delete input_buffer;
-       if(output_buffer) delete [] output_buffer;
-       if(freq_real) delete [] freq_real;
-       if(freq_imag) delete [] freq_imag;
-       if(output_real) delete [] output_real;
-       if(output_imag) delete [] output_imag;
+       delete input_buffer;      input_buffer = 0;
+       if( output_buffer ) {
+               for( int i=0; i<bands; ++i ) delete [] output_buffer[i];
+               delete [] output_buffer;  output_buffer = 0;
+       }
+       delete [] freq_real;      freq_real = 0;
+       delete [] freq_imag;      freq_imag = 0;
+       delete [] freq_real2;    freq_real2 = 0;
+       delete [] freq_imag2;    freq_imag2 = 0;
+       delete [] output_real;  output_real = 0;
+       delete [] output_imag;  output_imag = 0;
        reset();
        return 0;
 }
@@ -324,9 +331,10 @@ int CrossfadeFFT::fix_window_size()
        return 0;
 }
 
-int CrossfadeFFT::initialize(int window_size)
+int CrossfadeFFT::initialize(int window_size, int bands)
 {
        this->window_size = window_size;
+       this->bands = bands;
        first_window = 1;
        reconfigure();
        return 0;
@@ -341,277 +349,172 @@ int CrossfadeFFT::reconfigure()
 {
        delete_fft();
        fix_window_size();
+       return 0;
+}
+void CrossfadeFFT::allocate_output(int new_allocation)
+{
+// Allocate output buffer
+       if( new_allocation > output_allocation ) {
+               if( !output_buffer ) {
+                       output_buffer = new double*[bands];
+                       bzero(output_buffer, sizeof(double) * bands);
+               }
 
+               for( int i = 0; i < bands; i++ ) {
+                       double *new_output = new double[new_allocation];
+                       if( output_buffer[i] ) {
+                               memcpy(new_output, output_buffer[i],
+                                       sizeof(double) * (output_size + HALF_WINDOW));
+                               delete [] output_buffer[i];
+                       }
+                       output_buffer[i] = new_output;
+               }
+               output_allocation = new_allocation;
+       }
+}
 
-
-       return 0;
+int CrossfadeFFT::process_buffer(int64_t output_sample, long size,
+               Samples *output_ptr, int direction)
+{
+       Samples *output_temp[1];
+       output_temp[0] = output_ptr;
+       return process_buffer(output_sample, size, output_temp, direction);
 }
 
-// int CrossfadeFFT::process_fifo(long size,
-//     double *input_ptr,
-//     double *output_ptr)
+// int CrossfadeFFT::get_read_offset()
 // {
-// // Load next input buffer
-//     if(input_size + size > input_allocation)
-//     {
-//             double *new_input = new double[input_size + size];
-//             if(input_buffer)
-//             {
-//                     memcpy(new_input, input_buffer, sizeof(double) * input_size);
-//                     delete [] input_buffer;
-//             }
-//             input_buffer = new_input;
-//             input_allocation = input_size + size;
-//     }
-//
-//     memcpy(input_buffer + input_size,
-//             input_ptr,
-//             size * sizeof(double));
-//     input_size += size;
-//
-//
-//
-//
-//
-//
-//
-// // Have enough to do some windows
-//     while(input_size >= window_size)
-//     {
-//             if(!freq_real) freq_real = new double[window_size];
-//             if(!freq_imag) freq_imag = new double[window_size];
-//             if(!output_real) output_real = new double[window_size];
-//             if(!output_imag) output_imag = new double[window_size];
-//
-//
-//
-//             do_fft(window_size,  // must be a power of 2
-//                     0,               // 0 = forward FFT, 1 = inverse
-//                     input_buffer,    // array of input's real samples
-//                     0,       // array of input's imag samples
-//                     freq_real,      // array of output's reals
-//                     freq_imag);
-//
-//             int result = signal_process();
-//
-//             if(!result)
-//             {
-//                     do_fft(window_size,  // must be a power of 2
-//                             1,                         // 0 = forward FFT, 1 = inverse
-//                             freq_real,       // array of input's real samples
-//                             freq_imag,       // array of input's imag samples
-//                             output_real,     // array of output's reals
-//                             output_imag);
-//             }
-//
-//
-// // Crossfade into the output buffer
-//             long new_allocation = output_size + window_size;
-//             if(new_allocation > output_allocation)
-//             {
-//                     double *new_output = new double[new_allocation];
-//
-//                     if(output_buffer)
-//                     {
-//                             memcpy(new_output, output_buffer, sizeof(double) * output_size);
-//                             delete [] output_buffer;
-//                     }
-//                     output_buffer = new_output;
-//                     output_allocation = new_allocation;
-//             }
-//
-//             if(output_size >= HALF_WINDOW)
-//             {
-//                     for(int i = 0, j = output_size - HALF_WINDOW;
-//                             i < HALF_WINDOW;
-//                             i++, j++)
-//                     {
-//                             double src_level = (double)i / HALF_WINDOW;
-//                             double dst_level = (double)(HALF_WINDOW - i) / HALF_WINDOW;
-//                             output_buffer[j] = output_buffer[j] * dst_level + output_real[i] * src_level;
-//                     }
-//
-//                     memcpy(output_buffer + output_size,
-//                             output_real + HALF_WINDOW,
-//                             sizeof(double) * (window_size - HALF_WINDOW));
-//                     output_size += window_size - HALF_WINDOW;
-//             }
-//             else
-//             {
-// // First buffer has no crossfade
-//                     memcpy(output_buffer + output_size,
-//                             output_real,
-//                             sizeof(double) * window_size);
-//                     output_size += window_size;
-//             }
-//
-//
-// // Shift input buffer forward
-//             for(int i = window_size - HALF_WINDOW, j = 0;
-//                     i < input_size;
-//                     i++, j++)
-//                     input_buffer[j] = input_buffer[i];
-//             input_size -= window_size - HALF_WINDOW;
-//     }
-//
-//
-//
-//
-// // Have enough to send to output
-//     int samples_rendered = 0;
-//     if(output_size - HALF_WINDOW >= size)
-//     {
-//             memcpy(output_ptr, output_buffer, sizeof(double) * size);
-//             for(int i = size, j = 0; i < output_size; i++, j++)
-//                     output_buffer[j] = output_buffer[i];
-//             output_size -= size;
-//             samples_rendered = size;
-//     }
-//     else
-//     {
-//             bzero(output_ptr, sizeof(double) * size);
-//     }
-//
-//     return samples_rendered;
 // }
 
-
-
 int CrossfadeFFT::process_buffer(int64_t output_sample,
-       long size,
-       Samples *output_ptr,
-       int direction)
+               long size, Samples **output_ptr, int direction)
 {
        int result = 0;
        int step = (direction == PLAY_FORWARD) ? 1 : -1;
 
 // User seeked so output buffer is invalid
-       if(output_sample != this->output_sample)
-       {
+       if( output_sample != this->output_sample ) {
                output_size = 0;
                input_size = 0;
                first_window = 1;
                this->output_sample = output_sample;
-               this->input_sample = output_sample;
+               input_sample = output_sample;
        }
 
-// Fill output buffer half a window at a time until size samples are available
-       while(output_size < size)
-       {
-               if(!input_buffer) input_buffer = new Samples(window_size);
-               if(!freq_real) freq_real = new double[window_size];
-               if(!freq_imag) freq_imag = new double[window_size];
-               if(!output_real) output_real = new double[window_size];
-               if(!output_imag) output_imag = new double[window_size];
-
-// Fill enough input to make a window starting at output_sample
-               if(first_window)
-                       result = read_samples(this->input_sample,
-                               window_size,
-                               input_buffer);
-               else
-               {
-                       input_buffer->set_offset(HALF_WINDOW);
-// printf("CrossfadeFFT::process_buffer %d %lld %lld\n",
-// __LINE__,
-// this->input_sample + step * HALF_WINDOW,
-// this->input_sample + step * HALF_WINDOW + HALF_WINDOW);
-                       result = read_samples(this->input_sample + step * HALF_WINDOW,
-                               HALF_WINDOW,
-                               input_buffer);
-                       input_buffer->set_offset(0);
-               }
 
-               input_size = window_size;
-
-               if(!result)
-                       do_fft(window_size,   // must be a power of 2
-                               0,                              // 0 = forward FFT, 1 = inverse
-                               input_buffer->get_data(),        // array of input's real samples
-                               0,                              // array of input's imag samples
-                               freq_real,              // array of output's reals
-                               freq_imag);
-               if(!result)
-                       result = signal_process();
-
-               if(!result)
-                       do_fft(window_size,  // must be a power of 2
-                               1,                         // 0 = forward FFT, 1 = inverse
-                               freq_real,         // array of input's real samples
-                               freq_imag,         // array of input's imag samples
-                               output_real,       // array of output's reals
-                               output_imag);     // array of output's imaginaries
-
-               if(!result)
-                       result = post_process();
+// printf("CrossfadeFFT::process_buffer %d size=%ld input_size=%ld output_size=%ld window_size=%ld\n",
+// __LINE__, size, input_size, output_size, window_size);
 
-// Allocate output buffer
-               int new_allocation = output_size + window_size;
-               if(new_allocation > output_allocation)
-               {
-                       double *new_output = new double[new_allocation];
-                       if(output_buffer)
-                       {
-                               memcpy(new_output,
-                                       output_buffer,
-                                       sizeof(double) * (output_size + HALF_WINDOW));
-                               delete [] output_buffer;
-                       }
-                       output_buffer = new_output;
-                       output_allocation = new_allocation;
+// must call read_samples once so the upstream plugins don't have to seek
+// must be a multiple of 1/2 window
+       int need_samples = (size - output_size) / HALF_WINDOW * HALF_WINDOW;
+// round up a half window
+       if( need_samples + output_size < size ) {
+               need_samples += HALF_WINDOW;
+       }
+// half window tail
+       need_samples += HALF_WINDOW;
+
+// extend the buffer to need_samples
+       if( !input_buffer || input_buffer->get_allocated() < need_samples ) {
+               Samples *new_input_buffer = new Samples(need_samples);
+
+               if( input_buffer ) {
+                       memcpy(new_input_buffer->get_data(),
+                               input_buffer->get_data(),
+                               input_size * sizeof(double));
+                       delete input_buffer;
                }
 
-// Overlay processed buffer
-               if(first_window)
-               {
-                       memcpy(output_buffer + output_size,
-                               output_real,
-                               sizeof(double) * window_size);
-                       first_window = 0;
-               }
-               else
-               {
-                       for(int i = 0, j = output_size; i < HALF_WINDOW; i++, j++)
-                       {
-                               double src_level = (double)i / HALF_WINDOW;
-                               double dst_level = (double)(HALF_WINDOW - i) / HALF_WINDOW;
-                               output_buffer[j] = output_buffer[j] * dst_level +
-                                       output_real[i] * src_level;
+               input_buffer = new_input_buffer;
+       }
+
+       input_buffer->set_offset(input_size);
+       result = read_samples(input_sample, need_samples-input_size, input_buffer);
+       input_buffer->set_offset(0);
+       input_sample += step * (need_samples - input_size);
+       input_size = need_samples;
+
+
+       if( !freq_real ) freq_real = new double[window_size];
+       if( !freq_imag ) freq_imag = new double[window_size];
+       if( !freq_real2 ) freq_real2 = new double[window_size];
+       if( !freq_imag2 ) freq_imag2 = new double[window_size];
+       if( !output_real ) output_real = new double[window_size];
+       if( !output_imag ) output_imag = new double[window_size];
+
+// Fill output buffer half a window at a time until size samples are available
+       while( !result && output_size < size ) {
+               do_fft(window_size, 0,  // forward
+                       input_buffer->get_data(), 0, // input, real only
+                       freq_real2, freq_imag2); // output
+               allocate_output(output_size + window_size);
+// process & overlay each band separately
+               for( int band=0; band<bands; ++band ) {
+// restore from the backup
+                       memcpy(freq_real, freq_real2, sizeof(double) * window_size);
+                       memcpy(freq_imag, freq_imag2, sizeof(double) * window_size);
+                       signal_process(band);
+                       do_fft(window_size, 1,  // inverse
+                               freq_real, freq_imag, // input
+                               output_real, output_imag); // output
+                       post_process(band);
+
+// Overlay processed window on the output buffers
+                       if( first_window ) {
+// direct copy the 1st window
+                               memcpy(output_buffer[band] + output_size,
+                                       output_real,
+                                       sizeof(double) * window_size);
                        }
+                       else {
+// dissolve 1st half of later windows
+                               for( int i = 0, j = output_size; i < HALF_WINDOW; i++, j++ ) {
+                                       double src_level = (double)i / HALF_WINDOW;
+                                       double dst_level = (double)(HALF_WINDOW - i) / HALF_WINDOW;
+                                       output_buffer[band][j] = output_buffer[band][j] * dst_level +
+                                               output_real[i] * src_level;
+                               }
 
 //output_buffer[output_size] = 100.0;
 //output_buffer[output_size + HALF_WINDOW] = -100.0;
-
-                       memcpy(output_buffer + output_size + HALF_WINDOW,
-                               output_real + HALF_WINDOW,
-                               sizeof(double) * HALF_WINDOW);
+// copy 2nd half of window
+                               memcpy(output_buffer[band] + output_size + HALF_WINDOW,
+                                       output_real + HALF_WINDOW,
+                                       sizeof(double) * HALF_WINDOW);
+                       }
                }
 
-
+               first_window = 0;
                output_size += HALF_WINDOW;
 
-// Shift input buffer
-               double *input_samples = input_buffer->get_data();
-               for(int i = window_size - HALF_WINDOW, j = 0;
-                       i < input_size;
-                       i++, j++)
-               {
-                       input_samples[j] = input_samples[i];
-               }
+// Shift input buffer half a window forward
+               memcpy(input_buffer->get_data(),
+                       input_buffer->get_data() + HALF_WINDOW,
+                       (input_size - HALF_WINDOW) * sizeof(double));
+//             for( int i = HALF_WINDOW, j = 0;
+//                     i < input_size;
+//                     i++, j++ )
+//             {
+//                     input_buffer->get_data()[j] = input_buffer->get_data()[i];
+//             }
 
-               input_size = HALF_WINDOW;
-               this->input_sample += step * HALF_WINDOW;
+               input_size -= HALF_WINDOW;
        }
 
 
 
-// Transfer output buffer
-       if(output_ptr)
-       {
-               memcpy(output_ptr->get_data(), output_buffer, sizeof(double) * size);
+// Transfer output buffer if the user wants it
+       if( output_ptr ) {
+               for( int band = 0; band < bands; band++ ) {
+                       memcpy(output_ptr[band]->get_data(), output_buffer[band], sizeof(double) * size);
+               }
+       }
+
+// shift output buffers forward
+       for( int band = 0; band < bands; band++ ) {
+               memcpy(output_buffer[band], output_buffer[band] + size,
+                       sizeof(double) * (output_size + HALF_WINDOW - size));
        }
-       for(int i = 0, j = size; j < output_size + HALF_WINDOW; i++, j++)
-               output_buffer[i] = output_buffer[j];
 
        this->output_sample += step * size;
        this->output_size -= size;
@@ -621,8 +524,7 @@ int CrossfadeFFT::process_buffer(int64_t output_sample,
 
 
 int CrossfadeFFT::read_samples(int64_t output_sample,
-               int samples,
-               Samples *buffer)
+               int samples, Samples *buffer)
 {
        return 1;
 }
@@ -640,9 +542,18 @@ int CrossfadeFFT::post_process()
 }
 
 
+int CrossfadeFFT::signal_process(int band)
+{
+       signal_process();
+       return 0;
+}
 
 
 
-
+int CrossfadeFFT::post_process(int band)
+{
+       post_process();
+       return 0;
+}
 
 
index 482c6fb..9b83153 100644 (file)
@@ -33,16 +33,16 @@ class FFT
        static uint8_t rev_bytes[256];
        static unsigned int reverse_bits(unsigned int index, unsigned int bits);
        static void bit_reverse(unsigned int samples,
-               double *real_in, double *imag_in,
-               double *real_out, double *imag_out);
+                       double *real_in, double *imag_in,
+                       double *real_out, double *imag_out);
 public:
        FFT();
        virtual ~FFT();
        // can be in place, but imag_out must exist, imag_in optional
        int do_fft(int samples, // must be a power of 2
                int inverse,             // 0 = forward FFT, 1 = inverse
-               double *real_in, double *imag_in,       // complex input
-               double *real_out, double *imag_out);    // complex output
+                       double *real_in, double *imag_in,       // complex input
+                       double *real_out, double *imag_out);    // complex output
        int do_fft(int samples, int inverse, double *real, double *imag) {
                return do_fft(samples, inverse, real, imag, real, imag);
        }
@@ -58,8 +58,8 @@ public:
        virtual ~CrossfadeFFT();
 
        int reset();
-       int initialize(int window_size);
-       long get_delay();     // Number of samples fifo is delayed
+       int initialize(int window_size, int bands = 1);
+       long get_delay();        // Number of samples fifo is delayed
        int reconfigure();
        int fix_window_size();
        int delete_fft();
@@ -69,28 +69,33 @@ public:
 // output_sample - tells it if we've seeked and the output overflow is invalid.
 // return - 0 on success 1 on failure
 // output_sample - start of samples if forward.  End of samples if reverse.
-//               It's always contiguous.
+//                        It's always contiguous.
 // output_ptr - if nonzero, output is put here
 // direction - PLAY_FORWARD or PLAY_REVERSE
        int process_buffer(int64_t output_sample,
-               long size,
-               Samples *output_ptr,
-               int direction);
+               long size, Samples *output_ptr, int direction);
+// multiband processing
+       int process_buffer(int64_t output_sample,
+               long size, Samples **output_ptr, int direction);
 
 // Called by process_buffer to read samples from input.
 // Returns 1 on error or 0 on success.
        virtual int read_samples(int64_t output_sample,
-               int samples,
-               Samples *buffer);
+               int samples, Samples *buffer);
 
 // Process a window in the frequency domain
        virtual int signal_process();
 // Process a window in the time domain after the frequency domain
        virtual int post_process();
+// Multiband versions
+       virtual int signal_process(int band);
+       virtual int post_process(int band);
 
 // Size of a window.  Automatically fixed to a power of 2
        long window_size;
 
+// Time domane input of complete windows
+       Samples *input_buffer;
 // Frequency domain output of FFT
        double *freq_real;
        double *freq_imag;
@@ -99,13 +104,16 @@ public:
        double *output_imag;
 
 private:
+       void allocate_output(int new_allocation);
 
-// input for complete windows
-       Samples *input_buffer;
-// output for crossfaded windows with overflow
-       double *output_buffer;
+// output of crossfaded windows with overflow.  1 buffer for each band
+       double **output_buffer;
+
+// backup frequency domain for multiband
+       double *freq_real2;
+       double *freq_imag2;
 
-// samples in input_buffer
+// samples in input_buffer including the tail
        long input_size;
 // window_size
        long input_allocation;
@@ -119,6 +127,7 @@ private:
        int64_t input_sample;
 // Don't crossfade the first window
        int first_window;
+       int bands;
 };
 
 #endif
index 06e5384..698c4e8 100644 (file)
@@ -72,6 +72,7 @@ class MainTransport : public PlayTransport
 {
 public:
        MainTransport(MWindow *mwindow, MButtons *mbuttons, int x, int y);
+       bool use_mixers() { return true; }
        void goto_start();
        void goto_end();
 };
index 03e265e..e3bf6b6 100644 (file)
@@ -3457,11 +3457,12 @@ int MWindow::plugin_gui_open(Plugin *plugin)
        return result;
 }
 
+
 void MWindow::render_plugin_gui(void *data, Plugin *plugin)
 {
        int gui_id = plugin->gui_id;
        if( gui_id < 0 ) return;
-       plugin_gui_lock->lock("MWindow::render_plugin_gui");
+       plugin_gui_lock->lock("MWindow::render_plugin_gui 0");
        PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
        if( plugin_server )
                plugin_server->render_gui(data);
@@ -3472,13 +3473,44 @@ void MWindow::render_plugin_gui(void *data, int size, Plugin *plugin)
 {
        int gui_id = plugin->gui_id;
        if( gui_id < 0 ) return;
-       plugin_gui_lock->lock("MWindow::render_plugin_gui");
+       plugin_gui_lock->lock("MWindow::render_plugin_gui 1");
        PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
        if( plugin_server )
                plugin_server->render_gui(data, size);
        plugin_gui_lock->unlock();
 }
 
+void MWindow::reset_plugin_gui_frames(Plugin *plugin)
+{
+       int gui_id = plugin->gui_id;
+       if( gui_id < 0 ) return;
+       plugin_gui_lock->lock("MWindow::reset_plugin_gui_frames");
+       PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
+       if( plugin_server )
+               plugin_server->reset_plugin_gui_frames();
+       plugin_gui_lock->unlock();
+}
+
+void MWindow::render_plugin_gui_frames(PluginClientFrames *frames, Plugin *plugin)
+{
+       int gui_id = plugin->gui_id;
+       if( gui_id < 0 ) return;
+       plugin_gui_lock->lock("MWindow::render_plugin_gui_frames");
+       PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
+       if( plugin_server )
+               plugin_server->render_plugin_gui_frames(frames);
+       plugin_gui_lock->unlock();
+}
+
+double MWindow::get_tracking_position()
+{
+       return edl->local_session->get_selectionstart(1);
+}
+
+int MWindow::get_tracking_direction()
+{
+       return cwindow->playback_engine->get_direction();
+}
 
 void MWindow::update_plugin_states()
 {
index 513e1bf..b0e7877 100644 (file)
@@ -72,6 +72,7 @@
 #include "playback3d.inc"
 #include "playbackengine.inc"
 #include "plugin.inc"
+#include "pluginclient.inc"
 #include "pluginfclient.inc"
 #include "pluginserver.inc"
 #include "pluginset.inc"
@@ -304,6 +305,10 @@ public:
 // Searches for matching plugin and renders data in it.
        void render_plugin_gui(void *data, Plugin *plugin);
        void render_plugin_gui(void *data, int size, Plugin *plugin);
+       void reset_plugin_gui_frames(Plugin *plugin);
+       void render_plugin_gui_frames(PluginClientFrames *frames, Plugin *plugin);
+       double get_tracking_position();
+       int get_tracking_direction();
 
 // Called from PluginVClient::process_buffer
 // Returns 1 if a GUI for the plugin is open so OpenGL routines can determine if
index d9b636d..f95f575 100644 (file)
@@ -452,6 +452,11 @@ void PlaybackEngine::stop_playback(int wait_tracking)
        renderengine_lock->unlock();
 }
 
+int PlaybackEngine::get_direction()
+{
+       int curr_command = is_playing_back ? this->command->command : STOP;
+       return TransportCommand::get_direction(curr_command);
+}
 
 void PlaybackEngine::send_command(int command, EDL *edl, int wait_tracking, int use_inout)
 {
index ed646b0..c82d350 100644 (file)
@@ -85,6 +85,7 @@ public:
        void send_command(int command, EDL *edl, int wait_tracking, int use_inout);
        void stop_playback(int wait);
        void refresh_frame(int change_type, EDL *edl, int dir=1);
+       int get_direction();
 
 // Maintain caches through console changes
        CICache *audio_cache, *video_cache;
index 0690da0..d9d9a48 100644 (file)
@@ -315,7 +315,7 @@ void PlayTransport::handle_transport(int command, int wait_tracking,
        if( !edl ) return;
        using_inout = use_inout;
 
-       if( !is_vwindow() )
+       if( use_mixers() )
                mwindow->handle_mixers(edl, command, wait_tracking,
                                use_inout, toggle_audio, 0, speed);
        engine->next_command->toggle_audio = toggle_audio;
index 330a958..20d9899 100644 (file)
@@ -69,7 +69,7 @@ public:
 // Get the EDL to play back with default to mwindow->edl
        virtual EDL* get_edl();
        void change_position(double position);
-       virtual int is_vwindow() { return 0; }
+       virtual bool use_mixers() { return false; }
 
 // playback parameters
        int reverse;
index 8601bc0..b1434b0 100644 (file)
  *
  */
 
+#include "aattachmentpoint.h"
+#include "audiodevice.h"
 #include "edl.h"
 #include "edlsession.h"
 #include "pluginaclient.h"
 #include "pluginserver.h"
+#include "renderengine.h"
 #include "samples.h"
 
 #include <string.h>
@@ -32,15 +35,11 @@ PluginAClient::PluginAClient(PluginServer *server)
  : PluginClient(server)
 {
        sample_rate = 0;
-       if(server &&
-               server->edl &&
-               server->edl->session)
-       {
+       if(server && server->edl && server->edl->session) {
                project_sample_rate = server->edl->session->sample_rate;
                sample_rate = project_sample_rate;
        }
-       else
-       {
+       else {
                project_sample_rate = 1;
                sample_rate = 1;
        }
@@ -92,54 +91,47 @@ int PluginAClient::process_realtime(int64_t size,
 }
 
 int PluginAClient::process_realtime(int64_t size,
-       Samples *input_ptr,
-       Samples *output_ptr)
+       Samples *input_ptr, Samples *output_ptr)
 {
        return 0;
 }
 
-int PluginAClient::process_buffer(int64_t size,
-       Samples **buffer,
-       int64_t start_position,
-       int sample_rate)
+int PluginAClient::process_buffer(int64_t size, Samples **buffer,
+               int64_t start_position, int sample_rate)
 {
        for(int i = 0; i < PluginClient::total_in_buffers; i++)
-               read_samples(buffer[i],
-                       i,
-                       sample_rate,
-                       source_position,
-                       size);
+               read_samples(buffer[i], i, sample_rate, source_position, size);
        process_realtime(size, buffer, buffer);
        return 0;
 }
 
-int PluginAClient::process_buffer(int64_t size,
-       Samples *buffer,
-       int64_t start_position,
-       int sample_rate)
+int PluginAClient::process_buffer(int64_t size, Samples *buffer,
+               int64_t start_position, int sample_rate)
 {
-       read_samples(buffer,
-               0,
-               sample_rate,
-               source_position,
-               size);
+       read_samples(buffer, 0, sample_rate, source_position, size);
        process_realtime(size, buffer, buffer);
        return 0;
 }
 
 
+void PluginAClient::begin_process_buffer()
+{
+       frame_buffer.destroy();
+}
+
+void PluginAClient::end_process_buffer()
+{
+       if( !frame_buffer.first ) return;
+       server->render_gui_frames(&frame_buffer);
+}
 
 
-int PluginAClient::plugin_start_loop(int64_t start,
-       int64_t end,
-       int64_t buffer_size,
-       int total_buffers)
+int PluginAClient::plugin_start_loop(int64_t start, int64_t end,
+               int64_t buffer_size, int total_buffers)
 {
        sample_rate = get_project_samplerate();
-       return PluginClient::plugin_start_loop(start,
-               end,
-               buffer_size,
-               total_buffers);
+       return PluginClient::plugin_start_loop(start, end,
+               buffer_size, total_buffers);
 }
 
 int PluginAClient::plugin_get_parameters()
@@ -152,68 +144,44 @@ int PluginAClient::plugin_get_parameters()
 int64_t PluginAClient::local_to_edl(int64_t position)
 {
        if(position < 0) return position;
-       return (int64_t)(position *
-               get_project_samplerate() /
-               sample_rate);
+       return (int64_t)(position * get_project_samplerate() / sample_rate);
        return 0;
 }
 
 int64_t PluginAClient::edl_to_local(int64_t position)
 {
        if(position < 0) return position;
-       return (int64_t)(position *
-               sample_rate /
-               get_project_samplerate());
+       return (int64_t)(position * sample_rate / get_project_samplerate());
 }
 
-
 int PluginAClient::plugin_process_loop(Samples **buffers, int64_t &write_length)
 {
        write_length = 0;
-
-       if(is_multichannel())
-               return process_loop(buffers, write_length);
-       else
-               return process_loop(buffers[0], write_length);
+       return is_multichannel() ?
+               process_loop(buffers, write_length) :
+               process_loop(buffers[0], write_length);
 }
 
 int PluginAClient::read_samples(Samples *buffer,
-       int channel,
-       int64_t start_position,
-       int64_t total_samples)
+               int channel, int64_t start_position, int64_t total_samples)
 {
-       return server->read_samples(buffer,
-               channel,
-               start_position,
-               total_samples);
+       return server->read_samples(buffer, channel, start_position, total_samples);
 }
 
 int PluginAClient::read_samples(Samples *buffer,
-       int64_t start_position,
-       int64_t total_samples)
+               int64_t start_position, int64_t total_samples)
 {
-       return server->read_samples(buffer,
-               0,
-               start_position,
-               total_samples);
+       return server->read_samples(buffer, 0, start_position, total_samples);
 }
 
 int PluginAClient::read_samples(Samples *buffer,
-               int channel,
-               int sample_rate,
-               int64_t start_position,
-               int64_t len)
+               int channel, int sample_rate, int64_t start_position, int64_t len)
 {
        return server->read_samples(buffer,
-               channel,
-               sample_rate,
-               start_position,
-               len);
+               channel, sample_rate, start_position, len);
 }
 
 
-
-
 int PluginAClient::get_project_samplerate()
 {
        return project_sample_rate;
@@ -224,6 +192,11 @@ int PluginAClient::get_samplerate()
        return sample_rate;
 }
 
+Samples* PluginAClient::get_output(int channel)
+{
+       return output_buffers[channel];
+}
+
 
 int64_t PluginAClient::get_startproject()
 {
index b0761d0..e9b4385 100644 (file)
@@ -41,12 +41,10 @@ public:
 // These should return 1 if error or 0 if success.
 // Multichannel buffer process for backwards compatibility
        virtual int process_realtime(int64_t size,
-               Samples **input_ptr,
-               Samples **output_ptr);
+               Samples **input_ptr, Samples **output_ptr);
 // Single channel buffer process for backwards compatibility and transitions
        virtual int process_realtime(int64_t size,
-               Samples *input_ptr,
-               Samples *output_ptr);
+               Samples *input_ptr, Samples *output_ptr);
 
 // Process buffer using pull method.  By default this loads the input into the
 // buffer and calls process_realtime with input and output pointing to buffer.
@@ -54,23 +52,20 @@ public:
 //     to start of EDL.  End of buffer if reverse.
 // sample_rate - scale of start_position.
        virtual int process_buffer(int64_t size,
-               Samples **buffer,
-               int64_t start_position,
-               int sample_rate);
+               Samples **buffer, int64_t start_position, int sample_rate);
        virtual int process_buffer(int64_t size,
-               Samples *buffer,
-               int64_t start_position,
-               int sample_rate);
+               Samples *buffer, int64_t start_position, int sample_rate);
 
+// process render_gui frames via add_gui_frame
+       virtual void begin_process_buffer();
+       virtual void end_process_buffer();
 
        virtual int process_loop(Samples *buffer, int64_t &write_length) { return 1; };
        virtual int process_loop(Samples **buffers, int64_t &write_length) { return 1; };
        int plugin_process_loop(Samples **buffers, int64_t &write_length);
 
-       int plugin_start_loop(int64_t start,
-               int64_t end,
-               int64_t buffer_size,
-               int total_buffers);
+       int plugin_start_loop(int64_t start, int64_t end,
+               int64_t buffer_size, int total_buffers);
 
        int plugin_get_parameters();
 
@@ -92,20 +87,21 @@ public:
 // sample_rate - scale of start_position.  Provided so the client can get data
 //     at a higher fidelity than provided by the EDL.
        int read_samples(Samples *buffer,
-               int channel,
-               int sample_rate,
-               int64_t start_position,
-               int64_t len);
+               int channel, int sample_rate, int64_t start_position, int64_t len);
 
 // Get the sample rate of the EDL
        int get_project_samplerate();
 // Get the requested sample rate
        int get_samplerate();
+       Samples* get_output(int channel);
+       int64_t get_startproject();
+       int64_t get_endproject();
 
        int64_t local_to_edl(int64_t position);
        int64_t edl_to_local(int64_t position);
-       int64_t get_startproject();
-       int64_t get_endproject();
+
+// the buffers passed to process_buffer
+       Samples **output_buffers;
 
 // point to the start of the buffers
        ArrayList<float**> input_ptr_master;
index da9a697..dcafd89 100644 (file)
@@ -22,6 +22,7 @@
 #include "bcdisplayinfo.h"
 #include "bchash.h"
 #include "bcsignals.h"
+#include "attachmentpoint.h"
 #include "clip.h"
 #include "condition.h"
 #include "edits.h"
@@ -40,8 +41,9 @@
 #include "pluginclient.h"
 #include "pluginserver.h"
 #include "preferences.h"
+#include "renderengine.h"
 #include "track.h"
-#include "transportque.inc"
+#include "transportque.h"
 
 #include <stdio.h>
 #include <unistd.h>
 #include <ctype.h>
 #include <errno.h>
 
+PluginClientFrame::PluginClientFrame()
+{
+       position = -1;
+}
+
+PluginClientFrame::~PluginClientFrame()
+{
+}
+
 
 PluginClientThread::PluginClientThread(PluginClient *client)
  : Thread(1, 0, 0)
@@ -63,9 +74,7 @@ PluginClientThread::PluginClientThread(PluginClient *client)
 PluginClientThread::~PluginClientThread()
 {
        join();
-//printf("PluginClientThread::~PluginClientThread %p %d\n", this, __LINE__);
-       delete window;  window = 0;
-//printf("PluginClientThread::~PluginClientThread %p %d\n", this, __LINE__);
+       delete window;
        delete init_complete;
 }
 
@@ -120,29 +129,6 @@ PluginClient* PluginClientThread::get_client()
 }
 
 
-
-
-
-
-PluginClientFrame::PluginClientFrame(int data_size,
-       int period_n,
-       int period_d)
-{
-       this->data_size = data_size;
-       force = 0;
-       this->period_n = period_n;
-       this->period_d = period_d;
-}
-
-PluginClientFrame::~PluginClientFrame()
-{
-
-}
-
-
-
-
-
 PluginClientWindow::PluginClientWindow(PluginClient *client,
        int w, int h, int min_w, int min_h, int allow_resize)
  : BC_Window(client->gui_string,
@@ -421,9 +407,6 @@ int PluginText::handle_event()
 }
 
 
-
-
-
 PluginClient::PluginClient(PluginServer *server)
 {
        reset();
@@ -444,7 +427,6 @@ PluginClient::~PluginClient()
 
 // Virtual functions don't work here.
        if(defaults) delete defaults;
-       frame_buffer.remove_all_objects();
        delete update_timer;
 }
 
@@ -553,7 +535,6 @@ int PluginClient::is_multichannel() { return 0; }
 int PluginClient::is_synthesis() { return 0; }
 int PluginClient::is_realtime() { return 0; }
 int PluginClient::is_fileio() { return 0; }
-int PluginClient::delete_buffer_ptrs() { return 0; }
 const char* PluginClient::plugin_title() { return _("Untitled"); }
 
 Theme* PluginClient::new_theme() { return 0; }
@@ -606,90 +587,114 @@ int PluginClient::set_string()
 
 
 
-void PluginClient::begin_process_buffer()
+PluginClientFrames::PluginClientFrames()
+{
+       count = 0;
+}
+PluginClientFrames::~PluginClientFrames()
 {
-// Delete all unused GUI frames
-       frame_buffer.remove_all_objects();
 }
 
+int PluginClientFrames::fwd_cmpr(PluginClientFrame *a, PluginClientFrame *b)
+{
+       double d = a->position - b->position;
+       return d < 0 ? -1 : !d ? 0 : 1;
+}
 
-void PluginClient::end_process_buffer()
+int PluginClientFrames::rev_cmpr(PluginClientFrame *a, PluginClientFrame *b)
 {
-       if(frame_buffer.size())
-       {
-               send_render_gui();
-       }
+       double d = b->position - a->position;
+       return d < 0 ? -1 : !d ? 0 : 1;
 }
 
+void PluginClientFrames::reset()
+{
+       destroy();
+       count = 0;
+}
 
+void PluginClientFrames::add_gui_frame(PluginClientFrame *frame)
+{
+       append(frame);
+       ++count;
+}
 
-void PluginClient::plugin_update_gui()
+void PluginClientFrames::concatenate(PluginClientFrames *frames)
 {
+       concat(*frames);
+       count += frames->count;
+       frames->count = 0;
+}
 
-       update_gui();
+void PluginClientFrames::sort_position(int dir)
+{
+// enforce order
+       if( dir == PLAY_REVERSE )
+               rev_sort();
+       else
+               fwd_sort();
+}
 
-// Delete unused GUI frames
-       while(frame_buffer.size() > MAX_FRAME_BUFFER)
-               frame_buffer.remove_object_number(0);
+// pop frames until buffer passes position=pos in direction=dir
+// dir==0, pop frame; pos<0, pop all frames
+// delete past frames, return last popped frame
+PluginClientFrame* PluginClientFrames::get_gui_frame(double pos, int dir)
+{
+       if( dir ) {
+               while( first != last ) {
+                       if( pos >= 0 && dir*(first->next->position - pos) > 0 ) break;
+                       delete first;  --count;
+               }
+       }
+       PluginClientFrame *frame = first;
+       if( frame ) { remove_pointer(frame);  --count; }
+       return frame;
+}
 
+PluginClientFrame* PluginClient::get_gui_frame(double pos, int dir)
+{
+       return frame_buffer.get_gui_frame(pos, dir);
+}
+PluginClientFrame* PluginClient::next_gui_frame()
+{
+       return frame_buffer.first;
 }
 
-void PluginClient::update_gui()
+
+void PluginClient::plugin_update_gui()
 {
+       update_gui();
 }
 
-int PluginClient::get_gui_update_frames()
+void PluginClient::update_gui()
 {
-       if(frame_buffer.size())
-       {
-               PluginClientFrame *frame = frame_buffer.get(0);
-               int total_frames = update_timer->get_difference() *
-                       frame->period_d /
-                       frame->period_n /
-                       1000;
-               if(total_frames) update_timer->subtract(total_frames *
-                       frame->period_n *
-                       1000 /
-                       frame->period_d);
-
-// printf("PluginClient::get_gui_update_frames %d %ld %d %d %d\n",
-// __LINE__,
-// update_timer->get_difference(),
-// frame->period_n * 1000 / frame->period_d,
-// total_frames,
-// frame_buffer.size());
-
-// Add forced frames
-               for(int i = 0; i < frame_buffer.size(); i++)
-                       if(frame_buffer.get(i)->force) total_frames++;
-               total_frames = MIN(frame_buffer.size(), total_frames);
-
-
-               return total_frames;
-       }
-       else
-       {
-               return 0;
-       }
 }
 
-PluginClientFrame* PluginClient::get_gui_frame()
+int PluginClient::pending_gui_frames()
 {
-       if(frame_buffer.size())
-       {
-               PluginClientFrame *frame = frame_buffer.get(0);
-               frame_buffer.remove_number(0);
-               return frame;
-       }
-       else
-       {
-               return 0;
-       }
+       PluginClientFrame *frame = frame_buffer.first;
+       if( !frame ) return 0;
+       double tracking_position = get_tracking_position();
+       int direction = get_tracking_direction();
+       int ret = !(direction == PLAY_REVERSE ?
+               frame->position < tracking_position :
+               frame->position > tracking_position);
+       return ret;
 }
 
 void PluginClient::add_gui_frame(PluginClientFrame *frame)
 {
-       frame_buffer.append(frame);
+       frame_buffer.add_gui_frame(frame);
+}
+
+double PluginClient::get_tracking_position()
+{
+       return server->mwindow->get_tracking_position();
+}
+
+int PluginClient::get_tracking_direction()
+{
+       return server->mwindow->get_tracking_direction();
 }
 
 void PluginClient::send_render_gui()
@@ -707,54 +712,56 @@ void PluginClient::send_render_gui(void *data, int size)
        server->send_render_gui(data, size);
 }
 
-void PluginClient::plugin_render_gui(void *data, int size)
+
+void PluginClient::plugin_reset_gui_frames()
 {
-       render_gui(data, size);
+       if( !thread ) return;
+       BC_WindowBase *window = thread->get_window();
+       if( !window ) return;
+       window->lock_window("PluginClient::plugin_reset_gui_frames");
+       frame_buffer.reset();
+       window->unlock_window();
 }
 
+void PluginClient::plugin_render_gui_frames(PluginClientFrames *frames)
+{
+       if( !thread ) return;
+       BC_WindowBase *window = thread->get_window();
+       if( !window ) return;
+       window->lock_window("PluginClient::render_gui");
+       while( frame_buffer.count > MAX_FRAME_BUFFER )
+               delete get_gui_frame(0, 0);
+// append client frames to gui frame_buffer, consumes frames
+       frame_buffer.concatenate(frames);
+       frame_buffer.sort_position(get_tracking_direction());
+       update_timer->update();
+       window->unlock_window();
+}
 
 void PluginClient::plugin_render_gui(void *data)
 {
        render_gui(data);
 }
 
-void PluginClient::render_gui(void *data)
+void PluginClient::plugin_render_gui(void *data, int size)
 {
-       if(thread)
-       {
-               thread->get_window()->lock_window("PluginClient::render_gui");
-
-// Set all previous frames to draw immediately
-               for(int i = 0; i < frame_buffer.size(); i++)
-                       frame_buffer.get(i)->force = 1;
-
-               ArrayList<PluginClientFrame*> *src =
-                       (ArrayList<PluginClientFrame*>*)data;
-
-// Shift GUI data to GUI client
-               while(src->size())
-               {
-                       this->frame_buffer.append(src->get(0));
-                       src->remove_number(0);
-               }
+       render_gui(data, size);
+}
 
-// Start the timer for the current buffer
-               update_timer->update();
-               thread->get_window()->unlock_window();
-       }
+void PluginClient::render_gui(void *data)
+{
+        printf("PluginClient::render_gui %d\n", __LINE__);
 }
 
 void PluginClient::render_gui(void *data, int size)
 {
-       printf("PluginClient::render_gui %d\n", __LINE__);
+        printf("PluginClient::render_gui %d\n", __LINE__);
 }
 
-
-
-
-
-
-
+void PluginClient::reset_gui_frames()
+{
+       server->reset_gui_frames();
+}
 
 int PluginClient::is_audio() { return 0; }
 int PluginClient::is_video() { return 0; }
@@ -1009,7 +1016,6 @@ int PluginClient::get_direction()
        return direction;
 }
 
-
 int64_t PluginClient::local_to_edl(int64_t position)
 {
        return position;
index 96f5540..67a1148 100644 (file)
@@ -30,6 +30,7 @@ class PluginClient;
 
 
 #include "arraylist.h"
+#include "linklist.h"
 #include "bchash.inc"
 #include "condition.h"
 #include "edlsession.inc"
@@ -137,25 +138,42 @@ int plugin_class::load_configuration() \
 }
 
 
+class PluginClientFrame : public ListItem<PluginClientFrame>
+{
+public:
+       PluginClientFrame();
+       virtual ~PluginClientFrame();
+// offset in EDL seconds for synchronizing with GUI
+       double position;
+};
+
+class PluginClientFrames : public List<PluginClientFrame>
+{
+public:
+       PluginClientFrames();
+       ~PluginClientFrames();
+
+       static int fwd_cmpr(PluginClientFrame *ap, PluginClientFrame *bp);
+       static int rev_cmpr(PluginClientFrame *ap, PluginClientFrame *bp);
+       void fwd_sort() { sort(fwd_cmpr); }
+       void rev_sort() { sort(rev_cmpr); }
+       void sort_position(int dir);
+       void reset();
+       void add_gui_frame(PluginClientFrame *frame);
+       void concatenate(PluginClientFrames *frames);
+       PluginClientFrame *get_gui_frame(double pos, int dir);
+
+       int count;
+};
 
 
 class PluginClientWindow : public BC_Window
 {
 public:
        PluginClientWindow(PluginClient *client,
-               int w,
-               int h,
-               int min_w,
-               int min_h,
-               int allow_resize);
-       PluginClientWindow(const char *title,
-               int x,
-               int y,
-               int w,
-               int h,
-               int min_w,
-               int min_h,
-               int allow_resize);
+               int w, int h, int min_w, int min_h, int allow_resize);
+       PluginClientWindow(const char *title, int x, int y,
+               int w, int h, int min_w, int min_h, int allow_resize);
        virtual ~PluginClientWindow();
 
        virtual int translation_event();
@@ -171,86 +189,70 @@ public:
 class PluginFPot : public BC_FPot
 {
 public:
-    PluginFPot(PluginParam *param, int x, int y);
-    int handle_event();
-        PluginParam *param;
+       PluginFPot(PluginParam *param, int x, int y);
+       int handle_event();
+               PluginParam *param;
 };
 
 class PluginIPot : public BC_IPot
 {
 public:
-    PluginIPot(PluginParam *param, int x, int y);
-    int handle_event();
-        PluginParam *param;
+       PluginIPot(PluginParam *param, int x, int y);
+       int handle_event();
+               PluginParam *param;
 };
 
 class PluginQPot : public BC_QPot
 {
 public:
-    PluginQPot(PluginParam *param, int x, int y);
-    int handle_event();
-        PluginParam *param;
+       PluginQPot(PluginParam *param, int x, int y);
+       int handle_event();
+               PluginParam *param;
 };
 
 class PluginText : public BC_TextBox
 {
 public:
-    PluginText(PluginParam *param, int x, int y, int value);
-    PluginText(PluginParam *param, int x, int y, float value);
-    int handle_event();
-        PluginParam *param;
+       PluginText(PluginParam *param, int x, int y, int value);
+       PluginText(PluginParam *param, int x, int y, float value);
+       int handle_event();
+               PluginParam *param;
 };
 
 class PluginParam
 {
 public:
-    PluginParam(PluginClient *plugin,
-        PluginClientWindow *gui,
-        int x1,
-        int x2,
-        int x3,
-        int y,
-        int text_w,
-        int *output_i,
-        float *output_f, // floating point output
-        int *output_q, // frequency output
-        const char *title,
-        float min,
-        float max);
-    ~PluginParam();
-
-    void initialize();
-    void update(int skip_text, int skip_pot);
+       PluginParam(PluginClient *plugin, PluginClientWindow *gui,
+               int x1, int x2, int x3, int y, int text_w,
+               int *output_i, float *output_f, // floating point output
+               int *output_q, // frequency output
+               const char *title, float min, float max);
+       ~PluginParam();
+
+       void initialize();
+       void update(int skip_text, int skip_pot);
 // set the number of fractional digits
-    void set_precision(int digits);
-
-// 2 possible outputs
-    float *output_f;
-    PluginFPot *fpot;
-
-    int *output_i;
-    PluginIPot *ipot;
-
-    int *output_q;
-    PluginQPot *qpot;
-
-    char *title;
-    PluginText *text;
-    PluginClientWindow *gui;
-    PluginClient *plugin;
-    int x1;
-    int x2;
-    int x3;
-    int y;
-    int text_w;
-    float min;
-    float max;
-    int precision;
+       void set_precision(int digits);
+
+// possible outputs
+       float *output_f;
+       PluginFPot *fpot;
+       int *output_i;
+       PluginIPot *ipot;
+       int *output_q;
+       PluginQPot *qpot;
+
+       char *title;
+       PluginText *text;
+       PluginClientWindow *gui;
+       PluginClient *plugin;
+       int x1, x2, x3;
+       int y, text_w;
+       float min, max;
+       int precision;
 };
 
 
-
-
 class PluginClientThread : public Thread
 {
 public:
@@ -271,22 +273,6 @@ private:
 
 
 
-// Client overrides for GUI update data
-class PluginClientFrame
-{
-public:
-// Period_d is 1 second
-       PluginClientFrame(int data_size, int period_n, int period_d);
-       virtual ~PluginClientFrame();
-       int data_size;
-       int period_n;
-       int period_d;
-// Draw immediately
-       int force;
-};
-
-
-
 class PluginClient
 {
 public:
@@ -311,11 +297,6 @@ public:
 // Get theme being used by Cinelerra currently.  Used by all plugins.
        Theme* get_theme();
 
-
-
-
-
-
 // Non realtime signal processors define these.
 // Give the samplerate of the output for a non realtime plugin.
 // For realtime plugins give the requested samplerate.
@@ -324,20 +305,17 @@ public:
 // For realtime plugins give the requested framerate.
        virtual double get_framerate();
        virtual int delete_nonrealtime_parameters();
-       virtual int get_parameters();     // get information from user before non realtime processing
+       virtual int get_parameters();    // get information from user before non realtime processing
        virtual int64_t get_in_buffers(int64_t recommended_size);  // return desired size for input buffers
-       virtual int64_t get_out_buffers(int64_t recommended_size);     // return desired size for output buffers
+       virtual int64_t get_out_buffers(int64_t recommended_size);       // return desired size for output buffers
        virtual int start_loop();
        virtual int process_loop();
        virtual int stop_loop();
 // Hash files are the defaults for rendered plugins
-       virtual int load_defaults();       // load default settings for the plugin
-       virtual int save_defaults();      // save the current settings as defaults
+       virtual int load_defaults();       // load default settings for the plugin
+       virtual int save_defaults();      // save the current settings as defaults
        BC_Hash* get_defaults();
 
-
-
-
 // Realtime commands for signal processors.
 // These must be defined by the plugin itself.
 // Set the GUI title identifying the plugin to modules and patches.
@@ -360,22 +338,14 @@ public:
        int is_defaults();
 
        virtual void update_gui();
-       virtual void save_data(KeyFrame *keyframe) {};    // write the plugin settings to text in text format
-       virtual void read_data(KeyFrame *keyframe) {};    // read the plugin settings from the text
-       int send_hide_gui();                                    // should be sent when the GUI receives a close event from the user
+       virtual void save_data(KeyFrame *keyframe) {};  // write the plugin settings to text in text format
+       virtual void read_data(KeyFrame *keyframe) {};  // read the plugin settings from the text
+       int send_hide_gui();                                                                    // should be sent when the GUI receives a close event from the user
 // Destroys the window but not the thread pointer.
        void hide_gui();
-
-       int get_configure_change();                             // get propogated configuration change from a send_configure_change
-
-// Called by plugin server to update GUI with rendered data.
-       void plugin_render_gui(void *data);
-       void plugin_render_gui(void *data, int size);
-
-       void begin_process_buffer();
-       void end_process_buffer();
-
        void plugin_update_gui();
+       virtual void begin_process_buffer() {}
+       virtual void end_process_buffer() {}
        virtual int plugin_process_loop(VFrame **buffers, int64_t &write_length) { return 1; };
        virtual int plugin_process_loop(Samples **buffers, int64_t &write_length) { return 1; };
 // get parameters depending on video or audio
@@ -396,8 +366,8 @@ public:
 // data for every frame.
 // If the result is the default keyframe, the keyframe's position is 0.
 // position - relative to EDL rate or local rate to allow simple
-//     passing of get_source_position.
-//     If -1 the tracking position in the edl is used.
+//      passing of get_source_position.
+//      If -1 the tracking position in the edl is used.
 // is_local - if 1, the position is converted to the EDL rate.
        KeyFrame* get_prev_keyframe(int64_t position, int is_local = 1);
        KeyFrame* get_next_keyframe(int64_t position, int is_local = 1);
@@ -449,6 +419,10 @@ public:
 // Get the direction of the most recent process_buffer
        int get_direction();
 
+// position and direction for plugin gui tracking draws
+       double get_tracking_position();
+       int get_tracking_direction();
+
 // Plugin must call this before performing OpenGL operations.
 // Returns 1 if the user supports opengl buffers.
        int get_use_opengl();
@@ -467,8 +441,6 @@ public:
        float get_green();
        float get_blue();
 
-
-
 // Operations for file handlers
        virtual int open_file() { return 0; };
        virtual int get_audio_parameters() { return 0; };
@@ -477,20 +449,12 @@ public:
        virtual int open_file(char *path, int wr, int rd) { return 1; };
        virtual int close_file() { return 0; };
 
-
-
-
-
 // All plugins define these.
        PluginClientThread* get_thread();
 
-
-
 // Non realtime operations for signal processors.
-       virtual int plugin_start_loop(int64_t start,
-               int64_t end,
-               int64_t buffer_size,
-               int total_buffers);
+       virtual int plugin_start_loop(int64_t start, int64_t end,
+               int64_t buffer_size, int total_buffers);
        int plugin_stop_loop();
        int plugin_process_loop();
        MainProgressBar* start_progress(char *string, int64_t length);
@@ -508,7 +472,7 @@ public:
        int write_frames(int64_t total_frames);  // returns 1 for failure / tells the server that all output channel buffers are ready to go
        int write_samples(int64_t total_samples);  // returns 1 for failure / tells the server that all output channel buffers are ready to go
        virtual int plugin_get_parameters();
-       const char* get_defaultdir();     // Directory defaults should be stored in
+       const char* get_defaultdir();    // Directory defaults should be stored in
        void set_interactive();
 
 // Realtime operations.
@@ -517,48 +481,37 @@ public:
        virtual int plugin_command_derived(int plugin_command) { return 0; };
        int plugin_get_range();
        int plugin_init_realtime(int realtime_priority,
-               int total_in_buffers,
-               int buffer_size);
-
+               int total_in_buffers, int buffer_size);
 
 // GUI updating wrappers for realtime plugins
 // Append frame to queue for next send_frame_buffer
        void add_gui_frame(PluginClientFrame *frame);
 
-
-
        virtual void render_gui(void *data);
        virtual void render_gui(void *data, int size);
-
-// Called by client to get the total number of frames to draw in update_gui
-       int get_gui_update_frames();
-// Get GUI frame from frame_buffer.  Client must delete it.
-       PluginClientFrame* get_gui_frame();
-
-// Called by client to cause GUI to be rendered with data.
        void send_render_gui();
        void send_render_gui(void *data);
        void send_render_gui(void *data, int size);
+       void plugin_render_gui(void *data);
+       void plugin_render_gui(void *data, int size);
 
-
-
-
-
-
-
-
-// create pointers to buffers of the plugin's type before realtime rendering
-       virtual int delete_buffer_ptrs();
-
-
-
+       void reset_gui_frames();
+       void reset_plugin_gui_frames();
+       void plugin_reset_gui_frames();
+       void plugin_render_gui_frames(PluginClientFrames *frames);
+       int get_gui_frames();
+// Called by client to get the total number of frames to draw in update_gui
+       int pending_gui_frames();
+// pop frames until buffer passes position=pos(-1 or seconds) in direction=dir(-1,0,1)
+       PluginClientFrame *get_gui_frame(double pos, int dir);
+       PluginClientFrame* next_gui_frame();
 
 // communication convenience routines for the base class
        int stop_gui_client();
        int save_data_client();
        int load_data_client();
-       int set_string_client(char *string);                // set the string identifying the plugin
-       int send_cancelled();        // non realtime plugin sends when cancelled
+       int set_string_client(char *string);                            // set the string identifying the plugin
+       int send_cancelled();           // non realtime plugin sends when cancelled
 
 // ================================= Buffers ===============================
 
@@ -580,18 +533,18 @@ public:
        ArrayList<PluginClientAuto> automation;
 
 // ================================== Messages ===========================
-       char gui_string[BCTEXTLEN];          // string identifying module and plugin
-       int master_gui_on;              // Status of the master gui plugin
-       int client_gui_on;              // Status of this client's gui
+       char gui_string[BCTEXTLEN];               // string identifying module and plugin
+       int master_gui_on;                        // Status of the master gui plugin
+       int client_gui_on;                        // Status of this client's gui
 
-       int show_initially;             // set to show a realtime plugin initially
+       int show_initially;                      // set to show a realtime plugin initially
 // range in project for processing
        int64_t start, end;
-       int interactive;                // for the progress bar plugin
+       int interactive;                                // for the progress bar plugin
        int success;
-       int total_out_buffers;          // total send buffers allocated by the server
-       int total_in_buffers;           // total receive buffers allocated by the server
-       int wr, rd;                     // File permissions for fileio plugins.
+       int total_out_buffers;            // total send buffers allocated by the server
+       int total_in_buffers;              // total receive buffers allocated by the server
+       int wr, rd;                                      // File permissions for fileio plugins.
 
 // These give the largest fragment the plugin is expected to handle.
 // size of a send buffer to the server
@@ -624,7 +577,7 @@ public:
        PluginClientThread *thread;
 
 // Frames for updating GUI
-       ArrayList<PluginClientFrame*> frame_buffer;
+       PluginClientFrames frame_buffer;
 // Time of last GUI update
        Timer *update_timer;
 
@@ -634,7 +587,7 @@ private:
 // Temporaries set in new_window
        int window_x, window_y;
 // File handlers:
-//     Asset *asset;     // Point to asset structure in shared memory
+//     Asset *asset;    // Point to asset structure in shared memory
 };
 
 
index f9ee65b..494af12 100644 (file)
@@ -23,6 +23,8 @@
 #define PLUGINCLIENT_INC
 
 class PluginClientAuto;
+class PluginClientFrames;
+class PluginClientFrame;
 class PluginClientWindow;
 class PluginFPot;
 class PluginIPot;
@@ -30,7 +32,6 @@ class PluginQPot;
 class PluginText;
 class PluginParam;
 class PluginClientThread;
-class PluginClientFrame;
 class PluginClient;
 
 #endif
index 68724f2..02f19f3 100644 (file)
@@ -92,8 +92,8 @@ PluginServer::PluginServer(MWindow *mwindow, const char *path, int type)
 {
        char fpath[BCTEXTLEN];
        init();
-        this->plugin_type = type;
-        this->mwindow = mwindow;
+       this->plugin_type = type;
+       this->mwindow = mwindow;
        if( type == PLUGIN_TYPE_FFMPEG ) {
                ff_name = cstrdup(path);
                sprintf(fpath, "ff_%s", path);
@@ -295,12 +295,12 @@ void PluginServer::set_title(const char *string)
 void PluginServer::generate_display_title(char *string)
 {
        char ltitle[BCTEXTLEN];
-       if(BC_Resources::locale_utf8)
+       if( BC_Resources::locale_utf8 )
                strcpy(ltitle, _(title));
        else
                BC_Resources::encode(BC_Resources::encoding, 0,
                                _(title),strlen(title)+1, ltitle,BCTEXTLEN);
-       if(plugin && plugin->track)
+       if( plugin && plugin->track )
                sprintf(string, "%s: %s", plugin->track->title, ltitle);
        else
                strcpy(string, ltitle);
@@ -344,7 +344,7 @@ int PluginServer::open_plugin(int master,
        EDL *edl,
        Plugin *plugin)
 {
-       if(plugin_open) return 0;
+       if( plugin_open ) return 0;
 
        this->preferences = preferences;
        this->plugin = plugin;
@@ -403,8 +403,8 @@ int PluginServer::open_plugin(int master,
 // Run initialization functions
        realtime = client->is_realtime();
 // Don't load defaults when probing the directory.
-       if(!master) {
-               if(realtime)
+       if( !master ) {
+               if( realtime )
                        client->load_defaults_xml();
                else
                        client->load_defaults();
@@ -426,12 +426,11 @@ int PluginServer::open_plugin(int master,
 
 int PluginServer::close_plugin()
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
 
-       if(client)
-       {
+       if( client ) {
 // Defaults are saved in the thread.
-//             if(client->defaults) client->save_defaults();
+//             if( client->defaults ) client->save_defaults();
                delete client;
        }
 
@@ -444,11 +443,10 @@ int PluginServer::close_plugin()
 void PluginServer::client_side_close()
 {
 // Last command executed in client thread
-       if(plugin)
+       if( plugin )
                mwindow->hide_plugin(plugin, 1);
        else
-       if(prompt)
-       {
+       if( prompt ) {
                prompt->lock_window();
                prompt->set_done(1);
                prompt->unlock_window();
@@ -457,13 +455,14 @@ void PluginServer::client_side_close()
 
 void PluginServer::render_stop()
 {
-       if(client)
+       if( client )
                client->render_stop();
+       send_reset_gui_frames();
 }
 
 void PluginServer::write_table(FILE *fp, const char *path, int idx, int64_t mtime)
 {
-       if(!fp) return;
+       if( !fp ) return;
        fprintf(fp, "%d \"%s\" \"%s\" %jd %d %d %d %d %d %d %d %d %d %d %d\n",
                plugin_type, path, title, mtime, idx, audio, video, theme, realtime,
                fileio, uses_gui, multichannel, synthesis, transition, lad_index);
@@ -492,7 +491,7 @@ int PluginServer::init_realtime(int realtime_sched,
                int buffer_size)
 {
 
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
 
 // set for realtime priority
 // initialize plugin
@@ -512,7 +511,7 @@ void PluginServer::process_transition(VFrame *input,
                int64_t current_position,
                int64_t total_len)
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        PluginVClient *vclient = (PluginVClient*)client;
 
        vclient->source_position = current_position;
@@ -538,7 +537,7 @@ void PluginServer::process_transition(Samples *input,
                int64_t fragment_size,
                int64_t total_len)
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        PluginAClient *aclient = (PluginAClient*)client;
 
        aclient->source_position = current_position;
@@ -556,50 +555,41 @@ void PluginServer::process_buffer(VFrame **frame,
        int64_t total_len,
        int direction)
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        PluginVClient *vclient = (PluginVClient*)client;
-
+       vclient->in_buffer_size = vclient->out_buffer_size = 1;
        vclient->source_position = current_position;
        vclient->total_len = total_len;
        vclient->frame_rate = frame_rate;
        vclient->input = new VFrame*[total_in_buffers];
        vclient->output = new VFrame*[total_in_buffers];
-       for(int i = 0; i < total_in_buffers; i++)
-       {
+       for( int i = 0; i < total_in_buffers; i++ ) {
                vclient->input[i] = frame[i];
                vclient->output[i] = frame[i];
        }
 
-       if(plugin)
-       {
+       if( plugin ) {
                vclient->source_start = (int64_t)plugin->startproject *
-                       frame_rate /
-                       vclient->project_frame_rate;
+                       frame_rate / vclient->project_frame_rate;
        }
        vclient->direction = direction;
 
-
 //PRINT_TRACE
 //printf("plugin=%p source_start=%ld\n", plugin, vclient->source_start);
-
-       vclient->begin_process_buffer();
-       if(multichannel)
-       {
+//     vclient->begin_process_buffer();
+       if( multichannel )
                vclient->process_buffer(frame, current_position, frame_rate);
-       }
        else
-       {
                vclient->process_buffer(frame[0], current_position, frame_rate);
-       }
-       vclient->end_process_buffer();
+//     vclient->end_process_buffer();
 
-       for(int i = 0; i < total_in_buffers; i++)
+       for( int i = 0; i < total_in_buffers; i++ )
                frame[i]->push_prev_effect(title);
 
        delete [] vclient->input;
        delete [] vclient->output;
 
-    vclient->age_temp();
+       vclient->age_temp();
        use_opengl = 0;
 }
 
@@ -610,38 +600,64 @@ void PluginServer::process_buffer(Samples **buffer,
        int64_t total_len,
        int direction)
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        PluginAClient *aclient = (PluginAClient*)client;
-
        aclient->source_position = current_position;
        aclient->total_len = total_len;
        aclient->sample_rate = sample_rate;
+       aclient->in_buffer_size = aclient->out_buffer_size = fragment_size;
+       aclient->output_buffers = buffer;
 
-       if(plugin)
+       if( plugin )
                aclient->source_start = plugin->startproject *
                        sample_rate /
                        aclient->project_sample_rate;
 
        aclient->direction = direction;
        aclient->begin_process_buffer();
-       if(multichannel)
-       {
+       if( multichannel ) {
                aclient->process_buffer(fragment_size,
-                       buffer,
-                       current_position,
-                       sample_rate);
+                       buffer, current_position, sample_rate);
        }
-       else
-       {
+       else {
                aclient->process_buffer(fragment_size,
-                       buffer[0],
-                       current_position,
-                       sample_rate);
+                       buffer[0], current_position, sample_rate);
        }
        aclient->end_process_buffer();
 }
 
 
+void PluginServer::send_reset_gui_frames()
+{
+       if( !attachmentpoint ) return;
+       attachmentpoint->reset_gui_frames(this);
+}
+
+void PluginServer::send_render_gui(void *data)
+{
+       if( !attachmentpoint ) return;
+       attachmentpoint->render_gui(data, this);
+}
+
+void PluginServer::send_render_gui(void *data, int size)
+{
+       if( !attachmentpoint ) return;
+       attachmentpoint->render_gui(data, size, this);
+}
+
+void PluginServer::render_gui(void *data)
+{
+       if( !client ) return;
+       client->plugin_render_gui(data);
+}
+
+void PluginServer::render_gui(void *data, int size)
+{
+       if( !client ) return;
+       client->plugin_render_gui(data, size);
+}
+
+
 PluginGUIs::PluginGUIs(MWindow *mwindow)
 {
        this->mwindow = mwindow;
@@ -668,26 +684,26 @@ PluginServer *PluginGUIs::gui_server(int gui_id)
 }
 
 
-void PluginServer::send_render_gui(void *data)
+void PluginServer::reset_gui_frames()
 {
-//printf("PluginServer::send_render_gui 1 %p\n", attachmentpoint);
-       if(attachmentpoint) attachmentpoint->render_gui(data, this);
+       mwindow->reset_plugin_gui_frames(plugin);
 }
 
-void PluginServer::send_render_gui(void *data, int size)
+void PluginServer::reset_plugin_gui_frames()
 {
-//printf("PluginServer::send_render_gui 1 %p\n", attachmentpoint);
-       if(attachmentpoint) attachmentpoint->render_gui(data, size, this);
+       if( !client ) return;
+       client->plugin_reset_gui_frames();
 }
 
-void PluginServer::render_gui(void *data)
+void PluginServer::render_gui_frames(PluginClientFrames *frames)
 {
-       if(client) client->plugin_render_gui(data);
+       mwindow->render_plugin_gui_frames(frames, plugin);
 }
 
-void PluginServer::render_gui(void *data, int size)
+void PluginServer::render_plugin_gui_frames(PluginClientFrames *frames)
 {
-       if(client) client->plugin_render_gui(data, size);
+       if( !client ) return;
+       client->plugin_render_gui_frames(frames);
 }
 
 MainProgressBar* PluginServer::start_progress(char *string, int64_t length)
@@ -700,30 +716,22 @@ MainProgressBar* PluginServer::start_progress(char *string, int64_t length)
 
 int64_t PluginServer::get_written_samples()
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
        return written_samples;
 }
 
 int64_t PluginServer::get_written_frames()
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
        return written_frames;
 }
 
 
-
-
-
-
-
-
-
-
 // ======================= Non-realtime plugin
 
 int PluginServer::get_parameters(int64_t start, int64_t end, int channels)
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
 
        client->start = start;
        client->end = end;
@@ -739,7 +747,7 @@ int PluginServer::get_parameters(int64_t start, int64_t end, int channels)
 
 int PluginServer::set_interactive()
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
        client->set_interactive();
        return 0;
 }
@@ -776,13 +784,13 @@ int PluginServer::set_realtime_sched()
 
 int PluginServer::process_loop(VFrame **buffers, int64_t &write_length)
 {
-       if(!plugin_open) return 1;
+       if( !plugin_open ) return 1;
        return client->plugin_process_loop(buffers, write_length);
 }
 
 int PluginServer::process_loop(Samples **buffers, int64_t &write_length)
 {
-       if(!plugin_open) return 1;
+       if( !plugin_open ) return 1;
        return client->plugin_process_loop(buffers, write_length);
 }
 
@@ -792,14 +800,14 @@ int PluginServer::start_loop(int64_t start,
        int64_t buffer_size,
        int total_buffers)
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
        client->plugin_start_loop(start, end, buffer_size, total_buffers);
        return 0;
 }
 
 int PluginServer::stop_loop()
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
        return client->plugin_stop_loop();
 }
 
@@ -808,66 +816,42 @@ int PluginServer::read_frame(VFrame *buffer,
        int64_t start_position)
 {
        ((VModule*)modules->values[channel])->render(buffer,
-               start_position,
-               PLAY_FORWARD,
-               mwindow->edl->session->frame_rate,
-               0,
-               0);
+               start_position, PLAY_FORWARD,
+               mwindow->edl->session->frame_rate, 0, 0);
        return 0;
 }
 
 int PluginServer::read_samples(Samples *buffer,
-       int channel,
-       int64_t sample_rate,
-       int64_t start_position,
-       int64_t len)
+       int channel, int64_t sample_rate, int64_t start_position, int64_t len)
 {
 // len is now in buffer
-       if(!multichannel) channel = 0;
+       if( !multichannel ) channel = 0;
 
-       if(nodes->total > channel)
+       if( nodes->total > channel )
                return ((VirtualANode*)nodes->values[channel])->read_data(buffer,
-                       len,
-                       start_position,
-                       sample_rate);
-       else
-       if(modules->total > channel)
+                       len, start_position, sample_rate);
+       if( modules->total > channel )
                return ((AModule*)modules->values[channel])->render(buffer,
-                       len,
-                       start_position,
-                       PLAY_FORWARD,
-                       sample_rate,
-                       0);
-       else
-       {
-               printf("PluginServer::read_samples no object available for channel=%d\n",
-                       channel);
-       }
-
+                       len, start_position, PLAY_FORWARD, sample_rate, 0);
+       printf("PluginServer::read_samples no object available for channel=%d\n",
+               channel);
        return -1;
 }
 
 
 int PluginServer::read_samples(Samples *buffer,
-       int channel,
-       int64_t start_position,
-       int64_t size)
+               int channel, int64_t start_position, int64_t size)
 {
 // total_samples is now set in buffer
        ((AModule*)modules->values[channel])->render(buffer,
-               size,
-               start_position,
-               PLAY_FORWARD,
-               mwindow->edl->session->sample_rate,
-               0);
+               size, start_position, PLAY_FORWARD,
+               mwindow->edl->session->sample_rate, 0);
        return 0;
 }
 
 int PluginServer::read_frame(VFrame *buffer,
-       int channel,
-       int64_t start_position,
-       double frame_rate,
-       int use_opengl)
+               int channel, int64_t start_position, double frame_rate,
+               int use_opengl)
 {
 // Data source depends on whether we're part of a virtual console or a
 // plugin array.
@@ -879,36 +863,26 @@ int PluginServer::read_frame(VFrame *buffer,
 //PRINT_TRACE
 
        int result = -1;
-       if(!multichannel) channel = 0;
+       if( !multichannel ) channel = 0;
 
 // Push our name on the next effect stack
        buffer->push_next_effect(title);
 //printf("PluginServer::read_frame %p\n", buffer);
 //buffer->dump_stacks();
 
-       if(nodes->total > channel)
-       {
+       if( nodes->total > channel ) {
 //printf("PluginServer::read_frame %d\n", __LINE__);
                result = ((VirtualVNode*)nodes->values[channel])->read_data(buffer,
-                       start_position,
-                       frame_rate,
-                       use_opengl);
+                       start_position, frame_rate, use_opengl);
        }
        else
-       if(modules->total > channel)
-       {
+       if( modules->total > channel ) {
 //printf("PluginServer::read_frame %d\n", __LINE__);
                result = ((VModule*)modules->values[channel])->render(buffer,
-                       start_position,
-//                     PLAY_FORWARD,
-                       client->direction,
-                       frame_rate,
-                       0,
-                       0,
-                       use_opengl);
+                       start_position, //                      PLAY_FORWARD,
+                       client->direction, frame_rate, 0, 0, use_opengl);
        }
-       else
-       {
+       else {
                printf("PluginServer::read_frame no object available for channel=%d\n",
                        channel);
        }
@@ -920,28 +894,10 @@ int PluginServer::read_frame(VFrame *buffer,
 }
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 // Called by client
 int PluginServer::get_gui_status()
 {
-       if(plugin)
+       if( plugin )
                return plugin->show ? GUI_ON : GUI_OFF;
        else
                return GUI_OFF;
@@ -949,27 +905,25 @@ int PluginServer::get_gui_status()
 
 void PluginServer::raise_window()
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        client->raise_window();
 }
 
 void PluginServer::show_gui()
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        if( plugin ) {
                plugin->gui_id = gui_id;
                client->total_len = plugin->length;
                client->source_start = plugin->startproject;
        }
-       if(video)
-       {
+       if( video ) {
                client->source_position = Units::to_int64(
                        mwindow->edl->local_session->get_selectionstart(1) *
                                mwindow->edl->session->frame_rate);
        }
        else
-       if(audio)
-       {
+       if( audio ) {
                client->source_position = Units::to_int64(
                        mwindow->edl->local_session->get_selectionstart(1) *
                                mwindow->edl->session->sample_rate);
@@ -981,28 +935,25 @@ void PluginServer::show_gui()
 
 void PluginServer::hide_gui()
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        if( plugin ) plugin->gui_id = -1;
-       if(client->defaults) client->save_defaults();
+       if( client->defaults ) client->save_defaults();
        client->hide_gui();
 }
 
 void PluginServer::update_gui()
 {
-       if(!plugin_open || !plugin) return;
+       if( !plugin_open || !plugin ) return;
 
        client->total_len = plugin->length;
        client->source_start = plugin->startproject;
 
-       if(video)
-       {
+       if( video ) {
                client->source_position = Units::to_int64(
                        mwindow->edl->local_session->get_selectionstart(1) *
                                mwindow->edl->session->frame_rate);
        }
-       else
-       if(audio)
-       {
+       else if( audio ) {
                client->source_position = Units::to_int64(
                        mwindow->edl->local_session->get_selectionstart(1) *
                                mwindow->edl->session->sample_rate);
@@ -1013,7 +964,7 @@ void PluginServer::update_gui()
 
 void PluginServer::update_title()
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
 
        client->update_display_title();
 }
@@ -1021,7 +972,7 @@ void PluginServer::update_title()
 
 int PluginServer::set_string(char *string)
 {
-       if(!plugin_open) return 0;
+       if( !plugin_open ) return 0;
 
        client->set_string_client(string);
        return 0;
@@ -1029,7 +980,7 @@ int PluginServer::set_string(char *string)
 
 int PluginServer::gui_open()
 {
-       if(attachmentpoint) return attachmentpoint->gui_open();
+       if( attachmentpoint ) return attachmentpoint->gui_open();
        return 0;
 }
 
@@ -1047,7 +998,7 @@ int PluginServer::get_use_opengl()
 
 void PluginServer::run_opengl(PluginClient *plugin_client)
 {
-       if(vdevice)
+       if( vdevice )
                ((VDeviceX11*)vdevice->get_output_base())->run_plugin(plugin_client);
 }
 
@@ -1058,8 +1009,8 @@ void PluginServer::get_defaults_path(char *path)
 // Get plugin name from path
        char *ptr1 = strrchr(get_path(), '/');
        char *ptr2 = strrchr(get_path(), '.');
-       if(!ptr1) ptr1 = get_path();
-       if(!ptr2) ptr2 = get_path() + strlen(get_path());
+       if( !ptr1 ) ptr1 = get_path();
+       if( !ptr2 ) ptr2 = get_path() + strlen(get_path());
        char string2[BCTEXTLEN], *ptr3 = string2;
        while( ptr1 < ptr2 ) *ptr3++ = *ptr1++;
        *ptr3 = 0;
@@ -1068,74 +1019,53 @@ void PluginServer::get_defaults_path(char *path)
 
 void PluginServer::save_defaults()
 {
-       if(client) client->save_defaults();
+       if( client ) client->save_defaults();
 }
 
 int PluginServer::get_samplerate()
 {
-       if(!plugin_open) return 0;
-       if(audio)
-       {
+       if( !plugin_open ) return 0;
+       if( audio )
                return client->get_samplerate();
-       }
-       else
-       if(mwindow)
+       if( mwindow )
                return mwindow->edl->session->sample_rate;
-       else
-       {
-               printf("PluginServer::get_samplerate audio and mwindow == NULL\n");
-               return 1;
-       }
+       printf("PluginServer::get_samplerate audio and mwindow == NULL\n");
+       return 1;
 }
 
 
 double PluginServer::get_framerate()
 {
-       if(!plugin_open) return 0;
-       if(video)
-       {
+       if( !plugin_open ) return 0;
+       if( video )
                return client->get_framerate();
-       }
-       else
-       if(mwindow)
+       if( mwindow )
                return mwindow->edl->session->frame_rate;
-       else
-       {
-               printf("PluginServer::get_framerate video and mwindow == NULL\n");
-               return 1;
-       }
+       printf("PluginServer::get_framerate video and mwindow == NULL\n");
+       return 1;
 }
 
 int PluginServer::get_project_samplerate()
 {
-       if(mwindow)
+       if( mwindow )
                return mwindow->edl->session->sample_rate;
-       else
-       if(edl)
+       if( edl )
                return edl->session->sample_rate;
-       else
-       {
-               printf("PluginServer::get_project_samplerate mwindow and edl are NULL.\n");
-               return 1;
-       }
+       printf("PluginServer::get_project_samplerate mwindow and edl are NULL.\n");
+       return 1;
 }
 
 double PluginServer::get_project_framerate()
 {
-       if(mwindow)
+       if( mwindow )
                return mwindow->edl->session->frame_rate;
-       else
-       if(edl)
+       if( edl )
                return edl->session->frame_rate;
-       else
-       {
-               printf("PluginServer::get_project_framerate mwindow and edl are NULL.\n");
-               return 1;
-       }
+       printf("PluginServer::get_project_framerate mwindow and edl are NULL.\n");
+       return 1;
 }
 
 
-
 int PluginServer::detach_buffers()
 {
        ring_buffers_out.remove_all();
@@ -1157,10 +1087,8 @@ int PluginServer::detach_buffers()
 }
 
 int PluginServer::arm_buffer(int buffer_number,
-               int64_t offset_in,
-               int64_t offset_out,
-               int double_buffer_in,
-               int double_buffer_out)
+               int64_t offset_in, int64_t offset_out,
+               int double_buffer_in, int double_buffer_out)
 {
        offset_in_render.values[buffer_number] = offset_in;
        offset_out_render.values[buffer_number] = offset_out;
@@ -1183,14 +1111,14 @@ int PluginServer::set_automation(FloatAutos *autos, FloatAuto **start_auto, Floa
 
 void PluginServer::save_data(KeyFrame *keyframe)
 {
-       if(!plugin_open) return;
+       if( !plugin_open ) return;
        client->save_data(keyframe);
 }
 
 KeyFrame* PluginServer::get_prev_keyframe(int64_t position)
 {
        KeyFrame *result = 0;
-       if(plugin)
+       if( plugin )
                result = plugin->get_prev_keyframe(position, client->direction);
        else
                result = keyframe;
@@ -1199,43 +1127,32 @@ KeyFrame* PluginServer::get_prev_keyframe(int64_t position)
 
 KeyFrame* PluginServer::get_next_keyframe(int64_t position)
 {
-       KeyFrame *result = 0;
-       if(plugin)
-               result = plugin->get_next_keyframe(position, client->direction);
-       else
-               result = keyframe;
+       KeyFrame *result = !plugin ? 0 :
+               plugin->get_next_keyframe(position, client->direction);
        return result;
 }
 
 // Called for
 KeyFrame* PluginServer::get_keyframe()
 {
-       if(plugin)
+       if( plugin )
 // Realtime plugin case
                return plugin->get_keyframe();
-       else
 // Rendered plugin case
-               return keyframe;
+       return keyframe;
 }
 
 
 void PluginServer::apply_keyframe(KeyFrame *src)
 {
-       if(!plugin)
-       {
+       if( !plugin )
                keyframe->copy_data(src);
-       }
        else
-       {
 // Span keyframes
                plugin->keyframes->update_parameter(src);
-       }
 }
 
 
-
-
-
 void PluginServer::get_camera(float *x, float *y, float *z,
        int64_t position, int direction)
 {
@@ -1256,17 +1173,14 @@ int PluginServer::get_interpolation_type()
 
 Theme* PluginServer::new_theme()
 {
-       if(theme)
-       {
+       if( theme )
                return client->new_theme();
-       }
-       else
-               return 0;
+       return 0;
 }
 
 Theme* PluginServer::get_theme()
 {
-       if(mwindow) return mwindow->theme;
+       if( mwindow ) return mwindow->theme;
        printf("PluginServer::get_theme mwindow not set\n");
        return 0;
 }
@@ -1321,11 +1235,10 @@ VFrame *PluginServer::get_picon()
 // Called when plugin interface is tweeked
 void PluginServer::sync_parameters()
 {
-       if(video) mwindow->restart_brender();
+       if( video ) mwindow->restart_brender();
        mwindow->sync_parameters();
        mwindow->update_keyframe_guis();
-       if(mwindow->edl->session->auto_conf->plugins)
-       {
+       if( mwindow->edl->session->auto_conf->plugins ) {
                mwindow->gui->lock_window("PluginServer::sync_parameters");
                mwindow->gui->draw_overlays(1);
                mwindow->gui->unlock_window();
index 5be37a4..16c2c5c 100644 (file)
@@ -270,9 +270,16 @@ public:
 // Called by rendering client to cause the GUI to display something with the data.
        void send_render_gui(void *data);
        void send_render_gui(void *data, int size);
+
 // Called by MWindow to cause GUI to display
        void render_gui(void *data);
        void render_gui(void *data, int size);
+// PluginClientFrames queuing to gui frame_buffer
+       void send_reset_gui_frames();
+       void reset_gui_frames();
+       void render_gui_frames(PluginClientFrames *frames);
+       void reset_plugin_gui_frames();
+       void render_plugin_gui_frames(PluginClientFrames *frames);
 
 // Send the boundary autos of the next fragment
        int set_automation(FloatAutos *autos, FloatAuto **start_auto, FloatAuto **end_auto, int reverse);
index c65d1b9..82dd7c8 100644 (file)
@@ -335,7 +335,6 @@ SET_TRACE
        IndexState *index_state = indexable->index_state;
        double asset_over_session = (double)indexable->get_sample_rate() /
                mwindow->edl->session->sample_rate;
-
 // Develop strategy for drawing
 // printf("ResourcePixmap::draw_audio_resource %d %p %d\n",
 // __LINE__,
@@ -345,52 +344,54 @@ SET_TRACE
        {
                case INDEX_NOTTESTED:
                        return;
-                       break;
 // Disabled.  All files have an index.
 //             case INDEX_TOOSMALL:
 //                     draw_audio_source(canvas, edit, x, w);
 //                     break;
                case INDEX_BUILDING:
-               case INDEX_READY:
-               {
+               case INDEX_READY: {
                        IndexFile indexfile(mwindow, indexable);
                        if( !indexfile.open_index() ) {
                                if( index_state->index_zoom >
-                                               mwindow->edl->local_session->zoom_sample *
+                                       mwindow->edl->local_session->zoom_sample *
                                                asset_over_session ) {
-//printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__);
-
                                        draw_audio_source(canvas, edit, x, w);
                                }
                                else {
-//printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__);
-                                       indexfile.draw_index(canvas,
-                                               this,
-                                               edit,
-                                               x,
-                                               w);
-SET_TRACE
+                                       indexfile.draw_index(canvas, this, edit, x, w);
                                }
-
                                indexfile.close_index();
-SET_TRACE
                        }
                        break;
                }
        }
+       if( !mwindow->preferences->rectify_audio ) {
+               int center_pixel = calculate_center_pixel(edit->track);
+               canvas->set_line_dashes(1);
+               canvas->set_color(mwindow->theme->zero_crossing_color);
+               canvas->draw_line(x, center_pixel, x + w, center_pixel, this);
+               canvas->set_line_dashes(0);
+       }
 }
 
 
-void ResourcePixmap::draw_audio_source(TrackCanvas *canvas, Edit *edit, int x, int w)
+int ResourcePixmap::calculate_center_pixel(Track *track)
 {
-       w++;
-       Indexable *indexable = edit->get_source();
        int rect_audio = mwindow->preferences->rectify_audio;
        int center_pixel = !rect_audio ?
                mwindow->edl->local_session->zoom_track / 2 :
                mwindow->edl->local_session->zoom_track ;
-       if( edit->track->show_titles() )
+       if( track->show_titles() )
                center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
+       return center_pixel;
+}
+
+void ResourcePixmap::draw_audio_source(TrackCanvas *canvas, Edit *edit, int x, int w)
+{
+       w++;
+       Indexable *indexable = edit->get_source();
+       int center_pixel = calculate_center_pixel(edit->track);
+       int rect_audio = mwindow->preferences->rectify_audio;
        int64_t scale_y = !rect_audio ?
                mwindow->edl->local_session->zoom_y :
                mwindow->edl->local_session->zoom_y * 2;
index 7f5fba2..3610c7a 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "bctimer.inc"
 #include "edit.inc"
+#include "track.inc"
 #include "guicast.h"
 #include "mwindow.inc"
 #include "trackcanvas.inc"
@@ -46,6 +47,7 @@ public:
                Edit *edit, int64_t edit_x, int64_t edit_w,
                int64_t pixmap_x, int64_t pixmap_w, int64_t pixmap_h,
                int mode, int indexes_only);
+       int calculate_center_pixel(Track *track);
        void draw_audio_resource(TrackCanvas *canvas,
                Edit *edit, int x, int w);
        void draw_video_resource(TrackCanvas *canvas,
index fb3ff4d..d35e475 100644 (file)
@@ -77,6 +77,13 @@ Theme::Theme()
        BC_WindowBase::get_resources()->button_highlighted = 0xffe000;
        BC_WindowBase::get_resources()->recursive_resizing = 0;
        audio_color = BLACK;
+       zero_crossing_color = 0xc03545;
+       graph_active_color = GRAPH_ACTIVE_COLOR;
+       graph_inactive_color = GRAPH_INACTIVE_COLOR;
+       graph_grid_color = GRAPH_GRID_COLOR;
+       graph_bg_color = GRAPH_BG_COLOR;
+       graph_border1_color = GRAPH_BORDER1_COLOR;
+       graph_border2_color = GRAPH_BORDER2_COLOR;
        fade_h = yS(22);
        inout_highlight_color = GREEN;
        meter_h = yS(17);
index 7055ee1..1b1e4b3 100644 (file)
@@ -25,6 +25,7 @@
 #include "awindowgui.inc"
 #include "batchrender.inc"
 #include "bctheme.h"
+#include "compressortools.inc"
 #include "cwindowgui.inc"
 #include "guicast.h"
 #include "keyframegui.inc"
@@ -165,6 +166,16 @@ public:
        int afolders_x, afolders_y, afolders_w, afolders_h;
        int alist_x, alist_y, alist_w, alist_h;
        int audio_color;
+// audio zero crossing
+       int zero_crossing_color;
+// compressor graph line
+       int graph_active_color;
+       int graph_inactive_color;
+// compressor graph background
+       int graph_grid_color;
+       int graph_bg_color;
+       int graph_border1_color;
+       int graph_border2_color;
        int assetedit_color;
        int browse_pad;
        int cauto_x, cauto_y, cauto_w, cauto_h;
index ef27522..602557f 100644 (file)
@@ -46,6 +46,17 @@ public:
                while( ++n<total ) values[n-1]=values[n];
                remove();
        }
+       void remove_block(int i, int n) {
+               if( i >= total ) return;
+               for( n+=i; n<total; ) values[i++] = values[n++];
+               total = i;
+       }
+       void remove_object_block(int i, int n) {
+               if( i >= total ) return;
+               for( n+=i; n<total; ) { del_value(i); values[i++] = values[n++]; }
+               for( n=i; n<total; ++n ) del_value(n);
+               total = i;
+       }
        void remove(TYPE value) {
                int out = 0;
                for( int in=0; in<total; ++in )
index e4eb2bd..673a684 100644 (file)
@@ -16,7 +16,6 @@
  * 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
- *
  */
 
 #include "bcbutton.h"
@@ -24,7 +23,7 @@
 #include "bcpixmap.h"
 #include "bcresources.h"
 #include "bcwindow.h"
-#include "bccolors.h"
+#include "colors.h"
 #include "fonts.h"
 #include "vframe.h"
 #include <string.h>
 #define METER_RIGHT 3
 
 
-BC_Meter::BC_Meter(int x,
-       int y,
-       int orientation,
-       int pixels,
-       int min,
-       int max,
-       int mode,
-       int use_titles,
-       int span,
-       int downmix)
+BC_Meter::BC_Meter(int x, int y, int orientation, int pixels,
+       int min, int max, int mode, int use_titles,
+       int span, int is_downmix, int is_gain_change)
  : BC_SubWindow(x, y, -1, -1)
 {
        this->over_delay = 150;
@@ -65,10 +57,10 @@ BC_Meter::BC_Meter(int x,
        this->orientation = orientation;
        this->pixels = pixels;
        this->span = span;
-       this->downmix = downmix;
-
+       this->is_downmix = is_downmix;
+       this->is_gain_change = is_gain_change;
 //printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels);
-       for(int i = 0; i < TOTAL_METER_IMAGES; i++) images[i] = 0;
+       for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = 0;
        db_titles.set_array_delete();
 }
 
@@ -76,8 +68,9 @@ BC_Meter::~BC_Meter()
 {
        db_titles.remove_all_objects();
        title_pixels.remove_all();
+       tick_w.remove_all();
        tick_pixels.remove_all();
-       for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
+       for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i];
 }
 
 int BC_Meter::get_title_w()
@@ -104,26 +97,29 @@ int BC_Meter::initialize()
        level_pixel = peak_pixel = 0;
        over_timer = 0;
        over_count = 0;
-       peak = level = -100;
 
-       if(orientation == METER_VERT)
-       {
+       if( is_gain_change ) {
+               peak = level = 0;
+       }
+       else {
+               peak = level = -100;
+       }
+
+       if( orientation == METER_VERT ) {
                set_images(get_resources()->ymeter_images);
                h = pixels;
-               if(span < 0)
-               {
+               if( span < 0 ) {
                        w = images[0]->get_w();
-                       if(use_titles) w += get_title_w();
+                       if( use_titles ) w += get_title_w();
                }
                else
                        w = span;
        }
-       else
-       {
+       else {
                set_images(get_resources()->xmeter_images);
                h = images[0]->get_h();
                w = pixels;
-               if(use_titles) h += get_title_w();
+               if( use_titles ) h += get_title_w();
        }
 
 // calibrate the db titles
@@ -138,21 +134,19 @@ int BC_Meter::initialize()
 
 void BC_Meter::set_images(VFrame **data)
 {
-       for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
-       for(int i = 0; i < TOTAL_METER_IMAGES; i++)
+       for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i];
+       for( int i = 0; i < TOTAL_METER_IMAGES; i++ )
                images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA);
 }
 
 int BC_Meter::reposition_window(int x, int y, int span, int pixels)
 {
-       if(pixels < 0) pixels = this->pixels;
+       if( pixels < 0 ) pixels = this->pixels;
        this->span = span;
        this->pixels = pixels;
-       if(orientation == METER_VERT)
-               BC_SubWindow::reposition_window(x,
-                       y,
-                       this->span < 0 ? w : span,
-                       pixels);
+       if( orientation == METER_VERT )
+               BC_SubWindow::reposition_window(x, y,
+                       this->span < 0 ? w : span, pixels);
        else
                BC_SubWindow::reposition_window(x, y, pixels, get_h());
 
@@ -168,7 +162,7 @@ int BC_Meter::reposition_window(int x, int y, int span, int pixels)
        return 0;
 }
 
-int BC_Meter::reset(int dmix)
+int BC_Meter::reset(int downmix)
 {
        level = min;
        peak = min;
@@ -176,15 +170,15 @@ int BC_Meter::reset(int dmix)
        peak_timer = 0;
        over_timer = 0;
        over_count = 0;
-       if(dmix >= 0) downmix = dmix;
+       if( downmix >= 0 )
+               is_downmix = downmix;
        draw_face(1);
        return 0;
 }
 
 int BC_Meter::button_press_event()
 {
-       if(cursor_inside() && is_event_win())
-       {
+       if( cursor_inside() && is_event_win() ) {
                reset_over();
                return 1;
        }
@@ -210,15 +204,11 @@ int BC_Meter::change_format(int mode, int min, int max)
 int BC_Meter::level_to_pixel(float level)
 {
        int result;
-       if(mode == METER_DB)
-       {
-               result = (int)(pixels *
-                       (level - min) /
-                       (max - min));
-               if(level <= min) result = 0;
+       if( mode == METER_DB ) {
+               result = (int)(pixels * (level - min) / (max - min));
+               if( level <= min ) result = 0;
        }
-       else
-       {
+       else {
 // Not implemented anymore
                result = 0;
        }
@@ -236,6 +226,7 @@ void BC_Meter::get_divisions()
        db_titles.remove_all_objects();
        title_pixels.remove_all();
        tick_pixels.remove_all();
+       tick_w.remove_all();
 
        low_division = 0;
        medium_division = 0;
@@ -243,111 +234,104 @@ void BC_Meter::get_divisions()
 
        int current_pixel;
 // Create tick marks and titles in one pass
-       for(int current = min; current <= max; current++)
-       {
-               if(orientation == METER_VERT)
-               {
+       for( int current = min; current <= max; current++ ) {
+               if( orientation == METER_VERT ) {
 // Create tick mark
                        current_pixel = (pixels - METER_MARGIN * 2 - 2) *
                                (current - min) / (max - min) + 2;
                        tick_pixels.append(current_pixel);
 
 // Create titles in selected positions
-                       if(current == min ||
-                               current == max ||
-                               current == 0 ||
-                               (current - min > 4 && max - current > 4 && !(current % 5)))
-                       {
-                               int title_pixel = (pixels -
-                                       METER_MARGIN * 2) * (current - min) / (max - min);
-                               sprintf(string, "%ld", labs(current));
+                       if( current == min || current == max || current == 0 ||
+                               (current - min > 4 && max - current > 4 && !(current % 5)) ) {
+                               int title_pixel = (pixels - METER_MARGIN * 2) *
+                                       (current - min) / (max - min);
+                               sprintf(string, "%d", (int)labs(current));
                                new_string = new char[strlen(string) + 1];
                                strcpy(new_string, string);
                                db_titles.append(new_string);
                                title_pixels.append(title_pixel);
+                               tick_w.append(TICK_W1);
+                       }
+                       else {
+                               tick_w.append(TICK_W2);
                        }
                }
-               else
-               {
+               else {
                        current_pixel = (pixels - METER_MARGIN * 2) *
                                (current - min) /
                                (max - min);
                        tick_pixels.append(current_pixel);
+                       tick_w.append(TICK_W1);
 // Titles not supported for horizontal
                }
 
 // Create color divisions
-               if(current == -20)
-               {
+               if( current == -20 ) {
                        low_division = current_pixel;
                }
                else
-               if(current == -5)
-               {
+               if( current == -5 ) {
                        medium_division = current_pixel;
                }
                else
-               if(current == 0)
-               {
+               if( current == 0 ) {
                        high_division = current_pixel;
                }
        }
-// if(orientation == METER_VERT)
+// if( orientation == METER_VERT )
 // printf("BC_Meter::get_divisions %d %d %d %d\n",
 // low_division, medium_division, high_division, pixels);
 }
 
 void BC_Meter::draw_titles(int flush)
 {
-       if(!use_titles) return;
+       if( !use_titles ) return;
+       int tick_xfudge = xS(1);
 
        set_font(get_resources()->meter_font);
 
-       if(orientation == METER_HORIZ)
-       {
+       if( orientation == METER_HORIZ ) {
                draw_top_background(parent_window, 0, 0, get_w(), get_title_w());
 
-               for(int i = 0; i < db_titles.total; i++)
-               {
+               for( int i = 0; i < db_titles.total; i++ ) {
                        draw_text(0, title_pixels.values[i], db_titles.values[i]);
                }
 
                flash(0, 0, get_w(), get_title_w(), flush);
        }
        else
-       if(orientation == METER_VERT)
-       {
+       if( orientation == METER_VERT ) {
                draw_top_background(parent_window, 0, 0, get_title_w(), get_h());
 
 // Titles
-               for(int i = 0; i < db_titles.total; i++)
-               {
-                       int title_y = pixels -
-                               title_pixels.values[i];
-                       if(i == 0)
-                               title_y -= get_text_descent(SMALLFONT_3D);
+               for( int i = 0; i < db_titles.total; i++ ) {
+                       int title_y = pixels - title_pixels.values[i];
+                       if( i == 0 )
+                               title_y -= get_text_descent(get_resources()->meter_font);
                        else
-                       if(i == db_titles.total - 1)
-                               title_y += get_text_ascent(SMALLFONT_3D);
+                       if( i == db_titles.total - 1 )
+                               title_y += get_text_ascent(get_resources()->meter_font);
                        else
-                               title_y += get_text_ascent(SMALLFONT_3D) / 2;
+                               title_y += get_text_ascent(get_resources()->meter_font) / 2;
+                       int title_x = get_title_w() - TICK_W1 - tick_xfudge -
+                               get_text_width(get_resources()->meter_font, db_titles.values[i]);
 
                        set_color(get_resources()->meter_font_color);
-                       draw_text(0,
-                               title_y,
-                               db_titles.values[i]);
+                       draw_text(title_x, title_y, db_titles.values[i]);
                }
 
-               for(int i = 0; i < tick_pixels.total; i++)
-               {
+               for( int i = 0; i < tick_pixels.total; i++ ) {
 // Tick marks
                        int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN;
                        set_color(get_resources()->meter_font_color);
-                       draw_line(get_title_w() - xS(10) - 1, tick_y, get_title_w() - 1, tick_y);
-                       if(get_resources()->meter_3d)
-                       {
+                       draw_line(get_title_w() - tick_w.get(i) - tick_xfudge,
+                               tick_y, get_title_w() - tick_xfudge, tick_y);
+
+                       if( get_resources()->meter_3d ) {
                                set_color(BLACK);
-                               draw_line(get_title_w() - xS(10), tick_y + 1, get_title_w(), tick_y + 1);
+                               draw_line(get_title_w() - tick_w.get(i),
+                                       tick_y + 1, get_title_w(), tick_y + 1);
                        }
                }
 
@@ -358,9 +342,9 @@ void BC_Meter::draw_titles(int flush)
 int BC_Meter::region_pixel(int region)
 {
        VFrame **reference_images = get_resources()->xmeter_images;
-       int result;
+       int result = 0;
 
-       if(region == METER_RIGHT)
+       if( region == METER_RIGHT )
                result = region * reference_images[0]->get_w() / 4;
        else
                result = region * reference_images[0]->get_w() / 4;
@@ -377,7 +361,7 @@ int BC_Meter::region_pixels(int region)
 
        x1 = region * reference_images[0]->get_w() / 4;
        x2 = (region + 1) * reference_images[0]->get_w() / 4;
-       if(region == METER_MID)
+       if( region == METER_MID )
                result = (x2 - x1) * 2;
        else
                result = x2 - x1;
@@ -400,197 +384,258 @@ void BC_Meter::draw_face(int flush)
        draw_top_background(parent_window, x, 0, w, h);
 
 // printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n",
-// __LINE__,
-// span,
-// this->w,
-// get_title_w(),
-// w,
-// h);
-
-       while(pixel < pixels)
-       {
-// Select image to draw
-               if(pixel < level_pixel ||
-                       (pixel >= peak_pixel1 && pixel < peak_pixel2))
-               {
-                       if(pixel < low_division)
-                               image_number = METER_GREEN;
-                       else
-                       if(pixel < medium_division)
-                               image_number = METER_YELLOW;
-                       else
-                       if(pixel < high_division)
-                               image_number = METER_RED;
-                       else
-                               image_number = METER_WHITE;
-               }
-               else
-               {
-                       image_number = METER_NORMAL;
-               }
+// __LINE__, span, this->w, get_title_w(), w, h);
+       if( is_gain_change ) {
+               int in_h = images[0]->get_h();
+               int in_third = in_h / 3;
+               int in_third3 = in_h - in_third * 2;
 
-// Select region of image to duplicate
-               if(pixel < left_pixel)
-               {
-                       region = METER_LEFT;
-                       in_start = pixel + region_pixel(region);
-                       in_span = region_pixels(region) - (in_start - region_pixel(region));
-               }
-               else
-               if(pixel < right_pixel)
-               {
-                       region = METER_MID;
-                       in_start = region_pixel(region);
-                       in_span = region_pixels(region);
-               }
-               else
-               {
-                       region = METER_RIGHT;
-                       in_start = (pixel - right_pixel) + region_pixel(region);
-                       in_span = region_pixels(region) - (in_start - region_pixel(region));;
+// printf("BC_Meter::draw_face %d level=%f level_pixel=%d high_division=%d\n",
+// __LINE__, level, level_pixel, high_division);
+
+
+// fudge a line when no gain change
+               if( level_pixel == high_division ) {
+                       level_pixel += 1;
                }
 
-//printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span);
-               if(in_span > 0)
-               {
-// Clip length to peaks
-                       if(pixel < level_pixel && pixel + in_span > level_pixel)
-                               in_span = level_pixel - pixel;
-                       else
-                       if(pixel < peak_pixel1 && pixel + in_span > peak_pixel1)
-                               in_span = peak_pixel1 - pixel;
-                       else
-                       if(pixel < peak_pixel2 && pixel + in_span > peak_pixel2)
-                               in_span = peak_pixel2 - pixel;
+               while( pixel < pixels ) {
+// Select image to draw & extents
+                       if( level_pixel < high_division ) {
+// always vertical
+                               if( pixel < level_pixel ) {
+                                       image_number = METER_NORMAL;
+                                       in_span = level_pixel - pixel;
+                               }
+                               else
+                               if( pixel < high_division ) {
+                                       image_number = METER_RED;
+                                       in_span = high_division - pixel;
+                               }
+                               else {
+                                       image_number = METER_NORMAL;
+                                       in_span = pixels - pixel;
+                               }
+                       }
+                       else {
+// determine pixel range & image to draw
+                               if( pixel < high_division ) {
+                                       image_number = METER_NORMAL;
+                                       in_span = high_division - pixel;
+                               }
+                               else
+                               if( pixel < level_pixel ) {
+                                       image_number = METER_GREEN;
+                                       in_span = level_pixel - pixel;
+                               }
+                               else {
+                                       image_number = METER_NORMAL;
+                                       in_span = pixels - pixel;
+                               }
+                       }
 
-// Clip length to color changes
-                       if(image_number == METER_GREEN && pixel + in_span > low_division)
-                               in_span = low_division - pixel;
+// determine starting point in source to draw
+// draw starting section
+                       if( pixel == 0 ) {
+                               in_start = 0;
+                       }
                        else
-                       if(image_number == METER_YELLOW && pixel + in_span > medium_division)
-                               in_span = medium_division - pixel;
+// draw middle section
+                       if( pixels - pixel > in_third3 ) {
+                               in_start = in_third;
+                       }
                        else
-                       if(image_number == METER_RED && pixel + in_span > high_division)
-                               in_span = high_division - pixel;
+// draw last section
+                       {
+                               in_start = in_third * 2;
+                       }
 
-// Clip length to regions
-                       if(pixel < left_pixel && pixel + in_span > left_pixel)
-                               in_span = left_pixel - pixel;
+// clamp the region to the source dimensions
+                       if( in_start < in_third * 2 ) {
+                               if( in_span > in_third ) {
+                                       in_span = in_third;
+                               }
+                       }
                        else
-                       if(pixel < right_pixel && pixel + in_span > right_pixel)
-                               in_span = right_pixel - pixel;
+// last segment
+                       if( pixels - pixel < in_third3 ) {
+                               in_span = pixels - pixel;
+                               in_start = in_h - in_span;
+                       }
 
-//printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span);
-//printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span);
-                       if(orientation == METER_HORIZ)
-                       {
-                               draw_pixmap(images[image_number],
-                                       pixel,
-                                       x,
-                                       in_span + 1,
-                                       get_h(),
-                                       in_start,
-                                       0);
+// printf("BC_Meter::draw_face %d dst_y=%d src_y=%d"
+// " pixels=%d pixel=%d in_h=%d in_start=%d in_span=%d in_third=%d in_third3=%d\n",
+// __LINE__, get_h() - pixel - in_span, in_h - in_start - in_span,
+// pixels, pixel, in_h, in_start, in_span, in_third, in_third3);
+                       draw_pixmap(images[image_number], x, get_h() - pixel - in_span,
+                               get_w(), in_span + 1, 0, in_h - in_start - in_span);
+                       pixel += in_span;
+               }
+       }
+       else {
+               while( pixel < pixels ) {
+// Select image to draw
+                       if( pixel < level_pixel ||
+                               (pixel >= peak_pixel1 && pixel < peak_pixel2) ) {
+                               if( pixel < low_division )
+                                       image_number = METER_GREEN;
+                               else
+                               if( pixel < medium_division )
+                                       image_number = METER_YELLOW;
+                               else
+                               if( pixel < high_division )
+                                       image_number = METER_RED;
+                               else
+                                       image_number = METER_WHITE;
+                       }
+                       else {
+                               image_number = METER_NORMAL;
+                       }
+
+// Select region of image to duplicate
+                       if( pixel < left_pixel ) {
+                               region = METER_LEFT;
+                               in_start = pixel + region_pixel(region);
+                               in_span = region_pixels(region) - (in_start - region_pixel(region));
                        }
                        else
-                       {
-//printf("BC_Meter::draw_face %d %d\n", __LINE__, span);
-                               if(span < 0)
-                               {
-                                       draw_pixmap(images[image_number],
-                                               x,
-                                               get_h() - pixel - in_span,
-                                               get_w(),
-                                               in_span + 1,
-                                               0,
-                                               images[image_number]->get_h() - in_start - in_span);
-                               }
-                               else
-                               {
-                                       int total_w = get_w() - x;
-                                       int third = images[image_number]->get_w() / 3 + 1;
-
-
-                                       for(int x1 = 0; x1 < total_w; x1 += third)
-                                       {
-                                               int in_x = 0;
-                                               int in_w = third;
-                                               if(x1 >= third) in_x = third;
-                                               if(x1 >= total_w - third)
-                                               {
-                                                       in_x = images[image_number]->get_w() -
-                                                               (total_w - x1);
-                                                       in_w = total_w - x1;
-                                               }
+                       if( pixel < right_pixel ) {
+                               region = METER_MID;
+                               in_start = region_pixel(region);
+                               in_span = region_pixels(region);
+                       }
+                       else {
+                               region = METER_RIGHT;
+                               in_start = (pixel - right_pixel) + region_pixel(region);
+                               in_span = region_pixels(region) - (in_start - region_pixel(region));;
+                       }
 
-                                               int in_y = images[image_number]->get_h() - in_start - in_span;
-//printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span);
+       //printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span);
+                       if( in_span > 0 ) {
+       // Clip length to peaks
+                               if( pixel < level_pixel && pixel + in_span > level_pixel )
+                                       in_span = level_pixel - pixel;
+                               else
+                               if( pixel < peak_pixel1 && pixel + in_span > peak_pixel1 )
+                                       in_span = peak_pixel1 - pixel;
+                               else
+                               if( pixel < peak_pixel2 && pixel + in_span > peak_pixel2 )
+                                       in_span = peak_pixel2 - pixel;
 
+       // Clip length to color changes
+                               if( image_number == METER_GREEN && pixel + in_span > low_division )
+                                       in_span = low_division - pixel;
+                               else
+                               if( image_number == METER_YELLOW && pixel + in_span > medium_division )
+                                       in_span = medium_division - pixel;
+                               else
+                               if( image_number == METER_RED && pixel + in_span > high_division )
+                                       in_span = high_division - pixel;
 
+       // Clip length to regions
+                               if( pixel < left_pixel && pixel + in_span > left_pixel )
+                                       in_span = left_pixel - pixel;
+                               else
+                               if( pixel < right_pixel && pixel + in_span > right_pixel )
+                                       in_span = right_pixel - pixel;
+
+       //printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span);
+       //printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span);
+                               if( orientation == METER_HORIZ ) {
+                                       draw_pixmap(images[image_number], pixel,
+                                               x, in_span + 1, get_h(), in_start, 0);
+                               }
+                               else {
+       //printf("BC_Meter::draw_face %d %d\n", __LINE__, span);
+                                       if( span < 0 ) {
                                                draw_pixmap(images[image_number],
-                                                       x + x1, get_h() - pixel - in_span,
-                                                       in_w, in_span + 1, in_x, in_y);
+                                                       x, get_h() - pixel - in_span,
+                                                       get_w(), in_span + 1, 0,
+                                                       images[image_number]->get_h() - in_start - in_span);
+                                       }
+                                       else {
+                                               int total_w = get_w() - x;
+                                               int third = images[image_number]->get_w() / 3 + 1;
+
+
+                                               for( int x1 = 0; x1 < total_w; x1 += third ) {
+                                                       int in_x = 0;
+                                                       int in_w = third;
+                                                       if( x1 >= third ) in_x = third;
+                                                       if( x1 >= total_w - third ) {
+                                                               in_x = images[image_number]->get_w() -
+                                                                       (total_w - x1);
+                                                               in_w = total_w - x1;
+                                                       }
+
+                                                       int in_y = images[image_number]->get_h() - in_start - in_span;
+       //printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span);
+
+
+                                                       draw_pixmap(images[image_number],
+                                                               x + x1, get_h() - pixel - in_span,
+                                                               in_w, in_span + 1, in_x, in_y);
+                                               }
                                        }
                                }
-                       }
 
-                       pixel += in_span;
-               }
-               else
-               {
-// Sanity check
-                       break;
+                               pixel += in_span;
+                       }
+                       else {
+       // Sanity check
+                               break;
+                       }
                }
        }
 
-       if(downmix) {
-               if(orientation == METER_HORIZ)
+       if( is_downmix ) {
+               if( orientation == METER_HORIZ )
                        draw_pixmap(images[METER_DOWNMIX], 0, 0);
                else
-                       draw_pixmap(images[METER_DOWNMIX], x,
-                               get_h() - images[METER_DOWNMIX]->get_h()-1);
+                       draw_pixmap(images[METER_DOWNMIX],
+                               x, get_h() - images[METER_DOWNMIX]->get_h() - 1);
        }
-
-       if(over_timer)
-       {
-               if(orientation == METER_HORIZ)
+       if( over_timer ) {
+               if( orientation == METER_HORIZ )
                        draw_pixmap(images[METER_OVER], xS(20), yS(2));
                else
-                       draw_pixmap(images[METER_OVER], x, get_h() - yS(100));
+                       draw_pixmap(images[METER_OVER],
+                               x + xS(2), get_h() - yS(100));
 
                over_timer--;
        }
 
-       if(orientation == METER_HORIZ)
+       if( orientation == METER_HORIZ )
                flash(0, 0, pixels, get_h(), flush);
        else
                flash(x, 0, w, pixels, flush);
 }
 
-int BC_Meter::update(float new_value, int over, int dmix)
+int BC_Meter::update(float new_value, int over, int downmix)
 {
        peak_timer++;
 
-       if(mode == METER_DB)
-       {
-               if(new_value == 0)
+       if( mode == METER_DB ) {
+               if( new_value == 0 )
                        level = min;
                else
-                       level = db.todb(new_value);        // db value
+                       level = DB::todb(new_value);            // db value
        }
 
-       if(level > peak || peak_timer > peak_delay)
-       {
+
+       if( is_gain_change && fabs(level) > fabs(peak) ||
+               !is_gain_change && level > peak ||
+               peak_timer > peak_delay ) {
                peak = level;
                peak_timer = 0;
        }
 
-// if(orientation == METER_HORIZ)
+// if( orientation == METER_HORIZ )
 // printf("BC_Meter::update %f\n", level);
-       if(over) over_timer = over_delay;
+       if( over ) over_timer = over_delay;
 // only draw if window is visible
-       if(dmix >= 0) downmix = dmix;
-
+       if( downmix >= 0 )
+               is_downmix = downmix;
        draw_face(1);
        return 0;
 }
index 9a5cf80..e43c52b 100644 (file)
@@ -1,7 +1,6 @@
-
 /*
  * CINELERRA
- * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * Copyright (C) 2008-2019 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
@@ -16,7 +15,6 @@
  * 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
- *
  */
 
 #ifndef BCMETER_H
 
 // Distance from subwindow border to top and bottom tick mark
 #define METER_MARGIN 0
+// tick width
+#define TICK_W1 xS(10)
+#define TICK_W2 xS(5)
 
 class BC_Meter : public BC_SubWindow
 {
 public:
-       BC_Meter(int x,
-               int y,
-               int orientation,
-               int pixels,
-               int min, /* = -40, */
-               int max,
+       BC_Meter(int x, int y, int orientation, int pixels,
+               int min, /* = -40, */ int max,
                int mode, /* = METER_DB, */
                int use_titles, /* = 0, */
-               int span, /* = -1 width for vertical mode only */
-               int downmix = 0);
+               int span /* = -1 width for vertical mode only */,
+               int is_downmix = 0,
+               int is_gain_change = 0);
        virtual ~BC_Meter();
 
        int initialize();
@@ -64,12 +62,11 @@ public:
 
        static int get_title_w();
        static int get_meter_w();
-       int update(float new_value, int over, int dmix=-1);
-       int reposition_window(int x,
-               int y,
+       int update(float new_value, int over, int downmix=-1);
+       int reposition_window(int x, int y,
                int span /* = -1 for vertical mode only */,
                int pixels);
-       int reset(int dmix=-1);
+       int reset(int downmix=-1);
        int reset_over();
        int change_format(int mode, int min, int max);
 
@@ -91,25 +88,22 @@ private:
        int use_titles;
 // Tick mark positions
        ArrayList<int> tick_pixels;
+// Tick widths
+       ArrayList<int> tick_w;
 // Title positions
        ArrayList<int> title_pixels;
        ArrayList<char*> db_titles;
        float level, peak;
        int mode;
-       DB db;
        int peak_timer;
-
-
-
-
-
+       int is_gain_change;
 
        int peak_pixel, level_pixel, peak_pixel1, peak_pixel2;
        int over_count, over_timer;
        int min, max;
-       int downmix;
-       int over_delay;       // Number of updates the over warning lasts.
-       int peak_delay;       // Number of updates the peak lasts.
+       int is_downmix;
+       int over_delay;    // Number of updates the over warning lasts.
+       int peak_delay;    // Number of updates the peak lasts.
 };
 
 #endif
index b979f91..6b91b33 100644 (file)
@@ -44,6 +44,8 @@ BC_Pot::BC_Pot(int x, int y, VFrame **data)
 
 BC_Pot::~BC_Pot()
 {
+       for(int i = 0; i < POT_STATES; i++)
+               if(images[i]) delete images[i];
 }
 
 int BC_Pot::calculate_h()
index 0d9f95d..e979d5c 100644 (file)
@@ -624,7 +624,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title
                }
 
                if(!hidden) show_window();
-
+               init_glyphs();
        }
 
        draw_background(0, 0, this->w, this->h);
@@ -2427,6 +2427,30 @@ xft_init_lock.unlock();
 #endif // HAVE_XFT
 }
 
+void BC_WindowBase::init_glyphs()
+{
+// draw all ascii char glyphs
+//  There are problems with some/my graphics boards/drivers
+//  which cause some glyphs to be munged if draws occur while
+//  the font is being loaded.  This code fills the font caches
+//  by drawing all the ascii glyphs before the system starts.
+//  Not a fix, but much better than nothing.
+       static int inited = 0;
+       if( inited ) return;
+       inited = 1;
+       int cur_font = current_font;
+// locale encodings, needed glyphs to be preloaded
+       const char *text = _( // ascii 0x20...0x7e
+               " !\"#$%&'()*+,-./0123456789:;<=>?"
+               "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+               "`abcdefghijklmnopqrstuvwxyz{|}~");
+       for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
+               set_font(font);
+               draw_text(5,5, text, 0);
+       }
+       set_font(cur_font);
+}
+
 void BC_WindowBase::init_im()
 {
        XIMStyles *xim_styles;
index 36add9c..153aab3 100644 (file)
@@ -576,6 +576,7 @@ private:
        int allocate_color_table();
        int init_gc();
        int init_fonts();
+       void init_glyphs();
        void init_xft();
        void init_im();
        void finit_im();
index 0c580de..3accf28 100644 (file)
@@ -28,6 +28,7 @@ public:
        void remove_pointer(ListItem<TYPE> *item);
        TYPE *append(TYPE *new_item);
        TYPE *append() { return append(new TYPE()); }
+       void destroy() { while(last) delete last; }
        TYPE *insert_before(TYPE *here, TYPE *item);
        TYPE *insert_before(TYPE *here) { return insert_before(here, new TYPE()); }
        TYPE *insert_after(TYPE *here, TYPE *item);
@@ -45,8 +46,9 @@ public:
        void swap(TYPE *item1, TYPE *item2);
        void sort(TYPE *ap=0, TYPE *bp=0) { return sort(cmpr,ap,bp); }
        void sort(int (*cmp)(TYPE *a, TYPE *b), TYPE *ap=0, TYPE *bp=0);
+       void concat(List<TYPE> &b);
        List() { first = last = 0; }
-       virtual ~List() { while(last) delete last; }
+       virtual ~List() { destroy(); }
 };
 
 // convenience macros
@@ -132,4 +134,14 @@ void List<TYPE>::sort(int (*cmpr)(TYPE *a, TYPE *b), TYPE *ll, TYPE *rr)
        }
 }
 
+template<class TYPE>
+void List<TYPE>::concat(List<TYPE> &b)
+{
+       if( !b.first ) return;
+       *(last ? &last->next : &first) = b.first;
+       b.first->previous = last;  last = b.last;
+       TYPE *bp = b.first;  b.first = b.last = 0;
+       while( bp ) { bp->list = this;  bp = bp->next; }
+}
+
 #endif
index 9c2f1d2..e0a170d 100644 (file)
@@ -90,14 +90,14 @@ int Freq::fromfreq()
        int i = 0;
        while( i<TOTALFREQS && freqtable[i]<freq ) ++i;
        return i;
-};
+}
 
 int Freq::fromfreq(int index)
 {
        int i = 0;
        while( i<TOTALFREQS && freqtable[i]<index ) ++i;
        return i;
-};
+}
 
 int Freq::tofreq(int index)
 {
@@ -105,6 +105,25 @@ int Freq::tofreq(int index)
        return freqtable[index];
 }
 
+// frequency doubles for every OCTAVE slots.  OCTAVE must be divisible by 3
+// 27.5 is at i=1
+// 55 is at i=106
+// 110 is at i=211
+// 220 is at i=316
+// 440 is at i=421
+// 880 is at i=526
+double Freq::tofreq_f(double index)
+{
+       if( index < 0.5 ) return 0;
+       return 440.0 * pow(2, (double)(index - 421) / OCTAVE);
+}
+double Freq::fromfreq_f(double f)
+{
+       if( f < 0.5 ) return 0;
+       double result = log(f / 440) / log(2.0) * OCTAVE + 421;
+       return result < 0 ? 0 : result;
+}
+
 Freq& Freq::operator++()
 {
        if(freq < TOTALFREQS) ++freq;
@@ -136,17 +155,8 @@ void Units::init()
        topower[INFINITYGAIN * 10] = 0;   // infinity gain
 
        Freq::freqtable = new int[TOTALFREQS + 1];
-// starting frequency
-       double freq1 = 27.5, freq2 = 55;
-// Some number divisable by three.  This depends on the value of TOTALFREQS
-       int scale = 105;
-
-       Freq::freqtable[0] = 0;
-       for(int i = 1, j = 0; i <= TOTALFREQS; i++, j++) {
-               Freq::freqtable[i] = (int)(freq1 + (freq2 - freq1) / scale * j + 0.5);
-               if(j < scale) continue;
-               freq1 = freq2;  freq2 *= 2;  j = 0;
-       }
+       for( int i=0; i<=TOTALFREQS; ++i )
+               Freq::freqtable[i] = Freq::tofreq_f(i);
 }
 void Units::finit()
 {
index 7456b66..7e2243e 100644 (file)
@@ -32,6 +32,8 @@
 #define INFINITYGAIN -96
 #define MAXGAIN 50
 #define TOTALFREQS 1024
+// slots per octave
+#define OCTAVE 105
 #define TOTAL_TIMEFORMATS 7
 
 // h:mm:ss.sss
@@ -125,7 +127,8 @@ public:
 // return index of frequency
        int fromfreq();
        static int fromfreq(int index);
-
+       static double tofreq_f(double index);
+       static double fromfreq_f(double freq);
 // increment frequency by one
        Freq& operator++();
        Freq& operator--();
index 2620b4d..1f464d2 100644 (file)
@@ -42,6 +42,7 @@ DIRS = $(OPENCV_OBJS) \
        color3way \
        colorbalance \
        compressor \
+       compressormulti \
        crikey \
        crop \
        crossfade \
index 893d991..b7294bd 100644 (file)
@@ -6,5 +6,4 @@ PLUGIN = compressor
 
 include ../../plugin_config
 
-
 $(OBJDIR)/compressor.o: compressor.C
index b167c6e..956d2ae 100644 (file)
@@ -1,7 +1,6 @@
-
 /*
  * CINELERRA
- * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * Copyright (C) 2008-2019 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
  */
 
 #include "bcdisplayinfo.h"
+#include "bchash.h"
 #include "bcsignals.h"
+#include "asset.h"
 #include "clip.h"
 #include "compressor.h"
 #include "cursors.h"
-#include "bchash.h"
+#include "edl.h"
+#include "edlsession.h"
 #include "filexml.h"
 #include "language.h"
 #include "samples.h"
 #include "theme.h"
+#include "tracking.inc"
+#include "transportque.inc"
 #include "units.h"
 #include "vframe.h"
 
 #include <math.h>
 #include <string.h>
 
-
-
-
-
 REGISTER_PLUGIN(CompressorEffect)
 
-
-
-
-
-
 // More potential compressor algorithms:
-// Use single reaction time parameter.  Negative reaction time uses
-// readahead.  Positive reaction time uses slope.
+// Use single readahead time parameter.  Negative readahead time uses
+// readahead.  Positive readahead time uses slope.
 
 // Smooth input stage if readahead.
 // Determine slope from current smoothed sample to every sample in readahead area.
@@ -58,7 +53,7 @@ REGISTER_PLUGIN(CompressorEffect)
 
 // Smooth input stage if not readahead.
 // For every sample, calculate slope needed to reach current sample from
-// current smoothed value in the reaction time.  If higher than current slope,
+// current smoothed value in the readahead time.  If higher than current slope,
 // make it the current slope and count number of samples remaining until it is
 // reached.  If this count is met and no higher slopes are found, base slope
 // on current sample when count is met.
@@ -66,123 +61,97 @@ REGISTER_PLUGIN(CompressorEffect)
 // Gain stage.
 // For every sample, calculate gain from smoothed input value.
 
-
-
-
-
 CompressorEffect::CompressorEffect(PluginServer *server)
  : PluginAClient(server)
 {
-       reset();
+       input_buffer = 0;
+       input_size = 0;
+       input_allocated = 0;
+       input_start = 0;
 
+       need_reconfigure = 1;
+       engine = 0;
 }
 
 CompressorEffect::~CompressorEffect()
 {
-
-       delete_dsp();
-       levels.remove_all();
-}
-
-void CompressorEffect::delete_dsp()
-{
-       if(input_buffer)
-       {
-               for(int i = 0; i < PluginClient::total_in_buffers; i++)
+       if( input_buffer ) {
+               for( int i=0; i<PluginClient::total_in_buffers; ++i )
                        delete input_buffer[i];
-               delete [] input_buffer;
+               delete [] input_buffer;  input_buffer = 0;
        }
-
-
-       input_buffer = 0;
        input_size = 0;
        input_allocated = 0;
+       levels.remove_all();
+       delete engine;
 }
 
-
-void CompressorEffect::reset()
+void CompressorEffect::allocate_input(int size)
 {
-       input_buffer = 0;
-       input_size = 0;
-       input_allocated = 0;
-       input_start = 0;
+       int channels = PluginClient::total_in_buffers;
+       if( size > input_allocated ) {
+               Samples **new_input_buffer = new Samples*[channels];
+               for( int i=0; i<channels; ++i ) {
+                       new_input_buffer[i] = new Samples(size);
+                       if( !input_buffer ) continue;
+                       memcpy(new_input_buffer[i]->get_data(),
+                               input_buffer[i]->get_data(),
+                               input_size * sizeof(double));
+                       delete input_buffer[i];
+               }
+               if( input_buffer ) delete [] input_buffer;
 
-       next_target = 1.0;
-       previous_target = 1.0;
-       target_samples = 1;
-       target_current_sample = -1;
-       current_value = 1.0;
+               input_allocated = size;
+               input_buffer = new_input_buffer;
+       }
 }
 
 const char* CompressorEffect::plugin_title() { return N_("Compressor"); }
 int CompressorEffect::is_realtime() { return 1; }
 int CompressorEffect::is_multichannel() { return 1; }
 
-
-
 void CompressorEffect::read_data(KeyFrame *keyframe)
 {
        FileXML input;
-       input.set_shared_input(keyframe->xbuf);
+       BandConfig *band_config = &config.bands[0];
+        input.set_shared_input(keyframe->xbuf);
 
        int result = 0;
-       config.levels.remove_all();
-       while(!result)
-       {
-               result = input.read_tag();
-
-               if(!result)
-               {
-                       if(input.tag.title_is("COMPRESSOR"))
-                       {
-                               config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len);
-                               config.decay_len = input.tag.get_property("DECAY_LEN", config.decay_len);
-                               config.trigger = input.tag.get_property("TRIGGER", config.trigger);
-                               config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
-                               config.input = input.tag.get_property("INPUT", config.input);
-                       }
-                       else
-                       if(input.tag.title_is("LEVEL"))
-                       {
-                               double x = input.tag.get_property("X", (double)0);
-                               double y = input.tag.get_property("Y", (double)0);
-                               compressor_point_t point = { x, y };
-
-                               config.levels.append(point);
-                       }
+       while( !(result=input.read_tag()) ) {
+               if( input.tag.title_is("COMPRESSOR") ) {
+//                     band_config->readahead_len = input.tag.get_property("READAHEAD_LEN", band_config->readahead_len);
+                       band_config->attack_len = input.tag.get_property("ATTACK_LEN", band_config->attack_len);
+                       band_config->release_len = input.tag.get_property("RELEASE_LEN", band_config->release_len);
+                       config.trigger = input.tag.get_property("TRIGGER", config.trigger);
+                       config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
+                       config.input = input.tag.get_property("INPUT", config.input);
+               }
+               else if( input.tag.title_is("COMPRESSORBAND") ) {
+                       band_config->read_data(&input, 0);
                }
        }
+       config.boundaries();
 }
 
 void CompressorEffect::save_data(KeyFrame *keyframe)
 {
        FileXML output;
+       BandConfig *band_config = &config.bands[0];
        output.set_shared_output(keyframe->xbuf);
 
        output.tag.set_title("COMPRESSOR");
        output.tag.set_property("TRIGGER", config.trigger);
-       output.tag.set_property("REACTION_LEN", config.reaction_len);
-       output.tag.set_property("DECAY_LEN", config.decay_len);
        output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
        output.tag.set_property("INPUT", config.input);
+       output.tag.set_property("ATTACK_LEN", band_config->attack_len);
+       output.tag.set_property("RELEASE_LEN", band_config->release_len);
        output.append_tag();
+       output.append_newline();
+
+       band_config->save_data(&output, 0, 0);
        output.tag.set_title("/COMPRESSOR");
        output.append_tag();
        output.append_newline();
-
-
-       for(int i = 0; i < config.levels.total; i++)
-       {
-               output.tag.set_title("LEVEL");
-               output.tag.set_property("X", config.levels.values[i].x);
-               output.tag.set_property("Y", config.levels.values[i].y);
-
-               output.append_tag();
-               output.tag.set_title("/LEVEL");
-               output.append_tag();
-               output.append_newline();
-       }
-
        output.terminate_string();
 }
 
@@ -190,469 +159,184 @@ void CompressorEffect::save_data(KeyFrame *keyframe)
 void CompressorEffect::update_gui()
 {
        if( !thread ) return;
-       CompressorWindow *window = (CompressorWindow*)thread->window;
-// load_configuration,read_data deletes levels
-       window->lock_window("CompressorEffect::update_gui");
-       if( load_configuration() )
+// user can't change levels when loading configuration
+       thread->window->lock_window("CompressorEffect::update_gui");
+       CompressorWindow *window = (CompressorWindow *)thread->window;
+// Can't update points if the user is editing
+       int reconfigured = window->canvas->is_dragging() ? 0 :
+               load_configuration();
+       if( reconfigured )
                window->update();
+// delete frames up to current tracking position
+       int dir = get_tracking_direction() == PLAY_FORWARD ? 1 : -1;
+       double tracking_position = get_tracking_position();
+       CompressorGainFrame *gain_frame = (CompressorGainFrame *)
+               get_gui_frame(tracking_position, dir);
+       if( gain_frame ) {
+               window->update_meter(gain_frame);
+               delete gain_frame;
+       }
        window->unlock_window();
 }
 
+void CompressorEffect::render_stop()
+{
+       if( !thread ) return;
+       thread->window->lock_window("CompressorEffect::render_stop");
+       CompressorWindow *window = (CompressorWindow *)thread->window;
+       window->in->reset();
+       window->gain_change->update(1, 0);
+       window->unlock_window();
+}
 
 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
 NEW_WINDOW_MACRO(CompressorEffect, CompressorWindow)
 
-
-
-
-int CompressorEffect::process_buffer(int64_t size,
-               Samples **buffer,
-               int64_t start_position,
-               int sample_rate)
+int CompressorEffect::process_buffer(int64_t size, Samples **buffer,
+               int64_t start_position, int sample_rate)
 {
+       int channels = PluginClient::total_in_buffers;
+       BandConfig *band_config = &config.bands[0];
+       int sign = get_direction() == PLAY_REVERSE ? -1 : 1;
        load_configuration();
 
-// Calculate linear transfer from db
-       levels.remove_all();
-       for(int i = 0; i < config.levels.total; i++)
-       {
-               levels.append();
-               levels.values[i].x = DB::fromdb(config.levels.values[i].x);
-               levels.values[i].y = DB::fromdb(config.levels.values[i].y);
+// restart after seeking
+       if( last_position != start_position ) {
+               last_position = start_position;
+               if( engine )
+                       engine->reset();
+               input_size = 0;
+               input_start = start_position;
        }
-       min_x = DB::fromdb(config.min_db);
-       min_y = DB::fromdb(config.min_db);
-       max_x = 1.0;
-       max_y = 1.0;
-
-
-       int reaction_samples = (int)(config.reaction_len * sample_rate + 0.5);
-       int decay_samples = (int)(config.decay_len * sample_rate + 0.5);
-       int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1);
 
-       CLAMP(reaction_samples, -1000000, 1000000);
-       CLAMP(decay_samples, reaction_samples, 1000000);
-       CLAMP(decay_samples, 1, 1000000);
-       if(labs(reaction_samples) < 1) reaction_samples = 1;
-       if(labs(decay_samples) < 1) decay_samples = 1;
-
-       int total_buffers = get_total_buffers();
-       if(reaction_samples >= 0)
-       {
-               if(target_current_sample < 0) target_current_sample = reaction_samples;
-               for(int i = 0; i < total_buffers; i++)
-               {
-                       read_samples(buffer[i],
-                               i,
-                               sample_rate,
-                               start_position,
-                               size);
-               }
-
-               double current_slope = (next_target - previous_target) /
-                       reaction_samples;
-               double *trigger_buffer = buffer[trigger]->get_data();
-               for(int i = 0; i < size; i++)
-               {
-// Get slope required to reach current sample from smoothed sample over reaction
-// length.
-                       double sample = 0.;
-                       switch(config.input)
-                       {
-                               case CompressorConfig::MAX:
-                               {
-                                       double max = 0;
-                                       for(int j = 0; j < total_buffers; j++)
-                                       {
-                                               sample = fabs(buffer[j]->get_data()[i]);
-                                               if(sample > max) max = sample;
-                                       }
-                                       sample = max;
-                                       break;
-                               }
-
-                               case CompressorConfig::TRIGGER:
-                                       sample = fabs(trigger_buffer[i]);
-                                       break;
-
-                               case CompressorConfig::SUM:
-                               {
-                                       double max = 0;
-                                       for(int j = 0; j < total_buffers; j++)
-                                       {
-                                               sample = fabs(buffer[j]->get_data()[i]);
-                                               max += sample;
-                                       }
-                                       sample = max;
-                                       break;
-                               }
-                       }
+// Calculate linear transfer from db
+       int nbands = band_config->levels.size();
+       while( levels.size() < nbands ) levels.append();
 
-                       double new_slope = (sample - current_value) /
-                               reaction_samples;
-
-// Slope greater than current slope
-                       if(new_slope >= current_slope &&
-                               (current_slope >= 0 ||
-                               new_slope >= 0))
-                       {
-                               next_target = sample;
-                               previous_target = current_value;
-                               target_current_sample = 0;
-                               target_samples = reaction_samples;
-                               current_slope = new_slope;
-                       }
-                       else
-                       if(sample > next_target && current_slope < 0)
-                       {
-                               next_target = sample;
-                               previous_target = current_value;
-                               target_current_sample = 0;
-                               target_samples = decay_samples;
-                               current_slope = (sample - current_value) / decay_samples;
-                       }
-// Current smoothed sample came up without finding higher slope
-                       if(target_current_sample >= target_samples)
-                       {
-                               next_target = sample;
-                               previous_target = current_value;
-                               target_current_sample = 0;
-                               target_samples = decay_samples;
-                               current_slope = (sample - current_value) / decay_samples;
-                       }
+       for( int i=0; i<band_config->levels.total; ++i ) {
+               levels.values[i].x = DB::fromdb(band_config->levels.values[i].x);
+               levels.values[i].y = DB::fromdb(band_config->levels.values[i].y);
+       }
+//     min_x = DB::fromdb(config.min_db);
+//     min_y = DB::fromdb(config.min_db);
+//     max_x = 1.0;
+//     max_y = 1.0;
 
-// Update current value and store gain
-                       current_value = (next_target * target_current_sample +
-                               previous_target * (target_samples - target_current_sample)) /
-                               target_samples;
 
-                       target_current_sample++;
+       int attack_samples;
+       int release_samples;
+       int preview_samples;
 
-                       if(config.smoothing_only)
-                       {
-                               for(int j = 0; j < total_buffers; j++)
-                                       buffer[j]->get_data()[i] = current_value;
-                       }
-                       else
-                       {
-                               double gain = calculate_gain(current_value);
-                               for(int j = 0; j < total_buffers; j++)
-                               {
-                                       buffer[j]->get_data()[i] *= gain;
-                               }
-                       }
-               }
+       if( !engine ) {
+               engine = new CompressorEngine(&config, 0);
+               engine->gui_frame_samples = sample_rate / TRACKING_RATE + 1;
        }
-       else
-       {
-               if(target_current_sample < 0) target_current_sample = target_samples;
-               int64_t preview_samples = -reaction_samples;
+       engine->calculate_ranges(&attack_samples, &release_samples,
+                       &preview_samples, sample_rate);
 
 // Start of new buffer is outside the current buffer.  Start buffer over.
-               if(start_position < input_start ||
-                       start_position >= input_start + input_size)
-               {
-                       input_size = 0;
-                       input_start = start_position;
-               }
-               else
+       if( get_direction() == PLAY_FORWARD &&
+               (start_position < input_start ||
+               start_position >= input_start + input_size)
+               ||
+               get_direction() == PLAY_REVERSE &&
+               (start_position > input_start ||
+               start_position <= input_start - input_size) ) {
+// printf("CompressorEffect::process_buffer %d start_position=%ld input_start=%ld input_size=%ld\n",
+// __LINE__, start_position, input_start, input_size);
+               input_size = 0;
+               input_start = start_position;
+       }
+       else
 // Shift current buffer so the buffer starts on start_position
-               if(start_position > input_start &&
-                       start_position < input_start + input_size)
-               {
-                       if(input_buffer)
-                       {
-                               int len = input_start + input_size - start_position;
-                               for(int i = 0; i < total_buffers; i++)
-                               {
-                                       memcpy(input_buffer[i]->get_data(),
-                                               input_buffer[i]->get_data() + (start_position - input_start),
-                                               len * sizeof(double));
-                               }
-                               input_size = len;
-                               input_start = start_position;
+       if( get_direction() == PLAY_FORWARD &&
+               start_position > input_start &&
+               start_position < input_start + input_size
+               ||
+               get_direction() == PLAY_REVERSE &&
+               start_position < input_start &&
+               start_position > input_start - input_size ) {
+               if( input_buffer ) {
+                       int len, offset;
+                       if( get_direction() == PLAY_FORWARD ) {
+                               len = input_start + input_size - start_position;
+                               offset = start_position - input_start;
                        }
-               }
-
-// Expand buffer to handle preview size
-               if(size + preview_samples > input_allocated)
-               {
-                       Samples **new_input_buffer = new Samples*[total_buffers];
-                       for(int i = 0; i < total_buffers; i++)
-                       {
-                               new_input_buffer[i] = new Samples(size + preview_samples);
-                               if(input_buffer)
-                               {
-                                       memcpy(new_input_buffer[i]->get_data(),
-                                               input_buffer[i]->get_data(),
-                                               input_size * sizeof(double));
-                                       delete input_buffer[i];
-                               }
+                       else {
+                               len = start_position - (input_start - input_size);
+                               offset = input_start - start_position;
                        }
-                       if(input_buffer) delete [] input_buffer;
 
-                       input_allocated = size + preview_samples;
-                       input_buffer = new_input_buffer;
-               }
-
-// Append data to input buffer to construct readahead area.
-#define MAX_FRAGMENT_SIZE 131072
-               while(input_size < size + preview_samples)
-               {
-                       int fragment_size = MAX_FRAGMENT_SIZE;
-                       if(fragment_size + input_size > size + preview_samples)
-                               fragment_size = size + preview_samples - input_size;
-                       for(int i = 0; i < total_buffers; i++)
-                       {
-                               input_buffer[i]->set_offset(input_size);
-//printf("CompressorEffect::process_buffer %d %p %d\n", __LINE__, input_buffer[i], input_size);
-                               read_samples(input_buffer[i],
-                                       i,
-                                       sample_rate,
-                                       input_start + input_size,
-                                       fragment_size);
-                               input_buffer[i]->set_offset(0);
+                       for( int i = 0; i < channels; i++ ) {
+                               memcpy(input_buffer[i]->get_data(),
+                                       input_buffer[i]->get_data() + offset,
+                                       len * sizeof(double));
                        }
-                       input_size += fragment_size;
+                       input_size = len;
+                       input_start = start_position;
                }
+       }
 
-
-               double current_slope = (next_target - previous_target) /
-                       target_samples;
-               double *trigger_buffer = input_buffer[trigger]->get_data();
-               for(int i = 0; i < size; i++)
-               {
-// Get slope from current sample to every sample in preview_samples.
-// Take highest one or first one after target_samples are up.
-
-// For optimization, calculate the first slope we really need.
-// Assume every slope up to the end of preview_samples has been calculated and
-// found <= to current slope.
-            int first_slope = preview_samples - 1;
-// Need new slope immediately
-                       if(target_current_sample >= target_samples)
-                               first_slope = 1;
-                       for(int j = first_slope;
-                               j < preview_samples;
-                               j++)
-                       {
-                               double sample = 0.;
-                               switch(config.input)
-                               {
-                                       case CompressorConfig::MAX:
-                                       {
-                                               double max = 0;
-                                               for(int k = 0; k < total_buffers; k++)
-                                               {
-                                                       sample = fabs(input_buffer[k]->get_data()[i + j]);
-                                                       if(sample > max) max = sample;
-                                               }
-                                               sample = max;
-                                               break;
-                                       }
-
-                                       case CompressorConfig::TRIGGER:
-                                               sample = fabs(trigger_buffer[i + j]);
-                                               break;
-
-                                       case CompressorConfig::SUM:
-                                       {
-                                               double max = 0;
-                                               for(int k = 0; k < total_buffers; k++)
-                                               {
-                                                       sample = fabs(input_buffer[k]->get_data()[i + j]);
-                                                       max += sample;
-                                               }
-                                               sample = max;
-                                               break;
-                                       }
-                               }
-
-
-
-
-
-
-                               double new_slope = (sample - current_value) /
-                                       j;
-// Got equal or higher slope
-                               if(new_slope >= current_slope &&
-                                       (current_slope >= 0 ||
-                                       new_slope >= 0))
-                               {
-                                       target_current_sample = 0;
-                                       target_samples = j;
-                                       current_slope = new_slope;
-                                       next_target = sample;
-                                       previous_target = current_value;
-                               }
-                               else
-                               if(sample > next_target && current_slope < 0)
-                               {
-                                       target_current_sample = 0;
-                                       target_samples = decay_samples;
-                                       current_slope = (sample - current_value) /
-                                               decay_samples;
-                                       next_target = sample;
-                                       previous_target = current_value;
-                               }
-
-// Hit end of current slope range without finding higher slope
-                               if(target_current_sample >= target_samples)
-                               {
-                                       target_current_sample = 0;
-                                       target_samples = decay_samples;
-                                       current_slope = (sample - current_value) / decay_samples;
-                                       next_target = sample;
-                                       previous_target = current_value;
-                               }
-                       }
-
-// Update current value and multiply gain
-                       current_value = (next_target * target_current_sample +
-                               previous_target * (target_samples - target_current_sample)) /
-                               target_samples;
-//buffer[0][i] = current_value;
-                       target_current_sample++;
-
-                       if(config.smoothing_only)
-                       {
-                               for(int j = 0; j < total_buffers; j++)
-                               {
-                                       buffer[j]->get_data()[i] = current_value;
-                               }
-                       }
-                       else
-                       {
-                               double gain = calculate_gain(current_value);
-                               for(int j = 0; j < total_buffers; j++)
-                               {
-                                       buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
-                               }
+// Expand buffer to handle preview size
+       if( size + preview_samples > input_allocated ) {
+               Samples **new_input_buffer = new Samples*[channels];
+               for( int i = 0; i < channels; i++ ) {
+                       new_input_buffer[i] = new Samples(size + preview_samples);
+                       if( input_buffer ) {
+                               memcpy(new_input_buffer[i]->get_data(),
+                                       input_buffer[i]->get_data(),
+                                       input_size * sizeof(double));
+                               delete input_buffer[i];
                        }
                }
+               if( input_buffer ) delete [] input_buffer;
 
-
-
+               input_allocated = size + preview_samples;
+               input_buffer = new_input_buffer;
        }
 
-
-
-
-
-       return 0;
-}
-
-double CompressorEffect::calculate_output(double x)
-{
-       if(x > 0.999) return 1.0;
-
-       for(int i = levels.total - 1; i >= 0; i--)
-       {
-               if(levels.values[i].x <= x)
-               {
-                       if(i < levels.total - 1)
-                       {
-                               return levels.values[i].y +
-                                       (x - levels.values[i].x) *
-                                       (levels.values[i + 1].y - levels.values[i].y) /
-                                       (levels.values[i + 1].x - levels.values[i].x);
-                       }
-                       else
-                       {
-                               return levels.values[i].y +
-                                       (x - levels.values[i].x) *
-                                       (max_y - levels.values[i].y) /
-                                       (max_x - levels.values[i].x);
-                       }
+// Append data to input buffer to construct readahead area.
+       if( input_size < size + preview_samples ) {
+               int fragment_size = size + preview_samples - input_size;
+               for( int i = 0; i < channels; i++ ) {
+                       input_buffer[i]->set_offset(input_size);
+                       read_samples(input_buffer[i], i, sample_rate,
+                               input_start + input_size * sign, fragment_size);
+                       input_buffer[i]->set_offset(0);
                }
+               input_size += fragment_size;
        }
 
-       if(levels.total)
-       {
-               return min_y +
-                       (x - min_x) *
-                       (levels.values[0].y - min_y) /
-                       (levels.values[0].x - min_x);
-       }
-       else
-               return x;
-}
+       engine->process(buffer, input_buffer, size,
+               sample_rate, PluginClient::total_in_buffers, start_position);
 
+       double start_pos = (double)start_position / sample_rate;
+       for( int i = 0; i < engine->gui_gains.size(); i++ ) {
+               CompressorGainFrame *gain_frame = new CompressorGainFrame();
+               gain_frame->position = start_pos + sign*engine->gui_offsets[i];
+               gain_frame->gain = engine->gui_gains[i];
+               gain_frame->level = engine->gui_levels[i];
+               add_gui_frame(gain_frame);
+       }
 
-double CompressorEffect::calculate_gain(double input)
-{
-//     double x_db = DB::todb(input);
-//     double y_db = config.calculate_db(x_db);
-//     double y_linear = DB::fromdb(y_db);
-       double y_linear = calculate_output(input);
-       double gain;
-       if(input != 0)
-               gain = y_linear / input;
-       else
-               gain = 100000;
-       return gain;
+       last_position += sign * size;
+       return 0;
 }
 
 
-
-
-
-
-
-
-
-
 CompressorConfig::CompressorConfig()
+ : CompressorConfigBase(1)
 {
-       reaction_len = 1.0;
-       min_db = -80.0;
-       min_x = min_db;
-       min_y = min_db;
-       max_x = 0;
-       max_y = 0;
-       trigger = 0;
-       input = CompressorConfig::TRIGGER;
-       smoothing_only = 0;
-       decay_len = 1.0;
 }
 
 void CompressorConfig::copy_from(CompressorConfig &that)
 {
-       this->reaction_len = that.reaction_len;
-       this->decay_len = that.decay_len;
-       this->min_db = that.min_db;
-       this->min_x = that.min_x;
-       this->min_y = that.min_y;
-       this->max_x = that.max_x;
-       this->max_y = that.max_y;
-       this->trigger = that.trigger;
-       this->input = that.input;
-       this->smoothing_only = that.smoothing_only;
-       levels.remove_all();
-       for(int i = 0; i < that.levels.total; i++)
-               this->levels.append(that.levels.values[i]);
+       CompressorConfigBase::copy_from(that);
 }
 
 int CompressorConfig::equivalent(CompressorConfig &that)
 {
-       if(!EQUIV(this->reaction_len, that.reaction_len) ||
-               !EQUIV(this->decay_len, that.decay_len) ||
-               this->trigger != that.trigger ||
-               this->input != that.input ||
-               this->smoothing_only != that.smoothing_only)
-               return 0;
-       if(this->levels.total != that.levels.total) return 0;
-       for(int i = 0;
-               i < this->levels.total && i < that.levels.total;
-               i++)
-       {
-               compressor_point_t *this_level = &this->levels.values[i];
-               compressor_point_t *that_level = &that.levels.values[i];
-               if(!EQUIV(this_level->x, that_level->x) ||
-                       !EQUIV(this_level->y, that_level->y))
-                       return 0;
-       }
+       return CompressorConfigBase::equivalent(that);
        return 1;
 }
 
@@ -665,395 +349,149 @@ void CompressorConfig::interpolate(CompressorConfig &prev,
        copy_from(prev);
 }
 
-int CompressorConfig::total_points()
-{
-       if(!levels.total)
-               return 1;
-       else
-               return levels.total;
-}
-
-void CompressorConfig::dump()
-{
-       printf("CompressorConfig::dump\n");
-       for(int i = 0; i < levels.total; i++)
-       {
-               printf("        %f %f\n", levels.values[i].x, levels.values[i].y);
-       }
-}
-
-
-double CompressorConfig::get_y(int number)
-{
-       if(!levels.total)
-               return 1.0;
-       else
-       if(number >= levels.total)
-               return levels.values[levels.total - 1].y;
-       else
-               return levels.values[number].y;
-}
-
-double CompressorConfig::get_x(int number)
-{
-       if(!levels.total)
-               return 0.0;
-       else
-       if(number >= levels.total)
-               return levels.values[levels.total - 1].x;
-       else
-               return levels.values[number].x;
-}
-
-double CompressorConfig::calculate_db(double x)
-{
-       if(x > -0.001) return 0.0;
-
-       for(int i = levels.total - 1; i >= 0; i--)
-       {
-               if(levels.values[i].x <= x)
-               {
-                       if(i < levels.total - 1)
-                       {
-                               return levels.values[i].y +
-                                       (x - levels.values[i].x) *
-                                       (levels.values[i + 1].y - levels.values[i].y) /
-                                       (levels.values[i + 1].x - levels.values[i].x);
-                       }
-                       else
-                       {
-                               return levels.values[i].y +
-                                       (x - levels.values[i].x) *
-                                       (max_y - levels.values[i].y) /
-                                       (max_x - levels.values[i].x);
-                       }
-               }
-       }
-
-       if(levels.total)
-       {
-               return min_y +
-                       (x - min_x) *
-                       (levels.values[0].y - min_y) /
-                       (levels.values[0].x - min_x);
-       }
-       else
-               return x;
-}
-
-
-int CompressorConfig::set_point(double x, double y)
-{
-       for(int i = levels.total - 1; i >= 0; i--)
-       {
-               if(levels.values[i].x < x)
-               {
-                       levels.append();
-                       i++;
-                       for(int j = levels.total - 2; j >= i; j--)
-                       {
-                               levels.values[j + 1] = levels.values[j];
-                       }
-                       levels.values[i].x = x;
-                       levels.values[i].y = y;
-
-                       return i;
-               }
-       }
-
-       levels.append();
-       for(int j = levels.total - 2; j >= 0; j--)
-       {
-               levels.values[j + 1] = levels.values[j];
-       }
-       levels.values[0].x = x;
-       levels.values[0].y = y;
-       return 0;
-}
-
-void CompressorConfig::remove_point(int number)
-{
-       for(int j = number; j < levels.total - 1; j++)
-       {
-               levels.values[j] = levels.values[j + 1];
-       }
-       levels.remove();
-}
-
-void CompressorConfig::optimize()
+CompressorWindow::CompressorWindow(CompressorEffect *plugin)
+ : PluginClientWindow(plugin, xS(650), yS(480), xS(650), yS(480), 0)
 {
-       int done = 0;
-
-       while(!done)
-       {
-               done = 1;
-
-
-               for(int i = 0; i < levels.total - 1; i++)
-               {
-                       if(levels.values[i].x >= levels.values[i + 1].x)
-                       {
-                               done = 0;
-                               for(int j = i + 1; j < levels.total - 1; j++)
-                               {
-                                       levels.values[j] = levels.values[j + 1];
-                               }
-                               levels.remove();
-                       }
-               }
-
-       }
+       this->plugin = plugin;
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-CompressorWindow::CompressorWindow(CompressorEffect *plugin)
- : PluginClientWindow(plugin,
-       xS(650),
-       yS(480),
-       xS(650),
-       yS(480),
-       0)
+CompressorWindow::~CompressorWindow()
 {
-       this->plugin = plugin;
+//     delete readahead;
+       delete attack;
+       delete release;
+       delete trigger;
+       delete x_text;
+       delete y_text;
 }
 
 void CompressorWindow::create_objects()
 {
-       int xs10 = xS(10), xs20 = xS(20), xs50 = xS(50), xs110 = xS(110);
-       int ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys60 = yS(60), ys70 = yS(70);
-       int x = xS(35), y = yS(10);
+       int margin = client->get_theme()->widget_border;
+       int x = margin, y = margin;
        int control_margin = xS(130);
-
-       add_subwindow(canvas = new CompressorCanvas(plugin,
-               x,
-               y,
-               get_w() - x - control_margin - xs10,
-               get_h() - y - ys70));
-       canvas->set_cursor(CROSS_CURSOR, 0, 0);
+       int canvas_y2 = get_h()-yS(35);
+       BC_Title *title;
+
+       add_subwindow(title = new BC_Title(x, y, "In:"));
+       int y2 = y + title->get_h() + margin;
+       EDLSession *session = plugin->get_edl()->session;
+       add_subwindow(in = new BC_Meter(x, y2, METER_VERT, canvas_y2 - y2,
+               session->min_meter_db, session->max_meter_db, session->meter_format,
+               1, // use_titles
+               -1)); // span
+       x += in->get_w() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, "Gain:"));
+       add_subwindow(gain_change = new BC_Meter(x, y2, METER_VERT,
+               canvas_y2 - y2, MIN_GAIN_CHANGE, MAX_GAIN_CHANGE, METER_DB,
+               1, // use_titles
+               -1, // span
+               0, // is_downmix
+               1)); // is_gain_change
+//     gain_change->update(1, 0);
+
+       x += gain_change->get_w() + xS(35);
+       add_subwindow(title = new BC_Title(x, y, _("Sound level (Press shift to snap to grid):")));
+       y += title->get_h() + 1;
+       add_subwindow(canvas = new CompressorCanvas(plugin, this, x, y,
+               get_w() - x - control_margin - xS(10), canvas_y2 - y));
        x = get_w() - control_margin;
-       add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
-       y += ys20;
-       add_subwindow(reaction = new CompressorReaction(plugin, x, y));
-       y += ys30;
-       add_subwindow(new BC_Title(x, y, _("Decay secs:")));
-       y += ys20;
-       add_subwindow(decay = new CompressorDecay(plugin, x, y));
-       y += ys30;
-       add_subwindow(new BC_Title(x, y, _("Trigger Type:")));
-       y += ys20;
+
+//     add_subwindow(new BC_Title(x, y, _("Lookahead secs:")));
+//     y += title->get_h() + margin;
+//     readahead = new CompressorLookAhead(plugin, this, x, y);
+//     readahead->create_objects();
+//     y += readahead->get_h() + margin;
+
+       add_subwindow(new BC_Title(x, y, _("Attack secs:")));
+       y += title->get_h() + margin;
+       attack = new CompressorAttack(plugin, this, x, y);
+       attack->create_objects();
+       y += attack->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Release secs:")));
+       y += title->get_h() + margin;
+       release = new CompressorRelease(plugin, this, x, y);
+       release->create_objects();
+       y += release->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Trigger Type:")));
+       y += title->get_h() + margin;
        add_subwindow(input = new CompressorInput(plugin, x, y));
        input->create_objects();
-       y += ys30;
-       add_subwindow(new BC_Title(x, y, _("Trigger:")));
-       y += ys20;
-       add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
-       if(plugin->config.input != CompressorConfig::TRIGGER) trigger->disable();
-       y += ys30;
-       add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
-       y += ys60;
-       add_subwindow(clear = new CompressorClear(plugin, x, y));
-       x = xs10;
-   &nb