<?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>
$(OBJDIR)/clippopup.o \
$(OBJDIR)/colorpicker.o \
$(OBJDIR)/commonrender.o \
+ $(OBJDIR)/compressortools.o \
$(OBJDIR)/confirmquit.o \
$(OBJDIR)/confirmsave.o \
$(OBJDIR)/convert.o \
$(OBJDIR)/edl.o \
$(OBJDIR)/edlsession.o \
$(OBJDIR)/effectlist.o \
+ $(OBJDIR)/eqcanvas.o \
$(OBJDIR)/exportedl.o \
$(OBJDIR)/fadeengine.o \
$(OBJDIR)/ffmpeg.o \
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;
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;
renderengine->mwindow->render_plugin_gui(data, size, plugin);
}
+
int AttachmentPoint::gui_open()
{
if(renderengine && renderengine->mwindow)
#include "mwindow.inc"
#include "messages.inc"
#include "plugin.inc"
+#include "pluginclient.inc"
#include "pluginserver.inc"
#include "renderengine.inc"
#include "sharedlocation.h"
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();
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
+
+
+
--- /dev/null
+#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
{
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();
--- /dev/null
+#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);
+ }
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+#ifndef EQCANVAS_INC
+#define EQCANVAS_INC
+
+
+
+class EQCanvas;
+
+
+
+#endif
+
+
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;
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;
}
}
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;
}
}
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;
}
}
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;
}
}
{
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;
}
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];
}
CrossfadeFFT::CrossfadeFFT() : FFT()
{
+ bands = 1;
reset();
window_size = 4096;
}
output_buffer = 0;
freq_real = 0;
freq_imag = 0;
+ freq_real2 = 0;
+ freq_imag2 = 0;
output_real = 0;
output_imag = 0;
first_window = 1;
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;
}
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;
{
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;
int CrossfadeFFT::read_samples(int64_t output_sample,
- int samples,
- Samples *buffer)
+ int samples, Samples *buffer)
{
return 1;
}
}
+int CrossfadeFFT::signal_process(int band)
+{
+ signal_process();
+ return 0;
+}
-
+int CrossfadeFFT::post_process(int band)
+{
+ post_process();
+ return 0;
+}
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);
}
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();
// 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;
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;
int64_t input_sample;
// Don't crossfade the first window
int first_window;
+ int bands;
};
#endif
{
public:
MainTransport(MWindow *mwindow, MButtons *mbuttons, int x, int y);
+ bool use_mixers() { return true; }
void goto_start();
void goto_end();
};
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);
{
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()
{
#include "playback3d.inc"
#include "playbackengine.inc"
#include "plugin.inc"
+#include "pluginclient.inc"
#include "pluginfclient.inc"
#include "pluginserver.inc"
#include "pluginset.inc"
// 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
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)
{
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;
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;
// 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;
*
*/
+#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>
: 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;
}
}
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()
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;
return sample_rate;
}
+Samples* PluginAClient::get_output(int channel)
+{
+ return output_buffers[channel];
+}
+
int64_t PluginAClient::get_startproject()
{
// 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.
// 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();
// 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;
#include "bcdisplayinfo.h"
#include "bchash.h"
#include "bcsignals.h"
+#include "attachmentpoint.h"
#include "clip.h"
#include "condition.h"
#include "edits.h"
#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)
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;
}
}
-
-
-
-
-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,
}
-
-
-
PluginClient::PluginClient(PluginServer *server)
{
reset();
// Virtual functions don't work here.
if(defaults) delete defaults;
- frame_buffer.remove_all_objects();
delete update_timer;
}
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; }
-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()
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; }
return direction;
}
-
int64_t PluginClient::local_to_edl(int64_t position)
{
return position;
#include "arraylist.h"
+#include "linklist.h"
#include "bchash.inc"
#include "condition.h"
#include "edlsession.inc"
}
+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();
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:
-// 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:
// 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.
// 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.
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
// 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);
// 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();
float get_green();
float get_blue();
-
-
// Operations for file handlers
virtual int open_file() { return 0; };
virtual int get_audio_parameters() { return 0; };
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);
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.
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 ===============================
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
PluginClientThread *thread;
// Frames for updating GUI
- ArrayList<PluginClientFrame*> frame_buffer;
+ PluginClientFrames frame_buffer;
// Time of last GUI update
Timer *update_timer;
// 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
};
#define PLUGINCLIENT_INC
class PluginClientAuto;
+class PluginClientFrames;
+class PluginClientFrame;
class PluginClientWindow;
class PluginFPot;
class PluginIPot;
class PluginText;
class PluginParam;
class PluginClientThread;
-class PluginClientFrame;
class PluginClient;
#endif
{
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);
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);
EDL *edl,
Plugin *plugin)
{
- if(plugin_open) return 0;
+ if( plugin_open ) return 0;
this->preferences = preferences;
this->plugin = plugin;
// 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();
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;
}
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();
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);
int buffer_size)
{
- if(!plugin_open) return 0;
+ if( !plugin_open ) return 0;
// set for realtime priority
// initialize plugin
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;
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;
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;
}
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;
}
-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)
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;
int PluginServer::set_interactive()
{
- if(!plugin_open) return 0;
+ if( !plugin_open ) return 0;
client->set_interactive();
return 0;
}
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);
}
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();
}
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.
//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);
}
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
// Called by client
int PluginServer::get_gui_status()
{
- if(plugin)
+ if( plugin )
return plugin->show ? GUI_ON : GUI_OFF;
else
return GUI_OFF;
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);
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);
void PluginServer::update_title()
{
- if(!plugin_open) return;
+ if( !plugin_open ) return;
client->update_display_title();
}
int PluginServer::set_string(char *string)
{
- if(!plugin_open) return 0;
+ if( !plugin_open ) return 0;
client->set_string_client(string);
return 0;
int PluginServer::gui_open()
{
- if(attachmentpoint) return attachmentpoint->gui_open();
+ if( attachmentpoint ) return attachmentpoint->gui_open();
return 0;
}
void PluginServer::run_opengl(PluginClient *plugin_client)
{
- if(vdevice)
+ if( vdevice )
((VDeviceX11*)vdevice->get_output_base())->run_plugin(plugin_client);
}
// 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;
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();
}
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;
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;
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)
{
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;
}
// 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();
// 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);
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__,
{
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;
#include "bctimer.inc"
#include "edit.inc"
+#include "track.inc"
#include "guicast.h"
#include "mwindow.inc"
#include "trackcanvas.inc"
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,
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);
#include "awindowgui.inc"
#include "batchrender.inc"
#include "bctheme.h"
+#include "compressortools.inc"
#include "cwindowgui.inc"
#include "guicast.h"
#include "keyframegui.inc"
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;
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 )
* 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"
#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;
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();
}
{
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()
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
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());
return 0;
}
-int BC_Meter::reset(int dmix)
+int BC_Meter::reset(int downmix)
{
level = min;
peak = min;
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;
}
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;
}
db_titles.remove_all_objects();
title_pixels.remove_all();
tick_pixels.remove_all();
+ tick_w.remove_all();
low_division = 0;
medium_division = 0;
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);
}
}
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;
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;
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;
}
-
/*
* 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 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();
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);
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
BC_Pot::~BC_Pot()
{
+ for(int i = 0; i < POT_STATES; i++)
+ if(images[i]) delete images[i];
}
int BC_Pot::calculate_h()
}
if(!hidden) show_window();
-
+ init_glyphs();
}
draw_background(0, 0, this->w, this->h);
#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;
int allocate_color_table();
int init_gc();
int init_fonts();
+ void init_glyphs();
void init_xft();
void init_im();
void finit_im();
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);
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
}
}
+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
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)
{
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;
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()
{
#define INFINITYGAIN -96
#define MAXGAIN 50
#define TOTALFREQS 1024
+// slots per octave
+#define OCTAVE 105
#define TOTAL_TIMEFORMATS 7
// h:mm:ss.sss
// 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--();
color3way \
colorbalance \
compressor \
+ compressormulti \
crikey \
crop \
crossfade \
include ../../plugin_config
-
$(OBJDIR)/compressor.o: compressor.C
-
/*
* 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.
// 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.
// 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();
}
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;
}
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)
}
-
-
-
-
-
-
-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()
{
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;
}
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"))
{
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"))
{
return 1;
}
-
-
-
/*
* 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 "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;
};
{
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:
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;
};
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
+// }
+
+
--- /dev/null
+/*
+ * 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
+
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++;
}
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++)
{
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;
}
}
{
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();
}
-
-
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()
}
-
-
-
-
GraphicFFT::GraphicFFT(GraphicEQ *plugin)
: CrossfadeFFT()
{
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
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,
}
-
-
-
-
-
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;
}
}
-
-
-
-
-
-
-
-
ParametricFFT::ParametricFFT(ParametricEQ *plugin)
: CrossfadeFFT()
{
return 0;
}
-
-
-
-
-
-
-
-
-
-
void ParametricEQ::reset()
{
need_reconfigure = 1;
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();
}
-
-
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
-
/*
* 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
* 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"
#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"); }
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;
+ }
+ }
}
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);
}
}
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()
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)
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)
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;
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);
}
-
/*
* 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 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
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
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;
};
* 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
/*
* 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;
+}
/*
* 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
{
~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
* 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;