Improvements on the FFT analysis tool
authorSampo Savolainen <v2@iki.fi>
Tue, 4 Mar 2008 17:56:17 +0000 (17:56 +0000)
committerSampo Savolainen <v2@iki.fi>
Tue, 4 Mar 2008 17:56:17 +0000 (17:56 +0000)
 - Enable FFT analysis by default
 - FFT graph is now in cairo
 - The window is now a window instead of a dialog
 - Analysis window can be resized
 - The view can be switched between normalized
   and an absolute value
 - The minimum and maximum values for a graph can
   be shown

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3135 d708f5d6-7413-0410-9779-e7cbd77b26cf

SConstruct
gtk2_ardour/analysis_window.cc
gtk2_ardour/analysis_window.h
gtk2_ardour/fft_graph.cc
gtk2_ardour/fft_graph.h
gtk2_ardour/fft_result.cc
gtk2_ardour/fft_result.h

index b68a45d621fd5f508c677fd9e066a1575ee8f7b9..804fa6bf9119784e1835b42f061545b963cb8cf6 100644 (file)
@@ -38,7 +38,7 @@ opts.AddOptions(
     EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'leopard', 'none' ), ignorecase=2),
     BoolOption('DMALLOC', 'Compile and link using the dmalloc library', 0),
     BoolOption('EXTRA_WARN', 'Compile with -Wextra, -ansi, and -pedantic.  Might break compilation.  For pedants', 0),
-    BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0),
+    BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 1),
     BoolOption('FPU_OPTIMIZATION', 'Build runtime checked assembler code', 1),
     BoolOption('LIBLO', 'Compile with support for liblo library', 1),
     BoolOption('NLS', 'Set to turn on i18n support', 1),
@@ -527,7 +527,7 @@ if env['FFT_ANALYSIS']:
         conf = Configure(libraries['fftw3'])
 
         if conf.CheckHeader ('fftw3.h') == False:
-            print ('FFT Analysis cannot be compiled without the FFTW3 headers, which do not seem to be installed')
+            print ('Ardour cannot be compiled without the FFTW3 headers, which do not seem to be installed')
             sys.exit (1)            
         conf.Finish()
 
index 957dde3d1fcbcd60daf2ef9e9b519c942632bd96..ae557f4c106eabaea39c79b01d2e20182493745a 100644 (file)
 using namespace ARDOUR;
 using namespace PBD;
 
-AnalysisWindow::AnalysisWindow()
-       : ArdourDialog(_("analysis window")),
+AnalysisWindow::AnalysisWindow() :
+
+         show_minmax_button     (_("Show frequency power range")),
+         show_normalized_button (_("Normalize values")),
        
          source_selection_label       (_("Signal source")),
          source_selection_ranges_rb   (_("Selected ranges")),
@@ -52,8 +54,11 @@ AnalysisWindow::AnalysisWindow()
          display_model_composite_separate_rb   (_("Composite graphs for each track")),
          display_model_composite_all_tracks_rb (_("Composite graph of all tracks")),
 
-         fft_graph (2048)
+         fft_graph (16384)
 {
+       set_name(_("FFT analysis window"));
+       set_title(_("FFT analysis window"));
+
        track_list_ready = false;
        
        // Left side: track list + controls
@@ -124,17 +129,31 @@ AnalysisWindow::AnalysisWindow()
                                bind ( mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_all_tracks_rb));
        }
 
-       vbox.pack_start(hseparator2, false, false);
+       // Analyze button
 
        refresh_button.set_name("EditorGTKButton");
-       refresh_button.set_label(_("Analyze data"));
+       refresh_button.set_label(_("Re-analyze data"));
 
        refresh_button.signal_clicked().connect ( bind ( mem_fun(*this, &AnalysisWindow::analyze_data), &refresh_button)); 
 
        vbox.pack_start(refresh_button, false, false, 10);
+
+
+       // Feature checkboxes
+
+       // minmax
+       show_minmax_button.signal_toggled().connect( mem_fun(*this, &AnalysisWindow::show_minmax_changed));
+       vbox.pack_start(show_minmax_button, false, false);
+
+       // normalize
+       show_normalized_button.signal_toggled().connect( mem_fun(*this, &AnalysisWindow::show_normalized_changed));
+       vbox.pack_start(show_normalized_button, false, false);
+
        
