new/reworked audio plugins ported from hv72 compressor/multi/reverb, glyph workaround...
authorGood Guy <[email protected]>
Wed, 11 Dec 2019 16:17:30 +0000 (09:17 -0700)
committerGood Guy <[email protected]>
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 05acba1a7200b023e6d71b3dbd15248e70e88098..4a4c0e5849c5e407d399d990291220de4ab27190 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 f8c0bc111a95c22a5e63df48da1d0a2c4dfd70a2..e4f782d4c0936601c0875a926e3016e1d1e35c9f 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 362a5e1f43e88af652578204398d1a180b11ce3c..adc1c4acb8c557357588886f45d73feb91bce5c3 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 6a7dc0f5ffe68ed4a8cb52b36785a569ad53174f..ebcfdc1343cabb61b7d8c3ce1194c5e250911fd6 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 7dbfde86142c4d7e9157965069eade20b1fec116..14e0ebfe3285981c28c53c47695769e837950b72 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 2bf178ab8927d3b670e61defb7a502df38dade41..03d8e83ece22dabb9d211683c0aa1974965d8326 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 401a5faa1e3a56e2a452df630f3c1658a127ea9d..f78a45bd1e092a990864e9efd716a88cd430d954 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 482c6fb04bb01f197be96560e167c5218b8d3a8d..9b83153ab071c44d82aad55c0b734378b0f40f6a 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 06e538489b77fb1b8239fea72a9320a6e6d38107..698c4e839a11340f12867a1c3a0150246f2cb98b 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 03e265e6b75525023afea2276c49852e1ce2c89d..e3bf6b622d3fbaefb4f144bbe4b57d88c3f91223 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 513e1bfe3529eb1e918188af279e5188e1d63409..b0e7877257dbffe458926bacbc536082340b0715 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 d9b636dff1debb0cd6e10756be532b2c50f2b7d2..f95f57581b0dcf7f3ee025d5620912069624d8ca 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 ed646b06e2d3fbd74c269eb1834f67c33476c202..c82d35083daaa33014d0d315a8455b343656127c 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 0690da07bd8bd1db2119e1dfb06e9a9eee22c9ce..d9d9a48af851b5ed30df9bb1cdd323dac2e60a7a 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 330a958589eabe00b87c44d07a8ddebdf851b9c2..20d989997b31058ebaeb086b21b8a63131086707 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 8601bc0a195811e71e878e5f796aca290684da26..b1434b022ed1c4ff030c443ecdad5e872ab3a3b9 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 b0761d0da24fd53fcbffaa923fd8431bc4e7ab88..e9b43850b7f15b3cf91cc77a4e09ba2fc4ac3c8d 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 da9a697716a8be5b23c520055305ebdc83d2bfe4..dcafd890cb73360f37bc07e854fdb01b7e469708 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 96f5540b140bdfc1afd97369b245947cbdb3cd53..67a1148f6de91ce04c3bcf1abbab3e785bc37f69 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 f9ee65b6896790694df23f391e44e72c08057ebb..494af128379068016534f98a53e3251594efe213 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 68724f278e669cddf0c483984ec568be29e21885..02f19f33ac35d7090d48413186b2c62e12116d32 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 5be37a4bd7fa750ab9b4f730550fcd26611e4a88..16c2c5cb6a6c9a1e752703e6a1dbb459a98b1411 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 c65d1b952d83713c34e674475ce095e457b6163c..82dd7c802329f85cd65dde4eea3c839ae0564cfe 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 7f5fba2c780a61a486a260a82ccacd7ce9fbf67c..3610c7a29d989a621980f3d006c847b054b2ce16 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 fb3ff4df0b8cc6150e5639dca4f7f4dfc50e4b69..d35e475096b88eda2685b7b0aa718370b3f6b381 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 7055ee153caa72a28c72b97e8d59cb9801ad26f6..1b1e4b38111812fffe90d11b2f350e189b217376 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 ef27522f55b2f6077e3f7c0ec8e6887978ff5f75..602557f53f38e3249595582f0ae4003c4b69c84b 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 e4eb2bd91dd5b18c1751f430fa2617c865ca8173..673a684a166c39a01a1c1925014de509afb27de5 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 9a5cf807c43100a51b834e7f605477d832f5e4dd..e43c52b7f4cb41cb38cbb0ef630bc36794deb01a 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 b979f915b82f815e017c5155094ab7388031cb94..6b91b33c6e067011074d4e76cf3c9393d03baf71 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 0d9f95d4eede1a54100c45376e4b53a00fae282f..e979d5cae48965178314a54fde0f5d3d9f58cfaf 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 36add9c76656825749f2cf2851b4fdd95c12ad0d..153aab37ab354fd80aded0cbc98db4e25fa9d94c 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 0c580defded3c95b8ff8836fbdf562c98ebd9792..3accf28153c2e13ed4eadcbf87ad76f1907da5ab 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 9c2f1d27106b7f4be2cee7ec37f70c5bd91edf04..e0a170dcc88711911d48ed9643f64a9a29ed3cc1 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 7456b662de9e5c62a38696fc86af14a5f4fd8fbf..7e2243ef498b673f29798760756a55a1f0be38fb 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 2620b4d054e30a216cae4fcd7072ea272a750568..1f464d23bf7afc74b8965e72dadc5d97f8a8df7e 100644 (file)
@@ -42,6 +42,7 @@ DIRS = $(OPENCV_OBJS) \
        color3way \
        colorbalance \
        compressor \
+       compressormulti \
        crikey \
        crop \
        crossfade \
index 893d9917af18cb837a8735af995fba52099315a3..b7294bdd0fbc2d8c6c7b6f5480aa33a83893fa57 100644 (file)
@@ -6,5 +6,4 @@ PLUGIN = compressor
 
 include ../../plugin_config
 
-
 $(OBJDIR)/compressor.o: compressor.C
index b167c6ee3dfa0d6b64a5212366b183e0c9d3f4d8..956d2ae7279bc41beb3b9723f0bf52f61e685aa4 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;
-       y = get_h() - ys40;
-       add_subwindow(new BC_Title(x, y, _("Point:")));
-       x += xs50;
-       add_subwindow(x_text = new CompressorX(plugin, x, y));
-       x += xs110;
-       add_subwindow(new BC_Title(x, y, _("x")));
-       x += xs20;
-       add_subwindow(y_text = new CompressorY(plugin, x, y));
-       draw_scales();
-
-       update_canvas();
-       show_window();
-}
+       y += input->get_h() + margin;
 
-void CompressorWindow::draw_scales()
-{
-       draw_3d_border(canvas->get_x() - 2,
-               canvas->get_y() - 2,
-               canvas->get_w() + 4,
-               canvas->get_h() + 4,
-               get_bg_color(),
-               BLACK,
-               MDGREY,
-               get_bg_color());
-
-
-       set_font(SMALLFONT);
-       set_color(get_resources()->default_text_color);
-
-#define DIVISIONS 8
-       for(int i = 0; i <= DIVISIONS; i++)
-       {
-               int y = canvas->get_y() + yS(10) + canvas->get_h() / DIVISIONS * i;
-               int x = canvas->get_x() - xS(30);
-               char string[BCTEXTLEN];
-
-               sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
-               draw_text(x, y, string);
-
-               int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
-               int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
-               for(int j = 0; j < 10; j++)
-               {
-                       y = y1 + (y2 - y1) * j / 10;
-                       if(j == 0)
-                       {
-                               draw_line(canvas->get_x() - xS(10), y, canvas->get_x(), y);
-                       }
-                       else
-                       if(i < DIVISIONS)
-                       {
-                               draw_line(canvas->get_x() - xS(5), y, canvas->get_x(), y);
-                       }
-               }
-       }
+       add_subwindow(title = new BC_Title(x, y, _("Trigger:")));
+       y += title->get_h() + margin;
+       trigger = new CompressorTrigger(plugin, this, x, y);
+       trigger->create_objects();
+       if( plugin->config.input != CompressorConfig::TRIGGER ) trigger->disable();
+       y += trigger->get_h() + margin;
 
+       add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
+       y += smooth->get_h() + margin;
 
-       for(int i = 0; i <= DIVISIONS; i++)
-       {
-               int y = canvas->get_h() + yS(30);
-               int x = canvas->get_x() + (canvas->get_w() - xS(10)) / DIVISIONS * i;
-               char string[BCTEXTLEN];
-
-               sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
-               draw_text(x, y, string);
-
-               int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
-               int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
-               for(int j = 0; j < 10; j++)
-               {
-                       x = x1 + (x2 - x1) * j / 10;
-                       if(j == 0)
-                       {
-                               draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(10));
-                       }
-                       else
-                       if(i < DIVISIONS)
-                       {
-                               draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(5));
-                       }
-               }
-       }
+       add_subwindow(title = new BC_Title(x, y, _("Output:")));
+       y += title->get_h();
+       y_text = new CompressorY(plugin, this, x, y);
+       y_text->create_objects();
+       y += y_text->get_h() + margin;
 
+       add_subwindow(title = new BC_Title(x, y, _("Input:")));
+       y += title->get_h();
+       x_text = new CompressorX(plugin, this, x, y);
+       x_text->create_objects();
+       y += x_text->get_h() + margin;
 
+       add_subwindow(clear = new CompressorClear(plugin, x, y));
+       x = xS(10);
+       y = get_h() - yS(40);
 
-       flash();
+       canvas->create_objects();
+       canvas->update();
+       show_window();
 }
 
+
 void CompressorWindow::update()
 {
        update_textboxes();
-       update_canvas();
+       canvas->update();
+}
+
+void CompressorWindow::update_meter(CompressorGainFrame *gain_frame)
+{
+       gain_change->update(gain_frame->gain, 0);
+       in->update(gain_frame->level, 0);
 }
 
 void CompressorWindow::update_textboxes()
 {
-       if(atol(trigger->get_text()) != plugin->config.trigger)
+       BandConfig *band_config = &plugin->config.bands[0];
+
+       if( atol(trigger->get_text()) != plugin->config.trigger )
                trigger->update((int64_t)plugin->config.trigger);
-       if(strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)))
+       if( strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)) )
                input->set_text(CompressorInput::value_to_text(plugin->config.input));
 
-       if(plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled())
+       if( plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled() )
                trigger->disable();
        else
-       if(plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled())
+       if( plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled() )
                trigger->enable();
 
-       if(!EQUIV(atof(reaction->get_text()), plugin->config.reaction_len))
-               reaction->update((float)plugin->config.reaction_len);
-       if(!EQUIV(atof(decay->get_text()), plugin->config.decay_len))
-               decay->update((float)plugin->config.decay_len);
+//     if( !EQUIV(atof(readahead->get_text()), band_config->readahead_len) )
+//             readahead->update((float)band_config->readahead_len);
+       if( !EQUIV(atof(attack->get_text()), band_config->attack_len) )
+               attack->update((float)band_config->attack_len);
+       if( !EQUIV(atof(release->get_text()), band_config->release_len) )
+               release->update((float)band_config->release_len);
        smooth->update(plugin->config.smoothing_only);
-       if(canvas->current_operation == CompressorCanvas::DRAG)
-       {
-               x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
-               y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
-       }
-}
-
-#define POINT_W xS(10)
-void CompressorWindow::update_canvas()
-{
-       canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
-       canvas->set_line_dashes(1);
-       canvas->set_color(GREEN);
-
-       for(int i = 1; i < DIVISIONS; i++)
-       {
-               int y = canvas->get_h() * i / DIVISIONS;
-               canvas->draw_line(0, y, canvas->get_w(), y);
-
-               int x = canvas->get_w() * i / DIVISIONS;
-               canvas->draw_line(x, 0, x, canvas->get_h());
-       }
-       canvas->set_line_dashes(0);
-
-
-       canvas->set_font(MEDIUMFONT);
-       canvas->draw_text(plugin->get_theme()->widget_border,
-               canvas->get_h() / 2,
-               _("Output"));
-       canvas->draw_text(canvas->get_w() / 2 - canvas->get_text_width(MEDIUMFONT, _("Input")) / 2,
-               canvas->get_h() - plugin->get_theme()->widget_border,
-               _("Input"));
-
-
-       canvas->set_color(WHITE);
-       canvas->set_line_width(2);
-
-       double x_db = plugin->config.min_db;
-       double y_db = plugin->config.calculate_db(x_db);
-       int y1 = (int)(y_db / plugin->config.min_db * canvas->get_h());
-
-       for(int i=1; i<canvas->get_w(); i++)
-       {
-               x_db = (1. - (double)i / canvas->get_w()) * plugin->config.min_db;
-               y_db = plugin->config.calculate_db(x_db);
-               int y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
-               canvas->draw_line(i-1, y1, i, y2);
-               y1 = y2;
+       if( canvas->current_operation == CompressorCanvas::DRAG ) {
+               x_text->update((float)band_config->levels.values[canvas->current_point].x);
+               y_text->update((float)band_config->levels.values[canvas->current_point].y);
        }
-       canvas->set_line_width(1);
-
-       int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
-       for(int i=0; i < total; i++)
-       {
-               x_db = plugin->config.get_x(i);
-               y_db = plugin->config.get_y(i);
-
-               int x = (int)((1. - x_db / plugin->config.min_db) * canvas->get_w());
-               int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
-
-               canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
-       }
-
-       canvas->flash();
 }
 
 int CompressorWindow::resize_event(int w, int h)
@@ -1062,250 +500,123 @@ int CompressorWindow::resize_event(int w, int h)
 }
 
 
-
-
-
-
-
-
-CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h)
- : BC_SubWindow(x, y, w, h, BLACK)
+CompressorCanvas::CompressorCanvas(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y, int w, int h)
+ : CompressorCanvasBase(&plugin->config, plugin, window, x, y, w, h)
 {
-       this->plugin = plugin;
-       current_operation = NONE;
-       current_point = 0;
 }
 
-int CompressorCanvas::button_press_event()
-{
-// Check existing points
-       if(is_event_win() && cursor_inside())
-       {
-               for(int i = 0; i < plugin->config.levels.total; i++)
-               {
-                       double x_db = plugin->config.get_x(i);
-                       double y_db = plugin->config.get_y(i);
-
-                       int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
-                       int y = (int)(y_db / plugin->config.min_db * get_h());
-
-                       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;
-                       }
-               }
-
-
-
-// Create new point
-               double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
-               double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
-
-               current_point = plugin->config.set_point(x_db, y_db);
-               current_operation = DRAG;
-               ((CompressorWindow*)plugin->thread->window)->update();
-               plugin->send_configure_change();
-               return 1;
-       }
-       return 0;
-//plugin->config.dump();
-}
-
-int CompressorCanvas::button_release_event()
-{
-       if(current_operation == DRAG)
-       {
-               if(current_point > 0)
-               {
-                       if(plugin->config.levels.values[current_point].x <
-                               plugin->config.levels.values[current_point - 1].x)
-                               plugin->config.remove_point(current_point);
-               }
-
-               if(current_point < plugin->config.levels.total - 1)
-               {
-                       if(plugin->config.levels.values[current_point].x >=
-                               plugin->config.levels.values[current_point + 1].x)
-                               plugin->config.remove_point(current_point);
-               }
-
-               ((CompressorWindow*)plugin->thread->window)->update();
-               plugin->send_configure_change();
-               current_operation = NONE;
-               return 1;
-       }
-
-       return 0;
-}
 
-int CompressorCanvas::cursor_motion_event()
+void CompressorCanvas::update_window()
 {
-       if(current_operation == DRAG)
-       {
-               int x = get_cursor_x();
-               int y = get_cursor_y();
-               CLAMP(x, 0, get_w());
-               CLAMP(y, 0, get_h());
-               double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
-               double y_db = (double)y / get_h() * plugin->config.min_db;
-               plugin->config.levels.values[current_point].x = x_db;
-               plugin->config.levels.values[current_point].y = y_db;
-               ((CompressorWindow*)plugin->thread->window)->update();
-               plugin->send_configure_change();
-               return 1;
-//plugin->config.dump();
-       }
-       else
-// Change cursor over points
-       if(is_event_win() && cursor_inside())
-       {
-               int new_cursor = CROSS_CURSOR;
-
-               for(int i = 0; i < plugin->config.levels.total; i++)
-               {
-                       double x_db = plugin->config.get_x(i);
-                       double y_db = plugin->config.get_y(i);
-
-                       int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
-                       int y = (int)(y_db / plugin->config.min_db * get_h());
-
-                       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;
-                       }
-               }
-
-
-               if(new_cursor != get_cursor())
-               {
-                       set_cursor(new_cursor, 0, 1);
-               }
-       }
-       return 0;
+       ((CompressorWindow*)window)->update();
 }
 
 
+// CompressorLookAhead::CompressorLookAhead(CompressorEffect *plugin,
+//      CompressorWindow *window, int x, int y)
+//  : BC_TumbleTextBox(window, (float)plugin->config.bands[0].readahead_len,
+//      (float)MIN_LOOKAHEAD, (float)MAX_LOOKAHEAD, x, y, xS(100))
+// {
+//     this->plugin = plugin;
+//      set_increment(0.1);
+//      set_precision(2);
+// }
+//
+// int CompressorLookAhead::handle_event()
+// {
+//     plugin->config.bands[0].readahead_len = atof(get_text());
+//     plugin->send_configure_change();
+//     return 1;
+// }
+//
+//
 
 
-
-CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y)
- : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.reaction_len)
+CompressorAttack::CompressorAttack(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y)
+ : BC_TumbleTextBox(window, (float)plugin->config.bands[0].attack_len,
+       (float)MIN_ATTACK, (float)MAX_ATTACK, x, y, xS(100))
 {
        this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
 }
 
-int CompressorReaction::handle_event()
+int CompressorAttack::handle_event()
 {
-       plugin->config.reaction_len = atof(get_text());
+       plugin->config.bands[0].attack_len = atof(get_text());
        plugin->send_configure_change();
        return 1;
 }
 
-int CompressorReaction::button_press_event()
-{
-       if(is_event_win())
-       {
-               if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
-               if(get_buttonpress() == 4)
-               {
-                       plugin->config.reaction_len += 0.1;
-               }
-               else
-               if(get_buttonpress() == 5)
-               {
-                       plugin->config.reaction_len -= 0.1;
-               }
-               update((float)plugin->config.reaction_len);
-               plugin->send_configure_change();
-               return 1;
-       }
-       return 0;
-}
-
-CompressorDecay::CompressorDecay(CompressorEffect *plugin, int x, int y)
- : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.decay_len)
+CompressorRelease::CompressorRelease(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y)
+ : BC_TumbleTextBox(window, (float)plugin->config.bands[0].release_len,
+       (float)MIN_DECAY, (float)MAX_DECAY, x, y, xS(100))
 {
        this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
 }
-int CompressorDecay::handle_event()
+int CompressorRelease::handle_event()
 {
-       plugin->config.decay_len = atof(get_text());
+       plugin->config.bands[0].release_len = atof(get_text());
        plugin->send_configure_change();
        return 1;
 }
 
-int CompressorDecay::button_press_event()
-{
-       if(is_event_win())
-       {
-               if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
-               if(get_buttonpress() == 4)
-               {
-                       plugin->config.decay_len += 0.1;
-               }
-               else
-               if(get_buttonpress() == 5)
-               {
-                       plugin->config.decay_len -= 0.1;
-               }
-               update((float)plugin->config.decay_len);
-               plugin->send_configure_change();
-               return 1;
-       }
-       return 0;
-}
 
