improve delays created by vicon drawing locks, reset_cache segv fix, gang track toolt...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / compressortools.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008-2019 Adam Williams <broadcast at earthling dot net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * 
19  */
20
21
22 // Objects for compressors
23 #include "clip.h"
24 #include "compressortools.h"
25 #include "cursors.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "pluginclient.h"
29 #include "samples.h"
30 #include "theme.h"
31 #include <string.h>
32
33
34 BandConfig::BandConfig()
35 {
36         reset();
37 }
38
39 BandConfig::~BandConfig()
40 {
41         
42 }
43
44 void BandConfig::save_data(FileXML *xml, int number, int do_multiband)
45 {
46         xml->tag.set_title("COMPRESSORBAND");
47         if( do_multiband ) {
48                 xml->tag.set_property("NUMBER", number);
49                 xml->tag.set_property("FREQ", freq);
50                 xml->tag.set_property("BYPASS", bypass);
51                 xml->tag.set_property("SOLO", solo);
52                 xml->tag.set_property("ATTACK_LEN", attack_len);
53                 xml->tag.set_property("RELEASE_LEN", release_len);
54                 xml->tag.set_property("MKUP_GAIN", mkup_gain);
55         }
56         xml->append_tag();
57         xml->append_newline();
58
59         for( int i = 0; i < levels.total; i++ ) {
60                 xml->tag.set_title("LEVEL");
61                 xml->tag.set_property("X", levels[i].x);
62                 xml->tag.set_property("Y", levels[i].y);
63                 xml->append_tag();
64                 xml->append_newline();
65         }
66
67         xml->tag.set_title("/COMPRESSORBAND");
68         xml->append_tag();
69         xml->append_newline();
70 }
71
72 void BandConfig::read_data(FileXML *xml, int do_multiband)
73 {
74         if( do_multiband ) {
75                 freq = xml->tag.get_property("FREQ", freq);
76                 bypass = xml->tag.get_property("BYPASS", bypass);
77                 solo = xml->tag.get_property("SOLO", solo);
78                 attack_len = xml->tag.get_property("ATTACK_LEN", attack_len);
79                 release_len = xml->tag.get_property("RELEASE_LEN", release_len);
80                 mkup_gain = xml->tag.get_property("MKUP_GAIN", mkup_gain);
81         }
82
83         levels.remove_all();
84         int result = 0;
85         while( !result ) {
86                 result = xml->read_tag();
87                 if( !result ) {
88                         if( xml->tag.title_is("LEVEL") ) {
89                                 double x = xml->tag.get_property("X", (double)0);
90                                 double y = xml->tag.get_property("Y", (double)0);
91                                 compressor_point_t point = { x, y };
92
93                                 levels.append(point);
94                         }
95                         else
96                         if( xml->tag.title_is("/COMPRESSORBAND") ) {
97                                 break;
98                         }
99                 }
100         }
101 }
102
103 void BandConfig::copy_from(BandConfig *src)
104 {
105         levels.remove_all();
106         for( int i = 0; i < src->levels.total; i++ ) {
107                 levels.append(src->levels[i]);
108         }
109
110 //      readahead_len = src->readahead_len;
111         attack_len = src->attack_len;
112         release_len = src->release_len;
113         freq = src->freq;
114         solo = src->solo;
115         bypass = src->bypass;
116         mkup_gain = src->mkup_gain;
117 }
118
119 int BandConfig::equiv(BandConfig *src)
120 {
121         if( levels.total != src->levels.total ||
122                 solo != src->solo ||
123                 bypass != src->bypass ||
124                 freq != src->freq ||
125                 mkup_gain != src->mkup_gain ||
126 //              !EQUIV(readahead_len, src->readahead_len) ||
127                 !EQUIV(attack_len, src->attack_len) ||
128                 !EQUIV(release_len, src->release_len) ) {
129                 return 0;
130         }
131
132         for( int i = 0; i < levels.total && i < src->levels.total; i++ ) {
133                 compressor_point_t *this_level = &levels[i];
134                 compressor_point_t *that_level = &src->levels[i];
135                 if( !EQUIV(this_level->x, that_level->x) ||
136                         !EQUIV(this_level->y, that_level->y) ) {
137                         return 0;
138                 }
139         }
140         
141         return 1;
142 }
143
144 void BandConfig::boundaries(CompressorConfigBase *base)
145 {
146         for( int i = 0; i < levels.size(); i++ ) {
147                 compressor_point_t *level = &levels[i];
148                 if( level->x < base->min_db ) level->x = base->min_db;
149                 if( level->y < base->min_db ) level->y = base->min_db;
150                 if( level->x > base->max_db ) level->x = base->max_db;
151                 if( level->y > base->max_db ) level->y = base->max_db;
152         }
153 }
154
155 void BandConfig::reset()
156 {
157         freq = 0;
158         solo = 0;
159         bypass = 0;
160 //      readahead_len = 1.0;
161         attack_len = 1.0;
162         release_len = 1.0;
163         mkup_gain = 0.0;
164         levels.remove_all();
165 }
166
167
168 CompressorConfigBase::CompressorConfigBase(int total_bands)
169 {
170         this->total_bands = total_bands;
171         bands = new BandConfig[total_bands];
172         min_db = -78.0;
173         max_db = 6.0;
174         min_value = DB::fromdb(min_db) + 0.001;
175 //      min_x = min_db;  max_x = 0;
176 //      min_y = min_db;  max_y = 0;
177         reset_base();
178 }
179
180
181 CompressorConfigBase::~CompressorConfigBase()
182 {
183         delete [] bands;
184 }
185
186 void CompressorConfigBase::reset_base()
187 {
188         input = CompressorConfigBase::TRIGGER;
189         trigger = 0;
190         smoothing_only = 0;
191         for( int i=0; i<total_bands; ++i )
192                 bands[i].freq = Freq::tofreq((i+1) * TOTALFREQS / total_bands);
193         current_band = 0;
194 }
195
196 void CompressorConfigBase::reset_bands()
197 {
198         for( int i=0; i<total_bands; ++i )
199                 bands[i].reset();
200 }
201
202
203 void CompressorConfigBase::boundaries()
204 {
205         for( int i=0; i<total_bands; ++i ) {
206                 bands[i].boundaries(this);
207         }
208 }
209
210
211 void CompressorConfigBase::copy_from(CompressorConfigBase &that)
212 {
213 //      min_x = that.min_x;  max_x = that.max_x;
214 //      min_y = that.min_y;  max_y = that.max_y;
215         trigger = that.trigger;
216         input = that.input;
217         smoothing_only = that.smoothing_only;
218
219         for( int i=0; i<total_bands; ++i ) {
220                 BandConfig *dst = &bands[i];
221                 BandConfig *src = &that.bands[i];
222                 dst->copy_from(src);
223         }
224 }
225
226
227 int CompressorConfigBase::equivalent(CompressorConfigBase &that)
228 {
229         for( int i=0; i<total_bands; ++i ) {
230                 if( !bands[i].equiv(&that.bands[i]) )
231                         return 0;
232         }
233
234         return trigger == that.trigger &&
235                 input == that.input &&
236                 smoothing_only == that.smoothing_only;
237 }
238
239 double CompressorConfigBase::get_y(int band, int i)
240 {
241         ArrayList<compressor_point_t> &levels = bands[band].levels;
242         int sz = levels.size();
243         if( !sz ) return 1.;
244         if( i >= sz ) i = sz-1;
245         return levels[i].y;
246 }
247
248 double CompressorConfigBase::get_x(int band, int i)
249 {
250         ArrayList<compressor_point_t> &levels = bands[band].levels;
251         int sz = levels.size();
252         if( !sz ) return 0.;
253         if( i >= sz ) i = sz-1;
254         return levels[i].x;
255 }
256
257 double CompressorConfigBase::calculate_db(int band, double x)
258 {
259         ArrayList<compressor_point_t> &levels = bands[band].levels;
260         int sz = levels.size();
261         if( !sz ) return x;
262         compressor_point_t &point0 = levels[0];
263         double px0 = point0.x, py0 = point0.y, dx0 = x - px0;
264 // before 1st point, use 1:1 gain
265         double ret = py0 + dx0;
266         if( sz > 1 ) {
267 // find point <= x
268                 int k = sz;
269                 while( --k >= 0 && levels[k].x > x );
270                 if( k >= 0 ) {
271                         compressor_point_t &curr = levels[k];
272                         double cx = curr.x, cy = curr.y, dx = x - cx;
273 // between 2 points.  Use slope between 2 points
274 // the last point.  Use slope of last 2 points
275                         if( k >= sz-1 ) --k;
276                         compressor_point_t &prev = levels[k+0];
277                         compressor_point_t &next = levels[k+1];
278                         double px = prev.x, py = prev.y;
279                         double nx = next.x, ny = next.y;
280                         ret = cy + dx * (ny - py) / (nx - px);
281                 }
282         }
283         else
284 // the only point.  Use slope from min_db
285                 ret = py0 + dx0 * (py0 - min_db) / (px0 - min_db);
286
287         ret += bands[band].mkup_gain;
288         return ret;
289 }
290
291
292 int CompressorConfigBase::set_point(int band, double x, double y)
293 {
294         ArrayList<compressor_point_t> &levels = bands[band].levels;
295         int k = levels.size(), ret = k;
296         while( --k >= 0 && levels[k].x >= x ) ret = k;
297         compressor_point_t new_point = { x, y };
298         levels.insert(new_point, ret);
299         return ret;
300 }
301
302 void CompressorConfigBase::remove_point(int band, int i)
303 {
304         ArrayList<compressor_point_t> &levels = bands[band].levels;
305         levels.remove_number(i);
306 }
307
308
309 double CompressorConfigBase::calculate_output(int band, double x)
310 {
311         double x_db = DB::todb(x);
312         return DB::fromdb(calculate_db(band, x_db));
313 }
314
315
316 double CompressorConfigBase::calculate_gain(int band, double input_linear)
317 {
318         double output_linear = calculate_output(band, input_linear);
319 // output is below minimum.  Mute it
320         return output_linear < min_value ? 0. :
321 // input is below minimum.  Don't change it.
322                 fabs(input_linear - 0.0) < min_value ? 1. :
323 // gain
324                 output_linear / input_linear;
325 }
326
327
328 CompressorCanvasBase::CompressorCanvasBase(CompressorConfigBase *config, 
329                 PluginClient *plugin, PluginClientWindow *window, 
330                 int x, int y, int w, int h)
331  : BC_SubWindow(x, y, w, h, BLACK)
332 {
333         this->config = config;
334         this->plugin = plugin;
335         this->window = window;
336         current_operation = NONE;
337
338         graph_x = 0;
339         graph_y = 0;
340         graph_w = w - graph_x;
341         graph_h = h - graph_y;
342         subdivisions = 6;
343         divisions = (int)(config->max_db - config->min_db) / subdivisions;
344 }
345
346 CompressorCanvasBase::~CompressorCanvasBase()
347 {
348 }
349
350
351 void CompressorCanvasBase::create_objects()
352 {
353         add_subwindow(menu = new CompressorPopup(this));
354         menu->create_objects();
355
356         set_cursor(CROSS_CURSOR, 0, 0);
357         draw_scales();
358         update();
359 }
360
361 void CompressorCanvasBase::draw_scales()
362 {
363         int yfudge = yS(10);
364         window->set_font(SMALLFONT);
365         window->set_color(get_resources()->default_text_color);
366
367 // output divisions
368         for( int i=0; i<=divisions; ++i ) {
369                 int y = get_y() + yfudge + graph_y + graph_h * i / divisions;
370                 int x = get_x();
371                 char string[BCTEXTLEN];
372                 sprintf(string, "%.0f", config->max_db - 
373                         (float)i / divisions * (config->max_db - config->min_db));
374                 int text_w = get_text_width(SMALLFONT, string);
375                 if( i >= divisions ) y -= yfudge;
376                 window->draw_text(x-xS(10) - text_w, y, string);
377                 if( i >= divisions ) break;
378                 
379                 int y1 = get_y() + graph_y + graph_h * i / divisions;
380                 int y2 = get_y() + graph_y + graph_h * (i + 1) / divisions;
381                 int x1 = get_x() - xS(10), x2 = get_x() - xS(5);
382                 for( int j=0; j<subdivisions; ++j,x1=x2 ) {
383                         y = y1 + (y2 - y1) * j / subdivisions;
384                         window->draw_line(x, y, x1, y);
385                 }
386         }
387
388 // input divisions
389         for( int i=0; i<=divisions; ++i ) {
390                 int y = get_y() + get_h();
391                 int x = get_x() + graph_x + graph_w * i / divisions;
392                 int y0 = y + window->get_text_ascent(SMALLFONT);
393                 char string[BCTEXTLEN];
394                 sprintf(string, "%.0f", (float)i / divisions * 
395                                 (config->max_db - config->min_db) + config->min_db);
396                 int text_w = get_text_width(SMALLFONT, string);
397                 window->draw_text(x - text_w, y0 + yS(10), string);
398                 if( i >= divisions ) break;
399
400                 int x1 = get_x() + graph_x + graph_w * i / divisions;
401                 int x2 = get_x() + graph_x + graph_w * (i + 1) / divisions;
402                 int y1 = y + yS(10), y2 = y + yS(5);
403                 for( int j=0; j<subdivisions; ++j,y1=y2 ) {
404                         x = x1 + (x2 - x1) * j / subdivisions;
405                         window->draw_line(x, y, x, y1);
406                 }
407         }
408
409
410 }
411
412 #define POINT_W xS(10)
413
414 // get Y from X
415 int CompressorCanvasBase::x_to_y(int band, int x)
416 {
417         double min_db = config->min_db, max_db = config->max_db;
418         double rng_db = max_db - min_db;
419         double x_db = min_db + (double)x / graph_w * rng_db;
420         double y_db = config->calculate_db(band, x_db);
421         int y = graph_y + graph_h - (int)((y_db - min_db) * graph_h / rng_db);
422         return y;
423 }
424
425 // get X from DB
426 int CompressorCanvasBase::db_to_x(double db)
427 {
428         double min_db = config->min_db, max_db = config->max_db;
429         double rng_db = max_db - min_db;
430         int x = graph_x + (double)(db - min_db) * graph_w / rng_db;
431         return x;
432 }
433
434 // get Y from DB
435 int CompressorCanvasBase::db_to_y(double db)
436 {
437         double min_db = config->min_db, max_db = config->max_db;
438         double rng_db = max_db - min_db;
439         int y = graph_y + graph_h - (int)((db - min_db) * graph_h / rng_db); 
440         return y;
441 }
442
443
444 double CompressorCanvasBase::x_to_db(int x)
445 {
446         CLAMP(x, 0, get_w());
447         double min_db = config->min_db, max_db = config->max_db;
448         double rng_db = max_db - min_db;
449         double x_db = (double)(x - graph_x) * rng_db / graph_w + min_db;
450         CLAMP(x_db, min_db, max_db);
451         return x_db;
452 }
453
454 double CompressorCanvasBase::y_to_db(int y)
455 {
456         CLAMP(y, 0, get_h());
457         double min_db = config->min_db, max_db = config->max_db;
458         double rng_db = max_db - min_db;
459         double y_db = (double)(graph_y - y) * rng_db / graph_h + max_db;
460         CLAMP(y_db, min_db, max_db);
461         return y_db;
462 }
463
464
465
466 void CompressorCanvasBase::update()
467 {
468 // headroom boxes
469         set_color(window->get_bg_color());
470         draw_box(graph_x, 0, get_w(), graph_y);
471         draw_box(graph_w, graph_y, get_w() - graph_w, get_h() - graph_y);
472 //       const int checker_w = DP(10);
473 //       const int checker_h = DP(10);
474 //       set_color(MDGREY);
475 //       for( int i = 0; i < get_h(); i += checker_h )
476 //       {
477 //               for( int j = (i % 2) * checker_w; j < get_w(); j += checker_w * 2 )
478 //               {
479 //                       if( !(i >= graph_y && 
480 //                               i + checker_h < graph_y + graph_h &&
481 //                               j >= graph_x &&
482 //                               j + checker_w < graph_x + graph_w) )
483 //                       {
484 //                               draw_box(j, i, checker_w, checker_h);
485 //                       }
486 //               }
487 //       }
488
489 // canvas boxes
490         set_color(plugin->get_theme()->graph_bg_color);
491         draw_box(graph_x, graph_y, graph_w, graph_h);
492
493 // graph border
494         draw_3d_border(0, 0, get_w(), get_h(), window->get_bg_color(),
495                 plugin->get_theme()->graph_border1_color,
496                 plugin->get_theme()->graph_border2_color, 
497                 window->get_bg_color());
498
499         set_line_dashes(1);
500         set_color(plugin->get_theme()->graph_grid_color);
501         
502         for( int i = 1; i < divisions; i++ ) {
503                 int y = graph_y + graph_h * i / divisions;
504                 draw_line(graph_x, y, graph_x + graph_w, y);
505 // 0db 
506                 if( i == 1 ) {
507                         draw_line(graph_x, y + 1, graph_x + graph_w, y + 1);
508                 }
509                 
510                 int x = graph_x + graph_w * i / divisions;
511                 draw_line(x, graph_y, x, graph_y + graph_h);
512 // 0db 
513                 if( i == divisions - 1 ) {
514                         draw_line(x + 1, graph_y, x + 1, graph_y + graph_h);
515                 }
516         }
517         set_line_dashes(0);
518
519
520         set_font(MEDIUMFONT);
521         int border = plugin->get_theme()->widget_border; 
522         draw_text(border, get_h() / 2, _("Output"));
523         int tx = get_w() / 2 - get_text_width(MEDIUMFONT, _("Input")) / 2;
524         int ty = get_h() - get_text_height(MEDIUMFONT, _("Input")) - border;
525         draw_text(tx, ty, _("Input"));
526
527         for( int pass = 0; pass < 2; pass++ ) {
528                 for( int band = 0; band < config->total_bands; band++ ) {
529 // draw the active band on top of the others
530                         if( band == config->current_band && pass == 0 ||
531                                 band != config->current_band && pass == 1 ) {
532                                 continue;
533                         }
534
535                         if( band == config->current_band ) {
536                                 set_color(plugin->get_theme()->graph_active_color);
537                                 set_line_width(2);
538                         }
539                         else {
540                                 set_color(plugin->get_theme()->graph_inactive_color);
541                                 set_line_width(1);
542                         }
543
544 // draw the line
545                         int x1 = graph_x, y1 = x_to_y(band, x1);
546                         for( int i=0; ++i <= graph_w; ) {
547                                 int x2 = x1+1, y2 = x_to_y(band, x2);
548                                 draw_line(x1,y1, x2,y2);
549                                 x1 = x2;  y1 = y2;
550                         }
551
552                         set_line_width(1);
553
554 // draw the points
555                         if( band == config->current_band ) {
556                                 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
557                                 double mkup_gain = config->bands[band].mkup_gain;
558                                 for( int i = 0; i < levels.size(); i++ ) {
559                                         double x_db = config->get_x(band, i);
560                                         double y_db = config->get_y(band, i) + mkup_gain;
561                                         int x = db_to_x(x_db);
562                                         int y = db_to_y(y_db);
563
564                                         if( i == current_point ) {
565                                                 draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
566                                         }
567                                         else {
568                                                 draw_rectangle(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
569                                         }
570                                 }
571                         }
572                 }
573         }
574         
575         flash();
576 }
577
578 int CompressorCanvasBase::button_press_event()
579 {
580 // Check existing points
581         if( is_event_win() && cursor_inside() ) {
582                 if( get_buttonpress() == 3 ) {
583                         menu->activate_menu();
584                         return 1;
585                 }
586                 int x = get_cursor_x(), y = get_cursor_y();
587                 int band = config->current_band;
588                 ArrayList<compressor_point_t> &levels = config->bands[band].levels;
589                 double mkup_gain = config->bands[band].mkup_gain;
590                 for( int i=0; i<levels.size(); ++i ) {
591                         double x_db = config->get_x(config->current_band, i);
592                         double y_db = config->get_y(config->current_band, i) + mkup_gain;
593
594                         int px = db_to_x(x_db);
595                         int py = db_to_y(y_db);
596
597                         if( x <= px + POINT_W / 2 && x >= px - POINT_W / 2 &&
598                             y <= py + POINT_W / 2 && y >= py - POINT_W / 2 ) {
599                                 current_operation = DRAG;
600                                 current_point = i;
601                                 return 1;
602                         }
603                 }
604
605                 if( x >= graph_x && x < graph_x + graph_w &&
606                     y >= graph_y && y < graph_y + graph_h ) {
607 // Create new point
608                         double x_db = x_to_db(x);
609                         double y_db = y_to_db(y) + mkup_gain;
610
611                         current_point = config->set_point(config->current_band, x_db, y_db);
612                         current_operation = DRAG;
613                         update_window();
614                         plugin->send_configure_change();
615                         return 1;
616                 }
617         }
618         return 0;
619 }
620
621 int CompressorCanvasBase::button_release_event()
622 {
623         int band = config->current_band;
624         ArrayList<compressor_point_t> &levels = config->bands[band].levels;
625
626         if( current_operation == DRAG ) {
627                 if( current_point > 0 ) {
628                         if( levels[current_point].x < levels[current_point-1].x ) {
629                                 config->remove_point(config->current_band, current_point);
630                         }
631                 }
632
633                 if( current_point < levels.size()-1 ) {
634                         if( levels[current_point].x >= levels[current_point + 1].x ) {
635                                 config->remove_point(config->current_band, current_point);
636                         }
637                 }
638
639                 update_window();
640                 plugin->send_configure_change();
641                 current_operation = NONE;
642                 return 1;
643         }
644
645         return 0;
646 }
647
648 int CompressorCanvasBase::cursor_motion_event()
649 {
650         int band = config->current_band;
651         ArrayList<compressor_point_t> &levels = config->bands[band].levels;
652         double mkup_gain = config->bands[band].mkup_gain;
653         int x = get_cursor_x(), y = get_cursor_y();
654
655         if( current_operation == DRAG ) {
656                 double x_db = x_to_db(x);
657                 double y_db = y_to_db(y) - mkup_gain;
658                 
659                 if( shift_down() ) {
660                         const int grid_precision = 6;
661                         x_db = config->max_db + (double)(grid_precision *
662                                 (int)((x_db - config->max_db) / grid_precision - 0.5));
663                         y_db = config->max_db + (double)(grid_precision *
664                                 (int)((y_db - config->max_db) / grid_precision - 0.5));
665                 }
666                 
667                 
668 //printf("CompressorCanvasBase::cursor_motion_event %d x=%d y=%d x_db=%f y_db=%f\n", 
669 //__LINE__, x, y, x_db, y_db);
670                 levels[current_point].x = x_db;
671                 levels[current_point].y = y_db;
672                 update_window();
673                 plugin->send_configure_change();
674                 return 1;
675         }
676         else
677 // Change cursor over points
678         if( is_event_win() && cursor_inside() ) {
679                 int new_cursor = CROSS_CURSOR;
680
681                 for( int i = 0; i < levels.size(); i++ ) {
682                         double x_db = config->get_x(config->current_band, i);
683                         double y_db = config->get_y(config->current_band, i) + mkup_gain;
684                         int px = db_to_x(x_db);
685                         int py = db_to_y(y_db);
686
687                         if( x <= px + POINT_W / 2 && x >= px - POINT_W / 2 &&
688                                 y <= py + POINT_W / 2 && y >= py - POINT_W / 2 ) {
689                                 new_cursor = UPRIGHT_ARROW_CURSOR;
690                                 break;
691                         }
692                 }
693
694 // out of active area
695                 if( x >= graph_x + graph_w || y < graph_y ) {
696                         new_cursor = ARROW_CURSOR;
697                 }
698                 if( new_cursor != get_cursor() ) {
699                         set_cursor(new_cursor, 0, 1);
700                 }
701         }
702         return 0;
703 }
704
705
706 void CompressorCanvasBase::update_window()
707 {
708         printf("CompressorCanvasBase::update_window %d empty\n", __LINE__);
709 }
710
711
712 int CompressorCanvasBase::is_dragging()
713 {
714         return current_operation == DRAG;
715 }
716
717
718 CompressorClientFrame::CompressorClientFrame()
719 {
720         type = -1;
721         band = 0;
722 }
723 CompressorClientFrame::~CompressorClientFrame()
724 {
725 }
726
727 CompressorFreqFrame::CompressorFreqFrame()
728 {
729         type = FREQ_COMPRESSORFRAME;
730         data = 0;      data_size = 0;
731         freq_max = 0;  time_max = 0;
732         nyquist = 0;
733 }
734 CompressorFreqFrame::~CompressorFreqFrame()
735 {
736         delete [] data;
737 }
738
739 CompressorGainFrame::CompressorGainFrame()
740 {
741         type = GAIN_COMPRESSORFRAME;
742         gain = 0;
743         level = 0;
744 }
745 CompressorGainFrame::~CompressorGainFrame()
746 {
747 }
748
749 CompressorEngine::CompressorEngine(CompressorConfigBase *config,
750         int band)
751 {
752         this->config = config;
753         this->band = band;
754         reset();
755 }
756
757 CompressorEngine::~CompressorEngine()
758 {
759 }
760
761
762 void CompressorEngine::reset()
763 {
764         slope_samples = 0;
765         slope_current_sample = 0;
766         peak_samples = 0;
767         slope_value1 = 1.0;
768         slope_value2 = 1.0;
769         current_value = 0.5;
770         gui_frame_samples = 2048;
771         gui_max_gain = 1.0;
772         gui_frame_counter = 0;
773 }
774
775
776 void CompressorEngine::calculate_ranges(int *attack_samples,
777         int *release_samples,
778         int *preview_samples,
779         int sample_rate)
780 {
781         BandConfig *band_config = &config->bands[band];
782         *attack_samples = labs(Units::round(band_config->attack_len * sample_rate));
783         *release_samples = Units::round(band_config->release_len * sample_rate);
784         CLAMP(*attack_samples, 1, 1000000);
785         CLAMP(*release_samples, 1, 1000000);
786         *preview_samples = MAX(*attack_samples, *release_samples);
787 }
788
789
790 void CompressorEngine::process(Samples **output_buffer,
791         Samples **input_buffer,
792         int size,
793         int sample_rate,
794         int channels,
795         int64_t start_position)
796 {
797         BandConfig *band_config = &config->bands[band];
798         int attack_samples;
799         int release_samples;
800         int preview_samples;
801         int trigger = CLIP(config->trigger, 0, channels - 1);
802
803         gui_gains.remove_all();
804         gui_levels.remove_all();
805         gui_offsets.remove_all();
806         
807         calculate_ranges(&attack_samples,
808                 &release_samples,
809                 &preview_samples,
810                 sample_rate);
811         if( slope_current_sample < 0 ) slope_current_sample = slope_samples;
812
813         double *trigger_buffer = input_buffer[trigger]->get_data();
814
815         for( int i = 0; i < size; i++ ) {
816                 double current_slope = (slope_value2 - slope_value1) /
817                         slope_samples;
818
819 // maximums in the 2 time ranges
820                 double attack_slope = -0x7fffffff;
821                 double attack_sample = -1;
822                 int attack_offset = -1;
823                 int have_attack_sample = 0;
824                 double release_slope = -0x7fffffff;
825                 double release_sample = -1;
826                 int release_offset = -1;
827                 int have_release_sample = 0;
828                 if( slope_current_sample >= slope_samples ) {
829 // start new line segment
830                         for( int j = 1; j < preview_samples; j++ ) {
831                                 GET_TRIGGER(input_buffer[channel]->get_data(), i + j)
832                                 double new_slope = (sample - current_value) / j;
833                                 if( j < attack_samples && new_slope >= attack_slope ) {
834                                         attack_slope = new_slope;
835                                         attack_sample = sample;
836                                         attack_offset = j;
837                                         have_attack_sample = 1;
838                                 }
839                                 
840                                 if( j < release_samples && 
841                                         new_slope <= 0 && 
842                                         new_slope > release_slope ) {
843                                         release_slope = new_slope;
844                                         release_sample = sample;
845                                         release_offset = j;
846                                         have_release_sample = 1;
847                                 }
848                         }
849
850                         slope_current_sample = 0;
851                         if( have_attack_sample && attack_slope >= 0 ) {
852 // attack
853                                 peak_samples = attack_offset;
854                                 slope_samples = attack_offset;
855                                 slope_value1 = current_value;
856                                 slope_value2 = attack_sample;
857                                 current_slope = attack_slope;
858 //printf("CompressorEngine::process %d position=%ld slope=%f samples=%d\n", 
859 //__LINE__, start_position + i, current_slope, slope_samples);
860                         }
861                         else
862                         if( have_release_sample ) {
863 // release
864                                 slope_samples = release_offset;
865 //                              slope_samples = release_samples;
866                                 peak_samples = release_offset;
867                                 slope_value1 = current_value;
868                                 slope_value2 = release_sample;
869                                 current_slope = release_slope;
870 //printf("CompressorEngine::process %d position=%ld slope=%f\n", 
871 //__LINE__, start_position + i, current_slope);
872                         }
873                         else {
874 static int bug = 0;
875 if( !bug ) {
876 printf("CompressorEngine::process %d have neither attack nor release position=%ld attack=%f release=%f current_value=%f\n",
877 __LINE__, start_position + i, attack_slope, release_slope, current_value); bug = 1;
878 }
879                         }
880                 }
881                 else {
882 // check for new peak after the line segment
883                         GET_TRIGGER(input_buffer[channel]->get_data(), i + attack_samples)
884                         double new_slope = (sample - current_value) /
885                                 attack_samples;
886                         if( current_slope >= 0 ) {
887                                 if( new_slope > current_slope ) {
888                                         peak_samples = attack_samples;
889                                         slope_samples = attack_samples;
890                                         slope_current_sample = 0;
891                                         slope_value1 = current_value;
892                                         slope_value2 = sample;
893                                         current_slope = new_slope;
894 //printf("CompressorEngine::process %d position=%ld slope=%f\n", 
895 //__LINE__, start_position + i, current_slope);
896                                 }
897                         }
898                         else
899 // this strings together multiple release periods instead of 
900 // approaching but never reaching the ending gain
901                         if( current_slope < 0 ) {
902                                 if( sample > slope_value2 ) {
903                                         peak_samples = attack_samples;
904                                         slope_samples = release_samples;
905                                         slope_current_sample = 0;
906                                         slope_value1 = current_value;
907                                         slope_value2 = sample;
908                                         new_slope = (sample - current_value) /
909                                                 release_samples;
910                                         current_slope = new_slope;
911 //printf("CompressorEngine::process %d position=%ld slope=%f\n", 
912 //__LINE__, start_position + i, current_slope);
913                                 }
914 //                              else
915 //                               {
916 //                                       GET_TRIGGER(input_buffer[channel]->get_data(), i + release_samples)
917 //                                       new_slope = (sample - current_value) /
918 //                                              release_samples;
919 //                                       if( new_slope < current_slope &&
920 //                                               slope_current_sample >= peak_samples )
921 //                                       {
922 //                                               peak_samples = release_samples;
923 //                                               slope_samples = release_samples;
924 //                                               slope_current_sample = 0;
925 //                                               slope_value1 = current_value;
926 //                                               slope_value2 = sample;
927 //                                              current_slope = new_slope;
928 // printf("CompressorEngine::process %d position=%ld slope=%f\n", 
929 // __LINE__, start_position + i, current_slope);
930 //                                       }
931 //                               }
932                         }
933                 }
934
935 // Update current value and multiply gain
936                 slope_current_sample++;
937                 current_value = slope_value1 +
938                         (slope_value2 - slope_value1) * 
939                         slope_current_sample / 
940                         slope_samples;
941
942                 double gain = 1.0;
943                 if( !config->smoothing_only ) {
944                         if( !band_config->bypass )
945                                 gain = config->calculate_gain(band, current_value);
946 // update the GUI frames
947                         if( fabs(gain - 1.0) > fabs(gui_max_gain - 1.0) ) {
948                                 gui_max_gain = gain;
949                         }
950 // calculate the input level to draw.  Should it be the trigger or a channel?
951                         GET_TRIGGER(input_buffer[channel]->get_data(), i);
952                         if( sample > gui_max_level ) {
953                                 gui_max_level = sample;
954                         }
955                         gui_frame_counter++;
956                         if( gui_frame_counter > gui_frame_samples ) {
957 //if( !EQUIV(gui_frame_max, 1.0) ) printf("CompressorEngine::process %d offset=%d gui_frame_max=%f\n", __LINE__, i, gui_frame_max);
958                                 gui_gains.append(gui_max_gain);
959                                 gui_levels.append(gui_max_level);
960                                 gui_offsets.append((double)i / sample_rate);
961                                 gui_max_gain = 1.0;
962                                 gui_max_level = 0.0;
963                                 gui_frame_counter = 0;
964                         }
965                 }
966                 else {
967                         gain = current_value > 0.01 ? 0.5 / current_value : 50.;
968                 }
969                 for( int j = 0; j < channels; j++ ) {
970                         output_buffer[j]->get_data()[i] = input_buffer[j]->get_data()[i] * gain;
971                 }
972         }
973 }
974
975
976 CompressorPopup::CompressorPopup(CompressorCanvasBase *canvas)
977  : BC_PopupMenu(0, 0, 0, "", 0)
978 {
979         this->canvas = canvas;
980 }
981
982 CompressorPopup::~CompressorPopup()
983 {
984 }
985
986         
987 void CompressorPopup::create_objects()
988 {
989         add_item(new CompressorCopy(this));
990         add_item(new CompressorPaste(this));
991         add_item(new CompressorClearGraph(this));
992 }
993
994
995 CompressorCopy::CompressorCopy(CompressorPopup *popup)
996  : BC_MenuItem(_("Copy graph"))
997 {
998         this->popup = popup;
999 }
1000
1001
1002 CompressorCopy::~CompressorCopy()
1003 {
1004 }
1005
1006 int CompressorCopy::handle_event()
1007 {
1008         FileXML output;
1009         CompressorConfigBase *config = popup->canvas->config;
1010         config->bands[config->current_band].save_data(&output, 0, 0);
1011         output.terminate_string();
1012         char *cp = output.string(); 
1013         popup->to_clipboard(cp, strlen(cp), SECONDARY_SELECTION);
1014         return 1;
1015 }
1016
1017
1018 CompressorPaste::CompressorPaste(CompressorPopup *popup)
1019  : BC_MenuItem(_("Paste graph"))
1020 {
1021         this->popup = popup;
1022 }
1023
1024
1025 CompressorPaste::~CompressorPaste()
1026 {
1027 }
1028
1029 int CompressorPaste::handle_event()
1030 {
1031         int len = popup->get_clipboard()->clipboard_len(SECONDARY_SELECTION);
1032         if( len ) {
1033                 CompressorConfigBase *config = popup->canvas->config;
1034                 char *string = new char[len + 1];
1035                 popup->get_clipboard()->from_clipboard(string, len, SECONDARY_SELECTION);
1036                 
1037                 FileXML xml;
1038                 xml.read_from_string(string);
1039                 delete [] string;
1040                 int result = 0, got_it = 0;
1041                 while( !(result = xml.read_tag()) ) {
1042                         if( xml.tag.title_is("COMPRESSORBAND") ) {
1043                                 int band = config->current_band;
1044                                 BandConfig *band_config = &config->bands[band];
1045                                 band_config->read_data(&xml, 0);
1046                                 got_it = 1;
1047                                 break;
1048                         }
1049                 }
1050                 
1051                 if( got_it ) {
1052                         popup->canvas->update();
1053                         PluginClient *plugin = popup->canvas->plugin;
1054                         plugin->send_configure_change();
1055                 }
1056         }
1057         return 1;
1058 }
1059
1060
1061 CompressorClearGraph::CompressorClearGraph(CompressorPopup *popup)
1062  : BC_MenuItem(_("Clear graph"))
1063 {
1064         this->popup = popup;
1065 }
1066
1067
1068 CompressorClearGraph::~CompressorClearGraph()
1069 {
1070 }
1071
1072 int CompressorClearGraph::handle_event()
1073 {
1074         CompressorConfigBase *config = popup->canvas->config;
1075         config->bands[config->current_band].levels.remove_all();
1076         popup->canvas->update();
1077         PluginClient *plugin = popup->canvas->plugin;
1078         plugin->send_configure_change();
1079         return 1;
1080 }
1081