From 0e6cf5b52d1ebce9272270144bcf43df4683507e Mon Sep 17 00:00:00 2001 From: Good Guy Date: Wed, 11 Dec 2019 09:17:30 -0700 Subject: [PATCH] new/reworked audio plugins ported from hv72 compressor/multi/reverb, glyph workaround, centerline on audio, pro->mov extension --- cinelerra-5.1/Cinelerra_factory | 32 +- cinelerra-5.1/cinelerra/Makefile | 2 + cinelerra-5.1/cinelerra/attachmentpoint.C | 24 +- cinelerra-5.1/cinelerra/attachmentpoint.h | 7 +- cinelerra-5.1/cinelerra/compressortools.C | 1072 ++++++++++++ cinelerra-5.1/cinelerra/compressortools.h | 329 ++++ cinelerra-5.1/cinelerra/compressortools.inc | 22 + cinelerra-5.1/cinelerra/cwindowgui.h | 5 +- cinelerra-5.1/cinelerra/eqcanvas.C | 248 +++ cinelerra-5.1/cinelerra/eqcanvas.h | 62 + cinelerra-5.1/cinelerra/eqcanvas.inc | 12 + cinelerra-5.1/cinelerra/formattools.C | 3 +- cinelerra-5.1/cinelerra/fourier.C | 417 ++--- cinelerra-5.1/cinelerra/fourier.h | 43 +- cinelerra-5.1/cinelerra/mbuttons.h | 1 + cinelerra-5.1/cinelerra/mwindow.C | 36 +- cinelerra-5.1/cinelerra/mwindow.h | 5 + cinelerra-5.1/cinelerra/playbackengine.C | 5 + cinelerra-5.1/cinelerra/playbackengine.h | 1 + cinelerra-5.1/cinelerra/playtransport.C | 2 +- cinelerra-5.1/cinelerra/playtransport.h | 2 +- cinelerra-5.1/cinelerra/pluginaclient.C | 111 +- cinelerra-5.1/cinelerra/pluginaclient.h | 36 +- cinelerra-5.1/cinelerra/pluginclient.C | 250 +-- cinelerra-5.1/cinelerra/pluginclient.h | 275 ++-- cinelerra-5.1/cinelerra/pluginclient.inc | 3 +- cinelerra-5.1/cinelerra/pluginserver.C | 399 ++--- cinelerra-5.1/cinelerra/pluginserver.h | 7 + cinelerra-5.1/cinelerra/resourcepixmap.C | 41 +- cinelerra-5.1/cinelerra/resourcepixmap.h | 2 + cinelerra-5.1/cinelerra/theme.C | 7 + cinelerra-5.1/cinelerra/theme.h | 11 + cinelerra-5.1/guicast/arraylist.h | 11 + cinelerra-5.1/guicast/bcmeter.C | 519 +++--- cinelerra-5.1/guicast/bcmeter.h | 42 +- cinelerra-5.1/guicast/bcpot.C | 2 + cinelerra-5.1/guicast/bcwindowbase.C | 26 +- cinelerra-5.1/guicast/bcwindowbase.h | 1 + cinelerra-5.1/guicast/linklist.h | 14 +- cinelerra-5.1/guicast/units.C | 36 +- cinelerra-5.1/guicast/units.h | 5 +- cinelerra-5.1/plugins/Makefile | 1 + cinelerra-5.1/plugins/compressor/Makefile | 1 - cinelerra-5.1/plugins/compressor/compressor.C | 1463 +++++------------ cinelerra-5.1/plugins/compressor/compressor.h | 155 +- .../plugins/compressormulti/Makefile | 11 + .../plugins/compressormulti/comprmulti.C | 945 +++++++++++ .../plugins/compressormulti/comprmulti.h | 163 ++ .../plugins/compressormulti/comprmultigui.C | 732 +++++++++ .../plugins/compressormulti/comprmultigui.h | 220 +++ cinelerra-5.1/plugins/denoisefft/denoisefft.C | 5 +- cinelerra-5.1/plugins/graphic/graphic.C | 64 +- cinelerra-5.1/plugins/graphic/graphic.h | 1 + cinelerra-5.1/plugins/parametric/parametric.C | 120 +- cinelerra-5.1/plugins/parametric/parametric.h | 1 + cinelerra-5.1/plugins/reverb/reverb.C | 743 ++++----- cinelerra-5.1/plugins/reverb/reverb.h | 150 +- cinelerra-5.1/plugins/reverb/reverb.inc | 2 - cinelerra-5.1/plugins/reverb/reverbwindow.C | 739 ++++----- cinelerra-5.1/plugins/reverb/reverbwindow.h | 240 ++- cinelerra-5.1/plugins/reverb/reverbwindow.inc | 2 - 61 files changed, 6342 insertions(+), 3544 deletions(-) create mode 100644 cinelerra-5.1/cinelerra/compressortools.C create mode 100644 cinelerra-5.1/cinelerra/compressortools.h create mode 100644 cinelerra-5.1/cinelerra/compressortools.inc create mode 100644 cinelerra-5.1/cinelerra/eqcanvas.C create mode 100644 cinelerra-5.1/cinelerra/eqcanvas.h create mode 100644 cinelerra-5.1/cinelerra/eqcanvas.inc create mode 100644 cinelerra-5.1/plugins/compressormulti/Makefile create mode 100644 cinelerra-5.1/plugins/compressormulti/comprmulti.C create mode 100644 cinelerra-5.1/plugins/compressormulti/comprmulti.h create mode 100644 cinelerra-5.1/plugins/compressormulti/comprmultigui.C create mode 100644 cinelerra-5.1/plugins/compressormulti/comprmultigui.h diff --git a/cinelerra-5.1/Cinelerra_factory b/cinelerra-5.1/Cinelerra_factory index 05acba1a..4a4c0e58 100644 --- a/cinelerra-5.1/Cinelerra_factory +++ b/cinelerra-5.1/Cinelerra_factory @@ -1,17 +1,29 @@ - - + + + + + + + - - + + + + + + + - - - - - - + + + + + + + + diff --git a/cinelerra-5.1/cinelerra/Makefile b/cinelerra-5.1/cinelerra/Makefile index f8c0bc11..e4f782d4 100644 --- a/cinelerra-5.1/cinelerra/Makefile +++ b/cinelerra-5.1/cinelerra/Makefile @@ -96,6 +96,7 @@ OBJS := $(OVERLAYS) \ $(OBJDIR)/clippopup.o \ $(OBJDIR)/colorpicker.o \ $(OBJDIR)/commonrender.o \ + $(OBJDIR)/compressortools.o \ $(OBJDIR)/confirmquit.o \ $(OBJDIR)/confirmsave.o \ $(OBJDIR)/convert.o \ @@ -126,6 +127,7 @@ OBJS := $(OVERLAYS) \ $(OBJDIR)/edl.o \ $(OBJDIR)/edlsession.o \ $(OBJDIR)/effectlist.o \ + $(OBJDIR)/eqcanvas.o \ $(OBJDIR)/exportedl.o \ $(OBJDIR)/fadeengine.o \ $(OBJDIR)/ffmpeg.o \ diff --git a/cinelerra-5.1/cinelerra/attachmentpoint.C b/cinelerra-5.1/cinelerra/attachmentpoint.C index 362a5e1f..adc1c4ac 100644 --- a/cinelerra-5.1/cinelerra/attachmentpoint.C +++ b/cinelerra-5.1/cinelerra/attachmentpoint.C @@ -211,13 +211,32 @@ int AttachmentPoint::singlechannel() return 0; } +void AttachmentPoint::reset_gui_frames(PluginServer *server) +{ + if( server != plugin_servers.get(0) ) return; + if( renderengine && renderengine->mwindow ) + renderengine->mwindow->reset_plugin_gui_frames(plugin); +} -void AttachmentPoint::render_gui(void *data, PluginServer *server) +void AttachmentPoint::render_gui_frames(PluginClientFrames *frames, PluginServer *server) { //printf("AttachmentPoint::render_gui 1 %p %p\n", server, plugin_servers.get(0)); void *This = this; if(!This) printf("AttachmentPoint::render_gui 1 NULL\n"); +// Discard if not 1st plugin server, so single channel plugins don't get double GUI updates + if(server != plugin_servers.get(0)) return; + + if(renderengine && renderengine->mwindow) + renderengine->mwindow->render_plugin_gui_frames(frames, plugin); +} + + +void AttachmentPoint::render_gui(void *data, PluginServer *server) +{ + void *This = this; + if(!This) printf("AttachmentPoint::render_gui 2 NULL\n"); + // Discard if not 1st plugin server, so single channel plugins don't get double GUI updates if(server != plugin_servers.get(0)) return; @@ -228,7 +247,7 @@ void AttachmentPoint::render_gui(void *data, PluginServer *server) void AttachmentPoint::render_gui(void *data, int size, PluginServer *server) { void *This = this; - if(!This) printf("AttachmentPoint::render_gui 2 NULL\n"); + if(!This) printf("AttachmentPoint::render_gui 3 NULL\n"); // Discard if not 1st plugin server, so single channel plugins don't get double GUI updates if(server != plugin_servers.get(0)) return; @@ -237,6 +256,7 @@ void AttachmentPoint::render_gui(void *data, int size, PluginServer *server) renderengine->mwindow->render_plugin_gui(data, size, plugin); } + int AttachmentPoint::gui_open() { if(renderengine && renderengine->mwindow) diff --git a/cinelerra-5.1/cinelerra/attachmentpoint.h b/cinelerra-5.1/cinelerra/attachmentpoint.h index 6a7dc0f5..ebcfdc13 100644 --- a/cinelerra-5.1/cinelerra/attachmentpoint.h +++ b/cinelerra-5.1/cinelerra/attachmentpoint.h @@ -32,6 +32,7 @@ #include "mwindow.inc" #include "messages.inc" #include "plugin.inc" +#include "pluginclient.inc" #include "pluginserver.inc" #include "renderengine.inc" #include "sharedlocation.h" @@ -67,11 +68,9 @@ public: int attach_virtual_plugin(VirtualNode *virtual_plugin); virtual void delete_buffer_vector() {}; -// return 0 if ready to render -// check all the virtual plugins for waiting status -// all virtual plugins attached to this must be waiting for a render -// int sort(VirtualNode *virtual_plugin); // Called by plugin server to render GUI with data. + void reset_gui_frames(PluginServer *server); + void render_gui_frames(PluginClientFrames *frames, PluginServer *server); void render_gui(void *data, PluginServer *server); void render_gui(void *data, int size, PluginServer *server); int gui_open(); diff --git a/cinelerra-5.1/cinelerra/compressortools.C b/cinelerra-5.1/cinelerra/compressortools.C new file mode 100644 index 00000000..fee76d3d --- /dev/null +++ b/cinelerra-5.1/cinelerra/compressortools.C @@ -0,0 +1,1072 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 + + +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; icopy_from(src); + } +} + + +int CompressorConfigBase::equivalent(CompressorConfigBase &that) +{ + for( int i=0; i &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 &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 &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 &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 &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; jdraw_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; jdraw_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 &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 &levels = config->bands[band].levels; + for( int i=0; iget_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 &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 &levels = config->bands[band].levels; + + if( current_operation == DRAG ) { + int x = get_cursor_x(); + int y = get_cursor_y(); + double x_db = x_to_db(x); + double y_db = y_to_db(y); + + if( shift_down() ) { + const int grid_precision = 6; + x_db = config->max_db + (double)(grid_precision * (int)((x_db - config->max_db) / grid_precision - 0.5)); + y_db = config->max_db + (double)(grid_precision * (int)((y_db - config->max_db) / grid_precision - 0.5)); + } + + +//printf("CompressorCanvasBase::cursor_motion_event %d x=%d y=%d x_db=%f y_db=%f\n", +//__LINE__, x, y, x_db, y_db); + levels[current_point].x = x_db; + levels[current_point].y = y_db; + update_window(); + plugin->send_configure_change(); + return 1; + } + else +// Change cursor over points + if( is_event_win() && cursor_inside() ) { + int new_cursor = CROSS_CURSOR; + + for( int i = 0; i < levels.size(); i++ ) { + double x_db = config->get_x(config->current_band, i); + double y_db = config->get_y(config->current_band, i); + + int x = db_to_x(x_db); + int y = db_to_y(y_db); + + if( get_cursor_x() <= x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 && + get_cursor_y() <= y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2 ) { + new_cursor = UPRIGHT_ARROW_CURSOR; + break; + } + } + +// out of active area + if( get_cursor_x() >= graph_x + graph_w || + get_cursor_y() < graph_y ) { + new_cursor = ARROW_CURSOR; + } + + if( new_cursor != get_cursor() ) { + set_cursor(new_cursor, 0, 1); + } + } + return 0; +} + + +void CompressorCanvasBase::update_window() +{ + printf("CompressorCanvasBase::update_window %d empty\n", __LINE__); +} + + +int CompressorCanvasBase::is_dragging() +{ + return current_operation == DRAG; +} + + +CompressorClientFrame::CompressorClientFrame() +{ + type = -1; + band = 0; +} +CompressorClientFrame::~CompressorClientFrame() +{ +} + +CompressorFreqFrame::CompressorFreqFrame() +{ + type = FREQ_COMPRESSORFRAME; + data = 0; data_size = 0; + freq_max = 0; time_max = 0; + nyquist = 0; +} +CompressorFreqFrame::~CompressorFreqFrame() +{ + delete [] data; +} + +CompressorGainFrame::CompressorGainFrame() +{ + type = GAIN_COMPRESSORFRAME; + gain = 0; + level = 0; +} +CompressorGainFrame::~CompressorGainFrame() +{ +} + +CompressorEngine::CompressorEngine(CompressorConfigBase *config, + int band) +{ + this->config = config; + this->band = band; + reset(); +} + +CompressorEngine::~CompressorEngine() +{ +} + + +void CompressorEngine::reset() +{ + slope_samples = 0; + slope_current_sample = 0; + peak_samples = 0; + slope_value2 = 1.0; + slope_value1 = 1.0; + slope_samples = 0; + slope_current_sample = 0; + current_value = 1.0; + gui_frame_samples = 2048; + gui_max_gain = 1.0; + gui_frame_counter = 0; +} + + +void CompressorEngine::calculate_ranges(int *attack_samples, + int *release_samples, + int *preview_samples, + int sample_rate) +{ + BandConfig *band_config = &config->bands[band]; + *attack_samples = labs(Units::round(band_config->attack_len * sample_rate)); + *release_samples = Units::round(band_config->release_len * sample_rate); + CLAMP(*attack_samples, 1, 1000000); + CLAMP(*release_samples, 1, 1000000); + *preview_samples = MAX(*attack_samples, *release_samples); +} + + +void CompressorEngine::process(Samples **output_buffer, + Samples **input_buffer, + int size, + int sample_rate, + int channels, + int64_t start_position) +{ + BandConfig *band_config = &config->bands[band]; + int attack_samples; + int release_samples; + int preview_samples; + int trigger = CLIP(config->trigger, 0, channels - 1); + + gui_gains.remove_all(); + gui_levels.remove_all(); + gui_offsets.remove_all(); + + calculate_ranges(&attack_samples, + &release_samples, + &preview_samples, + sample_rate); + if( slope_current_sample < 0 ) slope_current_sample = slope_samples; + + double *trigger_buffer = input_buffer[trigger]->get_data(); + + for( int i = 0; i < size; i++ ) { + double current_slope = (slope_value2 - slope_value1) / + slope_samples; + +// maximums in the 2 time ranges + double attack_slope = -0x7fffffff; + double attack_sample = -1; + int attack_offset = -1; + int have_attack_sample = 0; + double release_slope = -0x7fffffff; + double release_sample = -1; + int release_offset = -1; + int have_release_sample = 0; + if( slope_current_sample >= slope_samples ) { +// start new line segment + for( int j = 1; j < preview_samples; j++ ) { + GET_TRIGGER(input_buffer[channel]->get_data(), i + j) + double new_slope = (sample - current_value) / j; + if( j < attack_samples && new_slope >= attack_slope ) { + attack_slope = new_slope; + attack_sample = sample; + attack_offset = j; + have_attack_sample = 1; + } + + if( j < release_samples && + new_slope <= 0 && + new_slope > release_slope ) { + release_slope = new_slope; + release_sample = sample; + release_offset = j; + have_release_sample = 1; + } + } + + slope_current_sample = 0; + if( have_attack_sample && attack_slope >= 0 ) { +// attack + peak_samples = attack_offset; + slope_samples = attack_offset; + slope_value1 = current_value; + slope_value2 = attack_sample; + current_slope = attack_slope; +//printf("CompressorEngine::process %d position=%ld slope=%f samples=%d\n", +//__LINE__, start_position + i, current_slope, slope_samples); + } + else + if( have_release_sample ) { +// release + slope_samples = release_offset; +// slope_samples = release_samples; + peak_samples = release_offset; + slope_value1 = current_value; + slope_value2 = release_sample; + current_slope = release_slope; +//printf("CompressorEngine::process %d position=%ld slope=%f\n", +//__LINE__, start_position + i, current_slope); + } + else { +static int bug = 0; +if( !bug ) { +printf("CompressorEngine::process %d have neither attack nor release position=%ld attack=%f release=%f current_value=%f\n", +__LINE__, start_position + i, attack_slope, release_slope, current_value); bug = 1; +} + } + } + else { +// check for new peak after the line segment + GET_TRIGGER(input_buffer[channel]->get_data(), i + attack_samples) + double new_slope = (sample - current_value) / + attack_samples; + if( current_slope >= 0 ) { + if( new_slope > current_slope ) { + peak_samples = attack_samples; + slope_samples = attack_samples; + slope_current_sample = 0; + slope_value1 = current_value; + slope_value2 = sample; + current_slope = new_slope; +//printf("CompressorEngine::process %d position=%ld slope=%f\n", +//__LINE__, start_position + i, current_slope); + } + } + else +// this strings together multiple release periods instead of +// approaching but never reaching the ending gain + if( current_slope < 0 ) { + if( sample > slope_value2 ) { + peak_samples = attack_samples; + slope_samples = release_samples; + slope_current_sample = 0; + slope_value1 = current_value; + slope_value2 = sample; + new_slope = (sample - current_value) / + release_samples; + current_slope = new_slope; +//printf("CompressorEngine::process %d position=%ld slope=%f\n", +//__LINE__, start_position + i, current_slope); + } +// else +// { +// GET_TRIGGER(input_buffer[channel]->get_data(), i + release_samples) +// new_slope = (sample - current_value) / +// release_samples; +// if( new_slope < current_slope && +// slope_current_sample >= peak_samples ) +// { +// peak_samples = release_samples; +// slope_samples = release_samples; +// slope_current_sample = 0; +// slope_value1 = current_value; +// slope_value2 = sample; +// current_slope = new_slope; +// printf("CompressorEngine::process %d position=%ld slope=%f\n", +// __LINE__, start_position + i, current_slope); +// } +// } + } + } + +// Update current value and multiply gain + slope_current_sample++; + current_value = slope_value1 + + (slope_value2 - slope_value1) * + slope_current_sample / + slope_samples; + + if( config->smoothing_only ) { + for( int j = 0; j < channels; j++ ) { + output_buffer[j]->get_data()[i] = current_value * 2 - 1; + } + } + else { + double gain = 1.0; + + if( band_config->bypass ) { + gain = 1.0; + } + else { + gain = config->calculate_gain(band, current_value); + } + +// update the GUI frames + if( fabs(gain - 1.0) > fabs(gui_max_gain - 1.0) ) { + gui_max_gain = gain; + } +//if( !EQUIV(gain, 1.0) ) printf("CompressorEngine::process %d gain=%f\n", __LINE__, gain); + +// calculate the input level to draw. Should it be the trigger or a channel? + GET_TRIGGER(input_buffer[channel]->get_data(), i); + if( sample > gui_max_level ) { + gui_max_level = sample; + } + + gui_frame_counter++; + if( gui_frame_counter > gui_frame_samples ) { +//if( !EQUIV(gui_frame_max, 1.0) ) printf("CompressorEngine::process %d offset=%d gui_frame_max=%f\n", __LINE__, i, gui_frame_max); + gui_gains.append(gui_max_gain); + gui_levels.append(gui_max_level); + gui_offsets.append((double)i / sample_rate); + gui_max_gain = 1.0; + gui_max_level = 0.0; + gui_frame_counter = 0; + } + + for( int j = 0; j < channels; j++ ) { + output_buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain; + } + } + } +} + + +CompressorPopup::CompressorPopup(CompressorCanvasBase *canvas) + : BC_PopupMenu(0, 0, 0, "", 0) +{ + this->canvas = canvas; +} + +CompressorPopup::~CompressorPopup() +{ +} + + +void CompressorPopup::create_objects() +{ + add_item(new CompressorCopy(this)); + add_item(new CompressorPaste(this)); + add_item(new CompressorClearGraph(this)); +} + + +CompressorCopy::CompressorCopy(CompressorPopup *popup) + : BC_MenuItem(_("Copy graph")) +{ + this->popup = popup; +} + + +CompressorCopy::~CompressorCopy() +{ +} + +int CompressorCopy::handle_event() +{ + FileXML output; + CompressorConfigBase *config = popup->canvas->config; + config->bands[config->current_band].save_data(&output, 0, 0); + output.terminate_string(); + char *cp = output.string(); + popup->to_clipboard(cp, strlen(cp), SECONDARY_SELECTION); + return 1; +} + + +CompressorPaste::CompressorPaste(CompressorPopup *popup) + : BC_MenuItem(_("Paste graph")) +{ + this->popup = popup; +} + + +CompressorPaste::~CompressorPaste() +{ +} + +int CompressorPaste::handle_event() +{ + int len = popup->get_clipboard()->clipboard_len(SECONDARY_SELECTION); + if( len ) { + CompressorConfigBase *config = popup->canvas->config; + char *string = new char[len + 1]; + popup->get_clipboard()->from_clipboard(string, len, SECONDARY_SELECTION); + + FileXML xml; + xml.read_from_string(string); + delete [] string; + int result = 0, got_it = 0; + while( !(result = xml.read_tag()) ) { + if( xml.tag.title_is("COMPRESSORBAND") ) { + int band = config->current_band; + BandConfig *band_config = &config->bands[band]; + band_config->read_data(&xml, 0); + got_it = 1; + break; + } + } + + if( got_it ) { + popup->canvas->update(); + PluginClient *plugin = popup->canvas->plugin; + plugin->send_configure_change(); + } + } + return 1; +} + + +CompressorClearGraph::CompressorClearGraph(CompressorPopup *popup) + : BC_MenuItem(_("Clear graph")) +{ + this->popup = popup; +} + + +CompressorClearGraph::~CompressorClearGraph() +{ +} + +int CompressorClearGraph::handle_event() +{ + CompressorConfigBase *config = popup->canvas->config; + config->bands[config->current_band].levels.remove_all(); + popup->canvas->update(); + PluginClient *plugin = popup->canvas->plugin; + plugin->send_configure_change(); + return 1; +} + diff --git a/cinelerra-5.1/cinelerra/compressortools.h b/cinelerra-5.1/cinelerra/compressortools.h new file mode 100644 index 00000000..abfb098d --- /dev/null +++ b/cinelerra-5.1/cinelerra/compressortools.h @@ -0,0 +1,329 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 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 gui_gains; +// input levels to draw on the GUI + ArrayList gui_levels; +// which second in process() the gui_values came from + ArrayList gui_offsets; +// samples between gui_values. Set by the user. + int gui_frame_samples; +// temporaries + int gui_frame_counter; + double gui_max_gain; + double gui_max_level; +}; + + +class CompressorCopy : public BC_MenuItem +{ +public: + CompressorCopy(CompressorPopup *popup); + ~CompressorCopy(); + int handle_event(); + CompressorPopup *popup; +}; + +class CompressorPaste : public BC_MenuItem +{ +public: + CompressorPaste(CompressorPopup *popup); + ~CompressorPaste(); + int handle_event(); + CompressorPopup *popup; +}; + +class CompressorClearGraph : public BC_MenuItem +{ +public: + CompressorClearGraph(CompressorPopup *popup); + ~CompressorClearGraph(); + int handle_event(); + CompressorPopup *popup; +}; + +class CompressorPopup : public BC_PopupMenu +{ +public: + CompressorPopup(CompressorCanvasBase *canvas); + ~CompressorPopup(); + + void create_objects(); + + + CompressorCanvasBase *canvas; +}; + +#define GRAPH_BG_COLOR 0x559977 +#define GRAPH_BORDER1_COLOR 0xeeaa44 +#define GRAPH_BORDER2_COLOR 0xeeaaff +#define GRAPH_GRID_COLOR 0xeeffcc +#define GRAPH_ACTIVE_COLOR 0x99cc77 +#define GRAPH_INACTIVE_COLOR 0x666688 + +#endif + + + + diff --git a/cinelerra-5.1/cinelerra/compressortools.inc b/cinelerra-5.1/cinelerra/compressortools.inc new file mode 100644 index 00000000..58810d9c --- /dev/null +++ b/cinelerra-5.1/cinelerra/compressortools.inc @@ -0,0 +1,22 @@ +#ifndef __COMPRESSORTOOLS_INC__ +#define __COMPRESSORTOOLS_INC__ + +class BandConfig; +class CompressorConfigBase; +class CompressorClientFrame; +class CompressorFreqFrame; +class CompressorGainFrame; +class CompressorEngine; +class CompressorCopy; +class CompressorPaste; +class CompressorClearGraph; +class CompressorPopup; + +#define GRAPH_BG_COLOR 0x559977 +#define GRAPH_BORDER1_COLOR 0xeeaa44 +#define GRAPH_BORDER2_COLOR 0xeeaaff +#define GRAPH_GRID_COLOR 0xeeffcc +#define GRAPH_ACTIVE_COLOR 0x99cc77 +#define GRAPH_INACTIVE_COLOR 0x666688 + +#endif diff --git a/cinelerra-5.1/cinelerra/cwindowgui.h b/cinelerra-5.1/cinelerra/cwindowgui.h index 7dbfde86..14e0ebfe 100644 --- a/cinelerra-5.1/cinelerra/cwindowgui.h +++ b/cinelerra-5.1/cinelerra/cwindowgui.h @@ -263,9 +263,8 @@ class CWindowTransport : public PlayTransport { public: CWindowTransport(MWindow *mwindow, - CWindowGUI *gui, - int x, - int y); + CWindowGUI *gui, int x, int y); + bool use_mixers() { return true; } EDL* get_edl(); void goto_start(); void goto_end(); diff --git a/cinelerra-5.1/cinelerra/eqcanvas.C b/cinelerra-5.1/cinelerra/eqcanvas.C new file mode 100644 index 00000000..24028889 --- /dev/null +++ b/cinelerra-5.1/cinelerra/eqcanvas.C @@ -0,0 +1,248 @@ +#include "clip.h" +#include "eqcanvas.h" +#include "mwindow.h" +#include "pluginaclient.h" +#include "theme.h" + +#define graph_bg_color 0x559977 +#define graph_border1_color 0xeeaa44 +#define graph_border2_color 0xeeaaff +#define graph_grid_color 0xeeffcc +#define graph_active_color 0x99cc77 +#define graph_inactive_color 0x666688 + +EQCanvas::EQCanvas(BC_WindowBase *parent, + int x, int y, int w, int h, + float min_db, float max_db) +{ + this->parent = parent; + this->x = x; this->y = y; + this->w = w; this->h = h; + this->min_db = min_db; + this->max_db = max_db; + canvas = 0; + + minor_divisions = 5; + freq_divisions = 5; +} + +EQCanvas::~EQCanvas() +{ + delete canvas; +} + +void EQCanvas::initialize() +{ + int big_xtick = xS(10), big_ytick = yS(10); + int small_xtick = xS(5), small_ytick = yS(5); + int tiny_xtick = xS(2); + int db_width = parent->get_text_width(SMALLFONT, "-00", -1) + big_xtick; + int freq_height = parent->get_text_ascent(SMALLFONT); + + canvas_x = x + db_width; + canvas_y = y; + canvas_w = w - db_width; + canvas_h = h - freq_height - big_ytick; + parent->add_subwindow(canvas = new BC_SubWindow( + canvas_x, canvas_y, canvas_w, canvas_h, BLACK)); + +// Draw canvas titles +// DB + parent->set_font(SMALLFONT); + int ascent = parent->get_text_ascent(SMALLFONT); +// DB per minor division + db_per_division = 1; + pixels_per_division = (float)canvas_h / (max_db - min_db) * db_per_division; +// increase the DB per minor division until they fit +//printf("EQCanvas::initialize %d pixels_per_division=%f\n", __LINE__, pixels_per_division); + while( pixels_per_division < 5 ) { + db_per_division *= 2; + pixels_per_division = (float)canvas_h / (max_db - min_db) * db_per_division; + } + total_divisions = (int)((max_db - min_db) / db_per_division); + + char string[BCTEXTLEN]; + for( int i = 0; i <= total_divisions; i++ ) { + int y1 = canvas_y + (int)(i * pixels_per_division); + int y2 = y1 + ascent; + int x2 = canvas_x - big_xtick; + int x3 = canvas_x - tiny_xtick; + int x4 = x3 - small_xtick; + + if( !(i % minor_divisions) || + i == total_divisions ) { + if( i == total_divisions ) { + sprintf(string, "oo"); + } + else { + sprintf(string, "%d", (int)(max_db - i * db_per_division)); + } + + parent->set_color(BLACK); + int text_w = parent->get_text_width(SMALLFONT, string, -1); + int x1 = canvas_x - big_xtick - text_w; + parent->set_color(parent->get_resources()->default_text_color); + parent->draw_text(x1, y2, string); + parent->draw_line(x2, y1, x3, y1); + } + else { + parent->draw_line(x4, y1, x3, y1); + } + } + +// freq + for( int i = 0; i <= freq_divisions; i++ ) { + int freq = Freq::tofreq(i * TOTALFREQS / freq_divisions); + sprintf(string, "%d", freq); + int x1 = canvas_x + i * canvas_w / freq_divisions; + int x2 = x1 - parent->get_text_width(SMALLFONT, string); + int y1 = canvas_y + canvas_h; + int y2 = y1 + big_ytick; + int y3 = y1 + small_ytick; + int y4 = y2 + parent->get_text_ascent(SMALLFONT); + +// parent->set_color(BLACK); +// parent->draw_text(x2 + 1, y4 + 1, string); +// parent->draw_line(x1 + 1, y1 + 1, x1 + 1, y2 + 1); +// parent->set_color(RED); + parent->set_color(parent->get_resources()->default_text_color); + parent->draw_text(x2, y4, string); + parent->draw_line(x1, y1, x1, y2); + + if( i < freq_divisions ) { + for( int j = 0; j < minor_divisions; j++ ) { + int x3 = (int)(x1 + + (canvas_w / freq_divisions) - + exp(-(double)j * 0.7) * + (canvas_w / freq_divisions)); +// parent->set_color(BLACK); +// parent->draw_line(x3 + 1, y1 + 1, x3 + 1, y3 + 1); +// parent->set_color(RED); + parent->set_color(parent->get_resources()->default_text_color); + parent->draw_line(x3, y1, x3, y3); + } + } + } + + draw_grid(); +} + + +void EQCanvas::draw_grid() +{ + canvas->set_line_dashes(1); + canvas->set_color(graph_grid_color); + for( int i = minor_divisions; i < total_divisions; i += minor_divisions ) { + int y = (int)(i * pixels_per_division); + canvas->draw_line(0, y, canvas_w, y); + } +// for( int i = 1; i < major_divisions; i++ ) { +// int y = canvas_h - i * canvas_h / major_divisions; +// canvas->draw_line(0, y, canvas_w, y); +// } + for( int i = 1; i < freq_divisions; i++ ) { + int x = i * canvas_w / freq_divisions; + canvas->draw_line(x, 0, x, canvas_h); + } + canvas->set_line_dashes(0); +} + +void EQCanvas::update_spectrogram(CompressorFreqFrame *frame, + int offset, int size, int window_size) +{ +//if( frame ) printf("EQCanvas::update_spectrogram %d frame->freq_max=%f frame->data=%p\n", +// __LINE__, frame->freq_max, frame->data); + canvas->set_color(graph_bg_color); + canvas->draw_box(0, 0, canvas->get_w(), canvas->get_h()); + draw_grid(); + +// Draw it + if( frame && !EQUIV(frame->freq_max, 0.0) && frame->data ) { + int y1 = 0, y2 = 0; + if( offset < 0 ) { + offset = 0; + size = frame->data_size; + window_size = frame->data_size * 2; + } + + canvas->set_color(graph_inactive_color); + if( !EQUIV(frame->freq_max, 0) ) { + for( int i = 0; i < canvas->get_w(); i++ ) { + int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w()); + + if( freq < frame->nyquist ) { + int index = offset + + (int64_t)freq * (int64_t)window_size / 2 / + frame->nyquist; + + if( index < frame->data_size ) { + double magnitude = frame->data[index] / + frame->freq_max * frame->time_max; + + y2 = (int)(canvas->get_h() - + (DB::todb(magnitude) - INFINITYGAIN) * + canvas->get_h() / + -INFINITYGAIN); + CLAMP(y2, 0, canvas->get_h() - 1); + if( i > 0 ) { + canvas->draw_line(i - 1, y1, i, y2); + } + y1 = y2; + } + } + else { +// printf("EQCanvas::update_spectrogram %d i=%d freq=%d nyquist=%d\n", +// __LINE__, i, freq, frame->nyquist); + } + } + } + } + +} + + +void EQCanvas::draw_envelope(double *envelope, + int samplerate, int window_size, int is_top, int flash_it) +{ + int niquist = samplerate / 2; + + if( is_top ) { + canvas->set_color(graph_active_color); + canvas->set_line_width(2); + } + else { + canvas->set_color(graph_inactive_color); + canvas->set_line_width(1); + } + + int y1; + for( int i = 0; i < canvas->get_w(); i++ ) { + int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w()); + int index = (int64_t)freq * (int64_t)window_size / 2 / niquist; + if( freq < niquist && index < window_size / 2 ) { + double mag = envelope[index]; + int y2 = (int)(DB::todb(mag) * canvas->get_h() / INFINITYGAIN); + + if( y2 >= canvas->get_h() ) { + y2 = canvas->get_h() - 1; + } + + if( i > 0 ) { + canvas->draw_line(i - 1, y1, i, y2); + } + y1 = y2; + } + else + if( i > 0 ) { + int y2 = canvas->get_h() - 1; + canvas->draw_line(i - 1, y1, i, y2); + y1 = y2; + } + } + + canvas->set_line_width(1); + if( flash_it ) { + canvas->flash(1); + } +} + diff --git a/cinelerra-5.1/cinelerra/eqcanvas.h b/cinelerra-5.1/cinelerra/eqcanvas.h new file mode 100644 index 00000000..318c99a7 --- /dev/null +++ b/cinelerra-5.1/cinelerra/eqcanvas.h @@ -0,0 +1,62 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +// A canvas for drawing a spectrogram on top of a filter envelope + +#ifndef EQCANVAS_H +#define EQCANVAS_H + +#include "compressortools.h" +#include "guicast.h" +#include "pluginclient.inc" +#include "pluginaclient.h" + +class EQCanvas +{ +public: + EQCanvas(BC_WindowBase *parent, + int x, int y, int w, int h, + float min_db, float max_db); + virtual ~EQCanvas(); + + void initialize(); + void draw_grid(); + void update_spectrogram(CompressorFreqFrame *frame, + int offset, int size, int window_size); + void draw_envelope(double *envelope, + int samplerate, int window_size, int is_top, int flash_it); + + BC_WindowBase *parent; + BC_SubWindow *canvas; + int x, y, w, h; + int canvas_x, canvas_y; + int canvas_w, canvas_h; + float min_db, max_db; +// divisions for the frequency + int freq_divisions; +// divisions for the DB + int db_per_division; + float pixels_per_division; + int minor_divisions; + int total_divisions; +}; + +#endif // EQCANVAS_H diff --git a/cinelerra-5.1/cinelerra/eqcanvas.inc b/cinelerra-5.1/cinelerra/eqcanvas.inc new file mode 100644 index 00000000..10ecc7a7 --- /dev/null +++ b/cinelerra-5.1/cinelerra/eqcanvas.inc @@ -0,0 +1,12 @@ +#ifndef EQCANVAS_INC +#define EQCANVAS_INC + + + +class EQCanvas; + + + +#endif + + diff --git a/cinelerra-5.1/cinelerra/formattools.C b/cinelerra-5.1/cinelerra/formattools.C index 2bf178ab..03d8e83e 100644 --- a/cinelerra-5.1/cinelerra/formattools.C +++ b/cinelerra-5.1/cinelerra/formattools.C @@ -388,7 +388,8 @@ void FormatTools::update_extension() char *ptr1 = ptr; // change "qt" to "mov" since ffmpeg does not know qt extension_ptr = asset->format != FILE_FFMPEG ? extensions.get(0) : - !strcmp(asset->fformat, "qt") ? "mov" : asset->fformat ; + !strcmp(asset->fformat, "qt" ) || + !strcmp(asset->fformat, "pro" ) ? "mov" : asset->fformat ; while(*extension_ptr != 0 && *extension_ptr != '/') *ptr1++ = *extension_ptr++; *ptr1 = 0; diff --git a/cinelerra-5.1/cinelerra/fourier.C b/cinelerra-5.1/cinelerra/fourier.C index 401a5faa..f78a45bd 100644 --- a/cinelerra-5.1/cinelerra/fourier.C +++ b/cinelerra-5.1/cinelerra/fourier.C @@ -109,7 +109,7 @@ int FFT::do_fft(int samples, // must be a power of 2 int j = i, k = i+1; // cos(0)=1, sin(0)=0 rj = real_out[j]; ij = imag_out[j]; rk = real_out[k]; ik = imag_out[k]; - real_out[j] += rk; imag_out[j] += ik; + real_out[j] += rk; imag_out[j] += ik; real_out[k] = rj - rk; imag_out[k] = ij - ik; } } @@ -119,12 +119,12 @@ int FFT::do_fft(int samples, // must be a power of 2 int j = i, k = j+2; // cos(0)=1,sin(0)=0 rj = real_out[j]; ij = imag_out[j]; rk = real_out[k]; ik = imag_out[k]; - real_out[j] += rk; imag_out[j] += ik; + real_out[j] += rk; imag_out[j] += ik; real_out[k] = rj - rk; imag_out[k] = ij - ik; j = i+1; k = j+2; // cos(-pi/2)=0, sin(-pi/2)=-1 rj = real_out[j]; ij = imag_out[j]; rk = real_out[k]; ik = imag_out[k]; - real_out[j] += ik; imag_out[j] -= rk; + real_out[j] += ik; imag_out[j] -= rk; real_out[k] = rj - ik; imag_out[k] = ij + rk; } } @@ -133,12 +133,12 @@ int FFT::do_fft(int samples, // must be a power of 2 int j = i, k = j+2; // cos(0)=1,sin(0)=0 rj = real_out[j]; ij = imag_out[j]; rk = real_out[k]; ik = imag_out[k]; - real_out[j] += rk; imag_out[j] += ik; + real_out[j] += rk; imag_out[j] += ik; real_out[k] = rj - rk; imag_out[k] = ij - ik; j = i+1; k = j+2; // cos(pi/2)=0, sin(pi/2)=1 rj = real_out[j]; ij = imag_out[j]; rk = real_out[k]; ik = imag_out[k]; - real_out[j] -= ik; imag_out[j] += rk; + real_out[j] -= ik; imag_out[j] += rk; real_out[k] = rj + ik; imag_out[k] = ij - rk; } } @@ -167,7 +167,7 @@ int FFT::do_fft(int samples, // must be a power of 2 double rk = *rkp, ik = *ikp; double tr = ar[0] * rk - ai[0] * ik; double ti = ar[0] * ik + ai[0] * rk; - *rjp++ += tr; *ijp++ += ti; + *rjp++ += tr; *ijp++ += ti; *rkp++ = rj - tr; *ikp++ = ij - ti; } } @@ -204,7 +204,7 @@ unsigned int FFT::reverse_bits(unsigned int index, unsigned int bits) { unsigned int i, rev; - for(i = rev = 0; i < bits; i++) { + for( i = rev = 0; i < bits; i++ ) { rev = (rev << 1) | (index & 1); index >>= 1; } @@ -259,8 +259,7 @@ unsigned int FFT::reverse_bits(unsigned int index, unsigned int bits) int FFT::symmetry(int size, double *freq_real, double *freq_imag) { int h = size / 2; - for(int i = h + 1; i < size; i++) - { + for( int i = h + 1; i < size; i++ ) { freq_real[i] = freq_real[size - i]; freq_imag[i] = -freq_imag[size - i]; } @@ -273,6 +272,7 @@ int FFT::symmetry(int size, double *freq_real, double *freq_imag) CrossfadeFFT::CrossfadeFFT() : FFT() { + bands = 1; reset(); window_size = 4096; } @@ -288,6 +288,8 @@ int CrossfadeFFT::reset() output_buffer = 0; freq_real = 0; freq_imag = 0; + freq_real2 = 0; + freq_imag2 = 0; output_real = 0; output_imag = 0; first_window = 1; @@ -303,12 +305,17 @@ int CrossfadeFFT::reset() int CrossfadeFFT::delete_fft() { - if(input_buffer) delete input_buffer; - if(output_buffer) delete [] output_buffer; - if(freq_real) delete [] freq_real; - if(freq_imag) delete [] freq_imag; - if(output_real) delete [] output_real; - if(output_imag) delete [] output_imag; + delete input_buffer; input_buffer = 0; + if( output_buffer ) { + for( int i=0; iwindow_size = window_size; + this->bands = bands; first_window = 1; reconfigure(); return 0; @@ -341,277 +349,172 @@ int CrossfadeFFT::reconfigure() { delete_fft(); fix_window_size(); + return 0; +} +void CrossfadeFFT::allocate_output(int new_allocation) +{ +// Allocate output buffer + if( new_allocation > output_allocation ) { + if( !output_buffer ) { + output_buffer = new double*[bands]; + bzero(output_buffer, sizeof(double) * bands); + } + for( int i = 0; i < bands; i++ ) { + double *new_output = new double[new_allocation]; + if( output_buffer[i] ) { + memcpy(new_output, output_buffer[i], + sizeof(double) * (output_size + HALF_WINDOW)); + delete [] output_buffer[i]; + } + output_buffer[i] = new_output; + } + output_allocation = new_allocation; + } +} - - return 0; +int CrossfadeFFT::process_buffer(int64_t output_sample, long size, + Samples *output_ptr, int direction) +{ + Samples *output_temp[1]; + output_temp[0] = output_ptr; + return process_buffer(output_sample, size, output_temp, direction); } -// int CrossfadeFFT::process_fifo(long size, -// double *input_ptr, -// double *output_ptr) +// int CrossfadeFFT::get_read_offset() // { -// // Load next input buffer -// if(input_size + size > input_allocation) -// { -// double *new_input = new double[input_size + size]; -// if(input_buffer) -// { -// memcpy(new_input, input_buffer, sizeof(double) * input_size); -// delete [] input_buffer; -// } -// input_buffer = new_input; -// input_allocation = input_size + size; -// } -// -// memcpy(input_buffer + input_size, -// input_ptr, -// size * sizeof(double)); -// input_size += size; -// -// -// -// -// -// -// -// // Have enough to do some windows -// while(input_size >= window_size) -// { -// if(!freq_real) freq_real = new double[window_size]; -// if(!freq_imag) freq_imag = new double[window_size]; -// if(!output_real) output_real = new double[window_size]; -// if(!output_imag) output_imag = new double[window_size]; -// -// -// -// do_fft(window_size, // must be a power of 2 -// 0, // 0 = forward FFT, 1 = inverse -// input_buffer, // array of input's real samples -// 0, // array of input's imag samples -// freq_real, // array of output's reals -// freq_imag); -// -// int result = signal_process(); -// -// if(!result) -// { -// do_fft(window_size, // must be a power of 2 -// 1, // 0 = forward FFT, 1 = inverse -// freq_real, // array of input's real samples -// freq_imag, // array of input's imag samples -// output_real, // array of output's reals -// output_imag); -// } -// -// -// // Crossfade into the output buffer -// long new_allocation = output_size + window_size; -// if(new_allocation > output_allocation) -// { -// double *new_output = new double[new_allocation]; -// -// if(output_buffer) -// { -// memcpy(new_output, output_buffer, sizeof(double) * output_size); -// delete [] output_buffer; -// } -// output_buffer = new_output; -// output_allocation = new_allocation; -// } -// -// if(output_size >= HALF_WINDOW) -// { -// for(int i = 0, j = output_size - HALF_WINDOW; -// i < HALF_WINDOW; -// i++, j++) -// { -// double src_level = (double)i / HALF_WINDOW; -// double dst_level = (double)(HALF_WINDOW - i) / HALF_WINDOW; -// output_buffer[j] = output_buffer[j] * dst_level + output_real[i] * src_level; -// } -// -// memcpy(output_buffer + output_size, -// output_real + HALF_WINDOW, -// sizeof(double) * (window_size - HALF_WINDOW)); -// output_size += window_size - HALF_WINDOW; -// } -// else -// { -// // First buffer has no crossfade -// memcpy(output_buffer + output_size, -// output_real, -// sizeof(double) * window_size); -// output_size += window_size; -// } -// -// -// // Shift input buffer forward -// for(int i = window_size - HALF_WINDOW, j = 0; -// i < input_size; -// i++, j++) -// input_buffer[j] = input_buffer[i]; -// input_size -= window_size - HALF_WINDOW; -// } -// -// -// -// -// // Have enough to send to output -// int samples_rendered = 0; -// if(output_size - HALF_WINDOW >= size) -// { -// memcpy(output_ptr, output_buffer, sizeof(double) * size); -// for(int i = size, j = 0; i < output_size; i++, j++) -// output_buffer[j] = output_buffer[i]; -// output_size -= size; -// samples_rendered = size; -// } -// else -// { -// bzero(output_ptr, sizeof(double) * size); -// } -// -// return samples_rendered; // } - - int CrossfadeFFT::process_buffer(int64_t output_sample, - long size, - Samples *output_ptr, - int direction) + long size, Samples **output_ptr, int direction) { int result = 0; int step = (direction == PLAY_FORWARD) ? 1 : -1; // User seeked so output buffer is invalid - if(output_sample != this->output_sample) - { + if( output_sample != this->output_sample ) { output_size = 0; input_size = 0; first_window = 1; this->output_sample = output_sample; - this->input_sample = output_sample; + input_sample = output_sample; } -// Fill output buffer half a window at a time until size samples are available - while(output_size < size) - { - if(!input_buffer) input_buffer = new Samples(window_size); - if(!freq_real) freq_real = new double[window_size]; - if(!freq_imag) freq_imag = new double[window_size]; - if(!output_real) output_real = new double[window_size]; - if(!output_imag) output_imag = new double[window_size]; - -// Fill enough input to make a window starting at output_sample - if(first_window) - result = read_samples(this->input_sample, - window_size, - input_buffer); - else - { - input_buffer->set_offset(HALF_WINDOW); -// printf("CrossfadeFFT::process_buffer %d %lld %lld\n", -// __LINE__, -// this->input_sample + step * HALF_WINDOW, -// this->input_sample + step * HALF_WINDOW + HALF_WINDOW); - result = read_samples(this->input_sample + step * HALF_WINDOW, - HALF_WINDOW, - input_buffer); - input_buffer->set_offset(0); - } - input_size = window_size; - - if(!result) - do_fft(window_size, // must be a power of 2 - 0, // 0 = forward FFT, 1 = inverse - input_buffer->get_data(), // array of input's real samples - 0, // array of input's imag samples - freq_real, // array of output's reals - freq_imag); - if(!result) - result = signal_process(); - - if(!result) - do_fft(window_size, // must be a power of 2 - 1, // 0 = forward FFT, 1 = inverse - freq_real, // array of input's real samples - freq_imag, // array of input's imag samples - output_real, // array of output's reals - output_imag); // array of output's imaginaries - - if(!result) - result = post_process(); +// printf("CrossfadeFFT::process_buffer %d size=%ld input_size=%ld output_size=%ld window_size=%ld\n", +// __LINE__, size, input_size, output_size, window_size); -// Allocate output buffer - int new_allocation = output_size + window_size; - if(new_allocation > output_allocation) - { - double *new_output = new double[new_allocation]; - if(output_buffer) - { - memcpy(new_output, - output_buffer, - sizeof(double) * (output_size + HALF_WINDOW)); - delete [] output_buffer; - } - output_buffer = new_output; - output_allocation = new_allocation; +// must call read_samples once so the upstream plugins don't have to seek +// must be a multiple of 1/2 window + int need_samples = (size - output_size) / HALF_WINDOW * HALF_WINDOW; +// round up a half window + if( need_samples + output_size < size ) { + need_samples += HALF_WINDOW; + } +// half window tail + need_samples += HALF_WINDOW; + +// extend the buffer to need_samples + if( !input_buffer || input_buffer->get_allocated() < need_samples ) { + Samples *new_input_buffer = new Samples(need_samples); + + if( input_buffer ) { + memcpy(new_input_buffer->get_data(), + input_buffer->get_data(), + input_size * sizeof(double)); + delete input_buffer; } -// Overlay processed buffer - if(first_window) - { - memcpy(output_buffer + output_size, - output_real, - sizeof(double) * window_size); - first_window = 0; - } - else - { - for(int i = 0, j = output_size; i < HALF_WINDOW; i++, j++) - { - double src_level = (double)i / HALF_WINDOW; - double dst_level = (double)(HALF_WINDOW - i) / HALF_WINDOW; - output_buffer[j] = output_buffer[j] * dst_level + - output_real[i] * src_level; + input_buffer = new_input_buffer; + } + + input_buffer->set_offset(input_size); + result = read_samples(input_sample, need_samples-input_size, input_buffer); + input_buffer->set_offset(0); + input_sample += step * (need_samples - input_size); + input_size = need_samples; + + + if( !freq_real ) freq_real = new double[window_size]; + if( !freq_imag ) freq_imag = new double[window_size]; + if( !freq_real2 ) freq_real2 = new double[window_size]; + if( !freq_imag2 ) freq_imag2 = new double[window_size]; + if( !output_real ) output_real = new double[window_size]; + if( !output_imag ) output_imag = new double[window_size]; + +// Fill output buffer half a window at a time until size samples are available + while( !result && output_size < size ) { + do_fft(window_size, 0, // forward + input_buffer->get_data(), 0, // input, real only + freq_real2, freq_imag2); // output + allocate_output(output_size + window_size); +// process & overlay each band separately + for( int band=0; bandget_data(); - for(int i = window_size - HALF_WINDOW, j = 0; - i < input_size; - i++, j++) - { - input_samples[j] = input_samples[i]; - } +// Shift input buffer half a window forward + memcpy(input_buffer->get_data(), + input_buffer->get_data() + HALF_WINDOW, + (input_size - HALF_WINDOW) * sizeof(double)); +// for( int i = HALF_WINDOW, j = 0; +// i < input_size; +// i++, j++ ) +// { +// input_buffer->get_data()[j] = input_buffer->get_data()[i]; +// } - input_size = HALF_WINDOW; - this->input_sample += step * HALF_WINDOW; + input_size -= HALF_WINDOW; } -// Transfer output buffer - if(output_ptr) - { - memcpy(output_ptr->get_data(), output_buffer, sizeof(double) * size); +// Transfer output buffer if the user wants it + if( output_ptr ) { + for( int band = 0; band < bands; band++ ) { + memcpy(output_ptr[band]->get_data(), output_buffer[band], sizeof(double) * size); + } + } + +// shift output buffers forward + for( int band = 0; band < bands; band++ ) { + memcpy(output_buffer[band], output_buffer[band] + size, + sizeof(double) * (output_size + HALF_WINDOW - size)); } - for(int i = 0, j = size; j < output_size + HALF_WINDOW; i++, j++) - output_buffer[i] = output_buffer[j]; this->output_sample += step * size; this->output_size -= size; @@ -621,8 +524,7 @@ int CrossfadeFFT::process_buffer(int64_t output_sample, int CrossfadeFFT::read_samples(int64_t output_sample, - int samples, - Samples *buffer) + int samples, Samples *buffer) { return 1; } @@ -640,9 +542,18 @@ int CrossfadeFFT::post_process() } +int CrossfadeFFT::signal_process(int band) +{ + signal_process(); + return 0; +} - +int CrossfadeFFT::post_process(int band) +{ + post_process(); + return 0; +} diff --git a/cinelerra-5.1/cinelerra/fourier.h b/cinelerra-5.1/cinelerra/fourier.h index 482c6fb0..9b83153a 100644 --- a/cinelerra-5.1/cinelerra/fourier.h +++ b/cinelerra-5.1/cinelerra/fourier.h @@ -33,16 +33,16 @@ class FFT static uint8_t rev_bytes[256]; static unsigned int reverse_bits(unsigned int index, unsigned int bits); static void bit_reverse(unsigned int samples, - double *real_in, double *imag_in, - double *real_out, double *imag_out); + double *real_in, double *imag_in, + double *real_out, double *imag_out); public: FFT(); virtual ~FFT(); // can be in place, but imag_out must exist, imag_in optional int do_fft(int samples, // must be a power of 2 int inverse, // 0 = forward FFT, 1 = inverse - double *real_in, double *imag_in, // complex input - double *real_out, double *imag_out); // complex output + double *real_in, double *imag_in, // complex input + double *real_out, double *imag_out); // complex output int do_fft(int samples, int inverse, double *real, double *imag) { return do_fft(samples, inverse, real, imag, real, imag); } @@ -58,8 +58,8 @@ public: virtual ~CrossfadeFFT(); int reset(); - int initialize(int window_size); - long get_delay(); // Number of samples fifo is delayed + int initialize(int window_size, int bands = 1); + long get_delay(); // Number of samples fifo is delayed int reconfigure(); int fix_window_size(); int delete_fft(); @@ -69,28 +69,33 @@ public: // output_sample - tells it if we've seeked and the output overflow is invalid. // return - 0 on success 1 on failure // output_sample - start of samples if forward. End of samples if reverse. -// It's always contiguous. +// It's always contiguous. // output_ptr - if nonzero, output is put here // direction - PLAY_FORWARD or PLAY_REVERSE int process_buffer(int64_t output_sample, - long size, - Samples *output_ptr, - int direction); + long size, Samples *output_ptr, int direction); +// multiband processing + int process_buffer(int64_t output_sample, + long size, Samples **output_ptr, int direction); // Called by process_buffer to read samples from input. // Returns 1 on error or 0 on success. virtual int read_samples(int64_t output_sample, - int samples, - Samples *buffer); + int samples, Samples *buffer); // Process a window in the frequency domain virtual int signal_process(); // Process a window in the time domain after the frequency domain virtual int post_process(); +// Multiband versions + virtual int signal_process(int band); + virtual int post_process(int band); // Size of a window. Automatically fixed to a power of 2 long window_size; +// Time domane input of complete windows + Samples *input_buffer; // Frequency domain output of FFT double *freq_real; double *freq_imag; @@ -99,13 +104,16 @@ public: double *output_imag; private: + void allocate_output(int new_allocation); -// input for complete windows - Samples *input_buffer; -// output for crossfaded windows with overflow - double *output_buffer; +// output of crossfaded windows with overflow. 1 buffer for each band + double **output_buffer; + +// backup frequency domain for multiband + double *freq_real2; + double *freq_imag2; -// samples in input_buffer +// samples in input_buffer including the tail long input_size; // window_size long input_allocation; @@ -119,6 +127,7 @@ private: int64_t input_sample; // Don't crossfade the first window int first_window; + int bands; }; #endif diff --git a/cinelerra-5.1/cinelerra/mbuttons.h b/cinelerra-5.1/cinelerra/mbuttons.h index 06e53848..698c4e83 100644 --- a/cinelerra-5.1/cinelerra/mbuttons.h +++ b/cinelerra-5.1/cinelerra/mbuttons.h @@ -72,6 +72,7 @@ class MainTransport : public PlayTransport { public: MainTransport(MWindow *mwindow, MButtons *mbuttons, int x, int y); + bool use_mixers() { return true; } void goto_start(); void goto_end(); }; diff --git a/cinelerra-5.1/cinelerra/mwindow.C b/cinelerra-5.1/cinelerra/mwindow.C index 03e265e6..e3bf6b62 100644 --- a/cinelerra-5.1/cinelerra/mwindow.C +++ b/cinelerra-5.1/cinelerra/mwindow.C @@ -3457,11 +3457,12 @@ int MWindow::plugin_gui_open(Plugin *plugin) return result; } + void MWindow::render_plugin_gui(void *data, Plugin *plugin) { int gui_id = plugin->gui_id; if( gui_id < 0 ) return; - plugin_gui_lock->lock("MWindow::render_plugin_gui"); + plugin_gui_lock->lock("MWindow::render_plugin_gui 0"); PluginServer *plugin_server = plugin_guis->gui_server(gui_id); if( plugin_server ) plugin_server->render_gui(data); @@ -3472,13 +3473,44 @@ void MWindow::render_plugin_gui(void *data, int size, Plugin *plugin) { int gui_id = plugin->gui_id; if( gui_id < 0 ) return; - plugin_gui_lock->lock("MWindow::render_plugin_gui"); + plugin_gui_lock->lock("MWindow::render_plugin_gui 1"); PluginServer *plugin_server = plugin_guis->gui_server(gui_id); if( plugin_server ) plugin_server->render_gui(data, size); plugin_gui_lock->unlock(); } +void MWindow::reset_plugin_gui_frames(Plugin *plugin) +{ + int gui_id = plugin->gui_id; + if( gui_id < 0 ) return; + plugin_gui_lock->lock("MWindow::reset_plugin_gui_frames"); + PluginServer *plugin_server = plugin_guis->gui_server(gui_id); + if( plugin_server ) + plugin_server->reset_plugin_gui_frames(); + plugin_gui_lock->unlock(); +} + +void MWindow::render_plugin_gui_frames(PluginClientFrames *frames, Plugin *plugin) +{ + int gui_id = plugin->gui_id; + if( gui_id < 0 ) return; + plugin_gui_lock->lock("MWindow::render_plugin_gui_frames"); + PluginServer *plugin_server = plugin_guis->gui_server(gui_id); + if( plugin_server ) + plugin_server->render_plugin_gui_frames(frames); + plugin_gui_lock->unlock(); +} + +double MWindow::get_tracking_position() +{ + return edl->local_session->get_selectionstart(1); +} + +int MWindow::get_tracking_direction() +{ + return cwindow->playback_engine->get_direction(); +} void MWindow::update_plugin_states() { diff --git a/cinelerra-5.1/cinelerra/mwindow.h b/cinelerra-5.1/cinelerra/mwindow.h index 513e1bfe..b0e78772 100644 --- a/cinelerra-5.1/cinelerra/mwindow.h +++ b/cinelerra-5.1/cinelerra/mwindow.h @@ -72,6 +72,7 @@ #include "playback3d.inc" #include "playbackengine.inc" #include "plugin.inc" +#include "pluginclient.inc" #include "pluginfclient.inc" #include "pluginserver.inc" #include "pluginset.inc" @@ -304,6 +305,10 @@ public: // Searches for matching plugin and renders data in it. void render_plugin_gui(void *data, Plugin *plugin); void render_plugin_gui(void *data, int size, Plugin *plugin); + void reset_plugin_gui_frames(Plugin *plugin); + void render_plugin_gui_frames(PluginClientFrames *frames, Plugin *plugin); + double get_tracking_position(); + int get_tracking_direction(); // Called from PluginVClient::process_buffer // Returns 1 if a GUI for the plugin is open so OpenGL routines can determine if diff --git a/cinelerra-5.1/cinelerra/playbackengine.C b/cinelerra-5.1/cinelerra/playbackengine.C index d9b636df..f95f5758 100644 --- a/cinelerra-5.1/cinelerra/playbackengine.C +++ b/cinelerra-5.1/cinelerra/playbackengine.C @@ -452,6 +452,11 @@ void PlaybackEngine::stop_playback(int wait_tracking) renderengine_lock->unlock(); } +int PlaybackEngine::get_direction() +{ + int curr_command = is_playing_back ? this->command->command : STOP; + return TransportCommand::get_direction(curr_command); +} void PlaybackEngine::send_command(int command, EDL *edl, int wait_tracking, int use_inout) { diff --git a/cinelerra-5.1/cinelerra/playbackengine.h b/cinelerra-5.1/cinelerra/playbackengine.h index ed646b06..c82d3508 100644 --- a/cinelerra-5.1/cinelerra/playbackengine.h +++ b/cinelerra-5.1/cinelerra/playbackengine.h @@ -85,6 +85,7 @@ public: void send_command(int command, EDL *edl, int wait_tracking, int use_inout); void stop_playback(int wait); void refresh_frame(int change_type, EDL *edl, int dir=1); + int get_direction(); // Maintain caches through console changes CICache *audio_cache, *video_cache; diff --git a/cinelerra-5.1/cinelerra/playtransport.C b/cinelerra-5.1/cinelerra/playtransport.C index 0690da07..d9d9a48a 100644 --- a/cinelerra-5.1/cinelerra/playtransport.C +++ b/cinelerra-5.1/cinelerra/playtransport.C @@ -315,7 +315,7 @@ void PlayTransport::handle_transport(int command, int wait_tracking, if( !edl ) return; using_inout = use_inout; - if( !is_vwindow() ) + if( use_mixers() ) mwindow->handle_mixers(edl, command, wait_tracking, use_inout, toggle_audio, 0, speed); engine->next_command->toggle_audio = toggle_audio; diff --git a/cinelerra-5.1/cinelerra/playtransport.h b/cinelerra-5.1/cinelerra/playtransport.h index 330a9585..20d98999 100644 --- a/cinelerra-5.1/cinelerra/playtransport.h +++ b/cinelerra-5.1/cinelerra/playtransport.h @@ -69,7 +69,7 @@ public: // Get the EDL to play back with default to mwindow->edl virtual EDL* get_edl(); void change_position(double position); - virtual int is_vwindow() { return 0; } + virtual bool use_mixers() { return false; } // playback parameters int reverse; diff --git a/cinelerra-5.1/cinelerra/pluginaclient.C b/cinelerra-5.1/cinelerra/pluginaclient.C index 8601bc0a..b1434b02 100644 --- a/cinelerra-5.1/cinelerra/pluginaclient.C +++ b/cinelerra-5.1/cinelerra/pluginaclient.C @@ -19,10 +19,13 @@ * */ +#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 @@ -32,15 +35,11 @@ PluginAClient::PluginAClient(PluginServer *server) : PluginClient(server) { sample_rate = 0; - if(server && - server->edl && - server->edl->session) - { + if(server && server->edl && server->edl->session) { project_sample_rate = server->edl->session->sample_rate; sample_rate = project_sample_rate; } - else - { + else { project_sample_rate = 1; sample_rate = 1; } @@ -92,54 +91,47 @@ int PluginAClient::process_realtime(int64_t size, } int PluginAClient::process_realtime(int64_t size, - Samples *input_ptr, - Samples *output_ptr) + Samples *input_ptr, Samples *output_ptr) { return 0; } -int PluginAClient::process_buffer(int64_t size, - Samples **buffer, - int64_t start_position, - int sample_rate) +int PluginAClient::process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate) { for(int i = 0; i < PluginClient::total_in_buffers; i++) - read_samples(buffer[i], - i, - sample_rate, - source_position, - size); + read_samples(buffer[i], i, sample_rate, source_position, size); process_realtime(size, buffer, buffer); return 0; } -int PluginAClient::process_buffer(int64_t size, - Samples *buffer, - int64_t start_position, - int sample_rate) +int PluginAClient::process_buffer(int64_t size, Samples *buffer, + int64_t start_position, int sample_rate) { - read_samples(buffer, - 0, - sample_rate, - source_position, - size); + read_samples(buffer, 0, sample_rate, source_position, size); process_realtime(size, buffer, buffer); return 0; } +void PluginAClient::begin_process_buffer() +{ + frame_buffer.destroy(); +} + +void PluginAClient::end_process_buffer() +{ + if( !frame_buffer.first ) return; + server->render_gui_frames(&frame_buffer); +} -int PluginAClient::plugin_start_loop(int64_t start, - int64_t end, - int64_t buffer_size, - int total_buffers) +int PluginAClient::plugin_start_loop(int64_t start, int64_t end, + int64_t buffer_size, int total_buffers) { sample_rate = get_project_samplerate(); - return PluginClient::plugin_start_loop(start, - end, - buffer_size, - total_buffers); + return PluginClient::plugin_start_loop(start, end, + buffer_size, total_buffers); } int PluginAClient::plugin_get_parameters() @@ -152,68 +144,44 @@ int PluginAClient::plugin_get_parameters() int64_t PluginAClient::local_to_edl(int64_t position) { if(position < 0) return position; - return (int64_t)(position * - get_project_samplerate() / - sample_rate); + return (int64_t)(position * get_project_samplerate() / sample_rate); return 0; } int64_t PluginAClient::edl_to_local(int64_t position) { if(position < 0) return position; - return (int64_t)(position * - sample_rate / - get_project_samplerate()); + return (int64_t)(position * sample_rate / get_project_samplerate()); } - int PluginAClient::plugin_process_loop(Samples **buffers, int64_t &write_length) { write_length = 0; - - if(is_multichannel()) - return process_loop(buffers, write_length); - else - return process_loop(buffers[0], write_length); + return is_multichannel() ? + process_loop(buffers, write_length) : + process_loop(buffers[0], write_length); } int PluginAClient::read_samples(Samples *buffer, - int channel, - int64_t start_position, - int64_t total_samples) + int channel, int64_t start_position, int64_t total_samples) { - return server->read_samples(buffer, - channel, - start_position, - total_samples); + return server->read_samples(buffer, channel, start_position, total_samples); } int PluginAClient::read_samples(Samples *buffer, - int64_t start_position, - int64_t total_samples) + int64_t start_position, int64_t total_samples) { - return server->read_samples(buffer, - 0, - start_position, - total_samples); + return server->read_samples(buffer, 0, start_position, total_samples); } int PluginAClient::read_samples(Samples *buffer, - int channel, - int sample_rate, - int64_t start_position, - int64_t len) + int channel, int sample_rate, int64_t start_position, int64_t len) { return server->read_samples(buffer, - channel, - sample_rate, - start_position, - len); + channel, sample_rate, start_position, len); } - - int PluginAClient::get_project_samplerate() { return project_sample_rate; @@ -224,6 +192,11 @@ int PluginAClient::get_samplerate() return sample_rate; } +Samples* PluginAClient::get_output(int channel) +{ + return output_buffers[channel]; +} + int64_t PluginAClient::get_startproject() { diff --git a/cinelerra-5.1/cinelerra/pluginaclient.h b/cinelerra-5.1/cinelerra/pluginaclient.h index b0761d0d..e9b43850 100644 --- a/cinelerra-5.1/cinelerra/pluginaclient.h +++ b/cinelerra-5.1/cinelerra/pluginaclient.h @@ -41,12 +41,10 @@ public: // These should return 1 if error or 0 if success. // Multichannel buffer process for backwards compatibility virtual int process_realtime(int64_t size, - Samples **input_ptr, - Samples **output_ptr); + Samples **input_ptr, Samples **output_ptr); // Single channel buffer process for backwards compatibility and transitions virtual int process_realtime(int64_t size, - Samples *input_ptr, - Samples *output_ptr); + Samples *input_ptr, Samples *output_ptr); // Process buffer using pull method. By default this loads the input into the // buffer and calls process_realtime with input and output pointing to buffer. @@ -54,23 +52,20 @@ public: // to start of EDL. End of buffer if reverse. // sample_rate - scale of start_position. virtual int process_buffer(int64_t size, - Samples **buffer, - int64_t start_position, - int sample_rate); + Samples **buffer, int64_t start_position, int sample_rate); virtual int process_buffer(int64_t size, - Samples *buffer, - int64_t start_position, - int sample_rate); + Samples *buffer, int64_t start_position, int sample_rate); +// process render_gui frames via add_gui_frame + virtual void begin_process_buffer(); + virtual void end_process_buffer(); virtual int process_loop(Samples *buffer, int64_t &write_length) { return 1; }; virtual int process_loop(Samples **buffers, int64_t &write_length) { return 1; }; int plugin_process_loop(Samples **buffers, int64_t &write_length); - int plugin_start_loop(int64_t start, - int64_t end, - int64_t buffer_size, - int total_buffers); + int plugin_start_loop(int64_t start, int64_t end, + int64_t buffer_size, int total_buffers); int plugin_get_parameters(); @@ -92,20 +87,21 @@ public: // sample_rate - scale of start_position. Provided so the client can get data // at a higher fidelity than provided by the EDL. int read_samples(Samples *buffer, - int channel, - int sample_rate, - int64_t start_position, - int64_t len); + int channel, int sample_rate, int64_t start_position, int64_t len); // Get the sample rate of the EDL int get_project_samplerate(); // Get the requested sample rate int get_samplerate(); + Samples* get_output(int channel); + int64_t get_startproject(); + int64_t get_endproject(); int64_t local_to_edl(int64_t position); int64_t edl_to_local(int64_t position); - int64_t get_startproject(); - int64_t get_endproject(); + +// the buffers passed to process_buffer + Samples **output_buffers; // point to the start of the buffers ArrayList input_ptr_master; diff --git a/cinelerra-5.1/cinelerra/pluginclient.C b/cinelerra-5.1/cinelerra/pluginclient.C index da9a6977..dcafd890 100644 --- a/cinelerra-5.1/cinelerra/pluginclient.C +++ b/cinelerra-5.1/cinelerra/pluginclient.C @@ -22,6 +22,7 @@ #include "bcdisplayinfo.h" #include "bchash.h" #include "bcsignals.h" +#include "attachmentpoint.h" #include "clip.h" #include "condition.h" #include "edits.h" @@ -40,8 +41,9 @@ #include "pluginclient.h" #include "pluginserver.h" #include "preferences.h" +#include "renderengine.h" #include "track.h" -#include "transportque.inc" +#include "transportque.h" #include #include @@ -51,6 +53,15 @@ #include #include +PluginClientFrame::PluginClientFrame() +{ + position = -1; +} + +PluginClientFrame::~PluginClientFrame() +{ +} + PluginClientThread::PluginClientThread(PluginClient *client) : Thread(1, 0, 0) @@ -63,9 +74,7 @@ PluginClientThread::PluginClientThread(PluginClient *client) PluginClientThread::~PluginClientThread() { join(); -//printf("PluginClientThread::~PluginClientThread %p %d\n", this, __LINE__); - delete window; window = 0; -//printf("PluginClientThread::~PluginClientThread %p %d\n", this, __LINE__); + delete window; delete init_complete; } @@ -120,29 +129,6 @@ PluginClient* PluginClientThread::get_client() } - - - - -PluginClientFrame::PluginClientFrame(int data_size, - int period_n, - int period_d) -{ - this->data_size = data_size; - force = 0; - this->period_n = period_n; - this->period_d = period_d; -} - -PluginClientFrame::~PluginClientFrame() -{ - -} - - - - - PluginClientWindow::PluginClientWindow(PluginClient *client, int w, int h, int min_w, int min_h, int allow_resize) : BC_Window(client->gui_string, @@ -421,9 +407,6 @@ int PluginText::handle_event() } - - - PluginClient::PluginClient(PluginServer *server) { reset(); @@ -444,7 +427,6 @@ PluginClient::~PluginClient() // Virtual functions don't work here. if(defaults) delete defaults; - frame_buffer.remove_all_objects(); delete update_timer; } @@ -553,7 +535,6 @@ int PluginClient::is_multichannel() { return 0; } int PluginClient::is_synthesis() { return 0; } int PluginClient::is_realtime() { return 0; } int PluginClient::is_fileio() { return 0; } -int PluginClient::delete_buffer_ptrs() { return 0; } const char* PluginClient::plugin_title() { return _("Untitled"); } Theme* PluginClient::new_theme() { return 0; } @@ -606,90 +587,114 @@ int PluginClient::set_string() -void PluginClient::begin_process_buffer() +PluginClientFrames::PluginClientFrames() +{ + count = 0; +} +PluginClientFrames::~PluginClientFrames() { -// Delete all unused GUI frames - frame_buffer.remove_all_objects(); } +int PluginClientFrames::fwd_cmpr(PluginClientFrame *a, PluginClientFrame *b) +{ + double d = a->position - b->position; + return d < 0 ? -1 : !d ? 0 : 1; +} -void PluginClient::end_process_buffer() +int PluginClientFrames::rev_cmpr(PluginClientFrame *a, PluginClientFrame *b) { - if(frame_buffer.size()) - { - send_render_gui(); - } + double d = b->position - a->position; + return d < 0 ? -1 : !d ? 0 : 1; } +void PluginClientFrames::reset() +{ + destroy(); + count = 0; +} +void PluginClientFrames::add_gui_frame(PluginClientFrame *frame) +{ + append(frame); + ++count; +} -void PluginClient::plugin_update_gui() +void PluginClientFrames::concatenate(PluginClientFrames *frames) { + concat(*frames); + count += frames->count; + frames->count = 0; +} - update_gui(); +void PluginClientFrames::sort_position(int dir) +{ +// enforce order + if( dir == PLAY_REVERSE ) + rev_sort(); + else + fwd_sort(); +} -// Delete unused GUI frames - while(frame_buffer.size() > MAX_FRAME_BUFFER) - frame_buffer.remove_object_number(0); +// pop frames until buffer passes position=pos in direction=dir +// dir==0, pop frame; pos<0, pop all frames +// delete past frames, return last popped frame +PluginClientFrame* PluginClientFrames::get_gui_frame(double pos, int dir) +{ + if( dir ) { + while( first != last ) { + if( pos >= 0 && dir*(first->next->position - pos) > 0 ) break; + delete first; --count; + } + } + PluginClientFrame *frame = first; + if( frame ) { remove_pointer(frame); --count; } + return frame; +} +PluginClientFrame* PluginClient::get_gui_frame(double pos, int dir) +{ + return frame_buffer.get_gui_frame(pos, dir); +} +PluginClientFrame* PluginClient::next_gui_frame() +{ + return frame_buffer.first; } -void PluginClient::update_gui() + +void PluginClient::plugin_update_gui() { + update_gui(); } -int PluginClient::get_gui_update_frames() +void PluginClient::update_gui() { - if(frame_buffer.size()) - { - PluginClientFrame *frame = frame_buffer.get(0); - int total_frames = update_timer->get_difference() * - frame->period_d / - frame->period_n / - 1000; - if(total_frames) update_timer->subtract(total_frames * - frame->period_n * - 1000 / - frame->period_d); - -// printf("PluginClient::get_gui_update_frames %d %ld %d %d %d\n", -// __LINE__, -// update_timer->get_difference(), -// frame->period_n * 1000 / frame->period_d, -// total_frames, -// frame_buffer.size()); - -// Add forced frames - for(int i = 0; i < frame_buffer.size(); i++) - if(frame_buffer.get(i)->force) total_frames++; - total_frames = MIN(frame_buffer.size(), total_frames); - - - return total_frames; - } - else - { - return 0; - } } -PluginClientFrame* PluginClient::get_gui_frame() +int PluginClient::pending_gui_frames() { - if(frame_buffer.size()) - { - PluginClientFrame *frame = frame_buffer.get(0); - frame_buffer.remove_number(0); - return frame; - } - else - { - return 0; - } + PluginClientFrame *frame = frame_buffer.first; + if( !frame ) return 0; + double tracking_position = get_tracking_position(); + int direction = get_tracking_direction(); + int ret = !(direction == PLAY_REVERSE ? + frame->position < tracking_position : + frame->position > tracking_position); + return ret; } void PluginClient::add_gui_frame(PluginClientFrame *frame) { - frame_buffer.append(frame); + frame_buffer.add_gui_frame(frame); +} + +double PluginClient::get_tracking_position() +{ + return server->mwindow->get_tracking_position(); +} + +int PluginClient::get_tracking_direction() +{ + return server->mwindow->get_tracking_direction(); } void PluginClient::send_render_gui() @@ -707,54 +712,56 @@ void PluginClient::send_render_gui(void *data, int size) server->send_render_gui(data, size); } -void PluginClient::plugin_render_gui(void *data, int size) + +void PluginClient::plugin_reset_gui_frames() { - render_gui(data, size); + if( !thread ) return; + BC_WindowBase *window = thread->get_window(); + if( !window ) return; + window->lock_window("PluginClient::plugin_reset_gui_frames"); + frame_buffer.reset(); + window->unlock_window(); } +void PluginClient::plugin_render_gui_frames(PluginClientFrames *frames) +{ + if( !thread ) return; + BC_WindowBase *window = thread->get_window(); + if( !window ) return; + window->lock_window("PluginClient::render_gui"); + while( frame_buffer.count > MAX_FRAME_BUFFER ) + delete get_gui_frame(0, 0); +// append client frames to gui frame_buffer, consumes frames + frame_buffer.concatenate(frames); + frame_buffer.sort_position(get_tracking_direction()); + update_timer->update(); + window->unlock_window(); +} void PluginClient::plugin_render_gui(void *data) { render_gui(data); } -void PluginClient::render_gui(void *data) +void PluginClient::plugin_render_gui(void *data, int size) { - if(thread) - { - thread->get_window()->lock_window("PluginClient::render_gui"); - -// Set all previous frames to draw immediately - for(int i = 0; i < frame_buffer.size(); i++) - frame_buffer.get(i)->force = 1; - - ArrayList *src = - (ArrayList*)data; - -// Shift GUI data to GUI client - while(src->size()) - { - this->frame_buffer.append(src->get(0)); - src->remove_number(0); - } + render_gui(data, size); +} -// Start the timer for the current buffer - update_timer->update(); - thread->get_window()->unlock_window(); - } +void PluginClient::render_gui(void *data) +{ + printf("PluginClient::render_gui %d\n", __LINE__); } void PluginClient::render_gui(void *data, int size) { - printf("PluginClient::render_gui %d\n", __LINE__); + printf("PluginClient::render_gui %d\n", __LINE__); } - - - - - - +void PluginClient::reset_gui_frames() +{ + server->reset_gui_frames(); +} int PluginClient::is_audio() { return 0; } int PluginClient::is_video() { return 0; } @@ -1009,7 +1016,6 @@ int PluginClient::get_direction() return direction; } - int64_t PluginClient::local_to_edl(int64_t position) { return position; diff --git a/cinelerra-5.1/cinelerra/pluginclient.h b/cinelerra-5.1/cinelerra/pluginclient.h index 96f5540b..67a1148f 100644 --- a/cinelerra-5.1/cinelerra/pluginclient.h +++ b/cinelerra-5.1/cinelerra/pluginclient.h @@ -30,6 +30,7 @@ class PluginClient; #include "arraylist.h" +#include "linklist.h" #include "bchash.inc" #include "condition.h" #include "edlsession.inc" @@ -137,25 +138,42 @@ int plugin_class::load_configuration() \ } +class PluginClientFrame : public ListItem +{ +public: + PluginClientFrame(); + virtual ~PluginClientFrame(); +// offset in EDL seconds for synchronizing with GUI + double position; +}; + +class PluginClientFrames : public List +{ +public: + PluginClientFrames(); + ~PluginClientFrames(); + + static int fwd_cmpr(PluginClientFrame *ap, PluginClientFrame *bp); + static int rev_cmpr(PluginClientFrame *ap, PluginClientFrame *bp); + void fwd_sort() { sort(fwd_cmpr); } + void rev_sort() { sort(rev_cmpr); } + void sort_position(int dir); + void reset(); + void add_gui_frame(PluginClientFrame *frame); + void concatenate(PluginClientFrames *frames); + PluginClientFrame *get_gui_frame(double pos, int dir); + + int count; +}; class PluginClientWindow : public BC_Window { public: PluginClientWindow(PluginClient *client, - int w, - int h, - int min_w, - int min_h, - int allow_resize); - PluginClientWindow(const char *title, - int x, - int y, - int w, - int h, - int min_w, - int min_h, - int allow_resize); + int w, int h, int min_w, int min_h, int allow_resize); + PluginClientWindow(const char *title, int x, int y, + int w, int h, int min_w, int min_h, int allow_resize); virtual ~PluginClientWindow(); virtual int translation_event(); @@ -171,86 +189,70 @@ public: class PluginFPot : public BC_FPot { public: - PluginFPot(PluginParam *param, int x, int y); - int handle_event(); - PluginParam *param; + PluginFPot(PluginParam *param, int x, int y); + int handle_event(); + PluginParam *param; }; class PluginIPot : public BC_IPot { public: - PluginIPot(PluginParam *param, int x, int y); - int handle_event(); - PluginParam *param; + PluginIPot(PluginParam *param, int x, int y); + int handle_event(); + PluginParam *param; }; class PluginQPot : public BC_QPot { public: - PluginQPot(PluginParam *param, int x, int y); - int handle_event(); - PluginParam *param; + PluginQPot(PluginParam *param, int x, int y); + int handle_event(); + PluginParam *param; }; class PluginText : public BC_TextBox { public: - PluginText(PluginParam *param, int x, int y, int value); - PluginText(PluginParam *param, int x, int y, float value); - int handle_event(); - PluginParam *param; + PluginText(PluginParam *param, int x, int y, int value); + PluginText(PluginParam *param, int x, int y, float value); + int handle_event(); + PluginParam *param; }; class PluginParam { public: - PluginParam(PluginClient *plugin, - PluginClientWindow *gui, - int x1, - int x2, - int x3, - int y, - int text_w, - int *output_i, - float *output_f, // floating point output - int *output_q, // frequency output - const char *title, - float min, - float max); - ~PluginParam(); - - void initialize(); - void update(int skip_text, int skip_pot); + PluginParam(PluginClient *plugin, PluginClientWindow *gui, + int x1, int x2, int x3, int y, int text_w, + int *output_i, float *output_f, // floating point output + int *output_q, // frequency output + const char *title, float min, float max); + ~PluginParam(); + + void initialize(); + void update(int skip_text, int skip_pot); // set the number of fractional digits - void set_precision(int digits); - -// 2 possible outputs - float *output_f; - PluginFPot *fpot; - - int *output_i; - PluginIPot *ipot; - - int *output_q; - PluginQPot *qpot; - - char *title; - PluginText *text; - PluginClientWindow *gui; - PluginClient *plugin; - int x1; - int x2; - int x3; - int y; - int text_w; - float min; - float max; - int precision; + void set_precision(int digits); + +// possible outputs + float *output_f; + PluginFPot *fpot; + int *output_i; + PluginIPot *ipot; + int *output_q; + PluginQPot *qpot; + + char *title; + PluginText *text; + PluginClientWindow *gui; + PluginClient *plugin; + int x1, x2, x3; + int y, text_w; + float min, max; + int precision; }; - - class PluginClientThread : public Thread { public: @@ -271,22 +273,6 @@ private: -// Client overrides for GUI update data -class PluginClientFrame -{ -public: -// Period_d is 1 second - PluginClientFrame(int data_size, int period_n, int period_d); - virtual ~PluginClientFrame(); - int data_size; - int period_n; - int period_d; -// Draw immediately - int force; -}; - - - class PluginClient { public: @@ -311,11 +297,6 @@ public: // Get theme being used by Cinelerra currently. Used by all plugins. Theme* get_theme(); - - - - - // Non realtime signal processors define these. // Give the samplerate of the output for a non realtime plugin. // For realtime plugins give the requested samplerate. @@ -324,20 +305,17 @@ public: // For realtime plugins give the requested framerate. virtual double get_framerate(); virtual int delete_nonrealtime_parameters(); - virtual int get_parameters(); // get information from user before non realtime processing + virtual int get_parameters(); // get information from user before non realtime processing virtual int64_t get_in_buffers(int64_t recommended_size); // return desired size for input buffers - virtual int64_t get_out_buffers(int64_t recommended_size); // return desired size for output buffers + virtual int64_t get_out_buffers(int64_t recommended_size); // return desired size for output buffers virtual int start_loop(); virtual int process_loop(); virtual int stop_loop(); // Hash files are the defaults for rendered plugins - virtual int load_defaults(); // load default settings for the plugin - virtual int save_defaults(); // save the current settings as defaults + virtual int load_defaults(); // load default settings for the plugin + virtual int save_defaults(); // save the current settings as defaults BC_Hash* get_defaults(); - - - // Realtime commands for signal processors. // These must be defined by the plugin itself. // Set the GUI title identifying the plugin to modules and patches. @@ -360,22 +338,14 @@ public: int is_defaults(); virtual void update_gui(); - virtual void save_data(KeyFrame *keyframe) {}; // write the plugin settings to text in text format - virtual void read_data(KeyFrame *keyframe) {}; // read the plugin settings from the text - int send_hide_gui(); // should be sent when the GUI receives a close event from the user + virtual void save_data(KeyFrame *keyframe) {}; // write the plugin settings to text in text format + virtual void read_data(KeyFrame *keyframe) {}; // read the plugin settings from the text + int send_hide_gui(); // should be sent when the GUI receives a close event from the user // Destroys the window but not the thread pointer. void hide_gui(); - - int get_configure_change(); // get propogated configuration change from a send_configure_change - -// Called by plugin server to update GUI with rendered data. - void plugin_render_gui(void *data); - void plugin_render_gui(void *data, int size); - - void begin_process_buffer(); - void end_process_buffer(); - void plugin_update_gui(); + virtual void begin_process_buffer() {} + virtual void end_process_buffer() {} virtual int plugin_process_loop(VFrame **buffers, int64_t &write_length) { return 1; }; virtual int plugin_process_loop(Samples **buffers, int64_t &write_length) { return 1; }; // get parameters depending on video or audio @@ -396,8 +366,8 @@ public: // data for every frame. // If the result is the default keyframe, the keyframe's position is 0. // position - relative to EDL rate or local rate to allow simple -// passing of get_source_position. -// If -1 the tracking position in the edl is used. +// passing of get_source_position. +// If -1 the tracking position in the edl is used. // is_local - if 1, the position is converted to the EDL rate. KeyFrame* get_prev_keyframe(int64_t position, int is_local = 1); KeyFrame* get_next_keyframe(int64_t position, int is_local = 1); @@ -449,6 +419,10 @@ public: // Get the direction of the most recent process_buffer int get_direction(); +// position and direction for plugin gui tracking draws + double get_tracking_position(); + int get_tracking_direction(); + // Plugin must call this before performing OpenGL operations. // Returns 1 if the user supports opengl buffers. int get_use_opengl(); @@ -467,8 +441,6 @@ public: float get_green(); float get_blue(); - - // Operations for file handlers virtual int open_file() { return 0; }; virtual int get_audio_parameters() { return 0; }; @@ -477,20 +449,12 @@ public: virtual int open_file(char *path, int wr, int rd) { return 1; }; virtual int close_file() { return 0; }; - - - - // All plugins define these. PluginClientThread* get_thread(); - - // Non realtime operations for signal processors. - virtual int plugin_start_loop(int64_t start, - int64_t end, - int64_t buffer_size, - int total_buffers); + virtual int plugin_start_loop(int64_t start, int64_t end, + int64_t buffer_size, int total_buffers); int plugin_stop_loop(); int plugin_process_loop(); MainProgressBar* start_progress(char *string, int64_t length); @@ -508,7 +472,7 @@ public: int write_frames(int64_t total_frames); // returns 1 for failure / tells the server that all output channel buffers are ready to go int write_samples(int64_t total_samples); // returns 1 for failure / tells the server that all output channel buffers are ready to go virtual int plugin_get_parameters(); - const char* get_defaultdir(); // Directory defaults should be stored in + const char* get_defaultdir(); // Directory defaults should be stored in void set_interactive(); // Realtime operations. @@ -517,48 +481,37 @@ public: virtual int plugin_command_derived(int plugin_command) { return 0; }; int plugin_get_range(); int plugin_init_realtime(int realtime_priority, - int total_in_buffers, - int buffer_size); - + int total_in_buffers, int buffer_size); // GUI updating wrappers for realtime plugins // Append frame to queue for next send_frame_buffer void add_gui_frame(PluginClientFrame *frame); - - virtual void render_gui(void *data); virtual void render_gui(void *data, int size); - -// Called by client to get the total number of frames to draw in update_gui - int get_gui_update_frames(); -// Get GUI frame from frame_buffer. Client must delete it. - PluginClientFrame* get_gui_frame(); - -// Called by client to cause GUI to be rendered with data. void send_render_gui(); void send_render_gui(void *data); void send_render_gui(void *data, int size); + void plugin_render_gui(void *data); + void plugin_render_gui(void *data, int size); - - - - - - - -// create pointers to buffers of the plugin's type before realtime rendering - virtual int delete_buffer_ptrs(); - - - + void reset_gui_frames(); + void reset_plugin_gui_frames(); + void plugin_reset_gui_frames(); + void plugin_render_gui_frames(PluginClientFrames *frames); + int get_gui_frames(); +// Called by client to get the total number of frames to draw in update_gui + int pending_gui_frames(); +// pop frames until buffer passes position=pos(-1 or seconds) in direction=dir(-1,0,1) + PluginClientFrame *get_gui_frame(double pos, int dir); + PluginClientFrame* next_gui_frame(); // communication convenience routines for the base class int stop_gui_client(); int save_data_client(); int load_data_client(); - int set_string_client(char *string); // set the string identifying the plugin - int send_cancelled(); // non realtime plugin sends when cancelled + int set_string_client(char *string); // set the string identifying the plugin + int send_cancelled(); // non realtime plugin sends when cancelled // ================================= Buffers =============================== @@ -580,18 +533,18 @@ public: ArrayList automation; // ================================== Messages =========================== - char gui_string[BCTEXTLEN]; // string identifying module and plugin - int master_gui_on; // Status of the master gui plugin - int client_gui_on; // Status of this client's gui + char gui_string[BCTEXTLEN]; // string identifying module and plugin + int master_gui_on; // Status of the master gui plugin + int client_gui_on; // Status of this client's gui - int show_initially; // set to show a realtime plugin initially + int show_initially; // set to show a realtime plugin initially // range in project for processing int64_t start, end; - int interactive; // for the progress bar plugin + int interactive; // for the progress bar plugin int success; - int total_out_buffers; // total send buffers allocated by the server - int total_in_buffers; // total receive buffers allocated by the server - int wr, rd; // File permissions for fileio plugins. + int total_out_buffers; // total send buffers allocated by the server + int total_in_buffers; // total receive buffers allocated by the server + int wr, rd; // File permissions for fileio plugins. // These give the largest fragment the plugin is expected to handle. // size of a send buffer to the server @@ -624,7 +577,7 @@ public: PluginClientThread *thread; // Frames for updating GUI - ArrayList frame_buffer; + PluginClientFrames frame_buffer; // Time of last GUI update Timer *update_timer; @@ -634,7 +587,7 @@ private: // Temporaries set in new_window int window_x, window_y; // File handlers: -// Asset *asset; // Point to asset structure in shared memory +// Asset *asset; // Point to asset structure in shared memory }; diff --git a/cinelerra-5.1/cinelerra/pluginclient.inc b/cinelerra-5.1/cinelerra/pluginclient.inc index f9ee65b6..494af128 100644 --- a/cinelerra-5.1/cinelerra/pluginclient.inc +++ b/cinelerra-5.1/cinelerra/pluginclient.inc @@ -23,6 +23,8 @@ #define PLUGINCLIENT_INC class PluginClientAuto; +class PluginClientFrames; +class PluginClientFrame; class PluginClientWindow; class PluginFPot; class PluginIPot; @@ -30,7 +32,6 @@ class PluginQPot; class PluginText; class PluginParam; class PluginClientThread; -class PluginClientFrame; class PluginClient; #endif diff --git a/cinelerra-5.1/cinelerra/pluginserver.C b/cinelerra-5.1/cinelerra/pluginserver.C index 68724f27..02f19f33 100644 --- a/cinelerra-5.1/cinelerra/pluginserver.C +++ b/cinelerra-5.1/cinelerra/pluginserver.C @@ -92,8 +92,8 @@ PluginServer::PluginServer(MWindow *mwindow, const char *path, int type) { char fpath[BCTEXTLEN]; init(); - this->plugin_type = type; - this->mwindow = mwindow; + this->plugin_type = type; + this->mwindow = mwindow; if( type == PLUGIN_TYPE_FFMPEG ) { ff_name = cstrdup(path); sprintf(fpath, "ff_%s", path); @@ -295,12 +295,12 @@ void PluginServer::set_title(const char *string) void PluginServer::generate_display_title(char *string) { char ltitle[BCTEXTLEN]; - if(BC_Resources::locale_utf8) + if( BC_Resources::locale_utf8 ) strcpy(ltitle, _(title)); else BC_Resources::encode(BC_Resources::encoding, 0, _(title),strlen(title)+1, ltitle,BCTEXTLEN); - if(plugin && plugin->track) + if( plugin && plugin->track ) sprintf(string, "%s: %s", plugin->track->title, ltitle); else strcpy(string, ltitle); @@ -344,7 +344,7 @@ int PluginServer::open_plugin(int master, EDL *edl, Plugin *plugin) { - if(plugin_open) return 0; + if( plugin_open ) return 0; this->preferences = preferences; this->plugin = plugin; @@ -403,8 +403,8 @@ int PluginServer::open_plugin(int master, // Run initialization functions realtime = client->is_realtime(); // Don't load defaults when probing the directory. - if(!master) { - if(realtime) + if( !master ) { + if( realtime ) client->load_defaults_xml(); else client->load_defaults(); @@ -426,12 +426,11 @@ int PluginServer::open_plugin(int master, int PluginServer::close_plugin() { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; - if(client) - { + if( client ) { // Defaults are saved in the thread. -// if(client->defaults) client->save_defaults(); +// if( client->defaults ) client->save_defaults(); delete client; } @@ -444,11 +443,10 @@ int PluginServer::close_plugin() void PluginServer::client_side_close() { // Last command executed in client thread - if(plugin) + if( plugin ) mwindow->hide_plugin(plugin, 1); else - if(prompt) - { + if( prompt ) { prompt->lock_window(); prompt->set_done(1); prompt->unlock_window(); @@ -457,13 +455,14 @@ void PluginServer::client_side_close() void PluginServer::render_stop() { - if(client) + if( client ) client->render_stop(); + send_reset_gui_frames(); } void PluginServer::write_table(FILE *fp, const char *path, int idx, int64_t mtime) { - if(!fp) return; + if( !fp ) return; fprintf(fp, "%d \"%s\" \"%s\" %jd %d %d %d %d %d %d %d %d %d %d %d\n", plugin_type, path, title, mtime, idx, audio, video, theme, realtime, fileio, uses_gui, multichannel, synthesis, transition, lad_index); @@ -492,7 +491,7 @@ int PluginServer::init_realtime(int realtime_sched, int buffer_size) { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; // set for realtime priority // initialize plugin @@ -512,7 +511,7 @@ void PluginServer::process_transition(VFrame *input, int64_t current_position, int64_t total_len) { - if(!plugin_open) return; + if( !plugin_open ) return; PluginVClient *vclient = (PluginVClient*)client; vclient->source_position = current_position; @@ -538,7 +537,7 @@ void PluginServer::process_transition(Samples *input, int64_t fragment_size, int64_t total_len) { - if(!plugin_open) return; + if( !plugin_open ) return; PluginAClient *aclient = (PluginAClient*)client; aclient->source_position = current_position; @@ -556,50 +555,41 @@ void PluginServer::process_buffer(VFrame **frame, int64_t total_len, int direction) { - if(!plugin_open) return; + if( !plugin_open ) return; PluginVClient *vclient = (PluginVClient*)client; - + vclient->in_buffer_size = vclient->out_buffer_size = 1; vclient->source_position = current_position; vclient->total_len = total_len; vclient->frame_rate = frame_rate; vclient->input = new VFrame*[total_in_buffers]; vclient->output = new VFrame*[total_in_buffers]; - for(int i = 0; i < total_in_buffers; i++) - { + for( int i = 0; i < total_in_buffers; i++ ) { vclient->input[i] = frame[i]; vclient->output[i] = frame[i]; } - if(plugin) - { + if( plugin ) { vclient->source_start = (int64_t)plugin->startproject * - frame_rate / - vclient->project_frame_rate; + frame_rate / vclient->project_frame_rate; } vclient->direction = direction; - //PRINT_TRACE //printf("plugin=%p source_start=%ld\n", plugin, vclient->source_start); - - vclient->begin_process_buffer(); - if(multichannel) - { +// vclient->begin_process_buffer(); + if( multichannel ) vclient->process_buffer(frame, current_position, frame_rate); - } else - { vclient->process_buffer(frame[0], current_position, frame_rate); - } - vclient->end_process_buffer(); +// vclient->end_process_buffer(); - for(int i = 0; i < total_in_buffers; i++) + for( int i = 0; i < total_in_buffers; i++ ) frame[i]->push_prev_effect(title); delete [] vclient->input; delete [] vclient->output; - vclient->age_temp(); + vclient->age_temp(); use_opengl = 0; } @@ -610,38 +600,64 @@ void PluginServer::process_buffer(Samples **buffer, int64_t total_len, int direction) { - if(!plugin_open) return; + if( !plugin_open ) return; PluginAClient *aclient = (PluginAClient*)client; - aclient->source_position = current_position; aclient->total_len = total_len; aclient->sample_rate = sample_rate; + aclient->in_buffer_size = aclient->out_buffer_size = fragment_size; + aclient->output_buffers = buffer; - if(plugin) + if( plugin ) aclient->source_start = plugin->startproject * sample_rate / aclient->project_sample_rate; aclient->direction = direction; aclient->begin_process_buffer(); - if(multichannel) - { + if( multichannel ) { aclient->process_buffer(fragment_size, - buffer, - current_position, - sample_rate); + buffer, current_position, sample_rate); } - else - { + else { aclient->process_buffer(fragment_size, - buffer[0], - current_position, - sample_rate); + buffer[0], current_position, sample_rate); } aclient->end_process_buffer(); } +void PluginServer::send_reset_gui_frames() +{ + if( !attachmentpoint ) return; + attachmentpoint->reset_gui_frames(this); +} + +void PluginServer::send_render_gui(void *data) +{ + if( !attachmentpoint ) return; + attachmentpoint->render_gui(data, this); +} + +void PluginServer::send_render_gui(void *data, int size) +{ + if( !attachmentpoint ) return; + attachmentpoint->render_gui(data, size, this); +} + +void PluginServer::render_gui(void *data) +{ + if( !client ) return; + client->plugin_render_gui(data); +} + +void PluginServer::render_gui(void *data, int size) +{ + if( !client ) return; + client->plugin_render_gui(data, size); +} + + PluginGUIs::PluginGUIs(MWindow *mwindow) { this->mwindow = mwindow; @@ -668,26 +684,26 @@ PluginServer *PluginGUIs::gui_server(int gui_id) } -void PluginServer::send_render_gui(void *data) +void PluginServer::reset_gui_frames() { -//printf("PluginServer::send_render_gui 1 %p\n", attachmentpoint); - if(attachmentpoint) attachmentpoint->render_gui(data, this); + mwindow->reset_plugin_gui_frames(plugin); } -void PluginServer::send_render_gui(void *data, int size) +void PluginServer::reset_plugin_gui_frames() { -//printf("PluginServer::send_render_gui 1 %p\n", attachmentpoint); - if(attachmentpoint) attachmentpoint->render_gui(data, size, this); + if( !client ) return; + client->plugin_reset_gui_frames(); } -void PluginServer::render_gui(void *data) +void PluginServer::render_gui_frames(PluginClientFrames *frames) { - if(client) client->plugin_render_gui(data); + mwindow->render_plugin_gui_frames(frames, plugin); } -void PluginServer::render_gui(void *data, int size) +void PluginServer::render_plugin_gui_frames(PluginClientFrames *frames) { - if(client) client->plugin_render_gui(data, size); + if( !client ) return; + client->plugin_render_gui_frames(frames); } MainProgressBar* PluginServer::start_progress(char *string, int64_t length) @@ -700,30 +716,22 @@ MainProgressBar* PluginServer::start_progress(char *string, int64_t length) int64_t PluginServer::get_written_samples() { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; return written_samples; } int64_t PluginServer::get_written_frames() { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; return written_frames; } - - - - - - - - // ======================= Non-realtime plugin int PluginServer::get_parameters(int64_t start, int64_t end, int channels) { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; client->start = start; client->end = end; @@ -739,7 +747,7 @@ int PluginServer::get_parameters(int64_t start, int64_t end, int channels) int PluginServer::set_interactive() { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; client->set_interactive(); return 0; } @@ -776,13 +784,13 @@ int PluginServer::set_realtime_sched() int PluginServer::process_loop(VFrame **buffers, int64_t &write_length) { - if(!plugin_open) return 1; + if( !plugin_open ) return 1; return client->plugin_process_loop(buffers, write_length); } int PluginServer::process_loop(Samples **buffers, int64_t &write_length) { - if(!plugin_open) return 1; + if( !plugin_open ) return 1; return client->plugin_process_loop(buffers, write_length); } @@ -792,14 +800,14 @@ int PluginServer::start_loop(int64_t start, int64_t buffer_size, int total_buffers) { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; client->plugin_start_loop(start, end, buffer_size, total_buffers); return 0; } int PluginServer::stop_loop() { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; return client->plugin_stop_loop(); } @@ -808,66 +816,42 @@ int PluginServer::read_frame(VFrame *buffer, int64_t start_position) { ((VModule*)modules->values[channel])->render(buffer, - start_position, - PLAY_FORWARD, - mwindow->edl->session->frame_rate, - 0, - 0); + start_position, PLAY_FORWARD, + mwindow->edl->session->frame_rate, 0, 0); return 0; } int PluginServer::read_samples(Samples *buffer, - int channel, - int64_t sample_rate, - int64_t start_position, - int64_t len) + int channel, int64_t sample_rate, int64_t start_position, int64_t len) { // len is now in buffer - if(!multichannel) channel = 0; + if( !multichannel ) channel = 0; - if(nodes->total > channel) + if( nodes->total > channel ) return ((VirtualANode*)nodes->values[channel])->read_data(buffer, - len, - start_position, - sample_rate); - else - if(modules->total > channel) + len, start_position, sample_rate); + if( modules->total > channel ) return ((AModule*)modules->values[channel])->render(buffer, - len, - start_position, - PLAY_FORWARD, - sample_rate, - 0); - else - { - printf("PluginServer::read_samples no object available for channel=%d\n", - channel); - } - + len, start_position, PLAY_FORWARD, sample_rate, 0); + printf("PluginServer::read_samples no object available for channel=%d\n", + channel); return -1; } int PluginServer::read_samples(Samples *buffer, - int channel, - int64_t start_position, - int64_t size) + int channel, int64_t start_position, int64_t size) { // total_samples is now set in buffer ((AModule*)modules->values[channel])->render(buffer, - size, - start_position, - PLAY_FORWARD, - mwindow->edl->session->sample_rate, - 0); + size, start_position, PLAY_FORWARD, + mwindow->edl->session->sample_rate, 0); return 0; } int PluginServer::read_frame(VFrame *buffer, - int channel, - int64_t start_position, - double frame_rate, - int use_opengl) + int channel, int64_t start_position, double frame_rate, + int use_opengl) { // Data source depends on whether we're part of a virtual console or a // plugin array. @@ -879,36 +863,26 @@ int PluginServer::read_frame(VFrame *buffer, //PRINT_TRACE int result = -1; - if(!multichannel) channel = 0; + if( !multichannel ) channel = 0; // Push our name on the next effect stack buffer->push_next_effect(title); //printf("PluginServer::read_frame %p\n", buffer); //buffer->dump_stacks(); - if(nodes->total > channel) - { + if( nodes->total > channel ) { //printf("PluginServer::read_frame %d\n", __LINE__); result = ((VirtualVNode*)nodes->values[channel])->read_data(buffer, - start_position, - frame_rate, - use_opengl); + start_position, frame_rate, use_opengl); } else - if(modules->total > channel) - { + if( modules->total > channel ) { //printf("PluginServer::read_frame %d\n", __LINE__); result = ((VModule*)modules->values[channel])->render(buffer, - start_position, -// PLAY_FORWARD, - client->direction, - frame_rate, - 0, - 0, - use_opengl); + start_position, // PLAY_FORWARD, + client->direction, frame_rate, 0, 0, use_opengl); } - else - { + else { printf("PluginServer::read_frame no object available for channel=%d\n", channel); } @@ -920,28 +894,10 @@ int PluginServer::read_frame(VFrame *buffer, } - - - - - - - - - - - - - - - - - - // Called by client int PluginServer::get_gui_status() { - if(plugin) + if( plugin ) return plugin->show ? GUI_ON : GUI_OFF; else return GUI_OFF; @@ -949,27 +905,25 @@ int PluginServer::get_gui_status() void PluginServer::raise_window() { - if(!plugin_open) return; + if( !plugin_open ) return; client->raise_window(); } void PluginServer::show_gui() { - if(!plugin_open) return; + if( !plugin_open ) return; if( plugin ) { plugin->gui_id = gui_id; client->total_len = plugin->length; client->source_start = plugin->startproject; } - if(video) - { + if( video ) { client->source_position = Units::to_int64( mwindow->edl->local_session->get_selectionstart(1) * mwindow->edl->session->frame_rate); } else - if(audio) - { + if( audio ) { client->source_position = Units::to_int64( mwindow->edl->local_session->get_selectionstart(1) * mwindow->edl->session->sample_rate); @@ -981,28 +935,25 @@ void PluginServer::show_gui() void PluginServer::hide_gui() { - if(!plugin_open) return; + if( !plugin_open ) return; if( plugin ) plugin->gui_id = -1; - if(client->defaults) client->save_defaults(); + if( client->defaults ) client->save_defaults(); client->hide_gui(); } void PluginServer::update_gui() { - if(!plugin_open || !plugin) return; + if( !plugin_open || !plugin ) return; client->total_len = plugin->length; client->source_start = plugin->startproject; - if(video) - { + if( video ) { client->source_position = Units::to_int64( mwindow->edl->local_session->get_selectionstart(1) * mwindow->edl->session->frame_rate); } - else - if(audio) - { + else if( audio ) { client->source_position = Units::to_int64( mwindow->edl->local_session->get_selectionstart(1) * mwindow->edl->session->sample_rate); @@ -1013,7 +964,7 @@ void PluginServer::update_gui() void PluginServer::update_title() { - if(!plugin_open) return; + if( !plugin_open ) return; client->update_display_title(); } @@ -1021,7 +972,7 @@ void PluginServer::update_title() int PluginServer::set_string(char *string) { - if(!plugin_open) return 0; + if( !plugin_open ) return 0; client->set_string_client(string); return 0; @@ -1029,7 +980,7 @@ int PluginServer::set_string(char *string) int PluginServer::gui_open() { - if(attachmentpoint) return attachmentpoint->gui_open(); + if( attachmentpoint ) return attachmentpoint->gui_open(); return 0; } @@ -1047,7 +998,7 @@ int PluginServer::get_use_opengl() void PluginServer::run_opengl(PluginClient *plugin_client) { - if(vdevice) + if( vdevice ) ((VDeviceX11*)vdevice->get_output_base())->run_plugin(plugin_client); } @@ -1058,8 +1009,8 @@ void PluginServer::get_defaults_path(char *path) // Get plugin name from path char *ptr1 = strrchr(get_path(), '/'); char *ptr2 = strrchr(get_path(), '.'); - if(!ptr1) ptr1 = get_path(); - if(!ptr2) ptr2 = get_path() + strlen(get_path()); + if( !ptr1 ) ptr1 = get_path(); + if( !ptr2 ) ptr2 = get_path() + strlen(get_path()); char string2[BCTEXTLEN], *ptr3 = string2; while( ptr1 < ptr2 ) *ptr3++ = *ptr1++; *ptr3 = 0; @@ -1068,74 +1019,53 @@ void PluginServer::get_defaults_path(char *path) void PluginServer::save_defaults() { - if(client) client->save_defaults(); + if( client ) client->save_defaults(); } int PluginServer::get_samplerate() { - if(!plugin_open) return 0; - if(audio) - { + if( !plugin_open ) return 0; + if( audio ) return client->get_samplerate(); - } - else - if(mwindow) + if( mwindow ) return mwindow->edl->session->sample_rate; - else - { - printf("PluginServer::get_samplerate audio and mwindow == NULL\n"); - return 1; - } + printf("PluginServer::get_samplerate audio and mwindow == NULL\n"); + return 1; } double PluginServer::get_framerate() { - if(!plugin_open) return 0; - if(video) - { + if( !plugin_open ) return 0; + if( video ) return client->get_framerate(); - } - else - if(mwindow) + if( mwindow ) return mwindow->edl->session->frame_rate; - else - { - printf("PluginServer::get_framerate video and mwindow == NULL\n"); - return 1; - } + printf("PluginServer::get_framerate video and mwindow == NULL\n"); + return 1; } int PluginServer::get_project_samplerate() { - if(mwindow) + if( mwindow ) return mwindow->edl->session->sample_rate; - else - if(edl) + if( edl ) return edl->session->sample_rate; - else - { - printf("PluginServer::get_project_samplerate mwindow and edl are NULL.\n"); - return 1; - } + printf("PluginServer::get_project_samplerate mwindow and edl are NULL.\n"); + return 1; } double PluginServer::get_project_framerate() { - if(mwindow) + if( mwindow ) return mwindow->edl->session->frame_rate; - else - if(edl) + if( edl ) return edl->session->frame_rate; - else - { - printf("PluginServer::get_project_framerate mwindow and edl are NULL.\n"); - return 1; - } + printf("PluginServer::get_project_framerate mwindow and edl are NULL.\n"); + return 1; } - int PluginServer::detach_buffers() { ring_buffers_out.remove_all(); @@ -1157,10 +1087,8 @@ int PluginServer::detach_buffers() } int PluginServer::arm_buffer(int buffer_number, - int64_t offset_in, - int64_t offset_out, - int double_buffer_in, - int double_buffer_out) + int64_t offset_in, int64_t offset_out, + int double_buffer_in, int double_buffer_out) { offset_in_render.values[buffer_number] = offset_in; offset_out_render.values[buffer_number] = offset_out; @@ -1183,14 +1111,14 @@ int PluginServer::set_automation(FloatAutos *autos, FloatAuto **start_auto, Floa void PluginServer::save_data(KeyFrame *keyframe) { - if(!plugin_open) return; + if( !plugin_open ) return; client->save_data(keyframe); } KeyFrame* PluginServer::get_prev_keyframe(int64_t position) { KeyFrame *result = 0; - if(plugin) + if( plugin ) result = plugin->get_prev_keyframe(position, client->direction); else result = keyframe; @@ -1199,43 +1127,32 @@ KeyFrame* PluginServer::get_prev_keyframe(int64_t position) KeyFrame* PluginServer::get_next_keyframe(int64_t position) { - KeyFrame *result = 0; - if(plugin) - result = plugin->get_next_keyframe(position, client->direction); - else - result = keyframe; + KeyFrame *result = !plugin ? 0 : + plugin->get_next_keyframe(position, client->direction); return result; } // Called for KeyFrame* PluginServer::get_keyframe() { - if(plugin) + if( plugin ) // Realtime plugin case return plugin->get_keyframe(); - else // Rendered plugin case - return keyframe; + return keyframe; } void PluginServer::apply_keyframe(KeyFrame *src) { - if(!plugin) - { + if( !plugin ) keyframe->copy_data(src); - } else - { // Span keyframes plugin->keyframes->update_parameter(src); - } } - - - void PluginServer::get_camera(float *x, float *y, float *z, int64_t position, int direction) { @@ -1256,17 +1173,14 @@ int PluginServer::get_interpolation_type() Theme* PluginServer::new_theme() { - if(theme) - { + if( theme ) return client->new_theme(); - } - else - return 0; + return 0; } Theme* PluginServer::get_theme() { - if(mwindow) return mwindow->theme; + if( mwindow ) return mwindow->theme; printf("PluginServer::get_theme mwindow not set\n"); return 0; } @@ -1321,11 +1235,10 @@ VFrame *PluginServer::get_picon() // Called when plugin interface is tweeked void PluginServer::sync_parameters() { - if(video) mwindow->restart_brender(); + if( video ) mwindow->restart_brender(); mwindow->sync_parameters(); mwindow->update_keyframe_guis(); - if(mwindow->edl->session->auto_conf->plugins) - { + if( mwindow->edl->session->auto_conf->plugins ) { mwindow->gui->lock_window("PluginServer::sync_parameters"); mwindow->gui->draw_overlays(1); mwindow->gui->unlock_window(); diff --git a/cinelerra-5.1/cinelerra/pluginserver.h b/cinelerra-5.1/cinelerra/pluginserver.h index 5be37a4b..16c2c5cb 100644 --- a/cinelerra-5.1/cinelerra/pluginserver.h +++ b/cinelerra-5.1/cinelerra/pluginserver.h @@ -270,9 +270,16 @@ public: // Called by rendering client to cause the GUI to display something with the data. void send_render_gui(void *data); void send_render_gui(void *data, int size); + // Called by MWindow to cause GUI to display void render_gui(void *data); void render_gui(void *data, int size); +// PluginClientFrames queuing to gui frame_buffer + void send_reset_gui_frames(); + void reset_gui_frames(); + void render_gui_frames(PluginClientFrames *frames); + void reset_plugin_gui_frames(); + void render_plugin_gui_frames(PluginClientFrames *frames); // Send the boundary autos of the next fragment int set_automation(FloatAutos *autos, FloatAuto **start_auto, FloatAuto **end_auto, int reverse); diff --git a/cinelerra-5.1/cinelerra/resourcepixmap.C b/cinelerra-5.1/cinelerra/resourcepixmap.C index c65d1b95..82dd7c80 100644 --- a/cinelerra-5.1/cinelerra/resourcepixmap.C +++ b/cinelerra-5.1/cinelerra/resourcepixmap.C @@ -335,7 +335,6 @@ SET_TRACE IndexState *index_state = indexable->index_state; double asset_over_session = (double)indexable->get_sample_rate() / mwindow->edl->session->sample_rate; - // Develop strategy for drawing // printf("ResourcePixmap::draw_audio_resource %d %p %d\n", // __LINE__, @@ -345,52 +344,54 @@ SET_TRACE { case INDEX_NOTTESTED: return; - break; // Disabled. All files have an index. // case INDEX_TOOSMALL: // draw_audio_source(canvas, edit, x, w); // break; case INDEX_BUILDING: - case INDEX_READY: - { + case INDEX_READY: { IndexFile indexfile(mwindow, indexable); if( !indexfile.open_index() ) { if( index_state->index_zoom > - mwindow->edl->local_session->zoom_sample * + mwindow->edl->local_session->zoom_sample * asset_over_session ) { -//printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__); - draw_audio_source(canvas, edit, x, w); } else { -//printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__); - indexfile.draw_index(canvas, - this, - edit, - x, - w); -SET_TRACE + indexfile.draw_index(canvas, this, edit, x, w); } - indexfile.close_index(); -SET_TRACE } break; } } + if( !mwindow->preferences->rectify_audio ) { + int center_pixel = calculate_center_pixel(edit->track); + canvas->set_line_dashes(1); + canvas->set_color(mwindow->theme->zero_crossing_color); + canvas->draw_line(x, center_pixel, x + w, center_pixel, this); + canvas->set_line_dashes(0); + } } -void ResourcePixmap::draw_audio_source(TrackCanvas *canvas, Edit *edit, int x, int w) +int ResourcePixmap::calculate_center_pixel(Track *track) { - w++; - Indexable *indexable = edit->get_source(); int rect_audio = mwindow->preferences->rectify_audio; int center_pixel = !rect_audio ? mwindow->edl->local_session->zoom_track / 2 : mwindow->edl->local_session->zoom_track ; - if( edit->track->show_titles() ) + if( track->show_titles() ) center_pixel += mwindow->theme->get_image("title_bg_data")->get_h(); + return center_pixel; +} + +void ResourcePixmap::draw_audio_source(TrackCanvas *canvas, Edit *edit, int x, int w) +{ + w++; + Indexable *indexable = edit->get_source(); + int center_pixel = calculate_center_pixel(edit->track); + int rect_audio = mwindow->preferences->rectify_audio; int64_t scale_y = !rect_audio ? mwindow->edl->local_session->zoom_y : mwindow->edl->local_session->zoom_y * 2; diff --git a/cinelerra-5.1/cinelerra/resourcepixmap.h b/cinelerra-5.1/cinelerra/resourcepixmap.h index 7f5fba2c..3610c7a2 100644 --- a/cinelerra-5.1/cinelerra/resourcepixmap.h +++ b/cinelerra-5.1/cinelerra/resourcepixmap.h @@ -24,6 +24,7 @@ #include "bctimer.inc" #include "edit.inc" +#include "track.inc" #include "guicast.h" #include "mwindow.inc" #include "trackcanvas.inc" @@ -46,6 +47,7 @@ public: Edit *edit, int64_t edit_x, int64_t edit_w, int64_t pixmap_x, int64_t pixmap_w, int64_t pixmap_h, int mode, int indexes_only); + int calculate_center_pixel(Track *track); void draw_audio_resource(TrackCanvas *canvas, Edit *edit, int x, int w); void draw_video_resource(TrackCanvas *canvas, diff --git a/cinelerra-5.1/cinelerra/theme.C b/cinelerra-5.1/cinelerra/theme.C index fb3ff4df..d35e4750 100644 --- a/cinelerra-5.1/cinelerra/theme.C +++ b/cinelerra-5.1/cinelerra/theme.C @@ -77,6 +77,13 @@ Theme::Theme() BC_WindowBase::get_resources()->button_highlighted = 0xffe000; BC_WindowBase::get_resources()->recursive_resizing = 0; audio_color = BLACK; + zero_crossing_color = 0xc03545; + graph_active_color = GRAPH_ACTIVE_COLOR; + graph_inactive_color = GRAPH_INACTIVE_COLOR; + graph_grid_color = GRAPH_GRID_COLOR; + graph_bg_color = GRAPH_BG_COLOR; + graph_border1_color = GRAPH_BORDER1_COLOR; + graph_border2_color = GRAPH_BORDER2_COLOR; fade_h = yS(22); inout_highlight_color = GREEN; meter_h = yS(17); diff --git a/cinelerra-5.1/cinelerra/theme.h b/cinelerra-5.1/cinelerra/theme.h index 7055ee15..1b1e4b38 100644 --- a/cinelerra-5.1/cinelerra/theme.h +++ b/cinelerra-5.1/cinelerra/theme.h @@ -25,6 +25,7 @@ #include "awindowgui.inc" #include "batchrender.inc" #include "bctheme.h" +#include "compressortools.inc" #include "cwindowgui.inc" #include "guicast.h" #include "keyframegui.inc" @@ -165,6 +166,16 @@ public: int afolders_x, afolders_y, afolders_w, afolders_h; int alist_x, alist_y, alist_w, alist_h; int audio_color; +// audio zero crossing + int zero_crossing_color; +// compressor graph line + int graph_active_color; + int graph_inactive_color; +// compressor graph background + int graph_grid_color; + int graph_bg_color; + int graph_border1_color; + int graph_border2_color; int assetedit_color; int browse_pad; int cauto_x, cauto_y, cauto_w, cauto_h; diff --git a/cinelerra-5.1/guicast/arraylist.h b/cinelerra-5.1/guicast/arraylist.h index ef27522f..602557f5 100644 --- a/cinelerra-5.1/guicast/arraylist.h +++ b/cinelerra-5.1/guicast/arraylist.h @@ -46,6 +46,17 @@ public: while( ++n= total ) return; + for( n+=i; n= total ) return; + for( n+=i; n @@ -44,16 +43,9 @@ #define METER_RIGHT 3 -BC_Meter::BC_Meter(int x, - int y, - int orientation, - int pixels, - int min, - int max, - int mode, - int use_titles, - int span, - int downmix) +BC_Meter::BC_Meter(int x, int y, int orientation, int pixels, + int min, int max, int mode, int use_titles, + int span, int is_downmix, int is_gain_change) : BC_SubWindow(x, y, -1, -1) { this->over_delay = 150; @@ -65,10 +57,10 @@ BC_Meter::BC_Meter(int x, this->orientation = orientation; this->pixels = pixels; this->span = span; - this->downmix = downmix; - + this->is_downmix = is_downmix; + this->is_gain_change = is_gain_change; //printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels); - for(int i = 0; i < TOTAL_METER_IMAGES; i++) images[i] = 0; + for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = 0; db_titles.set_array_delete(); } @@ -76,8 +68,9 @@ BC_Meter::~BC_Meter() { db_titles.remove_all_objects(); title_pixels.remove_all(); + tick_w.remove_all(); tick_pixels.remove_all(); - for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i]; + for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i]; } int BC_Meter::get_title_w() @@ -104,26 +97,29 @@ int BC_Meter::initialize() level_pixel = peak_pixel = 0; over_timer = 0; over_count = 0; - peak = level = -100; - if(orientation == METER_VERT) - { + if( is_gain_change ) { + peak = level = 0; + } + else { + peak = level = -100; + } + + if( orientation == METER_VERT ) { set_images(get_resources()->ymeter_images); h = pixels; - if(span < 0) - { + if( span < 0 ) { w = images[0]->get_w(); - if(use_titles) w += get_title_w(); + if( use_titles ) w += get_title_w(); } else w = span; } - else - { + else { set_images(get_resources()->xmeter_images); h = images[0]->get_h(); w = pixels; - if(use_titles) h += get_title_w(); + if( use_titles ) h += get_title_w(); } // calibrate the db titles @@ -138,21 +134,19 @@ int BC_Meter::initialize() void BC_Meter::set_images(VFrame **data) { - for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i]; - for(int i = 0; i < TOTAL_METER_IMAGES; i++) + for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i]; + for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA); } int BC_Meter::reposition_window(int x, int y, int span, int pixels) { - if(pixels < 0) pixels = this->pixels; + if( pixels < 0 ) pixels = this->pixels; this->span = span; this->pixels = pixels; - if(orientation == METER_VERT) - BC_SubWindow::reposition_window(x, - y, - this->span < 0 ? w : span, - pixels); + if( orientation == METER_VERT ) + BC_SubWindow::reposition_window(x, y, + this->span < 0 ? w : span, pixels); else BC_SubWindow::reposition_window(x, y, pixels, get_h()); @@ -168,7 +162,7 @@ int BC_Meter::reposition_window(int x, int y, int span, int pixels) return 0; } -int BC_Meter::reset(int dmix) +int BC_Meter::reset(int downmix) { level = min; peak = min; @@ -176,15 +170,15 @@ int BC_Meter::reset(int dmix) peak_timer = 0; over_timer = 0; over_count = 0; - if(dmix >= 0) downmix = dmix; + if( downmix >= 0 ) + is_downmix = downmix; draw_face(1); return 0; } int BC_Meter::button_press_event() { - if(cursor_inside() && is_event_win()) - { + if( cursor_inside() && is_event_win() ) { reset_over(); return 1; } @@ -210,15 +204,11 @@ int BC_Meter::change_format(int mode, int min, int max) int BC_Meter::level_to_pixel(float level) { int result; - if(mode == METER_DB) - { - result = (int)(pixels * - (level - min) / - (max - min)); - if(level <= min) result = 0; + if( mode == METER_DB ) { + result = (int)(pixels * (level - min) / (max - min)); + if( level <= min ) result = 0; } - else - { + else { // Not implemented anymore result = 0; } @@ -236,6 +226,7 @@ void BC_Meter::get_divisions() db_titles.remove_all_objects(); title_pixels.remove_all(); tick_pixels.remove_all(); + tick_w.remove_all(); low_division = 0; medium_division = 0; @@ -243,111 +234,104 @@ void BC_Meter::get_divisions() int current_pixel; // Create tick marks and titles in one pass - for(int current = min; current <= max; current++) - { - if(orientation == METER_VERT) - { + for( int current = min; current <= max; current++ ) { + if( orientation == METER_VERT ) { // Create tick mark current_pixel = (pixels - METER_MARGIN * 2 - 2) * (current - min) / (max - min) + 2; tick_pixels.append(current_pixel); // Create titles in selected positions - if(current == min || - current == max || - current == 0 || - (current - min > 4 && max - current > 4 && !(current % 5))) - { - int title_pixel = (pixels - - METER_MARGIN * 2) * (current - min) / (max - min); - sprintf(string, "%ld", labs(current)); + if( current == min || current == max || current == 0 || + (current - min > 4 && max - current > 4 && !(current % 5)) ) { + int title_pixel = (pixels - METER_MARGIN * 2) * + (current - min) / (max - min); + sprintf(string, "%d", (int)labs(current)); new_string = new char[strlen(string) + 1]; strcpy(new_string, string); db_titles.append(new_string); title_pixels.append(title_pixel); + tick_w.append(TICK_W1); + } + else { + tick_w.append(TICK_W2); } } - else - { + else { current_pixel = (pixels - METER_MARGIN * 2) * (current - min) / (max - min); tick_pixels.append(current_pixel); + tick_w.append(TICK_W1); // Titles not supported for horizontal } // Create color divisions - if(current == -20) - { + if( current == -20 ) { low_division = current_pixel; } else - if(current == -5) - { + if( current == -5 ) { medium_division = current_pixel; } else - if(current == 0) - { + if( current == 0 ) { high_division = current_pixel; } } -// if(orientation == METER_VERT) +// if( orientation == METER_VERT ) // printf("BC_Meter::get_divisions %d %d %d %d\n", // low_division, medium_division, high_division, pixels); } void BC_Meter::draw_titles(int flush) { - if(!use_titles) return; + if( !use_titles ) return; + int tick_xfudge = xS(1); set_font(get_resources()->meter_font); - if(orientation == METER_HORIZ) - { + if( orientation == METER_HORIZ ) { draw_top_background(parent_window, 0, 0, get_w(), get_title_w()); - for(int i = 0; i < db_titles.total; i++) - { + for( int i = 0; i < db_titles.total; i++ ) { draw_text(0, title_pixels.values[i], db_titles.values[i]); } flash(0, 0, get_w(), get_title_w(), flush); } else - if(orientation == METER_VERT) - { + if( orientation == METER_VERT ) { draw_top_background(parent_window, 0, 0, get_title_w(), get_h()); // Titles - for(int i = 0; i < db_titles.total; i++) - { - int title_y = pixels - - title_pixels.values[i]; - if(i == 0) - title_y -= get_text_descent(SMALLFONT_3D); + for( int i = 0; i < db_titles.total; i++ ) { + int title_y = pixels - title_pixels.values[i]; + if( i == 0 ) + title_y -= get_text_descent(get_resources()->meter_font); else - if(i == db_titles.total - 1) - title_y += get_text_ascent(SMALLFONT_3D); + if( i == db_titles.total - 1 ) + title_y += get_text_ascent(get_resources()->meter_font); else - title_y += get_text_ascent(SMALLFONT_3D) / 2; + title_y += get_text_ascent(get_resources()->meter_font) / 2; + int title_x = get_title_w() - TICK_W1 - tick_xfudge - + get_text_width(get_resources()->meter_font, db_titles.values[i]); set_color(get_resources()->meter_font_color); - draw_text(0, - title_y, - db_titles.values[i]); + draw_text(title_x, title_y, db_titles.values[i]); } - for(int i = 0; i < tick_pixels.total; i++) - { + for( int i = 0; i < tick_pixels.total; i++ ) { // Tick marks int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN; set_color(get_resources()->meter_font_color); - draw_line(get_title_w() - xS(10) - 1, tick_y, get_title_w() - 1, tick_y); - if(get_resources()->meter_3d) - { + draw_line(get_title_w() - tick_w.get(i) - tick_xfudge, + tick_y, get_title_w() - tick_xfudge, tick_y); + + if( get_resources()->meter_3d ) { set_color(BLACK); - draw_line(get_title_w() - xS(10), tick_y + 1, get_title_w(), tick_y + 1); + draw_line(get_title_w() - tick_w.get(i), + tick_y + 1, get_title_w(), tick_y + 1); } } @@ -358,9 +342,9 @@ void BC_Meter::draw_titles(int flush) int BC_Meter::region_pixel(int region) { VFrame **reference_images = get_resources()->xmeter_images; - int result; + int result = 0; - if(region == METER_RIGHT) + if( region == METER_RIGHT ) result = region * reference_images[0]->get_w() / 4; else result = region * reference_images[0]->get_w() / 4; @@ -377,7 +361,7 @@ int BC_Meter::region_pixels(int region) x1 = region * reference_images[0]->get_w() / 4; x2 = (region + 1) * reference_images[0]->get_w() / 4; - if(region == METER_MID) + if( region == METER_MID ) result = (x2 - x1) * 2; else result = x2 - x1; @@ -400,197 +384,258 @@ void BC_Meter::draw_face(int flush) draw_top_background(parent_window, x, 0, w, h); // printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n", -// __LINE__, -// span, -// this->w, -// get_title_w(), -// w, -// h); - - while(pixel < pixels) - { -// Select image to draw - if(pixel < level_pixel || - (pixel >= peak_pixel1 && pixel < peak_pixel2)) - { - if(pixel < low_division) - image_number = METER_GREEN; - else - if(pixel < medium_division) - image_number = METER_YELLOW; - else - if(pixel < high_division) - image_number = METER_RED; - else - image_number = METER_WHITE; - } - else - { - image_number = METER_NORMAL; - } +// __LINE__, span, this->w, get_title_w(), w, h); + if( is_gain_change ) { + int in_h = images[0]->get_h(); + int in_third = in_h / 3; + int in_third3 = in_h - in_third * 2; -// Select region of image to duplicate - if(pixel < left_pixel) - { - region = METER_LEFT; - in_start = pixel + region_pixel(region); - in_span = region_pixels(region) - (in_start - region_pixel(region)); - } - else - if(pixel < right_pixel) - { - region = METER_MID; - in_start = region_pixel(region); - in_span = region_pixels(region); - } - else - { - region = METER_RIGHT; - in_start = (pixel - right_pixel) + region_pixel(region); - in_span = region_pixels(region) - (in_start - region_pixel(region));; +// printf("BC_Meter::draw_face %d level=%f level_pixel=%d high_division=%d\n", +// __LINE__, level, level_pixel, high_division); + + +// fudge a line when no gain change + if( level_pixel == high_division ) { + level_pixel += 1; } -//printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span); - if(in_span > 0) - { -// Clip length to peaks - if(pixel < level_pixel && pixel + in_span > level_pixel) - in_span = level_pixel - pixel; - else - if(pixel < peak_pixel1 && pixel + in_span > peak_pixel1) - in_span = peak_pixel1 - pixel; - else - if(pixel < peak_pixel2 && pixel + in_span > peak_pixel2) - in_span = peak_pixel2 - pixel; + while( pixel < pixels ) { +// Select image to draw & extents + if( level_pixel < high_division ) { +// always vertical + if( pixel < level_pixel ) { + image_number = METER_NORMAL; + in_span = level_pixel - pixel; + } + else + if( pixel < high_division ) { + image_number = METER_RED; + in_span = high_division - pixel; + } + else { + image_number = METER_NORMAL; + in_span = pixels - pixel; + } + } + else { +// determine pixel range & image to draw + if( pixel < high_division ) { + image_number = METER_NORMAL; + in_span = high_division - pixel; + } + else + if( pixel < level_pixel ) { + image_number = METER_GREEN; + in_span = level_pixel - pixel; + } + else { + image_number = METER_NORMAL; + in_span = pixels - pixel; + } + } -// Clip length to color changes - if(image_number == METER_GREEN && pixel + in_span > low_division) - in_span = low_division - pixel; +// determine starting point in source to draw +// draw starting section + if( pixel == 0 ) { + in_start = 0; + } else - if(image_number == METER_YELLOW && pixel + in_span > medium_division) - in_span = medium_division - pixel; +// draw middle section + if( pixels - pixel > in_third3 ) { + in_start = in_third; + } else - if(image_number == METER_RED && pixel + in_span > high_division) - in_span = high_division - pixel; +// draw last section + { + in_start = in_third * 2; + } -// Clip length to regions - if(pixel < left_pixel && pixel + in_span > left_pixel) - in_span = left_pixel - pixel; +// clamp the region to the source dimensions + if( in_start < in_third * 2 ) { + if( in_span > in_third ) { + in_span = in_third; + } + } else - if(pixel < right_pixel && pixel + in_span > right_pixel) - in_span = right_pixel - pixel; +// last segment + if( pixels - pixel < in_third3 ) { + in_span = pixels - pixel; + in_start = in_h - in_span; + } -//printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span); -//printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span); - if(orientation == METER_HORIZ) - { - draw_pixmap(images[image_number], - pixel, - x, - in_span + 1, - get_h(), - in_start, - 0); +// printf("BC_Meter::draw_face %d dst_y=%d src_y=%d" +// " pixels=%d pixel=%d in_h=%d in_start=%d in_span=%d in_third=%d in_third3=%d\n", +// __LINE__, get_h() - pixel - in_span, in_h - in_start - in_span, +// pixels, pixel, in_h, in_start, in_span, in_third, in_third3); + draw_pixmap(images[image_number], x, get_h() - pixel - in_span, + get_w(), in_span + 1, 0, in_h - in_start - in_span); + pixel += in_span; + } + } + else { + while( pixel < pixels ) { +// Select image to draw + if( pixel < level_pixel || + (pixel >= peak_pixel1 && pixel < peak_pixel2) ) { + if( pixel < low_division ) + image_number = METER_GREEN; + else + if( pixel < medium_division ) + image_number = METER_YELLOW; + else + if( pixel < high_division ) + image_number = METER_RED; + else + image_number = METER_WHITE; + } + else { + image_number = METER_NORMAL; + } + +// Select region of image to duplicate + if( pixel < left_pixel ) { + region = METER_LEFT; + in_start = pixel + region_pixel(region); + in_span = region_pixels(region) - (in_start - region_pixel(region)); } else - { -//printf("BC_Meter::draw_face %d %d\n", __LINE__, span); - if(span < 0) - { - draw_pixmap(images[image_number], - x, - get_h() - pixel - in_span, - get_w(), - in_span + 1, - 0, - images[image_number]->get_h() - in_start - in_span); - } - else - { - int total_w = get_w() - x; - int third = images[image_number]->get_w() / 3 + 1; - - - for(int x1 = 0; x1 < total_w; x1 += third) - { - int in_x = 0; - int in_w = third; - if(x1 >= third) in_x = third; - if(x1 >= total_w - third) - { - in_x = images[image_number]->get_w() - - (total_w - x1); - in_w = total_w - x1; - } + if( pixel < right_pixel ) { + region = METER_MID; + in_start = region_pixel(region); + in_span = region_pixels(region); + } + else { + region = METER_RIGHT; + in_start = (pixel - right_pixel) + region_pixel(region); + in_span = region_pixels(region) - (in_start - region_pixel(region));; + } - int in_y = images[image_number]->get_h() - in_start - in_span; -//printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span); + //printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span); + if( in_span > 0 ) { + // Clip length to peaks + if( pixel < level_pixel && pixel + in_span > level_pixel ) + in_span = level_pixel - pixel; + else + if( pixel < peak_pixel1 && pixel + in_span > peak_pixel1 ) + in_span = peak_pixel1 - pixel; + else + if( pixel < peak_pixel2 && pixel + in_span > peak_pixel2 ) + in_span = peak_pixel2 - pixel; + // Clip length to color changes + if( image_number == METER_GREEN && pixel + in_span > low_division ) + in_span = low_division - pixel; + else + if( image_number == METER_YELLOW && pixel + in_span > medium_division ) + in_span = medium_division - pixel; + else + if( image_number == METER_RED && pixel + in_span > high_division ) + in_span = high_division - pixel; + // Clip length to regions + if( pixel < left_pixel && pixel + in_span > left_pixel ) + in_span = left_pixel - pixel; + else + if( pixel < right_pixel && pixel + in_span > right_pixel ) + in_span = right_pixel - pixel; + + //printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span); + //printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span); + if( orientation == METER_HORIZ ) { + draw_pixmap(images[image_number], pixel, + x, in_span + 1, get_h(), in_start, 0); + } + else { + //printf("BC_Meter::draw_face %d %d\n", __LINE__, span); + if( span < 0 ) { draw_pixmap(images[image_number], - x + x1, get_h() - pixel - in_span, - in_w, in_span + 1, in_x, in_y); + x, get_h() - pixel - in_span, + get_w(), in_span + 1, 0, + images[image_number]->get_h() - in_start - in_span); + } + else { + int total_w = get_w() - x; + int third = images[image_number]->get_w() / 3 + 1; + + + for( int x1 = 0; x1 < total_w; x1 += third ) { + int in_x = 0; + int in_w = third; + if( x1 >= third ) in_x = third; + if( x1 >= total_w - third ) { + in_x = images[image_number]->get_w() - + (total_w - x1); + in_w = total_w - x1; + } + + int in_y = images[image_number]->get_h() - in_start - in_span; + //printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span); + + + draw_pixmap(images[image_number], + x + x1, get_h() - pixel - in_span, + in_w, in_span + 1, in_x, in_y); + } } } - } - pixel += in_span; - } - else - { -// Sanity check - break; + pixel += in_span; + } + else { + // Sanity check + break; + } } } - if(downmix) { - if(orientation == METER_HORIZ) + if( is_downmix ) { + if( orientation == METER_HORIZ ) draw_pixmap(images[METER_DOWNMIX], 0, 0); else - draw_pixmap(images[METER_DOWNMIX], x, - get_h() - images[METER_DOWNMIX]->get_h()-1); + draw_pixmap(images[METER_DOWNMIX], + x, get_h() - images[METER_DOWNMIX]->get_h() - 1); } - - if(over_timer) - { - if(orientation == METER_HORIZ) + if( over_timer ) { + if( orientation == METER_HORIZ ) draw_pixmap(images[METER_OVER], xS(20), yS(2)); else - draw_pixmap(images[METER_OVER], x, get_h() - yS(100)); + draw_pixmap(images[METER_OVER], + x + xS(2), get_h() - yS(100)); over_timer--; } - if(orientation == METER_HORIZ) + if( orientation == METER_HORIZ ) flash(0, 0, pixels, get_h(), flush); else flash(x, 0, w, pixels, flush); } -int BC_Meter::update(float new_value, int over, int dmix) +int BC_Meter::update(float new_value, int over, int downmix) { peak_timer++; - if(mode == METER_DB) - { - if(new_value == 0) + if( mode == METER_DB ) { + if( new_value == 0 ) level = min; else - level = db.todb(new_value); // db value + level = DB::todb(new_value); // db value } - if(level > peak || peak_timer > peak_delay) - { + + if( is_gain_change && fabs(level) > fabs(peak) || + !is_gain_change && level > peak || + peak_timer > peak_delay ) { peak = level; peak_timer = 0; } -// if(orientation == METER_HORIZ) +// if( orientation == METER_HORIZ ) // printf("BC_Meter::update %f\n", level); - if(over) over_timer = over_delay; + if( over ) over_timer = over_delay; // only draw if window is visible - if(dmix >= 0) downmix = dmix; - + if( downmix >= 0 ) + is_downmix = downmix; draw_face(1); return 0; } diff --git a/cinelerra-5.1/guicast/bcmeter.h b/cinelerra-5.1/guicast/bcmeter.h index 9a5cf807..e43c52b7 100644 --- a/cinelerra-5.1/guicast/bcmeter.h +++ b/cinelerra-5.1/guicast/bcmeter.h @@ -1,7 +1,6 @@ - /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2008-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef BCMETER_H @@ -35,20 +33,20 @@ // Distance from subwindow border to top and bottom tick mark #define METER_MARGIN 0 +// tick width +#define TICK_W1 xS(10) +#define TICK_W2 xS(5) class BC_Meter : public BC_SubWindow { public: - BC_Meter(int x, - int y, - int orientation, - int pixels, - int min, /* = -40, */ - int max, + BC_Meter(int x, int y, int orientation, int pixels, + int min, /* = -40, */ int max, int mode, /* = METER_DB, */ int use_titles, /* = 0, */ - int span, /* = -1 width for vertical mode only */ - int downmix = 0); + int span /* = -1 width for vertical mode only */, + int is_downmix = 0, + int is_gain_change = 0); virtual ~BC_Meter(); int initialize(); @@ -64,12 +62,11 @@ public: static int get_title_w(); static int get_meter_w(); - int update(float new_value, int over, int dmix=-1); - int reposition_window(int x, - int y, + int update(float new_value, int over, int downmix=-1); + int reposition_window(int x, int y, int span /* = -1 for vertical mode only */, int pixels); - int reset(int dmix=-1); + int reset(int downmix=-1); int reset_over(); int change_format(int mode, int min, int max); @@ -91,25 +88,22 @@ private: int use_titles; // Tick mark positions ArrayList tick_pixels; +// Tick widths + ArrayList tick_w; // Title positions ArrayList title_pixels; ArrayList 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 diff --git a/cinelerra-5.1/guicast/bcpot.C b/cinelerra-5.1/guicast/bcpot.C index b979f915..6b91b33c 100644 --- a/cinelerra-5.1/guicast/bcpot.C +++ b/cinelerra-5.1/guicast/bcpot.C @@ -44,6 +44,8 @@ BC_Pot::BC_Pot(int x, int y, VFrame **data) BC_Pot::~BC_Pot() { + for(int i = 0; i < POT_STATES; i++) + if(images[i]) delete images[i]; } int BC_Pot::calculate_h() diff --git a/cinelerra-5.1/guicast/bcwindowbase.C b/cinelerra-5.1/guicast/bcwindowbase.C index 0d9f95d4..e979d5ca 100644 --- a/cinelerra-5.1/guicast/bcwindowbase.C +++ b/cinelerra-5.1/guicast/bcwindowbase.C @@ -624,7 +624,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title } if(!hidden) show_window(); - + init_glyphs(); } draw_background(0, 0, this->w, this->h); @@ -2427,6 +2427,30 @@ xft_init_lock.unlock(); #endif // HAVE_XFT } +void BC_WindowBase::init_glyphs() +{ +// draw all ascii char glyphs +// There are problems with some/my graphics boards/drivers +// which cause some glyphs to be munged if draws occur while +// the font is being loaded. This code fills the font caches +// by drawing all the ascii glyphs before the system starts. +// Not a fix, but much better than nothing. + static int inited = 0; + if( inited ) return; + inited = 1; + int cur_font = current_font; +// locale encodings, needed glyphs to be preloaded + const char *text = _( // ascii 0x20...0x7e + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"); + for( int font=SMALLFONT; font<=LARGEFONT; ++font ) { + set_font(font); + draw_text(5,5, text, 0); + } + set_font(cur_font); +} + void BC_WindowBase::init_im() { XIMStyles *xim_styles; diff --git a/cinelerra-5.1/guicast/bcwindowbase.h b/cinelerra-5.1/guicast/bcwindowbase.h index 36add9c7..153aab37 100644 --- a/cinelerra-5.1/guicast/bcwindowbase.h +++ b/cinelerra-5.1/guicast/bcwindowbase.h @@ -576,6 +576,7 @@ private: int allocate_color_table(); int init_gc(); int init_fonts(); + void init_glyphs(); void init_xft(); void init_im(); void finit_im(); diff --git a/cinelerra-5.1/guicast/linklist.h b/cinelerra-5.1/guicast/linklist.h index 0c580def..3accf281 100644 --- a/cinelerra-5.1/guicast/linklist.h +++ b/cinelerra-5.1/guicast/linklist.h @@ -28,6 +28,7 @@ public: void remove_pointer(ListItem *item); TYPE *append(TYPE *new_item); TYPE *append() { return append(new TYPE()); } + void destroy() { while(last) delete last; } TYPE *insert_before(TYPE *here, TYPE *item); TYPE *insert_before(TYPE *here) { return insert_before(here, new TYPE()); } TYPE *insert_after(TYPE *here, TYPE *item); @@ -45,8 +46,9 @@ public: void swap(TYPE *item1, TYPE *item2); void sort(TYPE *ap=0, TYPE *bp=0) { return sort(cmpr,ap,bp); } void sort(int (*cmp)(TYPE *a, TYPE *b), TYPE *ap=0, TYPE *bp=0); + void concat(List &b); List() { first = last = 0; } - virtual ~List() { while(last) delete last; } + virtual ~List() { destroy(); } }; // convenience macros @@ -132,4 +134,14 @@ void List::sort(int (*cmpr)(TYPE *a, TYPE *b), TYPE *ll, TYPE *rr) } } +template +void List::concat(List &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 diff --git a/cinelerra-5.1/guicast/units.C b/cinelerra-5.1/guicast/units.C index 9c2f1d27..e0a170dc 100644 --- a/cinelerra-5.1/guicast/units.C +++ b/cinelerra-5.1/guicast/units.C @@ -90,14 +90,14 @@ int Freq::fromfreq() int i = 0; while( i + * Copyright (C) 2008-2019 Adam Williams * * 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 @@ -20,35 +19,31 @@ */ #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 #include - - - - REGISTER_PLUGIN(CompressorEffect) - - - - - // More potential compressor algorithms: -// Use single reaction time parameter. Negative reaction time uses -// readahead. Positive reaction time uses slope. +// Use single readahead time parameter. Negative readahead time uses +// readahead. Positive readahead time uses slope. // Smooth input stage if readahead. // Determine slope from current smoothed sample to every sample in readahead area. @@ -58,7 +53,7 @@ REGISTER_PLUGIN(CompressorEffect) // Smooth input stage if not readahead. // For every sample, calculate slope needed to reach current sample from -// current smoothed value in the reaction time. If higher than current slope, +// current smoothed value in the readahead time. If higher than current slope, // make it the current slope and count number of samples remaining until it is // reached. If this count is met and no higher slopes are found, base slope // on current sample when count is met. @@ -66,123 +61,97 @@ REGISTER_PLUGIN(CompressorEffect) // Gain stage. // For every sample, calculate gain from smoothed input value. - - - - CompressorEffect::CompressorEffect(PluginServer *server) : PluginAClient(server) { - reset(); + input_buffer = 0; + input_size = 0; + input_allocated = 0; + input_start = 0; + need_reconfigure = 1; + engine = 0; } CompressorEffect::~CompressorEffect() { - - delete_dsp(); - levels.remove_all(); -} - -void CompressorEffect::delete_dsp() -{ - if(input_buffer) - { - for(int i = 0; i < PluginClient::total_in_buffers; i++) + if( input_buffer ) { + for( int i=0; i input_allocated ) { + Samples **new_input_buffer = new Samples*[channels]; + for( int i=0; iget_data(), + input_buffer[i]->get_data(), + input_size * sizeof(double)); + delete input_buffer[i]; + } + if( input_buffer ) delete [] input_buffer; - next_target = 1.0; - previous_target = 1.0; - target_samples = 1; - target_current_sample = -1; - current_value = 1.0; + input_allocated = size; + input_buffer = new_input_buffer; + } } const char* CompressorEffect::plugin_title() { return N_("Compressor"); } int CompressorEffect::is_realtime() { return 1; } int CompressorEffect::is_multichannel() { return 1; } - - void CompressorEffect::read_data(KeyFrame *keyframe) { FileXML input; - input.set_shared_input(keyframe->xbuf); + BandConfig *band_config = &config.bands[0]; + input.set_shared_input(keyframe->xbuf); int result = 0; - config.levels.remove_all(); - while(!result) - { - result = input.read_tag(); - - if(!result) - { - if(input.tag.title_is("COMPRESSOR")) - { - config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len); - config.decay_len = input.tag.get_property("DECAY_LEN", config.decay_len); - config.trigger = input.tag.get_property("TRIGGER", config.trigger); - config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only); - config.input = input.tag.get_property("INPUT", config.input); - } - else - if(input.tag.title_is("LEVEL")) - { - double x = input.tag.get_property("X", (double)0); - double y = input.tag.get_property("Y", (double)0); - compressor_point_t point = { x, y }; - - config.levels.append(point); - } + while( !(result=input.read_tag()) ) { + if( input.tag.title_is("COMPRESSOR") ) { +// band_config->readahead_len = input.tag.get_property("READAHEAD_LEN", band_config->readahead_len); + band_config->attack_len = input.tag.get_property("ATTACK_LEN", band_config->attack_len); + band_config->release_len = input.tag.get_property("RELEASE_LEN", band_config->release_len); + config.trigger = input.tag.get_property("TRIGGER", config.trigger); + config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only); + config.input = input.tag.get_property("INPUT", config.input); + } + else if( input.tag.title_is("COMPRESSORBAND") ) { + band_config->read_data(&input, 0); } } + config.boundaries(); } void CompressorEffect::save_data(KeyFrame *keyframe) { FileXML output; + BandConfig *band_config = &config.bands[0]; output.set_shared_output(keyframe->xbuf); output.tag.set_title("COMPRESSOR"); output.tag.set_property("TRIGGER", config.trigger); - output.tag.set_property("REACTION_LEN", config.reaction_len); - output.tag.set_property("DECAY_LEN", config.decay_len); output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only); output.tag.set_property("INPUT", config.input); + output.tag.set_property("ATTACK_LEN", band_config->attack_len); + output.tag.set_property("RELEASE_LEN", band_config->release_len); output.append_tag(); + output.append_newline(); + + band_config->save_data(&output, 0, 0); output.tag.set_title("/COMPRESSOR"); output.append_tag(); output.append_newline(); - - - for(int i = 0; i < config.levels.total; i++) - { - output.tag.set_title("LEVEL"); - output.tag.set_property("X", config.levels.values[i].x); - output.tag.set_property("Y", config.levels.values[i].y); - - output.append_tag(); - output.tag.set_title("/LEVEL"); - output.append_tag(); - output.append_newline(); - } - output.terminate_string(); } @@ -190,469 +159,184 @@ void CompressorEffect::save_data(KeyFrame *keyframe) void CompressorEffect::update_gui() { if( !thread ) return; - CompressorWindow *window = (CompressorWindow*)thread->window; -// load_configuration,read_data deletes levels - window->lock_window("CompressorEffect::update_gui"); - if( load_configuration() ) +// user can't change levels when loading configuration + thread->window->lock_window("CompressorEffect::update_gui"); + CompressorWindow *window = (CompressorWindow *)thread->window; +// Can't update points if the user is editing + int reconfigured = window->canvas->is_dragging() ? 0 : + load_configuration(); + if( reconfigured ) window->update(); +// delete frames up to current tracking position + int dir = get_tracking_direction() == PLAY_FORWARD ? 1 : -1; + double tracking_position = get_tracking_position(); + CompressorGainFrame *gain_frame = (CompressorGainFrame *) + get_gui_frame(tracking_position, dir); + if( gain_frame ) { + window->update_meter(gain_frame); + delete gain_frame; + } window->unlock_window(); } +void CompressorEffect::render_stop() +{ + if( !thread ) return; + thread->window->lock_window("CompressorEffect::render_stop"); + CompressorWindow *window = (CompressorWindow *)thread->window; + window->in->reset(); + window->gain_change->update(1, 0); + window->unlock_window(); +} LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig) NEW_WINDOW_MACRO(CompressorEffect, CompressorWindow) - - - -int CompressorEffect::process_buffer(int64_t size, - Samples **buffer, - int64_t start_position, - int sample_rate) +int CompressorEffect::process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate) { + int channels = PluginClient::total_in_buffers; + BandConfig *band_config = &config.bands[0]; + int sign = get_direction() == PLAY_REVERSE ? -1 : 1; load_configuration(); -// Calculate linear transfer from db - levels.remove_all(); - for(int i = 0; i < config.levels.total; i++) - { - levels.append(); - levels.values[i].x = DB::fromdb(config.levels.values[i].x); - levels.values[i].y = DB::fromdb(config.levels.values[i].y); +// restart after seeking + if( last_position != start_position ) { + last_position = start_position; + if( engine ) + engine->reset(); + input_size = 0; + input_start = start_position; } - min_x = DB::fromdb(config.min_db); - min_y = DB::fromdb(config.min_db); - max_x = 1.0; - max_y = 1.0; - - - int reaction_samples = (int)(config.reaction_len * sample_rate + 0.5); - int decay_samples = (int)(config.decay_len * sample_rate + 0.5); - int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1); - CLAMP(reaction_samples, -1000000, 1000000); - CLAMP(decay_samples, reaction_samples, 1000000); - CLAMP(decay_samples, 1, 1000000); - if(labs(reaction_samples) < 1) reaction_samples = 1; - if(labs(decay_samples) < 1) decay_samples = 1; - - int total_buffers = get_total_buffers(); - if(reaction_samples >= 0) - { - if(target_current_sample < 0) target_current_sample = reaction_samples; - for(int i = 0; i < total_buffers; i++) - { - read_samples(buffer[i], - i, - sample_rate, - start_position, - size); - } - - double current_slope = (next_target - previous_target) / - reaction_samples; - double *trigger_buffer = buffer[trigger]->get_data(); - for(int i = 0; i < size; i++) - { -// Get slope required to reach current sample from smoothed sample over reaction -// length. - double sample = 0.; - switch(config.input) - { - case CompressorConfig::MAX: - { - double max = 0; - for(int j = 0; j < total_buffers; j++) - { - sample = fabs(buffer[j]->get_data()[i]); - if(sample > max) max = sample; - } - sample = max; - break; - } - - case CompressorConfig::TRIGGER: - sample = fabs(trigger_buffer[i]); - break; - - case CompressorConfig::SUM: - { - double max = 0; - for(int j = 0; j < total_buffers; j++) - { - sample = fabs(buffer[j]->get_data()[i]); - max += sample; - } - sample = max; - break; - } - } +// Calculate linear transfer from db + int nbands = band_config->levels.size(); + while( levels.size() < nbands ) levels.append(); - double new_slope = (sample - current_value) / - reaction_samples; - -// Slope greater than current slope - if(new_slope >= current_slope && - (current_slope >= 0 || - new_slope >= 0)) - { - next_target = sample; - previous_target = current_value; - target_current_sample = 0; - target_samples = reaction_samples; - current_slope = new_slope; - } - else - if(sample > next_target && current_slope < 0) - { - next_target = sample; - previous_target = current_value; - target_current_sample = 0; - target_samples = decay_samples; - current_slope = (sample - current_value) / decay_samples; - } -// Current smoothed sample came up without finding higher slope - if(target_current_sample >= target_samples) - { - next_target = sample; - previous_target = current_value; - target_current_sample = 0; - target_samples = decay_samples; - current_slope = (sample - current_value) / decay_samples; - } + for( int i=0; ilevels.total; ++i ) { + levels.values[i].x = DB::fromdb(band_config->levels.values[i].x); + levels.values[i].y = DB::fromdb(band_config->levels.values[i].y); + } +// min_x = DB::fromdb(config.min_db); +// min_y = DB::fromdb(config.min_db); +// max_x = 1.0; +// max_y = 1.0; -// Update current value and store gain - current_value = (next_target * target_current_sample + - previous_target * (target_samples - target_current_sample)) / - target_samples; - target_current_sample++; + int attack_samples; + int release_samples; + int preview_samples; - if(config.smoothing_only) - { - for(int j = 0; j < total_buffers; j++) - buffer[j]->get_data()[i] = current_value; - } - else - { - double gain = calculate_gain(current_value); - for(int j = 0; j < total_buffers; j++) - { - buffer[j]->get_data()[i] *= gain; - } - } - } + if( !engine ) { + engine = new CompressorEngine(&config, 0); + engine->gui_frame_samples = sample_rate / TRACKING_RATE + 1; } - else - { - if(target_current_sample < 0) target_current_sample = target_samples; - int64_t preview_samples = -reaction_samples; + engine->calculate_ranges(&attack_samples, &release_samples, + &preview_samples, sample_rate); // Start of new buffer is outside the current buffer. Start buffer over. - if(start_position < input_start || - start_position >= input_start + input_size) - { - input_size = 0; - input_start = start_position; - } - else + if( get_direction() == PLAY_FORWARD && + (start_position < input_start || + start_position >= input_start + input_size) + || + get_direction() == PLAY_REVERSE && + (start_position > input_start || + start_position <= input_start - input_size) ) { +// printf("CompressorEffect::process_buffer %d start_position=%ld input_start=%ld input_size=%ld\n", +// __LINE__, start_position, input_start, input_size); + input_size = 0; + input_start = start_position; + } + else // Shift current buffer so the buffer starts on start_position - if(start_position > input_start && - start_position < input_start + input_size) - { - if(input_buffer) - { - int len = input_start + input_size - start_position; - for(int i = 0; i < total_buffers; i++) - { - memcpy(input_buffer[i]->get_data(), - input_buffer[i]->get_data() + (start_position - input_start), - len * sizeof(double)); - } - input_size = len; - input_start = start_position; + if( get_direction() == PLAY_FORWARD && + start_position > input_start && + start_position < input_start + input_size + || + get_direction() == PLAY_REVERSE && + start_position < input_start && + start_position > input_start - input_size ) { + if( input_buffer ) { + int len, offset; + if( get_direction() == PLAY_FORWARD ) { + len = input_start + input_size - start_position; + offset = start_position - input_start; } - } - -// Expand buffer to handle preview size - if(size + preview_samples > input_allocated) - { - Samples **new_input_buffer = new Samples*[total_buffers]; - for(int i = 0; i < total_buffers; i++) - { - new_input_buffer[i] = new Samples(size + preview_samples); - if(input_buffer) - { - memcpy(new_input_buffer[i]->get_data(), - input_buffer[i]->get_data(), - input_size * sizeof(double)); - delete input_buffer[i]; - } + else { + len = start_position - (input_start - input_size); + offset = input_start - start_position; } - if(input_buffer) delete [] input_buffer; - input_allocated = size + preview_samples; - input_buffer = new_input_buffer; - } - -// Append data to input buffer to construct readahead area. -#define MAX_FRAGMENT_SIZE 131072 - while(input_size < size + preview_samples) - { - int fragment_size = MAX_FRAGMENT_SIZE; - if(fragment_size + input_size > size + preview_samples) - fragment_size = size + preview_samples - input_size; - for(int i = 0; i < total_buffers; i++) - { - input_buffer[i]->set_offset(input_size); -//printf("CompressorEffect::process_buffer %d %p %d\n", __LINE__, input_buffer[i], input_size); - read_samples(input_buffer[i], - i, - sample_rate, - input_start + input_size, - fragment_size); - input_buffer[i]->set_offset(0); + for( int i = 0; i < channels; i++ ) { + memcpy(input_buffer[i]->get_data(), + input_buffer[i]->get_data() + offset, + len * sizeof(double)); } - input_size += fragment_size; + input_size = len; + input_start = start_position; } + } - - double current_slope = (next_target - previous_target) / - target_samples; - double *trigger_buffer = input_buffer[trigger]->get_data(); - for(int i = 0; i < size; i++) - { -// Get slope from current sample to every sample in preview_samples. -// Take highest one or first one after target_samples are up. - -// For optimization, calculate the first slope we really need. -// Assume every slope up to the end of preview_samples has been calculated and -// found <= to current slope. - int first_slope = preview_samples - 1; -// Need new slope immediately - if(target_current_sample >= target_samples) - first_slope = 1; - for(int j = first_slope; - j < preview_samples; - j++) - { - double sample = 0.; - switch(config.input) - { - case CompressorConfig::MAX: - { - double max = 0; - for(int k = 0; k < total_buffers; k++) - { - sample = fabs(input_buffer[k]->get_data()[i + j]); - if(sample > max) max = sample; - } - sample = max; - break; - } - - case CompressorConfig::TRIGGER: - sample = fabs(trigger_buffer[i + j]); - break; - - case CompressorConfig::SUM: - { - double max = 0; - for(int k = 0; k < total_buffers; k++) - { - sample = fabs(input_buffer[k]->get_data()[i + j]); - max += sample; - } - sample = max; - break; - } - } - - - - - - - double new_slope = (sample - current_value) / - j; -// Got equal or higher slope - if(new_slope >= current_slope && - (current_slope >= 0 || - new_slope >= 0)) - { - target_current_sample = 0; - target_samples = j; - current_slope = new_slope; - next_target = sample; - previous_target = current_value; - } - else - if(sample > next_target && current_slope < 0) - { - target_current_sample = 0; - target_samples = decay_samples; - current_slope = (sample - current_value) / - decay_samples; - next_target = sample; - previous_target = current_value; - } - -// Hit end of current slope range without finding higher slope - if(target_current_sample >= target_samples) - { - target_current_sample = 0; - target_samples = decay_samples; - current_slope = (sample - current_value) / decay_samples; - next_target = sample; - previous_target = current_value; - } - } - -// Update current value and multiply gain - current_value = (next_target * target_current_sample + - previous_target * (target_samples - target_current_sample)) / - target_samples; -//buffer[0][i] = current_value; - target_current_sample++; - - if(config.smoothing_only) - { - for(int j = 0; j < total_buffers; j++) - { - buffer[j]->get_data()[i] = current_value; - } - } - else - { - double gain = calculate_gain(current_value); - for(int j = 0; j < total_buffers; j++) - { - buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain; - } +// Expand buffer to handle preview size + if( size + preview_samples > input_allocated ) { + Samples **new_input_buffer = new Samples*[channels]; + for( int i = 0; i < channels; i++ ) { + new_input_buffer[i] = new Samples(size + preview_samples); + if( input_buffer ) { + memcpy(new_input_buffer[i]->get_data(), + input_buffer[i]->get_data(), + input_size * sizeof(double)); + delete input_buffer[i]; } } + if( input_buffer ) delete [] input_buffer; - - + input_allocated = size + preview_samples; + input_buffer = new_input_buffer; } - - - - - return 0; -} - -double CompressorEffect::calculate_output(double x) -{ - if(x > 0.999) return 1.0; - - for(int i = levels.total - 1; i >= 0; i--) - { - if(levels.values[i].x <= x) - { - if(i < levels.total - 1) - { - return levels.values[i].y + - (x - levels.values[i].x) * - (levels.values[i + 1].y - levels.values[i].y) / - (levels.values[i + 1].x - levels.values[i].x); - } - else - { - return levels.values[i].y + - (x - levels.values[i].x) * - (max_y - levels.values[i].y) / - (max_x - levels.values[i].x); - } +// Append data to input buffer to construct readahead area. + if( input_size < size + preview_samples ) { + int fragment_size = size + preview_samples - input_size; + for( int i = 0; i < channels; i++ ) { + input_buffer[i]->set_offset(input_size); + read_samples(input_buffer[i], i, sample_rate, + input_start + input_size * sign, fragment_size); + input_buffer[i]->set_offset(0); } + input_size += fragment_size; } - if(levels.total) - { - return min_y + - (x - min_x) * - (levels.values[0].y - min_y) / - (levels.values[0].x - min_x); - } - else - return x; -} + engine->process(buffer, input_buffer, size, + sample_rate, PluginClient::total_in_buffers, start_position); + double start_pos = (double)start_position / sample_rate; + for( int i = 0; i < engine->gui_gains.size(); i++ ) { + CompressorGainFrame *gain_frame = new CompressorGainFrame(); + gain_frame->position = start_pos + sign*engine->gui_offsets[i]; + gain_frame->gain = engine->gui_gains[i]; + gain_frame->level = engine->gui_levels[i]; + add_gui_frame(gain_frame); + } -double CompressorEffect::calculate_gain(double input) -{ -// double x_db = DB::todb(input); -// double y_db = config.calculate_db(x_db); -// double y_linear = DB::fromdb(y_db); - double y_linear = calculate_output(input); - double gain; - if(input != 0) - gain = y_linear / input; - else - gain = 100000; - return gain; + last_position += sign * size; + return 0; } - - - - - - - - CompressorConfig::CompressorConfig() + : CompressorConfigBase(1) { - reaction_len = 1.0; - min_db = -80.0; - min_x = min_db; - min_y = min_db; - max_x = 0; - max_y = 0; - trigger = 0; - input = CompressorConfig::TRIGGER; - smoothing_only = 0; - decay_len = 1.0; } void CompressorConfig::copy_from(CompressorConfig &that) { - this->reaction_len = that.reaction_len; - this->decay_len = that.decay_len; - this->min_db = that.min_db; - this->min_x = that.min_x; - this->min_y = that.min_y; - this->max_x = that.max_x; - this->max_y = that.max_y; - this->trigger = that.trigger; - this->input = that.input; - this->smoothing_only = that.smoothing_only; - levels.remove_all(); - for(int i = 0; i < that.levels.total; i++) - this->levels.append(that.levels.values[i]); + CompressorConfigBase::copy_from(that); } int CompressorConfig::equivalent(CompressorConfig &that) { - if(!EQUIV(this->reaction_len, that.reaction_len) || - !EQUIV(this->decay_len, that.decay_len) || - this->trigger != that.trigger || - this->input != that.input || - this->smoothing_only != that.smoothing_only) - return 0; - if(this->levels.total != that.levels.total) return 0; - for(int i = 0; - i < this->levels.total && i < that.levels.total; - i++) - { - compressor_point_t *this_level = &this->levels.values[i]; - compressor_point_t *that_level = &that.levels.values[i]; - if(!EQUIV(this_level->x, that_level->x) || - !EQUIV(this_level->y, that_level->y)) - return 0; - } + return CompressorConfigBase::equivalent(that); return 1; } @@ -665,395 +349,149 @@ void CompressorConfig::interpolate(CompressorConfig &prev, copy_from(prev); } -int CompressorConfig::total_points() -{ - if(!levels.total) - return 1; - else - return levels.total; -} - -void CompressorConfig::dump() -{ - printf("CompressorConfig::dump\n"); - for(int i = 0; i < levels.total; i++) - { - printf(" %f %f\n", levels.values[i].x, levels.values[i].y); - } -} - - -double CompressorConfig::get_y(int number) -{ - if(!levels.total) - return 1.0; - else - if(number >= levels.total) - return levels.values[levels.total - 1].y; - else - return levels.values[number].y; -} - -double CompressorConfig::get_x(int number) -{ - if(!levels.total) - return 0.0; - else - if(number >= levels.total) - return levels.values[levels.total - 1].x; - else - return levels.values[number].x; -} - -double CompressorConfig::calculate_db(double x) -{ - if(x > -0.001) return 0.0; - - for(int i = levels.total - 1; i >= 0; i--) - { - if(levels.values[i].x <= x) - { - if(i < levels.total - 1) - { - return levels.values[i].y + - (x - levels.values[i].x) * - (levels.values[i + 1].y - levels.values[i].y) / - (levels.values[i + 1].x - levels.values[i].x); - } - else - { - return levels.values[i].y + - (x - levels.values[i].x) * - (max_y - levels.values[i].y) / - (max_x - levels.values[i].x); - } - } - } - - if(levels.total) - { - return min_y + - (x - min_x) * - (levels.values[0].y - min_y) / - (levels.values[0].x - min_x); - } - else - return x; -} - - -int CompressorConfig::set_point(double x, double y) -{ - for(int i = levels.total - 1; i >= 0; i--) - { - if(levels.values[i].x < x) - { - levels.append(); - i++; - for(int j = levels.total - 2; j >= i; j--) - { - levels.values[j + 1] = levels.values[j]; - } - levels.values[i].x = x; - levels.values[i].y = y; - - return i; - } - } - - levels.append(); - for(int j = levels.total - 2; j >= 0; j--) - { - levels.values[j + 1] = levels.values[j]; - } - levels.values[0].x = x; - levels.values[0].y = y; - return 0; -} - -void CompressorConfig::remove_point(int number) -{ - for(int j = number; j < levels.total - 1; j++) - { - levels.values[j] = levels.values[j + 1]; - } - levels.remove(); -} - -void CompressorConfig::optimize() +CompressorWindow::CompressorWindow(CompressorEffect *plugin) + : PluginClientWindow(plugin, xS(650), yS(480), xS(650), yS(480), 0) { - int done = 0; - - while(!done) - { - done = 1; - - - for(int i = 0; i < levels.total - 1; i++) - { - if(levels.values[i].x >= levels.values[i + 1].x) - { - done = 0; - for(int j = i + 1; j < levels.total - 1; j++) - { - levels.values[j] = levels.values[j + 1]; - } - levels.remove(); - } - } - - } + this->plugin = plugin; } - - - - - - - - - - - - - - - - - - - - - - - - - - -CompressorWindow::CompressorWindow(CompressorEffect *plugin) - : PluginClientWindow(plugin, - xS(650), - yS(480), - xS(650), - yS(480), - 0) +CompressorWindow::~CompressorWindow() { - this->plugin = plugin; +// delete readahead; + delete attack; + delete release; + delete trigger; + delete x_text; + delete y_text; } void CompressorWindow::create_objects() { - int xs10 = xS(10), xs20 = xS(20), xs50 = xS(50), xs110 = xS(110); - int ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys60 = yS(60), ys70 = yS(70); - int x = xS(35), y = yS(10); + int margin = client->get_theme()->widget_border; + int x = margin, y = margin; int control_margin = xS(130); - - add_subwindow(canvas = new CompressorCanvas(plugin, - x, - y, - get_w() - x - control_margin - xs10, - get_h() - y - ys70)); - canvas->set_cursor(CROSS_CURSOR, 0, 0); + int canvas_y2 = get_h()-yS(35); + BC_Title *title; + + add_subwindow(title = new BC_Title(x, y, "In:")); + int y2 = y + title->get_h() + margin; + EDLSession *session = plugin->get_edl()->session; + add_subwindow(in = new BC_Meter(x, y2, METER_VERT, canvas_y2 - y2, + session->min_meter_db, session->max_meter_db, session->meter_format, + 1, // use_titles + -1)); // span + x += in->get_w() + margin; + + add_subwindow(title = new BC_Title(x, y, "Gain:")); + add_subwindow(gain_change = new BC_Meter(x, y2, METER_VERT, + canvas_y2 - y2, MIN_GAIN_CHANGE, MAX_GAIN_CHANGE, METER_DB, + 1, // use_titles + -1, // span + 0, // is_downmix + 1)); // is_gain_change +// gain_change->update(1, 0); + + x += gain_change->get_w() + xS(35); + add_subwindow(title = new BC_Title(x, y, _("Sound level (Press shift to snap to grid):"))); + y += title->get_h() + 1; + add_subwindow(canvas = new CompressorCanvas(plugin, this, x, y, + get_w() - x - control_margin - xS(10), canvas_y2 - y)); x = get_w() - control_margin; - add_subwindow(new BC_Title(x, y, _("Reaction secs:"))); - y += ys20; - add_subwindow(reaction = new CompressorReaction(plugin, x, y)); - y += ys30; - add_subwindow(new BC_Title(x, y, _("Decay secs:"))); - y += ys20; - add_subwindow(decay = new CompressorDecay(plugin, x, y)); - y += ys30; - add_subwindow(new BC_Title(x, y, _("Trigger Type:"))); - y += ys20; + +// add_subwindow(new BC_Title(x, y, _("Lookahead secs:"))); +// y += title->get_h() + margin; +// readahead = new CompressorLookAhead(plugin, this, x, y); +// readahead->create_objects(); +// y += readahead->get_h() + margin; + + add_subwindow(new BC_Title(x, y, _("Attack secs:"))); + y += title->get_h() + margin; + attack = new CompressorAttack(plugin, this, x, y); + attack->create_objects(); + y += attack->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Release secs:"))); + y += title->get_h() + margin; + release = new CompressorRelease(plugin, this, x, y); + release->create_objects(); + y += release->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Trigger Type:"))); + y += title->get_h() + margin; add_subwindow(input = new CompressorInput(plugin, x, y)); input->create_objects(); - y += ys30; - add_subwindow(new BC_Title(x, y, _("Trigger:"))); - y += ys20; - add_subwindow(trigger = new CompressorTrigger(plugin, x, y)); - if(plugin->config.input != CompressorConfig::TRIGGER) trigger->disable(); - y += ys30; - add_subwindow(smooth = new CompressorSmooth(plugin, x, y)); - y += ys60; - add_subwindow(clear = new CompressorClear(plugin, x, y)); - x = xs10; - y = get_h() - ys40; - add_subwindow(new BC_Title(x, y, _("Point:"))); - x += xs50; - add_subwindow(x_text = new CompressorX(plugin, x, y)); - x += xs110; - add_subwindow(new BC_Title(x, y, _("x"))); - x += xs20; - add_subwindow(y_text = new CompressorY(plugin, x, y)); - draw_scales(); - - update_canvas(); - show_window(); -} + y += input->get_h() + margin; -void CompressorWindow::draw_scales() -{ - draw_3d_border(canvas->get_x() - 2, - canvas->get_y() - 2, - canvas->get_w() + 4, - canvas->get_h() + 4, - get_bg_color(), - BLACK, - MDGREY, - get_bg_color()); - - - set_font(SMALLFONT); - set_color(get_resources()->default_text_color); - -#define DIVISIONS 8 - for(int i = 0; i <= DIVISIONS; i++) - { - int y = canvas->get_y() + yS(10) + canvas->get_h() / DIVISIONS * i; - int x = canvas->get_x() - xS(30); - char string[BCTEXTLEN]; - - sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db); - draw_text(x, y, string); - - int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i; - int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1); - for(int j = 0; j < 10; j++) - { - y = y1 + (y2 - y1) * j / 10; - if(j == 0) - { - draw_line(canvas->get_x() - xS(10), y, canvas->get_x(), y); - } - else - if(i < DIVISIONS) - { - draw_line(canvas->get_x() - xS(5), y, canvas->get_x(), y); - } - } - } + add_subwindow(title = new BC_Title(x, y, _("Trigger:"))); + y += title->get_h() + margin; + trigger = new CompressorTrigger(plugin, this, x, y); + trigger->create_objects(); + if( plugin->config.input != CompressorConfig::TRIGGER ) trigger->disable(); + y += trigger->get_h() + margin; + add_subwindow(smooth = new CompressorSmooth(plugin, x, y)); + y += smooth->get_h() + margin; - for(int i = 0; i <= DIVISIONS; i++) - { - int y = canvas->get_h() + yS(30); - int x = canvas->get_x() + (canvas->get_w() - xS(10)) / DIVISIONS * i; - char string[BCTEXTLEN]; - - sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db); - draw_text(x, y, string); - - int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i; - int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1); - for(int j = 0; j < 10; j++) - { - x = x1 + (x2 - x1) * j / 10; - if(j == 0) - { - draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(10)); - } - else - if(i < DIVISIONS) - { - draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(5)); - } - } - } + add_subwindow(title = new BC_Title(x, y, _("Output:"))); + y += title->get_h(); + y_text = new CompressorY(plugin, this, x, y); + y_text->create_objects(); + y += y_text->get_h() + margin; + add_subwindow(title = new BC_Title(x, y, _("Input:"))); + y += title->get_h(); + x_text = new CompressorX(plugin, this, x, y); + x_text->create_objects(); + y += x_text->get_h() + margin; + add_subwindow(clear = new CompressorClear(plugin, x, y)); + x = xS(10); + y = get_h() - yS(40); - flash(); + canvas->create_objects(); + canvas->update(); + show_window(); } + void CompressorWindow::update() { update_textboxes(); - update_canvas(); + canvas->update(); +} + +void CompressorWindow::update_meter(CompressorGainFrame *gain_frame) +{ + gain_change->update(gain_frame->gain, 0); + in->update(gain_frame->level, 0); } void CompressorWindow::update_textboxes() { - if(atol(trigger->get_text()) != plugin->config.trigger) + BandConfig *band_config = &plugin->config.bands[0]; + + if( atol(trigger->get_text()) != plugin->config.trigger ) trigger->update((int64_t)plugin->config.trigger); - if(strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input))) + if( strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)) ) input->set_text(CompressorInput::value_to_text(plugin->config.input)); - if(plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled()) + if( plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled() ) trigger->disable(); else - if(plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled()) + if( plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled() ) trigger->enable(); - if(!EQUIV(atof(reaction->get_text()), plugin->config.reaction_len)) - reaction->update((float)plugin->config.reaction_len); - if(!EQUIV(atof(decay->get_text()), plugin->config.decay_len)) - decay->update((float)plugin->config.decay_len); +// if( !EQUIV(atof(readahead->get_text()), band_config->readahead_len) ) +// readahead->update((float)band_config->readahead_len); + if( !EQUIV(atof(attack->get_text()), band_config->attack_len) ) + attack->update((float)band_config->attack_len); + if( !EQUIV(atof(release->get_text()), band_config->release_len) ) + release->update((float)band_config->release_len); smooth->update(plugin->config.smoothing_only); - if(canvas->current_operation == CompressorCanvas::DRAG) - { - x_text->update((float)plugin->config.levels.values[canvas->current_point].x); - y_text->update((float)plugin->config.levels.values[canvas->current_point].y); - } -} - -#define POINT_W xS(10) -void CompressorWindow::update_canvas() -{ - canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h()); - canvas->set_line_dashes(1); - canvas->set_color(GREEN); - - for(int i = 1; i < DIVISIONS; i++) - { - int y = canvas->get_h() * i / DIVISIONS; - canvas->draw_line(0, y, canvas->get_w(), y); - - int x = canvas->get_w() * i / DIVISIONS; - canvas->draw_line(x, 0, x, canvas->get_h()); - } - canvas->set_line_dashes(0); - - - canvas->set_font(MEDIUMFONT); - canvas->draw_text(plugin->get_theme()->widget_border, - canvas->get_h() / 2, - _("Output")); - canvas->draw_text(canvas->get_w() / 2 - canvas->get_text_width(MEDIUMFONT, _("Input")) / 2, - canvas->get_h() - plugin->get_theme()->widget_border, - _("Input")); - - - canvas->set_color(WHITE); - canvas->set_line_width(2); - - double x_db = plugin->config.min_db; - double y_db = plugin->config.calculate_db(x_db); - int y1 = (int)(y_db / plugin->config.min_db * canvas->get_h()); - - for(int i=1; iget_w(); i++) - { - x_db = (1. - (double)i / canvas->get_w()) * plugin->config.min_db; - y_db = plugin->config.calculate_db(x_db); - int y2 = (int)(y_db / plugin->config.min_db * canvas->get_h()); - canvas->draw_line(i-1, y1, i, y2); - y1 = y2; + if( canvas->current_operation == CompressorCanvas::DRAG ) { + x_text->update((float)band_config->levels.values[canvas->current_point].x); + y_text->update((float)band_config->levels.values[canvas->current_point].y); } - canvas->set_line_width(1); - - int total = plugin->config.levels.total ? plugin->config.levels.total : 1; - for(int i=0; i < total; i++) - { - x_db = plugin->config.get_x(i); - y_db = plugin->config.get_y(i); - - int x = (int)((1. - x_db / plugin->config.min_db) * canvas->get_w()); - int y = (int)(y_db / plugin->config.min_db * canvas->get_h()); - - canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W); - } - - canvas->flash(); } int CompressorWindow::resize_event(int w, int h) @@ -1062,250 +500,123 @@ int CompressorWindow::resize_event(int w, int h) } - - - - - - -CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h) - : BC_SubWindow(x, y, w, h, BLACK) +CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, + CompressorWindow *window, int x, int y, int w, int h) + : CompressorCanvasBase(&plugin->config, plugin, window, x, y, w, h) { - this->plugin = plugin; - current_operation = NONE; - current_point = 0; } -int CompressorCanvas::button_press_event() -{ -// Check existing points - if(is_event_win() && cursor_inside()) - { - for(int i = 0; i < plugin->config.levels.total; i++) - { - double x_db = plugin->config.get_x(i); - double y_db = plugin->config.get_y(i); - - int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w()); - int y = (int)(y_db / plugin->config.min_db * get_h()); - - if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 && - get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2) - { - current_operation = DRAG; - current_point = i; - return 1; - } - } - - - -// Create new point - double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db; - double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db; - - current_point = plugin->config.set_point(x_db, y_db); - current_operation = DRAG; - ((CompressorWindow*)plugin->thread->window)->update(); - plugin->send_configure_change(); - return 1; - } - return 0; -//plugin->config.dump(); -} - -int CompressorCanvas::button_release_event() -{ - if(current_operation == DRAG) - { - if(current_point > 0) - { - if(plugin->config.levels.values[current_point].x < - plugin->config.levels.values[current_point - 1].x) - plugin->config.remove_point(current_point); - } - - if(current_point < plugin->config.levels.total - 1) - { - if(plugin->config.levels.values[current_point].x >= - plugin->config.levels.values[current_point + 1].x) - plugin->config.remove_point(current_point); - } - - ((CompressorWindow*)plugin->thread->window)->update(); - plugin->send_configure_change(); - current_operation = NONE; - return 1; - } - - return 0; -} -int CompressorCanvas::cursor_motion_event() +void CompressorCanvas::update_window() { - if(current_operation == DRAG) - { - int x = get_cursor_x(); - int y = get_cursor_y(); - CLAMP(x, 0, get_w()); - CLAMP(y, 0, get_h()); - double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db; - double y_db = (double)y / get_h() * plugin->config.min_db; - plugin->config.levels.values[current_point].x = x_db; - plugin->config.levels.values[current_point].y = y_db; - ((CompressorWindow*)plugin->thread->window)->update(); - plugin->send_configure_change(); - return 1; -//plugin->config.dump(); - } - else -// Change cursor over points - if(is_event_win() && cursor_inside()) - { - int new_cursor = CROSS_CURSOR; - - for(int i = 0; i < plugin->config.levels.total; i++) - { - double x_db = plugin->config.get_x(i); - double y_db = plugin->config.get_y(i); - - int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w()); - int y = (int)(y_db / plugin->config.min_db * get_h()); - - if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 && - get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2) - { - new_cursor = UPRIGHT_ARROW_CURSOR; - break; - } - } - - - if(new_cursor != get_cursor()) - { - set_cursor(new_cursor, 0, 1); - } - } - return 0; + ((CompressorWindow*)window)->update(); } +// CompressorLookAhead::CompressorLookAhead(CompressorEffect *plugin, +// CompressorWindow *window, int x, int y) +// : BC_TumbleTextBox(window, (float)plugin->config.bands[0].readahead_len, +// (float)MIN_LOOKAHEAD, (float)MAX_LOOKAHEAD, x, y, xS(100)) +// { +// this->plugin = plugin; +// set_increment(0.1); +// set_precision(2); +// } +// +// int CompressorLookAhead::handle_event() +// { +// plugin->config.bands[0].readahead_len = atof(get_text()); +// plugin->send_configure_change(); +// return 1; +// } +// +// - -CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y) - : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.reaction_len) +CompressorAttack::CompressorAttack(CompressorEffect *plugin, + CompressorWindow *window, int x, int y) + : BC_TumbleTextBox(window, (float)plugin->config.bands[0].attack_len, + (float)MIN_ATTACK, (float)MAX_ATTACK, x, y, xS(100)) { this->plugin = plugin; + set_increment(0.1); + set_precision(2); } -int CompressorReaction::handle_event() +int CompressorAttack::handle_event() { - plugin->config.reaction_len = atof(get_text()); + plugin->config.bands[0].attack_len = atof(get_text()); plugin->send_configure_change(); return 1; } -int CompressorReaction::button_press_event() -{ - if(is_event_win()) - { - if(get_buttonpress() < 4) return BC_TextBox::button_press_event(); - if(get_buttonpress() == 4) - { - plugin->config.reaction_len += 0.1; - } - else - if(get_buttonpress() == 5) - { - plugin->config.reaction_len -= 0.1; - } - update((float)plugin->config.reaction_len); - plugin->send_configure_change(); - return 1; - } - return 0; -} - -CompressorDecay::CompressorDecay(CompressorEffect *plugin, int x, int y) - : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.decay_len) +CompressorRelease::CompressorRelease(CompressorEffect *plugin, + CompressorWindow *window, int x, int y) + : BC_TumbleTextBox(window, (float)plugin->config.bands[0].release_len, + (float)MIN_DECAY, (float)MAX_DECAY, x, y, xS(100)) { this->plugin = plugin; + set_increment(0.1); + set_precision(2); } -int CompressorDecay::handle_event() +int CompressorRelease::handle_event() { - plugin->config.decay_len = atof(get_text()); + plugin->config.bands[0].release_len = atof(get_text()); plugin->send_configure_change(); return 1; } -int CompressorDecay::button_press_event() -{ - if(is_event_win()) - { - if(get_buttonpress() < 4) return BC_TextBox::button_press_event(); - if(get_buttonpress() == 4) - { - plugin->config.decay_len += 0.1; - } - else - if(get_buttonpress() == 5) - { - plugin->config.decay_len -= 0.1; - } - update((float)plugin->config.decay_len); - plugin->send_configure_change(); - return 1; - } - return 0; -} - - -CompressorX::CompressorX(CompressorEffect *plugin, int x, int y) - : BC_TextBox(x, y, xS(100), 1, "") +CompressorX::CompressorX(CompressorEffect *plugin, + CompressorWindow *window, int x, int y) + : BC_TumbleTextBox(window, (float)0.0, + plugin->config.min_db, plugin->config.max_db, x, y, xS(100)) { this->plugin = plugin; + set_increment(0.1); + set_precision(2); } int CompressorX::handle_event() { + BandConfig *band_config = &plugin->config.bands[0]; int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point; - if(current_point < plugin->config.levels.total) - { - plugin->config.levels.values[current_point].x = atof(get_text()); - ((CompressorWindow*)plugin->thread->window)->update_canvas(); + if( current_point < band_config->levels.total ) { + band_config->levels.values[current_point].x = atof(get_text()); + ((CompressorWindow*)plugin->thread->window)->canvas->update(); plugin->send_configure_change(); } return 1; } - - -CompressorY::CompressorY(CompressorEffect *plugin, int x, int y) - : BC_TextBox(x, y, xS(100), 1, "") +CompressorY::CompressorY(CompressorEffect *plugin, + CompressorWindow *window, int x, int y) + : BC_TumbleTextBox(window, (float)0.0, + plugin->config.min_db, plugin->config.max_db, x, y, xS(100)) { this->plugin = plugin; + set_increment(0.1); + set_precision(2); } int CompressorY::handle_event() { + BandConfig *band_config = &plugin->config.bands[0]; int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point; - if(current_point < plugin->config.levels.total) - { - plugin->config.levels.values[current_point].y = atof(get_text()); - ((CompressorWindow*)plugin->thread->window)->update_canvas(); + if( current_point < band_config->levels.total ) { + band_config->levels.values[current_point].y = atof(get_text()); + ((CompressorWindow*)plugin->thread->window)->canvas->update(); plugin->send_configure_change(); } return 1; } - - - -CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y) - : BC_TextBox(x, y, xS(100), 1, (int64_t)plugin->config.trigger) +CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, + CompressorWindow *window, int x, int y) + : BC_TumbleTextBox(window, (int)plugin->config.trigger, + MIN_TRIGGER, MAX_TRIGGER, x, y, xS(100)) { this->plugin = plugin; + set_increment(1); } int CompressorTrigger::handle_event() { @@ -1314,37 +625,10 @@ int CompressorTrigger::handle_event() return 1; } -int CompressorTrigger::button_press_event() -{ - if(is_event_win()) - { - if(get_buttonpress() < 4) return BC_TextBox::button_press_event(); - if(get_buttonpress() == 4) - { - plugin->config.trigger++; - } - else - if(get_buttonpress() == 5) - { - plugin->config.trigger--; - } - update((int64_t)plugin->config.trigger); - plugin->send_configure_change(); - return 1; - } - return 0; -} - - - - CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y) - : BC_PopupMenu(x, - y, - xS(120), - CompressorInput::value_to_text(plugin->config.input), - 1) + : BC_PopupMenu(x, y, xS(100), + CompressorInput::value_to_text(plugin->config.input), 1) { this->plugin = plugin; } @@ -1358,39 +642,32 @@ int CompressorInput::handle_event() void CompressorInput::create_objects() { - for(int i = 0; i < 3; i++) - { + for( int i = 0; i < 3; i++ ) { add_item(new BC_MenuItem(value_to_text(i))); } } const char* CompressorInput::value_to_text(int value) { - switch(value) + switch( value ) { - case CompressorConfig::TRIGGER: return _("Trigger"); - case CompressorConfig::MAX: return _("Maximum"); - case CompressorConfig::SUM: return _("Total"); + case CompressorConfig::TRIGGER: return "Trigger"; + case CompressorConfig::MAX: return "Maximum"; + case CompressorConfig::SUM: return "Total"; } - return _("Trigger"); + return "Trigger"; } int CompressorInput::text_to_value(char *text) { - for(int i = 0; i < 3; i++) - { - if(!strcmp(value_to_text(i), text)) return i; + for( int i = 0; i < 3; i++ ) { + if( !strcmp(value_to_text(i), text) ) return i; } return CompressorConfig::TRIGGER; } - - - - - CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y) : BC_GenericButton(x, y, _("Clear")) { @@ -1399,15 +676,14 @@ CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y) int CompressorClear::handle_event() { - plugin->config.levels.remove_all(); + BandConfig *band_config = &plugin->config.bands[0]; + band_config->levels.remove_all(); //plugin->config.dump(); ((CompressorWindow*)plugin->thread->window)->update(); plugin->send_configure_change(); return 1; } - - CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y) : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only")) { @@ -1421,6 +697,3 @@ int CompressorSmooth::handle_event() return 1; } - - - diff --git a/cinelerra-5.1/plugins/compressor/compressor.h b/cinelerra-5.1/plugins/compressor/compressor.h index 412b8db9..dc9c54ad 100644 --- a/cinelerra-5.1/plugins/compressor/compressor.h +++ b/cinelerra-5.1/plugins/compressor/compressor.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2008-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #include "bchash.inc" +#include "compressortools.h" #include "guicast.h" #include "mutex.h" #include "pluginaclient.h" @@ -32,80 +33,78 @@ #include "vframe.inc" class CompressorEffect; +class CompressorWindow; - - - -class CompressorCanvas : public BC_SubWindow +class CompressorCanvas : public CompressorCanvasBase { public: - CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h); - int button_press_event(); - int button_release_event(); - int cursor_motion_event(); - + CompressorCanvas(CompressorEffect *plugin, CompressorWindow *window, + int x, int y, int w, int h); + void update_window(); +}; - enum - { - NONE, - DRAG - }; - int current_point; - int current_operation; +class CompressorLookAhead : public BC_TumbleTextBox +{ +public: + CompressorLookAhead(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); + int handle_event(); CompressorEffect *plugin; }; - -class CompressorReaction : public BC_TextBox +class CompressorAttack : public BC_TumbleTextBox { public: - CompressorReaction(CompressorEffect *plugin, int x, int y); + CompressorAttack(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); int handle_event(); - int button_press_event(); CompressorEffect *plugin; }; -class CompressorClear : public BC_GenericButton +class CompressorRelease : public BC_TumbleTextBox { public: - CompressorClear(CompressorEffect *plugin, int x, int y); + CompressorRelease(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); int handle_event(); CompressorEffect *plugin; }; -class CompressorX : public BC_TextBox + +class CompressorClear : public BC_GenericButton { public: - CompressorX(CompressorEffect *plugin, int x, int y); + CompressorClear(CompressorEffect *plugin, int x, int y); int handle_event(); CompressorEffect *plugin; }; -class CompressorY : public BC_TextBox +class CompressorX : public BC_TumbleTextBox { public: - CompressorY(CompressorEffect *plugin, int x, int y); + CompressorX(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); int handle_event(); CompressorEffect *plugin; }; -class CompressorTrigger : public BC_TextBox +class CompressorY : public BC_TumbleTextBox { public: - CompressorTrigger(CompressorEffect *plugin, int x, int y); + CompressorY(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); int handle_event(); - int button_press_event(); CompressorEffect *plugin; }; -class CompressorDecay : public BC_TextBox +class CompressorTrigger : public BC_TumbleTextBox { public: - CompressorDecay(CompressorEffect *plugin, int x, int y); + CompressorTrigger(CompressorEffect *plugin, CompressorWindow *window, + int x, int y); int handle_event(); - int button_press_event(); CompressorEffect *plugin; }; @@ -134,74 +133,44 @@ class CompressorWindow : public PluginClientWindow { public: CompressorWindow(CompressorEffect *plugin); + ~CompressorWindow(); + void create_objects(); void update(); void update_textboxes(); - void update_canvas(); void draw_scales(); + void update_meter(CompressorGainFrame *gain_frame); int resize_event(int w, int h); CompressorCanvas *canvas; - CompressorReaction *reaction; + CompressorLookAhead *readahead; + CompressorAttack *attack; CompressorClear *clear; CompressorX *x_text; CompressorY *y_text; CompressorTrigger *trigger; - CompressorDecay *decay; + CompressorRelease *release; CompressorSmooth *smooth; CompressorInput *input; + BC_Meter *in; + BC_Meter *gain_change; CompressorEffect *plugin; }; -typedef struct -{ -// DB from min_db - 0 - double x, y; -} compressor_point_t; - -class CompressorConfig +class CompressorConfig : public CompressorConfigBase { public: CompressorConfig(); void copy_from(CompressorConfig &that); int equivalent(CompressorConfig &that); - void interpolate(CompressorConfig &prev, - CompressorConfig &next, - int64_t prev_frame, - int64_t next_frame, - int64_t current_frame); - - int total_points(); - void remove_point(int number); - void optimize(); -// Return values of a specific point - double get_y(int number); - double get_x(int number); -// Returns db output from db input - double calculate_db(double x); - int set_point(double x, double y); - void dump(); - - int trigger; - int input; - enum - { - TRIGGER, - MAX, - SUM - }; - double min_db; - double reaction_len; - double decay_len; - double min_x, min_y; - double max_x, max_y; - int smoothing_only; - ArrayList levels; + void interpolate(CompressorConfig &prev, CompressorConfig &next, + int64_t prev_frame, int64_t next_frame, int64_t current_frame); }; + class CompressorEffect : public PluginAClient { public: @@ -212,45 +181,35 @@ public: int is_realtime(); void read_data(KeyFrame *keyframe); void save_data(KeyFrame *keyframe); - int process_buffer(int64_t size, - Samples **buffer, - int64_t start_position, - int sample_rate); - double calculate_gain(double input); - -// Calculate linear output from linear input - double calculate_output(double x); + int process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate); + void allocate_input(int size); - void reset(); void update_gui(); - void delete_dsp(); + void render_stop(); PLUGIN_CLASS_MEMBERS(CompressorConfig) -// The raw input data for each channel with readahead +// Input data + read ahead for each channel Samples **input_buffer; + // Number of samples in the input buffer int64_t input_size; // Number of samples allocated in the input buffer int64_t input_allocated; // Starting sample of input buffer relative to project in requested rate. int64_t input_start; + int64_t last_position; + int need_reconfigure; + + + CompressorEngine *engine; -// ending input value of smoothed input - double next_target; -// starting input value of smoothed input - double previous_target; -// samples between previous and next target value for readahead - int target_samples; -// current sample from 0 to target_samples - int target_current_sample; -// current smoothed input value - double current_value; // Temporaries for linear transfer ArrayList levels; - double min_x, min_y; - double max_x, max_y; +// double min_x, min_y; +// double max_x, max_y; }; diff --git a/cinelerra-5.1/plugins/compressormulti/Makefile b/cinelerra-5.1/plugins/compressormulti/Makefile new file mode 100644 index 00000000..0a6cae53 --- /dev/null +++ b/cinelerra-5.1/plugins/compressormulti/Makefile @@ -0,0 +1,11 @@ +include ../../plugin_defs + +OBJS = $(OBJDIR)/comprmulti.o \ + $(OBJDIR)/comprmultigui.o + +PLUGIN = compressormulti + +include ../../plugin_config + +$(OBJDIR)/compressor.o: comprmulti.C +$(OBJDIR)/compressorgui.o: comprmultigui.C diff --git a/cinelerra-5.1/plugins/compressormulti/comprmulti.C b/cinelerra-5.1/plugins/compressormulti/comprmulti.C new file mode 100644 index 00000000..2207ba08 --- /dev/null +++ b/cinelerra-5.1/plugins/compressormulti/comprmulti.C @@ -0,0 +1,945 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 +#include + +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; iconfig.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 time_max ) + time_max = fabs(buffer[i]); + } + if( time_max > frame->time_max ) + frame->time_max = time_max; + plugin->add_gui_frame(frame); + } + return 0; +} + + +int ComprMultiFFT::read_samples(int64_t output_sample, + int samples, Samples *buffer) +{ + int result = plugin->read_samples(buffer, channel, + plugin->get_samplerate(), output_sample, samples); +#ifndef DRAW_AFTER_BANDPASS +// append unprocessed samples to the input_buffer + int new_input_size = plugin->new_input_size + samples; + plugin->allocate_input(new_input_size); + memcpy(plugin->input_buffer[channel]->get_data() + plugin->new_input_size, + buffer->get_data(), samples * sizeof(double)); + plugin->new_input_size = new_input_size; +#endif // !DRAW_AFTER_BANDPASS + return result; +} + diff --git a/cinelerra-5.1/plugins/compressormulti/comprmulti.h b/cinelerra-5.1/plugins/compressormulti/comprmulti.h new file mode 100644 index 00000000..bc542670 --- /dev/null +++ b/cinelerra-5.1/plugins/compressormulti/comprmulti.h @@ -0,0 +1,163 @@ + +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 levels; + ComprMultiEffect *plugin; + int band; + CompressorEngine *engine; +}; + +class ComprMultiEffect : public PluginAClient +{ +public: + ComprMultiEffect(PluginServer *server); + ~ComprMultiEffect(); + + int is_multichannel(); + int is_realtime(); + void read_data(KeyFrame *keyframe); + void save_data(KeyFrame *keyframe); + int process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate); + +// calculate envelopes of all the bands + void calculate_envelope(); + + void allocate_input(int new_size); + + void reset(); + void update_gui(); + void render_stop(); + void delete_dsp(); + void dump_frames(); + + PLUGIN_CLASS_MEMBERS(ComprMultiConfig) + +#ifndef DRAW_AFTER_BANDPASS +// The out of band data for each channel with readahead + Samples **input_buffer; +// Number of samples in the unfiltered input buffers + int64_t input_size; +// input buffer size being calculated by the FFT readers + int64_t new_input_size; +#endif // !DRAW_AFTER_BANDPASS +// Starting sample of the input buffer relative to project in the requested rate. + int64_t input_start; +// Number of samples in the filtered input buffers + int64_t filtered_size; + +// detect seeking + int64_t last_position; + +// count spectrogram frames for each band + int new_spectrogram_frames[TOTAL_BANDS]; + + BandState *band_states[TOTAL_BANDS]; +// The big FFT with multiple channels & multiple bands extracted per channel. + ComprMultiFFT **fft; + + int need_reconfigure; + double start_pos; + int dir; +}; + + +#endif diff --git a/cinelerra-5.1/plugins/compressormulti/comprmultigui.C b/cinelerra-5.1/plugins/compressormulti/comprmultigui.C new file mode 100644 index 00000000..95b4c5d9 --- /dev/null +++ b/cinelerra-5.1/plugins/compressormulti/comprmultigui.C @@ -0,0 +1,732 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 + +ComprMultiWindow::ComprMultiWindow(ComprMultiEffect *plugin) + : PluginClientWindow(plugin, xS(650),yS(560), xS(650),yS(560), 0) +{ + this->plugin = plugin; + char string[BCTEXTLEN]; +// set the default directory + sprintf(string, "%s/compressormulti.rc", File::get_config_path()); + defaults = new BC_Hash(string); + defaults->load(); + plugin->config.current_band = defaults->get("CURRENT_BAND", plugin->config.current_band); + gain_frame = 0; + freq_frame = 0; +} + +ComprMultiWindow::~ComprMultiWindow() +{ + defaults->update("CURRENT_BAND", plugin->config.current_band); + defaults->save(); + delete defaults; + + delete eqcanvas; + delete reaction; + delete x_text; + delete y_text; + delete trigger; + delete decay; + delete gain_frame; + delete freq_frame; +} + + +void ComprMultiWindow::create_objects() +{ + int margin = client->get_theme()->widget_border; + int x = margin, y = margin; + int control_margin = xS(150); + int canvas_y2 = get_h() * 2 / 3; + BC_Title *title; + BandConfig *band_config = &plugin->config.bands[plugin->config.current_band]; + BandConfig *prev_band = 0; + if( plugin->config.current_band > 0 ) { + prev_band = &plugin->config.bands[plugin->config.current_band - 1]; + } + + add_subwindow(title = new BC_Title(x, y, "In:")); + int y2 = y + title->get_h() + margin; + EDL *edl = plugin->get_edl(); + add_subwindow(in = new BC_Meter(x, y2, METER_VERT, + canvas_y2 - y2, + edl->session->min_meter_db, + edl->session->max_meter_db, + edl->session->meter_format, + 1, // use_titles + -1)); // span + x += in->get_w() + margin; + + add_subwindow(title = new BC_Title(x, y, "Gain:")); + add_subwindow(gain_change = new BC_Meter(x, y2, METER_VERT, + canvas_y2 - y2, + MIN_GAIN_CHANGE, + MAX_GAIN_CHANGE, + METER_DB, + 1, // use_titles + -1, // span + 0, // downmix + 1)); // is_gain_change + x += gain_change->get_w() + xS(35); + + add_subwindow(title = new BC_Title(x, y, _("Current band:"))); + + int x1 = title->get_x() + title->get_w() + margin; + char string[BCTEXTLEN]; + for( int i = 0; i < TOTAL_BANDS; i++ ) { + sprintf(string, "%d", i + 1); + add_subwindow(band[i] = new ComprMultiBand(this, plugin, + x1, y, i, string)); + x1 += band[i]->get_w() + margin; + } + y += band[0]->get_h() + 1; + + + add_subwindow(title = new BC_Title(x, y, + _("Sound level (Press shift to snap to grid):"))); + y += title->get_h() + 1; + add_subwindow(canvas = new ComprMultiCanvas(plugin, this, + x, y, get_w() - x - control_margin - xS(10), canvas_y2 - y)); + y += canvas->get_h() + yS(30); + + add_subwindow(title = new BC_Title(margin, y, _("Bandwidth:"))); + y += title->get_h(); + eqcanvas = new EQCanvas(this, margin, y, + canvas->get_w() + x - margin, get_h() - y - margin, + plugin->config.min_db, 0.0); + eqcanvas->freq_divisions = 10; + eqcanvas->initialize(); + + x = get_w() - control_margin; + y = margin; + add_subwindow(title = new BC_Title(x, y, _("Attack secs:"))); + y += title->get_h(); + reaction = new ComprMultiReaction(plugin, this, x, y); + reaction->create_objects(); + y += reaction->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Release secs:"))); + y += title->get_h(); + decay = new ComprMultiDecay(plugin, this, x, y); + decay->create_objects(); + y += decay->get_h() + margin; + + add_subwindow(solo = new ComprMultiSolo(plugin, x, y)); + y += solo->get_h() + margin; + add_subwindow(bypass = new ComprMultiBypass(plugin, x, y)); + y += bypass->get_h() + margin; + add_subwindow(title = new BC_Title(x, y, _("Output:"))); + y += title->get_h(); + + y_text = new ComprMultiY(plugin, this, x, y); + y_text->create_objects(); + y += y_text->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Input:"))); + y += title->get_h(); + x_text = new ComprMultiX(plugin, this, x, y); + x_text->create_objects(); + y += x_text->get_h() + margin; + + add_subwindow(clear = new ComprMultiClear(plugin, x, y)); + y += clear->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Freq range:"))); + y += title->get_h(); + +// the previous high frequency + int *ptr = 0; + if( prev_band ) { + ptr = &prev_band->freq; + } + + add_subwindow(freq1 = new ComprMultiQPot(this, + plugin, + get_w() - (margin + BC_Pot::calculate_w()) * 2, + y, + ptr)); + +// the current high frequency + ptr = &band_config->freq; + if( plugin->config.current_band == TOTAL_BANDS - 1 ) { + ptr = 0; + } + + add_subwindow(freq2 = new ComprMultiQPot(this, + plugin, + get_w() - margin - BC_Pot::calculate_w(), + y, + ptr)); + y += freq1->get_h() + margin; + + BC_Bar *bar; + add_subwindow(bar = new BC_Bar(x, y, get_w() - x - margin)); + y += bar->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Trigger Type:"))); + y += title->get_h(); + add_subwindow(input = new ComprMultiInput(plugin, x, y)); + input->create_objects(); + y += input->get_h() + margin; + add_subwindow(title = new BC_Title(x, y, _("Trigger:"))); + y += title->get_h(); + + trigger = new ComprMultiTrigger(plugin, this, x, y); + trigger->create_objects(); + if( plugin->config.input != ComprMultiConfig::TRIGGER ) trigger->disable(); + y += trigger->get_h() + margin; + + add_subwindow(smooth = new ComprMultiSmooth(plugin, x, y)); + y += smooth->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Steepness:"))); + add_subwindow(q = new ComprMultiFPot(this, plugin, + get_w() - margin - BC_Pot::calculate_w(), y, + &plugin->config.q, 0, 1)); + y += q->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Window size:"))); + y += title->get_h(); + add_subwindow(size = new ComprMultiSize(this, + plugin, + x, + y)); + size->create_objects(); + size->update(plugin->config.window_size); + y += size->get_h() + margin; + + canvas->create_objects(); + update_eqcanvas(); + show_window(); +} + +// called when the user selects a different band +void ComprMultiWindow::update() +{ + BandConfig *band_config = &plugin->config.bands[plugin->config.current_band]; + + for( int i = 0; i < TOTAL_BANDS; i++ ) { + if( plugin->config.current_band == i ) { + band[i]->update(1); + } + else { + band[i]->update(0); + } + } + + int *ptr = 0; + if( plugin->config.current_band > 0 ) { + ptr = &plugin->config.bands[plugin->config.current_band - 1].freq; + } + else { + ptr = 0; + } + + freq1->output = ptr; + if( ptr ) { + freq1->update(*ptr); + freq1->enable(); + } + else { + freq1->update(0); + freq1->disable(); + } + +// top band edits the penultimate band + if( plugin->config.current_band < TOTAL_BANDS - 1 ) { + ptr = &band_config->freq; + } + else { + ptr = 0; + } + + freq2->output = ptr; + if( ptr ) { + freq2->update(*ptr); + freq2->enable(); + } + else { + freq2->update(0); + freq2->disable(); + } + + q->update(plugin->config.q); + solo->update(band_config->solo); + bypass->update(band_config->bypass); + size->update(plugin->config.window_size); + + if( atol(trigger->get_text()) != plugin->config.trigger ) { + trigger->update((int64_t)plugin->config.trigger); + } + + if( strcmp(input->get_text(), ComprMultiInput::value_to_text(plugin->config.input)) ) { + input->set_text(ComprMultiInput::value_to_text(plugin->config.input)); + } + + if( plugin->config.input != ComprMultiConfig::TRIGGER && trigger->get_enabled() ) { + trigger->disable(); + } + else + if( plugin->config.input == ComprMultiConfig::TRIGGER && !trigger->get_enabled() ) { + trigger->enable(); + } + + if( !EQUIV(atof(reaction->get_text()), band_config->attack_len) ) { + reaction->update((float)band_config->attack_len); + } + + if( !EQUIV(atof(decay->get_text()), band_config->release_len) ) { + decay->update((float)band_config->release_len); + } + + smooth->update(plugin->config.smoothing_only); + if( canvas->current_operation == ComprMultiCanvas::DRAG ) { + x_text->update((float)band_config->levels.values[canvas->current_point].x); + y_text->update((float)band_config->levels.values[canvas->current_point].y); + } + + canvas->update(); + update_eqcanvas(); +} + + + + +void ComprMultiWindow::update_eqcanvas() +{ + plugin->calculate_envelope(); +// filter GUI frames by band & data type + int have_meter = 0; + CompressorClientFrame *frame = 0; +// gdb plugin->dump_frames(); + double tracking_position = plugin->get_tracking_position(); + int dir = plugin->get_tracking_direction() == PLAY_REVERSE ? -1 : 1; + while( (frame = (CompressorClientFrame*)plugin->next_gui_frame()) ) { + if( dir*(frame->position - tracking_position) > 0 ) break; + if( frame->band == plugin->config.current_band ) { +// only frames for desired band + switch( frame->type ) { + case FREQ_COMPRESSORFRAME: { + delete freq_frame; + freq_frame = (CompressorFreqFrame *) + plugin->get_gui_frame(0, 0); + continue; } + case GAIN_COMPRESSORFRAME: { + delete gain_frame; + gain_frame = (CompressorGainFrame *) + plugin->get_gui_frame(0, 0); + have_meter = 1; + continue; } + } + } + delete plugin->get_gui_frame(0, 0); + } + if( have_meter ) { + gain_change->update(gain_frame->gain, 0); + in->update(gain_frame->level, 0); + } + +#ifndef DRAW_AFTER_BANDPASS + eqcanvas->update_spectrogram(freq_frame); +#else + eqcanvas->update_spectrogram(freq_frame, + plugin->config.current_band * plugin->config.window_size / 2, + TOTAL_BANDS * plugin->config.window_size / 2, + plugin->config.window_size); +#endif + +// draw the active band on top of the others + for( int pass = 0; pass < 2; pass++ ) { + for( int band = 0; band < TOTAL_BANDS; band++ ) { + if( band == plugin->config.current_band && pass == 0 || + band != plugin->config.current_band && pass == 1 ) { + continue; + } + + eqcanvas->draw_envelope(plugin->band_states[band]->envelope, + plugin->PluginAClient::project_sample_rate, + plugin->config.window_size, + band == plugin->config.current_band, + 0); + } + } + eqcanvas->canvas->flash(1); +} + +int ComprMultiWindow::resize_event(int w, int h) +{ + return 1; +} + + +ComprMultiFPot::ComprMultiFPot(ComprMultiWindow *gui, ComprMultiEffect *plugin, + int x, int y, double *output, double min, double max) + : BC_FPot(x, y, *output, min, max) +{ + this->gui = gui; + this->plugin = plugin; + this->output = output; + set_precision(0.01); +} + +int ComprMultiFPot::handle_event() +{ + *output = get_value(); + plugin->send_configure_change(); + gui->update_eqcanvas(); + return 1; +} + + +ComprMultiQPot::ComprMultiQPot(ComprMultiWindow *gui, ComprMultiEffect *plugin, + int x, int y, int *output) + : BC_QPot(x, y, output ? *output : 0) +{ + this->gui = gui; + this->plugin = plugin; + this->output = output; +} + + +int ComprMultiQPot::handle_event() +{ + if( output ) { + *output = get_value(); + plugin->send_configure_change(); + gui->update_eqcanvas(); + } + return 1; +} + + +ComprMultiSize::ComprMultiSize(ComprMultiWindow *gui, + ComprMultiEffect *plugin, int x, int y) + : BC_PopupMenu(x, y, xS(100), "4096", 1) +{ + this->gui = gui; + this->plugin = plugin; +} + +int ComprMultiSize::handle_event() +{ + plugin->config.window_size = atoi(get_text()); + plugin->send_configure_change(); + gui->update_eqcanvas(); + return 1; +} + + +void ComprMultiSize::create_objects() +{ + add_item(new BC_MenuItem("2048")); + add_item(new BC_MenuItem("4096")); + add_item(new BC_MenuItem("8192")); + add_item(new BC_MenuItem("16384")); + add_item(new BC_MenuItem("32768")); + add_item(new BC_MenuItem("65536")); + add_item(new BC_MenuItem("131072")); + add_item(new BC_MenuItem("262144")); +} + + +void ComprMultiSize::update(int size) +{ + char string[BCTEXTLEN]; + sprintf(string, "%d", size); + set_text(string); +} + + +ComprMultiCanvas::ComprMultiCanvas(ComprMultiEffect *plugin, + ComprMultiWindow *window, int x, int y, int w, int h) + : CompressorCanvasBase(&plugin->config, plugin, window, x, y, w, h) +{ +} + +void ComprMultiCanvas::update_window() +{ + ((ComprMultiWindow*)window)->update(); +} + + +ComprMultiReaction::ComprMultiReaction(ComprMultiEffect *plugin, + ComprMultiWindow *window, int x, int y) + : BC_TumbleTextBox(window, + (float)plugin->config.bands[plugin->config.current_band].attack_len, + (float)MIN_ATTACK, (float)MAX_ATTACK, x, y, xS(100)) +{ + this->plugin = plugin; + set_increment(0.1); + set_precision(2); +} + +int ComprMultiReaction::handle_event() +{ + plugin->config.bands[plugin->config.current_band].attack_len = atof(get_text()); + plugin->send_configure_change(); + return 1; +} + + + +ComprMultiDecay::ComprMultiDecay(ComprMultiEffect *plugin, + ComprMultiWindow *window, int x, int y) + : BC_TumbleTextBox(window, + (float)plugin->config.bands[plugin->config.current_band].release_len, + (float)MIN_DECAY, (float)MAX_DECAY, x, y, xS(100)) +{ + this->plugin = plugin; + set_increment(0.1); + set_precision(2); +} +int ComprMultiDecay::handle_event() +{ + plugin->config.bands[plugin->config.current_band].release_len = atof(get_text()); + plugin->send_configure_change(); + return 1; +} + + +ComprMultiX::ComprMultiX(ComprMultiEffect *plugin, + ComprMultiWindow *window, int x, int y) + : BC_TumbleTextBox(window, (float)0.0, + plugin->config.min_db, plugin->config.max_db, x, y, xS(100)) +{ + this->plugin = plugin; + set_increment(0.1); + set_precision(2); +} +int ComprMultiX::handle_event() +{ + BandConfig *band_config = &plugin->config.bands[plugin->config.current_band]; + + int current_point = ((ComprMultiWindow*)plugin->thread->window)->canvas->current_point; + if( current_point < band_config->levels.total ) { + band_config->levels.values[current_point].x = atof(get_text()); + ((ComprMultiWindow*)plugin->thread->window)->canvas->update(); + plugin->send_configure_change(); + } + return 1; +} + + +ComprMultiY::ComprMultiY(ComprMultiEffect *plugin, + ComprMultiWindow *window, + int x, + int y) + : BC_TumbleTextBox(window, (float)0.0, + plugin->config.min_db, plugin->config.max_db, x, y, xS(100)) +{ + this->plugin = plugin; + set_increment(0.1); + set_precision(2); +} +int ComprMultiY::handle_event() +{ + BandConfig *band_config = &plugin->config.bands[plugin->config.current_band]; + + int current_point = ((ComprMultiWindow*)plugin->thread->window)->canvas->current_point; + if( current_point < band_config->levels.total ) { + band_config->levels.values[current_point].y = atof(get_text()); + ((ComprMultiWindow*)plugin->thread->window)->canvas->update(); + plugin->send_configure_change(); + } + return 1; +} + + +ComprMultiTrigger::ComprMultiTrigger(ComprMultiEffect *plugin, + ComprMultiWindow *window, + int x, + int y) + : BC_TumbleTextBox(window, (int)plugin->config.trigger, + MIN_TRIGGER, MAX_TRIGGER, x, y, xS(100)) +{ + this->plugin = plugin; +} +int ComprMultiTrigger::handle_event() +{ + plugin->config.trigger = atol(get_text()); + plugin->send_configure_change(); + return 1; +} + + +ComprMultiInput::ComprMultiInput(ComprMultiEffect *plugin, int x, int y) + : BC_PopupMenu(x, y, xS(100), + ComprMultiInput::value_to_text(plugin->config.input), 1) +{ + this->plugin = plugin; +} +int ComprMultiInput::handle_event() +{ + plugin->config.input = text_to_value(get_text()); + ((ComprMultiWindow*)plugin->thread->window)->update(); + plugin->send_configure_change(); + return 1; +} + +void ComprMultiInput::create_objects() +{ + for( int i = 0; i < 3; i++ ) { + add_item(new BC_MenuItem(value_to_text(i))); + } +} + +const char* ComprMultiInput::value_to_text(int value) +{ + switch( value ) { + case ComprMultiConfig::TRIGGER: return "Trigger"; + case ComprMultiConfig::MAX: return "Maximum"; + case ComprMultiConfig::SUM: return "Total"; + } + + return "Trigger"; +} + +int ComprMultiInput::text_to_value(char *text) +{ + for( int i = 0; i < 3; i++ ) { + if( !strcmp(value_to_text(i), text) ) return i; + } + + return ComprMultiConfig::TRIGGER; +} + + +ComprMultiClear::ComprMultiClear(ComprMultiEffect *plugin, int x, int y) + : BC_GenericButton(x, y, _("Clear")) +{ + this->plugin = plugin; +} + +int ComprMultiClear::handle_event() +{ + BandConfig *band_config = &plugin->config.bands[plugin->config.current_band]; + + band_config->levels.remove_all(); +//plugin->config.dump(); + ((ComprMultiWindow*)plugin->thread->window)->update(); + plugin->send_configure_change(); + return 1; +} + + +ComprMultiSmooth::ComprMultiSmooth(ComprMultiEffect *plugin, int x, int y) + : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only")) +{ + this->plugin = plugin; +} + +int ComprMultiSmooth::handle_event() +{ + plugin->config.smoothing_only = get_value(); + plugin->send_configure_change(); + return 1; +} + + +ComprMultiSolo::ComprMultiSolo(ComprMultiEffect *plugin, int x, int y) + : BC_CheckBox(x, y, plugin->config.bands[plugin->config.current_band].solo, _("Solo band")) +{ + this->plugin = plugin; +} + +int ComprMultiSolo::handle_event() +{ + plugin->config.bands[plugin->config.current_band].solo = get_value(); + for( int i = 0; i < TOTAL_BANDS; i++ ) { + if( i != plugin->config.current_band ) { + plugin->config.bands[i].solo = 0; + } + } + plugin->send_configure_change(); + return 1; +} + + +ComprMultiBypass::ComprMultiBypass(ComprMultiEffect *plugin, int x, int y) + : BC_CheckBox(x, y, plugin->config.bands[plugin->config.current_band].bypass, _("Bypass band")) +{ + this->plugin = plugin; +} + +int ComprMultiBypass::handle_event() +{ + plugin->config.bands[plugin->config.current_band].bypass = get_value(); + plugin->send_configure_change(); + return 1; +} + + +ComprMultiBand::ComprMultiBand(ComprMultiWindow *window, + ComprMultiEffect *plugin, int x, int y, int number, + char *text) + : BC_Radial(x, y, plugin->config.current_band == number, text) +{ + this->window = window; + this->plugin = plugin; + this->number = number; +} + +int ComprMultiBand::handle_event() +{ + if( plugin->config.current_band != number ) { + plugin->config.current_band = number; + window->update(); + } + return 1; +} + +// dump envelope sum +// printf("ComprMultiWindow::update_eqcanvas %d\n", __LINE__); +// for( int i = 0; i < plugin->config.window_size / 2; i++ ) +// { +// double sum = 0; +// for( int band = 0; band < TOTAL_BANDS; band++ ) +// { +// sum += plugin->engines[band]->envelope[i]; +// } +// +// printf("%f ", sum); +// for( int band = 0; band < TOTAL_BANDS; band++ ) +// { +// printf("%f ", plugin->engines[band]->envelope[i]); +// } +// printf("\n"); +// } + + diff --git a/cinelerra-5.1/plugins/compressormulti/comprmultigui.h b/cinelerra-5.1/plugins/compressormulti/comprmultigui.h new file mode 100644 index 00000000..b7722163 --- /dev/null +++ b/cinelerra-5.1/plugins/compressormulti/comprmultigui.h @@ -0,0 +1,220 @@ +/* + * CINELERRA + * Copyright (C) 2008-2019 Adam Williams + * + * 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 + diff --git a/cinelerra-5.1/plugins/denoisefft/denoisefft.C b/cinelerra-5.1/plugins/denoisefft/denoisefft.C index 20b99005..5737d4ce 100644 --- a/cinelerra-5.1/plugins/denoisefft/denoisefft.C +++ b/cinelerra-5.1/plugins/denoisefft/denoisefft.C @@ -434,10 +434,7 @@ void DenoiseFFTEffect::collect_noise() for(int i = 0; i < config.samples; i += WINDOW_SIZE) { collect_engine->process_buffer(collection_start, - WINDOW_SIZE, - 0, - get_direction()); - + WINDOW_SIZE, (Samples*)0, get_direction()); collection_start += step * WINDOW_SIZE; total_windows++; } diff --git a/cinelerra-5.1/plugins/graphic/graphic.C b/cinelerra-5.1/plugins/graphic/graphic.C index 16c2f546..b7cf0722 100644 --- a/cinelerra-5.1/plugins/graphic/graphic.C +++ b/cinelerra-5.1/plugins/graphic/graphic.C @@ -320,29 +320,15 @@ void GraphicCanvas::process(int buttonpress, int motion, int draw) if(draw) { clear_box(0, 0, get_w(), get_h()); - - int niquist = plugin->PluginAClient::project_sample_rate / 2; - int total_frames = plugin->get_gui_update_frames(); - GraphicGUIFrame *frame = (GraphicGUIFrame*)plugin->get_gui_frame(); - - if(frame) - { - delete plugin->last_frame; - plugin->last_frame = frame; - } - else - { - frame = plugin->last_frame; - } -// Draw most recent frame - if(frame) - { +// delete frames up to current tracking position + double tracking_position = plugin->get_tracking_position(); + GraphicGUIFrame *frame = (GraphicGUIFrame *) + plugin->get_gui_frame(tracking_position, 1); + if( frame ) { + int y1 = 0, y2 = 0; set_color(MEGREY); - int y1 = 0; - int y2 = 0; - for(int i = 0; i < get_w(); i++) { @@ -366,23 +352,7 @@ void GraphicCanvas::process(int buttonpress, int motion, int draw) y1 = y2; } } -//printf( "\n"); - - total_frames--; - } - - - - - - -// Delete remaining frames - while(total_frames > 0) - { - PluginClientFrame *frame = plugin->get_gui_frame(); - - if(frame) delete frame; - total_frames--; + delete frame; } } @@ -1131,18 +1101,15 @@ void GraphicEQ::update_gui() { if( !thread ) return; GraphicGUI *window = (GraphicGUI *)thread->window; + window->lock_window("GraphicEQ::update_gui"); //lock here for points, needed by window cursor_motion callback // deleted in load_configuration by read_data - window->lock_window("GraphicEQ::update_gui"); if( load_configuration() && window->canvas->state != GraphicCanvas::DRAG_POINT ) { window->update_canvas(); window->update_textboxes(); } - else { - int total_frames = get_gui_update_frames(); -//printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames); - if( total_frames ) + else if( pending_gui_frames() ) { window->update_canvas(); } window->unlock_window(); @@ -1299,15 +1266,14 @@ void GraphicEQ::calculate_envelope(ArrayList *points, } - - GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate) - : PluginClientFrame(window_size / 2, window_size / 2, sample_rate) + : PluginClientFrame() { - data = new double[window_size / 2]; + this->window_size = window_size; + data_size = window_size / 2; + data = new double[data_size]; freq_max = 0; time_max = 0; - this->window_size = window_size; } GraphicGUIFrame::~GraphicGUIFrame() @@ -1316,10 +1282,6 @@ GraphicGUIFrame::~GraphicGUIFrame() } - - - - GraphicFFT::GraphicFFT(GraphicEQ *plugin) : CrossfadeFFT() { diff --git a/cinelerra-5.1/plugins/graphic/graphic.h b/cinelerra-5.1/plugins/graphic/graphic.h index 1517dfe4..ec55c476 100644 --- a/cinelerra-5.1/plugins/graphic/graphic.h +++ b/cinelerra-5.1/plugins/graphic/graphic.h @@ -211,6 +211,7 @@ public: GraphicGUIFrame(int window_size, int sample_rate); virtual ~GraphicGUIFrame(); double *data; + int data_size; // Maximum of window in frequency domain double freq_max; // Maximum of window in time domain diff --git a/cinelerra-5.1/plugins/parametric/parametric.C b/cinelerra-5.1/plugins/parametric/parametric.C index ceb7b1d8..59bd8e5e 100644 --- a/cinelerra-5.1/plugins/parametric/parametric.C +++ b/cinelerra-5.1/plugins/parametric/parametric.C @@ -569,71 +569,33 @@ void ParametricWindow::update_canvas() canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h()); - - // Draw spectrogram - int total_frames = plugin->get_gui_update_frames(); - ParametricGUIFrame *frame = (ParametricGUIFrame*)plugin->get_gui_frame(); - - if(frame) - { - delete plugin->last_frame; - plugin->last_frame = frame; - } - else - { - frame = plugin->last_frame; - } - + double tracking_position = plugin->get_tracking_position(); + ParametricGUIFrame *frame = (ParametricGUIFrame *) + plugin->get_gui_frame(tracking_position, 1); // Draw most recent frame - if(frame) - { + if( frame ) { + int y1 = 0, y2 = 0; canvas->set_color(MEGREY); - int y1 = 0; - int y2 = 0; - for(int i = 0; i < canvas->get_w(); i++) - { + + for(int i = 0; i < canvas->get_w(); i++) { int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w()); int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist; - if(index < frame->window_size / 2) - { + if(index < frame->window_size / 2) { double magnitude = frame->data[index] / - frame->freq_max * - frame->time_max; + frame->freq_max * frame->time_max; y2 = (int)(canvas->get_h() - (DB::todb(magnitude) - INFINITYGAIN) * - canvas->get_h() / - -INFINITYGAIN); + canvas->get_h() / -INFINITYGAIN); CLAMP(y2, 0, canvas->get_h() - 1); if(i > 0) - { canvas->draw_line(i - 1, y1, i, y2); - } y1 = y2; } } - - total_frames--; + delete frame; } - - - - - -// Delete remaining frames - while(total_frames > 0) - { - PluginClientFrame *frame = plugin->get_gui_frame(); - - if(frame) delete frame; - total_frames--; - } - - - - - // canvas->set_color(GREEN); // canvas->draw_line(0, wetness, canvas->get_w(), wetness); // canvas->draw_line(0, @@ -693,16 +655,12 @@ void ParametricWindow::update_canvas() } - - - - - ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate) - : PluginClientFrame(window_size / 2, window_size / 2, sample_rate) + : PluginClientFrame() { this->window_size = window_size; - data = new double[window_size / 2]; + data_size = window_size / 2; + data = new double[data_size]; freq_max = 0; time_max = 0; } @@ -713,14 +671,6 @@ ParametricGUIFrame::~ParametricGUIFrame() } - - - - - - - - ParametricFFT::ParametricFFT(ParametricEQ *plugin) : CrossfadeFFT() { @@ -1029,16 +979,6 @@ int ParametricEQ::process_buffer(int64_t size, return 0; } - - - - - - - - - - void ParametricEQ::reset() { need_reconfigure = 1; @@ -1048,28 +988,16 @@ void ParametricEQ::reset() void ParametricEQ::update_gui() { - if(thread) - { - if(load_configuration()) - { - ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui"); - calculate_envelope(); - ((ParametricWindow*)thread->window)->update_gui(); - ((ParametricWindow*)thread->window)->unlock_window(); - } - else - { - int total_frames = get_gui_update_frames(); -//printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames); - if(total_frames) - { - ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui"); - ((ParametricWindow*)thread->window)->update_canvas(); - ((ParametricWindow*)thread->window)->unlock_window(); - } - } + if( !thread ) return; + ParametricWindow *window = (ParametricWindow*)thread->window; + window->lock_window("ParametricEQ::update_gui"); + if( load_configuration() ) { + calculate_envelope(); + window->update_gui(); } + else if(pending_gui_frames()) { + window->update_canvas(); + } + window->unlock_window(); } - - diff --git a/cinelerra-5.1/plugins/parametric/parametric.h b/cinelerra-5.1/plugins/parametric/parametric.h index 9626fb67..164028eb 100644 --- a/cinelerra-5.1/plugins/parametric/parametric.h +++ b/cinelerra-5.1/plugins/parametric/parametric.h @@ -232,6 +232,7 @@ public: ParametricGUIFrame(int window_size, int sample_rate); virtual ~ParametricGUIFrame(); double *data; + int data_size; // Maximum of window in frequency domain double freq_max; // Maximum of window in time domain diff --git a/cinelerra-5.1/plugins/reverb/reverb.C b/cinelerra-5.1/plugins/reverb/reverb.C index b5bddf2a..4248a3cd 100644 --- a/cinelerra-5.1/plugins/reverb/reverb.C +++ b/cinelerra-5.1/plugins/reverb/reverb.C @@ -1,7 +1,6 @@ - /* * CINELERRA - * Copyright (C) 2017 Adam Williams + * Copyright (C) 2017-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include "clip.h" @@ -29,6 +27,7 @@ #include "reverb.h" #include "reverbwindow.h" #include "samples.h" +#include "transportque.inc" #include "units.h" #include "vframe.h" @@ -39,63 +38,48 @@ #include - PluginClient* new_plugin(PluginServer *server) { return new Reverb(server); } - - Reverb::Reverb(PluginServer *server) : PluginAClient(server) { srand(time(0)); - redo_buffers = 1; // set to redo buffers before the first render - dsp_in_length = 0; ref_channels = 0; ref_offsets = 0; ref_levels = 0; - ref_lowpass = 0; dsp_in = 0; - lowpass_in1 = 0; - lowpass_in2 = 0; - initialized = 0; - + dsp_in_length = 0; + dsp_in_allocated = 0; + need_reconfigure = 1; + envelope = 0; + last_position = 0; + start_pos = 0; + dir = 0; + fft = 0; } Reverb::~Reverb() { - - - if(initialized) - { - for(int i = 0; i < total_in_buffers; i++) - { + if( fft ) { + for( int i = 0; i < total_in_buffers; i++ ) { delete [] dsp_in[i]; delete [] ref_channels[i]; delete [] ref_offsets[i]; delete [] ref_levels[i]; - delete [] ref_lowpass[i]; - delete [] lowpass_in1[i]; - delete [] lowpass_in2[i]; + delete fft[i]; } + delete [] fft; delete [] dsp_in; delete [] ref_channels; delete [] ref_offsets; delete [] ref_levels; - delete [] ref_lowpass; - delete [] lowpass_in1; - delete [] lowpass_in2; - - for(int i = 0; i < (smp + 1); i++) - { - delete engine[i]; - } - delete [] engine; - initialized = 0; + delete engine; } + delete [] envelope; } const char* Reverb::plugin_title() { return N_("Reverb"); } @@ -103,184 +87,220 @@ int Reverb::is_realtime() { return 1; } int Reverb::is_multichannel() { return 1; } int Reverb::is_synthesis() { return 1; } -int Reverb::process_realtime(int64_t size, - Samples **input_ptr, - Samples **output_ptr) -{ - int64_t new_dsp_length, i, j; - main_in = new double*[total_in_buffers]; - main_out = new double*[total_in_buffers]; - for(i = 0; i < total_in_buffers; i++) - { - main_in[i] = input_ptr[i]->get_data(); - main_out[i] = output_ptr[i]->get_data(); +void Reverb::reset() +{ + dsp_in_length = 0; + if( fft ) { + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) + if( fft[i] ) fft[i]->delete_fft(); } - -//printf("Reverb::process_realtime 1\n"); - redo_buffers |= load_configuration(); - -//printf("Reverb::process_realtime 1\n"); - if(!config.ref_total) - { - delete [] main_in; - delete [] main_out; - return 0; + if( dsp_in ) { + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + if( !dsp_in[i] ) continue; + bzero(dsp_in[i], sizeof(double) * dsp_in_allocated); + } } + need_reconfigure = 1; +} + +void Reverb::render_stop() +{ + reset(); +} - if(!initialized) - { - dsp_in = new double*[total_in_buffers]; - ref_channels = new int64_t*[total_in_buffers]; - ref_offsets = new int64_t*[total_in_buffers]; - ref_levels = new double*[total_in_buffers]; - ref_lowpass = new int64_t*[total_in_buffers]; - lowpass_in1 = new double*[total_in_buffers]; - lowpass_in2 = new double*[total_in_buffers]; +int Reverb::process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate) +{ + start_pos = (double)start_position / sample_rate; + dir = get_direction() == PLAY_REVERSE ? -1 : 1; + need_reconfigure |= load_configuration(); + if( need_reconfigure ) { + need_reconfigure = 0; + calculate_envelope(); - for(i = 0; i < total_in_buffers; i++) - { - dsp_in[i] = new double[1]; - ref_channels[i] = new int64_t[1]; - ref_offsets[i] = new int64_t[1]; - ref_levels[i] = new double[1]; - ref_lowpass[i] = new int64_t[1]; - lowpass_in1[i] = new double[1]; - lowpass_in2[i] = new double[1]; + if( fft && fft[0]->window_size != config.window_size ) { + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) + delete fft[i]; + delete [] fft; fft = 0; } - engine = new ReverbEngine*[(smp + 1)]; - for(i = 0; i < (smp + 1); i++) - { - engine[i] = new ReverbEngine(this); -//printf("Reverb::start_realtime %d\n", Thread::calculate_realtime()); -// Realtime priority moved to sound driver -// engine[i]->set_realtime(realtime_priority); - engine[i]->start(); + if( !fft ) { + fft = new ReverbFFT*[PluginClient::total_in_buffers]; + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + fft[i] = new ReverbFFT(this, i); + fft[i]->initialize(config.window_size); + } } - initialized = 1; - redo_buffers = 1; - } - new_dsp_length = size + - ((int64_t)config.delay_init + config.ref_length) * project_sample_rate / 1000 + 1; +// allocate the stuff + if( !dsp_in ) { + dsp_in = new double*[PluginClient::total_in_buffers]; + ref_channels = new int*[PluginClient::total_in_buffers]; + ref_offsets = new int*[PluginClient::total_in_buffers]; + ref_levels = new double*[PluginClient::total_in_buffers]; + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + dsp_in[i] = 0; + ref_channels[i] = 0; + ref_offsets[i] = 0; + ref_levels[i] = 0; + } + engine = new ReverbEngine(this); + } - if(redo_buffers || new_dsp_length != dsp_in_length) - { - for(i = 0; i < total_in_buffers; i++) - { - double *old_dsp = dsp_in[i]; - double *new_dsp = new double[new_dsp_length]; - for(j = 0; j < dsp_in_length && j < new_dsp_length; j++) - new_dsp[j] = old_dsp[j]; + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + delete [] ref_channels[i]; ref_channels[i] = new int[config.ref_total]; + delete [] ref_offsets[i]; ref_offsets[i] = new int[config.ref_total]; + delete [] ref_levels[i]; ref_levels[i] = new double[config.ref_total]; - for( ; j < new_dsp_length; j++) new_dsp[j] = 0; - delete [] old_dsp; - dsp_in[i] = new_dsp; - } +// 1st reflection is fixed by the user + ref_channels[i][0] = i; + ref_offsets[i][0] = config.delay_init * project_sample_rate / 1000; + ref_levels[i][0] = db.fromdb(config.ref_level1); - dsp_in_length = new_dsp_length; - redo_buffers = 1; - } -//printf("Reverb::process_realtime 1\n"); - - if(redo_buffers) - { - for(i = 0; i < total_in_buffers; i++) - { - delete [] ref_channels[i]; - delete [] ref_offsets[i]; - delete [] ref_lowpass[i]; - delete [] ref_levels[i]; - delete [] lowpass_in1[i]; - delete [] lowpass_in2[i]; - - ref_channels[i] = new int64_t[config.ref_total + 1]; - ref_offsets[i] = new int64_t[config.ref_total + 1]; - ref_lowpass[i] = new int64_t[config.ref_total + 1]; - ref_levels[i] = new double[config.ref_total + 1]; - lowpass_in1[i] = new double[config.ref_total + 1]; - lowpass_in2[i] = new double[config.ref_total + 1]; - -// set channels - ref_channels[i][0] = i; // primary noise - ref_channels[i][1] = i; // first reflection -// set offsets - ref_offsets[i][0] = 0; - ref_offsets[i][1] = config.delay_init * project_sample_rate / 1000; -// set levels - ref_levels[i][0] = db.fromdb(config.level_init); - ref_levels[i][1] = db.fromdb(config.ref_level1); -// set lowpass - ref_lowpass[i][0] = -1; // ignore first noise - ref_lowpass[i][1] = config.lowpass1; - lowpass_in1[i][0] = 0; - lowpass_in2[i][0] = 0; - lowpass_in1[i][1] = 0; - lowpass_in2[i][1] = 0; - - int64_t ref_division = config.ref_length * project_sample_rate / 1000 / (config.ref_total + 1); - for(j = 2; j < config.ref_total + 1; j++) - { + int64_t ref_division = config.ref_length * + project_sample_rate / 1000 / (config.ref_total + 1); + for( int j = 1; j < config.ref_total; j++ ) { // set random channels for remaining reflections ref_channels[i][j] = rand() % total_in_buffers; // set random offsets after first reflection - ref_offsets[i][j] = ref_offsets[i][1]; - if( ref_division > 0 ) - ref_offsets[i][j] += ref_division * j - (rand() % ref_division) / 2; - + ref_offsets[i][j] = ref_offsets[i][0]; + ref_offsets[i][j] += ref_division * j - (rand() % ref_division) / 2; // set changing levels - ref_levels[i][j] = db.fromdb(config.ref_level1 + (config.ref_level2 - config.ref_level1) / (config.ref_total - 1) * (j - 2)); - //ref_levels[i][j] /= 100; - -// set changing lowpass as linear - ref_lowpass[i][j] = (int64_t)(config.lowpass1 + (double)(config.lowpass2 - config.lowpass1) / (config.ref_total - 1) * (j - 2)); - lowpass_in1[i][j] = 0; - lowpass_in2[i][j] = 0; + double level_db = config.ref_level1 + + (config.ref_level2 - config.ref_level1) * + (j - 1) / (config.ref_total - 1); + ref_levels[i][j] = DB::fromdb(level_db); } } + } - redo_buffers = 0; +// guess DSP allocation from the reflection time & requested samples + int new_dsp_allocated = size + + ((int64_t)config.delay_init + config.ref_length) * + project_sample_rate / 1000 + 1; + reallocate_dsp(new_dsp_allocated); + +// Always read in the new samples & process the bandpass, even if there is no +// bandpass. This way the user can tweek the bandpass without causing glitches. + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + new_dsp_length = dsp_in_length; + new_spectrogram_frames = 0; + fft[i]->process_buffer(start_position, size, + buffer[i], // temporary storage for the bandpassed output + get_direction()); } -//printf("Reverb::process_realtime 1\n"); - for(i = 0; i < total_in_buffers; ) - { - for(j = 0; j < (smp + 1) && (i + j) < total_in_buffers; j++) - { - engine[j]->process_overlays(i + j, size); - } +// update the length with what the FFT reads appended + dsp_in_length = new_dsp_length; - for(j = 0; j < (smp + 1) && i < total_in_buffers; j++, i++) - { - engine[j]->wait_process_overlays(); - } +// now paint the reflections + engine->process_packages(); + +// copy the DSP buffer to the output + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + memcpy(buffer[i]->get_data(), dsp_in[i], size * sizeof(double)); } -//printf("Reverb::process_realtime 2 %d %d\n", total_in_buffers, size); - for(i = 0; i < total_in_buffers; i++) - { - double *current_out = main_out[i]; - double *current_in = dsp_in[i]; +// shift the DSP buffer forward + int remain = dsp_in_allocated - size; + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + memcpy(dsp_in[i], dsp_in[i] + size, remain * sizeof(double)); + bzero(dsp_in[i] + remain, size * sizeof(double)); + } + + dsp_in_length -= size; + last_position = start_position + dir * size; + return 0; +} - for(j = 0; j < size; j++) current_out[j] = current_in[j]; - int64_t k; - for(k = 0; j < dsp_in_length; j++, k++) current_in[k] = current_in[j]; +void Reverb::reallocate_dsp(int new_dsp_allocated) +{ + if( new_dsp_allocated > dsp_in_allocated ) { +// copy samples already read into the new buffers + for( int i = 0; i < PluginClient::total_in_buffers; i++ ) { + double *old_dsp = dsp_in[i]; + double *new_dsp = new double[new_dsp_allocated]; - for(; k < dsp_in_length; k++) current_in[k] = 0; + if( old_dsp ) { + memcpy(new_dsp, old_dsp, sizeof(double) * dsp_in_length); + delete [] old_dsp; + } + bzero(new_dsp + dsp_in_allocated, + sizeof(double) * (new_dsp_allocated - dsp_in_allocated)); + dsp_in[i] = new_dsp; + } + dsp_in_allocated = new_dsp_allocated; } -//printf("Reverb::process_realtime 2 %d %d\n", total_in_buffers, size); +} - delete [] main_in; - delete [] main_out; - return 0; + +double Reverb::gauss(double sigma, double center, double x) +{ + if( EQUIV(sigma, 0) ) sigma = 0.01; + + double result = 1.0 / sqrt(2 * M_PI * sigma * sigma) * + exp(-(x - center) * (x - center) / (2 * sigma * sigma)); + return result; +} + +void Reverb::calculate_envelope() +{ +// assume the window size changed + delete [] envelope; + int window_size2 = config.window_size/2; + envelope = new double[window_size2]; + + int max_freq = Freq::tofreq_f(TOTALFREQS-1); + int nyquist = PluginAClient::project_sample_rate/2; + int low = config.low; + int high = config.high; + +// limit the frequencies + if( high >= max_freq ) high = nyquist; + if( low > high ) low = high; + +// frequency slots of the edge + double edge = (1.0 - config.q) * TOTALFREQS/2; + double low_slot = Freq::fromfreq_f(low); + double high_slot = Freq::fromfreq_f(high); + for( int i = 0; i < window_size2; i++ ) { + double freq = i * nyquist / window_size2; + double slot = Freq::fromfreq_f(freq); + +// printf("Reverb::calculate_envelope %d i=%d freq=%f slot=%f slot1=%f\n", +// __LINE__, i, freq, slot, low_slot - edge); + if( slot < low_slot - edge ) { + envelope[i] = 0.0; + } + else if( slot < low_slot ) { +#ifndef LOG_CROSSOVER + envelope[i] = 1.0 - (low_slot - slot) / edge; +#else + envelope[i] = DB::fromdb((low_slot - slot) * INFINITYGAIN / edge); +#endif + } + else if( slot < high_slot ) { + envelope[i] = 1.0; + } + else if( slot < high_slot + edge ) { +#ifndef LOG_CROSSOVER + envelope[i] = 1.0 - (slot - high_slot) / edge; +#else + envelope[i] = DB::fromdb((slot - high_slot) * INFINITYGAIN / edge); +#endif + } + else { + envelope[i] = 0.0; + } + } } @@ -292,54 +312,41 @@ LOAD_CONFIGURATION_MACRO(Reverb, ReverbConfig) void Reverb::save_data(KeyFrame *keyframe) { -//printf("Reverb::save_data 1\n"); FileXML output; -//printf("Reverb::save_data 1\n"); - -// cause xml file to store data directly in text output.set_shared_output(keyframe->xbuf); -//printf("Reverb::save_data 1\n"); - output.tag.set_title("REVERB"); output.tag.set_property("LEVELINIT", config.level_init); output.tag.set_property("DELAY_INIT", config.delay_init); output.tag.set_property("REF_LEVEL1", config.ref_level1); output.tag.set_property("REF_LEVEL2", config.ref_level2); output.tag.set_property("REF_TOTAL", config.ref_total); -//printf("Reverb::save_data 1\n"); output.tag.set_property("REF_LENGTH", config.ref_length); - output.tag.set_property("LOWPASS1", config.lowpass1); - output.tag.set_property("LOWPASS2", config.lowpass2); -//printf("Reverb::save_data config.ref_level2 %f\n", config.ref_level2); - output.append_tag(); - output.tag.set_title("/REVERB"); + output.tag.set_property("HIGH", config.high); + output.tag.set_property("LOW", config.low); + output.tag.set_property("Q", config.q); + output.tag.set_property("WINDOW_SIZE", config.window_size); output.append_tag(); output.append_newline(); output.terminate_string(); -//printf("Reverb::save_data 2\n"); } void Reverb::read_data(KeyFrame *keyframe) { FileXML input; -// cause xml file to read directly from text input.set_shared_input(keyframe->xbuf); int result = 0; - - result = input.read_tag(); - - if(!result) - { - if(input.tag.title_is("REVERB")) - { + while( !(result = input.read_tag()) ) { + if( input.tag.title_is("REVERB") ) { config.level_init = input.tag.get_property("LEVELINIT", config.level_init); config.delay_init = input.tag.get_property("DELAY_INIT", config.delay_init); config.ref_level1 = input.tag.get_property("REF_LEVEL1", config.ref_level1); config.ref_level2 = input.tag.get_property("REF_LEVEL2", config.ref_level2); config.ref_total = input.tag.get_property("REF_TOTAL", config.ref_total); config.ref_length = input.tag.get_property("REF_LENGTH", config.ref_length); - config.lowpass1 = input.tag.get_property("LOWPASS1", config.lowpass1); - config.lowpass2 = input.tag.get_property("LOWPASS2", config.lowpass2); + config.high = input.tag.get_property("HIGH", config.high); + config.low = input.tag.get_property("LOW", config.low); + config.q = input.tag.get_property("Q", config.q); + config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size); } } @@ -348,186 +355,182 @@ void Reverb::read_data(KeyFrame *keyframe) void Reverb::update_gui() { - if(thread) - { - if(load_configuration()) - { -//printf("Reverb::update_gui %d %d\n", __LINE__, config.ref_length); - thread->window->lock_window("Reverb::update_gui"); - ((ReverbWindow*)thread->window)->level_init->update(config.level_init); - ((ReverbWindow*)thread->window)->delay_init->update(config.delay_init); - ((ReverbWindow*)thread->window)->ref_level1->update(config.ref_level1); - ((ReverbWindow*)thread->window)->ref_level2->update(config.ref_level2); - ((ReverbWindow*)thread->window)->ref_total->update(config.ref_total); - ((ReverbWindow*)thread->window)->ref_length->update(config.ref_length); - ((ReverbWindow*)thread->window)->lowpass1->update(config.lowpass1); - ((ReverbWindow*)thread->window)->lowpass2->update(config.lowpass2); - thread->window->unlock_window(); - } - } + if( !thread ) return; + ReverbWindow *window = (ReverbWindow *)thread->window; + if( !window ) return; + int reconfigured = load_configuration(); + int total_frames = pending_gui_frames(); + if( !reconfigured && !total_frames ) return; + window->lock_window("Reverb::update_gui 1"); + if( reconfigured ) + window->update(); + if( total_frames ) + window->update_canvas(); + window->unlock_window(); } +ReverbClientFrame::ReverbClientFrame(int size) + : CompressorFreqFrame() +{ + data = new double[size]; + bzero(data, sizeof(double) * size); + data_size = size; +} +ReverbClientFrame::~ReverbClientFrame() +{ + delete [] data; data = 0; +} - -// int Reverb::load_from_file(char *path) -// { -// FILE *in; -// int result = 0; -// int length; -// char string[1024]; -// -// if(in = fopen(path, "rb")) -// { -// fseek(in, 0, SEEK_END); -// length = ftell(in); -// fseek(in, 0, SEEK_SET); -// int temp = fread(string, length, 1, in); -// fclose(in); -// // read_data(string); -// } -// else -// { -// perror("fopen:"); -// // failed -// ErrorBox errorbox(""); -// char string[1024]; -// sprintf(string, _("Couldn't open %s."), path); -// errorbox.create_objects(string); -// errorbox.run_window(); -// result = 1; -// } -// -// return result; -// } -// -// int Reverb::save_to_file(char *path) -// { -// FILE *out; -// int result = 0; -// char string[1024]; -// -// { -// // ConfirmSave confirm; -// // result = confirm.test_file("", path); -// } -// -// if(!result) -// { -// if(out = fopen(path, "wb")) -// { -// // save_data(string); -// fwrite(string, strlen(string), 1, out); -// fclose(out); -// } -// else -// { -// result = 1; -// // failed -// ErrorBox errorbox(""); -// char string[1024]; -// sprintf(string, _("Couldn't save %s."), path); -// errorbox.create_objects(string); -// errorbox.run_window(); -// result = 1; -// } -// } -// -// return result; -// } - -ReverbEngine::ReverbEngine(Reverb *plugin) - : Thread(1, 0, 0) +ReverbFFT::ReverbFFT(Reverb *plugin, int channel) { this->plugin = plugin; - completed = 0; - input_lock.lock(); - output_lock.lock(); + this->channel = channel; } -ReverbEngine::~ReverbEngine() +ReverbFFT::~ReverbFFT() { - completed = 1; - input_lock.unlock(); - join(); } -int ReverbEngine::process_overlays(int output_buffer, int64_t size) +int ReverbFFT::signal_process() { - this->output_buffer = output_buffer; - this->size = size; - input_lock.unlock(); +// Create new spectrogram for updating the GUI + frame = new ReverbClientFrame(window_size/2); + frame->nyquist = plugin->PluginAClient::project_sample_rate/2; + frame->position = plugin->start_pos + plugin->dir * + (double)plugin->new_spectrogram_frames * frame->data_size / + plugin->get_samplerate(); + plugin->add_gui_frame(frame); + + for( int i=0; idata_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 time_max ) + time_max = fabs(output_real[i]); + } + if( time_max > frame->time_max ) + frame->time_max = time_max; + + ++plugin->new_spectrogram_frames; return 0; } -int ReverbEngine::process_overlay(double *in, double *out, double &out1, double &out2, double level, int64_t lowpass, int64_t samplerate, int64_t size) + +int ReverbFFT::read_samples(int64_t output_sample, int samples, Samples *buffer) { -// Modern niquist frequency is 44khz but pot limit is 20khz so can't use -// niquist - if(lowpass == -1 || lowpass >= 20000) - { -// no lowpass filter - for(int i = 0; i < size; i++) out[i] += in[i] * level; + int result = plugin->read_samples(buffer, + channel, plugin->get_samplerate(), output_sample, samples); + +// append original samples to the DSP buffer as the initial reflection + int new_dsp_allocation = plugin->new_dsp_length + samples; + plugin->reallocate_dsp(new_dsp_allocation); + double *dst = plugin->dsp_in[channel] + plugin->new_dsp_length; + double *src = buffer->get_data(); + double level = DB::fromdb(plugin->config.level_init); + if( plugin->config.level_init <= INFINITYGAIN ) { + level = 0; } - else - { - double coef = 0.25 * 2.0 * M_PI * (double)lowpass / (double)plugin->project_sample_rate; - double a = coef * 0.25; - double b = coef * 0.50; - - for(int i = 0; i < size; i++) - { - out2 += a * (3 * out1 + in[i] - out2); - out2 += b * (out1 + in[i] - out2); - out2 += a * (out1 + 3 * in[i] - out2); - out2 += coef * (in[i] - out2); - out1 = in[i]; - out[i] += out2 * level; - } + + for( int i = 0; i < samples; i++ ) { + *dst++ += *src++ * level; } - return 0; + + plugin->new_dsp_length += samples; + return result; } -void ReverbEngine::run() -{ - int j, i; -//printf("ReverbEngine::run 1 %d\n", calculate_realtime()); - while(1) - { - input_lock.lock(); - if(completed) return; - -// Process reverb - for(i = 0; i < plugin->total_in_buffers; i++) - { - for(j = 0; j < plugin->config.ref_total + 1; j++) - { - if(plugin->ref_channels[i][j] == output_buffer) - process_overlay(plugin->main_in[i], - &(plugin->dsp_in[plugin->ref_channels[i][j]][plugin->ref_offsets[i][j]]), - plugin->lowpass_in1[i][j], - plugin->lowpass_in2[i][j], - plugin->ref_levels[i][j], - plugin->ref_lowpass[i][j], - plugin->project_sample_rate, - size); - } + +ReverbPackage::ReverbPackage() + : LoadPackage() +{ +} + + +ReverbUnit::ReverbUnit(ReverbEngine *engine, Reverb *plugin) + : LoadClient(engine) +{ + this->plugin = plugin; +} + +ReverbUnit::~ReverbUnit() +{ +} + +void ReverbUnit::process_package(LoadPackage *package) +{ + ReverbPackage *pkg = (ReverbPackage*)package; + int channel = pkg->channel; + + for( int i = 0; i < plugin->config.ref_total; i++ ) { + int src_channel = plugin->ref_channels[channel][i]; + int dst_offset = plugin->ref_offsets[channel][i]; + double level = plugin->ref_levels[channel][i]; + double *dst = plugin->dsp_in[channel] + dst_offset; + double *src = plugin->get_output(src_channel)->get_data(); + int size = plugin->get_buffer_size(); + + if( size + dst_offset > plugin->dsp_in_allocated ) { + printf("ReverbUnit::process_package %d size=%d dst_offset=%d needed=%d allocated=%d\n", + __LINE__, size, dst_offset, size + dst_offset, plugin->dsp_in_allocated); } - output_lock.unlock(); + for( int j = 0; j < size; j++ ) + *dst++ += *src++ * level; } } +ReverbEngine::ReverbEngine(Reverb *plugin) + : LoadServer(plugin->PluginClient::smp + 1, plugin->total_in_buffers) +{ + this->plugin = plugin; +} + +ReverbEngine::~ReverbEngine() +{ +} + + +void ReverbEngine::init_packages() +{ + for( int i = 0; i < LoadServer::get_total_packages(); i++ ) { + ReverbPackage *package = (ReverbPackage*)LoadServer::get_package(i); + package->channel = i; + } +} +LoadClient* ReverbEngine::new_client() +{ + return new ReverbUnit(this, plugin); +} +LoadPackage* ReverbEngine::new_package() +{ + return new ReverbPackage; +} ReverbConfig::ReverbConfig() @@ -536,11 +539,12 @@ ReverbConfig::ReverbConfig() delay_init = 0; ref_level1 = -20; ref_level2 = INFINITYGAIN; - ref_total = 100; + ref_total = 128; ref_length = 600; - lowpass1 = Freq::tofreq(TOTALFREQS); - lowpass2 = Freq::tofreq(TOTALFREQS); - + high = Freq::tofreq(TOTALFREQS); + low = Freq::tofreq(0); + q = 1.0; + window_size = 4096; } int ReverbConfig::equivalent(ReverbConfig &that) @@ -551,8 +555,10 @@ int ReverbConfig::equivalent(ReverbConfig &that) EQUIV(ref_level2, that.ref_level2) && ref_total == that.ref_total && ref_length == that.ref_length && - lowpass1 == that.lowpass1 && - lowpass2 == that.lowpass2); + high == that.high && + low == that.low && + EQUIV(q, that.q) && + window_size == that.window_size); } void ReverbConfig::copy_from(ReverbConfig &that) @@ -563,15 +569,14 @@ void ReverbConfig::copy_from(ReverbConfig &that) ref_level2 = that.ref_level2; ref_total = that.ref_total; ref_length = that.ref_length; - lowpass1 = that.lowpass1; - lowpass2 = that.lowpass2; + high = that.high; + low = that.low; + q = that.q; + window_size = that.window_size; } -void ReverbConfig::interpolate(ReverbConfig &prev, - ReverbConfig &next, - int64_t prev_frame, - int64_t next_frame, - int64_t current_frame) +void ReverbConfig::interpolate(ReverbConfig &prev, ReverbConfig &next, + int64_t prev_frame, int64_t next_frame, int64_t current_frame) { level_init = prev.level_init; delay_init = prev.delay_init; @@ -579,28 +584,30 @@ void ReverbConfig::interpolate(ReverbConfig &prev, ref_level2 = prev.ref_level2; ref_total = prev.ref_total; ref_length = prev.ref_length; - lowpass1 = prev.lowpass1; - lowpass2 = prev.lowpass2; + high = prev.high; + low = prev.low; + q = prev.q; + window_size = prev.window_size; } void ReverbConfig::boundaries() { - CLAMP(level_init, INFINITYGAIN, 0); CLAMP(delay_init, 0, MAX_DELAY_INIT); CLAMP(ref_level1, INFINITYGAIN, 0); CLAMP(ref_level2, INFINITYGAIN, 0); CLAMP(ref_total, MIN_REFLECTIONS, MAX_REFLECTIONS); - CLAMP(ref_length, 0, MAX_REFLENGTH); - CLAMP(lowpass1, 0, Freq::tofreq(TOTALFREQS)); - CLAMP(lowpass2, 0, Freq::tofreq(TOTALFREQS)); + CLAMP(ref_length, MIN_REFLENGTH, MAX_REFLENGTH); + CLAMP(high, 0, Freq::tofreq(TOTALFREQS)); + CLAMP(low, 0, Freq::tofreq(TOTALFREQS)); + CLAMP(q, 0.0, 1.0); } void ReverbConfig::dump() { - printf("ReverbConfig::dump %f %jd %f %f %jd %jd %jd %jd\n", - level_init, delay_init, ref_level1, ref_level2, - ref_total, ref_length, lowpass1, lowpass2); + printf("ReverbConfig::dump %d level_init=%f delay_init=%d ref_level1=%f" + " ref_level2=%f ref_total=%d ref_length=%d high=%d low=%d q=%f\n", + __LINE__, level_init, (int)delay_init, ref_level1, ref_level2, + (int)ref_total, (int)ref_length, (int)high, (int)low, q); } - diff --git a/cinelerra-5.1/plugins/reverb/reverb.h b/cinelerra-5.1/plugins/reverb/reverb.h index 629c74ce..2b889c89 100644 --- a/cinelerra-5.1/plugins/reverb/reverb.h +++ b/cinelerra-5.1/plugins/reverb/reverb.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2008-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef REVERB_H @@ -24,13 +23,18 @@ class Reverb; class ReverbEngine; +class ReverbFFT; +#include "compressortools.h" +#include "fourier.h" #include "reverbwindow.h" +#include "loadbalance.h" #include "pluginaclient.h" #define MAX_DELAY_INIT 1000 #define MIN_REFLECTIONS 1 #define MAX_REFLECTIONS 255 +#define MIN_REFLENGTH 3 #define MAX_REFLENGTH 5000 class ReverbConfig @@ -41,21 +45,23 @@ public: int equivalent(ReverbConfig &that); void copy_from(ReverbConfig &that); - void interpolate(ReverbConfig &prev, - ReverbConfig &next, - int64_t prev_frame, - int64_t next_frame, - int64_t current_frame); + void interpolate(ReverbConfig &prev, ReverbConfig &next, + int64_t prev_frame, int64_t next_frame, int64_t current_frame); void dump(); void boundaries(); - double level_init; - int64_t delay_init; - double ref_level1; - double ref_level2; - int64_t ref_total; - int64_t ref_length; - int64_t lowpass1, lowpass2; + float level_init; + int delay_init; + float ref_level1; + float ref_level2; + int ref_total; + int ref_length; +// high frequency + int high; +// low frequency + int low; + float q; + int window_size; }; class Reverb : public PluginAClient @@ -64,54 +70,110 @@ public: Reverb(PluginServer *server); ~Reverb(); + void reset(); + void render_stop(); void update_gui(); -// int load_from_file(char *data); -// int save_to_file(char *data); - -// data for reverb - char config_directory[1024]; - double **main_in, **main_out; - double **dsp_in; - int64_t **ref_channels, **ref_offsets, **ref_lowpass; - double **ref_levels; - int64_t dsp_in_length; - int redo_buffers; -// skirts for lowpass filter - double **lowpass_in1, **lowpass_in2; - DB db; // required for all realtime/multichannel plugins - PLUGIN_CLASS_MEMBERS(ReverbConfig); - int process_realtime(int64_t size, Samples **input_ptr, Samples **output_ptr); + int process_buffer(int64_t size, Samples **buffer, + int64_t start_position, int sample_rate); + double gauss(double sigma, double center, double x); + void calculate_envelope(); + void reallocate_dsp(int new_dsp_allocated); + int is_realtime(); int is_synthesis(); int is_multichannel(); - int show_gui(); - int set_string(); void save_data(KeyFrame *keyframe); void read_data(KeyFrame *keyframe); - void raise_window(); - ReverbEngine **engine; - int initialized; +// the output all reflections are painted on + double **dsp_in; +// may have to expand it for fft windows larger than the reflected time + int dsp_in_allocated; +// total samples read into dsp_in by the FFT + int dsp_in_length; +// new value calculated by the FFT readers + int new_dsp_length; +// total spectrogram frames generated by the FFT. Each channel overwrites the same +// spectrograms + int new_spectrogram_frames; + +// source channels of reflections + int **ref_channels; +// destination offsets of reflections + int **ref_offsets; +// levels of reflections + double **ref_levels; +// detect seeking + int64_t last_position; +// start_position / sample_rate + double start_pos; +// get_direction fwd=1, rev=-1, stop=0 + int dir; + DB db; + + ReverbEngine *engine; + ReverbFFT **fft; + double *envelope; + int need_reconfigure; +}; + +class ReverbClientFrame : public CompressorFreqFrame +{ +public: + ReverbClientFrame(int size); + ~ReverbClientFrame(); +}; + + +class ReverbFFT :public CrossfadeFFT +{ +public: + ReverbFFT(Reverb *plugin, int channel); + ~ReverbFFT(); + + int signal_process(); + int post_process(); + int read_samples(int64_t output_sample, int samples, Samples *buffer); + + Reverb *plugin; + ReverbClientFrame *frame; + int channel; +}; + + +class ReverbPackage : public LoadPackage +{ +public: + ReverbPackage(); + int channel; +}; + + +class ReverbUnit : public LoadClient +{ +public: + ReverbUnit(ReverbEngine *engine, Reverb *plugin); + ~ReverbUnit(); + void process_package(LoadPackage *package); + ReverbEngine *engine; + Reverb *plugin; }; -class ReverbEngine : public Thread +// This allocates 1 CPU for each output channel. +// They simultaneously read from all the input FFT channels. +class ReverbEngine : public LoadServer { public: ReverbEngine(Reverb *plugin); ~ReverbEngine(); - int process_overlay(double *in, double *out, double &out1, double &out2, double level, int64_t lowpass, int64_t samplerate, int64_t size); - int process_overlays(int output_buffer, int64_t size); - int wait_process_overlays(); - void run(); + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); - Mutex input_lock, output_lock; - int completed; - int output_buffer; - int64_t size; Reverb *plugin; }; diff --git a/cinelerra-5.1/plugins/reverb/reverb.inc b/cinelerra-5.1/plugins/reverb/reverb.inc index f827f6c4..9683150a 100644 --- a/cinelerra-5.1/plugins/reverb/reverb.inc +++ b/cinelerra-5.1/plugins/reverb/reverb.inc @@ -16,13 +16,11 @@ * 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 diff --git a/cinelerra-5.1/plugins/reverb/reverbwindow.C b/cinelerra-5.1/plugins/reverb/reverbwindow.C index 86933363..63a21daf 100644 --- a/cinelerra-5.1/plugins/reverb/reverbwindow.C +++ b/cinelerra-5.1/plugins/reverb/reverbwindow.C @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2008-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,518 +16,387 @@ * 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 - +#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; +} diff --git a/cinelerra-5.1/plugins/reverb/reverbwindow.h b/cinelerra-5.1/plugins/reverb/reverbwindow.h index cc7eb265..b4e3968a 100644 --- a/cinelerra-5.1/plugins/reverb/reverbwindow.h +++ b/cinelerra-5.1/plugins/reverb/reverbwindow.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2008-2019 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,33 +16,33 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef REVERBWINDOW_H #define REVERBWINDOW_H -#define TOTAL_LOADS 5 +#define TOTAL_PARAMS 9 -class ReverbThread; class ReverbWindow; +class ReverbParam; +#include "eqcanvas.inc" #include "guicast.h" #include "mutex.h" #include "pluginclient.h" #include "reverb.inc" - class ReverbLevelInit; class ReverbDelayInit; class ReverbRefLevel1; class ReverbRefLevel2; class ReverbRefTotal; class ReverbRefLength; -class ReverbLowPass1; -class ReverbLowPass2; -class ReverbMenu; +class ReverbHigh; +class ReverbLow; +class ReverbQ; +class ReverbSize; class ReverbWindow : public PluginClientWindow { @@ -51,225 +51,183 @@ public: ~ReverbWindow(); void create_objects(); + void update(); + void update_canvas(); Reverb *reverb; - ReverbLevelInit *level_init; - ReverbDelayInit *delay_init; - ReverbRefLevel1 *ref_level1; - ReverbRefLevel2 *ref_level2; - ReverbRefTotal *ref_total; - ReverbRefLength *ref_length; - ReverbLowPass1 *lowpass1; - ReverbLowPass2 *lowpass2; - ReverbMenu *menu; -}; -class ReverbLevelInit : public BC_FPot -{ -public: - ReverbLevelInit(Reverb *reverb, int x, int y); - ~ReverbLevelInit(); - int handle_event(); - Reverb *reverb; -}; + ReverbParam *params[TOTAL_PARAMS]; -class ReverbDelayInit : public BC_IPot -{ -public: - ReverbDelayInit(Reverb *reverb, int x, int y); - ~ReverbDelayInit(); - int handle_event(); - Reverb *reverb; + ReverbParam *level_init; + ReverbParam *delay_init; + ReverbParam *ref_level1; + ReverbParam *ref_level2; + ReverbParam *ref_total; + ReverbParam *ref_length; + ReverbParam *high; + ReverbParam *low; + ReverbParam *q; + EQCanvas *canvas; + ReverbSize *size; }; -class ReverbRefLevel1 : public BC_FPot +class ReverbSize : public BC_PopupMenu { public: - ReverbRefLevel1(Reverb *reverb, int x, int y); - ~ReverbRefLevel1(); + ReverbSize(ReverbWindow *window, Reverb *plugin, int x, int y); + int handle_event(); - Reverb *reverb; + void create_objects(); // add initial items + void update(int size); + + ReverbWindow *window; + Reverb *plugin; }; -class ReverbRefLevel2 : public BC_FPot + +class ReverbFPot : public BC_FPot { public: - ReverbRefLevel2(Reverb *reverb, int x, int y); - ~ReverbRefLevel2(); + ReverbFPot(ReverbParam *param, int x, int y); int handle_event(); - Reverb *reverb; + ReverbParam *param; }; -class ReverbRefTotal : public BC_IPot +class ReverbIPot : public BC_IPot { public: - ReverbRefTotal(Reverb *reverb, int x, int y); - ~ReverbRefTotal(); + ReverbIPot(ReverbParam *param, int x, int y); int handle_event(); - Reverb *reverb; + ReverbParam *param; }; -class ReverbRefLength : public BC_IPot +class ReverbQPot : public BC_QPot { public: - ReverbRefLength(Reverb *reverb, int x, int y); - ~ReverbRefLength(); + ReverbQPot(ReverbParam *param, int x, int y); int handle_event(); - Reverb *reverb; + ReverbParam *param; }; -class ReverbLowPass1 : public BC_QPot +class ReverbText : public BC_TextBox { public: - ReverbLowPass1(Reverb *reverb, int x, int y); - ~ReverbLowPass1(); + ReverbText(ReverbParam *param, int x, int y, int w, int value); + ReverbText(ReverbParam *param, int x, int y, int w, float value); int handle_event(); - Reverb *reverb; + ReverbParam *param; }; -class ReverbLowPass2 : public BC_QPot +class ReverbParam { public: - ReverbLowPass2(Reverb *reverb, int x, int y); - ~ReverbLowPass2(); - int handle_event(); - Reverb *reverb; -}; + ReverbParam(Reverb *reverb, ReverbWindow *gui, + int x, int x2, int x3, int y, int text_w, + int *output_i, + float *output_f, // floating point output + int *output_q, // frequency output + const char *title, float min, float max); + ~ReverbParam(); + void initialize(); + void update(int skip_text, int skip_pot); -class ReverbLoad; -class ReverbSave; -class ReverbSetDefault; -class ReverbLoadPrev; -class ReverbLoadPrevThread; + float *output_f; + ReverbFPot *fpot; + int *output_i; + ReverbIPot *ipot; + int *output_q; + ReverbQPot *qpot; -class ReverbMenu : public BC_MenuBar -{ -public: - ReverbMenu(Reverb *reverb, ReverbWindow *window); - ~ReverbMenu(); - - void create_objects(BC_Hash *defaults); - int load_defaults(BC_Hash *defaults); - int save_defaults(BC_Hash *defaults); -// most recent loads - int add_load(char *path); - ReverbLoadPrevThread *prev_load_thread; - - int total_loads; - BC_Menu *filemenu; - ReverbWindow *window; + char *title; + ReverbText *text; + ReverbWindow *gui; Reverb *reverb; - ReverbLoad *load; - ReverbSave *save; - ReverbSetDefault *set_default; - ReverbLoadPrev *prev_load[TOTAL_LOADS]; + int x, y; + int x2, x3, text_w; + float min, max; }; -class ReverbSaveThread; -class ReverbLoadThread; -class ReverbLoad : public BC_MenuItem +class ReverbLevelInit : public BC_FPot { public: - ReverbLoad(Reverb *reverb, ReverbMenu *menu); - ~ReverbLoad(); + ReverbLevelInit(Reverb *reverb, int x, int y); + ~ReverbLevelInit(); int handle_event(); Reverb *reverb; - ReverbLoadThread *thread; - ReverbMenu *menu; }; -class ReverbSave : public BC_MenuItem +class ReverbDelayInit : public BC_IPot { public: - ReverbSave(Reverb *reverb, ReverbMenu *menu); - ~ReverbSave(); + ReverbDelayInit(Reverb *reverb, int x, int y); + ~ReverbDelayInit(); int handle_event(); Reverb *reverb; - ReverbSaveThread *thread; - ReverbMenu *menu; }; -class ReverbSetDefault : public BC_MenuItem +class ReverbRefLevel1 : public BC_FPot { public: - ReverbSetDefault(); + ReverbRefLevel1(Reverb *reverb, int x, int y); + ~ReverbRefLevel1(); int handle_event(); + Reverb *reverb; }; -class ReverbLoadPrev : public BC_MenuItem +class ReverbRefLevel2 : public BC_FPot { public: - ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu, char *filename, char *path); - ReverbLoadPrev(Reverb *reverb, ReverbMenu *menu); + ReverbRefLevel2(Reverb *reverb, int x, int y); + ~ReverbRefLevel2(); int handle_event(); - int set_path(char *path); - char path[1024]; Reverb *reverb; - ReverbMenu *menu; }; - -class ReverbLoadPrevThread : public Thread +class ReverbRefTotal : public BC_IPot { public: - ReverbLoadPrevThread(Reverb *reverb, ReverbMenu *menu); - ~ReverbLoadPrevThread(); - void run(); - int set_path(char *path); - char path[1024]; + ReverbRefTotal(Reverb *reverb, int x, int y); + ~ReverbRefTotal(); + int handle_event(); Reverb *reverb; - ReverbMenu *menu; }; - - -class ReverbSaveThread : public Thread +class ReverbRefLength : public BC_IPot { public: - ReverbSaveThread(Reverb *reverb, ReverbMenu *menu); - ~ReverbSaveThread(); - void run(); + ReverbRefLength(Reverb *reverb, int x, int y); + ~ReverbRefLength(); + int handle_event(); Reverb *reverb; - ReverbMenu *menu; }; -class ReverbSaveDialog : public BC_FileBox +class ReverbHigh : public BC_QPot { public: - ReverbSaveDialog(Reverb *reverb); - ~ReverbSaveDialog(); - - int ok_event(); - int cancel_event(); + ReverbHigh(Reverb *reverb, int x, int y); + ~ReverbHigh(); + int handle_event(); Reverb *reverb; }; - -class ReverbLoadThread : public Thread +class ReverbLow : public BC_QPot { public: - ReverbLoadThread(Reverb *reverb, ReverbMenu *menu); - ~ReverbLoadThread(); - void run(); + ReverbLow(Reverb *reverb, int x, int y); + ~ReverbLow(); + int handle_event(); Reverb *reverb; - ReverbMenu *menu; }; -class ReverbLoadDialog : public BC_FileBox +class ReverbQ: public BC_QPot { public: - ReverbLoadDialog(Reverb *reverb); - ~ReverbLoadDialog(); - - int ok_event(); - int cancel_event(); + ReverbQ(Reverb *reverb, int x, int y); + ~ReverbQ(); + int handle_event(); Reverb *reverb; }; - - - - #endif diff --git a/cinelerra-5.1/plugins/reverb/reverbwindow.inc b/cinelerra-5.1/plugins/reverb/reverbwindow.inc index a6e14921..512486df 100644 --- a/cinelerra-5.1/plugins/reverb/reverbwindow.inc +++ b/cinelerra-5.1/plugins/reverb/reverbwindow.inc @@ -16,13 +16,11 @@ * 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; -- 2.26.2