add BC_SCALE env var for hi def monitors, cleanup theme data
[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
324
325                 int niquist = plugin->PluginAClient::project_sample_rate / 2;
326                 int total_frames = plugin->get_gui_update_frames();
327                 GraphicGUIFrame *frame = (GraphicGUIFrame*)plugin->get_gui_frame();
328
329                 if(frame)
330                 {
331                         delete plugin->last_frame;
332                         plugin->last_frame = frame;
333                 }
334                 else
335                 {
336                         frame = plugin->last_frame;
337                 }
338
339 // Draw most recent frame
340                 if(frame)
341                 {
342                         set_color(MEGREY);
343                         int y1 = 0;
344                         int y2 = 0;
345
346
347                         for(int i = 0; i < get_w(); i++)
348                         {
349                                 int freq = Freq::tofreq(i * TOTALFREQS / get_w());
350                                 int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
351                                 if(index < frame->window_size / 2)
352                                 {
353                                         double magnitude = frame->data[index] /
354                                                 frame->freq_max *
355                                                 frame->time_max;
356                                         y2 = (int)(get_h() -
357                                                 (DB::todb(magnitude) - INFINITYGAIN) *
358                                                 get_h() /
359                                                 -INFINITYGAIN);
360                                         CLAMP(y2, 0, get_h() - 1);
361                                         if(i > 0)
362                                         {
363                                                 draw_line(i - 1, y1, i, y2);
364 //printf(" %.0f", frame->data[index]);
365                                         }
366                                         y1 = y2;
367                                 }
368                         }
369 //printf( "\n");
370
371                         total_frames--;
372                 }
373
374
375
376
377
378
379 // Delete remaining frames
380                 while(total_frames > 0)
381                 {
382                         PluginClientFrame *frame = plugin->get_gui_frame();
383
384                         if(frame) delete frame;
385                         total_frames--;
386                 }
387         }
388
389
390 // Determine if active point is out of order
391         if(plugin->active_point_exists())
392         {
393                 GraphicPoint *active_point = points->get(plugin->active_point);
394                 for(int i = 0; i < points->size(); i++)
395                 {
396                         if(i == plugin->active_point)
397                         {
398                                 if( (i < points->size() - 1 &&
399                                         active_point->freq >= points->get(i + 1)->freq) ||
400                                     (i > 0 &&
401                                         active_point->freq <= points->get(i - 1)->freq) )
402                                 {
403                                         out_of_order = 1;
404                                 }
405                                 break;
406                         }
407                 }
408         }
409
410
411         if(motion)
412         {
413                 if(state == GraphicCanvas::DRAG_POINT)
414                 {
415                         int point_x = get_cursor_x() + x_diff;
416                         int point_y = get_cursor_y() + y_diff;
417                         CLAMP(point_x, 0, get_w());
418                         CLAMP(point_y, 0, get_h());
419
420                         int frequency = Freq::tofreq(point_x * TOTALFREQS / get_w());
421                         double magnitude_db = (double)(center_y - point_y) * MAXMAGNITUDE / center_y;
422                         int minfreq = Freq::tofreq(0);
423                         int maxfreq = Freq::tofreq(TOTALFREQS - 1);
424
425                         CLAMP(frequency, minfreq, maxfreq);
426                         CLAMP(magnitude_db, -MAXMAGNITUDE, MAXMAGNITUDE);
427                         if(plugin->active_point >= 0)
428                         {
429                                 GraphicPoint *active_point = points->get(plugin->active_point);
430                                 active_point->freq = frequency;
431                                 active_point->value = magnitude_db;
432                         }
433
434 // Redraw with new value
435                         process(0, 0, 1);
436                         save_temps();
437                         plugin->send_configure_change();
438                         gui->update_textboxes();
439                         return;
440                 }
441         }
442
443 // Magnitude bars
444         if(draw)
445         {
446                 set_color(GREEN);
447                 set_line_dashes(1);
448                 for(int i = 1; i < MAJOR_DIVISIONS; i++)
449                 {
450                         int y = i * get_h() / (MAJOR_DIVISIONS - 1);
451                         draw_line(0, y, get_w(), y);
452                 }
453                 set_line_dashes(0);
454         }
455
456         int y1 = 0;
457         if(draw) set_color(WHITE);
458
459 // Control points, cursor change and control point selection
460         int new_cursor = CROSS_CURSOR;
461         for(int i = 0; i < points->size(); i++)
462         {
463                 GraphicPoint *point = points->get(i);
464                 int x = Freq::fromfreq(point->freq) * get_w() / TOTALFREQS;
465                 int y = freq_to_y(point->freq, points, envelope);
466
467                 if(draw)
468                 {
469                         y1 = y;
470 // Draw point under cursor if out of order
471                         if(i == plugin->active_point && out_of_order)
472                                 y1 = get_cursor_y() + y_diff;
473
474                         if(i == plugin->active_point)
475                                 draw_box(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
476                         else
477                                 draw_rectangle(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
478                 }
479
480                 if(motion &&
481                         state == GraphicCanvas::NONE &&
482                         is_event_win() &&
483                         cursor_inside())
484                 {
485                         if(get_cursor_x() >= x - BOX_SIZE / 2 &&
486                                 get_cursor_y() >= y - BOX_SIZE / 2 &&
487                                 get_cursor_x() < x + BOX_SIZE / 2 &&
488                                 get_cursor_y() < y + BOX_SIZE / 2)
489                         {
490                                 new_cursor = UPRIGHT_ARROW_CURSOR;
491                         }
492                 }
493
494                 if(buttonpress &&
495                         state == GraphicCanvas::NONE &&
496                         is_event_win() &&
497                         cursor_inside() &&
498                         !got_button)
499                 {
500                         if(get_cursor_x() >= x - BOX_SIZE / 2 &&
501                                 get_cursor_y() >= y - BOX_SIZE / 2 &&
502                                 get_cursor_x() < x + BOX_SIZE / 2 &&
503                                 get_cursor_y() < y + BOX_SIZE / 2)
504                         {
505                                 plugin->active_point = i;
506                                 state = GraphicCanvas::DRAG_POINT;
507                                 new_temps();
508                                 points = &temp_points;
509                                 envelope = temp_envelope;
510
511                                 x_diff = x - get_cursor_x();
512                                 y_diff = y - get_cursor_y();
513                                 got_button = 1;
514                                 process(0, 0, 1);
515                                 save_temps();
516                                 plugin->send_configure_change();
517                                 gui->update_textboxes();
518                         }
519                 }
520         }
521
522         if(motion && new_cursor != get_cursor())
523         {
524                 set_cursor(new_cursor, 0, 1);
525         }
526
527 // Envelope line;
528         y1 = 0;
529         set_line_width(2);
530         for(int i = 0; i < get_w(); i++)
531         {
532                 int y = freq_to_y(Freq::tofreq(i * TOTALFREQS / get_w()),
533                         points,
534                         envelope);
535
536                 if(draw)
537                 {
538                         if(i > 0) draw_line(i - 1, y1, i, y);
539                 }
540
541
542                 y1 = y;
543         }
544         set_line_width(1);
545
546         if(buttonpress && !got_button)
547         {
548                 if(is_event_win() && cursor_inside())
549                 {
550                         GraphicPoint *new_point = new GraphicPoint;
551                         new_point->freq = Freq::tofreq(get_cursor_x() *
552                                 TOTALFREQS /
553                                 get_w());
554                         new_point->value = (double)(center_y - get_cursor_y()) *
555                                 MAXMAGNITUDE /
556                                 center_y;
557                         state = GraphicCanvas::DRAG_POINT;
558                         new_temps();
559                         points = &temp_points;
560                         envelope = temp_envelope;
561
562                         insert_point(new_point);
563                         plugin->active_point = points->number_of(new_point);
564                         x_diff = 0;
565                         y_diff = 0;
566
567 // Redraw with new point
568                         process(0, 0, 1);
569                         save_temps();
570                         plugin->send_configure_change();
571                         gui->update_textboxes();
572                 }
573         }
574
575
576         if(draw)
577         {
578                 flash();
579         }
580 }
581
582
583
584
585
586
587
588
589
590 FreqTextBox::FreqTextBox(GraphicEQ *plugin,
591         GraphicGUI *gui,
592         int x,
593         int y,
594         int w)
595  : BC_TextBox(x, y, w, 1, "")
596 {
597         this->plugin = plugin;
598         this->gui = gui;
599 }
600
601 int FreqTextBox::handle_event()
602 {
603         if(plugin->active_point_exists())
604         {
605                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
606                 if(atoi(get_text()) != active_point->freq)
607                 {
608                         active_point->freq = atoi(get_text());
609                         gui->update_canvas();
610                         plugin->send_configure_change();
611                         return 1;
612                 }
613         }
614
615         return 0;
616 }
617
618 void FreqTextBox::update(int freq)
619 {
620         if(plugin->active_point_exists())
621         {
622                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
623                 if(atoi(get_text()) != active_point->freq)
624                 {
625                         char string[BCTEXTLEN];
626                         sprintf(string, "%d", active_point->freq);
627                         BC_TextBox::update(string);
628                 }
629         }
630 }
631
632
633
634
635 ValueTextBox::ValueTextBox(GraphicEQ *plugin,
636         GraphicGUI *gui,
637         int x,
638         int y,
639         int w)
640  : BC_TextBox(x, y, w, 1, "")
641 {
642         this->plugin = plugin;
643         this->gui = gui;
644 }
645
646 int ValueTextBox::handle_event()
647 {
648         if(plugin->active_point_exists())
649         {
650                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
651                 if(!EQUIV(atof(get_text()), active_point->value))
652                 {
653                         active_point->value = atof(get_text());
654                         gui->update_canvas();
655                         plugin->send_configure_change();
656                         return 1;
657                 }
658         }
659
660         return 0;
661 }
662
663 void ValueTextBox::update(float value)
664 {
665         if(plugin->active_point_exists())
666         {
667                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
668                 if(!EQUIV(atof(get_text()), active_point->value))
669                 {
670                         char string[BCTEXTLEN];
671                         sprintf(string, "%.04f", active_point->value);
672                         BC_TextBox::update(string);
673                 }
674         }
675 }
676
677 GraphicReset::GraphicReset(GraphicEQ *plugin,
678         GraphicGUI *gui,
679         int x,
680         int y)
681  : BC_GenericButton(x, y, _("Reset"))
682 {
683         this->plugin = plugin;
684         this->gui = gui;
685 }
686
687 int GraphicReset::handle_event()
688 {
689         plugin->config.points.remove_all_objects();
690         plugin->active_point = -1;
691         gui->update_canvas();
692         gui->update_textboxes();
693         plugin->send_configure_change();
694         return 1;
695 }
696
697
698
699
700
701 GraphicSize::GraphicSize(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
702  : BC_PopupMenu(x, y, xS(100), "4096", 1)
703 {
704         this->plugin = plugin;
705         this->window = window;
706 }
707
708
709 int GraphicSize::handle_event()
710 {
711         plugin->config.window_size = atoi(get_text());
712         plugin->send_configure_change();
713
714         window->update_canvas();
715         return 1;
716 }
717
718 void GraphicSize::create_objects()
719 {
720         add_item(new BC_MenuItem("2048"));
721         add_item(new BC_MenuItem("4096"));
722         add_item(new BC_MenuItem("8192"));
723         add_item(new BC_MenuItem("16384"));
724         add_item(new BC_MenuItem("32768"));
725         add_item(new BC_MenuItem("65536"));
726         add_item(new BC_MenuItem("131072"));
727         add_item(new BC_MenuItem("262144"));
728 }
729
730 void GraphicSize::update(int size)
731 {
732         char string[BCTEXTLEN];
733         sprintf(string, "%d", size);
734         set_text(string);
735 }
736
737
738
739
740 //
741 //
742 // GraphicWetness::GraphicWetness(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
743 //  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
744 // {
745 //      this->plugin = plugin;
746 //      this->window = window;
747 // }
748 //
749 // int GraphicWetness::handle_event()
750 // {
751 //      plugin->config.wetness = get_value();
752 //      plugin->send_configure_change();
753 //      window->update_canvas();
754 //      return 1;
755 // }
756 //
757 //
758 //
759
760
761
762
763
764
765 GraphicGUI::GraphicGUI(GraphicEQ *plugin)
766  : PluginClientWindow(plugin,
767         plugin->w,
768         plugin->h,
769         xS(320),
770         yS(200),
771         1)
772 {
773         this->plugin = plugin;
774 }
775
776 GraphicGUI::~GraphicGUI()
777 {
778 }
779
780
781 void GraphicGUI::create_objects()
782 {
783         int margin = plugin->get_theme()->widget_border;
784         int x = get_text_width(SMALLFONT, "-00") + LINE_W4 + margin;
785         int y = margin;
786         int freq_h = get_text_height(SMALLFONT) + LINE_W4;
787
788         add_subwindow(canvas = new GraphicCanvas(plugin,
789                 this,
790                 x,
791                 y,
792                 get_w() - x - margin,
793                 get_h() -
794 //                      BC_Pot::calculate_h() -
795                         BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) -
796                         margin * 3 -
797                         y -
798                         freq_h));
799         y += canvas->get_h() + freq_h + margin;
800
801 //      int x1 = x;
802 //      int y1 = y;
803         add_subwindow(freq_title = new BC_Title(x, y, _("Frequency:")));
804         x += freq_title->get_w() + margin;
805         add_subwindow(freq_text = new FreqTextBox(plugin, this, x, y, xS(100)));
806         x += freq_text->get_w() + margin;
807
808         add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
809         x += level_title->get_w() + margin;
810         add_subwindow(value_text = new ValueTextBox(plugin, this, x, y, xS(100)));
811         x += value_text->get_w() + margin;
812
813         add_subwindow(reset = new GraphicReset(plugin, this, x, y));
814         x += reset->get_w() + margin;
815
816
817 //      x = x1;
818 //      y += value_text->get_h() + margin;
819
820         add_subwindow(size_title = new BC_Title(x, y, _("Window size:")));
821         x += size_title->get_w() + margin;
822         add_subwindow(size = new GraphicSize(this, plugin, x, y));
823         size->create_objects();
824         size->update(plugin->config.window_size);
825         x += size->get_w() + margin;
826
827 //      add_subwindow(title = new BC_Title(x, y, "Wetness:"));
828 //      x += title->get_w() + margin;
829 //      add_subwindow(wetness = new GraphicWetness(this, plugin,
830 //              x,
831 //              y));
832
833         draw_ticks();
834         update_canvas();
835         show_window();
836 }
837
838
839 int GraphicGUI::resize_event(int w, int h)
840 {
841         int difference = h - get_h();
842         int canvas_xdiff = get_w() - canvas->get_w();
843         int canvas_ydiff = get_h() - canvas->get_h();
844
845         canvas->reposition_window(canvas->get_x(),
846                 canvas->get_y(),
847                 w - canvas_xdiff,
848                 h - canvas_ydiff);
849         freq_text->reposition_window(freq_text->get_x(),
850                 freq_text->get_y() + difference);
851         value_text->reposition_window(value_text->get_x(),
852                 value_text->get_y() + difference);
853         freq_title->reposition_window(freq_title->get_x(),
854                 freq_title->get_y() + difference);
855         level_title->reposition_window(level_title->get_x(),
856                 level_title->get_y() + difference);
857         size_title->reposition_window(size_title->get_x(),
858                 size_title->get_y() + difference);
859         reset->reposition_window(reset->get_x(),
860                 reset->get_y() + difference);
861         size->reposition_window(size->get_x(),
862                 size->get_y() + difference);
863
864         draw_ticks();
865         update_canvas();
866         flash();
867
868         plugin->w = w;
869         plugin->h = h;
870         plugin->send_configure_change();
871         return 1;
872 }
873
874
875
876
877
878
879 int GraphicGUI::keypress_event()
880 {
881         if(get_keypress() == BACKSPACE ||
882                 get_keypress() == DELETE)
883         {
884                 if(plugin->active_point_exists())
885                 {
886                         int point_number = -1;
887                         for(int i = 0; i < plugin->config.points.size(); i++)
888                         {
889                                 if(i == plugin->active_point)
890                                 {
891                                         point_number = i;
892                                         break;
893                                 }
894                         }
895
896                         if(point_number >= 0)
897                         {
898                                 plugin->config.delete_point(point_number);
899                                 canvas->process(0, 0, 1);
900                                 plugin->send_configure_change();
901                                 return 1;
902                         }
903                 }
904         }
905         return 0;
906 }
907
908
909 void GraphicGUI::draw_ticks()
910 {
911         //int x = canvas->get_x() - 5 - get_text_width(SMALLFONT, "-00");
912         int y = canvas->get_y() - 1;
913         int x1 = canvas->get_x() - LINE_W3;
914         int x2 = canvas->get_x() - LINE_W2;
915         int x3 = canvas->get_x() - LINE_W1;
916         char string[BCTEXTLEN];
917
918 // Amplitude
919         set_font(SMALLFONT);
920         int major_division = canvas->get_h() / (MAJOR_DIVISIONS - 1);
921         for(int i = 0; i < MAJOR_DIVISIONS; i++)
922         {
923                 int current_db = (MAJOR_DIVISIONS - 1 - i) * (MAX_DB - MIN_DB) / (MAJOR_DIVISIONS - 1) + MIN_DB;
924                 if(current_db == MIN_DB)
925                         sprintf(string, "oo");
926                 else
927                 if(current_db <= 0.0)
928                         sprintf(string, "%d", current_db);
929                 else
930                         sprintf(string, "+%d", current_db);
931
932                 set_color(BLACK);
933                 int y1 = y + 1 + i * canvas->get_h() / (MAJOR_DIVISIONS - 1);
934                 int x4 = canvas->get_x() - LINE_W4 - get_text_width(SMALLFONT, string);
935                 draw_text(x4 + 1, y1 + get_text_ascent(SMALLFONT) / 2 + 1, string);
936                 draw_line(x1 + 1, y1 + 1, x3 + 1, y1 + 1);
937                 set_color(RED);
938                 draw_text(x4, y1 + get_text_ascent(SMALLFONT) / 2, string);
939                 draw_line(x1, y1, x3, y1);
940
941
942                 if(i < MAJOR_DIVISIONS - 1)
943                 {
944                         for(int j = 0; j < MINOR_DIVISIONS; j++)
945                         {
946                                 int y2 = y1 + j * major_division / MINOR_DIVISIONS;
947                                 set_color(BLACK);
948                                 draw_line(x2 + 1, y2 + 1, x3 + 1, y2 + 1);
949                                 set_color(RED);
950                                 draw_line(x2, y2, x3, y2);
951                         }
952                 }
953         }
954
955
956 // Frequency
957         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
958         {
959                 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
960                 char string[BCTEXTLEN];
961                 x1 = canvas->get_x() + i * canvas->get_w() / MAJOR_DIVISIONS;
962                 int y1 = canvas->get_y() + canvas->get_h() + LINE_W1 - 1;
963                 sprintf(string, "%d", freq);
964                 int x2 = x1 - get_text_width(SMALLFONT, string);
965                 int y2 = canvas->get_y() + canvas->get_h() + LINE_W2 - 1;
966                 int y3 = canvas->get_y() + canvas->get_h() + LINE_W3 - 1;
967                 int y4 = canvas->get_y() + canvas->get_h() + get_text_height(SMALLFONT) + LINE_W4 - 1;
968
969                 set_color(BLACK);
970                 draw_text(x2 + 1, y4 + 1, string);
971                 draw_line(x1 + 1, y3 + 1, x1 + 1, y1 + 1);
972
973                 set_color(RED);
974                 draw_text(x2, y4, string);
975                 draw_line(x1, y3, x1, y1);
976
977                 if(i < MAJOR_DIVISIONS)
978                 {
979                         for(int j = 0; j < MINOR_DIVISIONS; j++)
980                         {
981                                 int x3 = (int)(x1 +
982                                         (canvas->get_w() / MAJOR_DIVISIONS) -
983                                         exp(-(double)j * 0.7) *
984                                         (canvas->get_w() / MAJOR_DIVISIONS));
985                                 set_color(BLACK);
986                                 draw_line(x3 + 1, y2 + 1, x3 + 1, y1 + 1);
987                                 set_color(RED);
988                                 draw_line(x3, y2, x3, y1);
989                         }
990                 }
991         }
992 }
993
994 void GraphicGUI::update_canvas()
995 {
996         canvas->process(0, 0, 1);
997 }
998
999 void GraphicGUI::update_textboxes()
1000 {
1001         if(plugin->active_point_exists())
1002         {
1003                 GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
1004                 freq_text->update(active_point->freq);
1005                 value_text->update(active_point->value);
1006         }
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027 GraphicEQ::GraphicEQ(PluginServer *server)
1028  : PluginAClient(server)
1029 {
1030         last_frame = 0;
1031         fft = 0;
1032         need_reconfigure = 1;
1033         active_point = -1;
1034         w = xS(640);
1035         h = yS(480);
1036 }
1037
1038 GraphicEQ::~GraphicEQ()
1039 {
1040
1041         delete last_frame;
1042         if(fft) delete fft;
1043 }
1044
1045
1046 int GraphicEQ::is_realtime() { return 1; }
1047
1048 const char* GraphicEQ::plugin_title() { return N_("EQ Graphic"); }
1049
1050 NEW_WINDOW_MACRO(GraphicEQ, GraphicGUI)
1051
1052 LOAD_CONFIGURATION_MACRO(GraphicEQ, GraphicConfig)
1053
1054 int GraphicEQ::active_point_exists()
1055 {
1056         if(active_point >= 0 && active_point < config.points.size()) return 1;
1057         return 0;
1058 }
1059
1060
1061 void GraphicEQ::read_data(KeyFrame *keyframe)
1062 {
1063         FileXML input;
1064         int result = 0;
1065
1066         input.set_shared_input(keyframe->xbuf);
1067         config.points.remove_all_objects();
1068
1069         while(!result)
1070         {
1071                 result = input.read_tag();
1072
1073                 if(!result)
1074                 {
1075                         if(input.tag.title_is("GRAPHICEQ"))
1076                         {
1077                                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1078                                 if(is_defaults())
1079                                 {
1080                                         w = input.tag.get_property("W", w);
1081                                         h = input.tag.get_property("H", h);
1082                                 }
1083                         }
1084                         else
1085                         if(input.tag.title_is("POINT"))
1086                         {
1087                                 GraphicPoint *point;
1088                                 config.points.append(point = new GraphicPoint);
1089                                 point->freq = input.tag.get_property("X", 0);
1090                                 point->value = input.tag.get_property("Y", 0.0);
1091                         }
1092                 }
1093         }
1094
1095 //      if(!active_point_exists()) active_point = -1;
1096 }
1097
1098
1099
1100
1101 void GraphicEQ::save_data(KeyFrame *keyframe)
1102 {
1103         FileXML output;
1104         output.set_shared_output(keyframe->xbuf);
1105
1106         output.tag.set_title("GRAPHICEQ");
1107         output.tag.set_property("WINDOW_SIZE", config.window_size);
1108         output.tag.set_property("W", w);
1109         output.tag.set_property("H", h);
1110         output.append_tag();
1111         output.tag.set_title("/GRAPHICEQ");
1112         output.append_tag();
1113         output.append_newline();
1114
1115         for(int i = 0; i < config.points.total; i++)
1116         {
1117                 output.tag.set_title("POINT");
1118                 output.tag.set_property("X", config.points.values[i]->freq);
1119                 output.tag.set_property("Y", config.points.values[i]->value);
1120                 output.append_tag();
1121                 output.tag.set_title("/POINT");
1122                 output.append_tag();
1123                 output.append_newline();
1124         }
1125
1126         output.terminate_string();
1127 }
1128
1129
1130 void GraphicEQ::update_gui()
1131 {
1132         if( !thread ) return;
1133         GraphicGUI *window = (GraphicGUI *)thread->window;
1134 //lock here for points, needed by window cursor_motion callback
1135 //  deleted in load_configuration by read_data
1136         window->lock_window("GraphicEQ::update_gui");
1137         if( load_configuration() &&
1138             window->canvas->state != GraphicCanvas::DRAG_POINT ) {
1139                 window->update_canvas();
1140                 window->update_textboxes();
1141         }
1142         else {
1143                 int total_frames = get_gui_update_frames();
1144 //printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
1145                 if( total_frames )
1146                         window->update_canvas();
1147         }
1148         window->unlock_window();
1149 }
1150
1151 void GraphicEQ::reconfigure()
1152 {
1153         if(fft && fft->window_size != config.window_size)
1154         {
1155                 delete fft;
1156                 fft = 0;
1157         }
1158
1159         if(!fft)
1160         {
1161                 fft = new GraphicFFT(this);
1162                 fft->initialize(config.window_size);
1163         }
1164
1165         calculate_envelope(&config.points, envelope);
1166
1167         for(int i = 0; i < config.window_size / 2; i++)
1168         {
1169                 if(envelope[i] < 0) envelope[i] = 0;
1170         }
1171
1172         need_reconfigure = 0;
1173 }
1174
1175 int GraphicEQ::process_buffer(int64_t size,
1176         Samples *buffer,
1177         int64_t start_position,
1178         int sample_rate)
1179 {
1180         need_reconfigure |= load_configuration();
1181         if(need_reconfigure) reconfigure();
1182
1183         fft->process_buffer(start_position, size, buffer, get_direction());
1184
1185
1186         return 0;
1187 }
1188
1189
1190 double GraphicEQ::freq_to_magnitude(double frequency,
1191         ArrayList<GraphicPoint*> *points,
1192         double *envelope)
1193 {
1194 // Calculate using control points
1195         for(int i = 0; i < points->size(); i++)
1196         {
1197                 GraphicPoint *point = points->get(i);
1198                 if(point->freq == (int)frequency)
1199                 {
1200                         return DB::fromdb(point->value);
1201                 }
1202         }
1203
1204
1205         int nyquist = PluginAClient::project_sample_rate / 2;
1206         int slot = (int)(frequency * config.window_size / 2 / nyquist);
1207         if(slot >= config.window_size / 2) slot = config.window_size / 2 - 1;
1208 //printf("GraphicEQ::freq_to_db %d %f\n", slot, envelope[slot]);
1209         return envelope[slot];
1210 }
1211
1212
1213 void GraphicEQ::calculate_envelope(ArrayList<GraphicPoint*> *points,
1214                 double *envelope)
1215 {
1216         int niquist = PluginAClient::project_sample_rate / 2;
1217
1218 // Make temporary list of just points in order
1219         ArrayList<GraphicPoint*> temp_points;
1220         for(int i = 0; i < points->size(); i++)
1221         {
1222                 temp_points.append(points->get(i));
1223         }
1224
1225         for(int i = 0; i < temp_points.size(); i++)
1226         {
1227                 GraphicPoint *point = temp_points.get(i);
1228                 if(i == active_point)
1229                 {
1230                         GraphicPoint *prev_point = 0;
1231                         GraphicPoint *next_point = 0;
1232                         if(i >= 1) prev_point = temp_points.get(i - 1);
1233                         if(i < temp_points.size() - 1) next_point = temp_points.get(i + 1);
1234                         if( (prev_point && prev_point->freq >= point->freq) ||
1235                                 (next_point && next_point->freq <= point->freq) )
1236                                 temp_points.remove_number(i);
1237                         break;
1238                 }
1239         }
1240
1241
1242 // Join each point
1243         if(temp_points.size())
1244         {
1245                 GraphicPoint *first_point = temp_points.get(0);
1246                 GraphicPoint *last_point = temp_points.get(temp_points.size() - 1);
1247                 for(int i = 0; i < config.window_size / 2; i++)
1248                 {
1249                         int freq = i * niquist / (config.window_size / 2);
1250                         if(freq <= first_point->freq)
1251                                 envelope[i] = first_point->value;
1252                         else
1253                         if(freq >= last_point->freq)
1254                                 envelope[i] = last_point->value;
1255                         else
1256                         {
1257                                 GraphicPoint *point1 = first_point;
1258                                 GraphicPoint *point2 = last_point;
1259                                 for(int j = 0; j < temp_points.size(); j++)
1260                                 {
1261                                         if(temp_points.get(j)->freq <= freq)
1262                                                 point1 = temp_points.get(j);
1263                                         else
1264                                         {
1265                                                 point2 = temp_points.get(j);
1266                                                 break;
1267                                         }
1268                                 }
1269
1270                                 if(point2->freq != point1->freq)
1271                                 {
1272                                         int freqslot1 = Freq::fromfreq(point1->freq);
1273                                         int freqslot2 = Freq::fromfreq(point2->freq);
1274                                         int freqslot = Freq::fromfreq(freq);
1275
1276                                         envelope[i] = (double)(freqslot - freqslot1) *
1277                                                 (point2->value - point1->value) /
1278                                                 (freqslot2 - freqslot1) +
1279                                                 point1->value;
1280                                 }
1281                                 else
1282                                         envelope[i] = point1->value;
1283                         }
1284
1285
1286                         if(envelope[i] < MIN_DB + 0.01)
1287                                 envelope[i] = 0;
1288                         else
1289                                 envelope[i] = DB::fromdb(envelope[i]);
1290                 }
1291         }
1292         else
1293         {
1294                 for(int i = 0; i < config.window_size / 2; i++)
1295                 {
1296                         envelope[i] = 1.0;
1297                 }
1298         }
1299 }
1300
1301
1302
1303
1304 GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate)
1305  : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
1306 {
1307         data = new double[window_size / 2];
1308         freq_max = 0;
1309         time_max = 0;
1310         this->window_size = window_size;
1311 }
1312
1313 GraphicGUIFrame::~GraphicGUIFrame()
1314 {
1315         delete [] data;
1316 }
1317
1318
1319
1320
1321
1322
1323 GraphicFFT::GraphicFFT(GraphicEQ *plugin)
1324  : CrossfadeFFT()
1325 {
1326         this->plugin = plugin;
1327 }
1328
1329 GraphicFFT::~GraphicFFT()
1330 {
1331 }
1332
1333
1334 int GraphicFFT::signal_process()
1335 {
1336 // Create new frame for updating GUI
1337         frame = new GraphicGUIFrame(window_size,
1338                 plugin->PluginAClient::project_sample_rate);
1339         plugin->add_gui_frame(frame);
1340
1341         double freq_max = 0;
1342         for(int i = 0; i < window_size / 2; i++)
1343         {
1344                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
1345                 double angle = atan2(freq_imag[i], freq_real[i]);
1346                 freq_real[i] = result * cos(angle);
1347                 freq_imag[i] = result * sin(angle);
1348                 frame->data[i] = result;
1349                 if(result > freq_max) freq_max = result;
1350         }
1351         frame->freq_max = freq_max;
1352
1353         symmetry(window_size, freq_real, freq_imag);
1354
1355         return 0;
1356 }
1357
1358 int GraphicFFT::post_process()
1359 {
1360         double time_max = 0;
1361         for(int i = 0; i < window_size; i++)
1362         {
1363                 if(output_real[i] > time_max) time_max = output_real[i];
1364         }
1365         frame->time_max = time_max;
1366         return 0;
1367 }
1368
1369
1370
1371 int GraphicFFT::read_samples(int64_t output_sample,
1372         int samples,
1373         Samples *buffer)
1374 {
1375         return plugin->read_samples(buffer,
1376                 0,
1377                 plugin->get_samplerate(),
1378                 output_sample,
1379                 samples);
1380 }
1381
1382