new/reworked audio plugins ported from hv72 compressor/multi/reverb, glyph workaround...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / graphic / graphic.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "clip.h"
25 #include "cursors.h"
26 #include "bchash.h"
27 #include "filexml.h"
28 #include "graphic.h"
29 #include "keys.h"
30 #include "language.h"
31 #include "samples.h"
32 #include "theme.h"
33 #include "units.h"
34 #include "vframe.h"
35
36 #include <math.h>
37 #include <string.h>
38
39
40 // Canvas parameters
41 #define MAJOR_DIVISIONS 7
42 #define MINOR_DIVISIONS 5
43 #define LINE_W4 xS(12)
44 #define LINE_W3 xS(10)
45 #define LINE_W2 xS(5)
46 #define LINE_W1 xS(2)
47
48
49
50
51
52
53
54
55 REGISTER_PLUGIN(GraphicEQ)
56
57
58
59
60
61
62
63 GraphicPoint::GraphicPoint()
64 {
65         freq = 0;
66         value = 0.0;
67 }
68
69
70
71
72
73
74
75
76 GraphicConfig::GraphicConfig()
77 {
78         window_size = 4096;
79 //      wetness = INFINITYGAIN;
80 }
81
82 GraphicConfig::~GraphicConfig()
83 {
84         points.remove_all_objects();
85 }
86
87
88 int GraphicConfig::equivalent(GraphicConfig &that)
89 {
90         if(that.points.size() != points.size() ||
91                 window_size != that.window_size) return 0;
92
93         for(int i = 0; i < points.size(); i++)
94         {
95                 if(that.points.get(i)->freq != points.get(i)->freq ||
96                         !EQUIV(that.points.get(i)->value, points.get(i)->value))
97                         return 0;
98         }
99
100
101         return 1;
102 }
103
104 void GraphicConfig::copy_from(GraphicConfig &that)
105 {
106         points.remove_all_objects();
107         for(int i = 0; i < that.points.size(); i++)
108         {
109                 GraphicPoint *point;
110                 points.append(point = new GraphicPoint);
111                 point->freq = that.points.get(i)->freq;
112                 point->value = that.points.get(i)->value;
113         }
114
115         window_size = that.window_size;
116 }
117
118 void GraphicConfig::interpolate(GraphicConfig &prev,
119         GraphicConfig &next,
120         int64_t prev_frame,
121         int64_t next_frame,
122         int64_t current_frame)
123 {
124         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
125         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
126
127 // Get current set of points from previous configuration
128         copy_from(prev);
129
130
131 // Interpolate between current set of points and next set
132         for(int i = 0; i < MIN(next.points.size(), points.size()); i++)
133         {
134                 points.get(i)->freq = (int)(prev.points.get(i)->freq *
135                         prev_scale +
136                         next.points.get(i)->freq *
137                         next_scale);
138                 points.get(i)->value = prev.points.get(i)->value *
139                         prev_scale +
140                         next.points.get(i)->value *
141                         next_scale;
142         }
143 }
144
145
146 void GraphicConfig::delete_point(int number)
147 {
148         points.remove_object_number(number);
149 }
150
151
152
153
154
155
156
157 GraphicCanvas::GraphicCanvas(GraphicEQ *plugin,
158         GraphicGUI *gui,
159         int x,
160         int y,
161         int w,
162         int h)
163  : BC_SubWindow(x,
164         y,
165         w,
166         h,
167         BLACK)
168 {
169         this->plugin = plugin;
170         this->gui = gui;
171         state = GraphicCanvas::NONE;
172 }
173
174 GraphicCanvas::~GraphicCanvas()
175 {
176         temp_points.remove_all_objects();
177 }
178
179 int GraphicCanvas::button_press_event()
180 {
181         process(1, 0, 0);
182         if(state == GraphicCanvas::DRAG_POINT)
183                 return 1;
184         else
185                 return 0;
186 }
187
188 int GraphicCanvas::cursor_motion_event()
189 {
190         process(0, 1, 0);
191
192         if(state == GraphicCanvas::DRAG_POINT)
193         {
194                 return 1;
195         }
196         else
197                 return 0;
198 }
199
200 int GraphicCanvas::button_release_event()
201 {
202         if(state == GraphicCanvas::DRAG_POINT && plugin->active_point >= 0)
203         {
204 // Delete point if out of order
205                 int point_number = plugin->active_point;
206                 GraphicPoint *active_point = temp_points.get(point_number);
207
208
209                 for(int i = 0; i < temp_points.size(); i++)
210                 {
211                         GraphicPoint *point = temp_points.get(i);
212                         if((point->freq <= active_point->freq && i > point_number) ||
213                                 (point->freq >= active_point->freq && i < point_number))
214                         {
215                                 temp_points.remove_object_number(point_number);
216                                 plugin->active_point = -1;
217                                 process(0, 0, 1);
218                                 break;
219                         }
220                 }
221
222                 save_temps();
223                 plugin->send_configure_change();
224         }
225
226         state = GraphicCanvas::NONE;
227         return 0;
228 }
229
230 #define BOX_SIZE xS(10)
231
232 int GraphicCanvas::freq_to_y(int freq,
233         ArrayList<GraphicPoint*> *points,
234         double *envelope)
235 {
236         int center_y = get_h() / 2;
237         double magnitude = plugin->freq_to_magnitude(freq, points, envelope);
238         double magnitude_db = DB::todb(magnitude);
239         if(magnitude_db < -MAXMAGNITUDE) magnitude_db = -MAXMAGNITUDE;
240         int y = (int)(center_y - magnitude_db * center_y / MAXMAGNITUDE);
241 //printf("GraphicCanvas::freq_to_y magnitude=%f magnitude_db=%f y=%d\n",
242 //magnitude, magnitude_db, y);
243         return y;
244 }
245
246
247 void GraphicCanvas::new_temps()
248 {
249 // Copy configuration from plugin for editing
250         temp_points.remove_all_objects();
251         for(int i = 0; i < plugin->config.points.size(); i++)
252         {
253                 GraphicPoint *point = new GraphicPoint;
254                 *point = *plugin->config.points.get(i);
255                 temp_points.append(point);
256         }
257
258         plugin->calculate_envelope(&temp_points, temp_envelope);
259 }
260
261 void GraphicCanvas::save_temps()
262 {
263         plugin->config.points.remove_all_objects();
264         for(int i = 0; i < temp_points.size(); i++)
265         {
266                 GraphicPoint *point;
267                 plugin->config.points.append(point = new GraphicPoint);
268                 *point = *temp_points.get(i);
269         }
270 }
271
272
273 void GraphicCanvas::insert_point(GraphicPoint *point)
274 {
275         int done = 0;
276
277         temp_points.append(point);
278         while(!done)
279         {
280                 done = 1;
281                 for(int i = 0; i < temp_points.size() - 1; i++)
282                 {
283                         if(temp_points.get(i)->freq > temp_points.get(i + 1)->freq)
284                         {
285                                 GraphicPoint *point = temp_points.get(i);
286                                 temp_points.set(i, temp_points.get(i + 1));
287                                 temp_points.set(i + 1, point);
288                                 done = 0;
289                         }
290                 }
291         }
292 }
293
294
295 void GraphicCanvas::process(int buttonpress, int motion, int draw)
296 {
297         int got_button = 0;
298         int center_y = get_h() / 2;
299         int out_of_order = 0;
300         ArrayList<GraphicPoint*> *points;
301         double *envelope;
302         //const int debug = 0;
303
304
305         if(state == GraphicCanvas::NONE)
306         {
307                 points = &plugin->config.points;
308                 envelope = plugin->envelope;
309         }
310         else
311         {
312                 points = &temp_points;
313                 envelope = temp_envelope;
314         }
315
316         plugin->calculate_envelope(points, envelope);
317
318
319 // spectrogram
320         if(draw)
321         {
322                 clear_box(0, 0, get_w(), get_h());
323                 int niquist = plugin->PluginAClient::project_sample_rate / 2;
324
325 // delete frames up to current tracking position
326                 double tracking_position = plugin->get_tracking_position();
327                 GraphicGUIFrame *frame = (GraphicGUIFrame *)
328                         plugin->get_gui_frame(tracking_position, 1);
329                 if( frame ) {
330                         int y1 = 0, y2 = 0;
331                         set_color(MEGREY);
332
333                         for(int i = 0; i < get_w(); i++)
334                         {
335                                 int freq = Freq::tofreq(i * TOTALFREQS / get_w());
336                                 int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
337                                 if(index < frame->window_size / 2)
338                                 {
339                                         double magnitude = frame->data[index] /
340                                                 frame->freq_max *
341                                                 frame->time_max;
342                                         y2 = (int)(get_h() -
343                                                 (DB::todb(magnitude) - INFINITYGAIN) *
344                                                 get_h() /
345                                                 -INFINITYGAIN);
346                                         CLAMP(y2, 0, get_h() - 1);
347                                         if(i > 0)
348                                         {
349                                                 draw_line(i - 1, y1, i, y2);
350 //printf(" %.0f", frame->data[index]);
351                                         }
352                                         y1 = y2;
353                                 }
354                         }
355                         delete frame;
356                 }
357         }
358
359
360 // Determine if active point is out of order
361         if(plugin->active_point_exists())
362         {
363                 GraphicPoint *active_point = points->get(plugin->active_point);
364                 for(int i = 0; i < points->size(); i++)
365                 {
366                         if(i == plugin->active_point)
367                         {
368                                 if( (i < points->size() - 1 &&
369                                         active_point->freq >= points->get(i + 1)->freq) ||
370                                     (i > 0 &&
371                                         active_point->freq <= points->get(i - 1)->freq) )
372                                 {
373                                         out_of_order = 1;
374                                 }
375                                 break;
376                         }
377                 }
378         }
379
380
381         if(motion)
382         {
383                 if(state == GraphicCanvas::DRAG_POINT)
384                 {
385                         int point_x = get_cursor_x() + x_diff;
386                         int point_y = get_cursor_y() + y_diff;
387                         CLAMP(point_x, 0, get_w());
388                         CLAMP(point_y, 0, get_h());
389
390                         int frequency = Freq::tofreq(point_x * TOTALFREQS / get_w());
391                         double magnitude_db = (double)(center_y - point_y) * MAXMAGNITUDE / center_y;
392                         int minfreq = Freq::tofreq(0);
393                         int maxfreq = Freq::tofreq(TOTALFREQS - 1);
394
395                         CLAMP(frequency, minfreq, maxfreq);
396                         CLAMP(magnitude_db, -MAXMAGNITUDE, MAXMAGNITUDE);
397                         if(plugin->active_point >= 0)
398                         {
399                                 GraphicPoint *active_point = points->get(plugin->active_point);
400                                 active_point->freq = frequency;
401                                 active_point->value = magnitude_db;
402                         }
403
404 // Redraw with new value
405                         process(0, 0, 1);
406                         save_temps();
407                         plugin->send_configure_change();
408                         gui->update_textboxes();
409                         return;
410                 }
411         }
412
413 // Magnitude bars
414         if(draw)
415         {
416                 set_color(GREEN);
417                 set_line_dashes(1);
418                 for(int i = 1; i < MAJOR_DIVISIONS; i++)
419                 {
420                         int y = i * get_h() / (MAJOR_DIVISIONS - 1);
421                         draw_line(0, y, get_w(), y);
422                 }
423                 set_line_dashes(0);
424         }
425
426         int y1 = 0;
427         if(draw) set_color(WHITE);
428
429 // Control points, cursor change and control point selection
430         int new_cursor = CROSS_CURSOR;
431         for(int i = 0; i < points->size(); i++)
432         {
433                 GraphicPoint *point = points->get(i);
434                 int x = Freq::fromfreq(point->freq) * get_w() / TOTALFREQS;
435                 int y = freq_to_y(point->freq, points, envelope);
436
437                 if(draw)
438                 {
439                         y1 = y;
440 // Draw point under cursor if out of order
441                         if(i == plugin->active_point && out_of_order)
442                                 y1 = get_cursor_y() + y_diff;
443
444                         if(i == plugin->active_point)
445                                 draw_box(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
446                         else
447                                 draw_rectangle(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
448                 }
449
450                 if(motion &&
451                         state == GraphicCanvas::NONE &&
452                         is_event_win() &&
453                         cursor_inside())
454                 {
455                         if(get_cursor_x() >= x - BOX_SIZE / 2 &&
456                                 get_cursor_y() >= y - BOX_SIZE / 2 &&
457                                 get_cursor_x() < x + BOX_SIZE / 2 &&
458                                 get_cursor_y() < y + BOX_SIZE / 2)
459                         {
460                                 new_cursor = UPRIGHT_ARROW_CURSOR;
461                         }
462                 }
463
464                 if(buttonpress &&
465                         state == GraphicCanvas::NONE &&
466                         is_event_win() &&
467                         cursor_inside() &&
468                         !got_button)
469                 {
470                         if(get_cursor_x() >= x - BOX_SIZE / 2 &&
471                                 get_cursor_y() >= y - BOX_SIZE / 2 &&
472                                 get_cursor_x() < x + BOX_SIZE / 2 &&
473                                 get_cursor_y() < y + BOX_SIZE / 2)
474                         {
475                                 plugin->active_point = i;
476                                 state = GraphicCanvas::DRAG_POINT;
477                                 new_temps();
478                                 points = &temp_points;
479                                 envelope = temp_envelope;
480
481                                 x_diff = x - get_cursor_x();
482                                 y_diff = y - get_cursor_y();
483                                 got_button = 1;
484                                 process(0, 0, 1);
485                                 save_temps();
486                                 plugin->send_configure_change();
487                                 gui->update_textboxes();
488                         }
489                 }
490         }
491
492         if(motion && new_cursor != get_cursor())
493         {
494                 set_cursor(new_cursor, 0, 1);
495         }
496
497 // Envelope line;
498         y1 = 0;
499         set_line_width(2);
500         for(int i = 0; i < get_w(); i++)
501         {
502                 int y = freq_to_y(Freq::tofreq(i * TOTALFREQS / get_w()),
503                         points,
504                         envelope);
505
506                 if(draw)
507                 {
508                         if(i > 0) draw_line(i - 1, y1, i, y);
509                 }
510
511
512                 y1 = y;
513         }
514         set_line_width(1);
515
516         if(buttonpress && !got_button)
517         {
518                 if(is_event_win() && cursor_inside())
519                 {
520                         GraphicPoint *new_point = new GraphicPoint;
521                         new_point->freq = Freq::tofreq(get_cursor_x() *
522                                 TOTALFREQS /
523                                 get_w());
524                         new_point->value = (double)(center_y - get_cursor_y()) *
525                                 MAXMAGNITUDE /
526                                 center_y;
527                         state = GraphicCanvas::DRAG_POINT;
528                         new_temps();
529                         points = &temp_points;
530                         envelope = temp_envelope;
531
532                         insert_point(new_point);
533                         plugin->active_point = points->number_of(new_point);
534                         x_diff = 0;
535                         y_diff = 0;
536
537 // Redraw with new point
538                         process(0, 0, 1);
539                         save_temps();
540                         plugin->send_configure_change();
541                         gui->update_textboxes();
542                 }
543         }
544
545
546         if(draw)
547         {
548                 flash();
549         }
550 }
551
552
553
554
555
556
557
558
559
560 FreqTextBox::FreqTextBox(GraphicEQ *plugin,
561         GraphicGUI *gui,
562         int x,
563         int y,
564         int w)
565  : BC_TextBox(x, y, w, 1, "")
566 {
567         this->plugin = plugin;
568         this->gui = gui;
569 }
570
571 int FreqTextBox::handle_event()
572 {
573         if(plugin->active_point_exists())
574         {
575                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
576                 if(atoi(get_text()) != active_point->freq)
577                 {
578                         active_point->freq = atoi(get_text());
579                         gui->update_canvas();
580                         plugin->send_configure_change();
581                         return 1;
582                 }
583         }
584
585         return 0;
586 }
587
588 void FreqTextBox::update(int freq)
589 {
590         if(plugin->active_point_exists())
591         {
592                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
593                 if(atoi(get_text()) != active_point->freq)
594                 {
595                         char string[BCTEXTLEN];
596                         sprintf(string, "%d", active_point->freq);
597                         BC_TextBox::update(string);
598                 }
599         }
600 }
601
602
603
604
605 ValueTextBox::ValueTextBox(GraphicEQ *plugin,
606         GraphicGUI *gui,
607         int x,
608         int y,
609         int w)
610  : BC_TextBox(x, y, w, 1, "")
611 {
612         this->plugin = plugin;
613         this->gui = gui;
614 }
615
616 int ValueTextBox::handle_event()
617 {
618         if(plugin->active_point_exists())
619         {
620                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
621                 if(!EQUIV(atof(get_text()), active_point->value))
622                 {
623                         active_point->value = atof(get_text());
624                         gui->update_canvas();
625                         plugin->send_configure_change();
626                         return 1;
627                 }
628         }
629
630         return 0;
631 }
632
633 void ValueTextBox::update(float value)
634 {
635         if(plugin->active_point_exists())
636         {
637                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
638                 if(!EQUIV(atof(get_text()), active_point->value))
639                 {
640                         char string[BCTEXTLEN];
641                         sprintf(string, "%.04f", active_point->value);
642                         BC_TextBox::update(string);
643                 }
644         }
645 }
646
647 GraphicReset::GraphicReset(GraphicEQ *plugin,
648         GraphicGUI *gui,
649         int x,
650         int y)
651  : BC_GenericButton(x, y, _("Reset"))
652 {
653         this->plugin = plugin;
654         this->gui = gui;
655 }
656
657 int GraphicReset::handle_event()
658 {
659         plugin->config.points.remove_all_objects();
660         plugin->active_point = -1;
661         gui->update_canvas();
662         gui->update_textboxes();
663         plugin->send_configure_change();
664         return 1;
665 }
666
667
668
669
670
671 GraphicSize::GraphicSize(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
672  : BC_PopupMenu(x, y, xS(120), "4096", 1)
673 {
674         this->plugin = plugin;
675         this->window = window;
676 }
677
678
679 int GraphicSize::handle_event()
680 {
681         plugin->config.window_size = atoi(get_text());
682         plugin->send_configure_change();
683
684         window->update_canvas();
685         return 1;
686 }
687
688 void GraphicSize::create_objects()
689 {
690         add_item(new BC_MenuItem("2048"));
691         add_item(new BC_MenuItem("4096"));
692         add_item(new BC_MenuItem("8192"));
693         add_item(new BC_MenuItem("16384"));
694         add_item(new BC_MenuItem("32768"));
695         add_item(new BC_MenuItem("65536"));
696         add_item(new BC_MenuItem("131072"));
697         add_item(new BC_MenuItem("262144"));
698 }
699
700 void GraphicSize::update(int size)
701 {
702         char string[BCTEXTLEN];
703         sprintf(string, "%d", size);
704         set_text(string);
705 }
706
707
708
709
710 //
711 //
712 // GraphicWetness::GraphicWetness(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
713 //  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
714 // {
715 //      this->plugin = plugin;
716 //      this->window = window;
717 // }
718 //
719 // int GraphicWetness::handle_event()
720 // {
721 //      plugin->config.wetness = get_value();
722 //      plugin->send_configure_change();
723 //      window->update_canvas();
724 //      return 1;
725 // }
726 //
727 //
728 //
729
730
731
732
733
734
735 GraphicGUI::GraphicGUI(GraphicEQ *plugin)
736  : PluginClientWindow(plugin,
737         plugin->w,
738         plugin->h,
739         xS(320),
740         yS(200),
741         1)
742 {
743         this->plugin = plugin;
744 }
745
746 GraphicGUI::~GraphicGUI()
747 {
748 }
749
750
751 void GraphicGUI::create_objects()
752 {
753         int margin = plugin->get_theme()->widget_border;
754         int x = get_text_width(SMALLFONT, "-00") + LINE_W4 + margin;
755         int y = margin;
756         int freq_h = get_text_height(SMALLFONT) + LINE_W4;
757
758         add_subwindow(canvas = new GraphicCanvas(plugin,
759                 this,
760                 x,
761                 y,
762                 get_w() - x - margin,
763                 get_h() -
764 //                      BC_Pot::calculate_h() -
765                         BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) -
766                         margin * 3 -
767                         y -
768                         freq_h));
769         y += canvas->get_h() + freq_h + margin;
770
771 //      int x1 = x;
772 //      int y1 = y;
773         add_subwindow(freq_title = new BC_Title(x, y, _("Frequency:")));
774         x += freq_title->get_w() + margin;
775         add_subwindow(freq_text = new FreqTextBox(plugin, this, x, y, xS(100)));
776         x += freq_text->get_w() + margin;
777
778         add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
779         x += level_title->get_w() + margin;
780         add_subwindow(value_text = new ValueTextBox(plugin, this, x, y, xS(100)));
781         x += value_text->get_w() + margin;
782
783         add_subwindow(reset = new GraphicReset(plugin, this, x, y));
784         x += reset->get_w() + margin;
785
786
787 //      x = x1;
788 //      y += value_text->get_h() + margin;
789
790         add_subwindow(size_title = new BC_Title(x, y, _("Window size:")));
791         x += size_title->get_w() + margin;
792         add_subwindow(size = new GraphicSize(this, plugin, x, y));
793         size->create_objects();
794         size->update(plugin->config.window_size);
795         x += size->get_w() + margin;
796
797 //      add_subwindow(title = new BC_Title(x, y, "Wetness:"));
798 //      x += title->get_w() + margin;
799 //      add_subwindow(wetness = new GraphicWetness(this, plugin,
800 //              x,
801 //              y));
802
803         draw_ticks();
804         update_canvas();
805         show_window();
806 }
807
808
809 int GraphicGUI::resize_event(int w, int h)
810 {
811         int difference = h - get_h();
812         int canvas_xdiff = get_w() - canvas->get_w();
813         int canvas_ydiff = get_h() - canvas->get_h();
814
815         canvas->reposition_window(canvas->get_x(),
816                 canvas->get_y(),
817                 w - canvas_xdiff,
818                 h - canvas_ydiff);
819         freq_text->reposition_window(freq_text->get_x(),
820                 freq_text->get_y() + difference);
821         value_text->reposition_window(value_text->get_x(),
822                 value_text->get_y() + difference);
823         freq_title->reposition_window(freq_title->get_x(),
824                 freq_title->get_y() + difference);
825         level_title->reposition_window(level_title->get_x(),
826                 level_title->get_y() + difference);
827         size_title->reposition_window(size_title->get_x(),
828                 size_title->get_y() + difference);
829         reset->reposition_window(reset->get_x(),
830                 reset->get_y() + difference);
831         size->reposition_window(size->get_x(),
832                 size->get_y() + difference);
833
834         draw_ticks();
835         update_canvas();
836         flash();
837
838         plugin->w = w;
839         plugin->h = h;
840         plugin->send_configure_change();
841         return 1;
842 }
843
844
845
846
847
848
849 int GraphicGUI::keypress_event()
850 {
851         if(get_keypress() == BACKSPACE ||
852                 get_keypress() == DELETE)
853         {
854                 if(plugin->active_point_exists())
855                 {
856                         int point_number = -1;
857                         for(int i = 0; i < plugin->config.points.size(); i++)
858                         {
859                                 if(i == plugin->active_point)
860                                 {
861                                         point_number = i;
862                                         break;
863                                 }
864                         }
865
866                         if(point_number >= 0)
867                         {
868                                 plugin->config.delete_point(point_number);
869                                 canvas->process(0, 0, 1);
870                                 plugin->send_configure_change();
871                                 return 1;
872                         }
873                 }
874         }
875         return 0;
876 }
877
878
879 void GraphicGUI::draw_ticks()
880 {
881         //int x = canvas->get_x() - 5 - get_text_width(SMALLFONT, "-00");
882         int y = canvas->get_y() - 1;
883         int x1 = canvas->get_x() - LINE_W3;
884         int x2 = canvas->get_x() - LINE_W2;
885         int x3 = canvas->get_x() - LINE_W1;
886         char string[BCTEXTLEN];
887
888 // Amplitude
889         set_font(SMALLFONT);
890         int major_division = canvas->get_h() / (MAJOR_DIVISIONS - 1);
891         for(int i = 0; i < MAJOR_DIVISIONS; i++)
892         {
893                 int current_db = (MAJOR_DIVISIONS - 1 - i) * (MAX_DB - MIN_DB) / (MAJOR_DIVISIONS - 1) + MIN_DB;
894                 if(current_db == MIN_DB)
895                         sprintf(string, "oo");
896                 else
897                 if(current_db <= 0.0)
898                         sprintf(string, "%d", current_db);
899                 else
900                         sprintf(string, "+%d", current_db);
901
902                 set_color(BLACK);
903                 int y1 = y + 1 + i * canvas->get_h() / (MAJOR_DIVISIONS - 1);
904                 int x4 = canvas->get_x() - LINE_W4 - get_text_width(SMALLFONT, string);
905                 draw_text(x4 + 1, y1 + get_text_ascent(SMALLFONT) / 2 + 1, string);
906                 draw_line(x1 + 1, y1 + 1, x3 + 1, y1 + 1);
907                 set_color(RED);
908                 draw_text(x4, y1 + get_text_ascent(SMALLFONT) / 2, string);
909                 draw_line(x1, y1, x3, y1);
910
911
912                 if(i < MAJOR_DIVISIONS - 1)
913                 {
914                         for(int j = 0; j < MINOR_DIVISIONS; j++)
915                         {
916                                 int y2 = y1 + j * major_division / MINOR_DIVISIONS;
917                                 set_color(BLACK);
918                                 draw_line(x2 + 1, y2 + 1, x3 + 1, y2 + 1);
919                                 set_color(RED);
920                                 draw_line(x2, y2, x3, y2);
921                         }
922                 }
923         }
924
925
926 // Frequency
927         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
928         {
929                 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
930                 char string[BCTEXTLEN];
931                 x1 = canvas->get_x() + i * canvas->get_w() / MAJOR_DIVISIONS;
932                 int y1 = canvas->get_y() + canvas->get_h() + LINE_W1 - 1;
933                 sprintf(string, "%d", freq);
934                 int x2 = x1 - get_text_width(SMALLFONT, string);
935                 int y2 = canvas->get_y() + canvas->get_h() + LINE_W2 - 1;
936                 int y3 = canvas->get_y() + canvas->get_h() + LINE_W3 - 1;
937                 int y4 = canvas->get_y() + canvas->get_h() + get_text_height(SMALLFONT) + LINE_W4 - 1;
938
939                 set_color(BLACK);
940                 draw_text(x2 + 1, y4 + 1, string);
941                 draw_line(x1 + 1, y3 + 1, x1 + 1, y1 + 1);
942
943                 set_color(RED);
944                 draw_text(x2, y4, string);
945                 draw_line(x1, y3, x1, y1);
946
947                 if(i < MAJOR_DIVISIONS)
948                 {
949                         for(int j = 0; j < MINOR_DIVISIONS; j++)
950                         {
951                                 int x3 = (int)(x1 +
952                                         (canvas->get_w() / MAJOR_DIVISIONS) -
953                                         exp(-(double)j * 0.7) *
954                                         (canvas->get_w() / MAJOR_DIVISIONS));
955                                 set_color(BLACK);
956                                 draw_line(x3 + 1, y2 + 1, x3 + 1, y1 + 1);
957                                 set_color(RED);
958                                 draw_line(x3, y2, x3, y1);
959                         }
960                 }
961         }
962 }
963
964 void GraphicGUI::update_canvas()
965 {
966         canvas->process(0, 0, 1);
967 }
968
969 void GraphicGUI::update_textboxes()
970 {
971         if(plugin->active_point_exists())
972         {
973                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
974                 freq_text->update(active_point->freq);
975                 value_text->update(active_point->value);
976         }
977 }
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997 GraphicEQ::GraphicEQ(PluginServer *server)
998  : PluginAClient(server)
999 {
1000         last_frame = 0;
1001         fft = 0;
1002         need_reconfigure = 1;
1003         active_point = -1;
1004         w = xS(640);
1005         h = yS(480);
1006 }
1007
1008 GraphicEQ::~GraphicEQ()
1009 {
1010
1011         delete last_frame;
1012         if(fft) delete fft;
1013 }
1014
1015
1016 int GraphicEQ::is_realtime() { return 1; }
1017
1018 const char* GraphicEQ::plugin_title() { return N_("EQ Graphic"); }
1019
1020 NEW_WINDOW_MACRO(GraphicEQ, GraphicGUI)
1021
1022 LOAD_CONFIGURATION_MACRO(GraphicEQ, GraphicConfig)
1023
1024 int GraphicEQ::active_point_exists()
1025 {
1026         if(active_point >= 0 && active_point < config.points.size()) return 1;
1027         return 0;
1028 }
1029
1030
1031 void GraphicEQ::read_data(KeyFrame *keyframe)
1032 {
1033         FileXML input;
1034         int result = 0;
1035
1036         input.set_shared_input(keyframe->xbuf);
1037         config.points.remove_all_objects();
1038
1039         while(!result)
1040         {
1041                 result = input.read_tag();
1042
1043                 if(!result)
1044                 {
1045                         if(input.tag.title_is("GRAPHICEQ"))
1046                         {
1047                                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1048                                 if(is_defaults())
1049                                 {
1050                                         w = input.tag.get_property("W", w);
1051                                         h = input.tag.get_property("H", h);
1052                                 }
1053                         }
1054                         else
1055                         if(input.tag.title_is("POINT"))
1056                         {
1057                                 GraphicPoint *point;
1058                                 config.points.append(point = new GraphicPoint);
1059                                 point->freq = input.tag.get_property("X", 0);
1060                                 point->value = input.tag.get_property("Y", 0.0);
1061                         }
1062                 }
1063         }
1064
1065 //      if(!active_point_exists()) active_point = -1;
1066 }
1067
1068
1069
1070
1071 void GraphicEQ::save_data(KeyFrame *keyframe)
1072 {
1073         FileXML output;
1074         output.set_shared_output(keyframe->xbuf);
1075
1076         output.tag.set_title("GRAPHICEQ");
1077         output.tag.set_property("WINDOW_SIZE", config.window_size);
1078         output.tag.set_property("W", w);
1079         output.tag.set_property("H", h);
1080         output.append_tag();
1081         output.tag.set_title("/GRAPHICEQ");
1082         output.append_tag();
1083         output.append_newline();
1084
1085         for(int i = 0; i < config.points.total; i++)
1086         {
1087                 output.tag.set_title("POINT");
1088                 output.tag.set_property("X", config.points.values[i]->freq);
1089                 output.tag.set_property("Y", config.points.values[i]->value);
1090                 output.append_tag();
1091                 output.tag.set_title("/POINT");
1092                 output.append_tag();
1093                 output.append_newline();
1094         }
1095
1096         output.terminate_string();
1097 }
1098
1099
1100 void GraphicEQ::update_gui()
1101 {
1102         if( !thread ) return;
1103         GraphicGUI *window = (GraphicGUI *)thread->window;
1104         window->lock_window("GraphicEQ::update_gui");
1105 //lock here for points, needed by window cursor_motion callback
1106 //  deleted in load_configuration by read_data
1107         if( load_configuration() &&
1108             window->canvas->state != GraphicCanvas::DRAG_POINT ) {
1109                 window->update_canvas();
1110                 window->update_textboxes();
1111         }
1112         else if( pending_gui_frames() ) {
1113                         window->update_canvas();
1114         }
1115         window->unlock_window();
1116 }
1117
1118 void GraphicEQ::reconfigure()
1119 {
1120         if(fft && fft->window_size != config.window_size)
1121         {
1122                 delete fft;
1123                 fft = 0;
1124         }
1125
1126         if(!fft)
1127         {
1128                 fft = new GraphicFFT(this);
1129                 fft->initialize(config.window_size);
1130         }
1131
1132         calculate_envelope(&config.points, envelope);
1133
1134         for(int i = 0; i < config.window_size / 2; i++)
1135         {
1136                 if(envelope[i] < 0) envelope[i] = 0;
1137         }
1138
1139         need_reconfigure = 0;
1140 }
1141
1142 int GraphicEQ::process_buffer(int64_t size,
1143         Samples *buffer,
1144         int64_t start_position,
1145         int sample_rate)
1146 {
1147         need_reconfigure |= load_configuration();
1148         if(need_reconfigure) reconfigure();
1149
1150         fft->process_buffer(start_position, size, buffer, get_direction());
1151
1152
1153         return 0;
1154 }
1155
1156
1157 double GraphicEQ::freq_to_magnitude(double frequency,
1158         ArrayList<GraphicPoint*> *points,
1159         double *envelope)
1160 {
1161 // Calculate using control points
1162         for(int i = 0; i < points->size(); i++)
1163         {
1164                 GraphicPoint *point = points->get(i);
1165                 if(point->freq == (int)frequency)
1166                 {
1167                         return DB::fromdb(point->value);
1168                 }
1169         }
1170
1171
1172         int nyquist = PluginAClient::project_sample_rate / 2;
1173         int slot = (int)(frequency * config.window_size / 2 / nyquist);
1174         if(slot >= config.window_size / 2) slot = config.window_size / 2 - 1;
1175 //printf("GraphicEQ::freq_to_db %d %f\n", slot, envelope[slot]);
1176         return envelope[slot];
1177 }
1178
1179
1180 void GraphicEQ::calculate_envelope(ArrayList<GraphicPoint*> *points,
1181                 double *envelope)
1182 {
1183         int niquist = PluginAClient::project_sample_rate / 2;
1184
1185 // Make temporary list of just points in order
1186         ArrayList<GraphicPoint*> temp_points;
1187         for(int i = 0; i < points->size(); i++)
1188         {
1189                 temp_points.append(points->get(i));
1190         }
1191
1192         for(int i = 0; i < temp_points.size(); i++)
1193         {
1194                 GraphicPoint *point = temp_points.get(i);
1195                 if(i == active_point)
1196                 {
1197                         GraphicPoint *prev_point = 0;
1198                         GraphicPoint *next_point = 0;
1199                         if(i >= 1) prev_point = temp_points.get(i - 1);
1200                         if(i < temp_points.size() - 1) next_point = temp_points.get(i + 1);
1201                         if( (prev_point && prev_point->freq >= point->freq) ||
1202                                 (next_point && next_point->freq <= point->freq) )
1203                                 temp_points.remove_number(i);
1204                         break;
1205                 }
1206         }
1207
1208
1209 // Join each point
1210         if(temp_points.size())
1211         {
1212                 GraphicPoint *first_point = temp_points.get(0);
1213                 GraphicPoint *last_point = temp_points.get(temp_points.size() - 1);
1214                 for(int i = 0; i < config.window_size / 2; i++)
1215                 {
1216                         int freq = i * niquist / (config.window_size / 2);
1217                         if(freq <= first_point->freq)
1218                                 envelope[i] = first_point->value;
1219                         else
1220                         if(freq >= last_point->freq)
1221                                 envelope[i] = last_point->value;
1222                         else
1223                         {
1224                                 GraphicPoint *point1 = first_point;
1225                                 GraphicPoint *point2 = last_point;
1226                                 for(int j = 0; j < temp_points.size(); j++)
1227                                 {
1228                                         if(temp_points.get(j)->freq <= freq)
1229                                                 point1 = temp_points.get(j);
1230                                         else
1231                                         {
1232                                                 point2 = temp_points.get(j);
1233                                                 break;
1234                                         }
1235                                 }
1236
1237                                 if(point2->freq != point1->freq)
1238                                 {
1239                                         int freqslot1 = Freq::fromfreq(point1->freq);
1240                                         int freqslot2 = Freq::fromfreq(point2->freq);
1241                                         int freqslot = Freq::fromfreq(freq);
1242
1243                                         envelope[i] = (double)(freqslot - freqslot1) *
1244                                                 (point2->value - point1->value) /
1245                                                 (freqslot2 - freqslot1) +
1246                                                 point1->value;
1247                                 }
1248                                 else
1249                                         envelope[i] = point1->value;
1250                         }
1251
1252
1253                         if(envelope[i] < MIN_DB + 0.01)
1254                                 envelope[i] = 0;
1255                         else
1256                                 envelope[i] = DB::fromdb(envelope[i]);
1257                 }
1258         }
1259         else
1260         {
1261                 for(int i = 0; i < config.window_size / 2; i++)
1262                 {
1263                         envelope[i] = 1.0;
1264                 }
1265         }
1266 }
1267
1268
1269 GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate)
1270  : PluginClientFrame()
1271 {
1272         this->window_size = window_size;
1273         data_size = window_size / 2;
1274         data = new double[data_size];
1275         freq_max = 0;
1276         time_max = 0;
1277 }
1278
1279 GraphicGUIFrame::~GraphicGUIFrame()
1280 {
1281         delete [] data;
1282 }
1283
1284
1285 GraphicFFT::GraphicFFT(GraphicEQ *plugin)
1286  : CrossfadeFFT()
1287 {
1288         this->plugin = plugin;
1289 }
1290
1291 GraphicFFT::~GraphicFFT()
1292 {
1293 }
1294
1295
1296 int GraphicFFT::signal_process()
1297 {
1298 // Create new frame for updating GUI
1299         frame = new GraphicGUIFrame(window_size,
1300                 plugin->PluginAClient::project_sample_rate);
1301         plugin->add_gui_frame(frame);
1302
1303         double freq_max = 0;
1304         for(int i = 0; i < window_size / 2; i++)
1305         {
1306                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
1307                 double angle = atan2(freq_imag[i], freq_real[i]);
1308                 freq_real[i] = result * cos(angle);
1309                 freq_imag[i] = result * sin(angle);
1310                 frame->data[i] = result;
1311                 if(result > freq_max) freq_max = result;
1312         }
1313         frame->freq_max = freq_max;
1314
1315         symmetry(window_size, freq_real, freq_imag);
1316
1317         return 0;
1318 }
1319
1320 int GraphicFFT::post_process()
1321 {
1322         double time_max = 0;
1323         for(int i = 0; i < window_size; i++)
1324         {
1325                 if(output_real[i] > time_max) time_max = output_real[i];
1326         }
1327         frame->time_max = time_max;
1328         return 0;
1329 }
1330
1331
1332
1333 int GraphicFFT::read_samples(int64_t output_sample,
1334         int samples,
1335         Samples *buffer)
1336 {
1337         return plugin->read_samples(buffer,
1338                 0,
1339                 plugin->get_samplerate(),
1340                 output_sample,
1341                 samples);
1342 }
1343
1344