Exciting new Alt/h help key provided by sge (Georgy) with many thanks!
[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 context_help_check_and_show();
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 GraphicEQ::GraphicEQ(PluginServer *server)
981  : PluginAClient(server)
982 {
983         last_frame = 0;
984         fft = 0;
985         need_reconfigure = 1;
986         active_point = -1;
987         w = xS(640);
988         h = yS(480);
989 }
990
991 GraphicEQ::~GraphicEQ()
992 {
993
994         delete last_frame;
995         if(fft) delete fft;
996 }
997
998
999 int GraphicEQ::is_realtime() { return 1; }
1000
1001 const char* GraphicEQ::plugin_title() { return N_("EQ Graphic"); }
1002
1003 NEW_WINDOW_MACRO(GraphicEQ, GraphicGUI)
1004
1005 LOAD_CONFIGURATION_MACRO(GraphicEQ, GraphicConfig)
1006
1007 int GraphicEQ::active_point_exists()
1008 {
1009         if(active_point >= 0 && active_point < config.points.size()) return 1;
1010         return 0;
1011 }
1012
1013
1014 void GraphicEQ::read_data(KeyFrame *keyframe)
1015 {
1016         FileXML input;
1017         int result = 0;
1018
1019         input.set_shared_input(keyframe->xbuf);
1020         config.points.remove_all_objects();
1021
1022         while( !(result = input.read_tag()) ) {
1023                 if(input.tag.title_is("GRAPHICEQ")) {
1024                         config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1025                         if(is_defaults()) {
1026                                 w = input.tag.get_property("W", w);
1027                                 h = input.tag.get_property("H", h);
1028                         }
1029                 }
1030                 else if(input.tag.title_is("POINT")) {
1031                         GraphicPoint *point;
1032                         config.points.append(point = new GraphicPoint);
1033                         point->freq = input.tag.get_property("X", 0);
1034                         point->value = input.tag.get_property("Y", 0.0);
1035                 }
1036         }
1037 //      if(!active_point_exists()) active_point = -1;
1038 }
1039
1040
1041
1042
1043 void GraphicEQ::save_data(KeyFrame *keyframe)
1044 {
1045         FileXML output;
1046         output.set_shared_output(keyframe->xbuf);
1047
1048         output.tag.set_title("GRAPHICEQ");
1049         output.tag.set_property("WINDOW_SIZE", config.window_size);
1050         output.tag.set_property("W", w);
1051         output.tag.set_property("H", h);
1052         output.append_tag();
1053         output.tag.set_title("/GRAPHICEQ");
1054         output.append_tag();
1055         output.append_newline();
1056
1057         for(int i = 0; i < config.points.total; i++) {
1058                 output.tag.set_title("POINT");
1059                 output.tag.set_property("X", config.points.values[i]->freq);
1060                 output.tag.set_property("Y", config.points.values[i]->value);
1061                 output.append_tag();
1062                 output.tag.set_title("/POINT");
1063                 output.append_tag();
1064                 output.append_newline();
1065         }
1066
1067         output.terminate_string();
1068 }
1069
1070
1071 void GraphicEQ::update_gui()
1072 {
1073         if( !thread ) return;
1074         GraphicGUI *window = (GraphicGUI *)thread->window;
1075         window->lock_window("GraphicEQ::update_gui");
1076 //lock here for points, needed by window cursor_motion callback
1077 //  deleted in load_configuration by read_data
1078         if( load_configuration() &&
1079             window->canvas->state != GraphicCanvas::DRAG_POINT ) {
1080                 window->update_canvas();
1081                 window->update_textboxes();
1082         }
1083         else if( pending_gui_frame() ) {
1084                 window->update_canvas();
1085         }
1086         window->unlock_window();
1087 }
1088
1089 void GraphicEQ::reconfigure()
1090 {
1091         if(fft && fft->window_size != config.window_size) {
1092                 delete fft;
1093                 fft = 0;
1094         }
1095
1096         if(!fft) {
1097                 fft = new GraphicFFT(this);
1098                 fft->initialize(config.window_size);
1099         }
1100
1101         calculate_envelope(&config.points, envelope);
1102
1103         for(int i = 0; i < config.window_size / 2; i++) {
1104                 if(envelope[i] < 0) envelope[i] = 0;
1105         }
1106
1107         need_reconfigure = 0;
1108 }
1109
1110 int GraphicEQ::process_buffer(int64_t size,
1111         Samples *buffer,
1112         int64_t start_position,
1113         int sample_rate)
1114 {
1115         need_reconfigure |= load_configuration();
1116         if(need_reconfigure) reconfigure();
1117
1118         fft->process_buffer(start_position, size, buffer, get_direction());
1119
1120
1121         return 0;
1122 }
1123
1124
1125 double GraphicEQ::freq_to_magnitude(double frequency,
1126         ArrayList<GraphicPoint*> *points,
1127         double *envelope)
1128 {
1129 // Calculate using control points
1130         for(int i = 0; i < points->size(); i++)
1131         {
1132                 GraphicPoint *point = points->get(i);
1133                 if(point->freq == (int)frequency)
1134                 {
1135                         return DB::fromdb(point->value);
1136                 }
1137         }
1138
1139
1140         int nyquist = PluginAClient::project_sample_rate / 2;
1141         int slot = (int)(frequency * config.window_size / 2 / nyquist);
1142         if(slot >= config.window_size / 2) slot = config.window_size / 2 - 1;
1143 //printf("GraphicEQ::freq_to_db %d %f\n", slot, envelope[slot]);
1144         return envelope[slot];
1145 }
1146
1147
1148 void GraphicEQ::calculate_envelope(ArrayList<GraphicPoint*> *points,
1149                 double *envelope)
1150 {
1151         int niquist = PluginAClient::project_sample_rate / 2;
1152
1153 // Make temporary list of just points in order
1154         ArrayList<GraphicPoint*> temp_points;
1155         for(int i = 0; i < points->size(); i++)
1156         {
1157                 temp_points.append(points->get(i));
1158         }
1159
1160         for(int i = 0; i < temp_points.size(); i++)
1161         {
1162                 GraphicPoint *point = temp_points.get(i);
1163                 if(i == active_point)
1164                 {
1165                         GraphicPoint *prev_point = 0;
1166                         GraphicPoint *next_point = 0;
1167                         if(i >= 1) prev_point = temp_points.get(i - 1);
1168                         if(i < temp_points.size() - 1) next_point = temp_points.get(i + 1);
1169                         if( (prev_point && prev_point->freq >= point->freq) ||
1170                                 (next_point && next_point->freq <= point->freq) )
1171                                 temp_points.remove_number(i);
1172                         break;
1173                 }
1174         }
1175
1176
1177 // Join each point
1178         if(temp_points.size())
1179         {
1180                 GraphicPoint *first_point = temp_points.get(0);
1181                 GraphicPoint *last_point = temp_points.get(temp_points.size() - 1);
1182                 for(int i = 0; i < config.window_size / 2; i++)
1183                 {
1184                         int freq = i * niquist / (config.window_size / 2);
1185                         if(freq <= first_point->freq)
1186                                 envelope[i] = first_point->value;
1187                         else
1188                         if(freq >= last_point->freq)
1189                                 envelope[i] = last_point->value;
1190                         else
1191                         {
1192                                 GraphicPoint *point1 = first_point;
1193                                 GraphicPoint *point2 = last_point;
1194                                 for(int j = 0; j < temp_points.size(); j++)
1195                                 {
1196                                         if(temp_points.get(j)->freq <= freq)
1197                                                 point1 = temp_points.get(j);
1198                                         else
1199                                         {
1200                                                 point2 = temp_points.get(j);
1201                                                 break;
1202                                         }
1203                                 }
1204
1205                                 if(point2->freq != point1->freq)
1206                                 {
1207                                         int freqslot1 = Freq::fromfreq(point1->freq);
1208                                         int freqslot2 = Freq::fromfreq(point2->freq);
1209                                         int freqslot = Freq::fromfreq(freq);
1210
1211                                         envelope[i] = (double)(freqslot - freqslot1) *
1212                                                 (point2->value - point1->value) /
1213                                                 (freqslot2 - freqslot1) +
1214                                                 point1->value;
1215                                 }
1216                                 else
1217                                         envelope[i] = point1->value;
1218                         }
1219
1220
1221                         if(envelope[i] < MIN_DB + 0.01)
1222                                 envelope[i] = 0;
1223                         else
1224                                 envelope[i] = DB::fromdb(envelope[i]);
1225                 }
1226         }
1227         else
1228         {
1229                 for(int i = 0; i < config.window_size / 2; i++)
1230                 {
1231                         envelope[i] = 1.0;
1232                 }
1233         }
1234 }
1235
1236
1237 GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate)
1238  : PluginClientFrame()
1239 {
1240         this->window_size = window_size;
1241         data_size = window_size / 2;
1242         data = new double[data_size];
1243         freq_max = 0;
1244         time_max = 0;
1245 }
1246
1247 GraphicGUIFrame::~GraphicGUIFrame()
1248 {
1249         delete [] data;
1250 }
1251
1252
1253 GraphicFFT::GraphicFFT(GraphicEQ *plugin)
1254  : CrossfadeFFT()
1255 {
1256         this->plugin = plugin;
1257 }
1258
1259 GraphicFFT::~GraphicFFT()
1260 {
1261 }
1262
1263
1264 int GraphicFFT::signal_process()
1265 {
1266 // Create new frame for updating GUI
1267         frame = new GraphicGUIFrame(window_size,
1268                 plugin->PluginAClient::project_sample_rate);
1269         plugin->add_gui_frame(frame);
1270
1271         double freq_max = 0;
1272         for(int i = 0; i < window_size / 2; i++)
1273         {
1274                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
1275                 double angle = atan2(freq_imag[i], freq_real[i]);
1276                 freq_real[i] = result * cos(angle);
1277                 freq_imag[i] = result * sin(angle);
1278                 frame->data[i] = result;
1279                 if(result > freq_max) freq_max = result;
1280         }
1281         frame->freq_max = freq_max;
1282
1283         symmetry(window_size, freq_real, freq_imag);
1284
1285         return 0;
1286 }
1287
1288 int GraphicFFT::post_process()
1289 {
1290         double time_max = 0;
1291         for(int i = 0; i < window_size; i++)
1292         {
1293                 if(output_real[i] > time_max) time_max = output_real[i];
1294         }
1295         frame->time_max = time_max;
1296         return 0;
1297 }
1298
1299
1300
1301 int GraphicFFT::read_samples(int64_t output_sample,
1302         int samples,
1303         Samples *buffer)
1304 {
1305         return plugin->read_samples(buffer,
1306                 0,
1307                 plugin->get_samplerate(),
1308                 output_sample,
1309                 samples);
1310 }
1311
1312