4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
25 #include "compressor.h"
42 REGISTER_PLUGIN(CompressorEffect)
49 // More potential compressor algorithms:
50 // Use single reaction time parameter. Negative reaction time uses
51 // readahead. Positive reaction time uses slope.
53 // Smooth input stage if readahead.
54 // Determine slope from current smoothed sample to every sample in readahead area.
55 // Once highest slope is found, count of number of samples remaining until it is
56 // reached. Only search after this count for the next highest slope.
57 // Use highest slope to determine smoothed value.
59 // Smooth input stage if not readahead.
60 // For every sample, calculate slope needed to reach current sample from
61 // current smoothed value in the reaction time. If higher than current slope,
62 // make it the current slope and count number of samples remaining until it is
63 // reached. If this count is met and no higher slopes are found, base slope
64 // on current sample when count is met.
67 // For every sample, calculate gain from smoothed input value.
73 CompressorEffect::CompressorEffect(PluginServer *server)
74 : PluginAClient(server)
80 CompressorEffect::~CompressorEffect()
87 void CompressorEffect::delete_dsp()
91 for(int i = 0; i < PluginClient::total_in_buffers; i++)
92 delete input_buffer[i];
93 delete [] input_buffer;
103 void CompressorEffect::reset()
111 previous_target = 1.0;
113 target_current_sample = -1;
117 const char* CompressorEffect::plugin_title() { return N_("Compressor"); }
118 int CompressorEffect::is_realtime() { return 1; }
119 int CompressorEffect::is_multichannel() { return 1; }
123 void CompressorEffect::read_data(KeyFrame *keyframe)
126 input.set_shared_input(keyframe->xbuf);
129 config.levels.remove_all();
132 result = input.read_tag();
136 if(input.tag.title_is("COMPRESSOR"))
138 config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len);
139 config.decay_len = input.tag.get_property("DECAY_LEN", config.decay_len);
140 config.trigger = input.tag.get_property("TRIGGER", config.trigger);
141 config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
142 config.input = input.tag.get_property("INPUT", config.input);
145 if(input.tag.title_is("LEVEL"))
147 double x = input.tag.get_property("X", (double)0);
148 double y = input.tag.get_property("Y", (double)0);
149 compressor_point_t point = { x, y };
151 config.levels.append(point);
157 void CompressorEffect::save_data(KeyFrame *keyframe)
160 output.set_shared_output(keyframe->xbuf);
162 output.tag.set_title("COMPRESSOR");
163 output.tag.set_property("TRIGGER", config.trigger);
164 output.tag.set_property("REACTION_LEN", config.reaction_len);
165 output.tag.set_property("DECAY_LEN", config.decay_len);
166 output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
167 output.tag.set_property("INPUT", config.input);
169 output.tag.set_title("/COMPRESSOR");
171 output.append_newline();
174 for(int i = 0; i < config.levels.total; i++)
176 output.tag.set_title("LEVEL");
177 output.tag.set_property("X", config.levels.values[i].x);
178 output.tag.set_property("Y", config.levels.values[i].y);
181 output.tag.set_title("/LEVEL");
183 output.append_newline();
186 output.terminate_string();
190 void CompressorEffect::update_gui()
192 if( !thread ) return;
193 CompressorWindow *window = (CompressorWindow*)thread->window;
194 // load_configuration,read_data deletes levels
195 window->lock_window("CompressorEffect::update_gui");
196 if( load_configuration() )
198 window->unlock_window();
202 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
203 NEW_WINDOW_MACRO(CompressorEffect, CompressorWindow)
208 int CompressorEffect::process_buffer(int64_t size,
210 int64_t start_position,
213 load_configuration();
215 // Calculate linear transfer from db
217 for(int i = 0; i < config.levels.total; i++)
220 levels.values[i].x = DB::fromdb(config.levels.values[i].x);
221 levels.values[i].y = DB::fromdb(config.levels.values[i].y);
223 min_x = DB::fromdb(config.min_db);
224 min_y = DB::fromdb(config.min_db);
229 int reaction_samples = (int)(config.reaction_len * sample_rate + 0.5);
230 int decay_samples = (int)(config.decay_len * sample_rate + 0.5);
231 int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1);
233 CLAMP(reaction_samples, -1000000, 1000000);
234 CLAMP(decay_samples, reaction_samples, 1000000);
235 CLAMP(decay_samples, 1, 1000000);
236 if(labs(reaction_samples) < 1) reaction_samples = 1;
237 if(labs(decay_samples) < 1) decay_samples = 1;
239 int total_buffers = get_total_buffers();
240 if(reaction_samples >= 0)
242 if(target_current_sample < 0) target_current_sample = reaction_samples;
243 for(int i = 0; i < total_buffers; i++)
245 read_samples(buffer[i],
252 double current_slope = (next_target - previous_target) /
254 double *trigger_buffer = buffer[trigger]->get_data();
255 for(int i = 0; i < size; i++)
257 // Get slope required to reach current sample from smoothed sample over reaction
262 case CompressorConfig::MAX:
265 for(int j = 0; j < total_buffers; j++)
267 sample = fabs(buffer[j]->get_data()[i]);
268 if(sample > max) max = sample;
274 case CompressorConfig::TRIGGER:
275 sample = fabs(trigger_buffer[i]);
278 case CompressorConfig::SUM:
281 for(int j = 0; j < total_buffers; j++)
283 sample = fabs(buffer[j]->get_data()[i]);
291 double new_slope = (sample - current_value) /
294 // Slope greater than current slope
295 if(new_slope >= current_slope &&
296 (current_slope >= 0 ||
299 next_target = sample;
300 previous_target = current_value;
301 target_current_sample = 0;
302 target_samples = reaction_samples;
303 current_slope = new_slope;
306 if(sample > next_target && current_slope < 0)
308 next_target = sample;
309 previous_target = current_value;
310 target_current_sample = 0;
311 target_samples = decay_samples;
312 current_slope = (sample - current_value) / decay_samples;
314 // Current smoothed sample came up without finding higher slope
315 if(target_current_sample >= target_samples)
317 next_target = sample;
318 previous_target = current_value;
319 target_current_sample = 0;
320 target_samples = decay_samples;
321 current_slope = (sample - current_value) / decay_samples;
324 // Update current value and store gain
325 current_value = (next_target * target_current_sample +
326 previous_target * (target_samples - target_current_sample)) /
329 target_current_sample++;
331 if(config.smoothing_only)
333 for(int j = 0; j < total_buffers; j++)
334 buffer[j]->get_data()[i] = current_value;
338 double gain = calculate_gain(current_value);
339 for(int j = 0; j < total_buffers; j++)
341 buffer[j]->get_data()[i] *= gain;
348 if(target_current_sample < 0) target_current_sample = target_samples;
349 int64_t preview_samples = -reaction_samples;
351 // Start of new buffer is outside the current buffer. Start buffer over.
352 if(start_position < input_start ||
353 start_position >= input_start + input_size)
356 input_start = start_position;
359 // Shift current buffer so the buffer starts on start_position
360 if(start_position > input_start &&
361 start_position < input_start + input_size)
365 int len = input_start + input_size - start_position;
366 for(int i = 0; i < total_buffers; i++)
368 memcpy(input_buffer[i]->get_data(),
369 input_buffer[i]->get_data() + (start_position - input_start),
370 len * sizeof(double));
373 input_start = start_position;
377 // Expand buffer to handle preview size
378 if(size + preview_samples > input_allocated)
380 Samples **new_input_buffer = new Samples*[total_buffers];
381 for(int i = 0; i < total_buffers; i++)
383 new_input_buffer[i] = new Samples(size + preview_samples);
386 memcpy(new_input_buffer[i]->get_data(),
387 input_buffer[i]->get_data(),
388 input_size * sizeof(double));
389 delete input_buffer[i];
392 if(input_buffer) delete [] input_buffer;
394 input_allocated = size + preview_samples;
395 input_buffer = new_input_buffer;
398 // Append data to input buffer to construct readahead area.
399 #define MAX_FRAGMENT_SIZE 131072
400 while(input_size < size + preview_samples)
402 int fragment_size = MAX_FRAGMENT_SIZE;
403 if(fragment_size + input_size > size + preview_samples)
404 fragment_size = size + preview_samples - input_size;
405 for(int i = 0; i < total_buffers; i++)
407 input_buffer[i]->set_offset(input_size);
408 //printf("CompressorEffect::process_buffer %d %p %d\n", __LINE__, input_buffer[i], input_size);
409 read_samples(input_buffer[i],
412 input_start + input_size,
414 input_buffer[i]->set_offset(0);
416 input_size += fragment_size;
420 double current_slope = (next_target - previous_target) /
422 double *trigger_buffer = input_buffer[trigger]->get_data();
423 for(int i = 0; i < size; i++)
425 // Get slope from current sample to every sample in preview_samples.
426 // Take highest one or first one after target_samples are up.
428 // For optimization, calculate the first slope we really need.
429 // Assume every slope up to the end of preview_samples has been calculated and
430 // found <= to current slope.
431 int first_slope = preview_samples - 1;
432 // Need new slope immediately
433 if(target_current_sample >= target_samples)
435 for(int j = first_slope;
442 case CompressorConfig::MAX:
445 for(int k = 0; k < total_buffers; k++)
447 sample = fabs(input_buffer[k]->get_data()[i + j]);
448 if(sample > max) max = sample;
454 case CompressorConfig::TRIGGER:
455 sample = fabs(trigger_buffer[i + j]);
458 case CompressorConfig::SUM:
461 for(int k = 0; k < total_buffers; k++)
463 sample = fabs(input_buffer[k]->get_data()[i + j]);
476 double new_slope = (sample - current_value) /
478 // Got equal or higher slope
479 if(new_slope >= current_slope &&
480 (current_slope >= 0 ||
483 target_current_sample = 0;
485 current_slope = new_slope;
486 next_target = sample;
487 previous_target = current_value;
490 if(sample > next_target && current_slope < 0)
492 target_current_sample = 0;
493 target_samples = decay_samples;
494 current_slope = (sample - current_value) /
496 next_target = sample;
497 previous_target = current_value;
500 // Hit end of current slope range without finding higher slope
501 if(target_current_sample >= target_samples)
503 target_current_sample = 0;
504 target_samples = decay_samples;
505 current_slope = (sample - current_value) / decay_samples;
506 next_target = sample;
507 previous_target = current_value;
511 // Update current value and multiply gain
512 current_value = (next_target * target_current_sample +
513 previous_target * (target_samples - target_current_sample)) /
515 //buffer[0][i] = current_value;
516 target_current_sample++;
518 if(config.smoothing_only)
520 for(int j = 0; j < total_buffers; j++)
522 buffer[j]->get_data()[i] = current_value;
527 double gain = calculate_gain(current_value);
528 for(int j = 0; j < total_buffers; j++)
530 buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
546 double CompressorEffect::calculate_output(double x)
548 if(x > 0.999) return 1.0;
550 for(int i = levels.total - 1; i >= 0; i--)
552 if(levels.values[i].x <= x)
554 if(i < levels.total - 1)
556 return levels.values[i].y +
557 (x - levels.values[i].x) *
558 (levels.values[i + 1].y - levels.values[i].y) /
559 (levels.values[i + 1].x - levels.values[i].x);
563 return levels.values[i].y +
564 (x - levels.values[i].x) *
565 (max_y - levels.values[i].y) /
566 (max_x - levels.values[i].x);
575 (levels.values[0].y - min_y) /
576 (levels.values[0].x - min_x);
583 double CompressorEffect::calculate_gain(double input)
585 // double x_db = DB::todb(input);
586 // double y_db = config.calculate_db(x_db);
587 // double y_linear = DB::fromdb(y_db);
588 double y_linear = calculate_output(input);
591 gain = y_linear / input;
606 CompressorConfig::CompressorConfig()
615 input = CompressorConfig::TRIGGER;
620 void CompressorConfig::copy_from(CompressorConfig &that)
622 this->reaction_len = that.reaction_len;
623 this->decay_len = that.decay_len;
624 this->min_db = that.min_db;
625 this->min_x = that.min_x;
626 this->min_y = that.min_y;
627 this->max_x = that.max_x;
628 this->max_y = that.max_y;
629 this->trigger = that.trigger;
630 this->input = that.input;
631 this->smoothing_only = that.smoothing_only;
633 for(int i = 0; i < that.levels.total; i++)
634 this->levels.append(that.levels.values[i]);
637 int CompressorConfig::equivalent(CompressorConfig &that)
639 if(!EQUIV(this->reaction_len, that.reaction_len) ||
640 !EQUIV(this->decay_len, that.decay_len) ||
641 this->trigger != that.trigger ||
642 this->input != that.input ||
643 this->smoothing_only != that.smoothing_only)
645 if(this->levels.total != that.levels.total) return 0;
647 i < this->levels.total && i < that.levels.total;
650 compressor_point_t *this_level = &this->levels.values[i];
651 compressor_point_t *that_level = &that.levels.values[i];
652 if(!EQUIV(this_level->x, that_level->x) ||
653 !EQUIV(this_level->y, that_level->y))
659 void CompressorConfig::interpolate(CompressorConfig &prev,
660 CompressorConfig &next,
663 int64_t current_frame)
668 int CompressorConfig::total_points()
676 void CompressorConfig::dump()
678 printf("CompressorConfig::dump\n");
679 for(int i = 0; i < levels.total; i++)
681 printf(" %f %f\n", levels.values[i].x, levels.values[i].y);
686 double CompressorConfig::get_y(int number)
691 if(number >= levels.total)
692 return levels.values[levels.total - 1].y;
694 return levels.values[number].y;
697 double CompressorConfig::get_x(int number)
702 if(number >= levels.total)
703 return levels.values[levels.total - 1].x;
705 return levels.values[number].x;
708 double CompressorConfig::calculate_db(double x)
710 if(x > -0.001) return 0.0;
712 for(int i = levels.total - 1; i >= 0; i--)
714 if(levels.values[i].x <= x)
716 if(i < levels.total - 1)
718 return levels.values[i].y +
719 (x - levels.values[i].x) *
720 (levels.values[i + 1].y - levels.values[i].y) /
721 (levels.values[i + 1].x - levels.values[i].x);
725 return levels.values[i].y +
726 (x - levels.values[i].x) *
727 (max_y - levels.values[i].y) /
728 (max_x - levels.values[i].x);
737 (levels.values[0].y - min_y) /
738 (levels.values[0].x - min_x);
745 int CompressorConfig::set_point(double x, double y)
747 for(int i = levels.total - 1; i >= 0; i--)
749 if(levels.values[i].x < x)
753 for(int j = levels.total - 2; j >= i; j--)
755 levels.values[j + 1] = levels.values[j];
757 levels.values[i].x = x;
758 levels.values[i].y = y;
765 for(int j = levels.total - 2; j >= 0; j--)
767 levels.values[j + 1] = levels.values[j];
769 levels.values[0].x = x;
770 levels.values[0].y = y;
774 void CompressorConfig::remove_point(int number)
776 for(int j = number; j < levels.total - 1; j++)
778 levels.values[j] = levels.values[j + 1];
783 void CompressorConfig::optimize()
792 for(int i = 0; i < levels.total - 1; i++)
794 if(levels.values[i].x >= levels.values[i + 1].x)
797 for(int j = i + 1; j < levels.total - 1; j++)
799 levels.values[j] = levels.values[j + 1];
834 CompressorWindow::CompressorWindow(CompressorEffect *plugin)
835 : PluginClientWindow(plugin,
842 this->plugin = plugin;
845 void CompressorWindow::create_objects()
847 int xs10 = xS(10), xs20 = xS(20), xs50 = xS(50), xs110 = xS(110);
848 int ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys60 = yS(60), ys70 = yS(70);
849 int x = xS(35), y = yS(10);
850 int control_margin = xS(130);
852 add_subwindow(canvas = new CompressorCanvas(plugin,
855 get_w() - x - control_margin - xs10,
856 get_h() - y - ys70));
857 canvas->set_cursor(CROSS_CURSOR, 0, 0);
858 x = get_w() - control_margin;
859 add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
861 add_subwindow(reaction = new CompressorReaction(plugin, x, y));
863 add_subwindow(new BC_Title(x, y, _("Decay secs:")));
865 add_subwindow(decay = new CompressorDecay(plugin, x, y));
867 add_subwindow(new BC_Title(x, y, _("Trigger Type:")));
869 add_subwindow(input = new CompressorInput(plugin, x, y));
870 input->create_objects();
872 add_subwindow(new BC_Title(x, y, _("Trigger:")));
874 add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
875 if(plugin->config.input != CompressorConfig::TRIGGER) trigger->disable();
877 add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
879 add_subwindow(clear = new CompressorClear(plugin, x, y));
882 add_subwindow(new BC_Title(x, y, _("Point:")));
884 add_subwindow(x_text = new CompressorX(plugin, x, y));
886 add_subwindow(new BC_Title(x, y, _("x")));
888 add_subwindow(y_text = new CompressorY(plugin, x, y));
895 void CompressorWindow::draw_scales()
897 draw_3d_border(canvas->get_x() - 2,
908 set_color(get_resources()->default_text_color);
911 for(int i = 0; i <= DIVISIONS; i++)
913 int y = canvas->get_y() + yS(10) + canvas->get_h() / DIVISIONS * i;
914 int x = canvas->get_x() - xS(30);
915 char string[BCTEXTLEN];
917 sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
918 draw_text(x, y, string);
920 int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
921 int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
922 for(int j = 0; j < 10; j++)
924 y = y1 + (y2 - y1) * j / 10;
927 draw_line(canvas->get_x() - xS(10), y, canvas->get_x(), y);
932 draw_line(canvas->get_x() - xS(5), y, canvas->get_x(), y);
938 for(int i = 0; i <= DIVISIONS; i++)
940 int y = canvas->get_h() + yS(30);
941 int x = canvas->get_x() + (canvas->get_w() - xS(10)) / DIVISIONS * i;
942 char string[BCTEXTLEN];
944 sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
945 draw_text(x, y, string);
947 int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
948 int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
949 for(int j = 0; j < 10; j++)
951 x = x1 + (x2 - x1) * j / 10;
954 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(10));
959 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + yS(5));
969 void CompressorWindow::update()
975 void CompressorWindow::update_textboxes()
977 if(atol(trigger->get_text()) != plugin->config.trigger)
978 trigger->update((int64_t)plugin->config.trigger);
979 if(strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)))
980 input->set_text(CompressorInput::value_to_text(plugin->config.input));
982 if(plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled())
985 if(plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled())
988 if(!EQUIV(atof(reaction->get_text()), plugin->config.reaction_len))
989 reaction->update((float)plugin->config.reaction_len);
990 if(!EQUIV(atof(decay->get_text()), plugin->config.decay_len))
991 decay->update((float)plugin->config.decay_len);
992 smooth->update(plugin->config.smoothing_only);
993 if(canvas->current_operation == CompressorCanvas::DRAG)
995 x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
996 y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
1000 #define POINT_W xS(10)
1001 void CompressorWindow::update_canvas()
1003 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
1004 canvas->set_line_dashes(1);
1005 canvas->set_color(GREEN);
1007 for(int i = 1; i < DIVISIONS; i++)
1009 int y = canvas->get_h() * i / DIVISIONS;
1010 canvas->draw_line(0, y, canvas->get_w(), y);
1012 int x = canvas->get_w() * i / DIVISIONS;
1013 canvas->draw_line(x, 0, x, canvas->get_h());
1015 canvas->set_line_dashes(0);
1018 canvas->set_font(MEDIUMFONT);
1019 canvas->draw_text(plugin->get_theme()->widget_border,
1020 canvas->get_h() / 2,
1022 canvas->draw_text(canvas->get_w() / 2 - canvas->get_text_width(MEDIUMFONT, _("Input")) / 2,
1023 canvas->get_h() - plugin->get_theme()->widget_border,
1027 canvas->set_color(WHITE);
1028 canvas->set_line_width(2);
1030 double x_db = plugin->config.min_db;
1031 double y_db = plugin->config.calculate_db(x_db);
1032 int y1 = (int)(y_db / plugin->config.min_db * canvas->get_h());
1034 for(int i=1; i<canvas->get_w(); i++)
1036 x_db = (1. - (double)i / canvas->get_w()) * plugin->config.min_db;
1037 y_db = plugin->config.calculate_db(x_db);
1038 int y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
1039 canvas->draw_line(i-1, y1, i, y2);
1042 canvas->set_line_width(1);
1044 int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
1045 for(int i=0; i < total; i++)
1047 x_db = plugin->config.get_x(i);
1048 y_db = plugin->config.get_y(i);
1050 int x = (int)((1. - x_db / plugin->config.min_db) * canvas->get_w());
1051 int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
1053 canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
1059 int CompressorWindow::resize_event(int w, int h)
1071 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h)
1072 : BC_SubWindow(x, y, w, h, BLACK)
1074 this->plugin = plugin;
1075 current_operation = NONE;
1079 int CompressorCanvas::button_press_event()
1081 // Check existing points
1082 if(is_event_win() && cursor_inside())
1084 for(int i = 0; i < plugin->config.levels.total; i++)
1086 double x_db = plugin->config.get_x(i);
1087 double y_db = plugin->config.get_y(i);
1089 int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
1090 int y = (int)(y_db / plugin->config.min_db * get_h());
1092 if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
1093 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
1095 current_operation = DRAG;
1104 double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
1105 double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
1107 current_point = plugin->config.set_point(x_db, y_db);
1108 current_operation = DRAG;
1109 ((CompressorWindow*)plugin->thread->window)->update();
1110 plugin->send_configure_change();
1114 //plugin->config.dump();
1117 int CompressorCanvas::button_release_event()
1119 if(current_operation == DRAG)
1121 if(current_point > 0)
1123 if(plugin->config.levels.values[current_point].x <
1124 plugin->config.levels.values[current_point - 1].x)
1125 plugin->config.remove_point(current_point);
1128 if(current_point < plugin->config.levels.total - 1)
1130 if(plugin->config.levels.values[current_point].x >=
1131 plugin->config.levels.values[current_point + 1].x)
1132 plugin->config.remove_point(current_point);
1135 ((CompressorWindow*)plugin->thread->window)->update();
1136 plugin->send_configure_change();
1137 current_operation = NONE;
1144 int CompressorCanvas::cursor_motion_event()
1146 if(current_operation == DRAG)
1148 int x = get_cursor_x();
1149 int y = get_cursor_y();
1150 CLAMP(x, 0, get_w());
1151 CLAMP(y, 0, get_h());
1152 double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
1153 double y_db = (double)y / get_h() * plugin->config.min_db;
1154 plugin->config.levels.values[current_point].x = x_db;
1155 plugin->config.levels.values[current_point].y = y_db;
1156 ((CompressorWindow*)plugin->thread->window)->update();
1157 plugin->send_configure_change();
1159 //plugin->config.dump();
1162 // Change cursor over points
1163 if(is_event_win() && cursor_inside())
1165 int new_cursor = CROSS_CURSOR;
1167 for(int i = 0; i < plugin->config.levels.total; i++)
1169 double x_db = plugin->config.get_x(i);
1170 double y_db = plugin->config.get_y(i);
1172 int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
1173 int y = (int)(y_db / plugin->config.min_db * get_h());
1175 if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
1176 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
1178 new_cursor = UPRIGHT_ARROW_CURSOR;
1184 if(new_cursor != get_cursor())
1186 set_cursor(new_cursor, 0, 1);
1196 CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y)
1197 : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.reaction_len)
1199 this->plugin = plugin;
1202 int CompressorReaction::handle_event()
1204 plugin->config.reaction_len = atof(get_text());
1205 plugin->send_configure_change();
1209 int CompressorReaction::button_press_event()
1213 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1214 if(get_buttonpress() == 4)
1216 plugin->config.reaction_len += 0.1;
1219 if(get_buttonpress() == 5)
1221 plugin->config.reaction_len -= 0.1;
1223 update((float)plugin->config.reaction_len);
1224 plugin->send_configure_change();
1230 CompressorDecay::CompressorDecay(CompressorEffect *plugin, int x, int y)
1231 : BC_TextBox(x, y, xS(100), 1, (float)plugin->config.decay_len)
1233 this->plugin = plugin;
1235 int CompressorDecay::handle_event()
1237 plugin->config.decay_len = atof(get_text());
1238 plugin->send_configure_change();
1242 int CompressorDecay::button_press_event()
1246 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1247 if(get_buttonpress() == 4)
1249 plugin->config.decay_len += 0.1;
1252 if(get_buttonpress() == 5)
1254 plugin->config.decay_len -= 0.1;
1256 update((float)plugin->config.decay_len);
1257 plugin->send_configure_change();
1265 CompressorX::CompressorX(CompressorEffect *plugin, int x, int y)
1266 : BC_TextBox(x, y, xS(100), 1, "")
1268 this->plugin = plugin;
1270 int CompressorX::handle_event()
1272 int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
1273 if(current_point < plugin->config.levels.total)
1275 plugin->config.levels.values[current_point].x = atof(get_text());
1276 ((CompressorWindow*)plugin->thread->window)->update_canvas();
1277 plugin->send_configure_change();
1284 CompressorY::CompressorY(CompressorEffect *plugin, int x, int y)
1285 : BC_TextBox(x, y, xS(100), 1, "")
1287 this->plugin = plugin;
1289 int CompressorY::handle_event()
1291 int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
1292 if(current_point < plugin->config.levels.total)
1294 plugin->config.levels.values[current_point].y = atof(get_text());
1295 ((CompressorWindow*)plugin->thread->window)->update_canvas();
1296 plugin->send_configure_change();
1305 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y)
1306 : BC_TextBox(x, y, xS(100), 1, (int64_t)plugin->config.trigger)
1308 this->plugin = plugin;
1310 int CompressorTrigger::handle_event()
1312 plugin->config.trigger = atol(get_text());
1313 plugin->send_configure_change();
1317 int CompressorTrigger::button_press_event()
1321 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1322 if(get_buttonpress() == 4)
1324 plugin->config.trigger++;
1327 if(get_buttonpress() == 5)
1329 plugin->config.trigger--;
1331 update((int64_t)plugin->config.trigger);
1332 plugin->send_configure_change();
1342 CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y)
1346 CompressorInput::value_to_text(plugin->config.input),
1349 this->plugin = plugin;
1351 int CompressorInput::handle_event()
1353 plugin->config.input = text_to_value(get_text());
1354 ((CompressorWindow*)plugin->thread->window)->update();
1355 plugin->send_configure_change();
1359 void CompressorInput::create_objects()
1361 for(int i = 0; i < 3; i++)
1363 add_item(new BC_MenuItem(value_to_text(i)));
1367 const char* CompressorInput::value_to_text(int value)
1371 case CompressorConfig::TRIGGER: return _("Trigger");
1372 case CompressorConfig::MAX: return _("Maximum");
1373 case CompressorConfig::SUM: return _("Total");
1376 return _("Trigger");
1379 int CompressorInput::text_to_value(char *text)
1381 for(int i = 0; i < 3; i++)
1383 if(!strcmp(value_to_text(i), text)) return i;
1386 return CompressorConfig::TRIGGER;
1394 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
1395 : BC_GenericButton(x, y, _("Clear"))
1397 this->plugin = plugin;
1400 int CompressorClear::handle_event()
1402 plugin->config.levels.remove_all();
1403 //plugin->config.dump();
1404 ((CompressorWindow*)plugin->thread->window)->update();
1405 plugin->send_configure_change();
1411 CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y)
1412 : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
1414 this->plugin = plugin;
1417 int CompressorSmooth::handle_event()
1419 plugin->config.smoothing_only = get_value();
1420 plugin->send_configure_change();