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 BandConfig::~BandConfig()
44 void BandConfig::save_data(FileXML *xml, int number, int do_multiband)
46 xml->tag.set_title("COMPRESSORBAND");
48 xml->tag.set_property("NUMBER", number);
49 xml->tag.set_property("FREQ", freq);
50 xml->tag.set_property("BYPASS", bypass);
51 xml->tag.set_property("SOLO", solo);
52 xml->tag.set_property("ATTACK_LEN", attack_len);
53 xml->tag.set_property("RELEASE_LEN", release_len);
54 xml->tag.set_property("MKUP_GAIN", mkup_gain);
57 xml->append_newline();
59 for( int i = 0; i < levels.total; i++ ) {
60 xml->tag.set_title("LEVEL");
61 xml->tag.set_property("X", levels[i].x);
62 xml->tag.set_property("Y", levels[i].y);
64 xml->append_newline();
67 xml->tag.set_title("/COMPRESSORBAND");
69 xml->append_newline();
72 void BandConfig::read_data(FileXML *xml, int do_multiband)
75 freq = xml->tag.get_property("FREQ", freq);
76 bypass = xml->tag.get_property("BYPASS", bypass);
77 solo = xml->tag.get_property("SOLO", solo);
78 attack_len = xml->tag.get_property("ATTACK_LEN", attack_len);
79 release_len = xml->tag.get_property("RELEASE_LEN", release_len);
80 mkup_gain = xml->tag.get_property("MKUP_GAIN", mkup_gain);
86 result = xml->read_tag();
88 if( xml->tag.title_is("LEVEL") ) {
89 double x = xml->tag.get_property("X", (double)0);
90 double y = xml->tag.get_property("Y", (double)0);
91 compressor_point_t point = { x, y };
96 if( xml->tag.title_is("/COMPRESSORBAND") ) {
103 void BandConfig::copy_from(BandConfig *src)
106 for( int i = 0; i < src->levels.total; i++ ) {
107 levels.append(src->levels[i]);
110 // readahead_len = src->readahead_len;
111 attack_len = src->attack_len;
112 release_len = src->release_len;
115 bypass = src->bypass;
116 mkup_gain = src->mkup_gain;
119 int BandConfig::equiv(BandConfig *src)
121 if( levels.total != src->levels.total ||
123 bypass != src->bypass ||
125 mkup_gain != src->mkup_gain ||
126 // !EQUIV(readahead_len, src->readahead_len) ||
127 !EQUIV(attack_len, src->attack_len) ||
128 !EQUIV(release_len, src->release_len) ) {
132 for( int i = 0; i < levels.total && i < src->levels.total; i++ ) {
133 compressor_point_t *this_level = &levels[i];
134 compressor_point_t *that_level = &src->levels[i];
135 if( !EQUIV(this_level->x, that_level->x) ||
136 !EQUIV(this_level->y, that_level->y) ) {
144 void BandConfig::boundaries(CompressorConfigBase *base)
146 for( int i = 0; i < levels.size(); i++ ) {
147 compressor_point_t *level = &levels[i];
148 if( level->x < base->min_db ) level->x = base->min_db;
149 if( level->y < base->min_db ) level->y = base->min_db;
150 if( level->x > base->max_db ) level->x = base->max_db;
151 if( level->y > base->max_db ) level->y = base->max_db;
155 void BandConfig::reset()
160 // readahead_len = 1.0;
168 CompressorConfigBase::CompressorConfigBase(int total_bands)
170 this->total_bands = total_bands;
171 bands = new BandConfig[total_bands];
174 min_value = DB::fromdb(min_db) + 0.001;
175 // min_x = min_db; max_x = 0;
176 // min_y = min_db; max_y = 0;
181 CompressorConfigBase::~CompressorConfigBase()
186 void CompressorConfigBase::reset_base()
188 input = CompressorConfigBase::TRIGGER;
191 for( int i=0; i<total_bands; ++i )
192 bands[i].freq = Freq::tofreq((i+1) * TOTALFREQS / total_bands);
196 void CompressorConfigBase::reset_bands()
198 for( int i=0; i<total_bands; ++i )
203 void CompressorConfigBase::boundaries()
205 for( int i=0; i<total_bands; ++i ) {
206 bands[i].boundaries(this);
211 void CompressorConfigBase::copy_from(CompressorConfigBase &that)
213 // min_x = that.min_x; max_x = that.max_x;
214 // min_y = that.min_y; max_y = that.max_y;
215 trigger = that.trigger;
217 smoothing_only = that.smoothing_only;
219 for( int i=0; i<total_bands; ++i ) {
220 BandConfig *dst = &bands[i];
221 BandConfig *src = &that.bands[i];
227 int CompressorConfigBase::equivalent(CompressorConfigBase &that)
229 for( int i=0; i<total_bands; ++i ) {
230 if( !bands[i].equiv(&that.bands[i]) )
234 return trigger == that.trigger &&
235 input == that.input &&
236 smoothing_only == that.smoothing_only;
239 double CompressorConfigBase::get_y(int band, int i)
241 ArrayList<compressor_point_t> &levels = bands[band].levels;
242 int sz = levels.size();
244 if( i >= sz ) i = sz-1;
248 double CompressorConfigBase::get_x(int band, int i)
250 ArrayList<compressor_point_t> &levels = bands[band].levels;
251 int sz = levels.size();
253 if( i >= sz ) i = sz-1;
257 double CompressorConfigBase::calculate_db(int band, double x)
259 ArrayList<compressor_point_t> &levels = bands[band].levels;
260 int sz = levels.size();
262 compressor_point_t &point0 = levels[0];
263 double px0 = point0.x, py0 = point0.y, dx0 = x - px0;
264 // before 1st point, use 1:1 gain
265 double ret = py0 + dx0;
269 while( --k >= 0 && levels[k].x > x );
271 compressor_point_t &curr = levels[k];
272 double cx = curr.x, cy = curr.y, dx = x - cx;
273 // between 2 points. Use slope between 2 points
274 // the last point. Use slope of last 2 points
276 compressor_point_t &prev = levels[k+0];
277 compressor_point_t &next = levels[k+1];
278 double px = prev.x, py = prev.y;
279 double nx = next.x, ny = next.y;
280 ret = cy + dx * (ny - py) / (nx - px);
284 // the only point. Use slope from min_db
285 ret = py0 + dx0 * (py0 - min_db) / (px0 - min_db);
287 ret += bands[band].mkup_gain;
292 int CompressorConfigBase::set_point(int band, double x, double y)
294 ArrayList<compressor_point_t> &levels = bands[band].levels;
295 int k = levels.size(), ret = k;
296 while( --k >= 0 && levels[k].x >= x ) ret = k;
297 compressor_point_t new_point = { x, y };
298 levels.insert(new_point, ret);
302 void CompressorConfigBase::remove_point(int band, int i)
304 ArrayList<compressor_point_t> &levels = bands[band].levels;
305 levels.remove_number(i);
309 double CompressorConfigBase::calculate_output(int band, double x)
311 double x_db = DB::todb(x);
312 return DB::fromdb(calculate_db(band, x_db));
316 double CompressorConfigBase::calculate_gain(int band, double input_linear)
318 double output_linear = calculate_output(band, input_linear);
319 // output is below minimum. Mute it
320 return output_linear < min_value ? 0. :
321 // input is below minimum. Don't change it.
322 fabs(input_linear - 0.0) < min_value ? 1. :
324 output_linear / input_linear;
328 CompressorCanvasBase::CompressorCanvasBase(CompressorConfigBase *config,
329 PluginClient *plugin, PluginClientWindow *window,
330 int x, int y, int w, int h)
331 : BC_SubWindow(x, y, w, h, BLACK)
333 this->config = config;
334 this->plugin = plugin;
335 this->window = window;
336 current_operation = NONE;
340 graph_w = w - graph_x;
341 graph_h = h - graph_y;
343 divisions = (int)(config->max_db - config->min_db) / subdivisions;
346 CompressorCanvasBase::~CompressorCanvasBase()
351 void CompressorCanvasBase::create_objects()
353 add_subwindow(menu = new CompressorPopup(this));
354 menu->create_objects();
356 set_cursor(CROSS_CURSOR, 0, 0);
361 void CompressorCanvasBase::draw_scales()
364 window->set_font(SMALLFONT);
365 window->set_color(get_resources()->default_text_color);
368 for( int i=0; i<=divisions; ++i ) {
369 int y = get_y() + yfudge + graph_y + graph_h * i / divisions;
371 char string[BCTEXTLEN];
372 sprintf(string, "%.0f", config->max_db -
373 (float)i / divisions * (config->max_db - config->min_db));
374 int text_w = get_text_width(SMALLFONT, string);
375 if( i >= divisions ) y -= yfudge;
376 window->draw_text(x-xS(10) - text_w, y, string);
377 if( i >= divisions ) break;
379 int y1 = get_y() + graph_y + graph_h * i / divisions;
380 int y2 = get_y() + graph_y + graph_h * (i + 1) / divisions;
381 int x1 = get_x() - xS(10), x2 = get_x() - xS(5);
382 for( int j=0; j<subdivisions; ++j,x1=x2 ) {
383 y = y1 + (y2 - y1) * j / subdivisions;
384 window->draw_line(x, y, x1, y);
389 for( int i=0; i<=divisions; ++i ) {
390 int y = get_y() + get_h();
391 int x = get_x() + graph_x + graph_w * i / divisions;
392 int y0 = y + window->get_text_ascent(SMALLFONT);
393 char string[BCTEXTLEN];
394 sprintf(string, "%.0f", (float)i / divisions *
395 (config->max_db - config->min_db) + config->min_db);
396 int text_w = get_text_width(SMALLFONT, string);
397 window->draw_text(x - text_w, y0 + yS(10), string);
398 if( i >= divisions ) break;
400 int x1 = get_x() + graph_x + graph_w * i / divisions;
401 int x2 = get_x() + graph_x + graph_w * (i + 1) / divisions;
402 int y1 = y + yS(10), y2 = y + yS(5);
403 for( int j=0; j<subdivisions; ++j,y1=y2 ) {
404 x = x1 + (x2 - x1) * j / subdivisions;
405 window->draw_line(x, y, x, y1);
412 #define POINT_W xS(10)
415 int CompressorCanvasBase::x_to_y(int band, int x)
417 double min_db = config->min_db, max_db = config->max_db;
418 double rng_db = max_db - min_db;
419 double x_db = min_db + (double)x / graph_w * rng_db;
420 double y_db = config->calculate_db(band, x_db);
421 int y = graph_y + graph_h - (int)((y_db - min_db) * graph_h / rng_db);
426 int CompressorCanvasBase::db_to_x(double db)
428 double min_db = config->min_db, max_db = config->max_db;
429 double rng_db = max_db - min_db;
430 int x = graph_x + (double)(db - min_db) * graph_w / rng_db;
435 int CompressorCanvasBase::db_to_y(double db)
437 double min_db = config->min_db, max_db = config->max_db;
438 double rng_db = max_db - min_db;
439 int y = graph_y + graph_h - (int)((db - min_db) * graph_h / rng_db);
444 double CompressorCanvasBase::x_to_db(int x)
446 CLAMP(x, 0, get_w());
447 double min_db = config->min_db, max_db = config->max_db;
448 double rng_db = max_db - min_db;
449 double x_db = (double)(x - graph_x) * rng_db / graph_w + min_db;
450 CLAMP(x_db, min_db, max_db);
454 double CompressorCanvasBase::y_to_db(int y)
456 CLAMP(y, 0, get_h());
457 double min_db = config->min_db, max_db = config->max_db;
458 double rng_db = max_db - min_db;
459 double y_db = (double)(graph_y - y) * rng_db / graph_h + max_db;
460 CLAMP(y_db, min_db, max_db);
466 void CompressorCanvasBase::update()
469 set_color(window->get_bg_color());
470 draw_box(graph_x, 0, get_w(), graph_y);
471 draw_box(graph_w, graph_y, get_w() - graph_w, get_h() - graph_y);
472 // const int checker_w = DP(10);
473 // const int checker_h = DP(10);
474 // set_color(MDGREY);
475 // for( int i = 0; i < get_h(); i += checker_h )
477 // for( int j = (i % 2) * checker_w; j < get_w(); j += checker_w * 2 )
479 // if( !(i >= graph_y &&
480 // i + checker_h < graph_y + graph_h &&
482 // j + checker_w < graph_x + graph_w) )
484 // draw_box(j, i, checker_w, checker_h);
490 set_color(plugin->get_theme()->graph_bg_color);
491 draw_box(graph_x, graph_y, graph_w, graph_h);
494 draw_3d_border(0, 0, get_w(), get_h(), window->get_bg_color(),
495 plugin->get_theme()->graph_border1_color,
496 plugin->get_theme()->graph_border2_color,
497 window->get_bg_color());
500 set_color(plugin->get_theme()->graph_grid_color);
502 for( int i = 1; i < divisions; i++ ) {
503 int y = graph_y + graph_h * i / divisions;
504 draw_line(graph_x, y, graph_x + graph_w, y);
507 draw_line(graph_x, y + 1, graph_x + graph_w, y + 1);
510 int x = graph_x + graph_w * i / divisions;
511 draw_line(x, graph_y, x, graph_y + graph_h);
513 if( i == divisions - 1 ) {
514 draw_line(x + 1, graph_y, x + 1, graph_y + graph_h);
520 set_font(MEDIUMFONT);
521 int border = plugin->get_theme()->widget_border;
522 draw_text(border, get_h() / 2, _("Output"));
523 int tx = get_w() / 2 - get_text_width(MEDIUMFONT, _("Input")) / 2;
524 int ty = get_h() - get_text_height(MEDIUMFONT, _("Input")) - border;
525 draw_text(tx, ty, _("Input"));
527 for( int pass = 0; pass < 2; pass++ ) {
528 for( int band = 0; band < config->total_bands; band++ ) {
529 // draw the active band on top of the others
530 if( band == config->current_band && pass == 0 ||
531 band != config->current_band && pass == 1 ) {
535 if( band == config->current_band ) {
536 set_color(plugin->get_theme()->graph_active_color);
540 set_color(plugin->get_theme()->graph_inactive_color);
545 int x1 = graph_x, y1 = x_to_y(band, x1);
546 for( int i=0; ++i <= graph_w; ) {
547 int x2 = x1+1, y2 = x_to_y(band, x2);
548 draw_line(x1,y1, x2,y2);
555 if( band == config->current_band ) {
556 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
557 double mkup_gain = config->bands[band].mkup_gain;
558 for( int i = 0; i < levels.size(); i++ ) {
559 double x_db = config->get_x(band, i);
560 double y_db = config->get_y(band, i) + mkup_gain;
561 int x = db_to_x(x_db);
562 int y = db_to_y(y_db);
564 if( i == current_point ) {
565 draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
568 draw_rectangle(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
578 int CompressorCanvasBase::button_press_event()
580 // Check existing points
581 if( is_event_win() && cursor_inside() ) {
582 if( get_buttonpress() == 3 ) {
583 menu->activate_menu();
586 int x = get_cursor_x(), y = get_cursor_y();
587 int band = config->current_band;
588 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
589 double mkup_gain = config->bands[band].mkup_gain;
590 for( int i=0; i<levels.size(); ++i ) {
591 double x_db = config->get_x(config->current_band, i);
592 double y_db = config->get_y(config->current_band, i) + mkup_gain;
594 int px = db_to_x(x_db);
595 int py = db_to_y(y_db);
597 if( x <= px + POINT_W / 2 && x >= px - POINT_W / 2 &&
598 y <= py + POINT_W / 2 && y >= py - POINT_W / 2 ) {
599 current_operation = DRAG;
605 if( x >= graph_x && x < graph_x + graph_w &&
606 y >= graph_y && y < graph_y + graph_h ) {
608 double x_db = x_to_db(x);
609 double y_db = y_to_db(y) + mkup_gain;
611 current_point = config->set_point(config->current_band, x_db, y_db);
612 current_operation = DRAG;
614 plugin->send_configure_change();
621 int CompressorCanvasBase::button_release_event()
623 int band = config->current_band;
624 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
626 if( current_operation == DRAG ) {
627 if( current_point > 0 ) {
628 if( levels[current_point].x < levels[current_point-1].x ) {
629 config->remove_point(config->current_band, current_point);
633 if( current_point < levels.size()-1 ) {
634 if( levels[current_point].x >= levels[current_point + 1].x ) {
635 config->remove_point(config->current_band, current_point);
640 plugin->send_configure_change();
641 current_operation = NONE;
648 int CompressorCanvasBase::cursor_motion_event()
650 int band = config->current_band;
651 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
652 double mkup_gain = config->bands[band].mkup_gain;
653 int x = get_cursor_x(), y = get_cursor_y();
655 if( current_operation == DRAG ) {
656 double x_db = x_to_db(x);
657 double y_db = y_to_db(y) - mkup_gain;
660 const int grid_precision = 6;
661 x_db = config->max_db + (double)(grid_precision *
662 (int)((x_db - config->max_db) / grid_precision - 0.5));
663 y_db = config->max_db + (double)(grid_precision *
664 (int)((y_db - config->max_db) / grid_precision - 0.5));
668 //printf("CompressorCanvasBase::cursor_motion_event %d x=%d y=%d x_db=%f y_db=%f\n",
669 //__LINE__, x, y, x_db, y_db);
670 levels[current_point].x = x_db;
671 levels[current_point].y = y_db;
673 plugin->send_configure_change();
677 // Change cursor over points
678 if( is_event_win() && cursor_inside() ) {
679 int new_cursor = CROSS_CURSOR;
681 for( int i = 0; i < levels.size(); i++ ) {
682 double x_db = config->get_x(config->current_band, i);
683 double y_db = config->get_y(config->current_band, i) + mkup_gain;
684 int px = db_to_x(x_db);
685 int py = db_to_y(y_db);
687 if( x <= px + POINT_W / 2 && x >= px - POINT_W / 2 &&
688 y <= py + POINT_W / 2 && y >= py - POINT_W / 2 ) {
689 new_cursor = UPRIGHT_ARROW_CURSOR;
694 // out of active area
695 if( x >= graph_x + graph_w || y < graph_y ) {
696 new_cursor = ARROW_CURSOR;
698 if( new_cursor != get_cursor() ) {
699 set_cursor(new_cursor, 0, 1);
706 void CompressorCanvasBase::update_window()
708 printf("CompressorCanvasBase::update_window %d empty\n", __LINE__);
712 int CompressorCanvasBase::is_dragging()
714 return current_operation == DRAG;
718 CompressorClientFrame::CompressorClientFrame()
723 CompressorClientFrame::~CompressorClientFrame()
727 CompressorFreqFrame::CompressorFreqFrame()
729 type = FREQ_COMPRESSORFRAME;
730 data = 0; data_size = 0;
731 freq_max = 0; time_max = 0;
734 CompressorFreqFrame::~CompressorFreqFrame()
739 CompressorGainFrame::CompressorGainFrame()
741 type = GAIN_COMPRESSORFRAME;
745 CompressorGainFrame::~CompressorGainFrame()
749 CompressorEngine::CompressorEngine(CompressorConfigBase *config,
752 this->config = config;
757 CompressorEngine::~CompressorEngine()
762 void CompressorEngine::reset()
765 slope_current_sample = 0;
770 gui_frame_samples = 2048;
772 gui_frame_counter = 0;
776 void CompressorEngine::calculate_ranges(int *attack_samples,
777 int *release_samples,
778 int *preview_samples,
781 BandConfig *band_config = &config->bands[band];
782 *attack_samples = labs(Units::round(band_config->attack_len * sample_rate));
783 *release_samples = Units::round(band_config->release_len * sample_rate);
784 CLAMP(*attack_samples, 1, 1000000);
785 CLAMP(*release_samples, 1, 1000000);
786 *preview_samples = MAX(*attack_samples, *release_samples);
790 void CompressorEngine::process(Samples **output_buffer,
791 Samples **input_buffer,
795 int64_t start_position)
797 BandConfig *band_config = &config->bands[band];
801 int trigger = CLIP(config->trigger, 0, channels - 1);
803 gui_gains.remove_all();
804 gui_levels.remove_all();
805 gui_offsets.remove_all();
807 calculate_ranges(&attack_samples,
811 if( slope_current_sample < 0 ) slope_current_sample = slope_samples;
813 double *trigger_buffer = input_buffer[trigger]->get_data();
815 for( int i = 0; i < size; i++ ) {
816 double current_slope = (slope_value2 - slope_value1) /
819 // maximums in the 2 time ranges
820 double attack_slope = -0x7fffffff;
821 double attack_sample = -1;
822 int attack_offset = -1;
823 int have_attack_sample = 0;
824 double release_slope = -0x7fffffff;
825 double release_sample = -1;
826 int release_offset = -1;
827 int have_release_sample = 0;
828 if( slope_current_sample >= slope_samples ) {
829 // start new line segment
830 for( int j = 1; j < preview_samples; j++ ) {
831 GET_TRIGGER(input_buffer[channel]->get_data(), i + j)
832 double new_slope = (sample - current_value) / j;
833 if( j < attack_samples && new_slope >= attack_slope ) {
834 attack_slope = new_slope;
835 attack_sample = sample;
837 have_attack_sample = 1;
840 if( j < release_samples &&
842 new_slope > release_slope ) {
843 release_slope = new_slope;
844 release_sample = sample;
846 have_release_sample = 1;
850 slope_current_sample = 0;
851 if( have_attack_sample && attack_slope >= 0 ) {
853 peak_samples = attack_offset;
854 slope_samples = attack_offset;
855 slope_value1 = current_value;
856 slope_value2 = attack_sample;
857 current_slope = attack_slope;
858 //printf("CompressorEngine::process %d position=%ld slope=%f samples=%d\n",
859 //__LINE__, start_position + i, current_slope, slope_samples);
862 if( have_release_sample ) {
864 slope_samples = release_offset;
865 // slope_samples = release_samples;
866 peak_samples = release_offset;
867 slope_value1 = current_value;
868 slope_value2 = release_sample;
869 current_slope = release_slope;
870 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
871 //__LINE__, start_position + i, current_slope);
876 printf("CompressorEngine::process %d have neither attack nor release position=%ld attack=%f release=%f current_value=%f\n",
877 __LINE__, start_position + i, attack_slope, release_slope, current_value); bug = 1;
882 // check for new peak after the line segment
883 GET_TRIGGER(input_buffer[channel]->get_data(), i + attack_samples)
884 double new_slope = (sample - current_value) /
886 if( current_slope >= 0 ) {
887 if( new_slope > current_slope ) {
888 peak_samples = attack_samples;
889 slope_samples = attack_samples;
890 slope_current_sample = 0;
891 slope_value1 = current_value;
892 slope_value2 = sample;
893 current_slope = new_slope;
894 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
895 //__LINE__, start_position + i, current_slope);
899 // this strings together multiple release periods instead of
900 // approaching but never reaching the ending gain
901 if( current_slope < 0 ) {
902 if( sample > slope_value2 ) {
903 peak_samples = attack_samples;
904 slope_samples = release_samples;
905 slope_current_sample = 0;
906 slope_value1 = current_value;
907 slope_value2 = sample;
908 new_slope = (sample - current_value) /
910 current_slope = new_slope;
911 //printf("CompressorEngine::process %d position=%ld slope=%f\n",
912 //__LINE__, start_position + i, current_slope);
916 // GET_TRIGGER(input_buffer[channel]->get_data(), i + release_samples)
917 // new_slope = (sample - current_value) /
919 // if( new_slope < current_slope &&
920 // slope_current_sample >= peak_samples )
922 // peak_samples = release_samples;
923 // slope_samples = release_samples;
924 // slope_current_sample = 0;
925 // slope_value1 = current_value;
926 // slope_value2 = sample;
927 // current_slope = new_slope;
928 // printf("CompressorEngine::process %d position=%ld slope=%f\n",
929 // __LINE__, start_position + i, current_slope);
935 // Update current value and multiply gain
936 slope_current_sample++;
937 current_value = slope_value1 +
938 (slope_value2 - slope_value1) *
939 slope_current_sample /
943 if( !config->smoothing_only ) {
944 if( !band_config->bypass )
945 gain = config->calculate_gain(band, current_value);
946 // update the GUI frames
947 if( fabs(gain - 1.0) > fabs(gui_max_gain - 1.0) ) {
950 // calculate the input level to draw. Should it be the trigger or a channel?
951 GET_TRIGGER(input_buffer[channel]->get_data(), i);
952 if( sample > gui_max_level ) {
953 gui_max_level = sample;
956 if( gui_frame_counter > gui_frame_samples ) {
957 //if( !EQUIV(gui_frame_max, 1.0) ) printf("CompressorEngine::process %d offset=%d gui_frame_max=%f\n", __LINE__, i, gui_frame_max);
958 gui_gains.append(gui_max_gain);
959 gui_levels.append(gui_max_level);
960 gui_offsets.append((double)i / sample_rate);
963 gui_frame_counter = 0;
967 gain = current_value > 0.01 ? 0.5 / current_value : 50.;
969 for( int j = 0; j < channels; j++ ) {
970 output_buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
976 CompressorPopup::CompressorPopup(CompressorCanvasBase *canvas)
977 : BC_PopupMenu(0, 0, 0, "", 0)
979 this->canvas = canvas;
982 CompressorPopup::~CompressorPopup()
987 void CompressorPopup::create_objects()
989 add_item(new CompressorCopy(this));
990 add_item(new CompressorPaste(this));
991 add_item(new CompressorClearGraph(this));
995 CompressorCopy::CompressorCopy(CompressorPopup *popup)
996 : BC_MenuItem(_("Copy graph"))
1002 CompressorCopy::~CompressorCopy()
1006 int CompressorCopy::handle_event()
1009 CompressorConfigBase *config = popup->canvas->config;
1010 config->bands[config->current_band].save_data(&output, 0, 0);
1011 output.terminate_string();
1012 char *cp = output.string();
1013 popup->to_clipboard(cp, strlen(cp), SECONDARY_SELECTION);
1018 CompressorPaste::CompressorPaste(CompressorPopup *popup)
1019 : BC_MenuItem(_("Paste graph"))
1021 this->popup = popup;
1025 CompressorPaste::~CompressorPaste()
1029 int CompressorPaste::handle_event()
1031 int len = popup->get_clipboard()->clipboard_len(SECONDARY_SELECTION);
1033 CompressorConfigBase *config = popup->canvas->config;
1034 char *string = new char[len + 1];
1035 popup->get_clipboard()->from_clipboard(string, len, SECONDARY_SELECTION);
1038 xml.read_from_string(string);
1040 int result = 0, got_it = 0;
1041 while( !(result = xml.read_tag()) ) {
1042 if( xml.tag.title_is("COMPRESSORBAND") ) {
1043 int band = config->current_band;
1044 BandConfig *band_config = &config->bands[band];
1045 band_config->read_data(&xml, 0);
1052 popup->canvas->update();
1053 PluginClient *plugin = popup->canvas->plugin;
1054 plugin->send_configure_change();
1061 CompressorClearGraph::CompressorClearGraph(CompressorPopup *popup)
1062 : BC_MenuItem(_("Clear graph"))
1064 this->popup = popup;
1068 CompressorClearGraph::~CompressorClearGraph()
1072 int CompressorClearGraph::handle_event()
1074 CompressorConfigBase *config = popup->canvas->config;
1075 config->bands[config->current_band].levels.remove_all();
1076 popup->canvas->update();
1077 PluginClient *plugin = popup->canvas->plugin;
1078 plugin->send_configure_change();