-
-
-CompressorX::CompressorX(CompressorEffect *plugin, int x, int y)
: BC_TextBox(x, y, xS(100), 1, "")
+CompressorX::CompressorX(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y)
+ : BC_TumbleTextBox(window, (float)0.0,
      plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
 {
        this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
 }
 int CompressorX::handle_event()
 {
+       BandConfig *band_config = &plugin->config.bands[0];
        int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
-       if(current_point < plugin->config.levels.total)
-       {
-               plugin->config.levels.values[current_point].x = atof(get_text());
-               ((CompressorWindow*)plugin->thread->window)->update_canvas();
+       if( current_point < band_config->levels.total ) {
+               band_config->levels.values[current_point].x = atof(get_text());
+               ((CompressorWindow*)plugin->thread->window)->canvas->update();
                plugin->send_configure_change();
        }
        return 1;
 }
 
-
-
-CompressorY::CompressorY(CompressorEffect *plugin, int x, int y)
: BC_TextBox(x, y, xS(100), 1, "")
+CompressorY::CompressorY(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y)
+ : BC_TumbleTextBox(window, (float)0.0,
      plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
 {
        this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
 }
 int CompressorY::handle_event()
 {
+       BandConfig *band_config = &plugin->config.bands[0];
        int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
-       if(current_point < plugin->config.levels.total)
-       {
-               plugin->config.levels.values[current_point].y = atof(get_text());
-               ((CompressorWindow*)plugin->thread->window)->update_canvas();
+       if( current_point < band_config->levels.total ) {
+               band_config->levels.values[current_point].y = atof(get_text());
+               ((CompressorWindow*)plugin->thread->window)->canvas->update();
                plugin->send_configure_change();
        }
        return 1;
 }
 
 
-
-
-
-CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y)
- : BC_TextBox(x, y, xS(100), 1, (int64_t)plugin->config.trigger)
+CompressorTrigger::CompressorTrigger(CompressorEffect *plugin,
+       CompressorWindow *window, int x, int y)
+ : BC_TumbleTextBox(window, (int)plugin->config.trigger,
+       MIN_TRIGGER, MAX_TRIGGER, x, y, xS(100))
 {
        this->plugin = plugin;
+       set_increment(1);
 }
 int CompressorTrigger::handle_event()
 {
@@ -1314,37 +625,10 @@ int CompressorTrigger::handle_event()
        return 1;
 }
 
-int CompressorTrigger::button_press_event()
-{
-       if(is_event_win())
-       {
-               if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
-               if(get_buttonpress() == 4)
-               {
-                       plugin->config.trigger++;
-               }
-               else
-               if(get_buttonpress() == 5)
-               {
-                       plugin->config.trigger--;
-               }
-               update((int64_t)plugin->config.trigger);
-               plugin->send_configure_change();
-               return 1;
-       }
-       return 0;
-}
-
-
-
-
 
 CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y)
- : BC_PopupMenu(x,
-       y,
-       xS(120),
-       CompressorInput::value_to_text(plugin->config.input),
-       1)
+ : BC_PopupMenu(x, y, xS(100),
+       CompressorInput::value_to_text(plugin->config.input), 1)
 {
        this->plugin = plugin;
 }
@@ -1358,39 +642,32 @@ int CompressorInput::handle_event()
 
 void CompressorInput::create_objects()
 {
-       for(int i = 0; i < 3; i++)
-       {
+       for( int i = 0; i < 3; i++ ) {
                add_item(new BC_MenuItem(value_to_text(i)));
        }
 }
 
 const char* CompressorInput::value_to_text(int value)
 {
-       switch(value)
+       switch( value )
        {
-               case CompressorConfig::TRIGGER: return _("Trigger");
-               case CompressorConfig::MAX: return _("Maximum");
-               case CompressorConfig::SUM: return _("Total");
+               case CompressorConfig::TRIGGER: return "Trigger";
+               case CompressorConfig::MAX: return "Maximum";
+               case CompressorConfig::SUM: return "Total";
        }
 
-       return _("Trigger");
+       return "Trigger";
 }
 
 int CompressorInput::text_to_value(char *text)
 {
-       for(int i = 0; i < 3; i++)
-       {
-               if(!strcmp(value_to_text(i), text)) return i;
+       for( int i = 0; i < 3; i++ ) {
+               if( !strcmp(value_to_text(i), text) ) return i;
        }
 
        return CompressorConfig::TRIGGER;
 }
 
-
-
-
-
-
 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
  : BC_GenericButton(x, y, _("Clear"))
 {
@@ -1399,15 +676,14 @@ CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
 
 int CompressorClear::handle_event()
 {
-       plugin->config.levels.remove_all();
+       BandConfig *band_config = &plugin->config.bands[0];
+       band_config->levels.remove_all();
 //plugin->config.dump();
        ((CompressorWindow*)plugin->thread->window)->update();
        plugin->send_configure_change();
        return 1;
 }
 
-
-
 CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y)
  : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
 {
@@ -1421,6 +697,3 @@ int CompressorSmooth::handle_event()
        return 1;
 }
 
-
-
-
index 412b8db93b845f3d3b62bf0c133ac4a121bea711..dc9c54adbde762f4b28fc519eec69a1574169313 100644 (file)
@@ -1,7 +1,7 @@
 
 /*
  * 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
@@ -25,6 +25,7 @@
 
 
 #include "bchash.inc"
+#include "compressortools.h"
 #include "guicast.h"
 #include "mutex.h"
 #include "pluginaclient.h"
 #include "vframe.inc"
 
 class CompressorEffect;
+class CompressorWindow;
 
 
-
-
-
-class CompressorCanvas : public BC_SubWindow
+class CompressorCanvas : public CompressorCanvasBase
 {
 public:
-       CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h);
-       int button_press_event();
-       int button_release_event();
-       int cursor_motion_event();
-
+       CompressorCanvas(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y, int w, int h);
+       void update_window();
+};
 
-       enum
-       {
-               NONE,
-               DRAG
-       };
 
-       int current_point;
-       int current_operation;
+class CompressorLookAhead : public BC_TumbleTextBox
+{
+public:
+       CompressorLookAhead(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
+       int handle_event();
        CompressorEffect *plugin;
 };
 
-
-class CompressorReaction : public BC_TextBox
+class CompressorAttack : public BC_TumbleTextBox
 {
 public:
-       CompressorReaction(CompressorEffect *plugin, int x, int y);
+       CompressorAttack(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
        int handle_event();
-       int button_press_event();
        CompressorEffect *plugin;
 };
 
-class CompressorClear : public BC_GenericButton
+class CompressorRelease : public BC_TumbleTextBox
 {
 public:
-       CompressorClear(CompressorEffect *plugin, int x, int y);
+       CompressorRelease(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
        int handle_event();
        CompressorEffect *plugin;
 };
 
-class CompressorX : public BC_TextBox
+
+class CompressorClear : public BC_GenericButton
 {
 public:
-       CompressorX(CompressorEffect *plugin, int x, int y);
+       CompressorClear(CompressorEffect *plugin, int x, int y);
        int handle_event();
        CompressorEffect *plugin;
 };
 
-class CompressorY : public BC_TextBox
+class CompressorX : public BC_TumbleTextBox
 {
 public:
-       CompressorY(CompressorEffect *plugin, int x, int y);
+       CompressorX(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
        int handle_event();
        CompressorEffect *plugin;
 };
 
-class CompressorTrigger : public BC_TextBox
+class CompressorY : public BC_TumbleTextBox
 {
 public:
-       CompressorTrigger(CompressorEffect *plugin, int x, int y);
+       CompressorY(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
        int handle_event();
-       int button_press_event();
        CompressorEffect *plugin;
 };
 
-class CompressorDecay : public BC_TextBox
+class CompressorTrigger : public BC_TumbleTextBox
 {
 public:
-       CompressorDecay(CompressorEffect *plugin, int x, int y);
+       CompressorTrigger(CompressorEffect *plugin, CompressorWindow *window,
+                       int x, int y);
        int handle_event();
-       int button_press_event();
        CompressorEffect *plugin;
 };
 
@@ -134,74 +133,44 @@ class CompressorWindow : public PluginClientWindow
 {
 public:
        CompressorWindow(CompressorEffect *plugin);
+       ~CompressorWindow();
+
        void create_objects();
        void update();
        void update_textboxes();
-       void update_canvas();
        void draw_scales();
+       void update_meter(CompressorGainFrame *gain_frame);
        int resize_event(int w, int h);
 
        CompressorCanvas *canvas;
-       CompressorReaction *reaction;
+       CompressorLookAhead *readahead;
+       CompressorAttack *attack;
        CompressorClear *clear;
        CompressorX *x_text;
        CompressorY *y_text;
        CompressorTrigger *trigger;
-       CompressorDecay *decay;
+       CompressorRelease *release;
        CompressorSmooth *smooth;
        CompressorInput *input;
+       BC_Meter *in;
+       BC_Meter *gain_change;
        CompressorEffect *plugin;
 };
 
 
 
-typedef struct
-{
-// DB from min_db - 0
-       double x, y;
-} compressor_point_t;
-
-class CompressorConfig
+class CompressorConfig : public CompressorConfigBase
 {
 public:
        CompressorConfig();
 
        void copy_from(CompressorConfig &that);
        int equivalent(CompressorConfig &that);
-       void interpolate(CompressorConfig &prev,
-               CompressorConfig &next,
-               int64_t prev_frame,
-               int64_t next_frame,
-               int64_t current_frame);
-
-       int total_points();
-       void remove_point(int number);
-       void optimize();
-// Return values of a specific point
-       double get_y(int number);
-       double get_x(int number);
-// Returns db output from db input
-       double calculate_db(double x);
-       int set_point(double x, double y);
-       void dump();
-
-       int trigger;
-       int input;
-       enum
-       {
-               TRIGGER,
-               MAX,
-               SUM
-       };
-       double min_db;
-       double reaction_len;
-       double decay_len;
-       double min_x, min_y;
-       double max_x, max_y;
-       int smoothing_only;
-       ArrayList<compressor_point_t> levels;
+       void interpolate(CompressorConfig &prev, CompressorConfig &next,
+               int64_t prev_frame, int64_t next_frame, int64_t current_frame);
 };
 
+
 class CompressorEffect : public PluginAClient
 {
 public:
@@ -212,45 +181,35 @@ public:
        int is_realtime();
        void read_data(KeyFrame *keyframe);
        void save_data(KeyFrame *keyframe);
-       int process_buffer(int64_t size,
-               Samples **buffer,
-               int64_t start_position,
-               int sample_rate);
-       double calculate_gain(double input);
-
-// Calculate linear output from linear input
-       double calculate_output(double x);
+       int process_buffer(int64_t size, Samples **buffer,
+                       int64_t start_position, int sample_rate);
+       void allocate_input(int size);
 
 
-       void reset();
        void update_gui();
-       void delete_dsp();
+       void render_stop();
 
        PLUGIN_CLASS_MEMBERS(CompressorConfig)
 
-// The raw input data for each channel with readahead
+// Input data + read ahead for each channel
        Samples **input_buffer;
+
 // Number of samples in the input buffer
        int64_t input_size;
 // Number of samples allocated in the input buffer
        int64_t input_allocated;
 // Starting sample of input buffer relative to project in requested rate.
        int64_t input_start;
+       int64_t last_position;
+       int need_reconfigure;
+
+
+       CompressorEngine *engine;
 
-// ending input value of smoothed input
-       double next_target;
-// starting input value of smoothed input
-       double previous_target;
-// samples between previous and next target value for readahead
-       int target_samples;
-// current sample from 0 to target_samples
-       int target_current_sample;
-// current smoothed input value
-       double current_value;
 // Temporaries for linear transfer
        ArrayList<compressor_point_t> levels;
-       double min_x, min_y;
-       double max_x, max_y;
+//     double min_x, min_y;
+//     double max_x, max_y;
 };
 
 
diff --git a/cinelerra-5.1/plugins/compressormulti/Makefile b/cinelerra-5.1/plugins/compressormulti/Makefile
new file mode 100644 (file)
index 0000000..0a6cae5
--- /dev/null
@@ -0,0 +1,11 @@
+include ../../plugin_defs
+
+OBJS = $(OBJDIR)/comprmulti.o \
+       $(OBJDIR)/comprmultigui.o
+
+PLUGIN = compressormulti
+
+include ../../plugin_config
+
+$(OBJDIR)/compressor.o: comprmulti.C
+$(OBJDIR)/compressorgui.o: comprmultigui.C
diff --git a/cinelerra-5.1/plugins/compressormulti/comprmulti.C b/cinelerra-5.1/plugins/compressormulti/comprmulti.C
new file mode 100644 (file)
index 0000000..2207ba0
--- /dev/null
@@ -0,0 +1,945 @@
+/*
+ * 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
+ */
+
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "comprmulti.h"
+#include "comprmultigui.h"
+#include "bchash.h"
+#include "eqcanvas.h"
+#include "filexml.h"
+#include "language.h"
+#include "samples.h"
+#include "transportque.inc"
+#include "units.h"
+#include "vframe.h"
+
+#include <math.h>
+#include <string.h>
+
+REGISTER_PLUGIN(ComprMultiEffect)
+
+// More potential compressor algorithms:
+// Use single reaction time parameter.  Negative reaction time uses
+// readahead.  Positive reaction time uses slope.
+
+// Smooth input stage if readahead.
+// Determine slope from current smoothed sample to every sample in readahead area.
+// Once highest slope is found, count of number of samples remaining until it is
+// reached.  Only search after this count for the next highest slope.
+// Use highest slope to determine smoothed value.
+
+// 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,
+// 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.
+
+// Gain stage.
+// For every sample, calculate gain from smoothed input value.
+
+ComprMultiConfig::ComprMultiConfig()
+ : CompressorConfigBase(TOTAL_BANDS)
+{
+       q = 1.0;
+       window_size = 4096;
+}
+
+void ComprMultiConfig::copy_from(ComprMultiConfig &that)
+{
+       CompressorConfigBase::copy_from(that);
+
+       window_size = that.window_size;
+       q = that.q;
+}
+
+int ComprMultiConfig::equivalent(ComprMultiConfig &that)
+{
+       if( !CompressorConfigBase::equivalent(that) )
+               return 0;
+
+       if( !EQUIV(q, that.q) ||
+               window_size != that.window_size ) {
+               return 0;
+       }
+
+       return 1;
+}
+
+void ComprMultiConfig::interpolate(ComprMultiConfig &prev, ComprMultiConfig &next,
+       int64_t prev_frame, int64_t next_frame, int64_t current_frame)
+{
+       copy_from(prev);
+}
+
+
+ComprMultiEffect::ComprMultiEffect(PluginServer *server)
+ : PluginAClient(server)
+{
+       reset();
+       for( int i = 0; i < TOTAL_BANDS; i++ )
+               band_states[i] = new BandState(this, i);
+}
+
+ComprMultiEffect::~ComprMultiEffect()
+{
+       delete_dsp();
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               delete band_states[i];
+       }
+}
+
+void ComprMultiEffect::delete_dsp()
+{
+#ifndef DRAW_AFTER_BANDPASS
+       if( input_buffer ) {
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ )
+                       delete input_buffer[i];
+               delete [] input_buffer;
+       }
+       input_buffer = 0;
+       input_size = 0;
+       new_input_size = 0;
+#endif
+       if( fft ) {
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ )
+                       delete fft[i];
+               delete [] fft;
+       }
+
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               band_states[i]->delete_dsp();
+       }
+
+       filtered_size = 0;
+       fft = 0;
+}
+
+
+void ComprMultiEffect::reset()
+{
+       for( int i = 0; i < TOTAL_BANDS; i++ )
+               band_states[i] = 0;
+
+#ifndef DRAW_AFTER_BANDPASS
+       input_buffer = 0;
+       input_size = 0;
+       new_input_size = 0;
+#endif
+       input_start = 0;
+       filtered_size = 0;
+       last_position = 0;
+       fft = 0;
+       need_reconfigure = 1;
+       config.current_band = 0;
+}
+
+const char* ComprMultiEffect::plugin_title() { return N_("Compressor Multi"); }
+int ComprMultiEffect::is_realtime() { return 1; }
+int ComprMultiEffect::is_multichannel() { return 1; }
+
+
+void ComprMultiEffect::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+       input.set_shared_input(keyframe->xbuf);
+
+       int result = 0;
+       for( int i = 0; i < TOTAL_BANDS; i++ )
+               config.bands[i].levels.remove_all();
+
+       while( !(result = input.read_tag()) ) {
+               if( input.tag.title_is("COMPRESSOR_MULTI") ) {
+                       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);
+                       config.q = input.tag.get_property("Q", config.q);
+                       config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
+               }
+               else if( input.tag.title_is("COMPRESSORBAND") ) {
+                       int number = input.tag.get_property("NUMBER", 0);
+                       config.bands[number].read_data(&input, 1);
+               }
+       }
+
+       config.boundaries();
+}
+
+void ComprMultiEffect::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+       output.set_shared_output(keyframe->xbuf);
+
+       output.tag.set_title("COMPRESSOR_MULTI");
+       output.tag.set_property("TRIGGER", config.trigger);
+       output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
+       output.tag.set_property("INPUT", config.input);
+       output.tag.set_property("Q", config.q);
+       output.tag.set_property("WINDOW_SIZE", config.window_size);
+       output.append_tag();
+       output.append_newline();
+
+       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+               BandConfig *band_config = &config.bands[band];
+               band_config->save_data(&output, band, 1);
+       }
+
+
+       output.tag.set_title("/COMPRESSOR_MULTI");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void ComprMultiEffect::dump_frames()
+{
+       printf("tracking %f, direction %d\n", get_tracking_position(), get_tracking_direction());
+       CompressorClientFrame *cfp = (CompressorClientFrame *)frame_buffer.first;
+       for( int n=0; cfp; cfp=(CompressorClientFrame *)cfp->next,++n ) {
+               switch( cfp->type ) {
+               case GAIN_COMPRESSORFRAME: {
+                       CompressorGainFrame *gfp = (CompressorGainFrame *)cfp;
+                       printf("%3d: band %d, gain pos=%f, gain=%f, level=%f\n", n, cfp->band,
+                               gfp->position, gfp->gain, gfp->level);
+                       break; }
+               case FREQ_COMPRESSORFRAME: {
+                       CompressorFreqFrame *ffp = (CompressorFreqFrame *)cfp;
+                       printf("%3d: band %d, freq pos=%f, max=%f/%f, size=%d\n", n, ffp->band,
+                               ffp->position, ffp->freq_max, ffp->time_max, ffp->data_size);
+                       break; }
+               }
+       }
+}
+
+void ComprMultiEffect::update_gui()
+{
+//     dump_frames();
+       if( !thread ) return;
+       ComprMultiWindow *window = (ComprMultiWindow *)thread->window;
+       if( !window ) return;
+// user can't change levels when loading configuration
+       window->lock_window("ComprMultiEffect::update_gui 1");
+       int reconfigured = 0;
+// Can't update points if the user is editing
+       if( window->canvas->is_dragging() )
+               reconfigured = load_configuration();
+//printf("ComprMultiEffect::update_gui %d %d %d\n", __LINE__, reconfigured, total_frames);
+       if( reconfigured )
+               window->update();
+       if( pending_gui_frames() )
+               window->update_eqcanvas();
+       window->unlock_window();
+}
+
+
+void ComprMultiEffect::render_stop()
+{
+       if( !thread ) return;
+       ComprMultiWindow *window = (ComprMultiWindow*)thread->window;
+       if( !window ) return;
+       window->lock_window("ComprMultiEffect::render_stop");
+       window->in->reset();
+       window->gain_change->update(1, 0);
+       delete window->gain_frame;  window->gain_frame = 0;
+       delete window->freq_frame;  window->freq_frame = 0;
+       window->unlock_window();
+}
+
+
+LOAD_CONFIGURATION_MACRO(ComprMultiEffect, ComprMultiConfig)
+NEW_WINDOW_MACRO(ComprMultiEffect, ComprMultiWindow)
+
+
+int ComprMultiEffect::process_buffer(int64_t size, Samples **buffer,
+               int64_t start_position, int sample_rate)
+{
+       start_pos = (double)start_position / sample_rate;
+       dir = get_direction() == PLAY_REVERSE ? -1 : 1;
+       need_reconfigure |= load_configuration();
+       int channels = PluginClient::total_in_buffers;
+       if( need_reconfigure ) {
+               need_reconfigure = 0;
+//             min_x = DB::fromdb(config.min_db);  max_x = 1.0;
+//             min_y = DB::fromdb(config.min_db);  max_y = 1.0;
+
+               if( fft && fft[0]->window_size != config.window_size ) {
+                       for( int i = 0; i < channels; i++ )
+                               delete fft[i];
+                       delete [] fft;
+                       fft = 0;
+               }
+
+               if( !fft ) {
+                       fft = new ComprMultiFFT*[channels];
+                       for( int i = 0; i < channels; i++ ) {
+                               fft[i] = new ComprMultiFFT(this, i);
+                               fft[i]->initialize(config.window_size, TOTAL_BANDS);
+                       }
+               }
+
+               for( int i = 0; i < TOTAL_BANDS; i++ )
+                       band_states[i]->reconfigure();
+       }
+
+// reset after seeking
+       if( last_position != start_position ) {
+               if( fft ) {
+                       for( int i = 0; i < channels; i++ )
+                               if( fft[i] ) fft[i]->delete_fft();
+               }
+
+#ifndef DRAW_AFTER_BANDPASS
+               input_size = 0;
+#endif
+               filtered_size = 0;
+               input_start = start_position;
+               for( int band = 0; band < TOTAL_BANDS; band++ ) {
+                       BandState *band_state = band_states[band];
+                       if( band_state->engine )
+                               band_state->engine->reset();
+               }
+       }
+
+// process frequency domain for all bands simultaneously
+// read enough samples ahead to process all the bands
+       int new_filtered_size = 0;
+       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+               BandState *band_state = band_states[band];
+               if( !band_state->engine )
+                       band_state->engine = new CompressorEngine(&config, band);
+               int attack_samples, release_samples, preview_samples;
+               band_state->engine->calculate_ranges(
+                       &attack_samples, &release_samples, &preview_samples,
+                       sample_rate);
+
+               if( preview_samples > new_filtered_size )
+                       new_filtered_size = preview_samples;
+       }
+       new_filtered_size += size;
+       for( int band = 0; band < TOTAL_BANDS; band++ )
+               band_states[band]->allocate_filtered(new_filtered_size);
+
+// Append data to the buffers to fill the readahead area.
+       int remain = new_filtered_size - filtered_size;
+
+       if( remain > 0 ) {
+// printf("ComprMultiEffect::process_buffer %d filtered_size=%ld remain=%d\n",
+// __LINE__, filtered_size, remain);
+               for( int channel = 0; channel < channels; channel++ ) {
+#ifndef DRAW_AFTER_BANDPASS
+                       new_input_size = input_size;
+#endif
+// create an array of filtered buffers for the output
+                       Samples *filtered_arg[TOTAL_BANDS];
+                       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+                               new_spectrogram_frames[band] = 0;
+                               filtered_arg[band] = band_states[band]->filtered_buffer[channel];
+// temporarily set the output to the end to append data
+                               filtered_arg[band]->set_offset(filtered_size);
+                       }
+
+// starting position of the input reads
+                       int64_t start = input_start + dir * filtered_size;
+// printf("ComprMultiEffect::process_buffer %d start=%ld remain=%d\n", __LINE__, start, remain);
+                       fft[channel]->process_buffer(start, remain, filtered_arg, get_direction());
+
+                       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+                               filtered_arg[band]->set_offset(0);
+                       }
+//printf("ComprMultiEffect::process_buffer %d new_input_size=%ld\n", __LINE__, new_input_size);
+               }
+       }
+
+#ifndef DRAW_AFTER_BANDPASS
+       input_size = new_input_size;
+#endif
+       filtered_size = new_filtered_size;
+
+// process time domain for each band separately
+       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+               BandState *band_state = band_states[band];
+               CompressorEngine *engine = band_state->engine;
+
+               engine->process(band_states[band]->filtered_buffer,
+                       band_states[band]->filtered_buffer,
+                       size, sample_rate, channels, start_position);
+
+               for( int i = 0; i < engine->gui_gains.size(); i++ ) {
+                       CompressorGainFrame *frame = new CompressorGainFrame();
+                       frame->position = start_pos + dir*engine->gui_offsets[i];
+                       frame->gain = engine->gui_gains.get(i);
+                       frame->level = engine->gui_levels.get(i);
+                       frame->band = band;
+                       add_gui_frame(frame);
+               }
+       }
+
+// Add together filtered buffers + unfiltered buffer.
+// Apply the solo here.
+       int have_solo = 0;
+       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+               if( config.bands[band].solo ) {
+                       have_solo = 1;
+                       break;
+               }
+       }
+
+       for( int channel = 0; channel < channels; channel++ ) {
+               double *dst = buffer[channel]->get_data();
+               bzero(dst, size * sizeof(double));
+
+               for( int band = 0; band < TOTAL_BANDS; band++ ) {
+                       if( !have_solo || config.bands[band].solo ) {
+                               double *src = band_states[band]->filtered_buffer[channel]->get_data();
+                               for( int i = 0; i < size; i++ ) {
+                                       dst[i] += src[i];
+                               }
+                       }
+               }
+       }
+
+// shift input buffers
+       for( int band = 0; band < TOTAL_BANDS; band++ ) {
+
+               for( int i = 0; i < channels; i++ ) {
+                       memcpy(band_states[band]->filtered_buffer[i]->get_data(),
+                               band_states[band]->filtered_buffer[i]->get_data() + size,
+                               (filtered_size - size) * sizeof(double));
+
+               }
+       }
+
+#ifndef DRAW_AFTER_BANDPASS
+       for( int i = 0; i < channels; i++ ) {
+               memcpy(input_buffer[i]->get_data(),
+                       input_buffer[i]->get_data() + size,
+                       (input_size - size) * sizeof(double));
+       }
+       input_size -= size;
+#endif
+
+// update the counters
+       filtered_size -= size;
+       input_start += dir * size;
+       last_position = start_position + dir * size;
+       return 0;
+}
+
+void ComprMultiEffect::allocate_input(int new_size)
+{
+#ifndef DRAW_AFTER_BANDPASS
+       if( !input_buffer ||
+               new_size > input_buffer[0]->get_allocated() ) {
+               Samples **new_input_buffer = new Samples*[get_total_buffers()];
+
+               for( int i = 0; i < get_total_buffers(); i++ ) {
+                       new_input_buffer[i] = new Samples(new_size);
+
+                       if( input_buffer ) {
+                               memcpy(new_input_buffer[i]->get_data(),
+                                       input_buffer[i]->get_data(),
+                                       input_buffer[i]->get_allocated() * sizeof(double));
+                               delete input_buffer[i];
+                       }
+               }
+
+               if( input_buffer ) delete [] input_buffer;
+               input_buffer = new_input_buffer;
+       }
+#endif // !DRAW_AFTER_BANDPASS
+}
+
+
+void ComprMultiEffect::calculate_envelope()
+{
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               band_states[i]->calculate_envelope();
+       }
+}
+
+
+BandState::BandState(ComprMultiEffect *plugin, int band)
+{
+       this->plugin = plugin;
+       this->band = band;
+       reset();
+}
+
+BandState::~BandState()
+{
+       delete_dsp();
+}
+
+void BandState::delete_dsp()
+{
+       delete [] envelope;
+       levels.remove_all();
+       if( filtered_buffer ) {
+               for( int i = 0; i < plugin->total_in_buffers; i++ ) {
+                       delete filtered_buffer[i];
+               }
+               delete [] filtered_buffer;
+       }
+       if( engine ) {
+               delete engine;
+       }
+       reset();
+}
+
+void BandState::reset()
+{
+       engine = 0;
+       envelope = 0;
+       envelope_allocated = 0;
+       filtered_buffer = 0;
+
+       next_target = 1.0;
+       previous_target = 1.0;
+       target_samples = 1;
+       target_current_sample = -1;
+       current_value = 1.0;
+}
+
+void BandState::reconfigure()
+{
+// Calculate linear transfer from db
+       levels.remove_all();
+
+       BandConfig *config = &plugin->config.bands[band];
+       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);
+       }
+
+       calculate_envelope();
+}
+
+
+void BandState::allocate_filtered(int new_size)
+{
+       if( !filtered_buffer ||
+               new_size > filtered_buffer[0]->get_allocated() ) {
+               Samples **new_filtered_buffer = new Samples*[plugin->get_total_buffers()];
+               for( int i = 0; i < plugin->get_total_buffers(); i++ ) {
+                       new_filtered_buffer[i] = new Samples(new_size);
+
+                       if( filtered_buffer ) {
+                               memcpy(new_filtered_buffer[i]->get_data(),
+                                       filtered_buffer[i]->get_data(),
+                                       filtered_buffer[i]->get_allocated() * sizeof(double));
+                               delete filtered_buffer[i];
+                       }
+               }
+
+               if( filtered_buffer ) delete [] filtered_buffer;
+               filtered_buffer = new_filtered_buffer;
+       }
+}
+
+
+void BandState::calculate_envelope()
+{
+// the window size changed
+       if( envelope && envelope_allocated < plugin->config.window_size / 2 ) {
+               delete [] envelope;
+               envelope = 0;
+       }
+
+       if( !envelope ) {
+               envelope_allocated = plugin->config.window_size / 2;
+               envelope = new double[envelope_allocated];
+       }
+
+// number of slots in the edge
+       double edge = (1.0 - plugin->config.q) * TOTALFREQS / 2;
+       int max_freq = Freq::tofreq_f(TOTALFREQS - 1);
+       int nyquist = plugin->project_sample_rate / 2;
+       int freq = plugin->config.bands[band].freq;
+
+// max frequency of all previous bands is the low
+       int low = 0;
+       for( int i=0; i<band; ++i ) {
+               if( plugin->config.bands[i].freq > low )
+                       low = plugin->config.bands[i].freq;
+       }
+       int high = max_freq;
+// limit the frequencies
+       if( band < TOTAL_BANDS-1 ) high = freq;
+       if( high >= max_freq ) { high = max_freq;  edge = 0; }
+// hard edges on the lowest & highest
+       if( band == 0 && high <= 0 ) edge = 0;
+       if( low > high ) low = high;
+// number of slots to arrive at 1/2 power
+#ifndef LOG_CROSSOVER
+// linear
+       double edge2 = edge / 2;
+#else
+// log
+       double edge2 = edge * 6 / -INFINITYGAIN;
+#endif
+       double low_slot = Freq::fromfreq_f(low);
+       double high_slot = Freq::fromfreq_f(high);
+// shift slots to allow crossover
+       if( band < TOTAL_BANDS-1 ) high_slot -= edge2;
+       if( band > 0 ) low_slot += edge2;
+
+       for( int i = 0; i < plugin->config.window_size / 2; i++ ) {
+               double freq = i * nyquist / (plugin->config.window_size / 2);
+               double slot = Freq::fromfreq_f(freq);
+// sum of previous bands
+               double prev_sum = 0;
+               for( int prev_band = 0; prev_band < band; prev_band++ ) {
+                       double *prev_envelope = plugin->band_states[prev_band]->envelope;
+                       prev_sum += prev_envelope[i];
+               }
+
+               if( slot < high_slot )
+// remain of previous bands
+                       envelope[i] = 1.0 - prev_sum;
+               else if( slot < high_slot + edge ) {
+// next crossover
+                       double remain = 1.0 - prev_sum;
+#ifndef LOG_CROSSOVER
+// linear
+                       envelope[i] = remain - remain * (slot - high_slot) / edge;
+#else
+// log TODO
+                       envelope[i] = DB::fromdb((slot - high_slot) * INFINITYGAIN / edge);
+#endif
+
+               }
+               else
+                       envelope[i] = 0.0;
+       }
+}
+
+
+void BandState::process_readbehind(int size,
+               int reaction_samples, int decay_samples, int trigger)
+{
+       if( target_current_sample < 0 )
+               target_current_sample = reaction_samples;
+       double current_slope = (next_target - previous_target) / reaction_samples;
+       double *trigger_buffer = filtered_buffer[trigger]->get_data();
+       int channels = plugin->get_total_buffers();
+       for( int i = 0; i < size; i++ ) {
+// Get slope required to reach current sample from smoothed sample over reaction
+// length.
+               double sample = 0;
+               switch( plugin->config.input ) {
+               case ComprMultiConfig::MAX: {
+                       double max = 0;
+                       for( int j = 0; j < channels; j++ ) {
+                               sample = fabs(filtered_buffer[j]->get_data()[i]);
+                               if( sample > max ) max = sample;
+                       }
+                       sample = max;
+                       break; }
+
+               case ComprMultiConfig::TRIGGER:
+                       sample = fabs(trigger_buffer[i]);
+                       break;
+
+               case ComprMultiConfig::SUM: {
+                       double max = 0;
+                       for( int j = 0; j < channels; j++ ) {
+                               sample = fabs(filtered_buffer[j]->get_data()[i]);
+                               max += sample;
+                       }
+                       sample = max;
+                       break; }
+               }
+
+               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;
+               }
+
+// 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++;
+
+               if( plugin->config.smoothing_only ) {
+                       for( int j = 0; j < channels; j++ ) {
+                               filtered_buffer[j]->get_data()[i] = current_value;
+                       }
+               }
+               else
+               if( !plugin->config.bands[band].bypass ) {
+                       double gain = plugin->config.calculate_gain(band, current_value);
+                       for( int j = 0; j < channels; j++ ) {
+                               filtered_buffer[j]->get_data()[i] *= gain;
+                       }
+               }
+       }
+}
+
+void BandState::process_readahead(int size, int preview_samples,
+               int reaction_samples, int decay_samples, int trigger)
+{
+       if( target_current_sample < 0 ) target_current_sample = target_samples;
+       double current_slope = (next_target - previous_target) /
+               target_samples;
+       double *trigger_buffer = filtered_buffer[trigger]->get_data();
+       int channels = plugin->get_total_buffers();
+       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++ ) {
+                       int buffer_offset = i + j;
+                       double sample = 0;
+                       switch( plugin->config.input ) {
+                       case ComprMultiConfig::MAX: {
+                               double max = 0;
+                               for( int k = 0; k < channels; k++ ) {
+                                       sample = fabs(filtered_buffer[k]->get_data()[buffer_offset]);
+                                       if( sample > max ) max = sample;
+                               }
+                               sample = max;
+                               break; }
+
+                       case ComprMultiConfig::TRIGGER:
+                               sample = fabs(trigger_buffer[buffer_offset]);
+                               break;
+
+                       case ComprMultiConfig::SUM: {
+                               double max = 0;
+                               for( int k = 0; k < channels; k++ ) {
+                                       sample = fabs(filtered_buffer[k]->get_data()[buffer_offset]);
+                                       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;
+
+               target_current_sample++;
+
+               if( plugin->config.smoothing_only ) {
+                       for( int j = 0; j < channels; j++ ) {
+                               filtered_buffer[j]->get_data()[i] = current_value;
+                       }
+               }
+               else
+               if( !plugin->config.bands[band].bypass ) {
+                       double gain = plugin->config.calculate_gain(band, current_value);
+                       for( int j = 0; j < channels; j++ ) {
+                               filtered_buffer[j]->get_data()[i] *= gain;
+                       }
+               }
+       }
+}
+
+
+ComprMultiFFT::ComprMultiFFT(ComprMultiEffect *plugin, int channel)
+{
+       this->plugin = plugin;
+       this->channel = channel;
+}
+
+ComprMultiFFT::~ComprMultiFFT()
+{
+}
+
+int ComprMultiFFT::signal_process(int band)
+{
+       int sample_rate = plugin->PluginAClient::project_sample_rate;
+       BandState *band_state = plugin->band_states[band];
+
+// Create new spectrogram frame for updating the GUI
+       frame = 0;
+       if(
+#ifndef DRAW_AFTER_BANDPASS
+               band == 0 &&
+#endif
+               ((plugin->config.input != ComprMultiConfig::TRIGGER && channel == 0) ||
+               channel == plugin->config.trigger) ) {
+#ifndef DRAW_AFTER_BANDPASS
+               int total_data = window_size / 2;
+#else
+               int total_data = TOTAL_BANDS * window_size / 2;
+#endif
+
+// store all bands in the same GUI frame
+               frame = new CompressorFreqFrame();
+               frame->band = band;
+               frame->data_size = total_data;
+               frame->data = new double[total_data];
+               bzero(frame->data, sizeof(double) * total_data);
+               frame->nyquist = sample_rate / 2;
+
+//             int attack_samples, release_samples, preview_samples;
+//             band_state->engine->calculate_ranges(&attack_samples,
+//                      &release_samples, &preview_samples, sample_rate);
+
+// FFT advances 1/2 a window for each spectrogram frame
+               int n = plugin->new_spectrogram_frames[band]++;
+               double sample_pos = (n * window_size / 2) / sample_rate;
+               frame->position = plugin->start_pos + plugin->dir * sample_pos;
+//if( band == 1 ) {
+// printf("ComprMultiFFT::signal_process %d top_position=%ld frame->position=%ld\n",
+// __LINE__, plugin->get_playhead_position(), frame->position);
+// printf("ComprMultiFFT::signal_process %d band=%d preview_samples=%d frames size=%ld filtered_size=%ld\n",
+// __LINE__, band, preview_samples, plugin->new_spectrogram_frames[band] *
+//  window_size, plugin->filtered_size);
+//}
+       }
+//printf("ComprMultiFFT::signal_process %d channel=%d band=%d frame=%p\n", __LINE__, channel, band, frame);
+// apply the bandpass filter
+       for( int i = 0; i < window_size / 2; i++ ) {
+               double fr = freq_real[i], fi = freq_imag[i];
+               double env = band_state->envelope[i];
+               freq_real[i] *= env;  freq_imag[i] *= env;
+               double mag = sqrt(fr*fr + fi*fi);
+
+// update the spectrogram with the output
+// neglect the true average & max spectrograms, but always use the trigger
+               if( frame ) {
+                       int offset = band * window_size / 2 + i;
+#ifndef DRAW_AFTER_BANDPASS
+                       frame->data[offset] = MAX(frame->data[offset], mag);
+// get the maximum output in the frequency domain
+                       if( mag > frame->freq_max )
+                               frame->freq_max = mag;
+#else
+                       mag *= env;
+                       frame->data[offset] = MAX(frame->data[offset], mag);
+// get the maximum output in the frequency domain
+                       if( mag > frame->freq_max )
+                               frame->freq_max = mag;
+#endif
+               }
+       }
+
+       symmetry(window_size, freq_real, freq_imag);
+       return 0;
+}
+
+
+int ComprMultiFFT::post_process(int band)
+{
+       if( frame ) {
+// get the maximum output in the time domain
+               double *buffer = output_real;
+#ifndef DRAW_AFTER_BANDPASS
+               buffer = input_buffer->get_data();
+#endif
+               double time_max = 0;
+               for( int i = 0; i<window_size; ++i ) {
+                       if( fabs(buffer[i]) > time_max )
+                               time_max = fabs(buffer[i]);
+               }
+               if( time_max > frame->time_max )
+                       frame->time_max = time_max;
+               plugin->add_gui_frame(frame);
+       }
+       return 0;
+}
+
+
+int ComprMultiFFT::read_samples(int64_t output_sample,
+               int samples, Samples *buffer)
+{
+       int result = plugin->read_samples(buffer, channel,
+                       plugin->get_samplerate(), output_sample, samples);
+#ifndef DRAW_AFTER_BANDPASS
+// append unprocessed samples to the input_buffer
+       int new_input_size = plugin->new_input_size + samples;
+       plugin->allocate_input(new_input_size);
+       memcpy(plugin->input_buffer[channel]->get_data() + plugin->new_input_size,
+               buffer->get_data(), samples * sizeof(double));
+       plugin->new_input_size = new_input_size;
+#endif // !DRAW_AFTER_BANDPASS
+       return result;
+}
+
diff --git a/cinelerra-5.1/plugins/compressormulti/comprmulti.h b/cinelerra-5.1/plugins/compressormulti/comprmulti.h
new file mode 100644 (file)
index 0000000..bc54267
--- /dev/null
@@ -0,0 +1,163 @@
+
+/*
+ * 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
+ */
+
+#ifndef COMPRMULTI_H
+#define COMPRMULTI_H
+
+#include "bchash.inc"
+#include "compressortools.h"
+#include "fourier.h"
+#include "mutex.h"
+#include "pluginaclient.h"
+#include "samples.inc"
+#include "vframe.inc"
+
+class ComprMultiEffect;
+class ComprMultiFFT;
+
+//#define LOG_CROSSOVER
+#define DRAW_AFTER_BANDPASS
+
+#define TOTAL_BANDS 3
+
+class ComprMultiConfig : public CompressorConfigBase
+{
+public:
+       ComprMultiConfig();
+
+       void copy_from(ComprMultiConfig &that);
+       int equivalent(ComprMultiConfig &that);
+       void interpolate(ComprMultiConfig &prev, ComprMultiConfig &next,
+               int64_t prev_frame, int64_t next_frame, int64_t current_frame);
+       double q;
+       int window_size;
+};
+
+class ComprMultiFFT :public CrossfadeFFT
+{
+public:
+       ComprMultiFFT(ComprMultiEffect *plugin, int channel);
+       ~ComprMultiFFT();
+       
+       int signal_process(int band);
+       int post_process(int band);
+       int read_samples(int64_t output_sample, int samples, Samples *buffer);
+
+       ComprMultiEffect *plugin;
+       int channel;
+       CompressorFreqFrame *frame;
+};
+
+// processing state of a single band
+class BandState
+{
+public:
+       BandState(ComprMultiEffect *plugin, int band);
+       ~BandState();
+
+       void delete_dsp();
+       void reset();
+       void reconfigure();
+// calculate the envelope for only this band
+       void calculate_envelope();
+       void process_readbehind(int size,
+               int reaction_samples, int decay_samples, int trigger);
+       void process_readahead(int size, int preview_samples,
+               int reaction_samples, int decay_samples, int trigger);
+       void allocate_filtered(int new_size);
+
+// bandpass filter for this band
+       double *envelope;
+       int envelope_allocated;
+// The input for all channels with filtering by this band
+       Samples **filtered_buffer;
+// ending input value of smoothed input
+       double next_target;
+// starting input value of smoothed input
+       double previous_target;
+// samples between previous and next target value for readahead
+       int target_samples;
+// current sample from 0 to target_samples
+       int target_current_sample;
+// current smoothed input value
+       double current_value;
+// Temporaries for linear transfer
+       ArrayList<compressor_point_t> levels;
+       ComprMultiEffect *plugin;
+       int band;
+       CompressorEngine *engine;
+};
+
+class ComprMultiEffect : public PluginAClient
+{
+public:
+       ComprMultiEffect(PluginServer *server);
+       ~ComprMultiEffect();
+
+       int is_multichannel();
+       int is_realtime();
+       void read_data(KeyFrame *keyframe);
+       void save_data(KeyFrame *keyframe);
+       int process_buffer(int64_t size, Samples **buffer,
+               int64_t start_position, int sample_rate);
+
+// calculate envelopes of all the bands
+       void calculate_envelope();
+
+       void allocate_input(int new_size);
+
+       void reset();
+       void update_gui();
+       void render_stop();
+       void delete_dsp();
+       void dump_frames();
+
+       PLUGIN_CLASS_MEMBERS(ComprMultiConfig)
+
+#ifndef DRAW_AFTER_BANDPASS
+// The out of band data for each channel with readahead
+       Samples **input_buffer;
+// Number of samples in the unfiltered input buffers
+       int64_t input_size;
+// input buffer size being calculated by the FFT readers
+       int64_t new_input_size;
+#endif // !DRAW_AFTER_BANDPASS
+// Starting sample of the input buffer relative to project in the requested rate.
+       int64_t input_start;
+// Number of samples in the filtered input buffers
+       int64_t filtered_size;
+
+// detect seeking
+       int64_t last_position;
+
+// count spectrogram frames for each band
+       int new_spectrogram_frames[TOTAL_BANDS];
+
+       BandState *band_states[TOTAL_BANDS];
+// The big FFT with multiple channels & multiple bands extracted per channel.
+       ComprMultiFFT **fft;
+
+       int need_reconfigure;
+       double start_pos;
+       int dir;
+};
+
+
+#endif
diff --git a/cinelerra-5.1/plugins/compressormulti/comprmultigui.C b/cinelerra-5.1/plugins/compressormulti/comprmultigui.C
new file mode 100644 (file)
index 0000000..95b4c5d
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+ * 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
+ * 
+ */
+
+#include "asset.h"
+#include "bchash.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "comprmultigui.h"
+#include "cursors.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "eqcanvas.h"
+#include "file.h"
+#include "language.h"
+#include "theme.h"
+#include "units.h"
+
+#include <string.h>
+
+ComprMultiWindow::ComprMultiWindow(ComprMultiEffect *plugin)
+ : PluginClientWindow(plugin, xS(650),yS(560), xS(650),yS(560), 0)
+{
+       this->plugin = plugin;
+       char string[BCTEXTLEN];
+// set the default directory
+       sprintf(string, "%s/compressormulti.rc", File::get_config_path());
+       defaults = new BC_Hash(string);
+       defaults->load();
+       plugin->config.current_band = defaults->get("CURRENT_BAND", plugin->config.current_band);
+       gain_frame = 0;
+       freq_frame = 0;
+}
+
+ComprMultiWindow::~ComprMultiWindow()
+{
+       defaults->update("CURRENT_BAND", plugin->config.current_band);
+       defaults->save();
+       delete defaults;
+
+       delete eqcanvas;
+       delete reaction;
+       delete x_text;
+       delete y_text;
+       delete trigger;
+       delete decay;
+       delete gain_frame;
+       delete freq_frame;
+}
+
+
+void ComprMultiWindow::create_objects()
+{
+       int margin = client->get_theme()->widget_border;
+       int x = margin, y = margin;
+       int control_margin = xS(150);
+       int canvas_y2 = get_h() * 2 / 3;
+       BC_Title *title;
+       BandConfig *band_config = &plugin->config.bands[plugin->config.current_band];
+       BandConfig *prev_band = 0;
+       if( plugin->config.current_band > 0 ) {
+               prev_band = &plugin->config.bands[plugin->config.current_band - 1];
+       }
+
+       add_subwindow(title = new BC_Title(x, y, "In:"));
+       int y2 = y + title->get_h() + margin;
+       EDL *edl = plugin->get_edl();
+       add_subwindow(in = new BC_Meter(x, y2, METER_VERT,
+               canvas_y2 - y2,
+               edl->session->min_meter_db,
+               edl->session->max_meter_db,
+               edl->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, // downmix
+               1)); // is_gain_change
+       x += gain_change->get_w() + xS(35);
+
+       add_subwindow(title = new BC_Title(x, y, _("Current band:")));
+
+       int x1 = title->get_x() + title->get_w() + margin;
+       char string[BCTEXTLEN];
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               sprintf(string, "%d", i + 1);
+               add_subwindow(band[i] = new ComprMultiBand(this, plugin, 
+                       x1, y, i, string));
+               x1 += band[i]->get_w() + margin;
+       }
+       y += band[0]->get_h() + 1;
+
+
+       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 ComprMultiCanvas(plugin, this,
+               x, y, get_w() - x - control_margin - xS(10), canvas_y2 - y));
+       y += canvas->get_h() + yS(30);
+
+       add_subwindow(title = new BC_Title(margin, y, _("Bandwidth:")));
+       y += title->get_h();
+       eqcanvas = new EQCanvas(this, margin, y,
+               canvas->get_w() + x - margin, get_h() - y - margin,
+               plugin->config.min_db, 0.0);
+       eqcanvas->freq_divisions = 10;
+       eqcanvas->initialize();
+
+       x = get_w() - control_margin;
+       y = margin;
+       add_subwindow(title = new BC_Title(x, y, _("Attack secs:")));
+       y += title->get_h();
+       reaction = new ComprMultiReaction(plugin, this, x, y);
+       reaction->create_objects();
+       y += reaction->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Release secs:")));
+       y += title->get_h();
+       decay = new ComprMultiDecay(plugin, this, x, y);
+       decay->create_objects();
+       y += decay->get_h() + margin;
+
+       add_subwindow(solo = new ComprMultiSolo(plugin, x, y));
+       y += solo->get_h() + margin;
+       add_subwindow(bypass = new ComprMultiBypass(plugin, x, y));
+       y += bypass->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Output:")));
+       y += title->get_h();
+
+       y_text = new ComprMultiY(plugin, this, x, y);
+       y_text->create_objects();
+       y += y_text->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Input:")));
+       y += title->get_h();
+       x_text = new ComprMultiX(plugin, this, x, y);
+       x_text->create_objects();
+       y += x_text->get_h() + margin;
+
+       add_subwindow(clear = new ComprMultiClear(plugin, x, y));
+       y += clear->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Freq range:")));
+       y += title->get_h();
+
+// the previous high frequency
+       int *ptr = 0;
+       if( prev_band ) {
+               ptr = &prev_band->freq;
+       }
+
+       add_subwindow(freq1 = new ComprMultiQPot(this, 
+               plugin, 
+               get_w() - (margin + BC_Pot::calculate_w()) * 2, 
+               y, 
+               ptr));
+
+// the current high frequency
+       ptr = &band_config->freq;
+       if( plugin->config.current_band == TOTAL_BANDS - 1 ) {
+               ptr = 0;
+       }
+
+       add_subwindow(freq2 = new ComprMultiQPot(this, 
+               plugin, 
+               get_w() - margin - BC_Pot::calculate_w(), 
+               y, 
+               ptr));
+       y += freq1->get_h() + margin;
+
+       BC_Bar *bar;
+       add_subwindow(bar = new BC_Bar(x, y, get_w() - x - margin));
+       y += bar->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Trigger Type:")));
+       y += title->get_h();
+       add_subwindow(input = new ComprMultiInput(plugin, x, y));
+       input->create_objects();
+       y += input->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Trigger:")));
+       y += title->get_h();
+
+       trigger = new ComprMultiTrigger(plugin, this, x, y);
+       trigger->create_objects();
+       if( plugin->config.input != ComprMultiConfig::TRIGGER ) trigger->disable();
+       y += trigger->get_h() + margin;
+
+       add_subwindow(smooth = new ComprMultiSmooth(plugin, x, y));
+       y += smooth->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Steepness:")));
+       add_subwindow(q = new ComprMultiFPot(this, plugin, 
+               get_w() - margin - BC_Pot::calculate_w(), y, 
+               &plugin->config.q, 0, 1));
+       y += q->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Window size:")));
+       y += title->get_h();
+       add_subwindow(size = new ComprMultiSize(this,
+               plugin,
+               x,
+               y));
+       size->create_objects();
+       size->update(plugin->config.window_size);
+       y += size->get_h() + margin;
+
+       canvas->create_objects();
+       update_eqcanvas();
+       show_window();
+}
+
+// called when the user selects a different band
+void ComprMultiWindow::update()
+{
+       BandConfig *band_config = &plugin->config.bands[plugin->config.current_band];
+
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               if( plugin->config.current_band == i ) {
+                       band[i]->update(1);
+               }
+               else {
+                       band[i]->update(0);
+               }
+       }
+
+       int *ptr = 0;
+       if( plugin->config.current_band > 0 ) {
+               ptr = &plugin->config.bands[plugin->config.current_band - 1].freq;
+       }
+       else {
+               ptr = 0;
+       }
+
+       freq1->output = ptr;
+       if( ptr ) {
+               freq1->update(*ptr);
+               freq1->enable();
+       }
+       else {
+               freq1->update(0);
+               freq1->disable();
+       }
+
+// top band edits the penultimate band
+       if( plugin->config.current_band < TOTAL_BANDS - 1 ) {
+               ptr = &band_config->freq;
+       }
+       else {
+               ptr = 0;
+       }
+
+       freq2->output = ptr;
+       if( ptr ) {
+               freq2->update(*ptr);
+               freq2->enable();
+       }
+       else {
+               freq2->update(0);
+               freq2->disable();
+       }
+
+       q->update(plugin->config.q);
+       solo->update(band_config->solo);
+       bypass->update(band_config->bypass);
+       size->update(plugin->config.window_size);
+
+       if( atol(trigger->get_text()) != plugin->config.trigger ) {
+               trigger->update((int64_t)plugin->config.trigger);
+       }
+
+       if( strcmp(input->get_text(), ComprMultiInput::value_to_text(plugin->config.input)) ) {
+               input->set_text(ComprMultiInput::value_to_text(plugin->config.input));
+       }
+
+       if( plugin->config.input != ComprMultiConfig::TRIGGER && trigger->get_enabled() ) {
+               trigger->disable();
+       }
+       else
+       if( plugin->config.input == ComprMultiConfig::TRIGGER && !trigger->get_enabled() ) {
+               trigger->enable();
+       }
+
+       if( !EQUIV(atof(reaction->get_text()), band_config->attack_len) ) {
+               reaction->update((float)band_config->attack_len);
+       }
+
+       if( !EQUIV(atof(decay->get_text()), band_config->release_len) ) {
+               decay->update((float)band_config->release_len);
+       }
+
+       smooth->update(plugin->config.smoothing_only);
+       if( canvas->current_operation == ComprMultiCanvas::DRAG ) {
+               x_text->update((float)band_config->levels.values[canvas->current_point].x);
+               y_text->update((float)band_config->levels.values[canvas->current_point].y);
+       }
+
+       canvas->update();
+       update_eqcanvas();
+}
+
+
+
+
+void ComprMultiWindow::update_eqcanvas()
+{
+       plugin->calculate_envelope();
+// filter GUI frames by band & data type
+       int have_meter = 0;
+       CompressorClientFrame *frame = 0;
+// gdb plugin->dump_frames();
+       double tracking_position = plugin->get_tracking_position();
+       int dir = plugin->get_tracking_direction() == PLAY_REVERSE ? -1 : 1;
+       while( (frame = (CompressorClientFrame*)plugin->next_gui_frame()) ) {
+               if( dir*(frame->position - tracking_position) > 0 ) break;
+               if( frame->band == plugin->config.current_band ) {
+// only frames for desired band
+                       switch( frame->type ) {
+                       case FREQ_COMPRESSORFRAME: {
+                               delete freq_frame;
+                               freq_frame = (CompressorFreqFrame *)
+                                       plugin->get_gui_frame(0, 0);
+                               continue; }
+                       case GAIN_COMPRESSORFRAME: {
+                               delete gain_frame;
+                               gain_frame = (CompressorGainFrame *)
+                                       plugin->get_gui_frame(0, 0);
+                               have_meter = 1;
+                               continue; }
+                       }
+               }
+               delete plugin->get_gui_frame(0, 0);
+       }
+       if( have_meter ) {
+               gain_change->update(gain_frame->gain, 0);
+               in->update(gain_frame->level, 0);
+       }
+
+#ifndef DRAW_AFTER_BANDPASS
+       eqcanvas->update_spectrogram(freq_frame); 
+#else
+       eqcanvas->update_spectrogram(freq_frame,
+               plugin->config.current_band * plugin->config.window_size / 2,
+               TOTAL_BANDS * plugin->config.window_size / 2,
+               plugin->config.window_size);
+#endif
+
+// draw the active band on top of the others
+       for( int pass = 0; pass < 2; pass++ ) {
+               for( int band = 0; band < TOTAL_BANDS; band++ ) {
+                       if( band == plugin->config.current_band && pass == 0 ||
+                               band != plugin->config.current_band && pass == 1 ) {
+                               continue;
+                       }
+
+                       eqcanvas->draw_envelope(plugin->band_states[band]->envelope,
+                               plugin->PluginAClient::project_sample_rate,
+                               plugin->config.window_size,
+                               band == plugin->config.current_band,
+                               0);
+               }
+       }
+       eqcanvas->canvas->flash(1);
+}
+
+int ComprMultiWindow::resize_event(int w, int h)
+{
+       return 1;
+}
+
+
+ComprMultiFPot::ComprMultiFPot(ComprMultiWindow *gui, ComprMultiEffect *plugin, 
+               int x, int y, double *output, double min, double max)
+ : BC_FPot(x, y, *output, min, max)
+{
+       this->gui = gui;
+       this->plugin = plugin;
+       this->output = output;
+       set_precision(0.01);
+}
+
+int ComprMultiFPot::handle_event()
+{
+       *output = get_value();
+       plugin->send_configure_change();
+       gui->update_eqcanvas();
+       return 1;
+}
+
+
+ComprMultiQPot::ComprMultiQPot(ComprMultiWindow *gui, ComprMultiEffect *plugin, 
+               int x, int y, int *output)
+ : BC_QPot(x, y, output ? *output : 0)
+{
+       this->gui = gui;
+       this->plugin = plugin;
+       this->output = output;
+}
+
+
+int ComprMultiQPot::handle_event()
+{
+       if( output ) {
+               *output = get_value();
+               plugin->send_configure_change();
+               gui->update_eqcanvas();
+       }
+       return 1;
+}
+
+
+ComprMultiSize::ComprMultiSize(ComprMultiWindow *gui, 
+       ComprMultiEffect *plugin, int x, int y)
+ : BC_PopupMenu(x, y, xS(100), "4096", 1)
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+
+int ComprMultiSize::handle_event()
+{
+       plugin->config.window_size = atoi(get_text());
+       plugin->send_configure_change();
+       gui->update_eqcanvas();
+       return 1;
+}
+
+
+void ComprMultiSize::create_objects()
+{
+       add_item(new BC_MenuItem("2048"));
+       add_item(new BC_MenuItem("4096"));
+       add_item(new BC_MenuItem("8192"));
+       add_item(new BC_MenuItem("16384"));
+       add_item(new BC_MenuItem("32768"));
+       add_item(new BC_MenuItem("65536"));
+       add_item(new BC_MenuItem("131072"));
+       add_item(new BC_MenuItem("262144"));
+}
+
+
+void ComprMultiSize::update(int size)
+{
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", size);
+       set_text(string);
+}
+
+
+ComprMultiCanvas::ComprMultiCanvas(ComprMultiEffect *plugin, 
+               ComprMultiWindow *window, int x, int y, int w, int h)
+ : CompressorCanvasBase(&plugin->config, plugin, window, x, y, w, h)
+{
+}
+
+void ComprMultiCanvas::update_window()
+{
+       ((ComprMultiWindow*)window)->update();
+}
+
+
+ComprMultiReaction::ComprMultiReaction(ComprMultiEffect *plugin, 
+       ComprMultiWindow *window, int x, int y) 
+ : BC_TumbleTextBox(window,
+       (float)plugin->config.bands[plugin->config.current_band].attack_len,
+       (float)MIN_ATTACK, (float)MAX_ATTACK, x, y, xS(100))
+{
+       this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
+}
+
+int ComprMultiReaction::handle_event()
+{
+       plugin->config.bands[plugin->config.current_band].attack_len = atof(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+ComprMultiDecay::ComprMultiDecay(ComprMultiEffect *plugin, 
+       ComprMultiWindow *window, int x, int y) 
+ : BC_TumbleTextBox(window,
+       (float)plugin->config.bands[plugin->config.current_band].release_len,
+       (float)MIN_DECAY, (float)MAX_DECAY, x, y, xS(100))
+{
+       this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
+}
+int ComprMultiDecay::handle_event()
+{
+       plugin->config.bands[plugin->config.current_band].release_len = atof(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiX::ComprMultiX(ComprMultiEffect *plugin, 
+       ComprMultiWindow *window, int x, int y) 
+ : BC_TumbleTextBox(window, (float)0.0,
+       plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
+{
+       this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
+}
+int ComprMultiX::handle_event()
+{
+       BandConfig *band_config = &plugin->config.bands[plugin->config.current_band];
+
+       int current_point = ((ComprMultiWindow*)plugin->thread->window)->canvas->current_point;
+       if( current_point < band_config->levels.total ) {
+               band_config->levels.values[current_point].x = atof(get_text());
+               ((ComprMultiWindow*)plugin->thread->window)->canvas->update();
+               plugin->send_configure_change();
+       }
+       return 1;
+}
+
+
+ComprMultiY::ComprMultiY(ComprMultiEffect *plugin, 
+       ComprMultiWindow *window, 
+       int x, 
+       int y) 
+ : BC_TumbleTextBox(window, (float)0.0,
+       plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
+{
+       this->plugin = plugin;
+       set_increment(0.1);
+       set_precision(2);
+}
+int ComprMultiY::handle_event()
+{
+       BandConfig *band_config = &plugin->config.bands[plugin->config.current_band];
+
+       int current_point = ((ComprMultiWindow*)plugin->thread->window)->canvas->current_point;
+       if( current_point < band_config->levels.total ) {
+               band_config->levels.values[current_point].y = atof(get_text());
+               ((ComprMultiWindow*)plugin->thread->window)->canvas->update();
+               plugin->send_configure_change();
+       }
+       return 1;
+}
+
+
+ComprMultiTrigger::ComprMultiTrigger(ComprMultiEffect *plugin, 
+       ComprMultiWindow *window,
+       int x, 
+       int y) 
+ : BC_TumbleTextBox(window, (int)plugin->config.trigger,
+       MIN_TRIGGER, MAX_TRIGGER, x, y, xS(100))
+{
+       this->plugin = plugin;
+}
+int ComprMultiTrigger::handle_event()
+{
+       plugin->config.trigger = atol(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiInput::ComprMultiInput(ComprMultiEffect *plugin, int x, int y) 
+ : BC_PopupMenu(x, y, xS(100), 
+       ComprMultiInput::value_to_text(plugin->config.input), 1)
+{
+       this->plugin = plugin;
+}
+int ComprMultiInput::handle_event()
+{
+       plugin->config.input = text_to_value(get_text());
+       ((ComprMultiWindow*)plugin->thread->window)->update();
+       plugin->send_configure_change();
+       return 1;
+}
+
+void ComprMultiInput::create_objects()
+{
+       for( int i = 0; i < 3; i++ ) {
+               add_item(new BC_MenuItem(value_to_text(i)));
+       }
+}
+
+const char* ComprMultiInput::value_to_text(int value)
+{
+       switch( value ) {
+       case ComprMultiConfig::TRIGGER: return "Trigger";
+       case ComprMultiConfig::MAX: return "Maximum";
+       case ComprMultiConfig::SUM: return "Total";
+       }
+
+       return "Trigger";
+}
+
+int ComprMultiInput::text_to_value(char *text)
+{
+       for( int i = 0; i < 3; i++ ) {
+               if( !strcmp(value_to_text(i), text) ) return i;
+       }
+
+       return ComprMultiConfig::TRIGGER;
+}
+
+
+ComprMultiClear::ComprMultiClear(ComprMultiEffect *plugin, int x, int y) 
+ : BC_GenericButton(x, y, _("Clear"))
+{
+       this->plugin = plugin;
+}
+
+int ComprMultiClear::handle_event()
+{
+       BandConfig *band_config = &plugin->config.bands[plugin->config.current_band];
+
+       band_config->levels.remove_all();
+//plugin->config.dump();
+       ((ComprMultiWindow*)plugin->thread->window)->update();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiSmooth::ComprMultiSmooth(ComprMultiEffect *plugin, int x, int y) 
+ : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
+{
+       this->plugin = plugin;
+}
+
+int ComprMultiSmooth::handle_event()
+{
+       plugin->config.smoothing_only = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiSolo::ComprMultiSolo(ComprMultiEffect *plugin, int x, int y) 
+ : BC_CheckBox(x, y, plugin->config.bands[plugin->config.current_band].solo, _("Solo band"))
+{
+       this->plugin = plugin;
+}
+
+int ComprMultiSolo::handle_event()
+{
+       plugin->config.bands[plugin->config.current_band].solo = get_value();
+       for( int i = 0; i < TOTAL_BANDS; i++ ) {
+               if( i != plugin->config.current_band ) {
+                       plugin->config.bands[i].solo = 0;
+               }
+       }
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiBypass::ComprMultiBypass(ComprMultiEffect *plugin, int x, int y) 
+ : BC_CheckBox(x, y, plugin->config.bands[plugin->config.current_band].bypass, _("Bypass band"))
+{
+       this->plugin = plugin;
+}
+
+int ComprMultiBypass::handle_event()
+{
+       plugin->config.bands[plugin->config.current_band].bypass = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+ComprMultiBand::ComprMultiBand(ComprMultiWindow *window, 
+               ComprMultiEffect *plugin, int x, int y, int number,
+               char *text)
+ : BC_Radial(x, y, plugin->config.current_band == number, text)
+{
+       this->window = window;
+       this->plugin = plugin;
+       this->number = number;
+}
+
+int ComprMultiBand::handle_event()
+{
+       if( plugin->config.current_band != number ) {
+               plugin->config.current_band = number;
+               window->update();
+       }
+       return 1;
+}
+
+// dump envelope sum
+//      printf("ComprMultiWindow::update_eqcanvas %d\n", __LINE__);
+//      for( int i = 0; i < plugin->config.window_size / 2; i++ )
+//      {
+//              double sum = 0;
+//              for( int band = 0; band < TOTAL_BANDS; band++ )
+//              {
+//                      sum += plugin->engines[band]->envelope[i];
+//              }
+//              
+//              printf("%f ", sum);
+//              for( int band = 0; band < TOTAL_BANDS; band++ )
+//              {
+//                      printf("%f ", plugin->engines[band]->envelope[i]);
+//              }
+//              printf("\n");
+//      }
+
+
diff --git a/cinelerra-5.1/plugins/compressormulti/comprmultigui.h b/cinelerra-5.1/plugins/compressormulti/comprmultigui.h
new file mode 100644 (file)
index 0000000..b772216
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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
+ *
+ */
+
+
+
+#ifndef COMPRMULTIGUI_H
+#define COMPRMULTIGUI_H
+
+#include "bchash.inc"
+#include "comprmulti.h"
+#include "compressortools.h"
+#include "eqcanvas.inc"
+#include "guicast.h"
+
+class ComprMultiWindow;
+
+
+class ComprMultiCanvas : public CompressorCanvasBase
+{
+public:
+       ComprMultiCanvas(ComprMultiEffect *plugin,
+               ComprMultiWindow *window, int x, int y, int w, int h);
+       void update_window();
+};
+
+class ComprMultiBand : public BC_Radial
+{
+public:
+       ComprMultiBand(ComprMultiWindow *window,
+               ComprMultiEffect *plugin, int x, int y, int number, char *text);
+       int handle_event();
+
+       ComprMultiWindow *window;
+       ComprMultiEffect *plugin;
+// 0 - (TOTAL_BANDS-1)
+       int number;
+};
+
+
+class ComprMultiReaction : public BC_TumbleTextBox
+{
+public:
+       ComprMultiReaction(ComprMultiEffect *plugin,
+               ComprMultiWindow *window, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiX : public BC_TumbleTextBox
+{
+public:
+       ComprMultiX(ComprMultiEffect *plugin, ComprMultiWindow *window, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiY : public BC_TumbleTextBox
+{
+public:
+       ComprMultiY(ComprMultiEffect *plugin, ComprMultiWindow *window, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiTrigger : public BC_TumbleTextBox
+{
+public:
+       ComprMultiTrigger(ComprMultiEffect *plugin, ComprMultiWindow *window, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiDecay : public BC_TumbleTextBox
+{
+public:
+       ComprMultiDecay(ComprMultiEffect *plugin, ComprMultiWindow *window, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+
+class ComprMultiClear : public BC_GenericButton
+{
+public:
+       ComprMultiClear(ComprMultiEffect *plugin, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiSmooth : public BC_CheckBox
+{
+public:
+       ComprMultiSmooth(ComprMultiEffect *plugin, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiSolo : public BC_CheckBox
+{
+public:
+       ComprMultiSolo(ComprMultiEffect *plugin, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiBypass : public BC_CheckBox
+{
+public:
+       ComprMultiBypass(ComprMultiEffect *plugin, int x, int y);
+       int handle_event();
+       ComprMultiEffect *plugin;
+};
+
+class ComprMultiInput : public BC_PopupMenu
+{
+public:
+       ComprMultiInput(ComprMultiEffect *plugin, int x, int y);
+       void create_objects();
+       int handle_event();
+       static const char* value_to_text(int value);
+       static int text_to_value(char *text);
+       ComprMultiEffect *plugin;
+};
+
+
+class ComprMultiFPot : public BC_FPot
+{
+public:
+       ComprMultiFPot(ComprMultiWindow *gui, ComprMultiEffect *plugin,
+                       int x, int y, double *output, double min, double max);
+       int handle_event();
+       ComprMultiWindow *gui;
+       ComprMultiEffect *plugin;
+       double *output;
+};
+
+class ComprMultiQPot : public BC_QPot
+{
+public:
+       ComprMultiQPot(ComprMultiWindow *gui, ComprMultiEffect *plugin,
+               int x, int y, int *output);
+       int handle_event();
+       ComprMultiWindow *gui;
+       ComprMultiEffect *plugin;
+       int *output;
+};
+
+
+class ComprMultiSize : public BC_PopupMenu
+{
+public:
+       ComprMultiSize(ComprMultiWindow *gui,
+               ComprMultiEffect *plugin, int x, int y);
+       int handle_event();
+       void create_objects();           // add initial items
+       void update(int size);
+       ComprMultiWindow *gui;
+       ComprMultiEffect *plugin;       
+};
+
+
+class ComprMultiWindow : public PluginClientWindow
+{
+public:
+       ComprMultiWindow(ComprMultiEffect *plugin);
+       ~ComprMultiWindow();
+       void create_objects();
+       void update();
+// draw the dynamic range canvas
+       void update_canvas();
+// draw the bandpass canvas
+       void update_eqcanvas();
+       int resize_event(int w, int h); 
+
+       ComprMultiCanvas *canvas;
+       ComprMultiReaction *reaction;
+       ComprMultiClear *clear;
+       ComprMultiX *x_text;
+       ComprMultiY *y_text;
+       ComprMultiTrigger *trigger;
+       ComprMultiDecay *decay;
+       ComprMultiSmooth *smooth;
+       ComprMultiSolo *solo;
+       ComprMultiBypass *bypass;
+       ComprMultiInput *input;
+       BC_Meter *in;
+       BC_Meter *gain_change;
+       ComprMultiBand *band[TOTAL_BANDS];
+       CompressorGainFrame *gain_frame;
+       CompressorFreqFrame *freq_frame;
+
+       ComprMultiQPot *freq1;
+       ComprMultiQPot *freq2;
+       ComprMultiFPot *q;
+       ComprMultiSize *size;
+       EQCanvas *eqcanvas;
+
+       ComprMultiEffect *plugin;
+       BC_Hash *defaults;
+};
+
+#endif
+
index 20b9900511237ea6d7b6712511c086f754dbad9d..5737d4cea6a2cb58b17ecf1faa73bbc9bc6daad0 100644 (file)
@@ -434,10 +434,7 @@ void DenoiseFFTEffect::collect_noise()
        for(int i = 0; i < config.samples; i += WINDOW_SIZE)
        {
                collect_engine->process_buffer(collection_start,
-                       WINDOW_SIZE,
-                       0,
-                       get_direction());
-
+                       WINDOW_SIZE, (Samples*)0, get_direction());
                collection_start += step * WINDOW_SIZE;
                total_windows++;
        }
index 16c2f546ad564573c628108b9a2ed1ef8ff0d695..b7cf0722f3c6bb1dc2bbeb982a6bc79f1f9e87f6 100644 (file)
@@ -320,29 +320,15 @@ void GraphicCanvas::process(int buttonpress, int motion, int draw)
        if(draw)
        {
                clear_box(0, 0, get_w(), get_h());
-
-
                int niquist = plugin->PluginAClient::project_sample_rate / 2;
-               int total_frames = plugin->get_gui_update_frames();
-               GraphicGUIFrame *frame = (GraphicGUIFrame*)plugin->get_gui_frame();
-
-               if(frame)
-               {
-                       delete plugin->last_frame;
-                       plugin->last_frame = frame;
-               }
-               else
-               {
-                       frame = plugin->last_frame;
-               }
 
-// Draw most recent frame
-               if(frame)
-               {
+// delete frames up to current tracking position
+               double tracking_position = plugin->get_tracking_position();
+               GraphicGUIFrame *frame = (GraphicGUIFrame *)
+                       plugin->get_gui_frame(tracking_position, 1);
+               if( frame ) {
+                       int y1 = 0, y2 = 0;
                        set_color(MEGREY);
-                       int y1 = 0;
-                       int y2 = 0;
-
 
                        for(int i = 0; i < get_w(); i++)
                        {
@@ -366,23 +352,7 @@ void GraphicCanvas::process(int buttonpress, int motion, int draw)
                                        y1 = y2;
                                }
                        }
-//printf( "\n");
-
-                       total_frames--;
-               }
-
-
-
-
-
-
-// Delete remaining frames
-               while(total_frames > 0)
-               {
-                       PluginClientFrame *frame = plugin->get_gui_frame();
-
-                       if(frame) delete frame;
-                       total_frames--;
+                       delete frame;
                }
        }
 
@@ -1131,18 +1101,15 @@ void GraphicEQ::update_gui()
 {
        if( !thread ) return;
        GraphicGUI *window = (GraphicGUI *)thread->window;
+       window->lock_window("GraphicEQ::update_gui");
 //lock here for points, needed by window cursor_motion callback
 //  deleted in load_configuration by read_data
-       window->lock_window("GraphicEQ::update_gui");
        if( load_configuration() &&
            window->canvas->state != GraphicCanvas::DRAG_POINT ) {
                window->update_canvas();
                window->update_textboxes();
        }
-       else {
-               int total_frames = get_gui_update_frames();
-//printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
-               if( total_frames )
+       else if( pending_gui_frames() ) {
                        window->update_canvas();
        }
        window->unlock_window();
@@ -1299,15 +1266,14 @@ void GraphicEQ::calculate_envelope(ArrayList<GraphicPoint*> *points,
 }
 
 
-
-
 GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate)
- : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
+ : PluginClientFrame()
 {
-       data = new double[window_size / 2];
+       this->window_size = window_size;
+       data_size = window_size / 2;
+       data = new double[data_size];
        freq_max = 0;
        time_max = 0;
-       this->window_size = window_size;
 }
 
 GraphicGUIFrame::~GraphicGUIFrame()
@@ -1316,10 +1282,6 @@ GraphicGUIFrame::~GraphicGUIFrame()
 }
 
 
-
-
-
-
 GraphicFFT::GraphicFFT(GraphicEQ *plugin)
  : CrossfadeFFT()
 {
index 1517dfe494e686e29cb2eaa4a6524c95ef3f9c38..ec55c476c90977788706934051bc766cef6a0e7d 100644 (file)
@@ -211,6 +211,7 @@ public:
        GraphicGUIFrame(int window_size, int sample_rate);
        virtual ~GraphicGUIFrame();
        double *data;
+       int data_size;
 // Maximum of window in frequency domain
        double freq_max;
 // Maximum of window in time domain
index ceb7b1d866a8ce676dbb2ace82a4f626b4ffd71e..59bd8e5e74594df85f3021e1bf5506b266a0dee8 100644 (file)
@@ -569,71 +569,33 @@ void ParametricWindow::update_canvas()
 
        canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
 
-
-
 // Draw spectrogram
-       int total_frames = plugin->get_gui_update_frames();
-       ParametricGUIFrame *frame = (ParametricGUIFrame*)plugin->get_gui_frame();
-
-       if(frame)
-       {
-               delete plugin->last_frame;
-               plugin->last_frame = frame;
-       }
-       else
-       {
-               frame = plugin->last_frame;
-       }
-
+       double tracking_position = plugin->get_tracking_position();
+       ParametricGUIFrame *frame = (ParametricGUIFrame *)
+               plugin->get_gui_frame(tracking_position, 1);
 // Draw most recent frame
-       if(frame)
-       {
+       if( frame ) {
+               int y1 = 0, y2 = 0;
                canvas->set_color(MEGREY);
-               int y1 = 0;
-               int y2 = 0;
-               for(int i = 0; i < canvas->get_w(); i++)
-               {
+
+               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)frame->window_size / 2 / niquist;
-                       if(index < frame->window_size / 2)
-                       {
+                       if(index < frame->window_size / 2) {
                                double magnitude = frame->data[index] /
-                                       frame->freq_max *
-                                       frame->time_max;
+                                       frame->freq_max * frame->time_max;
                                y2 = (int)(canvas->get_h() -
                                        (DB::todb(magnitude) - INFINITYGAIN) *
-                                       canvas->get_h() /
-                                       -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;
                        }
                }
-
-               total_frames--;
+               delete frame;
        }
 
-
-
-
-
-
-// Delete remaining frames
-       while(total_frames > 0)
-       {
-               PluginClientFrame *frame = plugin->get_gui_frame();
-
-               if(frame) delete frame;
-               total_frames--;
-       }
-
-
-
-
-
 //     canvas->set_color(GREEN);
 //     canvas->draw_line(0, wetness, canvas->get_w(), wetness);
 //     canvas->draw_line(0,
@@ -693,16 +655,12 @@ void ParametricWindow::update_canvas()
 }
 
 
-
-
-
-
-
 ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate)
- : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
+ : PluginClientFrame()
 {
        this->window_size = window_size;
-       data = new double[window_size / 2];
+       data_size = window_size / 2;
+       data = new double[data_size];
        freq_max = 0;
        time_max = 0;
 }
@@ -713,14 +671,6 @@ ParametricGUIFrame::~ParametricGUIFrame()
 }
 
 
-
-
-
-
-
-
-
-
 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
  : CrossfadeFFT()
 {
@@ -1029,16 +979,6 @@ int ParametricEQ::process_buffer(int64_t size,
        return 0;
 }
 
-
-
-
-
-
-
-
-
-
-
 void ParametricEQ::reset()
 {
        need_reconfigure = 1;
@@ -1048,28 +988,16 @@ void ParametricEQ::reset()
 
 void ParametricEQ::update_gui()
 {
-       if(thread)
-       {
-               if(load_configuration())
-               {
-                       ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
-                       calculate_envelope();
-                       ((ParametricWindow*)thread->window)->update_gui();
-                       ((ParametricWindow*)thread->window)->unlock_window();
-               }
-               else
-               {
-                       int total_frames = get_gui_update_frames();
-//printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
-                       if(total_frames)
-                       {
-                               ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
-                               ((ParametricWindow*)thread->window)->update_canvas();
-                               ((ParametricWindow*)thread->window)->unlock_window();
-                       }
-               }
+       if( !thread ) return;
+       ParametricWindow *window = (ParametricWindow*)thread->window;
+       window->lock_window("ParametricEQ::update_gui");
+       if( load_configuration() ) {
+               calculate_envelope();
+               window->update_gui();
        }
+       else if(pending_gui_frames()) {
+               window->update_canvas();
+       }
+       window->unlock_window();
 }
 
-
-
index 9626fb67521abfa12bbd0e7bee451b2ab4c27c04..164028eb75722c1068ab247a06ae21d6d844c35d 100644 (file)
@@ -232,6 +232,7 @@ public:
        ParametricGUIFrame(int window_size, int sample_rate);
        virtual ~ParametricGUIFrame();
        double *data;
+       int data_size;
 // Maximum of window in frequency domain
        double freq_max;
 // Maximum of window in time domain
index b5bddf2ae77ae67bf6032cef72e258001338e37c..4248a3cdc836ef7dfb7a7ecf52a247393b596f14 100644 (file)
@@ -1,7 +1,6 @@
-
 /*
  * CINELERRA
- * Copyright (C) 2017 Adam Williams <broadcast at earthling dot net>
+ * Copyright (C) 2017-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
- *
  */
 
 #include "clip.h"
@@ -29,6 +27,7 @@
 #include "reverb.h"
 #include "reverbwindow.h"
 #include "samples.h"
+#include "transportque.inc"
 #include "units.h"
 
 #include "vframe.h"
 #include <unistd.h>
 
 
-
 PluginClient* new_plugin(PluginServer *server)
 {
        return new Reverb(server);
 }
 
-
-
 Reverb::Reverb(PluginServer *server)
  : PluginAClient(server)
 {
        srand(time(0));
-       redo_buffers = 1;       // set to redo buffers before the first render
-       dsp_in_length = 0;
        ref_channels = 0;
        ref_offsets = 0;
        ref_levels = 0;
-       ref_lowpass = 0;
        dsp_in = 0;
-       lowpass_in1 = 0;
-       lowpass_in2 = 0;
-       initialized = 0;
-
+       dsp_in_length = 0;
+       dsp_in_allocated = 0;
+       need_reconfigure = 1;
+       envelope = 0;
+       last_position = 0;
+       start_pos = 0;
+       dir = 0;
+       fft = 0;
 }
 
 Reverb::~Reverb()
 {
-
-
-       if(initialized)
-       {
-               for(int i = 0; i < total_in_buffers; i++)
-               {
+       if( fft ) {
+               for( int i = 0; i < total_in_buffers; i++ ) {
                        delete [] dsp_in[i];
                        delete [] ref_channels[i];
                        delete [] ref_offsets[i];
                        delete [] ref_levels[i];
-                       delete [] ref_lowpass[i];
-                       delete [] lowpass_in1[i];
-                       delete [] lowpass_in2[i];
+                       delete fft[i];
                }
 
+               delete [] fft;
                delete [] dsp_in;
                delete [] ref_channels;
                delete [] ref_offsets;
                delete [] ref_levels;
-               delete [] ref_lowpass;
-               delete [] lowpass_in1;
-               delete [] lowpass_in2;
-
-               for(int i = 0; i < (smp + 1); i++)
-               {
-                       delete engine[i];
-               }
-               delete [] engine;
-               initialized = 0;
+               delete engine;
        }
+       delete [] envelope;
 }
 
 const char* Reverb::plugin_title() { return N_("Reverb"); }
@@ -103,184 +87,220 @@ int Reverb::is_realtime() { return 1; }
 int Reverb::is_multichannel() { return 1; }
 int Reverb::is_synthesis() { return 1; }
 
-int Reverb::process_realtime(int64_t size,
-       Samples **input_ptr,
-       Samples **output_ptr)
-{
-       int64_t new_dsp_length, i, j;
-       main_in = new double*[total_in_buffers];
-       main_out = new double*[total_in_buffers];
 
-       for(i = 0; i < total_in_buffers; i++)
-       {
-               main_in[i] = input_ptr[i]->get_data();
-               main_out[i] = output_ptr[i]->get_data();
+void Reverb::reset()
+{
+       dsp_in_length = 0;
+       if( fft ) {
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ )
+                       if( fft[i] ) fft[i]->delete_fft();
        }
-
-//printf("Reverb::process_realtime 1\n");
-       redo_buffers |= load_configuration();
-
-//printf("Reverb::process_realtime 1\n");
-       if(!config.ref_total)
-       {
-               delete [] main_in;
-               delete [] main_out;
-               return 0;
+       if( dsp_in ) {
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+                       if( !dsp_in[i] ) continue;
+                       bzero(dsp_in[i], sizeof(double) * dsp_in_allocated);
+               }
        }
+       need_reconfigure = 1;
+}
+
+void Reverb::render_stop()
+{
+       reset();
+}
 
 
-       if(!initialized)
-       {
-               dsp_in = new double*[total_in_buffers];
-               ref_channels = new int64_t*[total_in_buffers];
-               ref_offsets = new int64_t*[total_in_buffers];
-               ref_levels = new double*[total_in_buffers];
-               ref_lowpass = new int64_t*[total_in_buffers];
-               lowpass_in1 = new double*[total_in_buffers];
-               lowpass_in2 = new double*[total_in_buffers];
+int Reverb::process_buffer(int64_t size, Samples **buffer,
+               int64_t start_position, int sample_rate)
+{
+       start_pos = (double)start_position / sample_rate;
+       dir = get_direction() == PLAY_REVERSE ? -1 : 1;
+       need_reconfigure |= load_configuration();
 
+       if( need_reconfigure ) {
+               need_reconfigure = 0;
 
+               calculate_envelope();
 
-               for(i = 0; i < total_in_buffers; i++)
-               {
-                       dsp_in[i] = new double[1];
-                       ref_channels[i] = new int64_t[1];
-                       ref_offsets[i] = new int64_t[1];
-                       ref_levels[i] = new double[1];
-                       ref_lowpass[i] = new int64_t[1];
-                       lowpass_in1[i] = new double[1];
-                       lowpass_in2[i] = new double[1];
+               if( fft && fft[0]->window_size != config.window_size ) {
+                       for( int i = 0; i < PluginClient::total_in_buffers; i++ )
+                               delete fft[i];
+                       delete [] fft;  fft = 0;
                }
 
-               engine = new ReverbEngine*[(smp + 1)];
-               for(i = 0; i < (smp + 1); i++)
-               {
-                       engine[i] = new ReverbEngine(this);
-//printf("Reverb::start_realtime %d\n", Thread::calculate_realtime());
-// Realtime priority moved to sound driver
-//             engine[i]->set_realtime(realtime_priority);
-                       engine[i]->start();
+               if( !fft ) {
+                       fft = new ReverbFFT*[PluginClient::total_in_buffers];
+                       for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+                               fft[i] = new ReverbFFT(this, i);
+                               fft[i]->initialize(config.window_size);
+                       }
                }
-               initialized = 1;
-               redo_buffers = 1;
-       }
 
-       new_dsp_length = size +
-               ((int64_t)config.delay_init + config.ref_length) * project_sample_rate / 1000 + 1;
+// allocate the stuff
+               if( !dsp_in ) {
+                       dsp_in = new double*[PluginClient::total_in_buffers];
+                       ref_channels = new int*[PluginClient::total_in_buffers];
+                       ref_offsets = new int*[PluginClient::total_in_buffers];
+                       ref_levels = new double*[PluginClient::total_in_buffers];
+                       for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+                               dsp_in[i] = 0;
+                               ref_channels[i] = 0;
+                               ref_offsets[i] = 0;
+                               ref_levels[i] = 0;
+                       }
+                       engine = new ReverbEngine(this);
+               }
 
-       if(redo_buffers || new_dsp_length != dsp_in_length)
-       {
-               for(i = 0; i < total_in_buffers; i++)
-               {
-                       double *old_dsp = dsp_in[i];
-                       double *new_dsp = new double[new_dsp_length];
-                       for(j = 0; j < dsp_in_length && j < new_dsp_length; j++)
-                               new_dsp[j] = old_dsp[j];
 
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+                       delete [] ref_channels[i];  ref_channels[i] = new int[config.ref_total];
+                       delete [] ref_offsets[i];   ref_offsets[i] = new int[config.ref_total];
+                       delete [] ref_levels[i];    ref_levels[i] = new double[config.ref_total];
 
-                       for( ; j < new_dsp_length; j++) new_dsp[j] = 0;
-                       delete [] old_dsp;
-                       dsp_in[i] = new_dsp;
-               }
+// 1st reflection is fixed by the user
+                       ref_channels[i][0] = i;
+                       ref_offsets[i][0] = config.delay_init * project_sample_rate / 1000;
+                       ref_levels[i][0] = db.fromdb(config.ref_level1);
 
-               dsp_in_length = new_dsp_length;
-               redo_buffers = 1;
-       }
-//printf("Reverb::process_realtime 1\n");
-
-       if(redo_buffers)
-       {
-               for(i = 0; i < total_in_buffers; i++)
-               {
-                       delete [] ref_channels[i];
-                       delete [] ref_offsets[i];
-                       delete [] ref_lowpass[i];
-                       delete [] ref_levels[i];
-                       delete [] lowpass_in1[i];
-                       delete [] lowpass_in2[i];
-
-                       ref_channels[i] = new int64_t[config.ref_total + 1];
-                       ref_offsets[i] = new int64_t[config.ref_total + 1];
-                       ref_lowpass[i] = new int64_t[config.ref_total + 1];
-                       ref_levels[i] = new double[config.ref_total + 1];
-                       lowpass_in1[i] = new double[config.ref_total + 1];
-                       lowpass_in2[i] = new double[config.ref_total + 1];
-
-// set channels
-                       ref_channels[i][0] = i;         // primary noise
-                       ref_channels[i][1] = i;         // first reflection
-// set offsets
-                       ref_offsets[i][0] = 0;
-                       ref_offsets[i][1] = config.delay_init * project_sample_rate / 1000;
-// set levels
-                       ref_levels[i][0] = db.fromdb(config.level_init);
-                       ref_levels[i][1] = db.fromdb(config.ref_level1);
-// set lowpass
-                       ref_lowpass[i][0] = -1;     // ignore first noise
-                       ref_lowpass[i][1] = config.lowpass1;
-                       lowpass_in1[i][0] = 0;
-                       lowpass_in2[i][0] = 0;
-                       lowpass_in1[i][1] = 0;
-                       lowpass_in2[i][1] = 0;
-
-                       int64_t ref_division = config.ref_length * project_sample_rate / 1000 / (config.ref_total + 1);
-                       for(j = 2; j < config.ref_total + 1; j++)
-                       {
+                       int64_t ref_division = config.ref_length *
+                               project_sample_rate / 1000 / (config.ref_total + 1);
+                       for( int j = 1; j < config.ref_total; j++ ) {
 // set random channels for remaining reflections
                                ref_channels[i][j] = rand() % total_in_buffers;
 
 // set random offsets after first reflection
-                               ref_offsets[i][j] = ref_offsets[i][1];
-                               if( ref_division > 0 )
-                                       ref_offsets[i][j] += ref_division * j - (rand() % ref_division) / 2;
-
+                               ref_offsets[i][j] = ref_offsets[i][0];
+                               ref_offsets[i][j] += ref_division * j - (rand() % ref_division) / 2;
 // set changing levels
-                               ref_levels[i][j] = db.fromdb(config.ref_level1 + (config.ref_level2 - config.ref_level1) / (config.ref_total - 1) * (j - 2));
-                               //ref_levels[i][j] /= 100;
-
-// set changing lowpass as linear
-                               ref_lowpass[i][j] = (int64_t)(config.lowpass1 + (double)(config.lowpass2 - config.lowpass1) / (config.ref_total - 1) * (j - 2));
-                               lowpass_in1[i][j] = 0;
-                               lowpass_in2[i][j] = 0;
+                               double level_db = config.ref_level1 +
+                                       (config.ref_level2 - config.ref_level1) *
+                                       (j - 1) / (config.ref_total - 1);
+                               ref_levels[i][j] = DB::fromdb(level_db);
                        }
                }
+       }
 
-               redo_buffers = 0;
+// guess DSP allocation from the reflection time & requested samples
+       int new_dsp_allocated = size +
+               ((int64_t)config.delay_init + config.ref_length) *
+               project_sample_rate / 1000 + 1;
+       reallocate_dsp(new_dsp_allocated);
+
+// Always read in the new samples & process the bandpass, even if there is no
+// bandpass.  This way the user can tweek the bandpass without causing glitches.
+       for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+               new_dsp_length = dsp_in_length;
+               new_spectrogram_frames = 0;
+               fft[i]->process_buffer(start_position, size,
+                       buffer[i],   // temporary storage for the bandpassed output
+                       get_direction());
        }
-//printf("Reverb::process_realtime 1\n");
 
-       for(i = 0; i < total_in_buffers; )
-       {
-               for(j = 0; j < (smp + 1) && (i + j) < total_in_buffers; j++)
-               {
-                       engine[j]->process_overlays(i + j, size);
-               }
+// update the length with what the FFT reads appended
+       dsp_in_length = new_dsp_length;
 
-               for(j = 0; j < (smp + 1) && i < total_in_buffers; j++, i++)
-               {
-                       engine[j]->wait_process_overlays();
-               }
+// now paint the reflections
+       engine->process_packages();
+
+// copy the DSP buffer to the output
+       for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+               memcpy(buffer[i]->get_data(), dsp_in[i], size * sizeof(double));
        }
-//printf("Reverb::process_realtime 2 %d %d\n", total_in_buffers, size);
 
-       for(i = 0; i < total_in_buffers; i++)
-       {
-               double *current_out = main_out[i];
-               double *current_in = dsp_in[i];
+// shift the DSP buffer forward
+       int remain = dsp_in_allocated - size;
+       for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+               memcpy(dsp_in[i], dsp_in[i] + size, remain * sizeof(double));
+               bzero(dsp_in[i] + remain, size * sizeof(double));
+       }
+
+       dsp_in_length -= size;
+       last_position = start_position + dir * size;
+       return 0;
+}
 
-               for(j = 0; j < size; j++) current_out[j] = current_in[j];
 
-               int64_t k;
-               for(k = 0; j < dsp_in_length; j++, k++) current_in[k] = current_in[j];
+void Reverb::reallocate_dsp(int new_dsp_allocated)
+{
+       if( new_dsp_allocated > dsp_in_allocated ) {
+// copy samples already read into the new buffers
+               for( int i = 0; i < PluginClient::total_in_buffers; i++ ) {
+                       double *old_dsp = dsp_in[i];
+                       double *new_dsp = new double[new_dsp_allocated];
 
-               for(; k < dsp_in_length; k++) current_in[k] = 0;
+                       if( old_dsp ) {
+                               memcpy(new_dsp, old_dsp, sizeof(double) * dsp_in_length);
+                               delete [] old_dsp;
+                       }
+                       bzero(new_dsp + dsp_in_allocated,
+                               sizeof(double) * (new_dsp_allocated - dsp_in_allocated));
+                       dsp_in[i] = new_dsp;
+               }
+               dsp_in_allocated = new_dsp_allocated;
        }
-//printf("Reverb::process_realtime 2 %d %d\n", total_in_buffers, size);
 
+}
 
-       delete [] main_in;
-       delete [] main_out;
-       return 0;
+
+double Reverb::gauss(double sigma, double center, double x)
+{
+       if( EQUIV(sigma, 0) ) sigma = 0.01;
+
+       double result = 1.0 / sqrt(2 * M_PI * sigma * sigma) *
+               exp(-(x - center) * (x - center) / (2 * sigma * sigma));
+       return result;
+}
+
+void Reverb::calculate_envelope()
+{
+// assume the window size changed
+       delete [] envelope;
+       int window_size2 = config.window_size/2;
+       envelope = new double[window_size2];
+
+       int max_freq = Freq::tofreq_f(TOTALFREQS-1);
+       int nyquist = PluginAClient::project_sample_rate/2;
+       int low = config.low;
+       int high = config.high;
+
+// limit the frequencies
+       if( high >= max_freq ) high = nyquist;
+       if( low > high ) low = high;
+
+// frequency slots of the edge
+       double edge = (1.0 - config.q) * TOTALFREQS/2;
+       double low_slot = Freq::fromfreq_f(low);
+       double high_slot = Freq::fromfreq_f(high);
+       for( int i = 0; i < window_size2; i++ ) {
+               double freq = i * nyquist / window_size2;
+               double slot = Freq::fromfreq_f(freq);
+
+// printf("Reverb::calculate_envelope %d i=%d freq=%f slot=%f slot1=%f\n",
+// __LINE__, i, freq, slot, low_slot - edge);
+               if( slot < low_slot - edge ) {
+                       envelope[i] = 0.0;
+               }
+               else if( slot < low_slot ) {
+#ifndef LOG_CROSSOVER
+                       envelope[i] = 1.0 - (low_slot - slot) / edge;
+#else
+                       envelope[i] = DB::fromdb((low_slot - slot) * INFINITYGAIN / edge);
+#endif
+               }
+               else if( slot < high_slot ) {
+                       envelope[i] = 1.0;
+               }
+               else if( slot < high_slot + edge ) {
+#ifndef LOG_CROSSOVER
+                       envelope[i] = 1.0 - (slot - high_slot) / edge;
+#else
+                       envelope[i] = DB::fromdb((slot - high_slot) * INFINITYGAIN / edge);
+#endif
+               }
+               else {
+                       envelope[i] = 0.0;
+               }
+       }
 }
 
 
@@ -292,54 +312,41 @@ LOAD_CONFIGURATION_MACRO(Reverb, ReverbConfig)
 
 void Reverb::save_data(KeyFrame *keyframe)
 {
-//printf("Reverb::save_data 1\n");
        FileXML output;
-//printf("Reverb::save_data 1\n");
-
-// cause xml file to store data directly in text
        output.set_shared_output(keyframe->xbuf);
-//printf("Reverb::save_data 1\n");
-
        output.tag.set_title("REVERB");
        output.tag.set_property("LEVELINIT", config.level_init);
        output.tag.set_property("DELAY_INIT", config.delay_init);
        output.tag.set_property("REF_LEVEL1", config.ref_level1);
        output.tag.set_property("REF_LEVEL2", config.ref_level2);
        output.tag.set_property("REF_TOTAL", config.ref_total);
-//printf("Reverb::save_data 1\n");
        output.tag.set_property("REF_LENGTH", config.ref_length);
-       output.tag.set_property("LOWPASS1", config.lowpass1);
-       output.tag.set_property("LOWPASS2", config.lowpass2);
-//printf("Reverb::save_data config.ref_level2 %f\n", config.ref_level2);
-       output.append_tag();
-       output.tag.set_title("/REVERB");
+       output.tag.set_property("HIGH", config.high);
+       output.tag.set_property("LOW", config.low);
+       output.tag.set_property("Q", config.q);
+       output.tag.set_property("WINDOW_SIZE", config.window_size);
        output.append_tag();
        output.append_newline();
        output.terminate_string();
-//printf("Reverb::save_data 2\n");
 }
 
 void Reverb::read_data(KeyFrame *keyframe)
 {
        FileXML input;
-// cause xml file to read directly from text
        input.set_shared_input(keyframe->xbuf);
        int result = 0;
-
-       result = input.read_tag();
-
-       if(!result)
-       {
-               if(input.tag.title_is("REVERB"))
-               {
+       while( !(result = input.read_tag()) ) {
+               if( input.tag.title_is("REVERB") ) {
                        config.level_init = input.tag.get_property("LEVELINIT", config.level_init);
                        config.delay_init = input.tag.get_property("DELAY_INIT", config.delay_init);
                        config.ref_level1 = input.tag.get_property("REF_LEVEL1", config.ref_level1);
                        config.ref_level2 = input.tag.get_property("REF_LEVEL2", config.ref_level2);
                        config.ref_total = input.tag.get_property("REF_TOTAL", config.ref_total);
                        config.ref_length = input.tag.get_property("REF_LENGTH", config.ref_length);
-                       config.lowpass1 = input.tag.get_property("LOWPASS1", config.lowpass1);
-                       config.lowpass2 = input.tag.get_property("LOWPASS2", config.lowpass2);
+                       config.high = input.tag.get_property("HIGH", config.high);
+                       config.low = input.tag.get_property("LOW", config.low);
+                       config.q = input.tag.get_property("Q", config.q);
+                       config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
                }
        }
 
@@ -348,186 +355,182 @@ void Reverb::read_data(KeyFrame *keyframe)
 
 void Reverb::update_gui()
 {
-       if(thread)
-       {
-               if(load_configuration())
-               {
-//printf("Reverb::update_gui %d %d\n", __LINE__, config.ref_length);
-                       thread->window->lock_window("Reverb::update_gui");
-                       ((ReverbWindow*)thread->window)->level_init->update(config.level_init);
-                       ((ReverbWindow*)thread->window)->delay_init->update(config.delay_init);
-                       ((ReverbWindow*)thread->window)->ref_level1->update(config.ref_level1);
-                       ((ReverbWindow*)thread->window)->ref_level2->update(config.ref_level2);
-                       ((ReverbWindow*)thread->window)->ref_total->update(config.ref_total);
-                       ((ReverbWindow*)thread->window)->ref_length->update(config.ref_length);
-                       ((ReverbWindow*)thread->window)->lowpass1->update(config.lowpass1);
-                       ((ReverbWindow*)thread->window)->lowpass2->update(config.lowpass2);
-                       thread->window->unlock_window();
-               }
-       }
+       if( !thread ) return;
+       ReverbWindow *window = (ReverbWindow *)thread->window;
+       if( !window ) return;
+       int reconfigured = load_configuration();
+       int total_frames = pending_gui_frames();
+       if( !reconfigured && !total_frames ) return;
+       window->lock_window("Reverb::update_gui 1");
+       if( reconfigured )
+               window->update();
+       if( total_frames )
+               window->update_canvas();
+       window->unlock_window();
 }
 
+ReverbClientFrame::ReverbClientFrame(int size)
+ : CompressorFreqFrame()
+{
+       data = new double[size];
+       bzero(data, sizeof(double) * size);
+       data_size = size;
+}
 
+ReverbClientFrame::~ReverbClientFrame()
+{
+       delete [] data;  data = 0;
+}
 
-
-// int Reverb::load_from_file(char *path)
-// {
-//     FILE *in;
-//     int result = 0;
-//     int length;
-//     char string[1024];
-//     
-//     if(in = fopen(path, "rb"))
-//     {
-//             fseek(in, 0, SEEK_END);
-//             length = ftell(in);
-//             fseek(in, 0, SEEK_SET);
-//             int temp = fread(string, length, 1, in);
-//             fclose(in);
-// //          read_data(string);
-//     }
-//     else
-//     {
-//             perror("fopen:");
-// // failed
-//             ErrorBox errorbox("");
-//             char string[1024];
-//             sprintf(string, _("Couldn't open %s."), path);
-//             errorbox.create_objects(string);
-//             errorbox.run_window();
-//             result = 1;
-//     }
-//     
-//     return result;
-// }
-// 
-// int Reverb::save_to_file(char *path)
-// {
-//     FILE *out;
-//     int result = 0;
-//     char string[1024];
-//     
-//     {
-// //          ConfirmSave confirm;
-// //          result = confirm.test_file("", path);
-//     }
-//     
-//     if(!result)
-//     {
-//             if(out = fopen(path, "wb"))
-//             {
-// //                  save_data(string);
-//                     fwrite(string, strlen(string), 1, out);
-//                     fclose(out);
-//             }
-//             else
-//             {
-//                     result = 1;
-// // failed
-//                     ErrorBox errorbox("");
-//                     char string[1024];
-//                     sprintf(string, _("Couldn't save %s."), path);
-//                     errorbox.create_objects(string);
-//                     errorbox.run_window();
-//                     result = 1;
-//             }
-//     }
-//     
-//     return result;
-// }
-
-ReverbEngine::ReverbEngine(Reverb *plugin)
- : Thread(1, 0, 0)
+ReverbFFT::ReverbFFT(Reverb *plugin, int channel)
 {
        this->plugin = plugin;
-       completed = 0;
-       input_lock.lock();
-       output_lock.lock();
+       this->channel = channel;
 }
 
-ReverbEngine::~ReverbEngine()
+ReverbFFT::~ReverbFFT()
 {
-       completed = 1;
-       input_lock.unlock();
-       join();
 }
 
-int ReverbEngine::process_overlays(int output_buffer, int64_t size)
+int ReverbFFT::signal_process()
 {
-       this->output_buffer = output_buffer;
-       this->size = size;
-       input_lock.unlock();
+// Create new spectrogram for updating the GUI
+       frame = new ReverbClientFrame(window_size/2);
+       frame->nyquist = plugin->PluginAClient::project_sample_rate/2;
+       frame->position = plugin->start_pos + plugin->dir *
+               (double)plugin->new_spectrogram_frames * frame->data_size /
+               plugin->get_samplerate();
+       plugin->add_gui_frame(frame);
+
+       for( int i=0; i<frame->data_size; i++ ) {
+               double env = plugin->envelope[i];
+               double fr = freq_real[i], fi = freq_imag[i];
+// scale complex signal by envelope
+               freq_real[i] = fr * env;
+               freq_imag[i] = fi * env;
+               double mag = sqrt(fr*fr + fi*fi);
+               double out = mag * env;
+// update the spectrogram with the output
+               if( frame->data[i] < out )
+                       frame->data[i] = out;
+// get the maximum output in the frequency domain
+               if( frame->freq_max < out )
+                       frame->freq_max = out;
+       }
+
+       symmetry(window_size, freq_real, freq_imag);
        return 0;
 }
 
-int ReverbEngine::wait_process_overlays()
+int ReverbFFT::post_process()
 {
-       output_lock.lock();
+// get the maximum output in the time domain
+       double time_max = 0;
+       for( int i=0; i<window_size; ++i ) {
+               if( fabs(output_real[i]) > time_max )
+                       time_max = fabs(output_real[i]);
+       }
+       if( time_max > frame->time_max )
+               frame->time_max = time_max;
+
+       ++plugin->new_spectrogram_frames;
        return 0;
 }
 
-int ReverbEngine::process_overlay(double *in, double *out, double &out1, double &out2, double level, int64_t lowpass, int64_t samplerate, int64_t size)
+
+int ReverbFFT::read_samples(int64_t output_sample, int samples, Samples *buffer)
 {
-// Modern niquist frequency is 44khz but pot limit is 20khz so can't use
-// niquist
-       if(lowpass == -1 || lowpass >= 20000)
-       {
-// no lowpass filter
-               for(int i = 0; i < size; i++) out[i] += in[i] * level;
+       int result = plugin->read_samples(buffer,
+               channel, plugin->get_samplerate(), output_sample, samples);
+
+// append original samples to the DSP buffer as the initial reflection
+       int new_dsp_allocation = plugin->new_dsp_length + samples;
+       plugin->reallocate_dsp(new_dsp_allocation);
+       double *dst = plugin->dsp_in[channel] + plugin->new_dsp_length;
+       double *src = buffer->get_data();
+       double level = DB::fromdb(plugin->config.level_init);
+       if( plugin->config.level_init <= INFINITYGAIN ) {
+               level = 0;
        }
-       else
-       {
-               double coef = 0.25 * 2.0 * M_PI * (double)lowpass / (double)plugin->project_sample_rate;
-               double a = coef * 0.25;
-               double b = coef * 0.50;
-
-               for(int i = 0; i < size; i++)
-               {
-                       out2 += a * (3 * out1 + in[i] - out2);
-                       out2 += b * (out1 + in[i] - out2);
-                       out2 += a * (out1 + 3 * in[i] - out2);
-                       out2 += coef * (in[i] - out2);
-                       out1 = in[i];
-                       out[i] += out2 * level;
-               }
+
+       for( int i = 0; i < samples; i++ ) {
+               *dst++ += *src++ * level;
        }
-       return 0;
+
+       plugin->new_dsp_length += samples;
+       return result;
 }
 
-void ReverbEngine::run()
-{
-       int j, i;
-//printf("ReverbEngine::run 1 %d\n", calculate_realtime());
-       while(1)
-       {
-               input_lock.lock();
-               if(completed) return;
-
-// Process reverb
-               for(i = 0; i < plugin->total_in_buffers; i++)
-               {
-                       for(j = 0; j < plugin->config.ref_total + 1; j++)
-                       {
-                               if(plugin->ref_channels[i][j] == output_buffer)
-                                       process_overlay(plugin->main_in[i],
-                                                               &(plugin->dsp_in[plugin->ref_channels[i][j]][plugin->ref_offsets[i][j]]),
-                                                               plugin->lowpass_in1[i][j],
-                                                               plugin->lowpass_in2[i][j],
-                                                               plugin->ref_levels[i][j],
-                                                               plugin->ref_lowpass[i][j],
-                                                               plugin->project_sample_rate,
-                                                               size);
-                       }
+
+ReverbPackage::ReverbPackage()
+ : LoadPackage()
+{
+}
+
+
+ReverbUnit::ReverbUnit(ReverbEngine *engine, Reverb *plugin)
+ : LoadClient(engine)
+{
+       this->plugin = plugin;
+}
+
+ReverbUnit::~ReverbUnit()
+{
+}
+
+void ReverbUnit::process_package(LoadPackage *package)
+{
+       ReverbPackage *pkg = (ReverbPackage*)package;
+       int channel = pkg->channel;
+
+       for( int i = 0; i < plugin->config.ref_total; i++ ) {
+               int src_channel = plugin->ref_channels[channel][i];
+               int dst_offset = plugin->ref_offsets[channel][i];
+               double level = plugin->ref_levels[channel][i];
+               double *dst = plugin->dsp_in[channel] + dst_offset;
+               double *src = plugin->get_output(src_channel)->get_data();
+               int size = plugin->get_buffer_size();
+
+               if( size + dst_offset > plugin->dsp_in_allocated ) {
+                       printf("ReverbUnit::process_package %d size=%d dst_offset=%d needed=%d allocated=%d\n",
+                       __LINE__, size, dst_offset, size + dst_offset, plugin->dsp_in_allocated);
                }
 
-               output_lock.unlock();
+               for( int j = 0; j < size; j++ )
+                       *dst++ += *src++ * level;
        }
 }
 
 
 
+ReverbEngine::ReverbEngine(Reverb *plugin)
+ : LoadServer(plugin->PluginClient::smp + 1, plugin->total_in_buffers)
+{
+       this->plugin = plugin;
+}
+
+ReverbEngine::~ReverbEngine()
+{
+}
+
+
+void ReverbEngine::init_packages()
+{
+       for( int i = 0; i < LoadServer::get_total_packages(); i++ ) {
+               ReverbPackage *package = (ReverbPackage*)LoadServer::get_package(i);
+               package->channel = i;
+       }
+}
 
+LoadClient* ReverbEngine::new_client()
+{
+       return new ReverbUnit(this, plugin);
+}
 
+LoadPackage* ReverbEngine::new_package()
+{
+       return new ReverbPackage;
+}
 
 
 ReverbConfig::ReverbConfig()
@@ -536,11 +539,12 @@ ReverbConfig::ReverbConfig()
        delay_init = 0;
        ref_level1 = -20;
        ref_level2 = INFINITYGAIN;
-       ref_total = 100;
+       ref_total = 128;
        ref_length = 600;
-       lowpass1 = Freq::tofreq(TOTALFREQS);
-       lowpass2 = Freq::tofreq(TOTALFREQS);
-
+       high = Freq::tofreq(TOTALFREQS);
+       low = Freq::tofreq(0);
+       q = 1.0;
+       window_size = 4096;
 }
 
 int ReverbConfig::equivalent(ReverbConfig &that)
@@ -551,8 +555,10 @@ int ReverbConfig::equivalent(ReverbConfig &that)
                EQUIV(ref_level2, that.ref_level2) &&
                ref_total == that.ref_total &&
                ref_length == that.ref_length &&
-               lowpass1 == that.lowpass1 &&
-               lowpass2 == that.lowpass2);
+               high == that.high &&
+               low == that.low &&
+               EQUIV(q, that.q) &&
+               window_size == that.window_size);
 }
 
 void ReverbConfig::copy_from(ReverbConfig &that)
@@ -563,15 +569,14 @@ void ReverbConfig::copy_from(ReverbConfig &that)
        ref_level2 = that.ref_level2;
        ref_total = that.ref_total;
        ref_length = that.ref_length;
-       lowpass1 = that.lowpass1;
-       lowpass2 = that.lowpass2;
+       high = that.high;
+       low = that.low;
+       q = that.q;
+       window_size = that.window_size;
 }
 
-void ReverbConfig::interpolate(ReverbConfig &prev,
-       ReverbConfig &next,
-       int64_t prev_frame,
-       int64_t next_frame,
-       int64_t current_frame)
+void ReverbConfig::interpolate(ReverbConfig &prev, ReverbConfig &next,
+       int64_t prev_frame, int64_t next_frame, int64_t current_frame)
 {
        level_init = prev.level_init;
        delay_init = prev.delay_init;
@@ -579,28 +584,30 @@ void ReverbConfig::interpolate(ReverbConfig &prev,
        ref_level2 = prev.ref_level2;
        ref_total = prev.ref_total;
        ref_length = prev.ref_length;
-       lowpass1 = prev.lowpass1;
-       lowpass2 = prev.lowpass2;
+       high = prev.high;
+       low = prev.low;
+       q = prev.q;
+       window_size = prev.window_size;
 }
 
 void ReverbConfig::boundaries()
 {
-
        CLAMP(level_init, INFINITYGAIN, 0);
        CLAMP(delay_init, 0, MAX_DELAY_INIT);
        CLAMP(ref_level1, INFINITYGAIN, 0);
        CLAMP(ref_level2, INFINITYGAIN, 0);
        CLAMP(ref_total, MIN_REFLECTIONS, MAX_REFLECTIONS);
-       CLAMP(ref_length, 0, MAX_REFLENGTH);
-       CLAMP(lowpass1, 0, Freq::tofreq(TOTALFREQS));
-       CLAMP(lowpass2, 0, Freq::tofreq(TOTALFREQS));
+       CLAMP(ref_length, MIN_REFLENGTH, MAX_REFLENGTH);
+       CLAMP(high, 0, Freq::tofreq(TOTALFREQS));
+       CLAMP(low, 0, Freq::tofreq(TOTALFREQS));
+       CLAMP(q, 0.0, 1.0);
 }
 
 void ReverbConfig::dump()
 {
-       printf("ReverbConfig::dump %f %jd %f %f %jd %jd %jd %jd\n",
-               level_init, delay_init, ref_level1, ref_level2,
-               ref_total, ref_length, lowpass1, lowpass2);
+       printf("ReverbConfig::dump %d level_init=%f delay_init=%d ref_level1=%f"
+               " ref_level2=%f ref_total=%d ref_length=%d high=%d low=%d q=%f\n",
+               __LINE__, level_init, (int)delay_init, ref_level1, ref_level2,
+               (int)ref_total, (int)ref_length, (int)high, (int)low, q);
 }
 
-
index 629c74ce173ef24dc38a620217fe4393f5c0f36b..2b889c89a7f1357bab9af288cb64169fa0672b38 100644 (file)
@@ -1,7 +1,7 @@
 
 /*
  * 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 +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
- *
  */
 
 #ifndef REVERB_H
 
 class Reverb;
 class ReverbEngine;
+class ReverbFFT;
 
+#include "compressortools.h"
+#include "fourier.h"
 #include "reverbwindow.h"
+#include "loadbalance.h"
 #include "pluginaclient.h"
 
 #define MAX_DELAY_INIT 1000
 #define MIN_REFLECTIONS 1
 #define MAX_REFLECTIONS 255
+#define MIN_REFLENGTH 3
 #define MAX_REFLENGTH 5000
 
 class ReverbConfig
@@ -41,21 +45,23 @@ public:
 
        int equivalent(ReverbConfig &that);
        void copy_from(ReverbConfig &that);
-       void interpolate(ReverbConfig &prev,
-               ReverbConfig &next,
-               int64_t prev_frame,
-               int64_t next_frame,
-               int64_t current_frame);
+       void interpolate(ReverbConfig &prev, ReverbConfig &next,
+               int64_t prev_frame, int64_t next_frame, int64_t current_frame);
        void dump();
        void boundaries();
 
-       double level_init;
-       int64_t delay_init;
-       double ref_level1;
-       double ref_level2;
-       int64_t ref_total;
-       int64_t ref_length;
-       int64_t lowpass1, lowpass2;
+       float level_init;
+       int delay_init;
+       float ref_level1;
+       float ref_level2;
+       int ref_total;
+       int ref_length;
+// high frequency
+       int high;
+// low frequency
+       int low;
+       float q;
+       int window_size;
 };
 
 class Reverb : public PluginAClient
@@ -64,54 +70,110 @@ public:
        Reverb(PluginServer *server);
        ~Reverb();
 
+       void reset();
+       void render_stop();
        void update_gui();
-//     int load_from_file(char *data);
-//     int save_to_file(char *data);
-
-// data for reverb
-       char config_directory[1024];
 
-       double **main_in, **main_out;
-       double **dsp_in;
-       int64_t **ref_channels, **ref_offsets, **ref_lowpass;
-       double **ref_levels;
-       int64_t dsp_in_length;
-       int redo_buffers;
-// skirts for lowpass filter
-       double **lowpass_in1, **lowpass_in2;
-       DB db;
 // required for all realtime/multichannel plugins
-
        PLUGIN_CLASS_MEMBERS(ReverbConfig);
-       int process_realtime(int64_t size, Samples **input_ptr, Samples **output_ptr);
+       int process_buffer(int64_t size, Samples **buffer,
+                       int64_t start_position, int sample_rate);
+       double gauss(double sigma, double center, double x);
+       void calculate_envelope();
+       void reallocate_dsp(int new_dsp_allocated);
+
        int is_realtime();
        int is_synthesis();
        int is_multichannel();
-       int show_gui();
-       int set_string();
        void save_data(KeyFrame *keyframe);
        void read_data(KeyFrame *keyframe);
-       void raise_window();
 
-       ReverbEngine **engine;
-       int initialized;
+// the output all reflections are painted on
+       double **dsp_in;
+// may have to expand it for fft windows larger than the reflected time
+       int dsp_in_allocated;
+// total samples read into dsp_in by the FFT
+       int dsp_in_length;
+// new value calculated by the FFT readers
+       int new_dsp_length;
+// total spectrogram frames generated by the FFT.  Each channel overwrites the same
+// spectrograms
+       int new_spectrogram_frames;
+
+// source channels of reflections
+       int **ref_channels;
+// destination offsets of reflections
+       int **ref_offsets;
+// levels of reflections
+       double **ref_levels;
+// detect seeking
+       int64_t last_position;
+// start_position / sample_rate
+       double start_pos;
+// get_direction fwd=1, rev=-1, stop=0
+       int dir;
+       DB db;
+
+       ReverbEngine *engine;
+       ReverbFFT **fft;
+       double *envelope;
+       int need_reconfigure;
+};
+
+class ReverbClientFrame : public CompressorFreqFrame
+{
+public:
+       ReverbClientFrame(int size);
+       ~ReverbClientFrame();
+};
+
+
+class ReverbFFT :public CrossfadeFFT
+{
+public:
+       ReverbFFT(Reverb *plugin, int channel);
+       ~ReverbFFT();
+
+       int signal_process();
+       int post_process();
+       int read_samples(int64_t output_sample, int samples, Samples *buffer);
+
+       Reverb *plugin;
+       ReverbClientFrame *frame;
+       int channel;
+};
+
+
+class ReverbPackage : public LoadPackage
+{
+public:
+       ReverbPackage();
+       int channel;
+};
+
+
+class ReverbUnit : public LoadClient
+{
+public:
+       ReverbUnit(ReverbEngine *engine, Reverb *plugin);
+       ~ReverbUnit();
+       void process_package(LoadPackage *package);
+       ReverbEngine *engine;
+       Reverb *plugin;
 };
 
-class ReverbEngine : public Thread
+// This allocates 1 CPU for each output channel.
+// They simultaneously read from all the input FFT channels.
+class ReverbEngine : public LoadServer
 {
 public:
        ReverbEngine(Reverb *plugin);
        ~ReverbEngine();
 
-       int process_overlay(double *in, double *out, double &out1, double &out2, double level, int64_t lowpass, int64_t samplerate, int64_t size);
-       int process_overlays(int output_buffer, int64_t size);
-       int wait_process_overlays();
-       void run();
+       void init_packages();
+       LoadClient* new_client();
+       LoadPackage* new_package();
 
-       Mutex input_lock, output_lock;
-       int completed;
-       int output_buffer;
-       int64_t size;
        Reverb *plugin;
 };
 
index f827f6c4a50b89bf5a0c493bf1a54bc594942322..9683150a2e6c21987e525f3adb3a876e2ec58f9c 100644 (file)
  * 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 REVERB_INC
 #define REVERB_INC
 
-
 class Reverb;
 
 #endif
index 869333634e17bc5138817c4f9f124397db4320d6..63a21daf2b34a3c19c944528147094df27c849a0 100644 (file)
@@ -1,7 +1,7 @@
 
 /*
  * 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
  * 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 "bcdisplayinfo.h"
 #include "bchash.h"
 #include "bcsignals.h"
 #include "filesystem.h"
+#include "eqcanvas.h"
 #include "language.h"
 #include "reverb.h"
 #include "reverbwindow.h"
+#include "theme.h"
 
 #include <string.h>
 
-
+#define TEXT_W xS(90)
+#define WINDOW_W xS(400)
+#define WINDOW_H yS(450)
 
 ReverbWindow::ReverbWindow(Reverb *reverb)
  : PluginClientWindow(reverb,
-       xS(300),
-       yS(230),
-       xS(300),
-       yS(230),
-       0)
+       WINDOW_W, WINDOW_H, WINDOW_W, WINDOW_H, 0)
 {
        this->reverb = reverb;
 }
 
 ReverbWindow::~ReverbWindow()
 {
+       for( int i = 0; i < TOTAL_PARAMS; i++ )
+               delete params[i];
+       delete canvas;
 }
 
 void ReverbWindow::create_objects()
 {
-       int xs5 = xS(5), xs35 = xS(35), xs200 = xS(200);
-       int ys10 = yS(10), ys25 = yS(25), ys40 = yS(40);
-       int x = xs200, y = ys10;
-       add_tool(new BC_Title(xs5, y + ys10, _("Initial signal level:")));
-       add_tool(level_init = new ReverbLevelInit(reverb, x, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("ms before reflections:")));
-       add_tool(delay_init = new ReverbDelayInit(reverb, x + xs35, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("First reflection level:")));
-       add_tool(ref_level1 = new ReverbRefLevel1(reverb, x, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("Last reflection level:")));
-       add_tool(ref_level2 = new ReverbRefLevel2(reverb, x + xs35, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("Number of reflections:")));
-       add_tool(ref_total = new ReverbRefTotal(reverb, x, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("ms of reflections:")));
-       add_tool(ref_length = new ReverbRefLength(reverb, x + xs35, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("Start band for lowpass:")));
-       add_tool(lowpass1 = new ReverbLowPass1(reverb, x, y)); y += ys25;
-       add_tool(new BC_Title(xs5, y + ys10, _("End band for lowpass:")));
-       add_tool(lowpass2 = new ReverbLowPass2(reverb, x + xs35, y)); y += ys40;
-       show_window();
-       flush();
-}
+       int margin = client->get_theme()->widget_border;
+       int x = xS(230), y = margin;
+       int x1 = x + BC_Pot::calculate_w();
+       int x2 = x1 + BC_Pot::calculate_w() + margin;
+       int text_w = get_w() - margin - x2;
+       int height = BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin;
+
+
+       int i = 0;
+       params[i] = new ReverbParam(reverb, this,
+               margin, x, x2, y, text_w,
+               0,  // output_i
+               &reverb->config.level_init, // output_f
+               0, // output_q
+               "Initial signal level (db):",
+               INFINITYGAIN, // min
+               0); // max
+       params[i]->initialize();
+       i++;
+       y += height;
+
+       params[i] = new ReverbParam(reverb, this,
+               margin, x1, x2, y, text_w,
+               &reverb->config.delay_init,  // output_i
+               0, // output_f
+               0, // output_q
+               "ms before reflections:",
+               0, // min
+               MAX_DELAY_INIT); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
+       params[i] = new ReverbParam(reverb, this,
+               margin, x, x2, y, text_w,
+               0,  // output_i
+               &reverb->config.ref_level1, // output_f
+               0, // output_q
+               "First reflection level (db):",
+               INFINITYGAIN, // min
+               0); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
+       params[i] = new ReverbParam(reverb, this,
+               margin, x1, x2, y, text_w,
+               0,  // output_i
+               &reverb->config.ref_level2, // output_f
+               0, // output_q
+               "Last reflection level (db):",
+               INFINITYGAIN, // min
+               0); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
+       params[i] = new ReverbParam(reverb, this,
+               margin, x, x2, y, text_w,
+               &reverb->config.ref_total,  // output_i
+               0, // output_f
+               0, // output_q
+               "Number of reflections:",
+               MIN_REFLECTIONS, // min
+               MAX_REFLECTIONS); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
+       params[i] = new ReverbParam(reverb, this,
+               margin, x1, x2, y, text_w,
+               &reverb->config.ref_length,  // output_i
+               0, // output_f
+               0, // output_q
+               "ms of reflections:",
+               0, // min
+               MAX_REFLENGTH); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
+       params[i] = new ReverbParam(reverb, this,
+               margin, x, x2, y, text_w,
+               0,  // output_i
+               0, // output_f
+               &reverb->config.low, // output_q
+               "Low freq of bandpass:",
+               0, // min
+               0); // max
+       params[i]->initialize();
+       i++;
+       y += height;
 
-ReverbLevelInit::ReverbLevelInit(Reverb *reverb, int x, int y)
- : BC_FPot(x,
-       y,
-       reverb->config.level_init,
-       INFINITYGAIN,
-       0)
+       params[i] = new ReverbParam(reverb, this,
+               margin, x1, x2, y, text_w,
+               0,  // output_i
+               0, // output_f
+               &reverb->config.high, // output_q
+               "High freq of bandpass:",
+               0, // min
+               0); // max
+       params[i]->initialize();
+       i++;
+       y += height;
+
+       params[i] = new ReverbParam(reverb, this,
+               margin, x, x2, y, text_w,
+               0,  // output_i
+               &reverb->config.q, // output_f
+               0, // output_q
+               "Steepness of bandpass:",
+               0.0, // min
+               1.0); // max
+       params[i]->initialize();
+       params[i]->fpot->set_precision(0.01);
+       i++;
+       y += BC_Pot::calculate_h() + margin;
+
+
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(margin, y, _("Window:")));
+       add_subwindow(size = new ReverbSize(this, reverb,
+               margin + title->get_w() + margin, y));
+       size->create_objects();
+       size->update(reverb->config.window_size);
+       y += size->get_h() + margin;
+
+       int canvas_x = 0;
+       int canvas_y = y;
+       int canvas_w = get_w() - margin;
+       int canvas_h = get_h() - canvas_y - margin;
+       canvas = new EQCanvas(this,
+               canvas_x, canvas_y, canvas_w, canvas_h,
+               INFINITYGAIN, 0.0);
+       canvas->initialize();
+       update();
+
+       show_window();
+}
+
+void ReverbWindow::update()
 {
-       this->reverb = reverb;
+       for( int i = 0; i < TOTAL_PARAMS; i++ )
+               params[i]->update(1, 1);
+       size->update(reverb->config.window_size);
+       update_canvas();
 }
-ReverbLevelInit::~ReverbLevelInit()
+
+
+void ReverbWindow::update_canvas()
 {
+       double tracking_position = reverb->get_tracking_position();
+       CompressorFreqFrame *frame = (CompressorFreqFrame *)
+               reverb->get_gui_frame(tracking_position, 1);
+       canvas->update_spectrogram(frame, -1, -1, -1);
+
+// draw the envelope
+       reverb->calculate_envelope();
+       canvas->draw_envelope(reverb->envelope,
+               reverb->PluginAClient::project_sample_rate,
+               reverb->config.window_size, 1, 0);
+       BC_SubWindow *gui = canvas->canvas;
+       gui->flash(1);
+//printf("ReverbWindow::update_canvas %d\n", __LINE__);
 }
-int ReverbLevelInit::handle_event()
+
+
+ReverbSize::ReverbSize(ReverbWindow *window, Reverb *plugin, int x, int y)
+ : BC_PopupMenu(x, y, xS(100), "4096", 1)
 {
-//printf("ReverbLevelInit::handle_event 1 %p\n", reverb);
-       reverb->config.level_init = get_value();
-//printf("ReverbLevelInit::handle_event 1\n");
-       reverb->send_configure_change();
-//printf("ReverbLevelInit::handle_event 2\n");
-       return 1;
+       this->plugin = plugin;
+       this->window = window;
 }
 
-ReverbDelayInit::ReverbDelayInit(Reverb *reverb, int x, int y)
- : BC_IPot(x,
-       y,
-       reverb->config.delay_init,
-       0,
-       MAX_DELAY_INIT)
+int ReverbSize::handle_event()
 {
-       this->reverb = reverb;
+       plugin->config.window_size = atoi(get_text());
+       plugin->send_configure_change();
+
+       window->update_canvas();
+       return 1;
 }
-ReverbDelayInit::~ReverbDelayInit()
+
+void ReverbSize::create_objects()
 {
+       add_item(new BC_MenuItem("2048"));
+       add_item(new BC_MenuItem("4096"));
+       add_item(new BC_MenuItem("8192"));
+       add_item(new BC_MenuItem("16384"));
+       add_item(new BC_MenuItem("32768"));
+       add_item(new BC_MenuItem("65536"));
+       add_item(new BC_MenuItem("131072"));
+       add_item(new BC_MenuItem("262144"));
 }
-int ReverbDelayInit::handle_event()
+
+void ReverbSize::update(int size)
 {
-       reverb->config.delay_init = get_value();
-       reverb->send_configure_change();
-       return 1;
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", size);
+       set_text(string);
 }
 
-ReverbRefLevel1::ReverbRefLevel1(Reverb *reverb, int x, int y)
- : BC_FPot(x,
-       y,
-       reverb->config.ref_level1,
-       INFINITYGAIN,
-       0)
+
+ReverbParam::ReverbParam(Reverb *reverb, ReverbWindow *gui,
+       int x, int x2, int x3, int y, int text_w,
+       int *output_i,
+       float *output_f, // floating point output
+       int *output_q,
+       const char *title, float min, float max)
 {
+       this->output_i = output_i;
+       this->output_f = output_f;
+       this->output_q = output_q;
+       this->title = cstrdup(title);
        this->reverb = reverb;
+       this->gui = gui;
+       this->x = x;
+       this->x2 = x2;
+       this->x3 = x3;
+       this->y = y;
+       this->text_w = text_w;
+       this->min = min;
+       this->max = max;
+       fpot = 0;
+       ipot = 0;
+       qpot = 0;
+       text = 0;
 }
-ReverbRefLevel1::~ReverbRefLevel1() {}
-int ReverbRefLevel1::handle_event()
+
+ReverbParam::~ReverbParam()
 {
-       reverb->config.ref_level1 = get_value();
-       reverb->send_configure_change();
-       return 1;
+       delete fpot;
+       delete ipot;
+       delete qpot;
+       delete text;
+       delete [] title;
 }
 
-
-ReverbRefLevel2::ReverbRefLevel2(Reverb *reverb, int x, int y)
- : BC_FPot(x,
-       y,
-       reverb->config.ref_level2,
-       INFINITYGAIN,
-       0)
+void ReverbParam::initialize()
 {
-       this->reverb = reverb;
+       BC_Title *title_;
+       int y2 = y + (BC_Pot::calculate_h() -
+               BC_Title::calculate_h(gui, _(title), MEDIUMFONT)) / 2;
+       gui->add_tool(title_ = new BC_Title(x, y2, _(title)));
+
+       if( output_f ) gui->add_tool(fpot = new ReverbFPot(this, x2, y));
+       if( output_i ) gui->add_tool(ipot = new ReverbIPot(this, x2, y));
+       if( output_q ) gui->add_tool(qpot = new ReverbQPot(this, x2, y));
+
+       int y3 = y + (BC_Pot::calculate_h() -
+               BC_TextBox::calculate_h(gui, MEDIUMFONT, 1, 1)) / 2;
+       if( output_i )
+               gui->add_tool(text = new ReverbText(this, x3, y3, text_w, *output_i));
+       if( output_f )
+               gui->add_tool(text = new ReverbText(this, x3, y3, text_w, *output_f));
+       if( output_q )
+               gui->add_tool(text = new ReverbText(this, x3, y3, text_w, *output_q));
 }
-ReverbRefLevel2::~ReverbRefLevel2() {}
-int ReverbRefLevel2::handle_event()
+
+void ReverbParam::update(int skip_text, int skip_pot)
 {
-       reverb->config.ref_level2 = get_value();
-       reverb->send_configure_change();
-       return 1;
+       if( !skip_text ) {
+               if( output_i ) text->update((int64_t)*output_i);
+               if( output_q ) text->update((int64_t)*output_q);
+               if( output_f ) text->update((float)*output_f);
+       }
+
+       if( !skip_pot ) {
+               if( ipot ) ipot->update((int64_t)*output_i);
+               if( qpot ) ipot->update((int64_t)*output_q);
+               if( fpot ) ipot->update((float)*output_f);
+       }
 }
 
-ReverbRefTotal::ReverbRefTotal(Reverb *reverb, int x, int y)
- : BC_IPot(x,
-       y,
-       reverb->config.ref_total,
-       MIN_REFLECTIONS,
-       MAX_REFLECTIONS)
+
+ReverbFPot::ReverbFPot(ReverbParam *param, int x, int y)
+ : BC_FPot(x, y, *param->output_f, param->min, param->max)
 {
-       this->reverb = reverb;
+       this->param = param;
+       set_use_caption(0);
 }
-ReverbRefTotal::~ReverbRefTotal() {}
-int ReverbRefTotal::handle_event()
+
+int ReverbFPot::handle_event()
 {
-       reverb->config.ref_total = get_value();
-       reverb->send_configure_change();
+       *param->output_f = get_value();
+       param->update(0, 1);
+       param->reverb->send_configure_change();
+       param->gui->update_canvas();
        return 1;
 }
 
 
-ReverbRefLength::ReverbRefLength(Reverb *reverb, int x, int y)
- : BC_IPot(x,
-       y,
-       reverb->config.ref_length,
-       0,
-       MAX_REFLENGTH)
+ReverbIPot::ReverbIPot(ReverbParam *param, int x, int y)
+ : BC_IPot(x, y, *param->output_i, (int)param->min, (int)param->max)
 {
-       this->reverb = reverb;
+       this->param = param;
+       set_use_caption(0);
 }
-ReverbRefLength::~ReverbRefLength() {}
-int ReverbRefLength::handle_event()
+
+int ReverbIPot::handle_event()
 {
-       reverb->config.ref_length = get_value();
-       reverb->send_configure_change();
+       *param->output_i = get_value();
+       param->update(0, 1);
+       param->reverb->send_configure_change();
+       param->gui->update_canvas();
        return 1;
 }
 
-ReverbLowPass1::ReverbLowPass1(Reverb *reverb, int x, int y)
- : BC_QPot(x,
-       y,
-       reverb->config.lowpass1)
+
+ReverbQPot::ReverbQPot(ReverbParam *param, int x, int y)
+ : BC_QPot(x, y, *param->output_q)
 {
-       this->reverb = reverb;
+       this->param = param;
+       set_use_caption(0);
 }
-ReverbLowPass1::~ReverbLowPass1() {}
-int ReverbLowPass1::handle_event()
+
+int ReverbQPot::handle_event()
 {
-       reverb->config.lowpass1 = get_value();
-       reverb->send_configure_change();
+       *param->output_q = get_value();
+       param->update(0, 1);
+       param->reverb->send_configure_change();
+       param->gui->update_canvas();
        return 1;
 }
 
-ReverbLowPass2::ReverbLowPass2(Reverb *reverb, int x, int y)
- : BC_QPot(x,
-       y,
-       reverb->config.lowpass2)
+
+ReverbText::ReverbText(ReverbParam *param, int x, int y, int text_w, int value)
+ : BC_TextBox(x, y, text_w, 1, (int64_t)value, 1, MEDIUMFONT)
 {
-       this->reverb = reverb;
+       this->param = param;
 }
-ReverbLowPass2::~ReverbLowPass2() {}
-int ReverbLowPass2::handle_event()
+
+ReverbText::ReverbText(ReverbParam *param, int x, int y, int text_w, float value)
+ : BC_TextBox(x, y, text_w, 1, (float)value, 1, MEDIUMFONT, 2)
 {
-       reverb->config.lowpass2 = get_value();
-       reverb->send_configure_change();
-       return 1;
+       this->param = param;
 }
 
-// ReverbMenu::ReverbMenu(Reverb *reverb, ReverbWindow *window)
-//  : BC_MenuBar(0, 0, window->get_w())
-// {
-//     this->window = window;
-//     this->reverb = reverb;
-// }
-// 
-// ReverbMenu::~ReverbMenu()
-// {
-//     delete load;
-//     delete save;
-//     //delete set_default;
-//     for(int i = 0; i < total_loads; i++)
-//     {
-//             delete prev_load[i];
-//     }
-//     delete prev_load_thread;
-// }
-// 
-// void ReverbMenu::create_objects(BC_Hash *defaults)
-// {
-//     add_menu(filemenu = new BC_Menu(_("File")));
-//     filemenu->add_item(load = new ReverbLoad(reverb, this));
-//     filemenu->add_item(save = new ReverbSave(reverb, this));
-//     //filemenu->add_item(set_default = new ReverbSetDefault);
-//     load_defaults(defaults);
-//     prev_load_thread = new ReverbLoadPrevThread(reverb, this);
-// }
-// 
-// int ReverbMenu::load_defaults(BC_Hash *defaults)
-// {
-//     FileSystem fs;
-//     total_loads = defaults->get("TOTAL_LOADS", 0);
-//     if(total_loads > 0)
-//     {
-//             filemenu->add_item(new BC_MenuItem("-"));
-//             char string[1024], path[1024], filename[1024];
-//     
-//             for(int i = 0; i < total_loads; i++)
-//             {
-//                     sprintf(string, "LOADPREVIOUS%d", i);
-//                     defaults->get(string, path);
-//                     fs.extract_name(filename, path);
-// //printf("ReverbMenu::load_defaults %s\n", path);
-//                     filemenu->add_item(prev_load[i] = new ReverbLoadPrev(reverb, this, filename, path));
-//             }
-//     }
-//     return 0;
-// }
-// 
-// int ReverbMenu::save_defaults(BC_Hash *defaults)
-// {
-//     if(total_loads > 0)
-//     {
-//             defaults->update("TOTAL_LOADS",  total_loads);
-//             char string[1024];
-//             
-//             for(int i = 0; i < total_loads; i++)
-//             {
-//                     sprintf(string, "LOADPREVIOUS%d", i);
-//                     defaults->update(string, prev_load[i]->path);
-//             }
-//     }
-//     return 0;
-// }
-// 
-// int ReverbMenu::add_load(char *path)
-// {
-//     if(total_loads == 0)
-//     {
-//             filemenu->add_item(new BC_MenuItem("-"));
-//     }
-//     
-// // test for existing copy
-//     FileSystem fs;
-//     char text[1024], new_path[1024];      // get text and path
-//     fs.extract_name(text, path);
-//     strcpy(new_path, path);
-//     
-//     for(int i = 0; i < total_loads; i++)
-//     {
-//             if(!strcmp(prev_load[i]->get_text(), text))     // already exists
-//             {                                // swap for top load
-//                     for(int j = i; j > 0; j--)   // move preceeding loads down
-//                     {
-//                             prev_load[j]->set_text(prev_load[j - 1]->get_text());
-//                             prev_load[j]->set_path(prev_load[j - 1]->path);
-//                     }
-//                     prev_load[0]->set_text(text);
-//                     prev_load[0]->set_path(new_path);
-//                     return 1;
-//             }
-//     }
-//     
-// // add another load
-//     if(total_loads < TOTAL_LOADS)
-//     {
-//             filemenu->add_item(prev_load[total_loads] = new ReverbLoadPrev(reverb, this));
-//             total_loads++;
-//     }
-//     
-// // cycle loads down
-//     for(int i = total_loads - 1; i > 0; i--)
-//     {         
-//     // set menu item text
-//             prev_load[i]->set_text(prev_load[i - 1]->get_text());
-//     // set filename
-//             prev_load[i]->set_path(prev_load[i - 1]->path);
-//     }
-// 
-// // set up the new load
-//     prev_load[0]->set_text(text);
-//     prev_load[0]->set_path(new_path);
-//     return 0;
-// }
-// 
-// ReverbLoad::ReverbLoad(Reverb *reverb, ReverbMenu *menu)
-//  : BC_MenuItem(_("Load..."))
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-//     thread = new ReverbLoadThread(reverb, menu);
-// }
-// ReverbLoad::~ReverbLoad()
-// {
-//     delete thread;
-// }
-// int ReverbLoad::handle_event()
-// {
-//     thread->start();
-//     return 0;
-// }
-// 
-// ReverbSave::ReverbSave(Reverb *reverb, ReverbMenu *menu)
-//  : BC_MenuItem(_("Save..."))
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-//     thread = new ReverbSaveThread(reverb, menu);
-// }
-// ReverbSave::~ReverbSave()
-// {
-//     delete thread;
-// }
-// int ReverbSave::handle_event()
-// {
-//     thread->start();
-//     return 0;
-// }
-// 
-// ReverbSetDefault::ReverbSetDefault()
-//  : BC_MenuItem(_("Set default"))
-// {
-// }
-// int ReverbSetDefault::handle_event()
-// {
-//     return 0;
-// }
-// 
-// ReverbLoadPrev::ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu, char *filename, char *path)
-//  : BC_MenuItem(filename)
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-//     strcpy(this->path, path);
-// }
-// ReverbLoadPrev::ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu)
-//  : BC_MenuItem("")
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-// }
-// int ReverbLoadPrev::handle_event()
-// {
-//     menu->prev_load_thread->set_path(path);
-//     menu->prev_load_thread->start();
-// }
-// int ReverbLoadPrev::set_path(char *path)
-// {
-//     strcpy(this->path, path);
-// }
-// 
-// 
-// ReverbSaveThread::ReverbSaveThread(Reverb *reverb, ReverbMenu *menu)
-//  : Thread()
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-// }
-// ReverbSaveThread::~ReverbSaveThread()
-// {
-// }
-// void ReverbSaveThread::run()
-// {
-//     int result = 0;
-//     {
-//             ReverbSaveDialog dialog(reverb);
-//             dialog.create_objects();
-//             result = dialog.run_window();
-// //          if(!result) strcpy(reverb->config_directory, dialog.get_path());
-//     }
-//     if(!result) 
-//     {
-//             result = reverb->save_to_file(reverb->config_directory);
-//             menu->add_load(reverb->config_directory);
-//     }
-// }
-// 
-// ReverbSaveDialog::ReverbSaveDialog(Reverb *reverb)
-//  : BC_FileBox(0,
-//                     0, 
-//                     reverb->config_directory, 
-//                     _("Save reverb"), 
-//                     _("Select the reverb file to save as"), 0, 0)
-// {
-//     this->reverb = reverb;
-// }
-// ReverbSaveDialog::~ReverbSaveDialog()
-// {
-// }
-// int ReverbSaveDialog::ok_event()
-// {
-//     set_done(0);
-//     return 0;
-// }
-// int ReverbSaveDialog::cancel_event()
-// {
-//     set_done(1);
-//     return 0;
-// }
-// 
-// 
-// 
-// ReverbLoadThread::ReverbLoadThread(Reverb *reverb, ReverbMenu *menu)
-//  : Thread()
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-// }
-// ReverbLoadThread::~ReverbLoadThread()
-// {
-// }
-// void ReverbLoadThread::run()
-// {
-//     int result = 0;
-//     {
-//             ReverbLoadDialog dialog(reverb);
-//             dialog.create_objects();
-//             result = dialog.run_window();
-// //          if(!result) strcpy(reverb->config_directory, dialog.get_path());
-//     }
-//     if(!result) 
-//     {
-//             result = reverb->load_from_file(reverb->config_directory);
-//             if(!result)
-//             {
-//                     menu->add_load(reverb->config_directory);
-//                     reverb->send_configure_change();
-//             }
-//     }
-// }
-// 
-// ReverbLoadPrevThread::ReverbLoadPrevThread(Reverb *reverb, ReverbMenu *menu) : Thread()
-// {
-//     this->reverb = reverb;
-//     this->menu = menu;
-// }
-// ReverbLoadPrevThread::~ReverbLoadPrevThread()
-// {
-// }
-// void ReverbLoadPrevThread::run()
-// {
-//     int result = 0;
-//     strcpy(reverb->config_directory, path);
-//     result = reverb->load_from_file(path);
-//     if(!result)
-//     {
-//             menu->add_load(path);
-//             reverb->send_configure_change();
-//     }
-// }
-// int ReverbLoadPrevThread::set_path(char *path)
-// {
-//     strcpy(this->path, path);
-//     return 0;
-// }
-// 
-// 
-// 
-// 
-// 
-// ReverbLoadDialog::ReverbLoadDialog(Reverb *reverb)
-//  : BC_FileBox(0,
-//                     0, 
-//                     reverb->config_directory, 
-//                     _("Load reverb"), 
-//                     _("Select the reverb file to load from"), 0, 0)
-// {
-//     this->reverb = reverb;
-// }
-// ReverbLoadDialog::~ReverbLoadDialog()
-// {
-// }
-// int ReverbLoadDialog::ok_event()
-// {
-//     set_done(0);
-//     return 0;
-// }
-// int ReverbLoadDialog::cancel_event()
-// {
-//     set_done(1);
-//     return 0;
-// }
+int ReverbText::handle_event()
+{
+       if( param->output_i ) *param->output_i = atoi(get_text());
+       if( param->output_f ) *param->output_f = atof(get_text());
+       if( param->output_q ) *param->output_q = atoi(get_text());
+
+       param->update(1, 0);
+       param->reverb->send_configure_change();
+       param->gui->update_canvas();
+       return 1;
+}
 
index cc7eb265b4d2b8b756adbb9a0b0158db111852be..b4e3968a455abb940a18441ce15748c4575bd3f6 100644 (file)
@@ -1,7 +1,7 @@
 
 /*
  * 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
  * 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 REVERBWINDOW_H
 #define REVERBWINDOW_H
 
-#define TOTAL_LOADS 5
+#define TOTAL_PARAMS 9
 
-class ReverbThread;
 class ReverbWindow;
+class ReverbParam;
 
+#include "eqcanvas.inc"
 #include "guicast.h"
 #include "mutex.h"
 #include "pluginclient.h"
 #include "reverb.inc"
 
 
-
 class ReverbLevelInit;
 class ReverbDelayInit;
 class ReverbRefLevel1;
 class ReverbRefLevel2;
 class ReverbRefTotal;
 class ReverbRefLength;
-class ReverbLowPass1;
-class ReverbLowPass2;
-class ReverbMenu;
+class ReverbHigh;
+class ReverbLow;
+class ReverbQ;
+class ReverbSize;
 
 class ReverbWindow : public PluginClientWindow
 {
@@ -51,225 +51,183 @@ public:
        ~ReverbWindow();
 
        void create_objects();
+       void update();
+       void update_canvas();
 
        Reverb *reverb;
-       ReverbLevelInit *level_init;
-       ReverbDelayInit *delay_init;
-       ReverbRefLevel1 *ref_level1;
-       ReverbRefLevel2 *ref_level2;
-       ReverbRefTotal *ref_total;
-       ReverbRefLength *ref_length;
-       ReverbLowPass1 *lowpass1;
-       ReverbLowPass2 *lowpass2;
-       ReverbMenu *menu;
-};
 
-class ReverbLevelInit : public BC_FPot
-{
-public:
-       ReverbLevelInit(Reverb *reverb, int x, int y);
-       ~ReverbLevelInit();
-       int handle_event();
-       Reverb *reverb;
-};
+       ReverbParam *params[TOTAL_PARAMS];
 
-class ReverbDelayInit : public BC_IPot
-{
-public:
-       ReverbDelayInit(Reverb *reverb, int x, int y);
-       ~ReverbDelayInit();
-       int handle_event();
-       Reverb *reverb;
+       ReverbParam *level_init;
+       ReverbParam *delay_init;
+       ReverbParam *ref_level1;
+       ReverbParam *ref_level2;
+       ReverbParam *ref_total;
+       ReverbParam *ref_length;
+       ReverbParam *high;
+       ReverbParam *low;
+       ReverbParam *q;
+       EQCanvas *canvas;
+       ReverbSize *size;
 };
 
-class ReverbRefLevel1 : public BC_FPot
+class ReverbSize : public BC_PopupMenu
 {
 public:
-       ReverbRefLevel1(Reverb *reverb, int x, int y);
-       ~ReverbRefLevel1();
+       ReverbSize(ReverbWindow *window, Reverb *plugin, int x, int y);
+
        int handle_event();
-       Reverb *reverb;
+       void create_objects();           // add initial items
+       void update(int size);
+
+       ReverbWindow *window;
+       Reverb *plugin;
 };
 
-class ReverbRefLevel2 : public BC_FPot
+
+class ReverbFPot : public BC_FPot
 {
 public:
-       ReverbRefLevel2(Reverb *reverb, int x, int y);
-       ~ReverbRefLevel2();
+       ReverbFPot(ReverbParam *param, int x, int y);
        int handle_event();
-       Reverb *reverb;
+       ReverbParam *param;
 };
 
-class ReverbRefTotal : public BC_IPot
+class ReverbIPot : public BC_IPot
 {
 public:
-       ReverbRefTotal(Reverb *reverb, int x, int y);
-       ~ReverbRefTotal();
+       ReverbIPot(ReverbParam *param, int x, int y);
        int handle_event();
-       Reverb *reverb;
+       ReverbParam *param;
 };
 
-class ReverbRefLength : public BC_IPot
+class ReverbQPot : public BC_QPot
 {
 public:
-       ReverbRefLength(Reverb *reverb, int x, int y);
-       ~ReverbRefLength();
+       ReverbQPot(ReverbParam *param, int x, int y);
        int handle_event();
-       Reverb *reverb;
+       ReverbParam *param;
 };
 
-class ReverbLowPass1 : public BC_QPot
+class ReverbText : public BC_TextBox
 {
 public:
-       ReverbLowPass1(Reverb *reverb, int x, int y);
-       ~ReverbLowPass1();
+       ReverbText(ReverbParam *param, int x, int y, int w, int value);
+       ReverbText(ReverbParam *param, int x, int y, int w, float value);
        int handle_event();
-       Reverb *reverb;
+       ReverbParam *param;
 };
 
-class ReverbLowPass2 : public BC_QPot
+class ReverbParam
 {
 public:
-       ReverbLowPass2(Reverb *reverb, int x, int y);
-       ~ReverbLowPass2();
-       int handle_event();
-       Reverb *reverb;
-};
+       ReverbParam(Reverb *reverb, ReverbWindow *gui,
+               int x, 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);
+       ~ReverbParam();
 
+       void initialize();
+       void update(int skip_text, int skip_pot);
 
-class ReverbLoad;
-class ReverbSave;
-class ReverbSetDefault;
-class ReverbLoadPrev;
-class ReverbLoadPrevThread;
+       float *output_f;
+       ReverbFPot *fpot;
+       int *output_i;
+       ReverbIPot *ipot;
+       int *output_q;
+       ReverbQPot *qpot;
 
-class ReverbMenu : public BC_MenuBar
-{
-public:
-       ReverbMenu(Reverb *reverb, ReverbWindow *window);
-       ~ReverbMenu();
-
-       void create_objects(BC_Hash *defaults);
-       int load_defaults(BC_Hash *defaults);
-       int save_defaults(BC_Hash *defaults);
-// most recent loads
-       int add_load(char *path);
-       ReverbLoadPrevThread *prev_load_thread;
-
-       int total_loads;
-       BC_Menu *filemenu;
-       ReverbWindow *window;
+       char *title;
+       ReverbText *text;
+       ReverbWindow *gui;
        Reverb *reverb;
-       ReverbLoad *load;
-       ReverbSave *save;
-       ReverbSetDefault *set_default;
-       ReverbLoadPrev *prev_load[TOTAL_LOADS];
+       int x, y;
+       int x2, x3, text_w;
+       float min, max;
 };
 
-class ReverbSaveThread;
-class ReverbLoadThread;
 
-class ReverbLoad : public BC_MenuItem
+class ReverbLevelInit : public BC_FPot
 {
 public:
-       ReverbLoad(Reverb *reverb, ReverbMenu *menu);
-       ~ReverbLoad();
+       ReverbLevelInit(Reverb *reverb, int x, int y);
+       ~ReverbLevelInit();
        int handle_event();
        Reverb *reverb;
-       ReverbLoadThread *thread;
-       ReverbMenu *menu;
 };
 
-class ReverbSave : public BC_MenuItem
+class ReverbDelayInit : public BC_IPot
 {
 public:
-       ReverbSave(Reverb *reverb, ReverbMenu *menu);
-       ~ReverbSave();
+       ReverbDelayInit(Reverb *reverb, int x, int y);
+       ~ReverbDelayInit();
        int handle_event();
        Reverb *reverb;
-       ReverbSaveThread *thread;
-       ReverbMenu *menu;
 };
 
-class ReverbSetDefault : public BC_MenuItem
+class ReverbRefLevel1 : public BC_FPot
 {
 public:
-       ReverbSetDefault();
+       ReverbRefLevel1(Reverb *reverb, int x, int y);
+       ~ReverbRefLevel1();
        int handle_event();
+       Reverb *reverb;
 };
 
-class ReverbLoadPrev : public BC_MenuItem
+class ReverbRefLevel2 : public BC_FPot
 {
 public:
-       ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu, char *filename, char *path);
-       ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu);
+       ReverbRefLevel2(Reverb *reverb, int x, int y);
+       ~ReverbRefLevel2();
        int handle_event();
-       int set_path(char *path);
-       char path[1024];
        Reverb *reverb;
-       ReverbMenu *menu;
 };
 
-
-class ReverbLoadPrevThread : public Thread
+class ReverbRefTotal : public BC_IPot
 {
 public:
-       ReverbLoadPrevThread(Reverb *reverb, ReverbMenu *menu);
-       ~ReverbLoadPrevThread();
-       void run();
-       int set_path(char *path);
-       char path[1024];
+       ReverbRefTotal(Reverb *reverb, int x, int y);
+       ~ReverbRefTotal();
+       int handle_event();
        Reverb *reverb;
-       ReverbMenu *menu;
 };
 
-
-
-class ReverbSaveThread : public Thread
+class ReverbRefLength : public BC_IPot
 {
 public:
-       ReverbSaveThread(Reverb *reverb, ReverbMenu *menu);
-       ~ReverbSaveThread();
-       void run();
+       ReverbRefLength(Reverb *reverb, int x, int y);
+       ~ReverbRefLength();
+       int handle_event();
        Reverb *reverb;
-       ReverbMenu *menu;
 };
 
-class ReverbSaveDialog : public BC_FileBox
+class ReverbHigh : public BC_QPot
 {
 public:
-       ReverbSaveDialog(Reverb *reverb);
-       ~ReverbSaveDialog();
-
-       int ok_event();
-       int cancel_event();
+       ReverbHigh(Reverb *reverb, int x, int y);
+       ~ReverbHigh();
+       int handle_event();
        Reverb *reverb;
 };
 
-
-class ReverbLoadThread : public Thread
+class ReverbLow : public BC_QPot
 {
 public:
-       ReverbLoadThread(Reverb *reverb, ReverbMenu *menu);
-       ~ReverbLoadThread();
-       void run();
+       ReverbLow(Reverb *reverb, int x, int y);
+       ~ReverbLow();
+       int handle_event();
        Reverb *reverb;
-       ReverbMenu *menu;
 };
 
-class ReverbLoadDialog : public BC_FileBox
+class ReverbQ: public BC_QPot
 {
 public:
-       ReverbLoadDialog(Reverb *reverb);
-       ~ReverbLoadDialog();
-
-       int ok_event();
-       int cancel_event();
+       ReverbQ(Reverb *reverb, int x, int y);
+       ~ReverbQ();
+       int handle_event();
        Reverb *reverb;
 };
 
-
-
-
-
 #endif
index a6e14921357d8c35569b92aba4d37e2a16d1a120..512486df15a2a0158a18386d69b3676c724ae385 100644 (file)
  * 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 REVERBWINDOW_INC
 #define REVERBWINDOW_INC
 
-
 class ReverbThread;
 class ReverbWindow;