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