no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / compressor / compressor.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008-2019 Adam Williams <broadcast at earthling dot net>
4  *
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.
9  *
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.
14  *
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
18  *
19  */
20
21 #include "bcdisplayinfo.h"
22 #include "bchash.h"
23 #include "bcsignals.h"
24 #include "asset.h"
25 #include "clip.h"
26 #include "compressor.h"
27 #include "cursors.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "language.h"
32 #include "samples.h"
33 #include "theme.h"
34 #include "tracking.inc"
35 #include "transportque.inc"
36 #include "units.h"
37 #include "vframe.h"
38
39 #include <math.h>
40 #include <string.h>
41
42 REGISTER_PLUGIN(CompressorEffect)
43
44 // More potential compressor algorithms:
45 // Use single readahead time parameter.  Negative readahead time uses
46 // readahead.  Positive readahead time uses slope.
47
48 // Smooth input stage if readahead.
49 // Determine slope from current smoothed sample to every sample in readahead area.
50 // Once highest slope is found, count of number of samples remaining until it is
51 // reached.  Only search after this count for the next highest slope.
52 // Use highest slope to determine smoothed value.
53
54 // Smooth input stage if not readahead.
55 // For every sample, calculate slope needed to reach current sample from
56 // current smoothed value in the readahead time.  If higher than current slope,
57 // make it the current slope and count number of samples remaining until it is
58 // reached.  If this count is met and no higher slopes are found, base slope
59 // on current sample when count is met.
60
61 // Gain stage.
62 // For every sample, calculate gain from smoothed input value.
63
64 CompressorEffect::CompressorEffect(PluginServer *server)
65  : PluginAClient(server)
66 {
67         input_buffer = 0;
68         input_size = 0;
69         input_allocated = 0;
70         input_start = 0;
71
72         need_reconfigure = 1;
73         engine = 0;
74 }
75
76 CompressorEffect::~CompressorEffect()
77 {
78         if( input_buffer ) {
79                 for( int i=0; i<PluginClient::total_in_buffers; ++i )
80                         delete input_buffer[i];
81                 delete [] input_buffer;  input_buffer = 0;
82         }
83         input_size = 0;
84         input_allocated = 0;
85         levels.remove_all();
86         delete engine;
87 }
88
89 void CompressorEffect::allocate_input(int size)
90 {
91         int channels = PluginClient::total_in_buffers;
92         if( size > input_allocated ) {
93                 Samples **new_input_buffer = new Samples*[channels];
94                 for( int i=0; i<channels; ++i ) {
95                         new_input_buffer[i] = new Samples(size);
96                         if( !input_buffer ) continue;
97                         memcpy(new_input_buffer[i]->get_data(),
98                                 input_buffer[i]->get_data(),
99                                 input_size * sizeof(double));
100                         delete input_buffer[i];
101                 }
102                 if( input_buffer ) delete [] input_buffer;
103
104                 input_allocated = size;
105                 input_buffer = new_input_buffer;
106         }
107 }
108
109 const char* CompressorEffect::plugin_title() { return N_("Compressor"); }
110 int CompressorEffect::is_realtime() { return 1; }
111 int CompressorEffect::is_multichannel() { return 1; }
112
113 void CompressorEffect::read_data(KeyFrame *keyframe)
114 {
115         FileXML input;
116         BandConfig *band_config = &config.bands[0];
117         input.set_shared_input(keyframe->xbuf);
118
119         int result = 0;
120         while( !(result=input.read_tag()) ) {
121                 if( input.tag.title_is("COMPRESSOR") ) {
122 //                      band_config->readahead_len = input.tag.get_property("READAHEAD_LEN", band_config->readahead_len);
123                         band_config->attack_len = input.tag.get_property("ATTACK_LEN", band_config->attack_len);
124                         band_config->release_len = input.tag.get_property("RELEASE_LEN", band_config->release_len);
125                         config.trigger = input.tag.get_property("TRIGGER", config.trigger);
126                         config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
127                         config.input = input.tag.get_property("INPUT", config.input);
128                 }
129                 else if( input.tag.title_is("COMPRESSORBAND") ) {
130                         band_config->read_data(&input, 0);
131                 }
132         }
133         config.boundaries();
134 }
135
136 void CompressorEffect::save_data(KeyFrame *keyframe)
137 {
138         FileXML output;
139         BandConfig *band_config = &config.bands[0];
140         output.set_shared_output(keyframe->xbuf);
141
142         output.tag.set_title("COMPRESSOR");
143         output.tag.set_property("TRIGGER", config.trigger);
144         output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
145         output.tag.set_property("INPUT", config.input);
146         output.tag.set_property("ATTACK_LEN", band_config->attack_len);
147         output.tag.set_property("RELEASE_LEN", band_config->release_len);
148         output.append_tag();
149         output.append_newline();
150
151         band_config->save_data(&output, 0, 0);
152         output.tag.set_title("/COMPRESSOR");
153         output.append_tag();
154         output.append_newline();
155         output.terminate_string();
156 }
157
158
159 void CompressorEffect::update_gui()
160 {
161         if( !thread ) return;
162 // user can't change levels when loading configuration
163         thread->window->lock_window("CompressorEffect::update_gui");
164         CompressorWindow *window = (CompressorWindow *)thread->window;
165 // Can't update points if the user is editing
166         int reconfigured = window->canvas->is_dragging() ? 0 :
167                 load_configuration();
168         if( reconfigured )
169                 window->update();
170 // delete frames up to current tracking position
171         int dir = get_tracking_direction() == PLAY_FORWARD ? 1 : -1;
172         double tracking_position = get_tracking_position();
173         CompressorGainFrame *gain_frame = (CompressorGainFrame *)
174                 get_gui_frame(tracking_position, dir);
175         if( gain_frame ) {
176                 window->update_meter(gain_frame);
177                 delete gain_frame;
178         }
179         window->unlock_window();
180 }
181
182 void CompressorEffect::render_stop()
183 {
184         if( !thread ) return;
185         thread->window->lock_window("CompressorEffect::render_stop");
186         CompressorWindow *window = (CompressorWindow *)thread->window;
187         window->in->reset();
188         window->gain_change->update(1, 0);
189         window->unlock_window();
190 }
191
192 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
193 NEW_WINDOW_MACRO(CompressorEffect, CompressorWindow)
194
195 int CompressorEffect::process_buffer(int64_t size, Samples **buffer,
196                 int64_t start_position, int sample_rate)
197 {
198         int channels = PluginClient::total_in_buffers;
199         BandConfig *band_config = &config.bands[0];
200         int sign = get_direction() == PLAY_REVERSE ? -1 : 1;
201         load_configuration();
202
203 // restart after seeking
204         if( last_position != start_position ) {
205                 last_position = start_position;
206                 if( engine )
207                         engine->reset();
208                 input_size = 0;
209                 input_start = start_position;
210         }
211
212 // Calculate linear transfer from db
213         int nbands = band_config->levels.size();
214         while( levels.size() < nbands ) levels.append();
215
216         for( int i=0; i<band_config->levels.total; ++i ) {
217                 levels[i].x = DB::fromdb(band_config->levels[i].x);
218                 levels[i].y = DB::fromdb(band_config->levels[i].y);
219         }
220 //      min_x = DB::fromdb(config.min_db);
221 //      min_y = DB::fromdb(config.min_db);
222 //      max_x = 1.0;
223 //      max_y = 1.0;
224
225
226         int attack_samples;
227         int release_samples;
228         int preview_samples;
229
230         if( !engine ) {
231                 engine = new CompressorEngine(&config, 0);
232                 engine->gui_frame_samples = sample_rate / TRACKING_RATE + 1;
233         }
234         engine->calculate_ranges(&attack_samples, &release_samples,
235                         &preview_samples, sample_rate);
236
237 // Start of new buffer is outside the current buffer.  Start buffer over.
238         if( get_direction() == PLAY_FORWARD &&
239                 (start_position < input_start ||
240                 start_position >= input_start + input_size)
241                 ||
242                 get_direction() == PLAY_REVERSE &&
243                 (start_position > input_start ||
244                 start_position <= input_start - input_size) ) {
245 // printf("CompressorEffect::process_buffer %d start_position=%ld input_start=%ld input_size=%ld\n",
246 // __LINE__, start_position, input_start, input_size);
247                 input_size = 0;
248                 input_start = start_position;
249         }
250         else
251 // Shift current buffer so the buffer starts on start_position
252         if( get_direction() == PLAY_FORWARD &&
253                 start_position > input_start &&
254                 start_position < input_start + input_size
255                 ||
256                 get_direction() == PLAY_REVERSE &&
257                 start_position < input_start &&
258                 start_position > input_start - input_size ) {
259                 if( input_buffer ) {
260                         int len, offset;
261                         if( get_direction() == PLAY_FORWARD ) {
262                                 len = input_start + input_size - start_position;
263                                 offset = start_position - input_start;
264                         }
265                         else {
266                                 len = start_position - (input_start - input_size);
267                                 offset = input_start - start_position;
268                         }
269
270                         for( int i = 0; i < channels; i++ ) {
271                                 memcpy(input_buffer[i]->get_data(),
272                                         input_buffer[i]->get_data() + offset,
273                                         len * sizeof(double));
274                         }
275                         input_size = len;
276                         input_start = start_position;
277                 }
278         }
279
280 // Expand buffer to handle preview size
281         if( size + preview_samples > input_allocated ) {
282                 Samples **new_input_buffer = new Samples*[channels];
283                 for( int i = 0; i < channels; i++ ) {
284                         new_input_buffer[i] = new Samples(size + preview_samples);
285                         if( input_buffer ) {
286                                 memcpy(new_input_buffer[i]->get_data(),
287                                         input_buffer[i]->get_data(),
288                                         input_size * sizeof(double));
289                                 delete input_buffer[i];
290                         }
291                 }
292                 if( input_buffer ) delete [] input_buffer;
293
294                 input_allocated = size + preview_samples;
295                 input_buffer = new_input_buffer;
296         }
297
298 // Append data to input buffer to construct readahead area.
299         if( input_size < size + preview_samples ) {
300                 int fragment_size = size + preview_samples - input_size;
301                 for( int i = 0; i < channels; i++ ) {
302                         input_buffer[i]->set_offset(input_size);
303                         read_samples(input_buffer[i], i, sample_rate,
304                                 input_start + input_size * sign, fragment_size);
305                         input_buffer[i]->set_offset(0);
306                 }
307                 input_size += fragment_size;
308         }
309
310         engine->process(buffer, input_buffer, size,
311                 sample_rate, PluginClient::total_in_buffers, start_position);
312
313         double start_pos = (double)start_position / sample_rate;
314         for( int i = 0; i < engine->gui_gains.size(); i++ ) {
315                 CompressorGainFrame *gain_frame = new CompressorGainFrame();
316                 gain_frame->position = start_pos + sign*engine->gui_offsets[i];
317                 gain_frame->gain = engine->gui_gains[i];
318                 gain_frame->level = engine->gui_levels[i];
319                 add_gui_frame(gain_frame);
320         }
321
322         last_position += sign * size;
323         return 0;
324 }
325
326
327 CompressorConfig::CompressorConfig()
328  : CompressorConfigBase(1)
329 {
330 }
331
332 void CompressorConfig::copy_from(CompressorConfig &that)
333 {
334         CompressorConfigBase::copy_from(that);
335 }
336
337 int CompressorConfig::equivalent(CompressorConfig &that)
338 {
339         return CompressorConfigBase::equivalent(that);
340 }
341
342 void CompressorConfig::interpolate(CompressorConfig &prev,
343         CompressorConfig &next,
344         int64_t prev_frame,
345         int64_t next_frame,
346         int64_t current_frame)
347 {
348         copy_from(prev);
349 }
350
351 CompressorWindow::CompressorWindow(CompressorEffect *plugin)
352  : PluginClientWindow(plugin, xS(650), yS(480), xS(650), yS(480), 0)
353 {
354         this->plugin = plugin;
355 }
356
357 CompressorWindow::~CompressorWindow()
358 {
359 //      delete readahead;
360         delete attack;
361         delete release;
362         delete trigger;
363         delete x_text;
364         delete y_text;
365 }
366
367 void CompressorWindow::create_objects()
368 {
369         int margin = client->get_theme()->widget_border;
370         int x = margin, y = margin;
371         int control_margin = xS(130);
372         int canvas_y2 = get_h()-yS(35);
373         BC_Title *title;
374
375         add_subwindow(title = new BC_Title(x, y, "In:"));
376         int y2 = y + title->get_h() + margin;
377         EDLSession *session = plugin->get_edl()->session;
378         add_subwindow(in = new BC_Meter(x, y2, METER_VERT, canvas_y2 - y2,
379                 session->min_meter_db, session->max_meter_db, session->meter_format,
380                 1, // use_titles
381                 -1)); // span
382         x += in->get_w() + margin;
383
384         add_subwindow(title = new BC_Title(x, y, "Gain:"));
385         add_subwindow(gain_change = new BC_Meter(x, y2, METER_VERT,
386                 canvas_y2 - y2, MIN_GAIN_CHANGE, MAX_GAIN_CHANGE, METER_DB,
387                 1, // use_titles
388                 -1, // span
389                 0, // is_downmix
390                 1)); // is_gain_change
391 //      gain_change->update(1, 0);
392
393         x += gain_change->get_w() + xS(35);
394         add_subwindow(title = new BC_Title(x, y, _("Sound level (Press shift to snap to grid):")));
395         y += title->get_h() + 1;
396         add_subwindow(canvas = new CompressorCanvas(plugin, this, x, y,
397                 get_w() - x - control_margin - xS(10), canvas_y2 - y));
398         x = get_w() - control_margin;
399
400 //      add_subwindow(new BC_Title(x, y, _("Lookahead secs:")));
401 //      y += title->get_h() + margin;
402 //      readahead = new CompressorLookAhead(plugin, this, x, y);
403 //      readahead->create_objects();
404 //      y += readahead->get_h() + margin;
405
406         add_subwindow(new BC_Title(x, y, _("Attack secs:")));
407         y += title->get_h() + margin;
408         attack = new CompressorAttack(plugin, this, x, y);
409         attack->create_objects();
410         y += attack->get_h() + margin;
411
412         add_subwindow(title = new BC_Title(x, y, _("Release secs:")));
413         y += title->get_h() + margin;
414         release = new CompressorRelease(plugin, this, x, y);
415         release->create_objects();
416         y += release->get_h() + margin;
417
418         add_subwindow(title = new BC_Title(x, y, _("Trigger Type:")));
419         y += title->get_h() + margin;
420         add_subwindow(input = new CompressorInput(plugin, x, y));
421         input->create_objects();
422         y += input->get_h() + margin;
423
424         add_subwindow(title = new BC_Title(x, y, _("Trigger:")));
425         y += title->get_h() + margin;
426         trigger = new CompressorTrigger(plugin, this, x, y);
427         trigger->create_objects();
428         if( plugin->config.input != CompressorConfig::TRIGGER ) trigger->disable();
429         y += trigger->get_h() + margin;
430
431         add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
432         y += smooth->get_h() + margin;
433
434         add_subwindow(title = new BC_Title(x, y, _("Output:")));
435         y += title->get_h();
436         y_text = new CompressorY(plugin, this, x, y);
437         y_text->create_objects();
438         y += y_text->get_h() + margin;
439
440         add_subwindow(title = new BC_Title(x, y, _("Input:")));
441         y += title->get_h();
442         x_text = new CompressorX(plugin, this, x, y);
443         x_text->create_objects();
444         y += x_text->get_h() + margin;
445
446         add_subwindow(clear = new CompressorClear(plugin, this, x, y));
447         y += clear->get_h() + margin;
448
449         add_subwindow(title = new BC_Title(x, y, "Gain:"));
450         y += title->get_h() + margin;
451         BandConfig *band_config = &plugin->config.bands[0];
452         add_subwindow(mkup_gain = new CompressorMkupGain(plugin, this, x, y,
453                 &band_config->mkup_gain, -10., 10.));
454         y += mkup_gain->get_h() + margin;
455
456         add_subwindow(reset = new CompressorReset(plugin, this, x, y));
457 //      y += reset->get_h() + margin;
458
459         x = xS(10);
460         y = get_h() - yS(40);
461
462         canvas->create_objects();
463         canvas->update();
464         show_window();
465 }
466
467
468 void CompressorWindow::update()
469 {
470         update_textboxes();
471         BandConfig *band_config = &plugin->config.bands[0];
472         mkup_gain->update(band_config->mkup_gain);
473         canvas->update();
474 }
475
476 void CompressorWindow::update_meter(CompressorGainFrame *gain_frame)
477 {
478         gain_change->update(gain_frame->gain, 0);
479         in->update(gain_frame->level, 0);
480 }
481
482 void CompressorWindow::update_textboxes()
483 {
484         BandConfig *band_config = &plugin->config.bands[0];
485
486         if( atol(trigger->get_text()) != plugin->config.trigger )
487                 trigger->update((int64_t)plugin->config.trigger);
488         if( strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)) )
489                 input->set_text(CompressorInput::value_to_text(plugin->config.input));
490
491         if( plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled() )
492                 trigger->disable();
493         else
494         if( plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled() )
495                 trigger->enable();
496
497 //      if( !EQUIV(atof(readahead->get_text()), band_config->readahead_len) )
498 //              readahead->update((float)band_config->readahead_len);
499         if( !EQUIV(atof(attack->get_text()), band_config->attack_len) )
500                 attack->update((float)band_config->attack_len);
501         if( !EQUIV(atof(release->get_text()), band_config->release_len) )
502                 release->update((float)band_config->release_len);
503         smooth->update(plugin->config.smoothing_only);
504         if( canvas->current_operation == CompressorCanvas::DRAG ) {
505                 x_text->update((float)band_config->levels[canvas->current_point].x);
506                 y_text->update((float)band_config->levels[canvas->current_point].y);
507         }
508 }
509
510 int CompressorWindow::resize_event(int w, int h)
511 {
512         return 1;
513 }
514
515
516 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin,
517         CompressorWindow *window, int x, int y, int w, int h)
518  : CompressorCanvasBase(&plugin->config, plugin, window, x, y, w, h)
519 {
520 }
521
522
523 void CompressorCanvas::update_window()
524 {
525         ((CompressorWindow*)window)->update();
526 }
527
528
529 // CompressorLookAhead::CompressorLookAhead(CompressorEffect *plugin,
530 //       CompressorWindow *window, int x, int y)
531 //  : BC_TumbleTextBox(window, (float)plugin->config.bands[0].readahead_len,
532 //       (float)MIN_LOOKAHEAD, (float)MAX_LOOKAHEAD, x, y, xS(100))
533 // {
534 //      this->plugin = plugin;
535 //       set_increment(0.1);
536 //       set_precision(2);
537 // }
538 //
539 // int CompressorLookAhead::handle_event()
540 // {
541 //      plugin->config.bands[0].readahead_len = atof(get_text());
542 //      plugin->send_configure_change();
543 //      return 1;
544 // }
545 //
546 //
547
548
549 CompressorAttack::CompressorAttack(CompressorEffect *plugin,
550         CompressorWindow *window, int x, int y)
551  : BC_TumbleTextBox(window, (float)plugin->config.bands[0].attack_len,
552         (float)MIN_ATTACK, (float)MAX_ATTACK, x, y, xS(100))
553 {
554         this->plugin = plugin;
555         set_increment(0.1);
556         set_precision(2);
557 }
558
559 int CompressorAttack::handle_event()
560 {
561         plugin->config.bands[0].attack_len = atof(get_text());
562         plugin->send_configure_change();
563         return 1;
564 }
565
566 CompressorRelease::CompressorRelease(CompressorEffect *plugin,
567         CompressorWindow *window, int x, int y)
568  : BC_TumbleTextBox(window, (float)plugin->config.bands[0].release_len,
569         (float)MIN_DECAY, (float)MAX_DECAY, x, y, xS(100))
570 {
571         this->plugin = plugin;
572         set_increment(0.1);
573         set_precision(2);
574 }
575 int CompressorRelease::handle_event()
576 {
577         plugin->config.bands[0].release_len = atof(get_text());
578         plugin->send_configure_change();
579         return 1;
580 }
581
582
583 CompressorX::CompressorX(CompressorEffect *plugin,
584         CompressorWindow *window, int x, int y)
585  : BC_TumbleTextBox(window, (float)0.0,
586         plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
587 {
588         this->plugin = plugin;
589         this->window = window;
590         set_increment(0.1);
591         set_precision(2);
592 }
593 int CompressorX::handle_event()
594 {
595         BandConfig *band_config = &plugin->config.bands[0];
596         int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
597         if( current_point < band_config->levels.total ) {
598                 band_config->levels[current_point].x = atof(get_text());
599                 window->canvas->update();
600                 plugin->send_configure_change();
601         }
602         return 1;
603 }
604
605 CompressorY::CompressorY(CompressorEffect *plugin,
606         CompressorWindow *window, int x, int y)
607  : BC_TumbleTextBox(window, (float)0.0,
608         plugin->config.min_db, plugin->config.max_db, x, y, xS(100))
609 {
610         this->plugin = plugin;
611         this->window = window;
612         set_increment(0.1);
613         set_precision(2);
614 }
615 int CompressorY::handle_event()
616 {
617         BandConfig *band_config = &plugin->config.bands[0];
618         int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
619         if( current_point < band_config->levels.total ) {
620                 band_config->levels[current_point].y = atof(get_text());
621                 window->canvas->update();
622                 plugin->send_configure_change();
623         }
624         return 1;
625 }
626
627
628 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin,
629         CompressorWindow *window, int x, int y)
630  : BC_TumbleTextBox(window, (int)plugin->config.trigger,
631         MIN_TRIGGER, MAX_TRIGGER, x, y, xS(100))
632 {
633         this->plugin = plugin;
634         set_increment(1);
635 }
636 int CompressorTrigger::handle_event()
637 {
638         plugin->config.trigger = atol(get_text());
639         plugin->send_configure_change();
640         return 1;
641 }
642
643
644 CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y)
645  : BC_PopupMenu(x, y, xS(100),
646         CompressorInput::value_to_text(plugin->config.input), 1)
647 {
648         this->plugin = plugin;
649 }
650 int CompressorInput::handle_event()
651 {
652         plugin->config.input = text_to_value(get_text());
653         ((CompressorWindow*)plugin->thread->window)->update();
654         plugin->send_configure_change();
655         return 1;
656 }
657
658 void CompressorInput::create_objects()
659 {
660         for( int i = 0; i < 3; i++ ) {
661                 add_item(new BC_MenuItem(value_to_text(i)));
662         }
663 }
664
665 const char* CompressorInput::value_to_text(int value)
666 {
667         switch( value )
668         {
669                 case CompressorConfig::TRIGGER: return "Trigger";
670                 case CompressorConfig::MAX: return "Maximum";
671                 case CompressorConfig::SUM: return "Total";
672         }
673
674         return "Trigger";
675 }
676
677 int CompressorInput::text_to_value(char *text)
678 {
679         for( int i = 0; i < 3; i++ ) {
680                 if( !strcmp(value_to_text(i), text) ) return i;
681         }
682
683         return CompressorConfig::TRIGGER;
684 }
685
686 CompressorClear::CompressorClear(CompressorEffect *plugin,
687                 CompressorWindow *window, int x, int y)
688  : BC_GenericButton(x, y, _("Clear"))
689 {
690         this->plugin = plugin;
691         this->window = window;
692 }
693
694 int CompressorClear::handle_event()
695 {
696         BandConfig *band_config = &plugin->config.bands[0];
697         band_config->reset();
698 //plugin->config.dump();
699         window->update();
700         plugin->send_configure_change();
701         return 1;
702 }
703
704 CompressorReset::CompressorReset(CompressorEffect *plugin,
705                 CompressorWindow *window, int x, int y) 
706  : BC_GenericButton(x, y, _("Reset"))
707 {
708         this->plugin = plugin;
709         this->window = window;
710 }
711
712 int CompressorReset::handle_event()
713 {
714         plugin->config.reset_bands();
715         plugin->config.reset_base();
716 //plugin->config.dump();
717         window->update();
718         plugin->send_configure_change();
719         return 1;
720 }
721
722
723 CompressorMkupGain::CompressorMkupGain(CompressorEffect *plugin, 
724                 CompressorWindow *window, int x, int y,
725                 double *output, double min, double max)
726  : BC_FPot(x, y, *output, min, max)
727 {
728         this->plugin = plugin;
729         this->window = window;
730         this->output = output;
731         set_precision(0.01);
732 }
733
734 int CompressorMkupGain::handle_event()
735 {
736         *output = get_value();
737         plugin->send_configure_change();
738         window->canvas->update();
739         return 1;
740 }
741
742
743 CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y)
744  : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
745 {
746         this->plugin = plugin;
747 }
748
749 int CompressorSmooth::handle_event()
750 {
751         plugin->config.smoothing_only = get_value();
752         plugin->send_configure_change();
753         return 1;
754 }
755