@@ -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-- ) | ||||
{ | { | ||||