+
        
-       hbox.pack_start(vbox);
+       
+       hbox.pack_start(vbox, Gtk::PACK_SHRINK);
        
        // Analysis window on the right
        fft_graph.ensure_style();
@@ -144,11 +163,9 @@ AnalysisWindow::AnalysisWindow()
        
 
        // And last we pack the hbox
-       get_vbox()->pack_start(hbox);
-
+       add(hbox);
+       show_all();
        track_list.show_all();
-
-       get_vbox()->show_all();
 }
 
 AnalysisWindow::~AnalysisWindow()
@@ -156,6 +173,18 @@ AnalysisWindow::~AnalysisWindow()
 
 }
 
+void
+AnalysisWindow::show_minmax_changed()
+{
+       fft_graph.set_show_minmax(show_minmax_button.get_active());
+}
+
+void
+AnalysisWindow::show_normalized_changed()
+{
+       fft_graph.set_show_normalized(show_normalized_button.get_active());
+}
+
 void
 AnalysisWindow::set_rangemode()
 {
index cd1243bb6ac63c0946556f931f6679db77d32123..45752c4a34a6886d4aa15f9e14d312d3a463d4eb 100644 (file)
@@ -31,6 +31,7 @@
 #include <gtkmm/label.h>
 #include <gtkmm/liststore.h>
 #include <gtkmm/separator.h>
+#include <gtkmm/window.h>
 
 #include <gtkmm2ext/dndtreeview.h>
 
@@ -42,7 +43,7 @@
 #include "fft_result.h"
 
 
-class AnalysisWindow : public ArdourDialog
+class AnalysisWindow : public Gtk::Window
 {
   public:
          AnalysisWindow  ();
@@ -55,12 +56,18 @@ class AnalysisWindow : public ArdourDialog
 
          void analyze ();
 
+         const void set_session(ARDOUR::Session *session) { _session = session; };
+
   private:
          
+         ARDOUR::Session *_session;
+
          void clear_tracklist();
 
          void source_selection_changed (Gtk::RadioButton *);
          void display_model_changed    (Gtk::RadioButton *);
+         void show_minmax_changed      ();
+         void show_normalized_changed  ();
 
          void analyze_data                             (Gtk::Button *);
          
@@ -88,7 +95,8 @@ class AnalysisWindow : public ArdourDialog
          Gtk::TreeView track_list;
          
          Gtk::Label source_selection_label;
-         
+
+
          Gtk::RadioButton source_selection_ranges_rb;
          Gtk::RadioButton source_selection_regions_rb;
 
@@ -98,9 +106,13 @@ class AnalysisWindow : public ArdourDialog
          Gtk::RadioButton display_model_composite_separate_rb;
          Gtk::RadioButton display_model_composite_all_tracks_rb;
          
-         Gtk::HSeparator hseparator2;
-
          Gtk::Button refresh_button;
+
+
+         Gtk::CheckButton show_minmax_button;
+         Gtk::CheckButton show_normalized_button;
+
+         
          
          // The graph
          FFTGraph fft_graph;
index e7a0fd75b6ffadb8904307c04f72f99e32c18b4b..077c1e162c3f913fab2050106a0672068b4b4341 100644 (file)
@@ -51,6 +51,9 @@ FFTGraph::FFTGraph(int windowSize)
 
        _a_window = 0;
 
+       _show_minmax     = false;
+       _show_normalized = false;
+
        setWindowSize(windowSize);
 }
 
@@ -151,23 +154,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)
@@ -198,17 +184,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) {
@@ -229,8 +216,24 @@ 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++;
@@ -251,7 +254,7 @@ FFTGraph::draw_scales(Glib::RefPtr<Gdk::Window> window)
                
                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);
@@ -268,12 +271,19 @@ FFTGraph::redraw()
        Glib::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
@@ -300,17 +310,24 @@ FFTGraph::redraw()
                        max = res->maximum();
                }
        }
-       
-       int graph_height = height - 2 * h_margin;
 
