OSC: Changed gainVCA to gainfader as VCA is already used.
[ardour.git] / gtk2_ardour / fft_graph.cc
index e7a0fd75b6ffadb8904307c04f72f99e32c18b4b..1edd483dab4f5148ba5d4b106cfbbaac1fbc86b4 100644 (file)
 
 */
 
+#ifdef COMPILER_MSVC
+#include <algorithm>
+using std::min; using std::max;
+#endif
+
 #include <iostream>
 
 #include <glibmm.h>
@@ -29,7 +34,7 @@
 #include <gtkmm/treemodel.h>
 #include <gtkmm/treepath.h>
 
-#include <pbd/stl_delete.h>
+#include "pbd/stl_delete.h"
 
 #include <math.h>
 
@@ -43,7 +48,7 @@ using namespace Gdk;
 FFTGraph::FFTGraph(int windowSize)
 {
        _logScale = 0;
-       
+
        _in       = 0;
        _out      = 0;
        _hanning  = 0;
@@ -51,6 +56,9 @@ FFTGraph::FFTGraph(int windowSize)
 
        _a_window = 0;
 
+       _show_minmax     = false;
+       _show_normalized = false;
+
        setWindowSize(windowSize);
 }
 
@@ -58,7 +66,7 @@ void
 FFTGraph::setWindowSize(int windowSize)
 {
        if (_a_window) {
-               Glib::Mutex::Lock lm  (_a_window->track_list_lock);
+               Glib::Threads::Mutex::Lock lm  (_a_window->track_list_lock);
                setWindowSize_internal(windowSize);
        } else {
                setWindowSize_internal(windowSize);
@@ -72,7 +80,7 @@ FFTGraph::setWindowSize_internal(int windowSize)
        if (_a_window) {
                _a_window->clear_tracklist();
        }
-       
+
        _windowSize = windowSize;
        _dataSize = windowSize / 2;
        if (_in != 0) {
@@ -80,12 +88,12 @@ FFTGraph::setWindowSize_internal(int windowSize)
                free(_in);
                _in = 0;
        }
-       
+
        if (_out != 0) {
                free(_out);
                _out = 0;
        }
-       
+
        if (_hanning != 0) {
                free(_hanning);
                _hanning = 0;
@@ -110,18 +118,18 @@ FFTGraph::setWindowSize_internal(int windowSize)
 
        // normalize the window
        double sum = 0.0;
-       
+
        for (int i=0; i < _windowSize; i++) {
                _hanning[i]=0.81f * ( 0.5f - (0.5f * (float) cos(2.0f * M_PI * (float)i / (float)(_windowSize))));
                sum += _hanning[i];
        }
 
        double isum = 1.0 / sum;
-       
+
        for (int i=0; i < _windowSize; i++) {
                _hanning[i] *= isum;
        }
-       
+
        _logScale = (int *) malloc(sizeof(int) * _dataSize);
        //float count = 0;
        for (int i = 0; i < _dataSize; i++) {
@@ -137,7 +145,7 @@ FFTGraph::~FFTGraph()
 }
 
 bool
-FFTGraph::on_expose_event (GdkEventExpose* event)
+FFTGraph::on_expose_event (GdkEventExpose* /*event*/)
 {
        redraw();
        return true;
@@ -151,23 +159,6 @@ FFTGraph::prepareResult(Gdk::Color color, string trackname)
        return res;
 }
 
-void
-FFTGraph::analyze(float *window, float *composite)
-{      
-       int i;
-       // Copy the data and apply the hanning window
-       for (i = 0; i < _windowSize; i++) {
-               _in[i] = window[ i ] * _hanning[ i ];
-       }
-
-       fftwf_execute(_plan);
-
-       composite[0] += (_out[0] * _out[0]);
-       
-       for (i=1; i < _dataSize - 1; i++) { // TODO: check with Jesse whether this is really correct
-               composite[i] += (_out[i] * _out[i]) + (_out[_windowSize-i] * _out[_windowSize-i]);
-       }
-}
 
 void
 FFTGraph::set_analysis_window(AnalysisWindow *a_window)
@@ -178,13 +169,13 @@ FFTGraph::set_analysis_window(AnalysisWindow *a_window)
 void
 FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
 {
-       
+
        Glib::RefPtr<Gtk::Style> style = get_style();
        Glib::RefPtr<Gdk::GC> black = style->get_black_gc();
        Glib::RefPtr<Gdk::GC> white = style->get_white_gc();
-       
+
        window->draw_rectangle(black, true, 0, 0, width, height);
-       
+
        /**
         *  4          5
         *  _          _
@@ -198,17 +189,18 @@ FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
        window->draw_line(white, h_margin, v_margin, h_margin, height - v_margin );
 
        // Line 2
-       window->draw_line(white, width - h_margin, v_margin, width - h_margin, height - v_margin );
+       window->draw_line(white, width - h_margin + 1, v_margin, width - h_margin + 1, height - v_margin );
 
        // Line 3
        window->draw_line(white, h_margin, height - v_margin, width - h_margin, height - v_margin );
 
 #define DB_METRIC_LENGTH 8
-       // Line 5
+       // Line 4
        window->draw_line(white, h_margin - DB_METRIC_LENGTH, v_margin, h_margin, v_margin );
-       
-       // Line 6
-       window->draw_line(white, width - h_margin, v_margin, width - h_margin + DB_METRIC_LENGTH, v_margin );
+
+       // Line 5
+       window->draw_line(white, width - h_margin + 1, v_margin, width - h_margin + DB_METRIC_LENGTH, v_margin );
+
 
 
        if (graph_gc == 0) {
@@ -218,7 +210,7 @@ FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
        Color grey;
 
        grey.set_rgb_p(0.2, 0.2, 0.2);
-       
+
        graph_gc->set_rgb_fg_color( grey );
 
        if (layout == 0) {
@@ -229,18 +221,34 @@ FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
        // Draw logscale
        int logscale_pos = 0;
        int position_on_scale;
+
+
+/* TODO, write better scales and change the log function so that octaves are of equal pixel length
+       float scale_points[10] = { 55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0, 14080.0, 28160.0 };
+
+       for (int x = 0; x < 10; x++) {
+
+               // i = 0.. _dataSize-1
+               float freq_at_bin = (SR/2.0) * ((double)i / (double)_dataSize);
+
+
+
+                       freq_at_pixel = FFT_START * exp( FFT_RANGE * pixel / (double)(currentScaleWidth - 1) );
+       }
+       */
+
        for (int x = 1; x < 8; x++) {
-               position_on_scale = (int)floor( (double)scaleWidth*(double)x/8.0);
+               position_on_scale = (int)floor( (double)currentScaleWidth*(double)x/8.0);
 
                while (_logScale[logscale_pos] < position_on_scale)
                        logscale_pos++;
-               
+
                int coord = (int)(v_margin + 1.0 + position_on_scale);
-               
+
                int SR = 44100;
 
                int rate_at_pos = (int)((double)(SR/2) * (double)logscale_pos / (double)_dataSize);
-               
+
                char buf[32];
                if (rate_at_pos < 1000)
                        snprintf(buf,32,"%dHz",rate_at_pos);
@@ -248,42 +256,49 @@ FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
                        snprintf(buf,32,"%dk",(int)floor( (float)rate_at_pos/(float)1000) );
 
                std::string label = buf;
-               
+
                layout->set_text(label);
-               
-               window->draw_line(graph_gc, coord, v_margin, coord, height - v_margin);
+
+               window->draw_line(graph_gc, coord, v_margin, coord, height - v_margin - 1);
 
                int width, height;
                layout->get_pixel_size (width, height);
-               
+
                window->draw_layout(white, coord - width / 2, v_margin / 2, layout);
-               
+
        }
 
 }
 
 void
 FFTGraph::redraw()
-{      
-       Glib::Mutex::Lock lm  (_a_window->track_list_lock);
+{
+       Glib::Threads::Mutex::Lock lm  (_a_window->track_list_lock);
 
        draw_scales(get_window());
-       
+
+
        if (_a_window == 0)
                return;
 
        if (!_a_window->track_list_ready)
                return;
-       
-       
+
+       cairo_t *cr;
+       cr = gdk_cairo_create(GDK_DRAWABLE(get_window()->gobj()));
+       cairo_set_line_width(cr, 1.5);
+       cairo_translate(cr, (float)v_margin + 1.0, (float)h_margin);
+
+
+
        // Find "session wide" min & max
-       float min =  1000000000000.0;
-       float max = -1000000000000.0;
-       
+       float minf =  1000000000000.0;
+       float maxf = -1000000000000.0;
+
        TreeNodeChildren track_rows = _a_window->track_list.get_model()->children();
-       
+
        for (TreeIter i = track_rows.begin(); i != track_rows.end(); i++) {
-               
+
                TreeModel::Row row = *i;
                FFTResult *res = row[_a_window->tlcols.graph];
 
@@ -291,107 +306,146 @@ FFTGraph::redraw()
                if (res->minimum() == res->maximum()) {
                        continue;
                }
-               
-               if ( res->minimum() < min) {
-                       min = res->minimum();
+
+               if ( res->minimum() < minf) {
+                       minf = res->minimum();
                }
 
-               if ( res->maximum() > max) {
-                       max = res->maximum();
+               if ( res->maximum() > maxf) {
+                       maxf = res->maximum();
                }
        }
-       
-       int graph_height = height - 2 * h_margin;
 
-       if (graph_gc == 0) {
-               graph_gc = GC::create( get_window() );
+       if (!_show_normalized) {
+               minf = -150.0f;
+               maxf = 0.0f;
        }
-       
-       
-       double pixels_per_db = (double)graph_height / (double)(max - min);
-       
-       
+
+       //int graph_height = height - 2 * h_margin;
+
+
+
+       float fft_pane_size_w = (float)(width  - 2*v_margin) - 1.0;
+       float fft_pane_size_h = (float)(height - 2*h_margin);
+
+       double pixels_per_db = (double)fft_pane_size_h / (double)(maxf - minf);
+
+       cairo_rectangle(cr, 0.0, 0.0, fft_pane_size_w, fft_pane_size_h);
+       cairo_clip(cr);
+
        for (TreeIter i = track_rows.begin(); i != track_rows.end(); i++) {
-               
+
                TreeModel::Row row = *i;
 
                // don't show graphs for tracks which are deselected
                if (!row[_a_window->tlcols.visible]) {
                        continue;
                }
-               
+
                FFTResult *res = row[_a_window->tlcols.graph];
 
                // don't show graphs for empty signals
                if (res->minimum() == res->maximum()) {
                        continue;
                }
-               
-               std::string name = row[_a_window->tlcols.trackname];
+
+               float mpp;
+
+               if (_show_minmax) {
+                       mpp = -1000000.0;
+
+                       cairo_set_source_rgba(cr, res->get_color().get_red_p(), res->get_color().get_green_p(), res->get_color().get_blue_p(), 0.30);
+                       cairo_move_to(cr, 0.5f + (float)_logScale[0], 0.5f + (float)( fft_pane_size_h - (int)floor( (res->maxAt(0) - minf) * pixels_per_db) ));
+
+                       // Draw the line of maximum values
+                       for (int x = 1; x < res->length(); x++) {
+                               if (res->maxAt(x) > mpp)
+                                       mpp = res->maxAt(x);
+                               mpp = fmax(mpp, minf);
+                               mpp = fmin(mpp, maxf);
+
+                               // If the next point on the log scale is at the same location,
+                               // don't draw yet
+                               if (x + 1 < res->length() && _logScale[x] == _logScale[x + 1]) {
+                                       continue;
+                               }
+
+                               float X = 0.5f + (float)_logScale[x];
+                               float Y = 0.5f + (float)( fft_pane_size_h - (int)floor( (mpp - minf) * pixels_per_db) );
+
+                               cairo_line_to(cr, X, Y);
+
+                               mpp = -1000000.0;
+                       }
+
+                       mpp = +10000000.0;
+                       // Draw back to the start using the minimum value
+                       for (int x = res->length()-1; x >= 0; x--) {
+                               if (res->minAt(x) < mpp)
+                                       mpp = res->minAt(x);
+                               mpp = fmax(mpp, minf);
+                               mpp = fmin(mpp, maxf);
+
+                               // If the next point on the log scale is at the same location,
+                               // don't draw yet
+                               if (x - 1 > 0 && _logScale[x] == _logScale[x - 1]) {
+                                       continue;
+                               }
+
+                               float X = 0.5f + (float)_logScale[x];
+                               float Y = 0.5f + (float)( fft_pane_size_h - (int)floor( (mpp - minf) * pixels_per_db) );
+
+                               cairo_line_to(cr, X, Y );
+
+                               mpp = +10000000.0;
+                       }
+
+                       cairo_close_path(cr);
+
+                       cairo_fill(cr);
+               }
+
+
 
                // Set color from track
-               graph_gc->set_rgb_fg_color( res->get_color() );
-
-               float mpp = -1000000.0;
-               int prevx = 0;
-               float prevSample = min;
-               
-               for (int x = 0; x < res->length() - 1; x++) {
-                       
-                       if (res->sampleAt(x) > mpp)
-                               mpp = res->sampleAt(x);
-                       
+               cairo_set_source_rgb(cr, res->get_color().get_red_p(), res->get_color().get_green_p(), res->get_color().get_blue_p());
+
+               mpp = -1000000.0;
+
+               cairo_move_to(cr, 0.5, fft_pane_size_h-0.5);
+
+               for (int x = 0; x < res->length(); x++) {
+
+
+                       if (res->avgAt(x) > mpp)
+                               mpp = res->avgAt(x);
+                       mpp = fmax(mpp, minf);
+                       mpp = fmin(mpp, maxf);
+
                        // If the next point on the log scale is at the same location,
                        // don't draw yet
-                       if (x + 1 < res->length() && 
-                               _logScale[x] == _logScale[x + 1]) {
+                       if (x + 1 < res->length() && _logScale[x] == _logScale[x + 1]) {
                                continue;
                        }
 
-                       get_window()->draw_line(
-                                       graph_gc,
-                                       v_margin + 1 + prevx,
-                                       graph_height - (int)floor( (prevSample - min) * pixels_per_db) + h_margin - 1,
-                                       v_margin + 1 + _logScale[x],
-                                       graph_height - (int)floor( (mpp        - min) * pixels_per_db) + h_margin - 1);
-                       
-                       prevx = _logScale[x];
-                       prevSample = mpp;
-                       
+                       cairo_line_to(cr, 0.5f + (float)_logScale[x], 0.5f + (float)( fft_pane_size_h - (int)floor( (mpp - minf) * pixels_per_db) ));
 
                        mpp = -1000000.0;
-                       
                }
+
+               cairo_stroke(cr);
        }
 
+       cairo_destroy(cr);
 }
 
 void
 FFTGraph::on_size_request(Gtk::Requisition* requisition)
 {
-       width  = scaleWidth  + h_margin * 2;
-       height = scaleHeight + 2 + v_margin * 2;
+       width  = max(requisition->width,  minScaleWidth  + h_margin * 2);
+       height = max(requisition->height, minScaleHeight + 2 + v_margin * 2);
 
-       if (_logScale != 0) {
-               free(_logScale);
-       }
-       _logScale = (int *) malloc(sizeof(int) * _dataSize);
-
-       float SR = 44100;
-       float FFT_START = SR/(double)_dataSize;
-       float FFT_END = SR/2.0;
-       float FFT_RANGE = log( FFT_END / FFT_START);
-       float pixel = 0;
-       for (int i = 0; i < _dataSize; i++) {
-               float freq_at_bin = (SR/2.0) * ((double)i / (double)_dataSize);
-               float freq_at_pixel = FFT_START * exp( FFT_RANGE * pixel / (double)scaleWidth );
-               while (freq_at_bin > freq_at_pixel) {
-                       pixel++;
-                       freq_at_pixel = FFT_START * exp( FFT_RANGE * pixel / (double)scaleWidth );
-               }
-               _logScale[i] = (int)floor(pixel);
-//printf("logscale at %d = %3.3f, freq_at_pixel %3.3f, freq_at_bin %3.3f, scaleWidth %d\n", i, pixel, freq_at_pixel, freq_at_bin, scaleWidth);
-       }
+       update_size();
 
        requisition->width  = width;;
        requisition->height = height;
@@ -402,8 +456,33 @@ FFTGraph::on_size_allocate(Gtk::Allocation & alloc)
 {
        width = alloc.get_width();
        height = alloc.get_height();
-       
+
+       update_size();
+
        DrawingArea::on_size_allocate (alloc);
+}
+
+void
+FFTGraph::update_size()
+{
+       currentScaleWidth  = width - h_margin*2;
+       currentScaleHeight = height - 2 - v_margin*2;
 
+       float SR = 44100;
+       float FFT_START = SR/(double)_dataSize;
+       float FFT_END = SR/2.0;
+       float FFT_RANGE = log( FFT_END / FFT_START);
+       float pixel = 0;
+       for (int i = 0; i < _dataSize; i++) {
+               float freq_at_bin = (SR/2.0) * ((double)i / (double)_dataSize);
+               float freq_at_pixel;
+               pixel--;
+               do {
+                       pixel++;
+                       freq_at_pixel = FFT_START * exp( FFT_RANGE * pixel / (double)(currentScaleWidth - 1) );
+               } while (freq_at_bin > freq_at_pixel);
+
+               _logScale[i] = (int)floor(pixel);
+       }
 }