| @@ -672,6 +672,41 @@ Module::draw_box ( int tx, int ty, int tw, int th ) | |||||
| fl_pop_clip(); | fl_pop_clip(); | ||||
| } | } | ||||
| #include "SpectrumView.H" | |||||
| #include <FL/Fl_Double_Window.H> | |||||
| bool | |||||
| Module::show_analysis_window ( void ) | |||||
| { | |||||
| nframes_t nframes = 4096; | |||||
| float *buf = new float[nframes]; | |||||
| if ( ! get_impulse_response( buf, nframes ) ) | |||||
| return false; | |||||
| Fl_Double_Window *w = new Fl_Double_Window( 1000, 500 ); | |||||
| { | |||||
| SpectrumView * o = new SpectrumView( 25,25, 1000 - 50, 500 - 50, label() ); | |||||
| o->labelsize(10); | |||||
| o->align(FL_ALIGN_RIGHT|FL_ALIGN_TOP); | |||||
| o->sample_rate( sample_rate() ); | |||||
| /* o->minimum_frequency( 10 ); */ | |||||
| /* o->maximum_frequency( 50000 ); */ | |||||
| o->data( buf, nframes ); | |||||
| } | |||||
| w->end(); | |||||
| w->show(); | |||||
| while ( w->shown() ) | |||||
| Fl::wait(); | |||||
| return true; | |||||
| } | |||||
| void | void | ||||
| Module::draw_label ( int tx, int ty, int tw, int th ) | Module::draw_label ( int tx, int ty, int tw, int th ) | ||||
| { | { | ||||
| @@ -888,6 +923,10 @@ Module::menu_cb ( const Fl_Menu_ *m ) | |||||
| { | { | ||||
| paste_before(); | paste_before(); | ||||
| } | } | ||||
| else if ( ! strcmp( picked, "Show Analysis" ) ) | |||||
| { | |||||
| show_analysis_window(); | |||||
| } | |||||
| else if ( ! strcmp( picked, "Remove" ) ) | else if ( ! strcmp( picked, "Remove" ) ) | ||||
| command_remove(); | command_remove(); | ||||
| } | } | ||||
| @@ -924,10 +963,12 @@ Module::menu ( void ) const | |||||
| m.add( "Insert", 0, &Module::menu_cb, (void*)this, 0); | m.add( "Insert", 0, &Module::menu_cb, (void*)this, 0); | ||||
| m.add( "Insert", 0, &Module::menu_cb, const_cast< Fl_Menu_Item *>( insert_menu->menu() ), FL_SUBMENU_POINTER ); | m.add( "Insert", 0, &Module::menu_cb, const_cast< Fl_Menu_Item *>( insert_menu->menu() ), FL_SUBMENU_POINTER ); | ||||
| m.add( "Edit Parameters", ' ', &Module::menu_cb, (void*)this, 0 ); | m.add( "Edit Parameters", ' ', &Module::menu_cb, (void*)this, 0 ); | ||||
| m.add( "Show Analysis", 's', &Module::menu_cb, (void*)this, 0); | |||||
| m.add( "Bypass", 'b', &Module::menu_cb, (void*)this, FL_MENU_TOGGLE | ( bypass() ? FL_MENU_VALUE : 0 ) ); | m.add( "Bypass", 'b', &Module::menu_cb, (void*)this, FL_MENU_TOGGLE | ( bypass() ? FL_MENU_VALUE : 0 ) ); | ||||
| m.add( "Cut", FL_CTRL + 'x', &Module::menu_cb, (void*)this, is_default() ? FL_MENU_INACTIVE : 0 ); | m.add( "Cut", FL_CTRL + 'x', &Module::menu_cb, (void*)this, is_default() ? FL_MENU_INACTIVE : 0 ); | ||||
| m.add( "Copy", FL_CTRL + 'c', &Module::menu_cb, (void*)this, is_default() ? FL_MENU_INACTIVE : 0 ); | m.add( "Copy", FL_CTRL + 'c', &Module::menu_cb, (void*)this, is_default() ? FL_MENU_INACTIVE : 0 ); | ||||
| m.add( "Paste", FL_CTRL + 'v', &Module::menu_cb, (void*)this, _copied_module_empty ? 0 : FL_MENU_INACTIVE ); | m.add( "Paste", FL_CTRL + 'v', &Module::menu_cb, (void*)this, _copied_module_empty ? 0 : FL_MENU_INACTIVE ); | ||||
| m.add( "Remove", FL_Delete, &Module::menu_cb, (void*)this ); | m.add( "Remove", FL_Delete, &Module::menu_cb, (void*)this ); | ||||
| // menu_set_callback( menu, &Module::menu_cb, (void*)this ); | // menu_set_callback( menu, &Module::menu_cb, (void*)this ); | ||||
| @@ -440,7 +440,9 @@ public: | |||||
| char *get_parameters ( void ) const; | char *get_parameters ( void ) const; | ||||
| void set_parameters ( const char * ); | void set_parameters ( const char * ); | ||||
| bool show_analysis_window ( void ); | |||||
| void send_feedback ( void ); | void send_feedback ( void ); | ||||
| virtual bool initialize ( void ) { return true; } | virtual bool initialize ( void ) { return true; } | ||||
| @@ -468,6 +470,13 @@ public: | |||||
| virtual void handle_port_connection_change () {} | virtual void handle_port_connection_change () {} | ||||
| /* module should create a new context, run against this impulse, | |||||
| * and return true if there's anything worth reporting */ | |||||
| virtual bool get_impulse_response ( sample_t *buf, nframes_t nframes ) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| #define MODULE_CLONE_FUNC(class) \ | #define MODULE_CLONE_FUNC(class) \ | ||||
| virtual Module *clone_empty ( void ) const \ | virtual Module *clone_empty ( void ) const \ | ||||
| { \ | { \ | ||||
| @@ -480,8 +489,6 @@ public: | |||||
| protected: | protected: | ||||
| nframes_t sample_rate ( void ) const { return Module::_sample_rate; } | |||||
| void draw_connections ( void ); | void draw_connections ( void ); | ||||
| void draw_label ( int X, int Y, int W, int H ); | void draw_label ( int X, int Y, int W, int H ); | ||||
| void draw_box ( int X, int Y, int W, int H ); | void draw_box ( int X, int Y, int W, int H ); | ||||
| @@ -495,6 +502,8 @@ protected: | |||||
| bool add_aux_port ( bool input, const char *prefix, int n ); | bool add_aux_port ( bool input, const char *prefix, int n ); | ||||
| public: | public: | ||||
| nframes_t sample_rate ( void ) const { return Module::_sample_rate; } | |||||
| void auto_connect_outputs(); | void auto_connect_outputs(); | ||||
| void auto_disconnect_outputs(); | void auto_disconnect_outputs(); | ||||
| @@ -47,6 +47,8 @@ | |||||
| #include "FL/menu_popup.H" | #include "FL/menu_popup.H" | ||||
| #include "SpectrumView.H" | |||||
| Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 900,240) | Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 900,240) | ||||
| { | { | ||||
| _module = module; | _module = module; | ||||
| @@ -97,6 +99,7 @@ Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_ | |||||
| o->flow( true ); | o->flow( true ); | ||||
| o->vspacing( 5 ); | o->vspacing( 5 ); | ||||
| o->hspacing( 5 ); | o->hspacing( 5 ); | ||||
| o->end(); | o->end(); | ||||
| } | } | ||||
| o->resizable( 0 ); | o->resizable( 0 ); | ||||
| @@ -117,6 +120,29 @@ Module_Parameter_Editor::~Module_Parameter_Editor ( ) | |||||
| void | |||||
| Module_Parameter_Editor::update_spectrum ( void ) | |||||
| { | |||||
| nframes_t nframes = 4096; | |||||
| float *buf = new float[nframes]; | |||||
| SpectrumView *o = spectrum_view; | |||||
| o->sample_rate( _module->sample_rate() ); | |||||
| if ( ! _module->get_impulse_response( buf, nframes ) ) | |||||
| { | |||||
| o->data( buf, 1 ); | |||||
| /* o->hide(); */ | |||||
| } | |||||
| else | |||||
| { | |||||
| o->data( buf, nframes ); | |||||
| o->parent()->show(); | |||||
| } | |||||
| o->redraw(); | |||||
| } | |||||
| void | void | ||||
| Module_Parameter_Editor::make_controls ( void ) | Module_Parameter_Editor::make_controls ( void ) | ||||
| { | { | ||||
| @@ -124,6 +150,17 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| control_pack->clear(); | control_pack->clear(); | ||||
| { SpectrumView *o = spectrum_view = new SpectrumView( 25, 40, 300, 240, "Spectrum" ); | |||||
| o->labelsize(9); | |||||
| o->align(FL_ALIGN_TOP); | |||||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( (Fl_Widget*)o ); | |||||
| flg->hide(); | |||||
| control_pack->add( flg ); | |||||
| } | |||||
| controls_by_port.clear(); | controls_by_port.clear(); | ||||
| /* these are for detecting related parameter groups which can be | /* these are for detecting related parameter groups which can be | ||||
| @@ -156,14 +193,14 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| control_pack->flow(true); | control_pack->flow(true); | ||||
| control_pack->flowdown(false); | control_pack->flowdown(false); | ||||
| control_pack->type( FL_HORIZONTAL ); | control_pack->type( FL_HORIZONTAL ); | ||||
| control_pack->size( 900, 350 ); | |||||
| control_pack->size( 900, 250 ); | |||||
| } | } | ||||
| else if ( mode_choice->value() == 0 ) | else if ( mode_choice->value() == 0 ) | ||||
| { | { | ||||
| control_pack->vspacing( 10 ); | control_pack->vspacing( 10 ); | ||||
| control_pack->hspacing( 10 ); | control_pack->hspacing( 10 ); | ||||
| control_pack->flow(true); | control_pack->flow(true); | ||||
| control_pack->flowdown(false); | |||||
| control_pack->flowdown(true); | |||||
| control_pack->type( FL_HORIZONTAL ); | control_pack->type( FL_HORIZONTAL ); | ||||
| control_pack->size( 700, 50 ); | control_pack->size( 700, 50 ); | ||||
| @@ -381,6 +418,8 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| } | } | ||||
| update_control_visibility(); | update_control_visibility(); | ||||
| update_spectrum(); | |||||
| control_pack->dolayout(); | control_pack->dolayout(); | ||||
| @@ -504,6 +543,8 @@ Module_Parameter_Editor::handle_control_changed ( Module::Port *p ) | |||||
| v->value( p->control_value() ); | v->value( p->control_value() ); | ||||
| } | } | ||||
| update_spectrum(); | |||||
| } | } | ||||
| @@ -524,6 +565,8 @@ Module_Parameter_Editor::set_value (int i, float value ) | |||||
| if ( _module->control_input[i].connected() ) | if ( _module->control_input[i].connected() ) | ||||
| _module->control_input[i].connected_port()->module()->handle_control_changed( _module->control_input[i].connected_port() ); | _module->control_input[i].connected_port()->module()->handle_control_changed( _module->control_input[i].connected_port() ); | ||||
| } | } | ||||
| update_spectrum(); | |||||
| // _module->handle_control_changed( &_module->control_input[i] ); | // _module->handle_control_changed( &_module->control_input[i] ); | ||||
| } | } | ||||
| @@ -27,6 +27,7 @@ class Module; | |||||
| class Fl_Menu_Button; | class Fl_Menu_Button; | ||||
| class Panner; | class Panner; | ||||
| class Fl_Scroll; | class Fl_Scroll; | ||||
| class SpectrumView; | |||||
| #include <vector> | #include <vector> | ||||
| #include <list> | #include <list> | ||||
| @@ -76,10 +77,12 @@ class Module_Parameter_Editor : public Fl_Double_Window | |||||
| void set_value (int i, float value ); | void set_value (int i, float value ); | ||||
| void bind_control ( int i ); | void bind_control ( int i ); | ||||
| void make_controls ( void ); | void make_controls ( void ); | ||||
| void update_spectrum ( void ); | |||||
| static void menu_cb ( Fl_Widget *w, void *v ); | static void menu_cb ( Fl_Widget *w, void *v ); | ||||
| void menu_cb ( Fl_Menu_ *m ); | void menu_cb ( Fl_Menu_ *m ); | ||||
| SpectrumView *spectrum_view; | |||||
| Fl_Scroll *control_scroll; | Fl_Scroll *control_scroll; | ||||
| Fl_Flowpack *control_pack; | Fl_Flowpack *control_pack; | ||||
| Fl_Menu_Button *mode_choice; | Fl_Menu_Button *mode_choice; | ||||
| @@ -778,6 +778,80 @@ Plugin_Module::handle_port_connection_change ( void ) | |||||
| bool | |||||
| Plugin_Module::get_impulse_response ( sample_t *buf, nframes_t nframes ) | |||||
| { | |||||
| memset( buf, 0, sizeof( float ) * nframes ); | |||||
| buf[0] = 1; | |||||
| apply( buf, nframes ); | |||||
| if ( buffer_is_digital_black( buf + 1, nframes - 1 )) | |||||
| /* no impulse response... */ | |||||
| return false; | |||||
| return true; | |||||
| } | |||||
| /** Instantiate a temporary version of the plugin, and run it (in place) against the provided buffer */ | |||||
| bool | |||||
| Plugin_Module::apply ( sample_t *buf, nframes_t nframes ) | |||||
| { | |||||
| // actually osc or UI THREAD_ASSERT( UI ); | |||||
| LADSPA_Handle h; | |||||
| if ( ! (h = _idata->descriptor->instantiate( _idata->descriptor, sample_rate() ) ) ) | |||||
| { | |||||
| WARNING( "Failed to instantiate plugin" ); | |||||
| return false; | |||||
| } | |||||
| int ij = 0; | |||||
| int oj = 0; | |||||
| for ( unsigned int k = 0; k < _idata->descriptor->PortCount; ++k ) | |||||
| { | |||||
| if ( LADSPA_IS_PORT_CONTROL( _idata->descriptor->PortDescriptors[k] ) ) | |||||
| { | |||||
| if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[k] ) ) | |||||
| _idata->descriptor->connect_port( h, k, (LADSPA_Data*)control_input[ij++].buffer() ); | |||||
| else if ( LADSPA_IS_PORT_OUTPUT( _idata->descriptor->PortDescriptors[k] ) ) | |||||
| _idata->descriptor->connect_port( h, k, (LADSPA_Data*)control_output[oj++].buffer() ); | |||||
| } | |||||
| } | |||||
| if ( _idata->descriptor->activate ) | |||||
| _idata->descriptor->activate( h ); | |||||
| int tframes = 512; | |||||
| float tmp[tframes]; | |||||
| memset( tmp, 0, sizeof( float ) * tframes ); | |||||
| for ( unsigned int k = 0; k < _idata->descriptor->PortCount; ++k ) | |||||
| if ( LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[k] ) ) | |||||
| _idata->descriptor->connect_port( h, k, tmp ); | |||||
| /* flush any parameter interpolation */ | |||||
| _idata->descriptor->run( h, tframes ); | |||||
| for ( unsigned int k = 0; k < _idata->descriptor->PortCount; ++k ) | |||||
| if ( LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[k] ) ) | |||||
| _idata->descriptor->connect_port( h, k, buf ); | |||||
| /* run for real */ | |||||
| _idata->descriptor->run( h, nframes ); | |||||
| if ( _idata->descriptor->deactivate ) | |||||
| _idata->descriptor->deactivate( h ); | |||||
| if ( _idata->descriptor->cleanup ) | |||||
| _idata->descriptor->cleanup( h ); | |||||
| return true; | |||||
| } | |||||
| /**********/ | /**********/ | ||||
| /* Client */ | /* Client */ | ||||
| /**********/ | /**********/ | ||||
| @@ -99,6 +99,8 @@ private: | |||||
| void set_control_buffer ( int n, void *buf ); | void set_control_buffer ( int n, void *buf ); | ||||
| void activate ( void ); | void activate ( void ); | ||||
| void deactivate ( void ); | void deactivate ( void ); | ||||
| bool apply ( sample_t *buf, nframes_t nframes ); | |||||
| void process ( unsigned long nframes ); | void process ( unsigned long nframes ); | ||||
| bool plugin_instances ( unsigned int ); | bool plugin_instances ( unsigned int ); | ||||
| @@ -109,6 +111,8 @@ private: | |||||
| public: | public: | ||||
| virtual bool get_impulse_response ( sample_t *buf, nframes_t nframes ); | |||||
| virtual nframes_t get_module_latency ( void ) const; | virtual nframes_t get_module_latency ( void ) const; | ||||
| virtual void update ( void ); | virtual void update ( void ); | ||||
| @@ -0,0 +1,342 @@ | |||||
| /*******************************************************************************/ | |||||
| /* Copyright (C) 2013 Mark McCurry */ | |||||
| /* Copyright (C) 2013 Jonathan Moore Liles */ | |||||
| /* */ | |||||
| /* 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; see the file COPYING. If not,write to the Free Software */ | |||||
| /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||||
| /*******************************************************************************/ | |||||
| #include "SpectrumView.H" | |||||
| #include <FL/Fl.H> | |||||
| #include <FL/fl_draw.H> | |||||
| #include <math.h> | |||||
| #include <cstdlib> | |||||
| #include <cstring> | |||||
| #include <map> | |||||
| #include <assert.h> | |||||
| static std::map<int,float*> _cached_plan; | |||||
| float SpectrumView::_fmin = 10; | |||||
| float SpectrumView::_fmax = 24000; | |||||
| unsigned int SpectrumView::_sample_rate = 48000; | |||||
| void | |||||
| SpectrumView::clear_bands ( void ) | |||||
| { | |||||
| if ( _bands ) | |||||
| delete[] _bands; | |||||
| _bands = NULL; | |||||
| } | |||||
| void | |||||
| SpectrumView::data ( float *data, unsigned int nframes ) | |||||
| { | |||||
| if ( _data ) | |||||
| delete[] _data; | |||||
| _data = data; | |||||
| _nframes = nframes; | |||||
| clear_bands(); | |||||
| redraw(); | |||||
| } | |||||
| void | |||||
| SpectrumView::sample_rate ( unsigned int sample_rate ) | |||||
| { | |||||
| if ( _sample_rate != sample_rate ) | |||||
| { | |||||
| _sample_rate = sample_rate; | |||||
| _fmin = 10; | |||||
| _fmax = _sample_rate * 0.5f; | |||||
| /* invalidate all plans */ | |||||
| for ( std::map<int,float*>::iterator i = _cached_plan.begin(); | |||||
| i != _cached_plan.end(); | |||||
| i++ ) | |||||
| { | |||||
| delete[] i->second; | |||||
| } | |||||
| _cached_plan.clear(); | |||||
| } | |||||
| } | |||||
| #define min(a,b) (a<b?a:b) | |||||
| #define max(a,b) (a<b?b:a) | |||||
| static float* | |||||
| qft_plan ( unsigned frames, unsigned samples, float Fs, float Fmin, float Fmax ) | |||||
| { | |||||
| float *op = new float[ frames * samples * 2 ]; | |||||
| //Our scaling function must be some f(0) = Fmin and f(1) = Fmax | |||||
| // Thus, | |||||
| // f(x)=10^(a*x+b) -> b=log(Fmin)/log(10) | |||||
| // log10(Fmax)=a+b -> a=log(Fmax)/log(10)-b | |||||
| const float b = logf(Fmin)/logf(10); | |||||
| const float a = logf(Fmax)/logf(10)-b; | |||||
| //Evaluate at set frequencies | |||||
| const float one_over_samples = 1.0f / samples; | |||||
| const float one_over_samplerate = 1.0f / Fs; | |||||
| for(unsigned i=0; i<samples; ++i) | |||||
| { | |||||
| const float F = powf(10.0,a*i*one_over_samples+b)*one_over_samplerate; | |||||
| const float Fp = -2*M_PI*F; | |||||
| float Fpj = 0; | |||||
| for(unsigned j = 0; j < frames; ++j, Fpj += Fp ) | |||||
| { | |||||
| const unsigned ji = i*frames*2+2*j; | |||||
| op[ji+0] = sinf(Fpj); | |||||
| op[ji+1] = cosf(Fpj); | |||||
| } | |||||
| } | |||||
| return op; | |||||
| } | |||||
| /** Input should be an impulse response of an EQ. Output will be a | |||||
| * buffer of /bands/ floats of dB values for each frequency band */ | |||||
| void | |||||
| SpectrumView::analyze_data ( unsigned int _plan_size ) | |||||
| { | |||||
| float res[_plan_size * 2]; | |||||
| memset(res,0,sizeof(float) * _plan_size * 2); | |||||
| if ( _cached_plan.find( _plan_size ) == _cached_plan.end() ) | |||||
| _cached_plan[_plan_size ] = qft_plan( _nframes, _plan_size, _sample_rate, _fmin, _fmax); | |||||
| const float *plan = _cached_plan[_plan_size]; | |||||
| //Evaluate at set frequencies | |||||
| for(unsigned i=0; i<_plan_size; ++i) { | |||||
| unsigned ti = i*2; | |||||
| unsigned tif = ti*_nframes; | |||||
| for(unsigned int j=0; j < _nframes ; ++j) { | |||||
| unsigned ji = tif+j*2; | |||||
| res[ti+0] += plan[ji+0]*_data[j]; | |||||
| res[ti+1] += plan[ji+1]*_data[j]; | |||||
| } | |||||
| } | |||||
| float *result = new float[_plan_size]; | |||||
| for(unsigned i=0; i<_plan_size; ++i) { | |||||
| const float abs_ = sqrtf(res[2*i]*res[2*i]+res[2*i+1]*res[2*i+1]); | |||||
| result[i] = 20*logf(abs_)/logf(10); | |||||
| } | |||||
| { | |||||
| if ( _auto_level ) | |||||
| { | |||||
| /* find range and normalize */ | |||||
| float _min=1000, _max=-1000; | |||||
| for(unsigned int i=0; i< _plan_size; ++i) | |||||
| { | |||||
| _min = min(_min, result[i]); | |||||
| _max = max(_max, result[i]); | |||||
| } | |||||
| _dbmin = _min; | |||||
| _dbmax = _max; | |||||
| } | |||||
| double minS = 1.0 / (_dbmax-_dbmin); | |||||
| for( unsigned int i=0; i<_plan_size; ++i) | |||||
| result[i] = (result[i]-_dbmin)*minS; | |||||
| } | |||||
| clear_bands(); | |||||
| _bands = result; | |||||
| } | |||||
| SpectrumView::~SpectrumView ( void ) | |||||
| { | |||||
| clear_bands(); | |||||
| if ( _data ) | |||||
| delete[] _data; | |||||
| } | |||||
| SpectrumView::SpectrumView ( int X, int Y, int W, int H, const char *L ) | |||||
| : Fl_Box(X,Y,W,H,L) | |||||
| { | |||||
| _auto_level = 0; | |||||
| _data = 0; | |||||
| _nframes = 0; | |||||
| _bands = 0; | |||||
| _dbmin = -70; | |||||
| _dbmax = 30; | |||||
| box(FL_FLAT_BOX); | |||||
| color(fl_rgb_color(20,20,20)); | |||||
| selection_color( fl_rgb_color( 210, 80, 80 ) ); | |||||
| // end(); | |||||
| } | |||||
| static int padding_right = 0; | |||||
| static int padding_bottom = 0; | |||||
| void | |||||
| SpectrumView::draw_semilog ( void ) | |||||
| { | |||||
| int W = w() - padding_right; | |||||
| int H = h() - padding_bottom; | |||||
| /* char dash[] = {5,5 }; */ | |||||
| /* fl_line_style(0, 1, dash); */ | |||||
| fl_line_style(FL_SOLID,0); | |||||
| //Db grid is easy, it is just a linear spacing | |||||
| for(int i=0; i<8; ++i) { | |||||
| int level = y()+H*i/8.0; | |||||
| fl_line(x(), level, x()+W, level); | |||||
| } | |||||
| //The frequency grid is defined with points at | |||||
| //10,11,12,...,18,19,20,30,40,50,60,70,80,90,100,200,400,... | |||||
| //Thus we find each scale that we cover and draw the nine lines unique to | |||||
| //that scale | |||||
| const int min_base = logf(_fmin)/logf(10); | |||||
| const int max_base = logf(_fmax)/logf(10); | |||||
| const float b = logf(_fmin)/logf(10); | |||||
| const float a = logf(_fmax)/logf(10)-b; | |||||
| for(int i=min_base; i<=max_base; ++i) { | |||||
| for(int j=1; j<10; ++j) { | |||||
| const float freq = pow(10.0, i)*j; | |||||
| const float xloc = (logf(freq)/logf(10)-b)/a; | |||||
| if(xloc<1.0 && xloc > -0.001) | |||||
| fl_line(xloc*W+x(), y(), xloc*W+x(), y()+H); | |||||
| } | |||||
| } | |||||
| fl_end_line(); | |||||
| fl_font( FL_HELVETICA_ITALIC, 7 ); | |||||
| //Place the text labels | |||||
| char label[256]; | |||||
| for(int i=0; i<8; ++i) { | |||||
| int level = (y()+H*i/8.0) + 3; | |||||
| float value = (1-i/8.0)*(_dbmax-_dbmin) + _dbmin; | |||||
| sprintf(label, "%.1f dB", value); | |||||
| // fl_draw(label, x()+w() + 3, level); | |||||
| fl_draw(label, x(), level, w(), 7, FL_ALIGN_RIGHT ); | |||||
| } | |||||
| for(int i=min_base; i<=max_base; ++i) { | |||||
| { | |||||
| const float freq = pow(10.0, i)*1; | |||||
| const float xloc = (logf(freq)/logf(10)-b)/a; | |||||
| sprintf(label, "%0.f %s", freq < 1000.0 ? freq : freq / 1000.0, freq < 1000.0 ? "Hz" : "KHz" ); | |||||
| if(xloc<1.0) | |||||
| fl_draw(label, xloc*W+x()+1, y()+h()); | |||||
| } | |||||
| } | |||||
| } | |||||
| void | |||||
| SpectrumView::draw_curve ( void ) | |||||
| { | |||||
| int W = w() - padding_right; | |||||
| //Build lines | |||||
| float inc = 1.0 / (float)W; | |||||
| float fx = 0; | |||||
| for( int i = 0; i < W; i++, fx += inc ) | |||||
| fl_vertex(fx, 1.0 - _bands[i]); | |||||
| } | |||||
| void | |||||
| SpectrumView::draw ( void ) | |||||
| { | |||||
| //Clear Widget | |||||
| Fl_Box::draw(); | |||||
| int W = w() - padding_right; | |||||
| int H = h() - padding_bottom; | |||||
| if ( !_bands ) { | |||||
| analyze_data( W ); | |||||
| } | |||||
| //Draw grid | |||||
| fl_color(fl_color_add_alpha(fl_rgb_color( 100,100,100), 50 )); | |||||
| draw_semilog(); | |||||
| fl_push_clip( x(),y(),W,H); | |||||
| fl_color(fl_color_add_alpha( selection_color(), 20 )); | |||||
| fl_push_matrix(); | |||||
| fl_translate( x(), y() + 2 ); | |||||
| fl_scale( W,H- 2 ); | |||||
| fl_begin_polygon(); | |||||
| fl_vertex(0.0,1.0); | |||||
| draw_curve(); | |||||
| fl_vertex(1.0,1.0); | |||||
| fl_end_polygon(); | |||||
| fl_color(fl_color_add_alpha( selection_color(), 100 )); | |||||
| fl_begin_line(); | |||||
| fl_line_style(FL_SOLID,2); | |||||
| /* fl_vertex(0.0,1.0); */ | |||||
| draw_curve(); | |||||
| /* fl_vertex(1.0,1.0); */ | |||||
| fl_end_line(); | |||||
| fl_pop_matrix(); | |||||
| fl_line_style(FL_SOLID,0); | |||||
| fl_pop_clip(); | |||||
| } | |||||
| void | |||||
| SpectrumView::resize ( int X, int Y, int W, int H ) | |||||
| { | |||||
| if ( W != w() ) | |||||
| clear_bands(); | |||||
| Fl_Box::resize(X,Y,W,H); | |||||
| } | |||||
| @@ -0,0 +1,62 @@ | |||||
| /*******************************************************************************/ | |||||
| /* Copyright (C) 2013 Mark McCurry */ | |||||
| /* Copyright (C) 2013 Jonathan Moore Liles */ | |||||
| /* */ | |||||
| /* 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; see the file COPYING. If not,write to the Free Software */ | |||||
| /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||||
| /*******************************************************************************/ | |||||
| #include <FL/Fl_Box.H> | |||||
| class SpectrumView : public Fl_Box | |||||
| { | |||||
| static unsigned int _sample_rate; | |||||
| static float _fmin; | |||||
| static float _fmax; | |||||
| float * _data; | |||||
| unsigned int _nframes; | |||||
| float * _bands; | |||||
| float _dbmin; | |||||
| float _dbmax; | |||||
| bool _auto_level; | |||||
| void draw_curve ( void ); | |||||
| void draw_semilog ( void ); | |||||
| void analyze_data ( unsigned int plan_size ); | |||||
| void clear_bands ( void ); | |||||
| public: | |||||
| static void sample_rate ( unsigned int sample_rate ); | |||||
| /* set dB range. If min == max, then auto leveling will be enabled */ | |||||
| void db_range ( float min, float max ) | |||||
| { | |||||
| _dbmin = min; | |||||
| _dbmax = max; | |||||
| _auto_level = min == max; | |||||
| } | |||||
| /** /data/ must point to allocated memory. It will be freed when new data is set or when the control is destroyed */ | |||||
| void data ( float *data, unsigned int nframes ); | |||||
| SpectrumView ( int X, int Y, int W, int H, const char *L=0 ); | |||||
| virtual ~SpectrumView ( ); | |||||
| virtual void resize ( int X, int Y, int W, int H ); | |||||
| virtual void draw ( void ); | |||||
| }; | |||||
| @@ -66,6 +66,7 @@ src/Plugin_Module.C | |||||
| src/Project.C | src/Project.C | ||||
| src/Group.C | src/Group.C | ||||
| src/main.C | src/main.C | ||||
| src/SpectrumView.C | |||||
| src/Spatialization_Console.C | src/Spatialization_Console.C | ||||
| ''', | ''', | ||||
| target = 'non-mixer', | target = 'non-mixer', | ||||
| @@ -181,7 +181,7 @@ buffer_fill_with_silence ( sample_t *buf, nframes_t nframes ) | |||||
| } | } | ||||
| bool | bool | ||||
| buffer_is_digital_black ( sample_t *buf, nframes_t nframes ) | |||||
| buffer_is_digital_black ( const sample_t *buf, nframes_t nframes ) | |||||
| { | { | ||||
| while ( nframes-- ) | while ( nframes-- ) | ||||
| { | { | ||||