-       if (graph_gc == 0) {
-               graph_gc = GC::create( get_window() );
+       if (!_show_normalized) {
+               min = -150.0f;
+               max = 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)(max - min);
+
+       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;
@@ -326,72 +343,104 @@ FFTGraph::redraw()
                if (res->minimum() == res->maximum()) {
                        continue;
                }
+       
+               float mpp;
                
-               std::string name = row[_a_window->tlcols.trackname];
+               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) - min) * 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, min);
+                               mpp = fmin(mpp, max);
+
+                               // 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 - min) * 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, min);
+                               mpp = fmin(mpp, max);
+
+                               // 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 - min) * 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() );
+               cairo_set_source_rgb(cr, res->get_color().get_red_p(), res->get_color().get_green_p(), res->get_color().get_blue_p());
 
-               float mpp = -1000000.0;
-               int prevx = 0;
-               float prevSample = min;
-               
-               for (int x = 0; x < res->length() - 1; x++) {
+               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->sampleAt(x) > mpp)
-                               mpp = res->sampleAt(x);
+
+                       if (res->avgAt(x) > mpp)
+                               mpp = res->avgAt(x);
+                       mpp = fmax(mpp, min);
+                       mpp = fmin(mpp, max);
                        
                        // 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 - min) * 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;
@@ -403,7 +452,32 @@ 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);
+       }
 }
 
index 73636b989d802d36007a36e3e522cbfdf270b04d..bbf777474199fd92503a5b72aac54f1fca2da3f2 100644 (file)
@@ -54,27 +54,34 @@ class FFTGraph : public Gtk::DrawingArea
                void on_size_allocate(Gtk::Allocation & alloc);
                FFTResult *prepareResult(Gdk::Color color, std::string trackname);
                
+               const void set_show_minmax     (bool v) { _show_minmax     = v; redraw(); };
+               const void set_show_normalized (bool v) { _show_normalized = v; redraw(); };
+
        private:
 
+               void update_size();
+
                void setWindowSize_internal(int windowSize);
 
                void draw_scales(Glib::RefPtr<Gdk::Window> window);
                
-               static const int scaleWidth = 512;
-               static const int scaleHeight = 420;
+               static const int minScaleWidth = 512;
+               static const int minScaleHeight = 420;
+
+               int currentScaleWidth;
+               int currentScaleHeight;
 
                static const int h_margin = 20;
                static const int v_margin = 20;
+               Glib::RefPtr<Gdk::GC> graph_gc;
 
                int width;
                int height;
                
-               void analyze(float *window, float *composite);
                int _windowSize;
                int _dataSize;
 
                Glib::RefPtr<Pango::Layout> layout;
-               Glib::RefPtr<Gdk::GC> graph_gc;
                AnalysisWindow *_a_window;
 
                fftwf_plan _plan;
@@ -84,6 +91,9 @@ class FFTGraph : public Gtk::DrawingArea
                float *_hanning;
                int *_logScale;
 
+               bool _show_minmax;
+               bool _show_normalized;
+
        friend class FFTResult;
 };
 
index f5acef92ed64b6a8ac905d72109549e7bbb0fddf..d692b9152b8c662871189b62d1a1205626aedf76 100644 (file)
@@ -37,8 +37,16 @@ FFTResult::FFTResult(FFTGraph *graph, Gdk::Color color, string trackname)
 
        _averages = 0;
 
-       _data = (float *) malloc(sizeof(float) * _dataSize);
-       memset(_data,0,sizeof(float) * _dataSize);
+       _data_avg = (float *) malloc(sizeof(float) * _dataSize);
+       memset(_data_avg,0,sizeof(float) * _dataSize);
+
+       _data_min = (float *) malloc(sizeof(float) * _dataSize);
+       _data_max = (float *) malloc(sizeof(float) * _dataSize);
+
+       for (int i = 0; i < _dataSize; i++) {
+               _data_min[i] = FLT_MAX;
+               _data_max[i] = FLT_MIN;
+       }
 
        _color     = color;
        _trackname = trackname;
