r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / compressor / compressor.C
blob3a8b33a443d0fe3b4d53ffca0621cfe29855b93c
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "compressor.h"
4 #include "cursors.h"
5 #include "defaults.h"
6 #include "filexml.h"
7 #include "picon_png.h"
8 #include "units.h"
9 #include "vframe.h"
11 #include <math.h>
12 #include <string.h>
14 #include <libintl.h>
15 #define _(String) gettext(String)
16 #define gettext_noop(String) String
17 #define N_(String) gettext_noop (String)
23 REGISTER_PLUGIN(CompressorEffect)
32 CompressorEffect::CompressorEffect(PluginServer *server)
33  : PluginAClient(server)
35         reset();
36         PLUGIN_CONSTRUCTOR_MACRO
39 CompressorEffect::~CompressorEffect()
41         PLUGIN_DESTRUCTOR_MACRO
42         delete_dsp();
45 void CompressorEffect::delete_dsp()
47         if(input_buffer)
48         {
49                 for(int i = 0; i < PluginClient::total_in_buffers; i++)
50                         delete [] input_buffer[i];
51                 delete [] input_buffer;
52         }
53         if(coefs) delete [] coefs;
55         if(reaction_buffer) delete [] reaction_buffer;
57         input_buffer = 0;
58         coefs = 0;
59         input_size = 0;
60         input_allocated = 0;
61         reaction_buffer = 0;
62         reaction_allocated = 0;
63         reaction_position = 0;
67 void CompressorEffect::reset()
69         input_buffer = 0;
70         coefs = 0;
71         input_size = 0;
72         input_allocated = 0;
73         coefs_allocated = 0;
74         reaction_buffer = 0;
75         reaction_allocated = 0;
76         reaction_position = 0;
77         current_coef = 1.0;
78         last_peak_age = 0;
79         last_peak = 0.0;
80         previous_intercept = 1.0;
81         previous_slope = 0.0;
82         previous_max = 0.0;
83         max_counter = 0;
86 char* CompressorEffect::plugin_title()
88         return _("Compressor");
92 int CompressorEffect::is_realtime()
94         return 1;
97 int CompressorEffect::is_multichannel()
99         return 1;
107 void CompressorEffect::read_data(KeyFrame *keyframe)
109         FileXML input;
110         input.set_shared_string(keyframe->data, strlen(keyframe->data));
112         int result = 0;
113         config.levels.remove_all();
114         while(!result)
115         {
116                 result = input.read_tag();
118                 if(!result)
119                 {
120                         if(input.tag.title_is("COMPRESSOR"))
121                         {
122                                 config.preview_len = input.tag.get_property("PREVIEW_LEN", config.preview_len);
123                                 config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len);
124                                 config.trigger = input.tag.get_property("TRIGGER", config.trigger);
125                         }
126                         else
127                         if(input.tag.title_is("LEVEL"))
128                         {
129                                 double x = input.tag.get_property("X", (double)0);
130                                 double y = input.tag.get_property("Y", (double)0);
131                                 compressor_point_t point = { x, y };
133                                 config.levels.append(point);
134                         }
135                 }
136         }
139 void CompressorEffect::save_data(KeyFrame *keyframe)
141         FileXML output;
142         output.set_shared_string(keyframe->data, MESSAGESIZE);
144         output.tag.set_title("COMPRESSOR");
145         output.tag.set_property("PREVIEW_LEN", config.preview_len);
146         output.tag.set_property("TRIGGER", config.trigger);
147         output.tag.set_property("REACTION_LEN", config.reaction_len);
148         output.append_tag();
149         output.append_newline();
152         for(int i = 0; i < config.levels.total; i++)
153         {
154                 output.tag.set_title("LEVEL");
155                 output.tag.set_property("X", config.levels.values[i].x);
156                 output.tag.set_property("Y", config.levels.values[i].y);
158                 output.append_tag();
159                 output.append_newline();
160         }
162         output.terminate_string();
165 int CompressorEffect::load_defaults()
167         char directory[BCTEXTLEN], string[BCTEXTLEN];
168         sprintf(directory, "%scompression.rc", BCASTDIR);
169         defaults = new Defaults(directory);
170         defaults->load();
172         config.preview_len = defaults->get("PREVIEW_LEN", config.preview_len);
173         config.trigger = defaults->get("TRIGGER", config.trigger);
174         config.reaction_len = defaults->get("REACTION_LEN", config.reaction_len);
176         config.levels.remove_all();
177         int total_levels = defaults->get("TOTAL_LEVELS", 0);
178         for(int i = 0; i < total_levels; i++)
179         {
180                 config.levels.append();
181                 sprintf(string, "X_%d", i);
182                 config.levels.values[i].x = defaults->get(string, (double)0);
183                 sprintf(string, "Y_%d", i);
184                 config.levels.values[i].y = defaults->get(string, (double)0);
185         }
186 //config.dump();
187         return 0;
190 int CompressorEffect::save_defaults()
192         char string[BCTEXTLEN];
194         defaults->update("PREVIEW_LEN", config.preview_len);
195         defaults->update("TRIGGER", config.trigger);
196         defaults->update("REACTION_LEN", config.reaction_len);
197         defaults->update("TOTAL_LEVELS", config.levels.total);
199         defaults->update("TOTAL_LEVELS", config.levels.total);
200         for(int i = 0; i < config.levels.total; i++)
201         {
202                 sprintf(string, "X_%d", i);
203                 defaults->update(string, config.levels.values[i].x);
204                 sprintf(string, "Y_%d", i);
205                 defaults->update(string, config.levels.values[i].y);
206         }
208         defaults->save();
210         return 0;
214 void CompressorEffect::update_gui()
216         if(thread)
217         {
218                 load_configuration();
219                 thread->window->lock_window();
220                 thread->window->update();
221                 thread->window->unlock_window();
222         }
226 NEW_PICON_MACRO(CompressorEffect)
227 SHOW_GUI_MACRO(CompressorEffect, CompressorThread)
228 RAISE_WINDOW_MACRO(CompressorEffect)
229 SET_STRING_MACRO(CompressorEffect)
230 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
234 int CompressorEffect::process_realtime(int64_t size, double **input_ptr, double **output_ptr)
236 //printf("CompressorEffect::process_realtime 1 %f\n", DB::fromdb(-100));
237         load_configuration();
239         if(coefs_allocated < size)
240         {
241                 if(coefs) delete [] coefs;
242                 coefs = new double[size];
243                 coefs_allocated = size;
244         }
246 //printf("CompressorEffect::process_realtime 2\n");
247         int preview_samples = (int)(config.preview_len * PluginAClient::project_sample_rate + 0.5);
248         int reaction_samples = (int)(config.reaction_len * PluginAClient::project_sample_rate + 0.5);
249         int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1);
250 //      trigger = PluginAClient::total_in_buffers - trigger - 1;
251         CLAMP(reaction_samples, 1, 128000000);
252         CLAMP(preview_samples, 1, 128000000);
254 //printf("CompressorEffect::process_realtime 3\n");
257 // Append input buffers
258         if(input_size + size > input_allocated)
259         {
260                 double **new_input = new double*[PluginClient::total_in_buffers];
261                 for(int i = 0; i < PluginClient::total_in_buffers; i++)
262                         new_input[i] = new double[input_size + size];
264                 if(input_buffer)
265                 {
266                         for(int i = 0; i < PluginClient::total_in_buffers; i++)
267                         {
268                                 memcpy(new_input[i], input_buffer[i], sizeof(double) * input_size);
269                                 delete [] input_buffer[i];
270                         }
272                         delete [] input_buffer;
273                 }
275                 input_buffer = new_input;
276                 input_allocated = input_size + size;
277         }
279 //printf("CompressorEffect::process_realtime 4\n");
280         for(int i = 0; i < PluginClient::total_in_buffers; i++)
281                 memcpy(input_buffer[i] + input_size, 
282                         input_ptr[i], 
283                         size * sizeof(double));
284         input_size += size;
288 //printf("CompressorEffect::process_realtime 5 %d\n", size);
293 // Have enough to send to output
294         if(input_size >= preview_samples)
295         {
296                 int output_offset = 0;
297                 if(input_size - preview_samples < size)
298                 {
299                         output_offset = size - (input_size - preview_samples);
300                         size = input_size - preview_samples;
301                         for(int i = 0; i < PluginAClient::total_in_buffers; i++)
302                                 bzero(output_ptr[i], output_offset * sizeof(double));
303                 }
304                         
305 //printf("CompressorEffect::process_realtime 6 %d\n", reaction_samples);
306                 if(reaction_allocated != reaction_samples)
307                 {
308                         double *new_buffer = new double[reaction_samples];
309                         if(reaction_buffer)
310                         {
311                                 memcpy(new_buffer, 
312                                         reaction_buffer, 
313                                         sizeof(double) * MIN(reaction_allocated, reaction_samples));
314                                 delete [] reaction_buffer;
315                         }
316                         if(reaction_samples - reaction_allocated > 0)
317                                 bzero(new_buffer + reaction_allocated, 
318                                         sizeof(double) * (reaction_samples - reaction_allocated));
319                         reaction_buffer = new_buffer;
320                         reaction_allocated = reaction_samples;
321                         if(reaction_position >= reaction_allocated) reaction_position = 0;
322                 }
325 //printf("CompressorEffect::process_realtime 7\n");
326 // Calculate coef buffer for current size
327                 double max = 0, min = 0;
328                 int64_t max_age = 0;
329                 int64_t min_age = 0;
330                 for(int i = 0; i < size; i++)
331                 {
332 // Put new sample in reaction buffer
333                         reaction_buffer[reaction_position] = 
334                                 input_buffer[trigger][i + preview_samples];
339 // Get peak in last reaction buffer size of samples
340                         max = 0;
341                         min = 0;
342 // Try to optimize
343                         if(last_peak_age < reaction_samples)
344                         {
345                                 if(fabs(input_buffer[trigger][i + preview_samples]) > last_peak)
346                                 {
347                                         last_peak = fabs(input_buffer[trigger][i + preview_samples]);
348                                         last_peak_age = 0;
349                                 }
350                                 max = last_peak;
351                         }
352                         else
353                         {
354 // Rescan history
355                                 for(int j = 0; j < reaction_samples; j++)
356                                 {
357                                         if(reaction_buffer[j] > max)
358                                         {
359                                                 max = reaction_buffer[j];
360                                                 max_age = (j <= reaction_position) ? 
361                                                         (reaction_position - j) :
362                                                         (reaction_samples - (j - reaction_position));
363                                         }
364                                         else
365                                         if(reaction_buffer[j] < min)
366                                         {
367                                                 min = reaction_buffer[j];
368                                                 min_age = (j <= reaction_position) ? 
369                                                         (reaction_position - j) :
370                                                         (reaction_samples - (j - reaction_position));
371                                         }
372                                 }
374                                 if(-min > max)
375                                 {
376                                         max = -min;
377                                         max_age = min_age;
378                                 }
380                                 last_peak = max;
381                                 last_peak_age = max_age;
382                         }
384 // Here's the brain of the effect.
386 // Test expiration of previous max.  If previous max is bigger than
387 // current max and still valid, it replaces the current max.
388 // Otherwise, the current max becomes the previous max and the counter
389 // is reset.
391                         if(max_counter > 0 && previous_max >= max)
392                         {
393                                 ;
394                         }
395                         else
396                         if(max > 0.00001)
397                         {
398 // Get new slope based on current coef, peak, and reaction len.
399 // The slope has a counter which needs to expire before it can be
400 // replaced with a less steep value.
401                                 double x_db = DB::todb(max);
402                                 double y_db = config.calculate_db(x_db);
403                                 double y_linear = DB::fromdb(y_db);
404                                 double new_coef = y_linear / max;
405                                 double slope = (new_coef - current_coef) / reaction_samples;
406                                 previous_slope = slope;
407                                 previous_max = max;
408                                 previous_intercept = current_coef;
409                                 max_counter = reaction_samples;
410                         }
411                         else
412                         {
413                                 previous_slope = 0.0;
414                                 previous_intercept = current_coef;
415                                 max_counter = 0;
416                         }
418 //printf("%f %f %f %d\n", current_coef, previous_slope, previous_intercept, max_counter);
419                         max_counter--;
420                         current_coef = previous_intercept + 
421                                 previous_slope * (reaction_samples - max_counter);
422                         coefs[i] = current_coef;
424                         last_peak_age++;
425                         reaction_position++;
426                         if(reaction_position >= reaction_allocated)
427                                 reaction_position = 0;
428                 }
430 //printf("CompressorEffect::process_realtime 8 %f %f\n", input_buffer[0][0], coefs[0]);
431 // Multiply coef buffer by input buffer
432                 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
433                 {
434                         for(int j = 0; j < size; j++)
435                         {
436                                 output_ptr[i][j + output_offset] = input_buffer[i][j] * coefs[j];
437                         }
438                 }
440 //printf("CompressorEffect::process_realtime 9 %d\n", PluginAClient::total_in_buffers);
441 // Shift input forward
442                 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
443                 {
444                         for(int j = 0, k = size; k < input_size; j++, k++)
445                         {
446                                 input_buffer[i][j] = input_buffer[i][k];
447                         }
448                 }
449 //printf("CompressorEffect::process_realtime 10\n");
450                 input_size -= size;
451         }
452         else
453         {
454                 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
455                         bzero(output_ptr[i], sizeof(double) * size);
456         }
458 //printf("CompressorEffect::process_realtime 11\n");
460         return 0;
475 CompressorConfig::CompressorConfig()
477         reaction_len = 1.0;
478         preview_len = 1.0;
479         min_db = -80.0;
480         min_x = min_db;
481         min_y = min_db;
482         max_x = 0;
483         max_y = 0;
484         trigger = 0;
487 void CompressorConfig::copy_from(CompressorConfig &that)
489         this->reaction_len = that.reaction_len;
490         this->preview_len = that.preview_len;
491         this->min_db = that.min_db;
492         this->min_x = that.min_x;
493         this->min_y = that.min_y;
494         this->max_x = that.max_x;
495         this->max_y = that.max_y;
496         this->trigger = that.trigger;
497         levels.remove_all();
498         for(int i = 0; i < that.levels.total; i++)
499                 this->levels.append(that.levels.values[i]);
502 int CompressorConfig::equivalent(CompressorConfig &that)
504         return 0;
507 void CompressorConfig::interpolate(CompressorConfig &prev, 
508         CompressorConfig &next, 
509         int64_t prev_frame, 
510         int64_t next_frame, 
511         int64_t current_frame)
513         copy_from(prev);
516 int CompressorConfig::total_points()
518         if(!levels.total) 
519                 return 1;
520         else
521                 return levels.total;
524 void CompressorConfig::dump()
526         printf("CompressorConfig::dump\n");
527         for(int i = 0; i < levels.total; i++)
528         {
529                 printf("        %f %f\n", levels.values[i].x, levels.values[i].y);
530         }
534 double CompressorConfig::get_y(int number)
536         if(!levels.total) 
537                 return 1.0;
538         else
539         if(number >= levels.total)
540                 return levels.values[levels.total - 1].y;
541         else
542                 return levels.values[number].y;
545 double CompressorConfig::get_x(int number)
547         if(!levels.total)
548                 return 0.0;
549         else
550         if(number >= levels.total)
551                 return levels.values[levels.total - 1].x;
552         else
553                 return levels.values[number].x;
556 // Returns linear output given linear input
557 double CompressorConfig::calculate_db(double x)
559         if(x > -0.001) return 0.0;
561         for(int i = levels.total - 1; i >= 0; i--)
562         {
563                 if(levels.values[i].x <= x)
564                 {
565                         if(i < levels.total - 1)
566                         {
567                                 return levels.values[i].y + 
568                                         (x - levels.values[i].x) *
569                                         (levels.values[i + 1].y - levels.values[i].y) / 
570                                         (levels.values[i + 1].x - levels.values[i].x);
571                         }
572                         else
573                         {
574                                 return levels.values[i].y +
575                                         (x - levels.values[i].x) * 
576                                         (max_y - levels.values[i].y) / 
577                                         (max_x - levels.values[i].x);
578                         }
579                 }
580         }
582         if(levels.total)
583         {
584                 return min_y + 
585                         (x - min_x) * 
586                         (levels.values[0].y - min_y) / 
587                         (levels.values[0].x - min_x);
588         }
589         else
590                 return x;
594 int CompressorConfig::set_point(double x, double y)
596         for(int i = levels.total - 1; i >= 0; i--)
597         {
598                 if(levels.values[i].x < x)
599                 {
600                         levels.append();
601                         i++;
602                         for(int j = levels.total - 2; j >= i; j--)
603                         {
604                                 levels.values[j + 1] = levels.values[j];
605                         }
606                         levels.values[i].x = x;
607                         levels.values[i].y = y;
609                         return i;
610                 }
611         }
613         levels.append();
614         for(int j = levels.total - 2; j >= 0; j--)
615         {
616                 levels.values[j + 1] = levels.values[j];
617         }
618         levels.values[0].x = x;
619         levels.values[0].y = y;
620         return 0;
623 void CompressorConfig::remove_point(int number)
625         for(int j = number; j < levels.total - 1; j++)
626         {
627                 levels.values[j] = levels.values[j + 1];
628         }
629         levels.remove();
632 void CompressorConfig::optimize()
634         int done = 0;
635         
636         while(!done)
637         {
638                 done = 1;
639                 
640                 
641                 for(int i = 0; i < levels.total - 1; i++)
642                 {
643                         if(levels.values[i].x >= levels.values[i + 1].x)
644                         {
645                                 done = 0;
646                                 for(int j = i + 1; j < levels.total - 1; j++)
647                                 {
648                                         levels.values[j] = levels.values[j + 1];
649                                 }
650                                 levels.remove();
651                         }
652                 }
653                 
654         }
670 PLUGIN_THREAD_OBJECT(CompressorEffect, CompressorThread, CompressorWindow)
684 CompressorWindow::CompressorWindow(CompressorEffect *plugin, int x, int y)
685  : BC_Window(plugin->gui_string, 
686         x, 
687         y, 
688         640, 
689         480, 
690         640, 
691         480,
692         0, 
693         0,
694         1)
696         this->plugin = plugin;
699 void CompressorWindow::create_objects()
701         int x = 35, y = 10;
703         add_subwindow(canvas = new CompressorCanvas(plugin, 
704                 x, 
705                 y, 
706                 get_w() - x - 120, 
707                 get_h() - y - 70));
708         canvas->set_cursor(CROSS_CURSOR);
709         x = get_w() - 110;
710         add_subwindow(new BC_Title(x, y, _("Preview secs:")));
711         y += 20;
712         add_subwindow(preview = new CompressorPreview(plugin, x, y));
713         y += 30;
714         add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
715         y += 20;
716         add_subwindow(reaction = new CompressorReaction(plugin, x, y));
717         y += 30;
718         add_subwindow(new BC_Title(x, y, _("Trigger:")));
719         y += 20;
720         add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
721         y += 30;
722         add_subwindow(clear = new CompressorClear(plugin, x, y));
723         x = 10;
724         y = get_h() - 40;
725         add_subwindow(new BC_Title(x, y, _("Point:")));
726         x += 50;
727         add_subwindow(x_text = new CompressorX(plugin, x, y));
728         x += 110;
729         add_subwindow(new BC_Title(x, y, _("x")));
730         x += 20;
731         add_subwindow(y_text = new CompressorY(plugin, x, y));
732         draw_scales();
734         update_canvas();
735         show_window();
736         flush();
739 WINDOW_CLOSE_EVENT(CompressorWindow)
741 void CompressorWindow::draw_scales()
743         set_font(SMALLFONT);
744         set_color(BLACK);
746 #define DIVISIONS 8
747         for(int i = 0; i <= DIVISIONS; i++)
748         {
749                 int y = canvas->get_y() + 10 + canvas->get_h() / DIVISIONS * i;
750                 int x = canvas->get_x() - 30;
751                 char string[BCTEXTLEN];
752                 
753                 sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
754                 draw_text(x, y, string);
755                 
756                 int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
757                 int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
758                 for(int j = 0; j < 10; j++)
759                 {
760                         y = y1 + (y2 - y1) * j / 10;
761                         if(j == 0)
762                         {
763                                 draw_line(canvas->get_x() - 10, y, canvas->get_x(), y);
764                         }
765                         else
766                         if(i < DIVISIONS)
767                         {
768                                 draw_line(canvas->get_x() - 5, y, canvas->get_x(), y);
769                         }
770                 }
771         }
775         for(int i = 0; i <= DIVISIONS; i++)
776         {
777                 int y = canvas->get_h() + 30;
778                 int x = canvas->get_x() + (canvas->get_w() - 10) / DIVISIONS * i;
779                 char string[BCTEXTLEN];
781                 sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
782                 draw_text(x, y, string);
784                 int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
785                 int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
786                 for(int j = 0; j < 10; j++)
787                 {
788                         x = x1 + (x2 - x1) * j / 10;
789                         if(j == 0)
790                         {
791                                 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 10);
792                         }
793                         else
794                         if(i < DIVISIONS)
795                         {
796                                 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 5);
797                         }
798                 }
799         }
803         flash();
806 void CompressorWindow::update()
808         update_textboxes();
809         update_canvas();
812 void CompressorWindow::update_textboxes()
814         preview->update((float)plugin->config.preview_len);
815         trigger->update((int64_t)plugin->config.trigger);
816         reaction->update((float)plugin->config.reaction_len);
817         if(canvas->current_operation == CompressorCanvas::DRAG)
818         {
819                 x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
820                 y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
821         }
824 #define POINT_W 6
825 void CompressorWindow::update_canvas()
827         int y1, y2;
830         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
831         canvas->set_color(GREEN);
832         for(int i = 1; i < DIVISIONS; i++)
833         {
834                 int y = canvas->get_h() * i / DIVISIONS;
835                 canvas->draw_line(0, y, canvas->get_w(), y);
836                 
837                 int x = canvas->get_w() * i / DIVISIONS;
838                 canvas->draw_line(x, 0, x, canvas->get_h());
839         }
840         
841         
842         
843         canvas->set_color(BLACK);
844         for(int i = 0; i < canvas->get_w(); i++)
845         {
846                 double x_db = ((double)1 - (double)i / canvas->get_w()) * plugin->config.min_db;
847                 double y_db = plugin->config.calculate_db(x_db);
848                 y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
850                 if(i > 0)
851                 {
852                         canvas->draw_line(i - 1, y1, i, y2);
853                 }
855                 y1 = y2;
856         }
858         int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
859         for(int i = 0; i < plugin->config.levels.total; i++)
860         {
861                 double x_db = plugin->config.get_x(i);
862                 double y_db = plugin->config.get_y(i);
864                 int x = (int)(((double)1 - x_db / plugin->config.min_db) * canvas->get_w());
865                 int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
866                 
867                 canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
868         }
869         
870         canvas->flash();
871         canvas->flush();
881 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h) 
882  : BC_SubWindow(x, y, w, h, WHITE)
884         this->plugin = plugin;
887 int CompressorCanvas::button_press_event()
889 // Check existing points
890         if(is_event_win() && cursor_inside())
891         {
892                 for(int i = 0; i < plugin->config.levels.total; i++)
893                 {
894                         double x_db = plugin->config.get_x(i);
895                         double y_db = plugin->config.get_y(i);
897                         int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
898                         int y = (int)(y_db / plugin->config.min_db * get_h());
900                         if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
901                                 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
902                         {
903                                 current_operation = DRAG;
904                                 current_point = i;
905                                 return 1;
906                         }
907                 }
911 // Create new point
912                 double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
913                 double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
915                 current_point = plugin->config.set_point(x_db, y_db);
916                 current_operation = DRAG;
917                 plugin->thread->window->update();
918                 plugin->send_configure_change();
919                 return 1;
920         }
921         return 0;
922 //plugin->config.dump();
925 int CompressorCanvas::button_release_event()
927         if(current_operation == DRAG)
928         {
929                 if(current_point > 0)
930                 {
931                         if(plugin->config.levels.values[current_point].x <
932                                 plugin->config.levels.values[current_point - 1].x)
933                                 plugin->config.remove_point(current_point);
934                 }
936                 if(current_point < plugin->config.levels.total - 1)
937                 {
938                         if(plugin->config.levels.values[current_point].x >=
939                                 plugin->config.levels.values[current_point + 1].x)
940                                 plugin->config.remove_point(current_point);
941                 }
943                 plugin->thread->window->update();
944                 plugin->send_configure_change();
945                 current_operation = NONE;
946                 return 1;
947         }
949         return 0;
952 int CompressorCanvas::cursor_motion_event()
954         if(current_operation == DRAG)
955         {
956                 int x = get_cursor_x();
957                 int y = get_cursor_y();
958                 CLAMP(x, 0, get_w());
959                 CLAMP(y, 0, get_h());
960                 double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
961                 double y_db = (double)y / get_h() * plugin->config.min_db;
962                 plugin->config.levels.values[current_point].x = x_db;
963                 plugin->config.levels.values[current_point].y = y_db;
964                 plugin->thread->window->update();
965                 plugin->send_configure_change();
966                 return 1;
967 //plugin->config.dump();
968         }
969         return 0;
974 CompressorPreview::CompressorPreview(CompressorEffect *plugin, int x, int y) 
975  : BC_TextBox(x, y, 100, 1, (float)plugin->config.preview_len)
977         this->plugin = plugin;
980 int CompressorPreview::handle_event()
982         plugin->config.preview_len = atof(get_text());
983         plugin->send_configure_change();
984         return 1;
989 CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y) 
990  : BC_TextBox(x, y, 100, 1, (float)plugin->config.reaction_len)
992         this->plugin = plugin;
995 int CompressorReaction::handle_event()
997 //printf("CompressorReaction::handle_event 1\n");
998         plugin->config.reaction_len = atof(get_text());
999         plugin->send_configure_change();
1000 //printf("CompressorReaction::handle_event 2\n");
1001         return 1;
1005 CompressorX::CompressorX(CompressorEffect *plugin, int x, int y) 
1006  : BC_TextBox(x, y, 100, 1, "")
1008         this->plugin = plugin;
1010 int CompressorX::handle_event()
1012         int current_point = plugin->thread->window->canvas->current_point;
1013         if(current_point < plugin->config.levels.total)
1014         {
1015                 plugin->config.levels.values[current_point].x = atof(get_text());
1016                 plugin->thread->window->update_canvas();
1017                 plugin->send_configure_change();
1018         }
1019         return 1;
1024 CompressorY::CompressorY(CompressorEffect *plugin, int x, int y) 
1025  : BC_TextBox(x, y, 100, 1, "")
1027         this->plugin = plugin;
1029 int CompressorY::handle_event()
1031         int current_point = plugin->thread->window->canvas->current_point;
1032         if(current_point < plugin->config.levels.total)
1033         {
1034                 plugin->config.levels.values[current_point].y = atof(get_text());
1035                 plugin->thread->window->update_canvas();
1036                 plugin->send_configure_change();
1037         }
1038         return 1;
1042 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y) 
1043  : BC_TextBox(x, y, (int64_t)100, (int64_t)1, (int64_t)plugin->config.trigger)
1045         this->plugin = plugin;
1047 int CompressorTrigger::handle_event()
1049         plugin->config.trigger = atol(get_text());
1050         plugin->send_configure_change();
1051         return 1;
1058 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y) 
1059  : BC_GenericButton(x, y, _("Clear"))
1061         this->plugin = plugin;
1064 int CompressorClear::handle_event()
1066         plugin->config.levels.remove_all();
1067 //plugin->config.dump();
1068         plugin->thread->window->update();
1069         plugin->send_configure_change();
1070         return 1;