add auto zoombar/status color, fix 3 batchrender boobies, rotate plugin tweaks, add...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / compressor / compressor.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "clip.h"
25 #include "compressor.h"
26 #include "cursors.h"
27 #include "bchash.h"
28 #include "filexml.h"
29 #include "language.h"
30 #include "samples.h"
31 #include "theme.h"
32 #include "units.h"
33 #include "vframe.h"
34
35 #include <math.h>
36 #include <string.h>
37
38
39
40
41
42 REGISTER_PLUGIN(CompressorEffect)
43
44
45
46
47
48
49 // More potential compressor algorithms:
50 // Use single reaction time parameter.  Negative reaction time uses
51 // readahead.  Positive reaction time uses slope.
52
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.
58
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.
65
66 // Gain stage.
67 // For every sample, calculate gain from smoothed input value.
68
69
70
71
72
73 CompressorEffect::CompressorEffect(PluginServer *server)
74  : PluginAClient(server)
75 {
76         reset();
77
78 }
79
80 CompressorEffect::~CompressorEffect()
81 {
82
83         delete_dsp();
84         levels.remove_all();
85 }
86
87 void CompressorEffect::delete_dsp()
88 {
89         if(input_buffer)
90         {
91                 for(int i = 0; i < PluginClient::total_in_buffers; i++)
92                         delete input_buffer[i];
93                 delete [] input_buffer;
94         }
95
96
97         input_buffer = 0;
98         input_size = 0;
99         input_allocated = 0;
100 }
101
102
103 void CompressorEffect::reset()
104 {
105         input_buffer = 0;
106         input_size = 0;
107         input_allocated = 0;
108         input_start = 0;
109
110         next_target = 1.0;
111         previous_target = 1.0;
112         target_samples = 1;
113         target_current_sample = -1;
114         current_value = 1.0;
115 }
116
117 const char* CompressorEffect::plugin_title() { return N_("Compressor"); }
118 int CompressorEffect::is_realtime() { return 1; }
119 int CompressorEffect::is_multichannel() { return 1; }
120
121
122
123 void CompressorEffect::read_data(KeyFrame *keyframe)
124 {
125         FileXML input;
126         input.set_shared_input(keyframe->xbuf);
127
128         int result = 0;
129         config.levels.remove_all();
130         while(!result)
131         {
132                 result = input.read_tag();
133
134                 if(!result)
135                 {
136                         if(input.tag.title_is("COMPRESSOR"))
137                         {
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);
143                         }
144                         else
145                         if(input.tag.title_is("LEVEL"))
146                         {
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 };
150
151                                 config.levels.append(point);
152                         }
153                 }
154         }
155 }
156
157 void CompressorEffect::save_data(KeyFrame *keyframe)
158 {
159         FileXML output;
160         output.set_shared_output(keyframe->xbuf);
161
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);
168         output.append_tag();
169         output.tag.set_title("/COMPRESSOR");
170         output.append_tag();
171         output.append_newline();
172
173
174         for(int i = 0; i < config.levels.total; i++)
175         {
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);
179
180                 output.append_tag();
181                 output.tag.set_title("/LEVEL");
182                 output.append_tag();
183                 output.append_newline();
184         }
185
186         output.terminate_string();
187 }
188
189
190 void CompressorEffect::update_gui()
191 {
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() )
197                 window->update();
198         window->unlock_window();
199 }
200
201
202 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
203 NEW_WINDOW_MACRO(CompressorEffect, CompressorWindow)
204
205
206
207
208 int CompressorEffect::process_buffer(int64_t size,
209                 Samples **buffer,
210                 int64_t start_position,
211                 int sample_rate)
212 {
213         load_configuration();
214
215 // Calculate linear transfer from db
216         levels.remove_all();
217         for(int i = 0; i < config.levels.total; i++)
218         {
219                 levels.append();
220                 levels.values[i].x = DB::fromdb(config.levels.values[i].x);
221                 levels.values[i].y = DB::fromdb(config.levels.values[i].y);
222         }
223         min_x = DB::fromdb(config.min_db);
224         min_y = DB::fromdb(config.min_db);
225         max_x = 1.0;
226         max_y = 1.0;
227
228
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);
232
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;
238
239         int total_buffers = get_total_buffers();
240         if(reaction_samples >= 0)
241         {
242                 if(target_current_sample < 0) target_current_sample = reaction_samples;
243                 for(int i = 0; i < total_buffers; i++)
244                 {
245                         read_samples(buffer[i],
246                                 i,
247                                 sample_rate,
248                                 start_position,
249                                 size);
250                 }
251
252                 double current_slope = (next_target - previous_target) /
253                         reaction_samples;
254                 double *trigger_buffer = buffer[trigger]->get_data();
255                 for(int i = 0; i < size; i++)
256                 {
257 // Get slope required to reach current sample from smoothed sample over reaction
258 // length.
259                         double sample = 0.;
260                         switch(config.input)
261                         {
262                                 case CompressorConfig::MAX:
263                                 {
264                                         double max = 0;
265                                         for(int j = 0; j < total_buffers; j++)
266                                         {
267                                                 sample = fabs(buffer[j]->get_data()[i]);
268                                                 if(sample > max) max = sample;
269                                         }
270                                         sample = max;
271                                         break;
272                                 }
273
274                                 case CompressorConfig::TRIGGER:
275                                         sample = fabs(trigger_buffer[i]);
276                                         break;
277
278                                 case CompressorConfig::SUM:
279                                 {
280                                         double max = 0;
281                                         for(int j = 0; j < total_buffers; j++)
282                                         {
283                                                 sample = fabs(buffer[j]->get_data()[i]);
284                                                 max += sample;
285                                         }
286                                         sample = max;
287                                         break;
288                                 }
289                         }
290
291                         double new_slope = (sample - current_value) /
292                                 reaction_samples;
293
294 // Slope greater than current slope
295                         if(new_slope >= current_slope &&
296                                 (current_slope >= 0 ||
297                                 new_slope >= 0))
298                         {
299                                 next_target = sample;
300                                 previous_target = current_value;
301                                 target_current_sample = 0;
302                                 target_samples = reaction_samples;
303                                 current_slope = new_slope;
304                         }
305                         else
306                         if(sample > next_target && current_slope < 0)
307                         {
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;
313                         }
314 // Current smoothed sample came up without finding higher slope
315                         if(target_current_sample >= target_samples)
316                         {
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;
322                         }
323
324 // Update current value and store gain
325                         current_value = (next_target * target_current_sample +
326                                 previous_target * (target_samples - target_current_sample)) /
327                                 target_samples;
328
329                         target_current_sample++;
330
331                         if(config.smoothing_only)
332                         {
333                                 for(int j = 0; j < total_buffers; j++)
334                                         buffer[j]->get_data()[i] = current_value;
335                         }
336                         else
337                         {
338                                 double gain = calculate_gain(current_value);
339                                 for(int j = 0; j < total_buffers; j++)
340                                 {
341                                         buffer[j]->get_data()[i] *= gain;
342                                 }
343                         }
344                 }
345         }
346         else
347         {
348                 if(target_current_sample < 0) target_current_sample = target_samples;
349                 int64_t preview_samples = -reaction_samples;
350
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)
354                 {
355                         input_size = 0;
356                         input_start = start_position;
357                 }
358                 else
359 // Shift current buffer so the buffer starts on start_position
360                 if(start_position > input_start &&
361                         start_position < input_start + input_size)
362                 {
363                         if(input_buffer)
364                         {
365                                 int len = input_start + input_size - start_position;
366                                 for(int i = 0; i < total_buffers; i++)
367                                 {
368                                         memcpy(input_buffer[i]->get_data(),
369                                                 input_buffer[i]->get_data() + (start_position - input_start),
370                                                 len * sizeof(double));
371                                 }
372                                 input_size = len;
373                                 input_start = start_position;
374                         }
375                 }
376
377 // Expand buffer to handle preview size
378                 if(size + preview_samples > input_allocated)
379                 {
380                         Samples **new_input_buffer = new Samples*[total_buffers];
381                         for(int i = 0; i < total_buffers; i++)
382                         {
383                                 new_input_buffer[i] = new Samples(size + preview_samples);
384                                 if(input_buffer)
385                                 {
386                                         memcpy(new_input_buffer[i]->get_data(),
387                                                 input_buffer[i]->get_data(),
388                                                 input_size * sizeof(double));
389                                         delete input_buffer[i];
390                                 }
391                         }
392                         if(input_buffer) delete [] input_buffer;
393
394                         input_allocated = size + preview_samples;
395                         input_buffer = new_input_buffer;
396                 }
397
398 // Append data to input buffer to construct readahead area.
399 #define MAX_FRAGMENT_SIZE 131072
400                 while(input_size < size + preview_samples)
401                 {
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++)
406                         {
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],
410                                         i,
411                                         sample_rate,
412                                         input_start + input_size,
413                                         fragment_size);
414                                 input_buffer[i]->set_offset(0);
415                         }
416                         input_size += fragment_size;
417                 }
418
419
420                 double current_slope = (next_target - previous_target) /
421                         target_samples;
422                 double *trigger_buffer = input_buffer[trigger]->get_data();
423                 for(int i = 0; i < size; i++)
424                 {
425 // Get slope from current sample to every sample in preview_samples.
426 // Take highest one or first one after target_samples are up.
427
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)
434                                 first_slope = 1;
435                         for(int j = first_slope;
436                                 j < preview_samples;
437                                 j++)
438                         {
439                                 double sample = 0.;
440                                 switch(config.input)
441                                 {
442                                         case CompressorConfig::MAX:
443                                         {
444                                                 double max = 0;
445                                                 for(int k = 0; k < total_buffers; k++)
446                                                 {
447                                                         sample = fabs(input_buffer[k]->get_data()[i + j]);
448                                                         if(sample > max) max = sample;
449                                                 }
450                                                 sample = max;
451                                                 break;
452                                         }
453
454                                         case CompressorConfig::TRIGGER:
455                                                 sample = fabs(trigger_buffer[i + j]);
456                                                 break;
457
458                                         case CompressorConfig::SUM:
459                                         {
460                                                 double max = 0;
461                                                 for(int k = 0; k < total_buffers; k++)
462                                                 {
463                                                         sample = fabs(input_buffer[k]->get_data()[i + j]);
464                                                         max += sample;
465                                                 }
466                                                 sample = max;
467                                                 break;
468                                         }
469                                 }
470
471
472
473
474
475
476                                 double new_slope = (sample - current_value) /
477                                         j;
478 // Got equal or higher slope
479                                 if(new_slope >= current_slope &&
480                                         (current_slope >= 0 ||
481                                         new_slope >= 0))
482                                 {
483                                         target_current_sample = 0;
484                                         target_samples = j;
485                                         current_slope = new_slope;
486                                         next_target = sample;
487                                         previous_target = current_value;
488                                 }
489                                 else
490                                 if(sample > next_target && current_slope < 0)
491                                 {
492                                         target_current_sample = 0;
493                                         target_samples = decay_samples;
494                                         current_slope = (sample - current_value) /
495                                                 decay_samples;
496                                         next_target = sample;
497                                         previous_target = current_value;
498                                 }
499
500 // Hit end of current slope range without finding higher slope
501                                 if(target_current_sample >= target_samples)
502                                 {
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;
508                                 }
509                         }
510
511 // Update current value and multiply gain
512                         current_value = (next_target * target_current_sample +
513                                 previous_target * (target_samples - target_current_sample)) /
514                                 target_samples;
515 //buffer[0][i] = current_value;
516                         target_current_sample++;
517
518                         if(config.smoothing_only)
519                         {
520                                 for(int j = 0; j < total_buffers; j++)
521                                 {
522                                         buffer[j]->get_data()[i] = current_value;
523                                 }
524                         }
525                         else
526                         {
527                                 double gain = calculate_gain(current_value);
528                                 for(int j = 0; j < total_buffers; j++)
529                                 {
530                                         buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
531                                 }
532                         }
533                 }
534
535
536
537         }
538
539
540
541
542
543         return 0;
544 }
545
546 double CompressorEffect::calculate_output(double x)
547 {
548         if(x > 0.999) return 1.0;
549
550         for(int i = levels.total - 1; i >= 0; i--)
551         {
552                 if(levels.values[i].x <= x)
553                 {
554                         if(i < levels.total - 1)
555                         {
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);
560                         }
561                         else
562                         {
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);
567                         }
568                 }
569         }
570
571         if(levels.total)
572         {
573                 return min_y +
574                         (x - min_x) *
575                         (levels.values[0].y - min_y) /
576                         (levels.values[0].x - min_x);
577         }
578         else
579                 return x;
580 }
581
582
583 double CompressorEffect::calculate_gain(double input)
584 {
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);
589         double gain;
590         if(input != 0)
591                 gain = y_linear / input;
592         else
593                 gain = 100000;
594         return gain;
595 }
596
597
598
599
600
601
602
603
604
605
606 CompressorConfig::CompressorConfig()
607 {
608         reaction_len = 1.0;
609         min_db = -80.0;
610         min_x = min_db;
611         min_y = min_db;
612         max_x = 0;
613         max_y = 0;
614         trigger = 0;
615         input = CompressorConfig::TRIGGER;
616         smoothing_only = 0;
617         decay_len = 1.0;
618 }
619
620 void CompressorConfig::copy_from(CompressorConfig &that)
621 {
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;
632         levels.remove_all();
633         for(int i = 0; i < that.levels.total; i++)
634                 this->levels.append(that.levels.values[i]);
635 }
636
637 int CompressorConfig::equivalent(CompressorConfig &that)
638 {
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)
644                 return 0;
645         if(this->levels.total != that.levels.total) return 0;
646         for(int i = 0;
647                 i < this->levels.total && i < that.levels.total;
648                 i++)
649         {
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))
654                         return 0;
655         }
656         return 1;
657 }
658
659 void CompressorConfig::interpolate(CompressorConfig &prev,
660         CompressorConfig &next,
661         int64_t prev_frame,
662         int64_t next_frame,
663         int64_t current_frame)
664 {
665         copy_from(prev);
666 }
667
668 int CompressorConfig::total_points()
669 {
670         if(!levels.total)
671                 return 1;
672         else
673                 return levels.total;
674 }
675
676 void CompressorConfig::dump()
677 {
678         printf("CompressorConfig::dump\n");
679         for(int i = 0; i < levels.total; i++)
680         {
681                 printf("        %f %f\n", levels.values[i].x, levels.values[i].y);
682         }
683 }
684
685
686 double CompressorConfig::get_y(int number)
687 {
688         if(!levels.total)
689                 return 1.0;
690         else
691         if(number >= levels.total)
692                 return levels.values[levels.total - 1].y;
693         else
694                 return levels.values[number].y;
695 }
696
697 double CompressorConfig::get_x(int number)
698 {
699         if(!levels.total)
700                 return 0.0;
701         else
702         if(number >= levels.total)
703                 return levels.values[levels.total - 1].x;
704         else
705                 return levels.values[number].x;
706 }
707
708 double CompressorConfig::calculate_db(double x)
709 {
710         if(x > -0.001) return 0.0;
711
712         for(int i = levels.total - 1; i >= 0; i--)
713         {
714                 if(levels.values[i].x <= x)
715                 {
716                         if(i < levels.total - 1)
717                         {
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);
722                         }
723                         else
724                         {
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);
729                         }
730                 }
731         }
732
733         if(levels.total)
734         {
735                 return min_y +
736                         (x - min_x) *
737                         (levels.values[0].y - min_y) /
738                         (levels.values[0].x - min_x);
739         }
740         else
741                 return x;
742 }
743
744
745 int CompressorConfig::set_point(double x, double y)
746 {
747         for(int i = levels.total - 1; i >= 0; i--)
748         {
749                 if(levels.values[i].x < x)
750                 {
751                         levels.append();
752                         i++;
753                         for(int j = levels.total - 2; j >= i; j--)
754                         {
755                                 levels.values[j + 1] = levels.values[j];
756                         }
757                         levels.values[i].x = x;
758                         levels.values[i].y = y;
759
760                         return i;
761                 }
762         }
763
764         levels.append();
765         for(int j = levels.total - 2; j >= 0; j--)
766         {
767                 levels.values[j + 1] = levels.values[j];
768         }
769         levels.values[0].x = x;
770         levels.values[0].y = y;
771         return 0;
772 }
773
774 void CompressorConfig::remove_point(int number)
775 {
776         for(int j = number; j < levels.total - 1; j++)
777         {
778                 levels.values[j] = levels.values[j + 1];
779         }
780         levels.remove();
781 }
782
783 void CompressorConfig::optimize()
784 {
785         int done = 0;
786
787         while(!done)
788         {
789                 done = 1;
790
791
792                 for(int i = 0; i < levels.total - 1; i++)
793                 {
794                         if(levels.values[i].x >= levels.values[i + 1].x)
795                         {
796                                 done = 0;
797                                 for(int j = i + 1; j < levels.total - 1; j++)
798                                 {
799                                         levels.values[j] = levels.values[j + 1];
800                                 }
801                                 levels.remove();
802                         }
803                 }
804
805         }
806 }
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834 CompressorWindow::CompressorWindow(CompressorEffect *plugin)
835  : PluginClientWindow(plugin,
836         650,
837         480,
838         650,
839         480,
840         0)
841 {
842         this->plugin = plugin;
843 }
844
845 void CompressorWindow::create_objects()
846 {
847         int x = 35, y = 10;
848         int control_margin = 130;
849
850         add_subwindow(canvas = new CompressorCanvas(plugin,
851                 x,
852                 y,
853                 get_w() - x - control_margin - 10,
854                 get_h() - y - 70));
855         canvas->set_cursor(CROSS_CURSOR, 0, 0);
856         x = get_w() - control_margin;
857         add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
858         y += 20;
859         add_subwindow(reaction = new CompressorReaction(plugin, x, y));
860         y += 30;
861         add_subwindow(new BC_Title(x, y, _("Decay secs:")));
862         y += 20;
863         add_subwindow(decay = new CompressorDecay(plugin, x, y));
864         y += 30;
865         add_subwindow(new BC_Title(x, y, _("Trigger Type:")));
866         y += 20;
867         add_subwindow(input = new CompressorInput(plugin, x, y));
868         input->create_objects();
869         y += 30;
870         add_subwindow(new BC_Title(x, y, _("Trigger:")));
871         y += 20;
872         add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
873         if(plugin->config.input != CompressorConfig::TRIGGER) trigger->disable();
874         y += 30;
875         add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
876         y += 60;
877         add_subwindow(clear = new CompressorClear(plugin, x, y));
878         x = 10;
879         y = get_h() - 40;
880         add_subwindow(new BC_Title(x, y, _("Point:")));
881         x += 50;
882         add_subwindow(x_text = new CompressorX(plugin, x, y));
883         x += 110;
884         add_subwindow(new BC_Title(x, y, _("x")));
885         x += 20;
886         add_subwindow(y_text = new CompressorY(plugin, x, y));
887         draw_scales();
888
889         update_canvas();
890         show_window();
891 }
892
893 void CompressorWindow::draw_scales()
894 {
895         draw_3d_border(canvas->get_x() - 2,
896                 canvas->get_y() - 2,
897                 canvas->get_w() + 4,
898                 canvas->get_h() + 4,
899                 get_bg_color(),
900                 BLACK,
901                 MDGREY,
902                 get_bg_color());
903
904
905         set_font(SMALLFONT);
906         set_color(get_resources()->default_text_color);
907
908 #define DIVISIONS 8
909         for(int i = 0; i <= DIVISIONS; i++)
910         {
911                 int y = canvas->get_y() + 10 + canvas->get_h() / DIVISIONS * i;
912                 int x = canvas->get_x() - 30;
913                 char string[BCTEXTLEN];
914
915                 sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
916                 draw_text(x, y, string);
917
918                 int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
919                 int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
920                 for(int j = 0; j < 10; j++)
921                 {
922                         y = y1 + (y2 - y1) * j / 10;
923                         if(j == 0)
924                         {
925                                 draw_line(canvas->get_x() - 10, y, canvas->get_x(), y);
926                         }
927                         else
928                         if(i < DIVISIONS)
929                         {
930                                 draw_line(canvas->get_x() - 5, y, canvas->get_x(), y);
931                         }
932                 }
933         }
934
935
936         for(int i = 0; i <= DIVISIONS; i++)
937         {
938                 int y = canvas->get_h() + 30;
939                 int x = canvas->get_x() + (canvas->get_w() - 10) / DIVISIONS * i;
940                 char string[BCTEXTLEN];
941
942                 sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
943                 draw_text(x, y, string);
944
945                 int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
946                 int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
947                 for(int j = 0; j < 10; j++)
948                 {
949                         x = x1 + (x2 - x1) * j / 10;
950                         if(j == 0)
951                         {
952                                 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 10);
953                         }
954                         else
955                         if(i < DIVISIONS)
956                         {
957                                 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 5);
958                         }
959                 }
960         }
961
962
963
964         flash();
965 }
966
967 void CompressorWindow::update()
968 {
969         update_textboxes();
970         update_canvas();
971 }
972
973 void CompressorWindow::update_textboxes()
974 {
975         if(atol(trigger->get_text()) != plugin->config.trigger)
976                 trigger->update((int64_t)plugin->config.trigger);
977         if(strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)))
978                 input->set_text(CompressorInput::value_to_text(plugin->config.input));
979
980         if(plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled())
981                 trigger->disable();
982         else
983         if(plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled())
984                 trigger->enable();
985
986         if(!EQUIV(atof(reaction->get_text()), plugin->config.reaction_len))
987                 reaction->update((float)plugin->config.reaction_len);
988         if(!EQUIV(atof(decay->get_text()), plugin->config.decay_len))
989                 decay->update((float)plugin->config.decay_len);
990         smooth->update(plugin->config.smoothing_only);
991         if(canvas->current_operation == CompressorCanvas::DRAG)
992         {
993                 x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
994                 y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
995         }
996 }
997
998 #define POINT_W 10
999 void CompressorWindow::update_canvas()
1000 {
1001         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
1002         canvas->set_line_dashes(1);
1003         canvas->set_color(GREEN);
1004
1005         for(int i = 1; i < DIVISIONS; i++)
1006         {
1007                 int y = canvas->get_h() * i / DIVISIONS;
1008                 canvas->draw_line(0, y, canvas->get_w(), y);
1009
1010                 int x = canvas->get_w() * i / DIVISIONS;
1011                 canvas->draw_line(x, 0, x, canvas->get_h());
1012         }
1013         canvas->set_line_dashes(0);
1014
1015
1016         canvas->set_font(MEDIUMFONT);
1017         canvas->draw_text(plugin->get_theme()->widget_border,
1018                 canvas->get_h() / 2,
1019                 _("Output"));
1020         canvas->draw_text(canvas->get_w() / 2 - canvas->get_text_width(MEDIUMFONT, _("Input")) / 2,
1021                 canvas->get_h() - plugin->get_theme()->widget_border,
1022                 _("Input"));
1023
1024
1025         canvas->set_color(WHITE);
1026         canvas->set_line_width(2);
1027
1028         double x_db = plugin->config.min_db;
1029         double y_db = plugin->config.calculate_db(x_db);
1030         int y1 = (int)(y_db / plugin->config.min_db * canvas->get_h());
1031
1032         for(int i=1; i<canvas->get_w(); i++)
1033         {
1034                 x_db = (1. - (double)i / canvas->get_w()) * plugin->config.min_db;
1035                 y_db = plugin->config.calculate_db(x_db);
1036                 int y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
1037                 canvas->draw_line(i-1, y1, i, y2);
1038                 y1 = y2;
1039         }
1040         canvas->set_line_width(1);
1041
1042         int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
1043         for(int i=0; i < total; i++)
1044         {
1045                 x_db = plugin->config.get_x(i);
1046                 y_db = plugin->config.get_y(i);
1047
1048                 int x = (int)((1. - x_db / plugin->config.min_db) * canvas->get_w());
1049                 int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
1050
1051                 canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
1052         }
1053
1054         canvas->flash();
1055 }
1056
1057 int CompressorWindow::resize_event(int w, int h)
1058 {
1059         return 1;
1060 }
1061
1062
1063
1064
1065
1066
1067
1068
1069 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h)
1070  : BC_SubWindow(x, y, w, h, BLACK)
1071 {
1072         this->plugin = plugin;
1073         current_operation = NONE;
1074         current_point = 0;
1075 }
1076
1077 int CompressorCanvas::button_press_event()
1078 {
1079 // Check existing points
1080         if(is_event_win() && cursor_inside())
1081         {
1082                 for(int i = 0; i < plugin->config.levels.total; i++)
1083                 {
1084                         double x_db = plugin->config.get_x(i);
1085                         double y_db = plugin->config.get_y(i);
1086
1087                         int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
1088                         int y = (int)(y_db / plugin->config.min_db * get_h());
1089
1090                         if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
1091                                 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
1092                         {
1093                                 current_operation = DRAG;
1094                                 current_point = i;
1095                                 return 1;
1096                         }
1097                 }
1098
1099
1100
1101 // Create new point
1102                 double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
1103                 double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
1104
1105                 current_point = plugin->config.set_point(x_db, y_db);
1106                 current_operation = DRAG;
1107                 ((CompressorWindow*)plugin->thread->window)->update();
1108                 plugin->send_configure_change();
1109                 return 1;
1110         }
1111         return 0;
1112 //plugin->config.dump();
1113 }
1114
1115 int CompressorCanvas::button_release_event()
1116 {
1117         if(current_operation == DRAG)
1118         {
1119                 if(current_point > 0)
1120                 {
1121                         if(plugin->config.levels.values[current_point].x <
1122                                 plugin->config.levels.values[current_point - 1].x)
1123                                 plugin->config.remove_point(current_point);
1124                 }
1125
1126                 if(current_point < plugin->config.levels.total - 1)
1127                 {
1128                         if(plugin->config.levels.values[current_point].x >=
1129                                 plugin->config.levels.values[current_point + 1].x)
1130                                 plugin->config.remove_point(current_point);
1131                 }
1132
1133                 ((CompressorWindow*)plugin->thread->window)->update();
1134                 plugin->send_configure_change();
1135                 current_operation = NONE;
1136                 return 1;
1137         }
1138
1139         return 0;
1140 }
1141
1142 int CompressorCanvas::cursor_motion_event()
1143 {
1144         if(current_operation == DRAG)
1145         {
1146                 int x = get_cursor_x();
1147                 int y = get_cursor_y();
1148                 CLAMP(x, 0, get_w());
1149                 CLAMP(y, 0, get_h());
1150                 double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
1151                 double y_db = (double)y / get_h() * plugin->config.min_db;
1152                 plugin->config.levels.values[current_point].x = x_db;
1153                 plugin->config.levels.values[current_point].y = y_db;
1154                 ((CompressorWindow*)plugin->thread->window)->update();
1155                 plugin->send_configure_change();
1156                 return 1;
1157 //plugin->config.dump();
1158         }
1159         else
1160 // Change cursor over points
1161         if(is_event_win() && cursor_inside())
1162         {
1163                 int new_cursor = CROSS_CURSOR;
1164
1165                 for(int i = 0; i < plugin->config.levels.total; i++)
1166                 {
1167                         double x_db = plugin->config.get_x(i);
1168                         double y_db = plugin->config.get_y(i);
1169
1170                         int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
1171                         int y = (int)(y_db / plugin->config.min_db * get_h());
1172
1173                         if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
1174                                 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
1175                         {
1176                                 new_cursor = UPRIGHT_ARROW_CURSOR;
1177                                 break;
1178                         }
1179                 }
1180
1181
1182                 if(new_cursor != get_cursor())
1183                 {
1184                         set_cursor(new_cursor, 0, 1);
1185                 }
1186         }
1187         return 0;
1188 }
1189
1190
1191
1192
1193
1194 CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y)
1195  : BC_TextBox(x, y, 100, 1, (float)plugin->config.reaction_len)
1196 {
1197         this->plugin = plugin;
1198 }
1199
1200 int CompressorReaction::handle_event()
1201 {
1202         plugin->config.reaction_len = atof(get_text());
1203         plugin->send_configure_change();
1204         return 1;
1205 }
1206
1207 int CompressorReaction::button_press_event()
1208 {
1209         if(is_event_win())
1210         {
1211                 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1212                 if(get_buttonpress() == 4)
1213                 {
1214                         plugin->config.reaction_len += 0.1;
1215                 }
1216                 else
1217                 if(get_buttonpress() == 5)
1218                 {
1219                         plugin->config.reaction_len -= 0.1;
1220                 }
1221                 update((float)plugin->config.reaction_len);
1222                 plugin->send_configure_change();
1223                 return 1;
1224         }
1225         return 0;
1226 }
1227
1228 CompressorDecay::CompressorDecay(CompressorEffect *plugin, int x, int y)
1229  : BC_TextBox(x, y, 100, 1, (float)plugin->config.decay_len)
1230 {
1231         this->plugin = plugin;
1232 }
1233 int CompressorDecay::handle_event()
1234 {
1235         plugin->config.decay_len = atof(get_text());
1236         plugin->send_configure_change();
1237         return 1;
1238 }
1239
1240 int CompressorDecay::button_press_event()
1241 {
1242         if(is_event_win())
1243         {
1244                 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1245                 if(get_buttonpress() == 4)
1246                 {
1247                         plugin->config.decay_len += 0.1;
1248                 }
1249                 else
1250                 if(get_buttonpress() == 5)
1251                 {
1252                         plugin->config.decay_len -= 0.1;
1253                 }
1254                 update((float)plugin->config.decay_len);
1255                 plugin->send_configure_change();
1256                 return 1;
1257         }
1258         return 0;
1259 }
1260
1261
1262
1263 CompressorX::CompressorX(CompressorEffect *plugin, int x, int y)
1264  : BC_TextBox(x, y, 100, 1, "")
1265 {
1266         this->plugin = plugin;
1267 }
1268 int CompressorX::handle_event()
1269 {
1270         int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
1271         if(current_point < plugin->config.levels.total)
1272         {
1273                 plugin->config.levels.values[current_point].x = atof(get_text());
1274                 ((CompressorWindow*)plugin->thread->window)->update_canvas();
1275                 plugin->send_configure_change();
1276         }
1277         return 1;
1278 }
1279
1280
1281
1282 CompressorY::CompressorY(CompressorEffect *plugin, int x, int y)
1283  : BC_TextBox(x, y, 100, 1, "")
1284 {
1285         this->plugin = plugin;
1286 }
1287 int CompressorY::handle_event()
1288 {
1289         int current_point = ((CompressorWindow*)plugin->thread->window)->canvas->current_point;
1290         if(current_point < plugin->config.levels.total)
1291         {
1292                 plugin->config.levels.values[current_point].y = atof(get_text());
1293                 ((CompressorWindow*)plugin->thread->window)->update_canvas();
1294                 plugin->send_configure_change();
1295         }
1296         return 1;
1297 }
1298
1299
1300
1301
1302
1303 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y)
1304  : BC_TextBox(x, y, (int64_t)100, (int64_t)1, (int64_t)plugin->config.trigger)
1305 {
1306         this->plugin = plugin;
1307 }
1308 int CompressorTrigger::handle_event()
1309 {
1310         plugin->config.trigger = atol(get_text());
1311         plugin->send_configure_change();
1312         return 1;
1313 }
1314
1315 int CompressorTrigger::button_press_event()
1316 {
1317         if(is_event_win())
1318         {
1319                 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
1320                 if(get_buttonpress() == 4)
1321                 {
1322                         plugin->config.trigger++;
1323                 }
1324                 else
1325                 if(get_buttonpress() == 5)
1326                 {
1327                         plugin->config.trigger--;
1328                 }
1329                 update((int64_t)plugin->config.trigger);
1330                 plugin->send_configure_change();
1331                 return 1;
1332         }
1333         return 0;
1334 }
1335
1336
1337
1338
1339
1340 CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y)
1341  : BC_PopupMenu(x,
1342         y,
1343         100,
1344         CompressorInput::value_to_text(plugin->config.input),
1345         1)
1346 {
1347         this->plugin = plugin;
1348 }
1349 int CompressorInput::handle_event()
1350 {
1351         plugin->config.input = text_to_value(get_text());
1352         ((CompressorWindow*)plugin->thread->window)->update();
1353         plugin->send_configure_change();
1354         return 1;
1355 }
1356
1357 void CompressorInput::create_objects()
1358 {
1359         for(int i = 0; i < 3; i++)
1360         {
1361                 add_item(new BC_MenuItem(value_to_text(i)));
1362         }
1363 }
1364
1365 const char* CompressorInput::value_to_text(int value)
1366 {
1367         switch(value)
1368         {
1369                 case CompressorConfig::TRIGGER: return _("Trigger");
1370                 case CompressorConfig::MAX: return _("Maximum");
1371                 case CompressorConfig::SUM: return _("Total");
1372         }
1373
1374         return _("Trigger");
1375 }
1376
1377 int CompressorInput::text_to_value(char *text)
1378 {
1379         for(int i = 0; i < 3; i++)
1380         {
1381                 if(!strcmp(value_to_text(i), text)) return i;
1382         }
1383
1384         return CompressorConfig::TRIGGER;
1385 }
1386
1387
1388
1389
1390
1391
1392 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
1393  : BC_GenericButton(x, y, _("Clear"))
1394 {
1395         this->plugin = plugin;
1396 }
1397
1398 int CompressorClear::handle_event()
1399 {
1400         plugin->config.levels.remove_all();
1401 //plugin->config.dump();
1402         ((CompressorWindow*)plugin->thread->window)->update();
1403         plugin->send_configure_change();
1404         return 1;
1405 }
1406
1407
1408
1409 CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y)
1410  : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
1411 {
1412         this->plugin = plugin;
1413 }
1414
1415 int CompressorSmooth::handle_event()
1416 {
1417         plugin->config.smoothing_only = get_value();
1418         plugin->send_configure_change();
1419         return 1;
1420 }
1421
1422
1423
1424