Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / graphic / graphic.C
diff --git a/cinelerra-5.1/plugins/graphic/graphic.C b/cinelerra-5.1/plugins/graphic/graphic.C
new file mode 100644 (file)
index 0000000..456b12c
--- /dev/null
@@ -0,0 +1,1387 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "cursors.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "graphic.h"
+#include "keys.h"
+#include "language.h"
+#include "samples.h"
+#include "theme.h"
+#include "units.h"
+#include "vframe.h"
+
+#include <math.h>
+#include <string.h>
+
+
+// Canvas parameters
+#define MAJOR_DIVISIONS 7
+#define MINOR_DIVISIONS 5
+#define LINE_W4 12
+#define LINE_W3 10
+#define LINE_W2 5
+#define LINE_W1 2
+
+
+
+
+
+
+
+
+REGISTER_PLUGIN(GraphicEQ)
+
+
+
+
+
+
+
+GraphicPoint::GraphicPoint()
+{
+       freq = 0;
+       value = 0.0;
+}
+
+
+
+
+
+
+
+
+GraphicConfig::GraphicConfig()
+{
+       window_size = 4096;
+//     wetness = INFINITYGAIN;
+}
+
+GraphicConfig::~GraphicConfig()
+{
+       points.remove_all_objects();
+}
+
+
+int GraphicConfig::equivalent(GraphicConfig &that)
+{
+       if(that.points.size() != points.size() ||
+               window_size != that.window_size) return 0;
+
+       for(int i = 0; i < points.size(); i++)
+       {
+               if(that.points.get(i)->freq != points.get(i)->freq ||
+                       !EQUIV(that.points.get(i)->value, points.get(i)->value))
+                       return 0;
+       }
+
+
+       return 1;
+}
+
+void GraphicConfig::copy_from(GraphicConfig &that)
+{
+       points.remove_all_objects();
+       for(int i = 0; i < that.points.size(); i++)
+       {
+               GraphicPoint *point;
+               points.append(point = new GraphicPoint);
+               point->freq = that.points.get(i)->freq;
+               point->value = that.points.get(i)->value;
+       }
+       
+       window_size = that.window_size;
+}
+
+void GraphicConfig::interpolate(GraphicConfig &prev, 
+       GraphicConfig &next, 
+       int64_t prev_frame, 
+       int64_t next_frame, 
+       int64_t current_frame)
+{
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+// Get current set of points from previous configuration
+       copy_from(prev);
+       
+
+// Interpolate between current set of points and next set
+       for(int i = 0; i < MIN(next.points.size(), points.size()); i++)
+       {
+               points.get(i)->freq = (int)(prev.points.get(i)->freq *
+                       prev_scale +
+                       next.points.get(i)->freq *
+                       next_scale);
+               points.get(i)->value = prev.points.get(i)->value *
+                       prev_scale +
+                       next.points.get(i)->value *
+                       next_scale;
+       }
+}
+
+
+void GraphicConfig::delete_point(int number)
+{
+       points.remove_object_number(number);
+}
+
+
+
+
+
+
+
+GraphicCanvas::GraphicCanvas(GraphicEQ *plugin, 
+       GraphicGUI *gui,
+       int x, 
+       int y, 
+       int w, 
+       int h)
+ : BC_SubWindow(x,
+       y,
+       w,
+       h,
+       BLACK)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       state = GraphicCanvas::NONE;
+}
+
+GraphicCanvas::~GraphicCanvas()
+{
+       temp_points.remove_all_objects();
+}
+
+int GraphicCanvas::button_press_event()
+{
+       process(1, 0, 0);
+       if(state == GraphicCanvas::DRAG_POINT)
+               return 1;
+       else 
+               return 0;
+}
+
+int GraphicCanvas::cursor_motion_event()
+{
+       process(0, 1, 0);
+
+       if(state == GraphicCanvas::DRAG_POINT)
+       {
+               return 1;
+       }
+       else
+               return 0;
+}
+
+int GraphicCanvas::button_release_event()
+{
+       if(state == GraphicCanvas::DRAG_POINT && plugin->active_point >= 0)
+       {
+// Delete point if out of order
+               int point_number = plugin->active_point;
+               GraphicPoint *active_point = temp_points.get(point_number);
+
+
+               for(int i = 0; i < temp_points.size(); i++)
+               {
+                       GraphicPoint *point = temp_points.get(i);
+                       if((point->freq <= active_point->freq && i > point_number) ||
+                               (point->freq >= active_point->freq && i < point_number))
+                       {
+                               temp_points.remove_object_number(point_number);
+                               plugin->active_point = -1;
+                               process(0, 0, 1);
+                               break;
+                       }
+               }
+
+               save_temps();
+               plugin->send_configure_change();
+       }
+
+       state = GraphicCanvas::NONE;
+       return 0;
+}
+
+#define BOX_SIZE 10
+
+int GraphicCanvas::freq_to_y(int freq,
+       ArrayList<GraphicPoint*> *points,
+       double *envelope)
+{
+       int center_y = get_h() / 2;
+       double magnitude = plugin->freq_to_magnitude(freq, points, envelope);
+       double magnitude_db = DB::todb(magnitude);
+       if(magnitude_db < -MAXMAGNITUDE) magnitude_db = -MAXMAGNITUDE;
+       int y = (int)(center_y - magnitude_db * center_y / MAXMAGNITUDE);
+//printf("GraphicCanvas::freq_to_y magnitude=%f magnitude_db=%f y=%d\n", 
+//magnitude, magnitude_db, y);
+       return y;
+}
+
+
+void GraphicCanvas::new_temps()
+{
+// Copy configuration from plugin for editing
+       temp_points.remove_all_objects();
+       for(int i = 0; i < plugin->config.points.size(); i++)
+       {
+               GraphicPoint *point = new GraphicPoint;
+               *point = *plugin->config.points.get(i);
+               temp_points.append(point);
+       }
+       
+       plugin->calculate_envelope(&temp_points, temp_envelope);
+}
+
+void GraphicCanvas::save_temps()
+{
+       plugin->config.points.remove_all_objects();
+       for(int i = 0; i < temp_points.size(); i++)
+       {
+               GraphicPoint *point;
+               plugin->config.points.append(point = new GraphicPoint);
+               *point = *temp_points.get(i);
+       }
+}
+
+
+void GraphicCanvas::insert_point(GraphicPoint *point)
+{
+       int done = 0;
+
+       temp_points.append(point);
+       while(!done)
+       {
+               done = 1;
+               for(int i = 0; i < temp_points.size() - 1; i++)
+               {
+                       if(temp_points.get(i)->freq > temp_points.get(i + 1)->freq)
+                       {
+                               GraphicPoint *point = temp_points.get(i);
+                               temp_points.set(i, temp_points.get(i + 1));
+                               temp_points.set(i + 1, point);
+                               done = 0;
+                       }
+               }
+       }
+}
+
+
+void GraphicCanvas::process(int buttonpress, int motion, int draw)
+{
+       int got_button = 0;
+       int center_y = get_h() / 2;
+       int out_of_order = 0;
+       ArrayList<GraphicPoint*> *points;
+       double *envelope;
+       //const int debug = 0;
+
+
+       if(state == GraphicCanvas::NONE)
+       {
+               points = &plugin->config.points;
+               envelope = plugin->envelope;
+       }
+       else
+       {
+               points = &temp_points;
+               envelope = temp_envelope;
+       }
+
+       plugin->calculate_envelope(points, envelope);
+
+
+// spectrogram
+       if(draw)
+       {
+               clear_box(0, 0, get_w(), get_h());
+
+
+               int niquist = plugin->PluginAClient::project_sample_rate / 2;
+               int total_frames = plugin->get_gui_update_frames();
+               GraphicGUIFrame *frame = (GraphicGUIFrame*)plugin->get_gui_frame();
+
+               if(frame)
+               {
+                       delete plugin->last_frame;
+                       plugin->last_frame = frame;
+               }
+               else
+               {
+                       frame = plugin->last_frame;
+               }
+
+// Draw most recent frame
+               if(frame)
+               {
+                       set_color(MEGREY);
+                       int y1 = 0;
+                       int y2 = 0;
+
+
+                       for(int i = 0; i < get_w(); i++)
+                       {
+                               int freq = Freq::tofreq(i * TOTALFREQS / get_w());
+                               int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
+                               if(index < frame->window_size / 2)
+                               {
+                                       double magnitude = frame->data[index] / 
+                                               frame->freq_max * 
+                                               frame->time_max;
+                                       y2 = (int)(get_h() - 
+                                               (DB::todb(magnitude) - INFINITYGAIN) *
+                                               get_h() / 
+                                               -INFINITYGAIN);
+                                       CLAMP(y2, 0, get_h() - 1);
+                                       if(i > 0)
+                                       {
+                                               draw_line(i - 1, y1, i, y2);
+//printf(" %.0f", frame->data[index]);
+                                       }
+                                       y1 = y2;
+                               }
+                       }
+//printf( "\n");
+
+                       total_frames--;
+               }
+
+
+
+
+
+
+// Delete remaining frames
+               while(total_frames > 0)
+               {
+                       PluginClientFrame *frame = plugin->get_gui_frame();
+
+                       if(frame) delete frame;
+                       total_frames--;
+               }
+       }
+
+
+// Determine if active point is out of order
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = points->get(plugin->active_point);
+               for(int i = 0; i < points->size(); i++)
+               {
+                       if(i == plugin->active_point)
+                       {
+                               if( (i < points->size() - 1 &&
+                                       active_point->freq >= points->get(i + 1)->freq) ||
+                                   (i > 0 &&
+                                       active_point->freq <= points->get(i - 1)->freq) )
+                               {
+                                       out_of_order = 1;
+                               }
+                               break;
+                       }
+               }
+       }
+
+
+       if(motion)
+       {
+               if(state == GraphicCanvas::DRAG_POINT)
+               {
+                       int point_x = get_cursor_x() + x_diff;
+                       int point_y = get_cursor_y() + y_diff;
+                       CLAMP(point_x, 0, get_w());
+                       CLAMP(point_y, 0, get_h());
+                       
+                       int frequency = Freq::tofreq(point_x * TOTALFREQS / get_w());
+                       double magnitude_db = (double)(center_y - point_y) * MAXMAGNITUDE / center_y;
+                       int minfreq = Freq::tofreq(0);
+                       int maxfreq = Freq::tofreq(TOTALFREQS - 1);
+
+                       CLAMP(frequency, minfreq, maxfreq);
+                       CLAMP(magnitude_db, -MAXMAGNITUDE, MAXMAGNITUDE);
+                       if(plugin->active_point >= 0)
+                       {
+                               GraphicPoint *active_point = points->get(plugin->active_point);
+                               active_point->freq = frequency;
+                               active_point->value = magnitude_db;
+                       }
+
+// Redraw with new value
+                       process(0, 0, 1);
+                       save_temps();
+                       plugin->send_configure_change();
+                       gui->update_textboxes();
+                       return;
+               }
+       }
+
+// Magnitude bars
+       if(draw)
+       {
+               set_color(GREEN);
+               set_line_dashes(1);
+               for(int i = 1; i < MAJOR_DIVISIONS; i++)
+               {
+                       int y = i * get_h() / (MAJOR_DIVISIONS - 1);
+                       draw_line(0, y, get_w(), y);
+               }
+               set_line_dashes(0);
+       }
+
+       int y1 = 0;
+       if(draw) set_color(WHITE);
+
+// Control points, cursor change and control point selection
+       int new_cursor = CROSS_CURSOR;
+       for(int i = 0; i < points->size(); i++)
+       {
+               GraphicPoint *point = points->get(i);
+               int x = Freq::fromfreq(point->freq) * get_w() / TOTALFREQS;
+               int y = freq_to_y(point->freq, points, envelope);
+
+               if(draw)
+               {
+                       y1 = y;
+// Draw point under cursor if out of order
+                       if(i == plugin->active_point && out_of_order) 
+                               y1 = get_cursor_y() + y_diff;
+
+                       if(i == plugin->active_point)
+                               draw_box(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
+                       else
+                               draw_rectangle(x - BOX_SIZE / 2, y1 - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
+               }
+
+               if(motion && 
+                       state == GraphicCanvas::NONE &&
+                       is_event_win() && 
+                       cursor_inside())
+               {
+                       if(get_cursor_x() >= x - BOX_SIZE / 2 &&
+                               get_cursor_y() >= y - BOX_SIZE / 2 &&
+                               get_cursor_x() < x + BOX_SIZE / 2 &&
+                               get_cursor_y() < y + BOX_SIZE / 2)
+                       {
+                               new_cursor = UPRIGHT_ARROW_CURSOR;
+                       }
+               }
+               
+               if(buttonpress &&
+                       state == GraphicCanvas::NONE &&
+                       is_event_win() && 
+                       cursor_inside() &&
+                       !got_button)
+               {
+                       if(get_cursor_x() >= x - BOX_SIZE / 2 &&
+                               get_cursor_y() >= y - BOX_SIZE / 2 &&
+                               get_cursor_x() < x + BOX_SIZE / 2 &&
+                               get_cursor_y() < y + BOX_SIZE / 2)
+                       {
+                               plugin->active_point = i;
+                               state = GraphicCanvas::DRAG_POINT;
+                               new_temps();
+                               points = &temp_points;
+                               envelope = temp_envelope;
+
+                               x_diff = x - get_cursor_x();
+                               y_diff = y - get_cursor_y();
+                               got_button = 1;
+                               process(0, 0, 1);
+                               save_temps();
+                               plugin->send_configure_change();
+                               gui->update_textboxes();
+                       }
+               }
+       }
+
+       if(motion && new_cursor != get_cursor())
+       {
+               set_cursor(new_cursor, 0, 1);
+       }
+
+// Envelope line;
+       y1 = 0;
+       set_line_width(2);
+       for(int i = 0; i < get_w(); i++)
+       {
+               int y = freq_to_y(Freq::tofreq(i * TOTALFREQS / get_w()), 
+                       points, 
+                       envelope);
+
+               if(draw)
+               {
+                       if(i > 0) draw_line(i - 1, y1, i, y);
+               }
+
+
+               y1 = y;
+       }
+       set_line_width(1);
+
+       if(buttonpress && !got_button)
+       {
+               if(is_event_win() && cursor_inside())
+               {
+                       GraphicPoint *new_point = new GraphicPoint;
+                       new_point->freq = Freq::tofreq(get_cursor_x() * 
+                               TOTALFREQS / 
+                               get_w());
+                       new_point->value = (double)(center_y - get_cursor_y()) * 
+                               MAXMAGNITUDE / 
+                               center_y;
+                       state = GraphicCanvas::DRAG_POINT;
+                       new_temps();
+                       points = &temp_points;
+                       envelope = temp_envelope;
+
+                       insert_point(new_point);
+                       plugin->active_point = points->number_of(new_point);
+                       x_diff = 0;
+                       y_diff = 0;
+
+// Redraw with new point
+                       process(0, 0, 1);
+                       save_temps();
+                       plugin->send_configure_change();
+                       gui->update_textboxes();
+               }
+       }
+
+
+       if(draw) 
+       {
+               flash();
+       }
+}
+
+
+
+
+
+
+
+
+
+FreqTextBox::FreqTextBox(GraphicEQ *plugin,
+       GraphicGUI *gui,
+       int x,
+       int y,
+       int w)
+ : BC_TextBox(x, y, w, 1, "")
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int FreqTextBox::handle_event()
+{
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
+               if(atoi(get_text()) != active_point->freq)
+               {
+                       active_point->freq = atoi(get_text());
+                       gui->update_canvas();
+                       plugin->send_configure_change();
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+void FreqTextBox::update(int freq)
+{
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
+               if(atoi(get_text()) != active_point->freq)
+               {
+                       char string[BCTEXTLEN];
+                       sprintf(string, "%d", active_point->freq);
+                       BC_TextBox::update(string);
+               }
+       }
+}
+
+
+
+
+ValueTextBox::ValueTextBox(GraphicEQ *plugin,
+       GraphicGUI *gui,
+       int x,
+       int y,
+       int w)
+ : BC_TextBox(x, y, w, 1, "")
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int ValueTextBox::handle_event()
+{
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
+               if(!EQUIV(atof(get_text()), active_point->value))
+               {
+                       active_point->value = atof(get_text());
+                       gui->update_canvas();
+                       plugin->send_configure_change();
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+void ValueTextBox::update(float value)
+{
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
+               if(!EQUIV(atof(get_text()), active_point->value))
+               {
+                       char string[BCTEXTLEN];
+                       sprintf(string, "%.04f", active_point->value);
+                       BC_TextBox::update(string);
+               }
+       }
+}
+
+GraphicReset::GraphicReset(GraphicEQ *plugin,
+       GraphicGUI *gui,
+       int x,
+       int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int GraphicReset::handle_event()
+{
+       plugin->config.points.remove_all_objects();
+       plugin->active_point = -1;
+       gui->update_canvas();
+       gui->update_textboxes();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+GraphicSize::GraphicSize(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
+ : BC_PopupMenu(x, y, 100, "4096", 1)
+{
+       this->plugin = plugin;
+       this->window = window;
+}
+
+
+int GraphicSize::handle_event()
+{
+       plugin->config.window_size = atoi(get_text());
+       plugin->send_configure_change();
+
+       window->update_canvas();
+       return 1;
+}
+
+void GraphicSize::create_objects()
+{
+       add_item(new BC_MenuItem("2048"));
+       add_item(new BC_MenuItem("4096"));
+       add_item(new BC_MenuItem("8192"));
+       add_item(new BC_MenuItem("16384"));
+       add_item(new BC_MenuItem("32768"));
+       add_item(new BC_MenuItem("65536"));
+       add_item(new BC_MenuItem("131072"));
+       add_item(new BC_MenuItem("262144"));
+}
+
+void GraphicSize::update(int size)
+{
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", size);
+       set_text(string);
+}
+
+
+
+
+// 
+// 
+// GraphicWetness::GraphicWetness(GraphicGUI *window, GraphicEQ *plugin, int x, int y)
+//  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
+// {
+//     this->plugin = plugin;
+//     this->window = window;
+// }
+// 
+// int GraphicWetness::handle_event()
+// {
+//     plugin->config.wetness = get_value();
+//     plugin->send_configure_change();
+//     window->update_canvas();
+//     return 1;
+// }
+// 
+// 
+// 
+
+
+
+
+
+
+GraphicGUI::GraphicGUI(GraphicEQ *plugin)
+ : PluginClientWindow(plugin, 
+       plugin->w, 
+       plugin->h, 
+       320, 
+       200,
+       1)
+{
+       this->plugin = plugin;
+}
+
+GraphicGUI::~GraphicGUI()
+{
+}
+
+
+void GraphicGUI::create_objects()
+{
+       int margin = plugin->get_theme()->widget_border;
+       int x = get_text_width(SMALLFONT, "-00") + LINE_W4 + margin;
+       int y = margin;
+       int freq_h = get_text_height(SMALLFONT) + LINE_W4;
+
+       add_subwindow(canvas = new GraphicCanvas(plugin,
+               this,
+               x, 
+               y, 
+               get_w() - x - margin, 
+               get_h() - 
+//                     BC_Pot::calculate_h() - 
+                       BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) -
+                       margin * 3 - 
+                       y - 
+                       freq_h));
+       y += canvas->get_h() + freq_h + margin;
+
+//     int x1 = x;
+//     int y1 = y;
+       add_subwindow(freq_title = new BC_Title(x, y, _("Frequency:")));
+       x += freq_title->get_w() + margin;
+       add_subwindow(freq_text = new FreqTextBox(plugin, this, x, y, 100));
+       x += freq_text->get_w() + margin;
+
+       add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
+       x += level_title->get_w() + margin;
+       add_subwindow(value_text = new ValueTextBox(plugin, this, x, y, 100));
+       x += value_text->get_w() + margin;
+
+       add_subwindow(reset = new GraphicReset(plugin, this, x, y));
+       x += reset->get_w() + margin;
+       
+
+//     x = x1;
+//     y += value_text->get_h() + margin;
+
+       add_subwindow(size_title = new BC_Title(x, y, _("Window size:")));
+       x += size_title->get_w() + margin;
+       add_subwindow(size = new GraphicSize(this, plugin, x, y));
+       size->create_objects();
+       size->update(plugin->config.window_size);
+       x += size->get_w() + margin;
+
+//     add_subwindow(title = new BC_Title(x, y, "Wetness:"));
+//     x += title->get_w() + margin;
+//     add_subwindow(wetness = new GraphicWetness(this, plugin, 
+//             x, 
+//             y));
+
+       draw_ticks();
+       update_canvas();
+       show_window();
+}
+
+
+int GraphicGUI::resize_event(int w, int h)
+{
+       int difference = h - get_h();
+       int canvas_xdiff = get_w() - canvas->get_w();
+       int canvas_ydiff = get_h() - canvas->get_h();
+       
+       canvas->reposition_window(canvas->get_x(),
+               canvas->get_y(),
+               w - canvas_xdiff,
+               h - canvas_ydiff);
+       freq_text->reposition_window(freq_text->get_x(),
+               freq_text->get_y() + difference);
+       value_text->reposition_window(value_text->get_x(),
+               value_text->get_y() + difference);
+       freq_title->reposition_window(freq_title->get_x(),
+               freq_title->get_y() + difference);
+       level_title->reposition_window(level_title->get_x(),
+               level_title->get_y() + difference);
+       size_title->reposition_window(size_title->get_x(),
+               size_title->get_y() + difference);
+       reset->reposition_window(reset->get_x(),
+               reset->get_y() + difference);
+       size->reposition_window(size->get_x(),
+               size->get_y() + difference);
+
+       draw_ticks();
+       update_canvas();
+       flash();
+
+       plugin->w = w;
+       plugin->h = h;
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+
+int GraphicGUI::keypress_event()
+{
+       if(get_keypress() == BACKSPACE ||
+               get_keypress() == DELETE)
+       {
+               if(plugin->active_point_exists())
+               {
+                       int point_number = -1;
+                       for(int i = 0; i < plugin->config.points.size(); i++)
+                       {
+                               if(i == plugin->active_point)
+                               {
+                                       point_number = i;
+                                       break;
+                               }
+                       }
+                       
+                       if(point_number >= 0)
+                       {
+                               plugin->config.delete_point(point_number);
+                               canvas->process(0, 0, 1);
+                               plugin->send_configure_change();
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+
+void GraphicGUI::draw_ticks()
+{
+       //int x = canvas->get_x() - 5 - get_text_width(SMALLFONT, "-00");
+       int y = canvas->get_y() - 1;
+       int x1 = canvas->get_x() - LINE_W3;
+       int x2 = canvas->get_x() - LINE_W2;
+       int x3 = canvas->get_x() - LINE_W1;
+       char string[BCTEXTLEN];
+
+// Amplitude
+       set_font(SMALLFONT);
+       int major_division = canvas->get_h() / (MAJOR_DIVISIONS - 1);
+       for(int i = 0; i < MAJOR_DIVISIONS; i++)
+       {
+               int current_db = (MAJOR_DIVISIONS - 1 - i) * (MAX_DB - MIN_DB) / (MAJOR_DIVISIONS - 1) + MIN_DB;
+               if(current_db == MIN_DB)
+                       sprintf(string, "oo");
+               else
+               if(current_db <= 0.0)
+                       sprintf(string, "%d", current_db);
+               else
+                       sprintf(string, "+%d", current_db);
+
+               set_color(BLACK);
+               int y1 = y + 1 + i * canvas->get_h() / (MAJOR_DIVISIONS - 1);
+               int x4 = canvas->get_x() - LINE_W4 - get_text_width(SMALLFONT, string);
+               draw_text(x4 + 1, y1 + get_text_ascent(SMALLFONT) / 2 + 1, string);
+               draw_line(x1 + 1, y1 + 1, x3 + 1, y1 + 1);
+               set_color(RED);
+               draw_text(x4, y1 + get_text_ascent(SMALLFONT) / 2, string);
+               draw_line(x1, y1, x3, y1);
+
+
+               if(i < MAJOR_DIVISIONS - 1)
+               {
+                       for(int j = 0; j < MINOR_DIVISIONS; j++)
+                       {
+                               int y2 = y1 + j * major_division / MINOR_DIVISIONS;
+                               set_color(BLACK);
+                               draw_line(x2 + 1, y2 + 1, x3 + 1, y2 + 1);
+                               set_color(RED);
+                               draw_line(x2, y2, x3, y2);
+                       }
+               }
+       }
+
+
+// Frequency
+       for(int i = 0; i <= MAJOR_DIVISIONS; i++)
+       {
+               int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
+               char string[BCTEXTLEN];
+               x1 = canvas->get_x() + i * canvas->get_w() / MAJOR_DIVISIONS;
+               int y1 = canvas->get_y() + canvas->get_h() + LINE_W1 - 1;
+               sprintf(string, "%d", freq);
+               int x2 = x1 - get_text_width(SMALLFONT, string);
+               int y2 = canvas->get_y() + canvas->get_h() + LINE_W2 - 1;
+               int y3 = canvas->get_y() + canvas->get_h() + LINE_W3 - 1;
+               int y4 = canvas->get_y() + canvas->get_h() + get_text_height(SMALLFONT) + LINE_W4 - 1;
+               
+               set_color(BLACK);
+               draw_text(x2 + 1, y4 + 1, string);
+               draw_line(x1 + 1, y3 + 1, x1 + 1, y1 + 1);
+               
+               set_color(RED);
+               draw_text(x2, y4, string);
+               draw_line(x1, y3, x1, y1);
+               
+               if(i < MAJOR_DIVISIONS)
+               {
+                       for(int j = 0; j < MINOR_DIVISIONS; j++)
+                       {
+                               int x3 = (int)(x1 +
+                                       (canvas->get_w() / MAJOR_DIVISIONS) -
+                                       exp(-(double)j * 0.7) *
+                                       (canvas->get_w() / MAJOR_DIVISIONS));
+                               set_color(BLACK);
+                               draw_line(x3 + 1, y2 + 1, x3 + 1, y1 + 1);
+                               set_color(RED);
+                               draw_line(x3, y2, x3, y1);
+                       }
+               }
+       }
+}
+
+void GraphicGUI::update_canvas()
+{
+       canvas->process(0, 0, 1);
+}
+
+void GraphicGUI::update_textboxes()
+{
+       if(plugin->active_point_exists())
+       {
+               GraphicPoint *active_point = plugin->config.points.get(plugin->active_point);
+               freq_text->update(active_point->freq);
+               value_text->update(active_point->value);
+       }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+GraphicEQ::GraphicEQ(PluginServer *server)
+ : PluginAClient(server)
+{
+       last_frame = 0; 
+       fft = 0;
+       need_reconfigure = 1;
+       active_point = -1;
+       w = 640;
+       h = 480;
+}
+
+GraphicEQ::~GraphicEQ()
+{
+       
+       delete last_frame;
+       if(fft) delete fft;
+}
+
+
+int GraphicEQ::is_realtime() { return 1; }
+
+const char* GraphicEQ::plugin_title() { return _("EQ Graphic"); }
+
+NEW_WINDOW_MACRO(GraphicEQ, GraphicGUI)
+
+LOAD_CONFIGURATION_MACRO(GraphicEQ, GraphicConfig)
+
+int GraphicEQ::active_point_exists()
+{
+       if(active_point >= 0 && active_point < config.points.size()) return 1;
+       return 0;
+}
+
+
+void GraphicEQ::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+       int result = 0;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+       config.points.remove_all_objects();
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("GRAPHICEQ"))
+                       {
+                               config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
+                               if(is_defaults())
+                               {
+                                       w = input.tag.get_property("W", w);
+                                       h = input.tag.get_property("H", h);
+                               }
+                       }
+                       else
+                       if(input.tag.title_is("POINT"))
+                       {
+                               GraphicPoint *point;
+                               config.points.append(point = new GraphicPoint);
+                               point->freq = input.tag.get_property("X", 0);
+                               point->value = input.tag.get_property("Y", 0.0);
+                       }
+               }
+       }
+
+//     if(!active_point_exists()) active_point = -1;
+}
+
+
+
+
+void GraphicEQ::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+
+       output.tag.set_title("GRAPHICEQ");
+       output.tag.set_property("WINDOW_SIZE", config.window_size);
+       output.tag.set_property("W", w);
+       output.tag.set_property("H", h);
+       output.append_tag();
+       output.tag.set_title("/GRAPHICEQ");
+       output.append_tag();
+       output.append_newline();
+
+       for(int i = 0; i < config.points.total; i++)
+       {
+               output.tag.set_title("POINT");
+               output.tag.set_property("X", config.points.values[i]->freq);
+               output.tag.set_property("Y", config.points.values[i]->value);
+               output.append_tag();
+               output.tag.set_title("/POINT");
+               output.append_tag();
+               output.append_newline();
+       }
+       
+       output.terminate_string();
+}
+
+
+void GraphicEQ::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration() && 
+                       ((GraphicGUI*)thread->window)->canvas->state != GraphicCanvas::DRAG_POINT)
+               {
+                       ((GraphicGUI*)thread->window)->lock_window("GraphicEQ::update_gui");
+                       ((GraphicGUI*)thread->window)->update_canvas();
+                       ((GraphicGUI*)thread->window)->update_textboxes();
+                       ((GraphicGUI*)thread->window)->unlock_window();
+               }
+               else
+               {
+                       int total_frames = get_gui_update_frames();
+//printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
+                       if(total_frames)
+                       {
+                               ((GraphicGUI*)thread->window)->lock_window("GraphicEQ::update_gui");
+                               ((GraphicGUI*)thread->window)->update_canvas();
+                               ((GraphicGUI*)thread->window)->unlock_window();
+                       }
+               }
+       }
+}
+
+void GraphicEQ::reconfigure()
+{
+       if(fft && fft->window_size != config.window_size)
+       {
+               delete fft;
+               fft = 0;
+       }
+       
+       if(!fft)
+       {
+               fft = new GraphicFFT(this);
+               fft->initialize(config.window_size);
+       }
+
+       calculate_envelope(&config.points, envelope);
+       
+       for(int i = 0; i < config.window_size / 2; i++)
+       {
+               if(envelope[i] < 0) envelope[i] = 0;
+       }
+
+       need_reconfigure = 0;
+}
+
+int GraphicEQ::process_buffer(int64_t size, 
+       Samples *buffer, 
+       int64_t start_position,
+       int sample_rate)
+{
+       need_reconfigure |= load_configuration();
+       if(need_reconfigure) reconfigure();
+       
+       fft->process_buffer(start_position, size, buffer, get_direction());
+
+
+       return 0;
+}
+
+
+double GraphicEQ::freq_to_magnitude(double frequency,
+       ArrayList<GraphicPoint*> *points,
+       double *envelope)
+{
+// Calculate using control points
+       for(int i = 0; i < points->size(); i++)
+       {
+               GraphicPoint *point = points->get(i);
+               if(point->freq == (int)frequency)
+               {
+                       return DB::fromdb(point->value);
+               }
+       }
+
+
+       int nyquist = PluginAClient::project_sample_rate / 2;
+       int slot = (int)(frequency * config.window_size / 2 / nyquist);
+       if(slot >= config.window_size / 2) slot = config.window_size / 2 - 1;
+//printf("GraphicEQ::freq_to_db %d %f\n", slot, envelope[slot]);
+       return envelope[slot];
+}
+
+
+void GraphicEQ::calculate_envelope(ArrayList<GraphicPoint*> *points,
+               double *envelope)
+{
+       int niquist = PluginAClient::project_sample_rate / 2;
+
+// Make temporary list of just points in order
+       ArrayList<GraphicPoint*> temp_points;
+       for(int i = 0; i < points->size(); i++)
+       {
+               temp_points.append(points->get(i));
+       }
+
+       for(int i = 0; i < temp_points.size(); i++)
+       {
+               GraphicPoint *point = temp_points.get(i);
+               if(i == active_point) 
+               {
+                       GraphicPoint *prev_point = 0;
+                       GraphicPoint *next_point = 0;
+                       if(i >= 1) prev_point = temp_points.get(i - 1);
+                       if(i < temp_points.size() - 1) next_point = temp_points.get(i + 1);
+                       if( (prev_point && prev_point->freq >= point->freq) ||
+                               (next_point && next_point->freq <= point->freq) )
+                               temp_points.remove_number(i);
+                       break;
+               }
+       }
+
+
+// Join each point
+       if(temp_points.size())
+       {
+               GraphicPoint *first_point = temp_points.get(0);
+               GraphicPoint *last_point = temp_points.get(temp_points.size() - 1);
+               for(int i = 0; i < config.window_size / 2; i++)
+               {
+                       int freq = i * niquist / (config.window_size / 2);
+                       if(freq <= first_point->freq)
+                               envelope[i] = first_point->value;
+                       else
+                       if(freq >= last_point->freq)
+                               envelope[i] = last_point->value;
+                       else
+                       {
+                               GraphicPoint *point1 = first_point;
+                               GraphicPoint *point2 = last_point;
+                               for(int j = 0; j < temp_points.size(); j++)
+                               {
+                                       if(temp_points.get(j)->freq <= freq)
+                                               point1 = temp_points.get(j);
+                                       else
+                                       {
+                                               point2 = temp_points.get(j);
+                                               break;
+                                       }
+                               }
+                               
+                               if(point2->freq != point1->freq)
+                               {
+                                       int freqslot1 = Freq::fromfreq(point1->freq);
+                                       int freqslot2 = Freq::fromfreq(point2->freq);
+                                       int freqslot = Freq::fromfreq(freq);
+                               
+                                       envelope[i] = (double)(freqslot - freqslot1) *
+                                               (point2->value - point1->value) / 
+                                               (freqslot2 - freqslot1) +
+                                               point1->value;
+                               }
+                               else
+                                       envelope[i] = point1->value;
+                       }
+
+
+                       if(envelope[i] < MIN_DB + 0.01) 
+                               envelope[i] = 0;
+                       else
+                               envelope[i] = DB::fromdb(envelope[i]);
+               }
+       }
+       else
+       {
+               for(int i = 0; i < config.window_size / 2; i++)
+               {
+                       envelope[i] = 1.0;
+               }
+       }
+}
+
+
+
+
+GraphicGUIFrame::GraphicGUIFrame(int window_size, int sample_rate)
+ : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
+{
+       data = new double[window_size / 2];
+       freq_max = 0;
+       time_max = 0;
+       this->window_size = window_size;
+}
+
+GraphicGUIFrame::~GraphicGUIFrame()
+{
+       delete [] data;
+}
+
+
+
+
+
+
+GraphicFFT::GraphicFFT(GraphicEQ *plugin)
+ : CrossfadeFFT()
+{
+       this->plugin = plugin;
+}
+
+GraphicFFT::~GraphicFFT()
+{
+}
+
+
+int GraphicFFT::signal_process()
+{
+// Create new frame for updating GUI
+       frame = new GraphicGUIFrame(window_size, 
+               plugin->PluginAClient::project_sample_rate);
+       plugin->add_gui_frame(frame);
+
+       double freq_max = 0;
+       for(int i = 0; i < window_size / 2; i++)
+       {
+               double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
+               double angle = atan2(freq_imag[i], freq_real[i]);
+               freq_real[i] = result * cos(angle);
+               freq_imag[i] = result * sin(angle);
+               frame->data[i] = result;
+               if(result > freq_max) freq_max = result;
+       }
+       frame->freq_max = freq_max;
+
+       symmetry(window_size, freq_real, freq_imag);
+
+       return 0;
+}
+
+int GraphicFFT::post_process()
+{
+       double time_max = 0;
+       for(int i = 0; i < window_size; i++)
+       {
+               if(output_real[i] > time_max) time_max = output_real[i];
+       }
+       frame->time_max = time_max;
+       return 0;
+}
+
+
+
+int GraphicFFT::read_samples(int64_t output_sample, 
+       int samples, 
+       Samples *buffer)
+{
+       return plugin->read_samples(buffer,
+               0,
+               plugin->get_samplerate(),
+               output_sample,
+               samples);
+}
+
+