new/reworked audio plugins ported from hv72 compressor/multi/reverb, glyph workaround...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcmeter.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "bcbutton.h"
22 #include "bcmeter.h"
23 #include "bcpixmap.h"
24 #include "bcresources.h"
25 #include "bcwindow.h"
26 #include "colors.h"
27 #include "fonts.h"
28 #include "vframe.h"
29 #include <string.h>
30
31 // Which source image to replicate
32 #define METER_NORMAL 0
33 #define METER_GREEN 1
34 #define METER_RED 2
35 #define METER_YELLOW 3
36 #define METER_WHITE 4
37 #define METER_OVER 5
38 #define METER_DOWNMIX 6
39
40 // Region of source image to replicate
41 #define METER_LEFT 0
42 #define METER_MID 1
43 #define METER_RIGHT 3
44
45
46 BC_Meter::BC_Meter(int x, int y, int orientation, int pixels,
47         int min, int max, int mode, int use_titles,
48         int span, int is_downmix, int is_gain_change)
49  : BC_SubWindow(x, y, -1, -1)
50 {
51         this->over_delay = 150;
52         this->peak_delay = 15;
53         this->use_titles = use_titles;
54         this->min = min;
55         this->max = max;
56         this->mode = mode;
57         this->orientation = orientation;
58         this->pixels = pixels;
59         this->span = span;
60         this->is_downmix = is_downmix;
61         this->is_gain_change = is_gain_change;
62 //printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels);
63         for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = 0;
64         db_titles.set_array_delete();
65 }
66
67 BC_Meter::~BC_Meter()
68 {
69         db_titles.remove_all_objects();
70         title_pixels.remove_all();
71         tick_w.remove_all();
72         tick_pixels.remove_all();
73         for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i];
74 }
75
76 int BC_Meter::get_title_w()
77 {
78         return get_resources()->meter_title_w;
79 }
80
81 int BC_Meter::get_meter_w()
82 {
83         return get_resources()->ymeter_images[0]->get_w();
84 }
85
86
87 int BC_Meter::set_delays(int over_delay, int peak_delay)
88 {
89         this->over_delay = over_delay;
90         this->peak_delay = peak_delay;
91         return 0;
92 }
93
94 int BC_Meter::initialize()
95 {
96         peak_timer = 0;
97         level_pixel = peak_pixel = 0;
98         over_timer = 0;
99         over_count = 0;
100
101         if( is_gain_change ) {
102                 peak = level = 0;
103         }
104         else {
105                 peak = level = -100;
106         }
107
108         if( orientation == METER_VERT ) {
109                 set_images(get_resources()->ymeter_images);
110                 h = pixels;
111                 if( span < 0 ) {
112                         w = images[0]->get_w();
113                         if( use_titles ) w += get_title_w();
114                 }
115                 else
116                         w = span;
117         }
118         else {
119                 set_images(get_resources()->xmeter_images);
120                 h = images[0]->get_h();
121                 w = pixels;
122                 if( use_titles ) h += get_title_w();
123         }
124
125 // calibrate the db titles
126         get_divisions();
127
128         BC_SubWindow::initialize();
129         draw_titles(0);
130         draw_face(0);
131         show_window(0);
132         return 0;
133 }
134
135 void BC_Meter::set_images(VFrame **data)
136 {
137         for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i];
138         for( int i = 0; i < TOTAL_METER_IMAGES; i++ )
139                 images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA);
140 }
141
142 int BC_Meter::reposition_window(int x, int y, int span, int pixels)
143 {
144         if( pixels < 0 ) pixels = this->pixels;
145         this->span = span;
146         this->pixels = pixels;
147         if( orientation == METER_VERT )
148                 BC_SubWindow::reposition_window(x, y,
149                         this->span < 0 ? w : span, pixels);
150         else
151                 BC_SubWindow::reposition_window(x, y, pixels, get_h());
152
153 //printf("BC_Meter::reposition_window 1 %d %d %d %d\n", x, y, w, h);
154         get_divisions();
155
156 //set_color(WHITE);
157 //draw_box(0, 0, w, h);
158 //flash();
159 //return 0;
160         draw_titles(0);
161         draw_face(0);
162         return 0;
163 }
164
165 int BC_Meter::reset(int downmix)
166 {
167         level = min;
168         peak = min;
169         level_pixel = peak_pixel = 0;
170         peak_timer = 0;
171         over_timer = 0;
172         over_count = 0;
173         if( downmix >= 0 )
174                 is_downmix = downmix;
175         draw_face(1);
176         return 0;
177 }
178
179 int BC_Meter::button_press_event()
180 {
181         if( cursor_inside() && is_event_win() ) {
182                 reset_over();
183                 return 1;
184         }
185         return 0;
186 }
187
188
189 int BC_Meter::reset_over()
190 {
191         over_timer = 0;
192         return 0;
193 }
194
195 int BC_Meter::change_format(int mode, int min, int max)
196 {
197         this->mode = mode;
198         this->min = min;
199         this->max = max;
200         reposition_window(get_x(), get_y(), w, pixels);
201         return 0;
202 }
203
204 int BC_Meter::level_to_pixel(float level)
205 {
206         int result;
207         if( mode == METER_DB ) {
208                 result = (int)(pixels * (level - min) / (max - min));
209                 if( level <= min ) result = 0;
210         }
211         else {
212 // Not implemented anymore
213                 result = 0;
214         }
215
216         return result;
217 }
218
219
220 void BC_Meter::get_divisions()
221 {
222         char string[BCTEXTLEN];
223         char *new_string;
224
225
226         db_titles.remove_all_objects();
227         title_pixels.remove_all();
228         tick_pixels.remove_all();
229         tick_w.remove_all();
230
231         low_division = 0;
232         medium_division = 0;
233         high_division = pixels;
234
235         int current_pixel;
236 // Create tick marks and titles in one pass
237         for( int current = min; current <= max; current++ ) {
238                 if( orientation == METER_VERT ) {
239 // Create tick mark
240                         current_pixel = (pixels - METER_MARGIN * 2 - 2) *
241                                 (current - min) / (max - min) + 2;
242                         tick_pixels.append(current_pixel);
243
244 // Create titles in selected positions
245                         if( current == min || current == max || current == 0 ||
246                                 (current - min > 4 && max - current > 4 && !(current % 5)) ) {
247                                 int title_pixel = (pixels - METER_MARGIN * 2) *
248                                         (current - min) / (max - min);
249                                 sprintf(string, "%d", (int)labs(current));
250                                 new_string = new char[strlen(string) + 1];
251                                 strcpy(new_string, string);
252                                 db_titles.append(new_string);
253                                 title_pixels.append(title_pixel);
254                                 tick_w.append(TICK_W1);
255                         }
256                         else {
257                                 tick_w.append(TICK_W2);
258                         }
259                 }
260                 else {
261                         current_pixel = (pixels - METER_MARGIN * 2) *
262                                 (current - min) /
263                                 (max - min);
264                         tick_pixels.append(current_pixel);
265                         tick_w.append(TICK_W1);
266 // Titles not supported for horizontal
267                 }
268
269 // Create color divisions
270                 if( current == -20 ) {
271                         low_division = current_pixel;
272                 }
273                 else
274                 if( current == -5 ) {
275                         medium_division = current_pixel;
276                 }
277                 else
278                 if( current == 0 ) {
279                         high_division = current_pixel;
280                 }
281         }
282 // if( orientation == METER_VERT )
283 // printf("BC_Meter::get_divisions %d %d %d %d\n",
284 // low_division, medium_division, high_division, pixels);
285 }
286
287 void BC_Meter::draw_titles(int flush)
288 {
289         if( !use_titles ) return;
290         int tick_xfudge = xS(1);
291
292         set_font(get_resources()->meter_font);
293
294         if( orientation == METER_HORIZ ) {
295                 draw_top_background(parent_window, 0, 0, get_w(), get_title_w());
296
297                 for( int i = 0; i < db_titles.total; i++ ) {
298                         draw_text(0, title_pixels.values[i], db_titles.values[i]);
299                 }
300
301                 flash(0, 0, get_w(), get_title_w(), flush);
302         }
303         else
304         if( orientation == METER_VERT ) {
305                 draw_top_background(parent_window, 0, 0, get_title_w(), get_h());
306
307 // Titles
308                 for( int i = 0; i < db_titles.total; i++ ) {
309                         int title_y = pixels - title_pixels.values[i];
310                         if( i == 0 )
311                                 title_y -= get_text_descent(get_resources()->meter_font);
312                         else
313                         if( i == db_titles.total - 1 )
314                                 title_y += get_text_ascent(get_resources()->meter_font);
315                         else
316                                 title_y += get_text_ascent(get_resources()->meter_font) / 2;
317                         int title_x = get_title_w() - TICK_W1 - tick_xfudge -
318                                 get_text_width(get_resources()->meter_font, db_titles.values[i]);
319
320                         set_color(get_resources()->meter_font_color);
321                         draw_text(title_x, title_y, db_titles.values[i]);
322                 }
323
324                 for( int i = 0; i < tick_pixels.total; i++ ) {
325 // Tick marks
326                         int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN;
327                         set_color(get_resources()->meter_font_color);
328                         draw_line(get_title_w() - tick_w.get(i) - tick_xfudge,
329                                 tick_y, get_title_w() - tick_xfudge, tick_y);
330
331                         if( get_resources()->meter_3d ) {
332                                 set_color(BLACK);
333                                 draw_line(get_title_w() - tick_w.get(i),
334                                         tick_y + 1, get_title_w(), tick_y + 1);
335                         }
336                 }
337
338                 flash(0, 0, get_title_w(), get_h(), flush);
339         }
340 }
341
342 int BC_Meter::region_pixel(int region)
343 {
344         VFrame **reference_images = get_resources()->xmeter_images;
345         int result = 0;
346
347         if( region == METER_RIGHT )
348                 result = region * reference_images[0]->get_w() / 4;
349         else
350                 result = region * reference_images[0]->get_w() / 4;
351
352         return result;
353 }
354
355 int BC_Meter::region_pixels(int region)
356 {
357         int x1;
358         int x2;
359         int result;
360         VFrame **reference_images = get_resources()->xmeter_images;
361
362         x1 = region * reference_images[0]->get_w() / 4;
363         x2 = (region + 1) * reference_images[0]->get_w() / 4;
364         if( region == METER_MID )
365                 result = (x2 - x1) * 2;
366         else
367                 result = x2 - x1;
368         return result;
369 }
370
371 void BC_Meter::draw_face(int flush)
372 {
373         int level_pixel = level_to_pixel(level);
374         int peak_pixel2 = level_to_pixel(peak);
375         int peak_pixel1 = peak_pixel2 - 2;
376         int left_pixel = region_pixel(METER_MID);
377         int right_pixel = pixels - region_pixels(METER_RIGHT);
378         int pixel = 0;
379         int image_number = 0, region = 0;
380         int in_span, in_start;
381         int x = use_titles ? get_title_w() : 0;
382         int w = use_titles ? (this->w - get_title_w()) : this->w;
383
384         draw_top_background(parent_window, x, 0, w, h);
385
386 // printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n",
387 // __LINE__, span, this->w, get_title_w(), w, h);
388         if( is_gain_change ) {
389                 int in_h = images[0]->get_h();
390                 int in_third = in_h / 3;
391                 int in_third3 = in_h - in_third * 2;
392
393 // printf("BC_Meter::draw_face %d level=%f level_pixel=%d high_division=%d\n",
394 // __LINE__, level, level_pixel, high_division);
395
396
397 // fudge a line when no gain change
398                 if( level_pixel == high_division ) {
399                         level_pixel += 1;
400                 }
401
402                 while( pixel < pixels ) {
403 // Select image to draw & extents
404                         if( level_pixel < high_division ) {
405 // always vertical
406                                 if( pixel < level_pixel ) {
407                                         image_number = METER_NORMAL;
408                                         in_span = level_pixel - pixel;
409                                 }
410                                 else
411                                 if( pixel < high_division ) {
412                                         image_number = METER_RED;
413                                         in_span = high_division - pixel;
414                                 }
415                                 else {
416                                         image_number = METER_NORMAL;
417                                         in_span = pixels - pixel;
418                                 }
419                         }
420                         else {
421 // determine pixel range & image to draw
422                                 if( pixel < high_division ) {
423                                         image_number = METER_NORMAL;
424                                         in_span = high_division - pixel;
425                                 }
426                                 else
427                                 if( pixel < level_pixel ) {
428                                         image_number = METER_GREEN;
429                                         in_span = level_pixel - pixel;
430                                 }
431                                 else {
432                                         image_number = METER_NORMAL;
433                                         in_span = pixels - pixel;
434                                 }
435                         }
436
437 // determine starting point in source to draw
438 // draw starting section
439                         if( pixel == 0 ) {
440                                 in_start = 0;
441                         }
442                         else
443 // draw middle section
444                         if( pixels - pixel > in_third3 ) {
445                                 in_start = in_third;
446                         }
447                         else
448 // draw last section
449                         {
450                                 in_start = in_third * 2;
451                         }
452
453 // clamp the region to the source dimensions
454                         if( in_start < in_third * 2 ) {
455                                 if( in_span > in_third ) {
456                                         in_span = in_third;
457                                 }
458                         }
459                         else
460 // last segment
461                         if( pixels - pixel < in_third3 ) {
462                                 in_span = pixels - pixel;
463                                 in_start = in_h - in_span;
464                         }
465
466 // printf("BC_Meter::draw_face %d dst_y=%d src_y=%d"
467 // " pixels=%d pixel=%d in_h=%d in_start=%d in_span=%d in_third=%d in_third3=%d\n",
468 // __LINE__, get_h() - pixel - in_span, in_h - in_start - in_span,
469 // pixels, pixel, in_h, in_start, in_span, in_third, in_third3);
470                         draw_pixmap(images[image_number], x, get_h() - pixel - in_span,
471                                 get_w(), in_span + 1, 0, in_h - in_start - in_span);
472                         pixel += in_span;
473                 }
474         }
475         else {
476                 while( pixel < pixels ) {
477 // Select image to draw
478                         if( pixel < level_pixel ||
479                                 (pixel >= peak_pixel1 && pixel < peak_pixel2) ) {
480                                 if( pixel < low_division )
481                                         image_number = METER_GREEN;
482                                 else
483                                 if( pixel < medium_division )
484                                         image_number = METER_YELLOW;
485                                 else
486                                 if( pixel < high_division )
487                                         image_number = METER_RED;
488                                 else
489                                         image_number = METER_WHITE;
490                         }
491                         else {
492                                 image_number = METER_NORMAL;
493                         }
494
495 // Select region of image to duplicate
496                         if( pixel < left_pixel ) {
497                                 region = METER_LEFT;
498                                 in_start = pixel + region_pixel(region);
499                                 in_span = region_pixels(region) - (in_start - region_pixel(region));
500                         }
501                         else
502                         if( pixel < right_pixel ) {
503                                 region = METER_MID;
504                                 in_start = region_pixel(region);
505                                 in_span = region_pixels(region);
506                         }
507                         else {
508                                 region = METER_RIGHT;
509                                 in_start = (pixel - right_pixel) + region_pixel(region);
510                                 in_span = region_pixels(region) - (in_start - region_pixel(region));;
511                         }
512
513         //printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span);
514                         if( in_span > 0 ) {
515         // Clip length to peaks
516                                 if( pixel < level_pixel && pixel + in_span > level_pixel )
517                                         in_span = level_pixel - pixel;
518                                 else
519                                 if( pixel < peak_pixel1 && pixel + in_span > peak_pixel1 )
520                                         in_span = peak_pixel1 - pixel;
521                                 else
522                                 if( pixel < peak_pixel2 && pixel + in_span > peak_pixel2 )
523                                         in_span = peak_pixel2 - pixel;
524
525         // Clip length to color changes
526                                 if( image_number == METER_GREEN && pixel + in_span > low_division )
527                                         in_span = low_division - pixel;
528                                 else
529                                 if( image_number == METER_YELLOW && pixel + in_span > medium_division )
530                                         in_span = medium_division - pixel;
531                                 else
532                                 if( image_number == METER_RED && pixel + in_span > high_division )
533                                         in_span = high_division - pixel;
534
535         // Clip length to regions
536                                 if( pixel < left_pixel && pixel + in_span > left_pixel )
537                                         in_span = left_pixel - pixel;
538                                 else
539                                 if( pixel < right_pixel && pixel + in_span > right_pixel )
540                                         in_span = right_pixel - pixel;
541
542         //printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span);
543         //printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span);
544                                 if( orientation == METER_HORIZ ) {
545                                         draw_pixmap(images[image_number], pixel,
546                                                 x, in_span + 1, get_h(), in_start, 0);
547                                 }
548                                 else {
549         //printf("BC_Meter::draw_face %d %d\n", __LINE__, span);
550                                         if( span < 0 ) {
551                                                 draw_pixmap(images[image_number],
552                                                         x, get_h() - pixel - in_span,
553                                                         get_w(), in_span + 1, 0,
554                                                         images[image_number]->get_h() - in_start - in_span);
555                                         }
556                                         else {
557                                                 int total_w = get_w() - x;
558                                                 int third = images[image_number]->get_w() / 3 + 1;
559
560
561                                                 for( int x1 = 0; x1 < total_w; x1 += third ) {
562                                                         int in_x = 0;
563                                                         int in_w = third;
564                                                         if( x1 >= third ) in_x = third;
565                                                         if( x1 >= total_w - third ) {
566                                                                 in_x = images[image_number]->get_w() -
567                                                                         (total_w - x1);
568                                                                 in_w = total_w - x1;
569                                                         }
570
571                                                         int in_y = images[image_number]->get_h() - in_start - in_span;
572         //printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span);
573
574
575                                                         draw_pixmap(images[image_number],
576                                                                 x + x1, get_h() - pixel - in_span,
577                                                                 in_w, in_span + 1, in_x, in_y);
578                                                 }
579                                         }
580                                 }
581
582                                 pixel += in_span;
583                         }
584                         else {
585         // Sanity check
586                                 break;
587                         }
588                 }
589         }
590
591         if( is_downmix ) {
592                 if( orientation == METER_HORIZ )
593                         draw_pixmap(images[METER_DOWNMIX], 0, 0);
594                 else
595                         draw_pixmap(images[METER_DOWNMIX],
596                                 x, get_h() - images[METER_DOWNMIX]->get_h() - 1);
597         }
598         if( over_timer ) {
599                 if( orientation == METER_HORIZ )
600                         draw_pixmap(images[METER_OVER], xS(20), yS(2));
601                 else
602                         draw_pixmap(images[METER_OVER],
603                                 x + xS(2), get_h() - yS(100));
604
605                 over_timer--;
606         }
607
608         if( orientation == METER_HORIZ )
609                 flash(0, 0, pixels, get_h(), flush);
610         else
611                 flash(x, 0, w, pixels, flush);
612 }
613
614 int BC_Meter::update(float new_value, int over, int downmix)
615 {
616         peak_timer++;
617
618         if( mode == METER_DB ) {
619                 if( new_value == 0 )
620                         level = min;
621                 else
622                         level = DB::todb(new_value);            // db value
623         }
624
625
626         if( is_gain_change && fabs(level) > fabs(peak) ||
627                 !is_gain_change && level > peak ||
628                 peak_timer > peak_delay ) {
629                 peak = level;
630                 peak_timer = 0;
631         }
632
633 // if( orientation == METER_HORIZ )
634 // printf("BC_Meter::update %f\n", level);
635         if( over ) over_timer = over_delay;
636 // only draw if window is visible
637         if( downmix >= 0 )
638                 is_downmix = downmix;
639         draw_face(1);
640         return 0;
641 }