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