3 * Copyright (C) 2008-2019 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 // Objects for compressors
24 #include "compressortools.h"
28 #include "pluginclient.h"
34 BandConfig::BandConfig()
39 // readahead_len = 1.0;
44 BandConfig::~BandConfig()
49 void BandConfig::save_data(FileXML *xml, int number, int do_multiband)
51 xml->tag.set_title("COMPRESSORBAND");
53 xml->tag.set_property("NUMBER", number);
54 xml->tag.set_property("FREQ", freq);
55 xml->tag.set_property("BYPASS", bypass);
56 xml->tag.set_property("SOLO", solo);
57 xml->tag.set_property("ATTACK_LEN", attack_len);
58 xml->tag.set_property("RELEASE_LEN", release_len);
61 xml->append_newline();
63 for( int i = 0; i < levels.total; i++ ) {
64 xml->tag.set_title("LEVEL");
65 xml->tag.set_property("X", levels.values[i].x);
66 xml->tag.set_property("Y", levels.values[i].y);
68 xml->append_newline();
71 xml->tag.set_title("/COMPRESSORBAND");
73 xml->append_newline();
76 void BandConfig::read_data(FileXML *xml, int do_multiband)
79 freq = xml->tag.get_property("FREQ", freq);
80 bypass = xml->tag.get_property("BYPASS", bypass);
81 solo = xml->tag.get_property("SOLO", solo);
82 attack_len = xml->tag.get_property("ATTACK_LEN", attack_len);
83 release_len = xml->tag.get_property("RELEASE_LEN", release_len);
89 result = xml->read_tag();
91 if( xml->tag.title_is("LEVEL") ) {
92 double x = xml->tag.get_property("X", (double)0);
93 double y = xml->tag.get_property("Y", (double)0);
94 compressor_point_t point = { x, y };
99 if( xml->tag.title_is("/COMPRESSORBAND") ) {
106 void BandConfig::copy_from(BandConfig *src)
109 for( int i = 0; i < src->levels.total; i++ ) {
110 levels.append(src->levels.values[i]);
113 // readahead_len = src->readahead_len;
114 attack_len = src->attack_len;
115 release_len = src->release_len;
118 bypass = src->bypass;
121 int BandConfig::equiv(BandConfig *src)
123 if( levels.total != src->levels.total ||
125 bypass != src->bypass ||
127 // !EQUIV(readahead_len, src->readahead_len) ||
128 !EQUIV(attack_len, src->attack_len) ||
129 !EQUIV(release_len, src->release_len) ) {
133 for( int i = 0; i < levels.total && i < src->levels.total; i++ ) {
134 compressor_point_t *this_level = &levels.values[i];
135 compressor_point_t *that_level = &src->levels.values[i];
136 if( !EQUIV(this_level->x, that_level->x) ||
137 !EQUIV(this_level->y, that_level->y) ) {
145 void BandConfig::boundaries(CompressorConfigBase *base)
147 for( int i = 0; i < levels.size(); i++ ) {
148 compressor_point_t *level = &levels.values[i];
149 if( level->x < base->min_db ) level->x = base->min_db;
150 if( level->y < base->min_db ) level->y = base->min_db;
151 if( level->x > base->max_db ) level->x = base->max_db;
152 if( level->y > base->max_db ) level->y = base->max_db;
157 CompressorConfigBase::CompressorConfigBase(int total_bands)
159 this->total_bands = total_bands;
160 bands = new BandConfig[total_bands];
163 min_value = DB::fromdb(min_db) + 0.001;
164 // min_x = min_db; max_x = 0;
165 // min_y = min_db; max_y = 0;
168 input = CompressorConfigBase::TRIGGER;
169 for( int i=0; i<total_bands; ++i ) {
170 bands[i].freq = Freq::tofreq((i+1) * TOTALFREQS / total_bands);
176 CompressorConfigBase::~CompressorConfigBase()
181 void CompressorConfigBase::boundaries()
183 for( int i=0; i<total_bands; ++i ) {
184 bands[i].boundaries(this);
189 void CompressorConfigBase::copy_from(CompressorConfigBase &that)
191 // min_x = that.min_x; max_x = that.max_x;
192 // min_y = that.min_y; max_y = that.max_y;
193 trigger = that.trigger;
195 smoothing_only = that.smoothing_only;
197 for( int i=0; i<total_bands; ++i ) {
198 BandConfig *dst = &bands[i];
199 BandConfig *src = &that.bands[i];
205 int CompressorConfigBase::equivalent(CompressorConfigBase &that)
207 for( int i=0; i<total_bands; ++i ) {
208 if( !bands[i].equiv(&that.bands[i]) )
212 return trigger == that.trigger &&
213 input == that.input &&
214 smoothing_only == that.smoothing_only;
217 double CompressorConfigBase::get_y(int band, int i)
219 ArrayList<compressor_point_t> &levels = bands[band].levels;
220 int sz = levels.size();
222 if( i >= sz ) i = sz-1;
223 return levels.values[i].y;
226 double CompressorConfigBase::get_x(int band, int i)
228 ArrayList<compressor_point_t> &levels = bands[band].levels;
229 int sz = levels.size();
231 if( i >= sz ) i = sz-1;
232 return levels.values[i].x;
235 double CompressorConfigBase::calculate_db(int band, double x)
237 ArrayList<compressor_point_t> &levels = bands[band].levels;
238 int sz = levels.size();
240 compressor_point_t &point0 = levels[0];
241 double px0 = point0.x, py0 = point0.y, dx0 = x - px0;
242 // the only point. Use slope from min_db
244 return py0 + dx0 * (py0 - min_db) / (px0 - min_db);
245 // before 1st point, use 1:1 gain
246 double ret = py0 + dx0;
249 while( --k >= 0 && levels[k].x > x );
251 compressor_point_t &curr = levels[k];
252 double cx = curr.x, cy = curr.y, dx = x - cx;
253 // between 2 points. Use slope between 2 points
254 // the last point. Use slope of last 2 points
256 compressor_point_t &prev = levels[k+0];
257 compressor_point_t &next = levels[k+1];
258 double px = prev.x, py = prev.y;
259 double nx = next.x, ny = next.y;
260 ret = cy + dx * (ny - py) / (nx - px);
267 int CompressorConfigBase::set_point(int band, double x, double y)
269 ArrayList<compressor_point_t> &levels = bands[band].levels;
270 int k = levels.size(), ret = k;
271 while( --k >= 0 && levels[k].x >= x ) ret = k;
272 compressor_point_t new_point = { x, y };
273 levels.insert(new_point, ret);
277 void CompressorConfigBase::remove_point(int band, int i)
279 ArrayList<compressor_point_t> &levels = bands[band].levels;
280 levels.remove_number(i);
284 double CompressorConfigBase::calculate_output(int band, double x)
286 double x_db = DB::todb(x);
287 return DB::fromdb(calculate_db(band, x_db));
291 double CompressorConfigBase::calculate_gain(int band, double input_linear)
293 double output_linear = calculate_output(band, input_linear);
294 // output is below minimum. Mute it
295 return output_linear < min_value ? 0. :
296 // input is below minimum. Don't change it.
297 fabs(input_linear - 0.0) < min_value ? 1. :
299 output_linear / input_linear;
303 CompressorCanvasBase::CompressorCanvasBase(CompressorConfigBase *config,
304 PluginClient *plugin, PluginClientWindow *window,
305 int x, int y, int w, int h)
306 : BC_SubWindow(x, y, w, h, BLACK)
308 this->config = config;
309 this->plugin = plugin;
310 this->window = window;
311 current_operation = NONE;
315 graph_w = w - graph_x;
316 graph_h = h - graph_y;
318 divisions = (int)(config->max_db - config->min_db) / subdivisions;
321 CompressorCanvasBase::~CompressorCanvasBase()
326 void CompressorCanvasBase::create_objects()
328 add_subwindow(menu = new CompressorPopup(this));
329 menu->create_objects();
331 set_cursor(CROSS_CURSOR, 0, 0);
336 void CompressorCanvasBase::draw_scales()
339 window->set_font(SMALLFONT);
340 window->set_color(get_resources()->default_text_color);
343 for( int i=0; i<=divisions; ++i ) {
344 int y = get_y() + yfudge + graph_y + graph_h * i / divisions;
346 char string[BCTEXTLEN];
347 sprintf(string, "%.0f", config->max_db -
348 (float)i / divisions * (config->max_db - config->min_db));
349 int text_w = get_text_width(SMALLFONT, string);
350 if( i >= divisions ) y -= yfudge;
351 window->draw_text(x-xS(10) - text_w, y, string);
352 if( i >= divisions ) break;
354 int y1 = get_y() + graph_y + graph_h * i / divisions;
355 int y2 = get_y() + graph_y + graph_h * (i + 1) / divisions;
356 int x1 = get_x() - xS(10), x2 = get_x() - xS(5);
357 for( int j=0; j<subdivisions; ++j,x1=x2 ) {
358 y = y1 + (y2 - y1) * j / subdivisions;
359 window->draw_line(x, y, x1, y);
364 for( int i=0; i<=divisions; ++i ) {
365 int y = get_y() + get_h();
366 int x = get_x() + graph_x + graph_w * i / divisions;
367 int y0 = y + window->get_text_ascent(SMALLFONT);
368 char string[BCTEXTLEN];
369 sprintf(string, "%.0f", (float)i / divisions *
370 (config->max_db - config->min_db) + config->min_db);
371 int text_w = get_text_width(SMALLFONT, string);
372 window->draw_text(x - text_w, y0 + yS(10), string);
373 if( i >= divisions ) break;
375 int x1 = get_x() + graph_x + graph_w * i / divisions;
376 int x2 = get_x() + graph_x + graph_w * (i + 1) / divisions;
377 int y1 = y + yS(10), y2 = y + yS(5);
378 for( int j=0; j<subdivisions; ++j,y1=y2 ) {
379 x = x1 + (x2 - x1) * j / subdivisions;
380 window->draw_line(x, y, x, y1);
387 #define POINT_W xS(10)
390 int CompressorCanvasBase::x_to_y(int band, int x)
392 double min_db = config->min_db, max_db = config->max_db;
393 double rng_db = max_db - min_db;
394 double x_db = min_db + (double)x / graph_w * rng_db;
395 double y_db = config->calculate_db(band, x_db);
396 int y = graph_y + graph_h - (int)((y_db - min_db) * graph_h / rng_db);
401 int CompressorCanvasBase::db_to_x(double db)
403 double min_db = config->min_db, max_db = config->max_db;
404 double rng_db = max_db - min_db;
405 int x = graph_x + (double)(db - min_db) * graph_w / rng_db;
410 int CompressorCanvasBase::db_to_y(double db)
412 double min_db = config->min_db, max_db = config->max_db;
413 double rng_db = max_db - min_db;
414 int y = graph_y + graph_h - (int)((db - min_db) * graph_h / rng_db);
419 double CompressorCanvasBase::x_to_db(int x)
421 CLAMP(x, 0, get_w());
422 double min_db = config->min_db, max_db = config->max_db;
423 double rng_db = max_db - min_db;
424 double x_db = (double)(x - graph_x) * rng_db / graph_w + min_db;
425 CLAMP(x_db, min_db, max_db);
429 double CompressorCanvasBase::y_to_db(int y)
431 CLAMP(y, 0, get_h());
432 double min_db = config->min_db, max_db = config->max_db;
433 double rng_db = max_db - min_db;
434 double y_db = (double)(graph_y - y) * rng_db / graph_h + max_db;
435 CLAMP(y_db, min_db, max_db);
441 void CompressorCanvasBase::update()
444 set_color(window->get_bg_color());
445 draw_box(graph_x, 0, get_w(), graph_y);
446 draw_box(graph_w, graph_y, get_w() - graph_w, get_h() - graph_y);
447 // const int checker_w = DP(10);
448 // const int checker_h = DP(10);
449 // set_color(MDGREY);
450 // for( int i = 0; i < get_h(); i += checker_h )
452 // for( int j = (i % 2) * checker_w; j < get_w(); j += checker_w * 2 )
454 // if( !(i >= graph_y &&
455 // i + checker_h < graph_y + graph_h &&
457 // j + checker_w < graph_x + graph_w) )
459 // draw_box(j, i, checker_w, checker_h);
465 set_color(plugin->get_theme()->graph_bg_color);
466 draw_box(graph_x, graph_y, graph_w, graph_h);
469 draw_3d_border(0, 0, get_w(), get_h(), window->get_bg_color(),
470 plugin->get_theme()->graph_border1_color,
471 plugin->get_theme()->graph_border2_color,
472 window->get_bg_color());
475 set_color(plugin->get_theme()->graph_grid_color);
477 for( int i = 1; i < divisions; i++ ) {
478 int y = graph_y + graph_h * i / divisions;
479 draw_line(graph_x, y, graph_x + graph_w, y);
482 draw_line(graph_x, y + 1, graph_x + graph_w, y + 1);
485 int x = graph_x + graph_w * i / divisions;
486 draw_line(x, graph_y, x, graph_y + graph_h);
488 if( i == divisions - 1 ) {
489 draw_line(x + 1, graph_y, x + 1, graph_y + graph_h);
495 set_font(MEDIUMFONT);
496 int border = plugin->get_theme()->widget_border;
497 draw_text(border, get_h() / 2, _("Output"));
498 int tx = get_w() / 2 - get_text_width(MEDIUMFONT, _("Input")) / 2;
499 int ty = get_h() - get_text_height(MEDIUMFONT, _("Input")) - border;
500 draw_text(tx, ty, _("Input"));
502 for( int pass = 0; pass < 2; pass++ ) {
503 for( int band = 0; band < config->total_bands; band++ ) {
504 // draw the active band on top of the others
505 if( band == config->current_band && pass == 0 ||
506 band != config->current_band && pass == 1 ) {
510 if( band == config->current_band ) {
511 set_color(plugin->get_theme()->graph_active_color);
515 set_color(plugin->get_theme()->graph_inactive_color);
520 int x1 = graph_x, y1 = x_to_y(band, x1);
521 for( int i=0; ++i <= graph_w; ) {
522 int x2 = x1+1, y2 = x_to_y(band, x2);
523 draw_line(x1,y1, x2,y2);
530 if( band == config->current_band ) {
531 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
532 for( int i = 0; i < levels.size(); i++ ) {
533 double x_db = config->get_x(band, i);
534 double y_db = config->get_y(band, i);
536 int x = db_to_x(x_db);
537 int y = db_to_y(y_db);
539 if( i == current_point ) {
540 draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
543 draw_rectangle(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
553 int CompressorCanvasBase::button_press_event()
555 // Check existing points
556 if( is_event_win() &&
558 if( get_buttonpress() == 3 ) {
559 menu->activate_menu();
562 int band = config->current_band;
563 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
564 for( int i=0; i<levels.size(); ++i ) {
565 double x_db = config->get_x(config->current_band, i);
566 double y_db = config->get_y(config->current_band, i);
568 int x = db_to_x(x_db);
569 int y = db_to_y(y_db);
571 if( get_cursor_x() <= x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
572 get_cursor_y() <= y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2 ) {
573 current_operation = DRAG;
579 if( get_cursor_x() >= graph_x &&
580 get_cursor_x() < graph_x + graph_w &&
581 get_cursor_y() >= graph_y &&
582 get_cursor_y() < graph_y + graph_h ) {
584 double x_db = x_to_db(get_cursor_x());
585 double y_db = y_to_db(get_cursor_y());
587 current_point = config->set_point(config->current_band, x_db, y_db);
588 current_operation = DRAG;
590 plugin->send_configure_change();
597 int CompressorCanvasBase::button_release_event()
599 int band = config->current_band;
600 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
602 if( current_operation == DRAG ) {
603 if( current_point > 0 ) {
604 if( levels[current_point].x < levels[current_point-1].x ) {
605 config->remove_point(config->current_band, current_point);
609 if( current_point < levels.size()-1 ) {
610 if( levels[current_point].x >= levels[current_point + 1].x ) {
611 config->remove_point(config->current_band, current_point);
616 plugin->send_configure_change();
617 current_operation = NONE;
624 int CompressorCanvasBase::cursor_motion_event()
626 int band = config->current_band;
627 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
629 if( current_operation == DRAG ) {
630 int x = get_cursor_x();
631 int y = get_cursor_y();
632 double x_db = x_to_db(x);
633 double y_db = y_to_db(y);
636 const int grid_precision = 6;
637 x_db = config->max_db + (double)(grid_precision * (int)((x_db - config->max_db) / grid_precision - 0.5));
638 y_db = config->max_db + (double)(grid_precision * (int)((y_db - config->max_db) / grid_precision - 0.5));
642 //printf("CompressorCanvasBase::cursor_motion_event %d x=%d y=%d x_db=%f y_db=%f\n",
643 //__LINE__, x, y, x_db, y_db);
644 levels[current_point].x = x_db;
645 levels[current_point].y = y_db;
647 plugin->send_configure_change();
651 // Change cursor over points
652 if( is_event_win() && cursor_inside() ) {
653 int new_cursor = CROSS_CURSOR;
655 for( int i = 0; i < levels.size(); i++ ) {
656 double x_db = config->get_x(config->current_band, i);
657 double y_db = config->get_y(config->current_band, i);
659 int x = db_to_x(x_db);
660 int y = db_to_y(y_db);
662 if( get_cursor_x() <= x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
663 get_cursor_y() <= y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2 ) {
664 new_cursor = UPRIGHT_ARROW_CURSOR;
669 // out of active area
670 if( get_cursor_x() >= graph_x + graph_w ||
671 get_cursor_y() < graph_y ) {
672 new_cursor = ARROW_CURSOR;
675 if( new_cursor != get_cursor() ) {
676 set_cursor(new_cursor, 0, 1);
683 void CompressorCanvasBase::update_window()
685 printf("CompressorCanvasBase::update_window %d empty\n", __LINE__);
689 int CompressorCanvasBase::is_dragging()
691 return current_operation == DRAG;
695 CompressorClientFrame::CompressorClientFrame()
700 CompressorClientFrame::~CompressorClientFrame()
704 CompressorFreqFrame::CompressorFreqFrame()
706 type = FREQ_COMPRESSORFRAME;
707 data = 0; data_size = 0;
708 freq_max = 0; time_max = 0;
711 CompressorFreqFrame::~CompressorFreqFrame()
716 CompressorGainFrame::CompressorGainFrame()
718 type = GAIN_COMPRESSORFRAME;
722 CompressorGainFrame::~CompressorGainFrame()
726 CompressorEngine::CompressorEngine(CompressorConfigBase *config,
729 this->config = config;
734 CompressorEngine::~CompressorEngine()
739 void CompressorEngine::reset()
742 slope_current_sample = 0;
747 slope_current_sample = 0;
749 gui_frame_samples = 2048;
751 gui_frame_counter = 0;
755 void CompressorEngine::calculate_ranges(int *attack_samples,
756 int *release_samples,
757 int *preview_samples,
760 BandConfig *band_config = &config->bands[band];
761 *attack_samples = labs(Units::round(band_config->attack_len * sample_rate));
762 *release_samples = Units::round(band_config->release_len * sample_rate);
763 CLAMP(*attack_samples, 1, 1000000);
764 CLAMP(*release_samples, 1, 1000000);
765 *preview_samples = MAX(*attack_samples, *release_samples);
769 void CompressorEngine::process(Samples **output_buffer,
770 Samples **input_buffer,
774 int64_t start_position)
776 BandConfig *band_config = &config->bands[band];
780 int trigger = CLIP(config->trigger, 0, channels - 1);
782 gui_gains.remove_all();
783 gui_levels.remove_all();
784 gui_offsets.remove_all();
786 calculate_ranges(&attack_samples,
790 if( slope_current_sample < 0 ) slope_current_sample = slope_samples;
792 double *trigger_buffer = input_buffer[trigger]->get_data();
794 for( int i = 0; i < size; i++ ) {
795 double current_slope = (slope_value2 - slope_value1) /
798 // maximums in the 2 time ranges
799 double attack_slope = -0x7fffffff;
800 double attack_sample = -1;
801 int attack_offset = -1;
802 int have_attack_sample = 0;
803 double release_slope = -0x7fffffff;
804 double release_sample = -1;
805 int release_offset = -1;
806 int have_release_sample = 0;
807 if( slope_current_sample >= slope_samples ) {
808 // start new line segment
809 for( int j = 1; j < preview_samples; j++ ) {
810 GET_TRIGGER(input_buffer[channel]->get_data(), i + j)
811 double new_slope = (sample - current_value) / j;
812 if( j < attack_samples && new_slope >= attack_slope ) {
813 attack_slope = new_slope;
814 attack_sample = sample;
816 have_attack_sample = 1;
819 if( j < release_samples &&
821 new_slope > release_slope ) {
822 release_slope = new_slope;
823 release_sample = sample;
825 have_release_sample = 1;
829 slope_current_sample = 0;
830 if( have_attack_sample && attack_slope >= 0 ) {
832 peak_samples = attack_offset;
833 slope_samples = attack_offset;
834 slope_value1 = current_value;
835 slope_value2 = attack_sample;
836 current_slope = attack_slope;
837 //printf("CompressorEngine::process %d position=%ld slope=%f samples=%d\n",
838 //__LINE__, start_position + i, current_slope, slope_samples);
841 if( have_release_sample ) {
843 slope_samples = release_offset;
844 // slope_samples = release_samples;
845 peak_samples = release_offset;
846 slope_value1 = current_value;
847 slope_value2 = release_sample;
848 current_slope = release_slope;
849 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
850 //__LINE__, start_position + i, current_slope);
855 printf("CompressorEngine::process %d have neither attack nor release position=%ld attack=%f release=%f current_value=%f\n",
856 __LINE__, start_position + i, attack_slope, release_slope, current_value); bug = 1;
861 // check for new peak after the line segment
862 GET_TRIGGER(input_buffer[channel]->get_data(), i + attack_samples)
863 double new_slope = (sample - current_value) /
865 if( current_slope >= 0 ) {
866 if( new_slope > current_slope ) {
867 peak_samples = attack_samples;
868 slope_samples = attack_samples;
869 slope_current_sample = 0;
870 slope_value1 = current_value;
871 slope_value2 = sample;
872 current_slope = new_slope;
873 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
874 //__LINE__, start_position + i, current_slope);
878 // this strings together multiple release periods instead of
879 // approaching but never reaching the ending gain
880 if( current_slope < 0 ) {
881 if( sample > slope_value2 ) {
882 peak_samples = attack_samples;
883 slope_samples = release_samples;
884 slope_current_sample = 0;
885 slope_value1 = current_value;
886 slope_value2 = sample;
887 new_slope = (sample - current_value) /
889 current_slope = new_slope;
890 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
891 //__LINE__, start_position + i, current_slope);
895 // GET_TRIGGER(input_buffer[channel]->get_data(), i + release_samples)
896 // new_slope = (sample - current_value) /
898 // if( new_slope < current_slope &&
899 // slope_current_sample >= peak_samples )
901 // peak_samples = release_samples;
902 // slope_samples = release_samples;
903 // slope_current_sample = 0;
904 // slope_value1 = current_value;
905 // slope_value2 = sample;
906 // current_slope = new_slope;
907 // printf("CompressorEngine::process %d position=%ld slope=%f\n",
908 // __LINE__, start_position + i, current_slope);
914 // Update current value and multiply gain
915 slope_current_sample++;
916 current_value = slope_value1 +
917 (slope_value2 - slope_value1) *
918 slope_current_sample /
921 if( config->smoothing_only ) {
922 for( int j = 0; j < channels; j++ ) {
923 output_buffer[j]->get_data()[i] = current_value * 2 - 1;
929 if( band_config->bypass ) {
933 gain = config->calculate_gain(band, current_value);
936 // update the GUI frames
937 if( fabs(gain - 1.0) > fabs(gui_max_gain - 1.0) ) {
940 //if( !EQUIV(gain, 1.0) ) printf("CompressorEngine::process %d gain=%f\n", __LINE__, gain);
942 // calculate the input level to draw. Should it be the trigger or a channel?
943 GET_TRIGGER(input_buffer[channel]->get_data(), i);
944 if( sample > gui_max_level ) {
945 gui_max_level = sample;
949 if( gui_frame_counter > gui_frame_samples ) {
950 //if( !EQUIV(gui_frame_max, 1.0) ) printf("CompressorEngine::process %d offset=%d gui_frame_max=%f\n", __LINE__, i, gui_frame_max);
951 gui_gains.append(gui_max_gain);
952 gui_levels.append(gui_max_level);
953 gui_offsets.append((double)i / sample_rate);
956 gui_frame_counter = 0;
959 for( int j = 0; j < channels; j++ ) {
960 output_buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
967 CompressorPopup::CompressorPopup(CompressorCanvasBase *canvas)
968 : BC_PopupMenu(0, 0, 0, "", 0)
970 this->canvas = canvas;
973 CompressorPopup::~CompressorPopup()
978 void CompressorPopup::create_objects()
980 add_item(new CompressorCopy(this));
981 add_item(new CompressorPaste(this));
982 add_item(new CompressorClearGraph(this));
986 CompressorCopy::CompressorCopy(CompressorPopup *popup)
987 : BC_MenuItem(_("Copy graph"))
993 CompressorCopy::~CompressorCopy()
997 int CompressorCopy::handle_event()
1000 CompressorConfigBase *config = popup->canvas->config;
1001 config->bands[config->current_band].save_data(&output, 0, 0);
1002 output.terminate_string();
1003 char *cp = output.string();
1004 popup->to_clipboard(cp, strlen(cp), SECONDARY_SELECTION);
1009 CompressorPaste::CompressorPaste(CompressorPopup *popup)
1010 : BC_MenuItem(_("Paste graph"))
1012 this->popup = popup;
1016 CompressorPaste::~CompressorPaste()
1020 int CompressorPaste::handle_event()
1022 int len = popup->get_clipboard()->clipboard_len(SECONDARY_SELECTION);
1024 CompressorConfigBase *config = popup->canvas->config;
1025 char *string = new char[len + 1];
1026 popup->get_clipboard()->from_clipboard(string, len, SECONDARY_SELECTION);
1029 xml.read_from_string(string);
1031 int result = 0, got_it = 0;
1032 while( !(result = xml.read_tag()) ) {
1033 if( xml.tag.title_is("COMPRESSORBAND") ) {
1034 int band = config->current_band;
1035 BandConfig *band_config = &config->bands[band];
1036 band_config->read_data(&xml, 0);
1043 popup->canvas->update();
1044 PluginClient *plugin = popup->canvas->plugin;
1045 plugin->send_configure_change();
1052 CompressorClearGraph::CompressorClearGraph(CompressorPopup *popup)
1053 : BC_MenuItem(_("Clear graph"))
1055 this->popup = popup;
1059 CompressorClearGraph::~CompressorClearGraph()
1063 int CompressorClearGraph::handle_event()
1065 CompressorConfigBase *config = popup->canvas->config;
1066 config->bands[config->current_band].levels.remove_all();
1067 popup->canvas->update();
1068 PluginClient *plugin = popup->canvas->plugin;
1069 plugin->send_configure_change();