@@ -47,7 +55,34 @@ FFTResult::FFTResult(FFTGraph *graph, Gdk::Color color, string trackname)
 void
 FFTResult::analyzeWindow(float *window)
 {
-       _graph->analyze(window, _data);
+       float *_hanning = _graph->_hanning;
+       float *_in = _graph->_in;
+       float *_out = _graph->_out;
+
+       int i;
+       // Copy the data and apply the hanning window
+       for (i = 0; i < _windowSize; i++) {
+               _in[i] = window[ i ] * _hanning[ i ];
+       }
+
+       fftwf_execute(_graph->_plan);
+
+       float b = _out[0] * _out[0];
+
+       _data_avg[0] += b;
+       if (b < _data_min[0]) _data_min[0] = b;
+       if (b > _data_max[0]) _data_max[0] = b;
+       
+       for (i=1; i < _dataSize - 1; i++) { // TODO: check with Jesse whether this is really correct
+               b = (_out[i] * _out[i]);
+
+               _data_avg[i] += b;  // + (_out[_windowSize-i] * _out[_windowSize-i]);, TODO: thanks to Stefan Kost
+               
+               if (_data_min[i] > b)  _data_min[i] = b;
+               if (_data_max[i] < b ) _data_max[i] = b;
+       }
+
+
        _averages++;
 }
 
@@ -59,21 +94,27 @@ FFTResult::finalize()
                _maximum = 0.0;
                return;
        }
-       
+
        // Average & scale
        for (int i = 0; i < _dataSize; i++) {
-               _data[i] /= _averages;
-               _data[i]  = 10.0f * log10f(_data[i]); 
+               _data_avg[i] /= _averages;
+               _data_avg[i]  = 10.0f * log10f(_data_avg[i]); 
+
+               _data_min[i]  = 10.0f * log10f(_data_min[i]); 
+               if (_data_min[i] < -10000.0f) {
+                       _data_min[i] = -10000.0f;
+               }
+               _data_max[i]  = 10.0f * log10f(_data_max[i]); 
        }
 
        // find min & max
-       _minimum = _maximum = _data[0];
+       _minimum = _maximum = _data_avg[0];
        
        for (int i = 1; i < _dataSize; i++) {
-               if (_data[i] < _minimum        && !isinf(_data[i])) {
-                       _minimum = _data[i];
-               } else if (_data[i] > _maximum && !isinf(_data[i])) {
-                       _maximum = _data[i];
+               if (_data_avg[i] < _minimum        && !isinf(_data_avg[i])) {
+                       _minimum = _data_avg[i];
+               } else if (_data_avg[i] > _maximum && !isinf(_data_avg[i])) {
+                       _maximum = _data_avg[i];
                }
        }
 
@@ -82,16 +123,36 @@ FFTResult::finalize()
 
 FFTResult::~FFTResult()
 {
-       free(_data);
+       free(_data_avg);
+       free(_data_min);
+       free(_data_max);
 }
 
 
 float
-FFTResult::sampleAt(int x)
+FFTResult::avgAt(int x)
+{
+       if (x < 0 || x>= _dataSize)
+               return 0.0f;
+
+       return _data_avg[x];
+}
+
+float
+FFTResult::minAt(int x)
+{
+       if (x < 0 || x>= _dataSize)
+               return 0.0f;
+
+       return _data_min[x];
+}
+
+float
+FFTResult::maxAt(int x)
 {
        if (x < 0 || x>= _dataSize)
                return 0.0f;
 
-       return _data[x];
+       return _data_max[x];
 }
 
index c8f17dc01c5b08b866a7d4a5d81129be5f373749..c6c952db1c348ae19740c7000cc0cf173915a6f8 100644 (file)
@@ -41,7 +41,9 @@ class FFTResult
 
                const int length() { return _dataSize; }
 
-               float sampleAt(int x);
+               float avgAt(int x);
+               float maxAt(int x);
+               float minAt(int x);
                
                const float minimum() { return _minimum; }
                const float maximum() { return _maximum; }
@@ -53,10 +55,13 @@ class FFTResult
                
                int     _averages;
 
-               float*  _data;
+               float*  _data_avg;
+               float*  _data_max;
+               float*  _data_min;
+
                float*  _work;
 
-               int             _windowSize;
+               int     _windowSize;
                int     _dataSize;
 
                float   _minimum;