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