| @@ -1 +1 @@ | |||||
| Subproject commit f084e8d2ccdcf8f5c997618b984c493462532a1c | |||||
| Subproject commit 977a180175b776f700e799112ac138281b29a7a4 | |||||
| @@ -112,12 +112,13 @@ AUX_Module::process ( nframes_t nframes ) | |||||
| { | { | ||||
| float gt = DB_CO( control_input[0].control_value() ); | float gt = DB_CO( control_input[0].control_value() ); | ||||
| if ( !smoothing.target_reached( gt ) ) | |||||
| sample_t gainbuf[nframes]; | |||||
| bool use_gainbuf = smoothing.apply( gainbuf, nframes, gt ); | |||||
| if ( use_gainbuf ) | |||||
| { | { | ||||
| sample_t gainbuf[nframes]; | |||||
| smoothing.apply( gainbuf, nframes, gt ); | |||||
| for ( unsigned int i = 0; i < audio_input.size(); ++i ) | for ( unsigned int i = 0; i < audio_input.size(); ++i ) | ||||
| { | { | ||||
| if ( audio_input[i].connected() ) | if ( audio_input[i].connected() ) | ||||
| @@ -908,8 +908,7 @@ Chain::update_connection_status ( void ) | |||||
| { | { | ||||
| Module *m = module(i); | Module *m = module(i); | ||||
| if ( !strcmp( m->name(), "JACK" ) || | |||||
| !strcmp( m->name(), "AUX" )) | |||||
| if ( !strcmp( m->basename(), "JACK" ) ) | |||||
| { | { | ||||
| ((JACK_Module*)m)->update_connection_status(); | ((JACK_Module*)m)->update_connection_status(); | ||||
| } | } | ||||
| @@ -234,6 +234,101 @@ Controller_Module::mode ( Mode m ) | |||||
| _mode = m ; | _mode = m ; | ||||
| } | } | ||||
| bool | |||||
| Controller_Module::connect_spatializer_radius_to ( Module *m ) | |||||
| { | |||||
| Port *radius_port = NULL; | |||||
| float radius_value = 0.0f; | |||||
| for ( unsigned int i = 0; i < m->control_input.size(); ++i ) | |||||
| { | |||||
| Port *p = &m->control_input[i]; | |||||
| if ( !strcasecmp( "Radius", p->name() ) ) | |||||
| /* 90.0f == p->hints.maximum && */ | |||||
| /* -90.0f == p->hints.minimum ) */ | |||||
| { | |||||
| radius_port = p; | |||||
| radius_value = p->control_value(); | |||||
| continue; | |||||
| } | |||||
| } | |||||
| if ( ! radius_port ) | |||||
| return false; | |||||
| if ( control_output.size() != 3 ) | |||||
| { | |||||
| control_output.clear(); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| } | |||||
| control_output[2].connect_to( radius_port ); | |||||
| maybe_create_panner(); | |||||
| Panner *o = (Panner*)control; | |||||
| o->point( 0 )->radius( radius_value ); | |||||
| if ( Mixer::spatialization_console ) | |||||
| Mixer::spatialization_console->update(); | |||||
| return true; | |||||
| } | |||||
| void | |||||
| Controller_Module::maybe_create_panner ( void ) | |||||
| { | |||||
| if ( _type != SPATIALIZATION ) | |||||
| { | |||||
| clear(); | |||||
| Panner *o = new Panner( 0,0, 92,92 ); | |||||
| o->box(FL_FLAT_BOX); | |||||
| o->color(FL_GRAY0); | |||||
| o->selection_color(FL_BACKGROUND_COLOR); | |||||
| o->labeltype(FL_NORMAL_LABEL); | |||||
| o->labelfont(0); | |||||
| o->labelcolor(FL_FOREGROUND_COLOR); | |||||
| o->align(FL_ALIGN_TOP); | |||||
| o->when(FL_WHEN_CHANGED); | |||||
| label( "Spatialization" ); | |||||
| o->align(FL_ALIGN_TOP); | |||||
| o->labelsize( 10 ); | |||||
| // o->callback( cb_panner_value_handle, new callback_data( this, azimuth_port_number, elevation_port_number ) ); | |||||
| o->callback( cb_spatializer_handle, this ); | |||||
| control = (Fl_Valuator*)o; | |||||
| if ( _pad ) | |||||
| { | |||||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( o ); | |||||
| flg->position( x(), y() ); | |||||
| flg->set_visible_focus(); | |||||
| size( flg->w(), flg->h() ); | |||||
| add( flg ); | |||||
| } | |||||
| else | |||||
| { | |||||
| o->resize( x(), y(), w(), h() ); | |||||
| add( o ); | |||||
| resizable( o ); | |||||
| init_sizes(); | |||||
| } | |||||
| _type = SPATIALIZATION; | |||||
| } | |||||
| } | |||||
| /** attempt to transform this controller into a spatialization | /** attempt to transform this controller into a spatialization | ||||
| controller and connect to the given module's spatialization | controller and connect to the given module's spatialization | ||||
| control inputs. Returns true on success, false if given module | control inputs. Returns true on success, false if given module | ||||
| @@ -241,6 +336,8 @@ Controller_Module::mode ( Mode m ) | |||||
| bool | bool | ||||
| Controller_Module::connect_spatializer_to ( Module *m ) | Controller_Module::connect_spatializer_to ( Module *m ) | ||||
| { | { | ||||
| connect_spatializer_radius_to( m ); | |||||
| /* these are for detecting related parameter groups which can be | /* these are for detecting related parameter groups which can be | ||||
| better represented by a single control */ | better represented by a single control */ | ||||
| Port *azimuth_port = NULL; | Port *azimuth_port = NULL; | ||||
| @@ -269,60 +366,28 @@ Controller_Module::connect_spatializer_to ( Module *m ) | |||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| if ( ! ( azimuth_port && elevation_port ) ) | if ( ! ( azimuth_port && elevation_port ) ) | ||||
| return false; | return false; | ||||
| control_output.clear(); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| if ( control_output.size() != 3 ) | |||||
| { | |||||
| control_output.clear(); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||||
| } | |||||
| control_output[0].connect_to( azimuth_port ); | control_output[0].connect_to( azimuth_port ); | ||||
| control_output[1].connect_to( elevation_port ); | control_output[1].connect_to( elevation_port ); | ||||
| clear(); | |||||
| Panner *o = new Panner( 0,0, 92,92 ); | |||||
| o->box(FL_FLAT_BOX); | |||||
| o->color(FL_GRAY0); | |||||
| o->selection_color(FL_BACKGROUND_COLOR); | |||||
| o->labeltype(FL_NORMAL_LABEL); | |||||
| o->labelfont(0); | |||||
| o->labelcolor(FL_FOREGROUND_COLOR); | |||||
| o->align(FL_ALIGN_TOP); | |||||
| o->when(FL_WHEN_CHANGED); | |||||
| label( "Spatialization" ); | |||||
| o->align(FL_ALIGN_TOP); | |||||
| o->labelsize( 10 ); | |||||
| // o->callback( cb_panner_value_handle, new callback_data( this, azimuth_port_number, elevation_port_number ) ); | |||||
| maybe_create_panner(); | |||||
| Panner *o = (Panner*)control; | |||||
| o->point( 0 )->azimuth( azimuth_value ); | o->point( 0 )->azimuth( azimuth_value ); | ||||
| o->point( 0 )->elevation( elevation_value ); | o->point( 0 )->elevation( elevation_value ); | ||||
| o->callback( cb_spatializer_handle, this ); | |||||
| control = (Fl_Valuator*)o; | |||||
| if ( _pad ) | |||||
| { | |||||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( o ); | |||||
| flg->position( x(), y() ); | |||||
| flg->set_visible_focus(); | |||||
| size( flg->w(), flg->h() ); | |||||
| add( flg ); | |||||
| } | |||||
| else | |||||
| { | |||||
| o->resize( x(), y(), w(), h() ); | |||||
| add( o ); | |||||
| resizable( o ); | |||||
| init_sizes(); | |||||
| } | |||||
| _type = SPATIALIZATION; | |||||
| if ( Mixer::spatialization_console ) | if ( Mixer::spatialization_console ) | ||||
| Mixer::spatialization_console->update(); | Mixer::spatialization_console->update(); | ||||
| @@ -491,6 +556,11 @@ Controller_Module::cb_spatializer_handle ( Fl_Widget *w ) | |||||
| control_output[0].connected_port()->control_value( pan->point( 0 )->azimuth() ); | control_output[0].connected_port()->control_value( pan->point( 0 )->azimuth() ); | ||||
| control_output[1].connected_port()->control_value( pan->point( 0 )->elevation() ); | control_output[1].connected_port()->control_value( pan->point( 0 )->elevation() ); | ||||
| } | } | ||||
| if ( control_output[2].connected() ) | |||||
| { | |||||
| control_output[2].connected_port()->control_value( pan->point( 0 )->radius() ); | |||||
| } | |||||
| } | } | ||||
| void | void | ||||
| @@ -806,6 +876,14 @@ Controller_Module::handle_control_changed ( Port *p ) | |||||
| pan->point( 0 )->azimuth( control_output[0].control_value() ); | pan->point( 0 )->azimuth( control_output[0].control_value() ); | ||||
| pan->point( 0 )->elevation( control_output[1].control_value() ); | pan->point( 0 )->elevation( control_output[1].control_value() ); | ||||
| if ( control_output[2].connected() ) | |||||
| { | |||||
| Port *pp = control_output[2].connected_port(); | |||||
| float v = control_output[2].control_value(); | |||||
| float s = pp->hints.maximum - pp->hints.minimum; | |||||
| pan->point( 0 )->radius( v ); | |||||
| } | |||||
| if ( visible_r() ) | if ( visible_r() ) | ||||
| pan->redraw(); | pan->redraw(); | ||||
| } | } | ||||
| @@ -84,6 +84,7 @@ public: | |||||
| void connect_to ( Port *p ); | void connect_to ( Port *p ); | ||||
| bool connect_spatializer_to ( Module *m ); | bool connect_spatializer_to ( Module *m ); | ||||
| bool connect_spatializer_radius_to ( Module *m ); | |||||
| void disconnect ( void ); | void disconnect ( void ); | ||||
| void handle_control_changed ( Port *p ); | void handle_control_changed ( Port *p ); | ||||
| @@ -111,6 +112,7 @@ protected: | |||||
| private: | private: | ||||
| void maybe_create_panner ( void ); | |||||
| char *generate_osc_path ( void ); | char *generate_osc_path ( void ); | ||||
| void change_osc_path ( char *path ); | void change_osc_path ( char *path ); | ||||
| @@ -90,12 +90,13 @@ Gain_Module::process ( nframes_t nframes ) | |||||
| { | { | ||||
| const float gt = DB_CO( control_input[0].control_value() ); | const float gt = DB_CO( control_input[0].control_value() ); | ||||
| if ( !smoothing.target_reached( gt ) ) | |||||
| { | |||||
| sample_t gainbuf[nframes]; | |||||
| smoothing.apply( gainbuf, nframes, gt ); | |||||
| sample_t gainbuf[nframes]; | |||||
| bool use_gainbuf = smoothing.apply( gainbuf, nframes, gt ); | |||||
| if ( use_gainbuf ) | |||||
| { | |||||
| for ( int i = audio_input.size(); i--; ) | for ( int i = audio_input.size(); i--; ) | ||||
| { | { | ||||
| if ( audio_input[i].connected() && audio_output[i].connected() ) | if ( audio_input[i].connected() && audio_output[i].connected() ) | ||||
| @@ -54,6 +54,12 @@ JACK_Module::JACK_Module ( bool log ) | |||||
| { | { | ||||
| _prefix = 0; | _prefix = 0; | ||||
| _connection_handle_outputs[0][0] = 0; | |||||
| _connection_handle_outputs[0][1] = 0; | |||||
| _connection_handle_outputs[1][0] = 0; | |||||
| _connection_handle_outputs[1][1] = 0; | |||||
| align( FL_ALIGN_TOP | FL_ALIGN_INSIDE ); | align( FL_ALIGN_TOP | FL_ALIGN_INSIDE ); | ||||
| if ( log ) | if ( log ) | ||||
| @@ -135,7 +141,13 @@ JACK_Module::JACK_Module ( bool log ) | |||||
| o->hide(); | o->hide(); | ||||
| } | } | ||||
| { Fl_Box *o = output_connection_handle = new Fl_Box( x(), y(), 18, 18 ); | |||||
| { Fl_Box *o = output_connection_handle = new Fl_Box( x(), y(), 12, 12 ); | |||||
| o->tooltip( "Drag and drop to make and break JACK connections."); | |||||
| o->image( output_connector_image ? output_connector_image : output_connector_image = new Fl_PNG_Image( "output_connector", img_io_output_connector_10x10_png, img_io_output_connector_10x10_png_len ) ); | |||||
| o->hide(); | |||||
| } | |||||
| { Fl_Box *o = output_connection2_handle = new Fl_Box( x(), y(), 12, 12 ); | |||||
| o->tooltip( "Drag and drop to make and break JACK connections."); | o->tooltip( "Drag and drop to make and break JACK connections."); | ||||
| o->image( output_connector_image ? output_connector_image : output_connector_image = new Fl_PNG_Image( "output_connector", img_io_output_connector_10x10_png, img_io_output_connector_10x10_png_len ) ); | o->image( output_connector_image ? output_connector_image : output_connector_image = new Fl_PNG_Image( "output_connector", img_io_output_connector_10x10_png, img_io_output_connector_10x10_png_len ) ); | ||||
| o->hide(); | o->hide(); | ||||
| @@ -335,6 +347,41 @@ JACK_Module::can_support_inputs ( int ) | |||||
| return audio_output.size(); | return audio_output.size(); | ||||
| } | } | ||||
| void | |||||
| JACK_Module::remove_jack_outputs ( void ) | |||||
| { | |||||
| for ( unsigned int i = jack_output.size(); i--; ) | |||||
| { | |||||
| jack_output.back().shutdown(); | |||||
| jack_output.pop_back(); | |||||
| } | |||||
| } | |||||
| bool | |||||
| JACK_Module::add_jack_output ( const char *prefix, int n ) | |||||
| { | |||||
| JACK::Port *po = NULL; | |||||
| if ( !prefix ) | |||||
| po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, n ); | |||||
| else | |||||
| po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, prefix, n ); | |||||
| if ( ! po->activate() ) | |||||
| { | |||||
| jack_port_activation_error( po ); | |||||
| return false; | |||||
| } | |||||
| if ( po->valid() ) | |||||
| { | |||||
| jack_output.push_back( *po ); | |||||
| } | |||||
| delete po; | |||||
| } | |||||
| bool | bool | ||||
| JACK_Module::configure_inputs ( int n ) | JACK_Module::configure_inputs ( int n ) | ||||
| { | { | ||||
| @@ -386,6 +433,9 @@ JACK_Module::configure_inputs ( int n ) | |||||
| } | } | ||||
| } | } | ||||
| _connection_handle_outputs[0][0] = 0; | |||||
| _connection_handle_outputs[0][1] = jack_output.size(); | |||||
| if ( is_default() ) | if ( is_default() ) | ||||
| control_input[0].control_value_no_callback( n ); | control_input[0].control_value_no_callback( n ); | ||||
| @@ -465,7 +515,7 @@ JACK_Module::initialize ( void ) | |||||
| void | void | ||||
| JACK_Module::handle_control_changed ( Port *p ) | JACK_Module::handle_control_changed ( Port *p ) | ||||
| { | { | ||||
| THREAD_ASSERT( UI ); | |||||
| // THREAD_ASSERT( UI ); | |||||
| if ( 0 == strcmp( p->name(), "Inputs" ) ) | if ( 0 == strcmp( p->name(), "Inputs" ) ) | ||||
| { | { | ||||
| @@ -492,6 +542,8 @@ JACK_Module::handle_control_changed ( Port *p ) | |||||
| p->connected_port()->control_value( noutputs() ); | p->connected_port()->control_value( noutputs() ); | ||||
| } | } | ||||
| } | } | ||||
| Module::handle_control_changed( p ); | |||||
| } | } | ||||
| void | void | ||||
| @@ -519,14 +571,28 @@ JACK_Module::handle ( int m ) | |||||
| return Module::handle(m) || 1; | return Module::handle(m) || 1; | ||||
| case FL_DRAG: | case FL_DRAG: | ||||
| { | { | ||||
| if ( ! Fl::event_inside( this ) && ! Fl::selection_owner() ) | |||||
| if ( Fl::event_is_click() ) | |||||
| return 1; | |||||
| int connection_handle = -1; | |||||
| if ( Fl::event_inside( output_connection_handle ) ) | |||||
| connection_handle = 0; | |||||
| if ( Fl::event_inside( output_connection2_handle ) ) | |||||
| connection_handle = 1; | |||||
| if ( Fl::event_button1() && | |||||
| connection_handle >= 0 | |||||
| && ! Fl::selection_owner() ) | |||||
| { | { | ||||
| DMESSAGE( "initiation of drag" ); | DMESSAGE( "initiation of drag" ); | ||||
| char *s = (char*)malloc(256); | char *s = (char*)malloc(256); | ||||
| s[0] = 0; | s[0] = 0; | ||||
| for ( unsigned int i = 0; i < jack_output.size(); ++i ) | |||||
| for ( unsigned int i = _connection_handle_outputs[connection_handle][0]; | |||||
| i < jack_output.size() && i < _connection_handle_outputs[connection_handle][1]; ++i ) | |||||
| { | { | ||||
| char *s2; | char *s2; | ||||
| asprintf(&s2, "jack.port://%s/%s:%s\r\n", instance_name, chain()->name(), jack_output[i].name() ); | asprintf(&s2, "jack.port://%s/%s:%s\r\n", instance_name, chain()->name(), jack_output[i].name() ); | ||||
| @@ -552,12 +618,15 @@ JACK_Module::handle ( int m ) | |||||
| } | } | ||||
| /* we have to prevent Fl_Group::handle() from getting these, otherwise it will mess up Fl::belowmouse() */ | /* we have to prevent Fl_Group::handle() from getting these, otherwise it will mess up Fl::belowmouse() */ | ||||
| case FL_MOVE: | case FL_MOVE: | ||||
| return 0; | |||||
| Module::handle(m); | |||||
| return 1; | |||||
| case FL_ENTER: | case FL_ENTER: | ||||
| case FL_DND_ENTER: | case FL_DND_ENTER: | ||||
| Module::handle(m); | |||||
| return 1; | return 1; | ||||
| case FL_LEAVE: | case FL_LEAVE: | ||||
| case FL_DND_LEAVE: | case FL_DND_LEAVE: | ||||
| Module::handle(m); | |||||
| if ( this == receptive_to_drop ) | if ( this == receptive_to_drop ) | ||||
| { | { | ||||
| receptive_to_drop = NULL; | receptive_to_drop = NULL; | ||||
| @@ -52,9 +52,14 @@ protected: | |||||
| Fl_Browser * connection_display; | Fl_Browser * connection_display; | ||||
| Fl_Box * input_connection_handle; | Fl_Box * input_connection_handle; | ||||
| Fl_Box * output_connection_handle; | Fl_Box * output_connection_handle; | ||||
| Fl_Box * output_connection2_handle; | |||||
| static void cb_button ( Fl_Widget *w, void *v ); | static void cb_button ( Fl_Widget *w, void *v ); | ||||
| void cb_button ( Fl_Widget *w ); | void cb_button ( Fl_Widget *w ); | ||||
| protected: | |||||
| int _connection_handle_outputs[2][2]; | |||||
| public: | public: | ||||
| @@ -63,6 +68,7 @@ public: | |||||
| JACK_Module ( bool log = true ); | JACK_Module ( bool log = true ); | ||||
| virtual ~JACK_Module ( ); | virtual ~JACK_Module ( ); | ||||
| virtual const char *basename ( void ) const { return "JACK"; } | |||||
| virtual const char *name ( void ) const { return "JACK"; } | virtual const char *name ( void ) const { return "JACK"; } | ||||
| virtual bool initialize ( void ); | virtual bool initialize ( void ); | ||||
| @@ -70,6 +76,8 @@ public: | |||||
| virtual int handle ( int m ); | virtual int handle ( int m ); | ||||
| virtual int can_support_inputs ( int ); | virtual int can_support_inputs ( int ); | ||||
| bool add_jack_output ( const char *prefix, int n ); | |||||
| void remove_jack_outputs ( void ); | |||||
| virtual bool configure_inputs ( int n ); | virtual bool configure_inputs ( int n ); | ||||
| virtual bool configure_outputs ( int n ); | virtual bool configure_outputs ( int n ); | ||||
| @@ -34,6 +34,7 @@ | |||||
| #include "Meter_Module.H" | #include "Meter_Module.H" | ||||
| #include "Plugin_Module.H" | #include "Plugin_Module.H" | ||||
| #include "AUX_Module.H" | #include "AUX_Module.H" | ||||
| #include "Spatializer_Module.H" | |||||
| #include <FL/Fl_Menu_Button.H> | #include <FL/Fl_Menu_Button.H> | ||||
| #include "FL/test_press.H" | #include "FL/test_press.H" | ||||
| @@ -137,6 +138,7 @@ Module::init ( void ) | |||||
| align( FL_ALIGN_CENTER | FL_ALIGN_INSIDE ); | align( FL_ALIGN_CENTER | FL_ALIGN_INSIDE ); | ||||
| set_visible_focus(); | set_visible_focus(); | ||||
| selection_color( FL_RED ); | selection_color( FL_RED ); | ||||
| labelsize(12); | |||||
| color( fl_color_average( FL_WHITE, FL_CYAN, 0.40 ) ); | color( fl_color_average( FL_WHITE, FL_CYAN, 0.40 ) ); | ||||
| } | } | ||||
| @@ -664,7 +666,7 @@ Module::draw_label ( int tx, int ty, int tw, int th ) | |||||
| fl_color( active_r() ? c : fl_inactive(c) ); | fl_color( active_r() ? c : fl_inactive(c) ); | ||||
| fl_font( FL_HELVETICA, 12 ); | |||||
| fl_font( FL_HELVETICA, labelsize() ); | |||||
| int LW = fl_width( lp ); | int LW = fl_width( lp ); | ||||
| @@ -720,8 +722,32 @@ Module::insert_menu_cb ( const Fl_Menu_ *m ) | |||||
| mod = jm; | mod = jm; | ||||
| } | } | ||||
| if ( !strcmp( picked, "Spatializer" ) ) | |||||
| { | |||||
| int n = 0; | |||||
| for ( int i = 0; i < chain()->modules(); i++ ) | |||||
| { | |||||
| if ( !strcmp( chain()->module(i)->name(), "Spatializer" ) ) | |||||
| n++; | |||||
| } | |||||
| if ( n == 0 ) | |||||
| { | |||||
| Spatializer_Module *jm = new Spatializer_Module(); | |||||
| jm->chain( chain() ); | |||||
| // jm->number( n ); | |||||
| // jm->configure_inputs( ninputs() ); | |||||
| // jm->configure_outputs( ninputs() ); | |||||
| jm->initialize(); | |||||
| mod = jm; | |||||
| } | |||||
| } | |||||
| else if ( !strcmp( picked, "Gain" ) ) | else if ( !strcmp( picked, "Gain" ) ) | ||||
| mod = new Gain_Module(); | mod = new Gain_Module(); | ||||
| /* else if ( !strcmp( picked, "Spatializer" ) ) */ | |||||
| /* mod = new Spatializer_Module(); */ | |||||
| else if ( !strcmp( picked, "Meter" ) ) | else if ( !strcmp( picked, "Meter" ) ) | ||||
| mod = new Meter_Module(); | mod = new Meter_Module(); | ||||
| else if ( !strcmp( picked, "Mono Pan" )) | else if ( !strcmp( picked, "Mono Pan" )) | ||||
| @@ -826,6 +852,8 @@ Module::menu ( void ) const | |||||
| insert_menu->add( "Meter", 0, 0 ); | insert_menu->add( "Meter", 0, 0 ); | ||||
| insert_menu->add( "Mono Pan", 0, 0 ); | insert_menu->add( "Mono Pan", 0, 0 ); | ||||
| insert_menu->add( "Aux", 0, 0 ); | insert_menu->add( "Aux", 0, 0 ); | ||||
| insert_menu->add( "Distance", 0, 0 ); | |||||
| insert_menu->add( "Spatializer", 0, 0 ); | |||||
| insert_menu->add( "Plugin", 0, 0 ); | insert_menu->add( "Plugin", 0, 0 ); | ||||
| insert_menu->callback( &Module::insert_menu_cb, (void*)this ); | insert_menu->callback( &Module::insert_menu_cb, (void*)this ); | ||||
| @@ -344,6 +344,7 @@ public: | |||||
| } | } | ||||
| virtual const char *name ( void ) const = 0; | virtual const char *name ( void ) const = 0; | ||||
| virtual const char *basename ( void ) const { return "Module"; } | |||||
| std::vector<Port> audio_input; | std::vector<Port> audio_input; | ||||
| std::vector<Port> audio_output; | std::vector<Port> audio_output; | ||||
| @@ -42,6 +42,8 @@ | |||||
| #include "debug.h" | #include "debug.h" | ||||
| Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 800, 600 ) | Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 800, 600 ) | ||||
| @@ -144,7 +146,9 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| float azimuth_value = 0.0f; | float azimuth_value = 0.0f; | ||||
| elevation_port_number = -1; | elevation_port_number = -1; | ||||
| float elevation_value = 0.0f; | float elevation_value = 0.0f; | ||||
| radius_port_number = -1; | |||||
| float radius_value = 0.0f; | |||||
| Fl_Color fc = fl_color_add_alpha( FL_CYAN, 200 ); | Fl_Color fc = fl_color_add_alpha( FL_CYAN, 200 ); | ||||
| Fl_Color bc = FL_BACKGROUND2_COLOR; | Fl_Color bc = FL_BACKGROUND2_COLOR; | ||||
| @@ -174,6 +178,12 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| elevation_port_number = i; | elevation_port_number = i; | ||||
| elevation_value = p->control_value(); | elevation_value = p->control_value(); | ||||
| continue; | continue; | ||||
| } | |||||
| else if ( !strcasecmp( "Radius", p->name() ) ) | |||||
| { | |||||
| radius_port_number = i; | |||||
| radius_value = p->control_value(); | |||||
| continue; | |||||
| } | } | ||||
| if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | ||||
| @@ -268,10 +278,12 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| w->align(FL_ALIGN_TOP); | w->align(FL_ALIGN_TOP); | ||||
| w->labelsize( 10 ); | w->labelsize( 10 ); | ||||
| _callback_data.push_back( callback_data( this, i ) ); | |||||
| if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | ||||
| w->callback( cb_button_handle, new callback_data( this, i ) ); | |||||
| w->callback( cb_button_handle, &_callback_data.back() ); | |||||
| else | else | ||||
| w->callback( cb_value_handle, new callback_data( this, i ) ); | |||||
| w->callback( cb_value_handle, &_callback_data.back() ); | |||||
| { Fl_Group *o = new Fl_Group( 0, 0, 50, 75 ); | { Fl_Group *o = new Fl_Group( 0, 0, 50, 75 ); | ||||
| { | { | ||||
| @@ -284,7 +296,7 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| o->value( p->connected() ); | o->value( p->connected() ); | ||||
| o->callback( cb_bound_handle, new callback_data( this, i ) ); | |||||
| o->callback( cb_bound_handle, &_callback_data.back() ); | |||||
| } | } | ||||
| o->resizable( 0 ); | o->resizable( 0 ); | ||||
| @@ -305,7 +317,7 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| if ( azimuth_port_number >= 0 && elevation_port_number >= 0 ) | if ( azimuth_port_number >= 0 && elevation_port_number >= 0 ) | ||||
| { | { | ||||
| Panner *o = new Panner( 0,0, 512,512 ); | |||||
| Panner *o = new Panner( 0,0, 502,502 ); | |||||
| o->box(FL_FLAT_BOX); | o->box(FL_FLAT_BOX); | ||||
| o->color(FL_GRAY0); | o->color(FL_GRAY0); | ||||
| o->selection_color(FL_BACKGROUND_COLOR); | o->selection_color(FL_BACKGROUND_COLOR); | ||||
| @@ -318,10 +330,13 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| o->align(FL_ALIGN_TOP); | o->align(FL_ALIGN_TOP); | ||||
| o->labelsize( 10 ); | o->labelsize( 10 ); | ||||
| o->callback( cb_panner_value_handle, new callback_data( this, azimuth_port_number, elevation_port_number ) ); | |||||
| _callback_data.push_back( callback_data( this, azimuth_port_number, elevation_port_number, radius_port_number ) ); | |||||
| o->callback( cb_panner_value_handle, &_callback_data.back() ); | |||||
| o->point( 0 )->azimuth( azimuth_value ); | o->point( 0 )->azimuth( azimuth_value ); | ||||
| o->point( 0 )->elevation( elevation_value ); | o->point( 0 )->elevation( elevation_value ); | ||||
| o->point( 0 )->radius( radius_value ); | |||||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( o ); | Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( o ); | ||||
| @@ -329,6 +344,7 @@ Module_Parameter_Editor::make_controls ( void ) | |||||
| controls_by_port[azimuth_port_number] = o; | controls_by_port[azimuth_port_number] = o; | ||||
| controls_by_port[elevation_port_number] = o; | controls_by_port[elevation_port_number] = o; | ||||
| controls_by_port[radius_port_number] = o; | |||||
| } | } | ||||
| @@ -367,6 +383,8 @@ Module_Parameter_Editor::cb_panner_value_handle ( Fl_Widget *w, void *v ) | |||||
| cd->base_widget->set_value( cd->port_number[0], ((Panner*)w)->point( 0 )->azimuth() ); | cd->base_widget->set_value( cd->port_number[0], ((Panner*)w)->point( 0 )->azimuth() ); | ||||
| cd->base_widget->set_value( cd->port_number[1], ((Panner*)w)->point( 0 )->elevation() ); | cd->base_widget->set_value( cd->port_number[1], ((Panner*)w)->point( 0 )->elevation() ); | ||||
| cd->base_widget->set_value( cd->port_number[2], ((Panner*)w)->point( 0 )->radius() ); | |||||
| } | } | ||||
| void | void | ||||
| @@ -417,7 +435,8 @@ Module_Parameter_Editor::handle_control_changed ( Module::Port *p ) | |||||
| Fl_Widget *w = controls_by_port[i]; | Fl_Widget *w = controls_by_port[i]; | ||||
| if ( i == azimuth_port_number || | if ( i == azimuth_port_number || | ||||
| i == elevation_port_number ) | |||||
| i == elevation_port_number || | |||||
| i == radius_port_number ) | |||||
| { | { | ||||
| Panner *_panner = (Panner*)w; | Panner *_panner = (Panner*)w; | ||||
| @@ -425,6 +444,8 @@ Module_Parameter_Editor::handle_control_changed ( Module::Port *p ) | |||||
| _panner->point(0)->azimuth( p->control_value() ); | _panner->point(0)->azimuth( p->control_value() ); | ||||
| else if ( i == elevation_port_number ) | else if ( i == elevation_port_number ) | ||||
| _panner->point(0)->elevation( p->control_value() ); | _panner->point(0)->elevation( p->control_value() ); | ||||
| else if ( i == radius_port_number ) | |||||
| _panner->point(0)->radius( p->control_value() ); | |||||
| _panner->redraw(); | _panner->redraw(); | ||||
| @@ -28,11 +28,12 @@ class Fl_Menu_Button; | |||||
| class Panner; | class Panner; | ||||
| #include <vector> | #include <vector> | ||||
| #include <list> | |||||
| class Module_Parameter_Editor : public Fl_Double_Window | class Module_Parameter_Editor : public Fl_Double_Window | ||||
| { | { | ||||
| Module *_module; | Module *_module; | ||||
| struct callback_data | struct callback_data | ||||
| { | { | ||||
| Module_Parameter_Editor *base_widget; | Module_Parameter_Editor *base_widget; | ||||
| @@ -44,6 +45,7 @@ class Module_Parameter_Editor : public Fl_Double_Window | |||||
| this->base_widget = base_widget; | this->base_widget = base_widget; | ||||
| this->port_number[0] = port_number; | this->port_number[0] = port_number; | ||||
| this->port_number[1] = -1; | this->port_number[1] = -1; | ||||
| this->port_number[2] = -1; | |||||
| } | } | ||||
| callback_data ( Module_Parameter_Editor *base_widget, int port_number1, int port_number2 ) | callback_data ( Module_Parameter_Editor *base_widget, int port_number1, int port_number2 ) | ||||
| @@ -51,6 +53,15 @@ class Module_Parameter_Editor : public Fl_Double_Window | |||||
| this->base_widget = base_widget; | this->base_widget = base_widget; | ||||
| this->port_number[0] = port_number1; | this->port_number[0] = port_number1; | ||||
| this->port_number[1] = port_number2; | this->port_number[1] = port_number2; | ||||
| this->port_number[2] = -1; | |||||
| } | |||||
| callback_data ( Module_Parameter_Editor *base_widget, int port_number1, int port_number2, int port_number3 ) | |||||
| { | |||||
| this->base_widget = base_widget; | |||||
| this->port_number[0] = port_number1; | |||||
| this->port_number[1] = port_number2; | |||||
| this->port_number[2] = port_number3; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -72,7 +83,9 @@ class Module_Parameter_Editor : public Fl_Double_Window | |||||
| int azimuth_port_number; | int azimuth_port_number; | ||||
| int elevation_port_number; | int elevation_port_number; | ||||
| int radius_port_number; | |||||
| std::list<callback_data> _callback_data; | |||||
| std::vector<Fl_Widget*> controls_by_port; | std::vector<Fl_Widget*> controls_by_port; | ||||
| public: | public: | ||||
| @@ -25,11 +25,49 @@ | |||||
| // #include <FL/fl_draw.H> | // #include <FL/fl_draw.H> | ||||
| #include <FL/Fl_Shared_Image.H> | #include <FL/Fl_Shared_Image.H> | ||||
| #include <FL/Fl_Tiled_Image.H> | |||||
| #include "debug.h" | |||||
| /* 2D Panner widget. Supports various multichannel configurations. */ | /* 2D Panner widget. Supports various multichannel configurations. */ | ||||
| Panner::Point *Panner::drag; | Panner::Point *Panner::drag; | ||||
| Panner::Panner ( int X, int Y, int W, int H, const char *L ) : | |||||
| Fl_Group( X, Y, W, H, L ) | |||||
| { | |||||
| _range = 15.0f; | |||||
| // _projection = POLAR; | |||||
| _points.push_back( Point( 1, 0 ) ); | |||||
| static int ranges[] = { 1,5,10,15 }; | |||||
| { Fl_Choice *o = _range_choice = new Fl_Choice(X + 40,Y + H - 18,75,18,"Range:"); | |||||
| o->box(FL_UP_FRAME); | |||||
| o->down_box(FL_DOWN_FRAME); | |||||
| o->textsize(9); | |||||
| o->labelsize(9); | |||||
| o->align(FL_ALIGN_LEFT); | |||||
| o->add("1 Meter",0,0,&ranges[0]); | |||||
| o->add("5 Meters",0,0,&ranges[1]); | |||||
| o->add("10 Meters",0,0,&ranges[2]); | |||||
| o->add("15 Meters",0,0,&ranges[3]); | |||||
| o->value(3); | |||||
| } | |||||
| { Fl_Choice *o = _projection_choice = new Fl_Choice(X + W - 75,Y + H - 18,75,18,"Projection:"); | |||||
| o->box(FL_UP_FRAME); | |||||
| o->down_box(FL_DOWN_FRAME); | |||||
| o->textsize(9); | |||||
| o->labelsize(9); | |||||
| o->align(FL_ALIGN_LEFT); | |||||
| o->add("Spherical"); | |||||
| o->add("Planar"); | |||||
| o->value(0); | |||||
| } | |||||
| end(); | |||||
| } | |||||
| /** set X, Y, W, and H to the bounding box of point /p/ in screen coords */ | /** set X, Y, W, and H to the bounding box of point /p/ in screen coords */ | ||||
| void | void | ||||
| Panner::point_bbox ( const Point *p, int *X, int *Y, int *W, int *H ) const | Panner::point_bbox ( const Point *p, int *X, int *Y, int *W, int *H ) const | ||||
| @@ -38,21 +76,32 @@ Panner::point_bbox ( const Point *p, int *X, int *Y, int *W, int *H ) const | |||||
| bbox( tx, ty, tw, th ); | bbox( tx, ty, tw, th ); | ||||
| const float PW = pw(); | |||||
| const float PH = ph(); | |||||
| float px, py; | |||||
| float s = 1.0f; | |||||
| if ( projection() == POLAR ) | |||||
| { | |||||
| project_polar( p, &px, &py, &s ); | |||||
| } | |||||
| else | |||||
| { | |||||
| project_ortho( p, &px, &py, &s ); | |||||
| } | |||||
| tw -= PW; | |||||
| th -= PH; | |||||
| const float htw = float(tw)*0.5f; | |||||
| const float hth = float(th)*0.5f; | |||||
| float px, py; | |||||
| p->axes( &px, &py ); | |||||
| *W = *H = tw * s; | |||||
| if ( *W < 8 ) | |||||
| *W = 8; | |||||
| if ( *H < 8 ) | |||||
| *H = 8; | |||||
| *X = tx + ((tw / 2) * px + (tw / 2)); | |||||
| *Y = ty + ((th / 2) * py + (th / 2)); | |||||
| *X = tx + (htw * px + htw) - *W/2; | |||||
| *Y = ty + (hth * py + hth) - *H/2; | |||||
| *W = PW; | |||||
| *H = PH; | |||||
| } | } | ||||
| Panner::Point * | Panner::Point * | ||||
| @@ -85,18 +134,183 @@ Panner::draw_the_box ( int tx, int ty, int tw, int th ) | |||||
| { | { | ||||
| draw_box(); | draw_box(); | ||||
| if ( tw == 92 ) | |||||
| { | |||||
| Fl_Image *i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-92x92.png" ); | |||||
| i->draw( tx, ty ); | |||||
| } | |||||
| else if ( tw > 400 ) | |||||
| { | |||||
| Fl_Image *i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-512x512.png" ); | |||||
| i->draw( tx, ty ); | |||||
| } | |||||
| Fl_Image *i = 0; | |||||
| if ( projection() == POLAR ) | |||||
| { | |||||
| switch ( tw ) | |||||
| { | |||||
| case 802: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-sphere-802x802.png" ); | |||||
| break; | |||||
| case 92: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-sphere-92x92.png" ); | |||||
| break; | |||||
| case 502: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-sphere-502x502.png" ); | |||||
| break; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| switch ( tw ) | |||||
| { | |||||
| case 802: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-plane-802x802.png" ); | |||||
| break; | |||||
| case 92: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-plane-92x92.png" ); | |||||
| break; | |||||
| case 502: | |||||
| i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-plane-502x502.png" ); | |||||
| break; | |||||
| } | |||||
| } | |||||
| if ( i ) | |||||
| i->draw( tx, ty ); | |||||
| } | |||||
| void | |||||
| Panner::draw_grid ( int tx, int ty, int tw, int th ) | |||||
| { | |||||
| fl_push_matrix(); | |||||
| fl_translate(tx,ty); | |||||
| fl_scale(tw,th); | |||||
| fl_color( fl_color_add_alpha( FL_WHITE, 25 ) ); | |||||
| for ( float x = 0.0f; x <= 1.0f; x += 0.05f ) | |||||
| { | |||||
| fl_begin_line(); | |||||
| for ( float y = 0.0f; y <= 1.0f; y += 0.5f ) | |||||
| { | |||||
| fl_vertex( x, y ); | |||||
| } | |||||
| fl_end_line(); | |||||
| } | |||||
| for ( float y = 0.0f; y <= 1.0f; y += 0.05f ) | |||||
| { | |||||
| fl_begin_line(); | |||||
| for ( float x = 0.0f; x <= 1.0f; x += 0.5f ) | |||||
| { | |||||
| fl_vertex( x, y ); | |||||
| } | |||||
| fl_end_line(); | |||||
| } | |||||
| for ( float x = 0.0f; x < 1.0f; x += 0.05f ) | |||||
| { | |||||
| fl_begin_points(); | |||||
| for ( float y = 0.0f; y < 1.0f; y += 0.05f ) | |||||
| { | |||||
| fl_vertex( x, y ); | |||||
| } | |||||
| fl_end_points(); | |||||
| } | |||||
| fl_pop_matrix(); | |||||
| } | |||||
| /** translate angle /a/ into x/y coords and place the result in /X/ and /Y/ */ | |||||
| void | |||||
| Panner::project_polar ( const Point *p, float *X, float *Y, float *S ) const | |||||
| { | |||||
| float xp = 0.0f; | |||||
| float yp = 0.0f; | |||||
| float zp = 8.0f; | |||||
| float x = 0 - p->y; | |||||
| float y = 0 - p->x; | |||||
| float z = 0 - p->z; | |||||
| x /= range(); | |||||
| y /= range(); | |||||
| z /= range(); | |||||
| *X = ((x-xp) / (z + zp)) * (zp); | |||||
| *Y = ((y-yp) / (z + zp)) * (zp); | |||||
| *S = (0.025f / (z + zp)) * (zp); | |||||
| } | |||||
| /** translate angle /a/ into x/y coords and place the result in /X/ and /Y/ */ | |||||
| void | |||||
| Panner::project_ortho ( const Point *p, float *X, float *Y, float *S ) const | |||||
| { | |||||
| const float x = ( 0 - p->y ) / range(); | |||||
| const float y = ( 0 - p->x ) / range(); | |||||
| const float z = p->z; | |||||
| float zp = 4.0f; | |||||
| *X = x; | |||||
| *Y = y; | |||||
| *S = 0.025f; | |||||
| } | |||||
| void | |||||
| Panner::set_ortho ( Point *p, float x, float y ) | |||||
| { | |||||
| y = 0 - y; | |||||
| y *= 2; | |||||
| x *= 2; | |||||
| p->x = (y) * range(); | |||||
| p->y = (0 - x) * range(); | |||||
| } | |||||
| void | |||||
| Panner::set_polar ( Point *p, float x, float y ) | |||||
| { | |||||
| /* FIXME: not quite the inverse of the projection... */ | |||||
| x = 0 - x; | |||||
| y = 0 - y; | |||||
| x *= 2; | |||||
| y *= 2; | |||||
| float r = powf( x,2 ) + powf(y,2 ); | |||||
| float X = ( 2 * x ) / ( 1 + r ); | |||||
| float Y = ( 2 * y ) / ( 1 + r ); | |||||
| float Z = ( -1 + r ) / ( 1 + r ); | |||||
| float S = p->radius() / range(); | |||||
| X *= S; | |||||
| Y *= S; | |||||
| Z *= S; | |||||
| p->azimuth( -atan2f( X,Y ) * ( 180 / M_PI ) ); | |||||
| if ( p->elevation() > 0.0f ) | |||||
| p->elevation( -atan2f( Z, sqrtf( powf(X,2) + powf( Y, 2 ))) * ( 180 / M_PI ) ); | |||||
| else | |||||
| p->elevation( atan2f( Z, sqrtf( powf(X,2) + powf( Y, 2 ))) * ( 180 / M_PI ) ); | |||||
| } | |||||
| void | |||||
| Panner::set_polar_radius ( Point *p, float x, float y ) | |||||
| { | |||||
| y = 0 - y; | |||||
| float r = sqrtf( powf( y, 2 ) + powf( x, 2 ) ); | |||||
| if ( r > 1.0f ) | |||||
| r = 1.0f; | |||||
| r *= range() * 2; | |||||
| if ( r > range() ) | |||||
| r = range(); | |||||
| p->radius( r ); | |||||
| } | } | ||||
| void | void | ||||
| @@ -115,21 +329,20 @@ Panner::draw ( void ) | |||||
| // draw_box(); | // draw_box(); | ||||
| draw_label(); | draw_label(); | ||||
| if ( _bypassed ) | |||||
| { | |||||
| draw_box(); | |||||
| fl_color( 0 ); | |||||
| fl_font( FL_HELVETICA, 12 ); | |||||
| fl_draw( "(bypass)", x(), y(), w(), h(), FL_ALIGN_CENTER ); | |||||
| goto done; | |||||
| } | |||||
| /* if ( _bypassed ) */ | |||||
| /* { */ | |||||
| /* draw_box(); */ | |||||
| /* fl_color( 0 ); */ | |||||
| /* fl_font( FL_HELVETICA, 12 ); */ | |||||
| /* fl_draw( "(bypass)", x(), y(), w(), h(), FL_ALIGN_CENTER ); */ | |||||
| /* goto done; */ | |||||
| /* } */ | |||||
| /* tx += b; */ | /* tx += b; */ | ||||
| /* ty += b; */ | /* ty += b; */ | ||||
| /* tw -= b * 2; */ | /* tw -= b * 2; */ | ||||
| /* th -= b * 2; */ | /* th -= b * 2; */ | ||||
| fl_line_style( FL_SOLID, 1 ); | fl_line_style( FL_SOLID, 1 ); | ||||
| fl_color( FL_WHITE ); | fl_color( FL_WHITE ); | ||||
| @@ -141,50 +354,72 @@ Panner::draw ( void ) | |||||
| if ( ! p->visible ) | if ( ! p->visible ) | ||||
| continue; | continue; | ||||
| Fl_Color c = fl_color_add_alpha( p->color, 127 ); | |||||
| Fl_Color c = fl_color_add_alpha( p->color, 100 ); | |||||
| fl_color(c); | |||||
| int px, py, pw, ph; | int px, py, pw, ph; | ||||
| point_bbox( p, &px, &py, &pw, &ph ); | point_bbox( p, &px, &py, &pw, &ph ); | ||||
| { | { | ||||
| const float S = ( 0.5 + ( 1.0f - p->d ) ); | |||||
| float po = 5 * S; | |||||
| float po = 5; | |||||
| fl_push_clip( px - ( po * 12 ), | fl_push_clip( px - ( po * 12 ), | ||||
| py - ( po * 12 ), | py - ( po * 12 ), | ||||
| pw + ( po * 24 ), ph + (po * 24 )); | pw + ( po * 24 ), ph + (po * 24 )); | ||||
| // fl_color( fl_color_add_alpha( fl_rgb_color( 254,254,254 ), 254 ) ); | |||||
| fl_pie( px + 5, py + 5, pw - 10, ph - 10, 0, 360 ); | fl_pie( px + 5, py + 5, pw - 10, ph - 10, 0, 360 ); | ||||
| fl_color(c); | |||||
| fl_pie( px, py, pw, ph, 0, 360 ); | fl_pie( px, py, pw, ph, 0, 360 ); | ||||
| if ( Fl::belowmouse() == this ) | |||||
| { | |||||
| /* draw echo */ | |||||
| fl_color( c = fl_darker( c ) ); | |||||
| fl_pop_clip(); | |||||
| fl_arc( px - po, py - po, pw + ( po * 2 ), ph + ( po * 2 ), 0, 360 ); | |||||
| fl_color( c = fl_darker( c ) ); | |||||
| fl_arc( px - ( po * 2 ), py - ( po * 2 ), pw + ( po * 4 ), ph + ( po * 4 ), 0, 360 ); | |||||
| if ( projection() == POLAR ) | |||||
| { | |||||
| fl_color( fl_color_average( fl_rgb_color( 127,127,127 ), p->color, 0.50 ) ); | |||||
| fl_begin_loop(); | |||||
| fl_circle( tx + tw/2, ty + th/2, tw/2.0f * ( ( p->radius() / range() ))); | |||||
| fl_end_loop(); | |||||
| } | } | ||||
| fl_pop_clip(); | |||||
| } | } | ||||
| const char *s = p->label; | const char *s = p->label; | ||||
| fl_color( fl_color_add_alpha( fl_rgb_color( 220,255,255 ), 127 ) ); | fl_color( fl_color_add_alpha( fl_rgb_color( 220,255,255 ), 127 ) ); | ||||
| fl_font( FL_HELVETICA_BOLD_ITALIC, 12 ); | |||||
| fl_font( FL_HELVETICA_BOLD_ITALIC, 10 ); | |||||
| fl_draw( s, px + 20, py + 1, 50, ph - 1, FL_ALIGN_LEFT ); | fl_draw( s, px + 20, py + 1, 50, ph - 1, FL_ALIGN_LEFT ); | ||||
| if ( tw > 100 ) | |||||
| { | |||||
| char pat[50]; | |||||
| snprintf( pat, sizeof(pat), "%.1f°:%.1f° %.1fm", p->azimuth(), p->elevation(), p->radius() ); | |||||
| // fl_color( fl_color_add_alpha( fl_rgb_color( 220,255,255 ), 127 ) ); | |||||
| fl_font( FL_COURIER, 9 ); | |||||
| fl_draw( pat, px + 20, py + 15, 50, ph - 1, FL_ALIGN_LEFT | FL_ALIGN_WRAP ); | |||||
| /* fl_font( FL_HELVETICA_ITALIC, 9 ); */ | |||||
| /* snprintf(pat, sizeof(pat), "range: %.1f meters", range() ); */ | |||||
| /* fl_draw( pat, tx, ty, tw, th, FL_ALIGN_LEFT | FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE ); */ | |||||
| /* if ( _projection == POLAR ) */ | |||||
| /* { */ | |||||
| /* fl_draw( "Polar perspective; azimuth, elevation and radius input. Right click controls radius.", tx, ty, tw, th, FL_ALIGN_BOTTOM | FL_ALIGN_RIGHT | FL_ALIGN_INSIDE ); */ | |||||
| /* } */ | |||||
| /* else */ | |||||
| /* { */ | |||||
| /* fl_draw( "Polar orthographic; angle and distance input.", tx, ty, tw, th, FL_ALIGN_BOTTOM | FL_ALIGN_RIGHT | FL_ALIGN_INSIDE ); */ | |||||
| /* } */ | |||||
| } | |||||
| } | } | ||||
| if ( tw > 200 ) | |||||
| draw_children(); | |||||
| done: | done: | ||||
| @@ -203,7 +438,7 @@ Panner::point( int i ) | |||||
| int | int | ||||
| Panner::handle ( int m ) | Panner::handle ( int m ) | ||||
| { | { | ||||
| int r = Fl_Widget::handle( m ); | |||||
| int r = Fl_Group::handle( m ); | |||||
| switch ( m ) | switch ( m ) | ||||
| { | { | ||||
| @@ -213,16 +448,16 @@ Panner::handle ( int m ) | |||||
| return 1; | return 1; | ||||
| case FL_PUSH: | case FL_PUSH: | ||||
| { | { | ||||
| if ( Fl::event_button1() || Fl::event_button3() ) | |||||
| drag = event_point(); | |||||
| if ( Fl::event_button2() ) | if ( Fl::event_button2() ) | ||||
| { | { | ||||
| _bypassed = ! _bypassed; | |||||
| redraw(); | |||||
| return 1; | |||||
| /* if ( _projection == POLAR ) */ | |||||
| /* _projection = ORTHO; */ | |||||
| /* else */ | |||||
| /* _projection = POLAR; */ | |||||
| } | } | ||||
| if ( Fl::event_button1() ) | |||||
| drag = event_point(); | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| case FL_RELEASE: | case FL_RELEASE: | ||||
| @@ -237,29 +472,42 @@ Panner::handle ( int m ) | |||||
| return 0; | return 0; | ||||
| case FL_MOUSEWHEEL: | case FL_MOUSEWHEEL: | ||||
| { | { | ||||
| /* TODO: place point on opposite face of sphere */ | |||||
| return 0; | |||||
| /* Point *p = event_point(); */ | |||||
| /* if ( p ) */ | |||||
| /* drag = p; */ | |||||
| /* if ( drag ) */ | |||||
| /* { */ | |||||
| /* // drag->elevation( drag->elevation() + Fl::event_dy()); */ | |||||
| /* drag->elevation( 0 - drag->elevation() ); */ | |||||
| /* do_callback(); */ | |||||
| /* redraw(); */ | |||||
| /* return 1; */ | |||||
| /* } */ | |||||
| return 1; | |||||
| } | } | ||||
| case FL_DRAG: | case FL_DRAG: | ||||
| { | { | ||||
| if ( ! drag ) | if ( ! drag ) | ||||
| return 0; | return 0; | ||||
| /* else if ( Fl::event_button1() && ( drag = event_point() ) ) */ | |||||
| /* return 1; */ | |||||
| /* else */ | |||||
| int tx, ty, tw, th; | int tx, ty, tw, th; | ||||
| bbox( tx, ty, tw, th ); | bbox( tx, ty, tw, th ); | ||||
| float X = Fl::event_x() - tx; | |||||
| float Y = Fl::event_y() - ty; | |||||
| /* if ( _outs < 3 ) */ | |||||
| /* drag->angle( (float)(X / (tw / 2)) - 1.0f, 0.0f ); */ | |||||
| /* else */ | |||||
| drag->angle( (float)(X / (tw / 2)) - 1.0f, (float)(Y / (th / 2)) - 1.0f ); | |||||
| float X = (float(Fl::event_x() - tx) / tw ) - 0.5f; | |||||
| float Y = (float(Fl::event_y() - ty) / th) - 0.5f; | |||||
| if ( Fl::event_button1() ) | |||||
| { | |||||
| if ( POLAR == projection() ) | |||||
| set_polar( drag,X,Y ); | |||||
| else | |||||
| set_ortho( drag, X,Y ); | |||||
| } | |||||
| else | |||||
| set_polar_radius( drag,X,Y ); | |||||
| if ( when() & FL_WHEN_CHANGED ) | if ( when() & FL_WHEN_CHANGED ) | ||||
| do_callback(); | do_callback(); | ||||
| @@ -19,130 +19,113 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <FL/Fl_Widget.H> | |||||
| #include <FL/Fl_Group.H> | |||||
| #include <FL/fl_draw.H> | #include <FL/fl_draw.H> | ||||
| #include <FL/Fl.H> | #include <FL/Fl.H> | ||||
| #include <FL/Fl_Choice.H> | |||||
| #include <math.h> | #include <math.h> | ||||
| #include <vector> | #include <vector> | ||||
| using namespace std; | using namespace std; | ||||
| class Panner : public Fl_Widget | |||||
| class Panner : public Fl_Group | |||||
| { | { | ||||
| Fl_Choice *_range_choice; | |||||
| Fl_Choice *_projection_choice; | |||||
| void draw_grid( int,int,int,int); | |||||
| void draw_the_box( int, int, int, int ); | void draw_the_box( int, int, int, int ); | ||||
| public: | public: | ||||
| struct Point | struct Point | ||||
| { | { | ||||
| /* axes */ | |||||
| /* distance from center (from 0 to 1) */ | |||||
| float d; | |||||
| /* angle */ | |||||
| float a; | |||||
| float x,y,z; | |||||
| const char *label; | const char *label; | ||||
| void *userdata; | void *userdata; | ||||
| Fl_Color color; | Fl_Color color; | ||||
| bool visible; | bool visible; | ||||
| Point ( ) : d( 0.0f ), a( 0.0f ), label(0), visible(1){ } | |||||
| Point ( float D, float A ) : d( D ), a( A ), label(0), visible(1) { } | |||||
| Point ( ) { | |||||
| x = 1; | |||||
| y = 0; | |||||
| z = 0; | |||||
| label = 0; | |||||
| visible = 1; | |||||
| color = FL_WHITE; | |||||
| } | |||||
| /** translate angle /a/ into x/y coords and place the result in /X/ and /Y/ */ | |||||
| void | |||||
| axes ( float *X, float *Y ) const | |||||
| Point ( float D, float A ) | |||||
| { | { | ||||
| /* rotate */ | |||||
| float A = ( 270 - a ) * ( M_PI / 180 ); | |||||
| *X = -d * cosf( A ); | |||||
| *Y = d * sinf( A ); | |||||
| radius( D ); | |||||
| azimuth( A ); | |||||
| label = 0; | |||||
| visible = 1; | |||||
| color = FL_WHITE; | |||||
| } | } | ||||
| float azimuth ( void ) const | |||||
| static inline void spherical_to_cartesian (float a, float e, float &x, float &y, float &z ) | |||||
| { | { | ||||
| return a > 180.0f ? a - 360.0f : a; | |||||
| a *= M_PI / 180.0f; | |||||
| e *= M_PI / 180.0f; | |||||
| z = sinf(e); | |||||
| const float ce = cosf(e); | |||||
| x = ce * cosf(-a); | |||||
| y = ce * sinf(-a); | |||||
| } | } | ||||
| float elevation ( void ) const | |||||
| float azimuth ( void ) const { | |||||
| return -atan2f( y,x ) * ( 180 / M_PI ); | |||||
| } | |||||
| float elevation ( void ) const { | |||||
| return atan2f(z,sqrtf(powf(x,2)+powf(y,2)) ) * ( 180 / M_PI ); | |||||
| } | |||||
| float radius ( void ) const { | |||||
| return sqrtf(powf(x,2)+powf(y,2)+powf(z,2)); | |||||
| } | |||||
| void azimuth ( float v ) | |||||
| { | { | ||||
| return ( 1.0f - d ) * 90.0f; | |||||
| } | |||||
| float r = radius(); | |||||
| void azimuth ( float v ) | |||||
| { | |||||
| a = v < 0.0f ? v + 360.0f : v; | |||||
| a = a < 0.0f ? 0.0f : a > 360.0f ? 360.0f : a; | |||||
| spherical_to_cartesian( v, elevation(), x,y,z ); | |||||
| x *= r; | |||||
| y *= r; | |||||
| z *= r; | |||||
| } | } | ||||
| void elevation ( float v ) | void elevation ( float v ) | ||||
| { | { | ||||
| d = 1.0f - ( v / 90.0f ); | |||||
| d = d < 0.0f ? 0.0f : d > 1.0f ? 1.0f : d; | |||||
| } | |||||
| /** set point position in X, Y coordinates (0.0 to 1.0) */ | |||||
| void | |||||
| angle ( float X1, float Y1 ) | |||||
| { | |||||
| float r = radius(); | |||||
| float X2, Y2; | |||||
| Y2 = X2 = 0; | |||||
| float t; | |||||
| t = atan2( X2 - X1, Y2 - Y1 ); | |||||
| a = t * (180 / M_PI); | |||||
| if ( a < 0 ) | |||||
| a = 360 + a; | |||||
| a = 360 - a; | |||||
| /* standard distance calculation */ | |||||
| d = sqrt( pow( X2 - X1, 2 ) + pow( Y2 - Y1, 2 ) ); | |||||
| if ( d > 1.0f ) | |||||
| d = 1.0f; | |||||
| spherical_to_cartesian( azimuth(), v, x,y,z ); | |||||
| x *= r; | |||||
| y *= r; | |||||
| z *= r; | |||||
| } | } | ||||
| /** return the distance between the point and that referenced by /p/ */ | |||||
| float | |||||
| distance ( const Point &p ) | |||||
| void radius ( float v ) | |||||
| { | { | ||||
| /* first, transform point coords */ | |||||
| float x1, y1, x2, y2; | |||||
| float r = v; | |||||
| axes( &x1, &y1 ); | |||||
| p.axes( &x2, &y2 ); | |||||
| spherical_to_cartesian( azimuth(), elevation(), x,y,z ); | |||||
| /* standard distance calculation */ | |||||
| return sqrt( pow( x1 - x2, 2 ) + pow( y1 - y2, 2 ) ); | |||||
| x *= r; | |||||
| y *= r; | |||||
| z *= r; | |||||
| } | } | ||||
| }; | }; | ||||
| private: | private: | ||||
| float _range; | |||||
| /* channel configuration */ | |||||
| int _ins, | |||||
| _outs; | |||||
| bool _bypassed; | |||||
| vector <Point> _points; | vector <Point> _points; | ||||
| static int pw ( void ) { return 16; } | |||||
| static int ph ( void ) { return 16; } | |||||
| static int _configs[][12]; | static int _configs[][12]; | ||||
| void bbox ( int &X, int &Y, int &W, int &H ) const | void bbox ( int &X, int &Y, int &W, int &H ) const | ||||
| @@ -169,6 +152,14 @@ private: | |||||
| static Point * drag; | static Point * drag; | ||||
| void set_polar ( Point *p, float x, float y ); | |||||
| void set_ortho ( Point *p, float x, float y ); | |||||
| void set_polar_radius ( Point *p, float x, float y ); | |||||
| void project_polar ( const Point *p, float *X, float *Y, float *S ) const; | |||||
| void project_ortho ( const Point *p, float *X, float *Y, float *S ) const; | |||||
| int _projection; | |||||
| protected: | protected: | ||||
| virtual void draw ( void ); | virtual void draw ( void ); | ||||
| @@ -176,18 +167,15 @@ protected: | |||||
| public: | public: | ||||
| enum { POLAR, ORTHO }; | |||||
| Panner ( int X, int Y, int W, int H, const char *L = 0 ) : | |||||
| Fl_Widget( X, Y, W, H, L ) | |||||
| { | |||||
| _bypassed = false; | |||||
| _ins = 1; | |||||
| float projection ( void ) const { return _projection_choice->value(); } | |||||
| void projection ( int v ) { _projection_choice->value(v); } | |||||
| _outs = 1; | |||||
| Panner ( int X, int Y, int W, int H, const char *L = 0 ); | |||||
| _points.push_back( Point( 1, 0 ) ); | |||||
| } | |||||
| float range ( void ) const { return *((int*)_range_choice->mvalue()->user_data()); } | |||||
| /* void range ( float v ) { _range = v; } */ | |||||
| void clear_points ( void ) { _points.clear(); } | void clear_points ( void ) { _points.clear(); } | ||||
| @@ -35,10 +35,11 @@ | |||||
| #include "Mixer.H" | #include "Mixer.H" | ||||
| #include "debug.h" | #include "debug.h" | ||||
| #include <FL/Fl_Menu_Bar.H> | |||||
| Spatialization_Console::Spatialization_Console ( void ) : Fl_Double_Window( 565, 565 ) | |||||
| Spatialization_Console::Spatialization_Console ( void ) : Fl_Double_Window( 850, 850 ) | |||||
| { | { | ||||
| _resized = false; | _resized = false; | ||||
| _min_width = 100; | _min_width = 100; | ||||
| @@ -46,8 +47,8 @@ Spatialization_Console::Spatialization_Console ( void ) : Fl_Double_Window( 565, | |||||
| label( "Spatialization Console" ); | label( "Spatialization Console" ); | ||||
| fl_font( FL_HELVETICA, 14 ); | fl_font( FL_HELVETICA, 14 ); | ||||
| panner = new Panner( 25,25, 512, 512 ); | |||||
| panner = new Panner( 25,25, 802,802 ); | |||||
| panner->callback( cb_panner_value_handle, this ); | panner->callback( cb_panner_value_handle, this ); | ||||
| panner->when( FL_WHEN_CHANGED ); | panner->when( FL_WHEN_CHANGED ); | ||||
| @@ -64,6 +65,11 @@ Spatialization_Console::~Spatialization_Console ( ) | |||||
| void | void | ||||
| Spatialization_Console::make_controls ( void ) | Spatialization_Console::make_controls ( void ) | ||||
| { | { | ||||
| @@ -87,6 +93,7 @@ Spatialization_Console::make_controls ( void ) | |||||
| p.azimuth( o->spatializer()->control_output[0].control_value() ); | p.azimuth( o->spatializer()->control_output[0].control_value() ); | ||||
| p.elevation( o->spatializer()->control_output[1].control_value() ); | p.elevation( o->spatializer()->control_output[1].control_value() ); | ||||
| p.radius( o->spatializer()->control_output[2].control_value() ); | |||||
| } | } | ||||
| else | else | ||||
| p.visible = false; | p.visible = false; | ||||
| @@ -111,6 +118,7 @@ Spatialization_Console::cb_panner_value_handle ( Fl_Widget *w, void *v ) | |||||
| cm->control_output[0].control_value( p->azimuth() ); | cm->control_output[0].control_value( p->azimuth() ); | ||||
| cm->control_output[1].control_value( p->elevation() ); | cm->control_output[1].control_value( p->elevation() ); | ||||
| cm->control_output[2].control_value( p->radius() ); | |||||
| } | } | ||||
| /* Display changes initiated via automation or from other parts of the GUI */ | /* Display changes initiated via automation or from other parts of the GUI */ | ||||
| @@ -128,6 +136,7 @@ Spatialization_Console::handle_control_changed ( Controller_Module *m ) | |||||
| { | { | ||||
| p->azimuth( m->control_output[0].control_value() ); | p->azimuth( m->control_output[0].control_value() ); | ||||
| p->elevation( m->control_output[1].control_value() ); | p->elevation( m->control_output[1].control_value() ); | ||||
| p->radius( m->control_output[2].control_value() ); | |||||
| if ( panner->visible_r() ) | if ( panner->visible_r() ) | ||||
| panner->redraw(); | panner->redraw(); | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include <FL/Fl_Double_Window.H> | #include <FL/Fl_Double_Window.H> | ||||
| #include <Module.H> | #include <Module.H> | ||||
| class Fl_Pack; | class Fl_Pack; | ||||
| class Fl_Flowpack; | class Fl_Flowpack; | ||||
| class Module; | class Module; | ||||
| @@ -0,0 +1,674 @@ | |||||
| /*******************************************************************************/ | |||||
| /* 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_draw.H> | |||||
| #include <FL/Fl_Box.H> | |||||
| #include "Spatializer_Module.H" | |||||
| #include "dsp.h" | |||||
| static const float max_distance = 15.0f; | |||||
| static const float HIGHPASS_FREQ = 200.0f; | |||||
| //static const float LOWPASS_FREQ = 70000.0f; | |||||
| static const float LOWPASS_FREQ = 22000.0f; | |||||
| #include <math.h> | |||||
| class filter | |||||
| { | |||||
| protected: | |||||
| float _sample_rate; | |||||
| float _w; | |||||
| float _last_output; | |||||
| float _last_cutoff; | |||||
| float _amount_of_current; | |||||
| float _amount_of_last; | |||||
| bool _bypass; | |||||
| void recalculate ( float cutoff ) | |||||
| { | |||||
| _last_cutoff = cutoff; | |||||
| if (_last_cutoff <= 10 ) | |||||
| { | |||||
| _bypass = true; | |||||
| } | |||||
| else if (_last_cutoff > _sample_rate * 0.5f ) | |||||
| { | |||||
| _bypass = true; | |||||
| } | |||||
| else | |||||
| { | |||||
| const float c = 2.0f - cosf(_w * _last_cutoff); | |||||
| _amount_of_last = c - sqrtf(c * c - 1.0f); | |||||
| _amount_of_current = 1 - _amount_of_last; | |||||
| _bypass = false; | |||||
| } | |||||
| } | |||||
| public: | |||||
| void sample_rate ( nframes_t srate ) | |||||
| { | |||||
| _sample_rate = srate; | |||||
| _w = (2 * M_PI) / (float)srate; | |||||
| } | |||||
| filter () | |||||
| { | |||||
| _last_output = 0; | |||||
| _last_cutoff = 0; | |||||
| _w = 0; | |||||
| _sample_rate = 0; | |||||
| _amount_of_current = 0; | |||||
| _amount_of_last = 0; | |||||
| _bypass = false; | |||||
| } | |||||
| void | |||||
| run_lowpass ( float *buf, float cutoff, nframes_t nframes ) | |||||
| { | |||||
| if (cutoff != _last_cutoff) | |||||
| { | |||||
| recalculate( cutoff ); | |||||
| } | |||||
| if ( !_bypass ) | |||||
| { | |||||
| while ( nframes-- ) | |||||
| { | |||||
| *buf = _last_output = (_amount_of_current * *buf + _amount_of_last * _last_output); | |||||
| buf++; | |||||
| } | |||||
| } | |||||
| } | |||||
| void | |||||
| run_highpass ( float *buf, float cutoff, nframes_t nframes ) | |||||
| { | |||||
| if (cutoff != _last_cutoff) | |||||
| { | |||||
| recalculate( cutoff ); | |||||
| } | |||||
| if ( !_bypass ) | |||||
| { | |||||
| while ( nframes-- ) | |||||
| { | |||||
| _last_output = ((_amount_of_current * *buf) + (_amount_of_last * _last_output)); | |||||
| *buf = *buf - _last_output; | |||||
| buf++; | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| class delay | |||||
| { | |||||
| unsigned int _sample_rate; | |||||
| float *_buffer; | |||||
| long _write_index; | |||||
| unsigned int _buffer_mask; | |||||
| float _max_delay; | |||||
| public: | |||||
| void sample_rate ( float srate ) | |||||
| { | |||||
| if ( _buffer ) | |||||
| free( _buffer ); | |||||
| unsigned int size, minsize; | |||||
| minsize = (unsigned long)(srate * _max_delay); | |||||
| size = 1; | |||||
| while (size < minsize) | |||||
| size <<= 1; | |||||
| _buffer = (float *)calloc(size, sizeof(float)); | |||||
| _buffer_mask = size - 1; | |||||
| _sample_rate = srate; | |||||
| _write_index = 0; | |||||
| } | |||||
| delay ( float max_delay ) | |||||
| { | |||||
| _max_delay = max_delay; | |||||
| _write_index = 0; | |||||
| _sample_rate = 0; | |||||
| _buffer = 0; | |||||
| _buffer_mask =0; | |||||
| } | |||||
| ~delay ( ) | |||||
| { | |||||
| if ( _buffer ) | |||||
| free( _buffer ); | |||||
| } | |||||
| void run ( float *buf, float *delaybuf, float delay, nframes_t nframes ) | |||||
| { | |||||
| const nframes_t min_delay_samples = 4; | |||||
| if ( delaybuf ) | |||||
| { | |||||
| for (nframes_t i = 0; i < nframes; i++ ) | |||||
| { | |||||
| float delay_samples = delaybuf[i] * _sample_rate; | |||||
| if ( delay_samples > _buffer_mask + 1 ) | |||||
| delay_samples = _buffer_mask; | |||||
| else if ( delay_samples < min_delay_samples ) | |||||
| delay_samples = min_delay_samples; | |||||
| long idelay_samples = (long)delay_samples; | |||||
| const float frac = delay_samples - idelay_samples; | |||||
| const long read_index = _write_index - idelay_samples; | |||||
| _buffer[_write_index++ & _buffer_mask] = buf[i]; | |||||
| const float read = interpolate_cubic (frac, | |||||
| _buffer[(read_index-1) & _buffer_mask], | |||||
| _buffer[read_index & _buffer_mask], | |||||
| _buffer[(read_index+1) & _buffer_mask], | |||||
| _buffer[(read_index+2) & _buffer_mask]); | |||||
| buf[i] = read; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| float delay_samples = delay * _sample_rate; | |||||
| if ( delay_samples > _buffer_mask + 1 ) | |||||
| delay_samples = _buffer_mask; | |||||
| else if ( delay_samples < min_delay_samples ) | |||||
| delay_samples = min_delay_samples; | |||||
| long idelay_samples = (long)delay_samples; | |||||
| const float frac = delay_samples - idelay_samples; | |||||
| for (nframes_t i = 0; i < nframes; i++ ) | |||||
| { | |||||
| const long read_index = _write_index - idelay_samples; | |||||
| _buffer[_write_index++ & _buffer_mask] = buf[i]; | |||||
| const float read = interpolate_cubic (frac, | |||||
| _buffer[(read_index-1) & _buffer_mask], | |||||
| _buffer[read_index & _buffer_mask], | |||||
| _buffer[(read_index+1) & _buffer_mask], | |||||
| _buffer[(read_index+2) & _buffer_mask]); | |||||
| buf[i] = read; | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| class ambisonic_panner | |||||
| { | |||||
| /* last values */ | |||||
| float _x, _y, _z; | |||||
| /* for stereo */ | |||||
| float _xr, _yr; | |||||
| static inline void spherical_to_cartesian (float a, float e, float &x, float &y, float &z ) | |||||
| { | |||||
| a *= DEG2RAD; | |||||
| e *= DEG2RAD; | |||||
| z = sinf(e); | |||||
| const float ce = cosf(e); | |||||
| x = ce * cosf(-a); | |||||
| y = ce * sinf(-a); | |||||
| } | |||||
| public: | |||||
| ambisonic_panner ( ) | |||||
| { | |||||
| _x = _y = _z = _xr = _yr = 1.0f; | |||||
| } | |||||
| void | |||||
| run_mono ( float *in, | |||||
| float *out_w, float *out_x, float *out_y, float *out_z, | |||||
| float a, float e, | |||||
| nframes_t nframes ) | |||||
| { | |||||
| float x = _x; | |||||
| float y = _y; | |||||
| float z = _z; | |||||
| spherical_to_cartesian( a, e, _x, _y, _z ); | |||||
| const float c = 1.0f / (float)nframes; | |||||
| /* calculate increment for linear interpolation */ | |||||
| const float dx = (_x - x) * c; | |||||
| const float dy = (_y - y) * c; | |||||
| const float dz = (_z - z) * c; | |||||
| while ( nframes-- ) | |||||
| { | |||||
| x += dx; | |||||
| y += dy; | |||||
| z += dz; | |||||
| const float t = *in++; | |||||
| *out_w++ = ONEOVERSQRT2 * t; | |||||
| *out_x++ = x * t; | |||||
| *out_y++ = y * t; | |||||
| *out_z++ = z * t; | |||||
| } | |||||
| } | |||||
| void | |||||
| run_stereo ( float *in_l, float *in_r, | |||||
| float *out_w, float *out_x, float *out_y, float *out_z, | |||||
| float a, float e, float w, | |||||
| nframes_t nframes ) | |||||
| { | |||||
| float x = _x; | |||||
| float y = _y; | |||||
| float z = _z; | |||||
| float xr = _xr; | |||||
| float yr = _yr; | |||||
| w *= 0.5f; | |||||
| spherical_to_cartesian( a - w, e, _x, _y, _z ); | |||||
| spherical_to_cartesian( a + w, e, _xr, _yr, _z ); | |||||
| const float c = 1.0f / (float)nframes; | |||||
| /* calculate increment for linear interpolation */ | |||||
| const float dx = (_x - x) * c; | |||||
| const float dy = (_y - y) * c; | |||||
| const float dz = (_z - z) * c; | |||||
| const float dxr = (_xr - xr) * c; | |||||
| const float dyr = (_yr - yr) * c; | |||||
| while ( nframes-- ) | |||||
| { | |||||
| x += dx; | |||||
| y += dy; | |||||
| z += dz; | |||||
| xr += dxr; | |||||
| yr += dyr; | |||||
| const float L = *in_l++; | |||||
| const float R = *in_r++; | |||||
| const float LR = L + R; | |||||
| *out_w++ = ONEOVERSQRT2 * LR; | |||||
| *out_x++ = x * L + xr * R; | |||||
| *out_y++ = y * L + yr * R; | |||||
| *out_z++ = z * LR; | |||||
| } | |||||
| } | |||||
| }; | |||||
| Spatializer_Module::Spatializer_Module ( ) : JACK_Module ( false ) | |||||
| { | |||||
| is_default( false ); | |||||
| _panner = 0; | |||||
| { | |||||
| Port p( this, Port::INPUT, Port::CONTROL, "Azimuth" ); | |||||
| p.hints.type = Port::Hints::LINEAR; | |||||
| p.hints.ranged = true; | |||||
| p.hints.minimum = -180.0f; | |||||
| p.hints.maximum = 180.0f; | |||||
| p.hints.default_value = 0.0f; | |||||
| p.connect_to( new float ); | |||||
| p.control_value( p.hints.default_value ); | |||||
| add_port( p ); | |||||
| } | |||||
| { | |||||
| Port p( this, Port::INPUT, Port::CONTROL, "Elevation" ); | |||||
| p.hints.type = Port::Hints::LINEAR; | |||||
| p.hints.ranged = true; | |||||
| p.hints.minimum = -90.0f; | |||||
| p.hints.maximum = 90.0f; | |||||
| p.hints.default_value = 0.0f; | |||||
| p.connect_to( new float ); | |||||
| p.control_value( p.hints.default_value ); | |||||
| add_port( p ); | |||||
| } | |||||
| { | |||||
| Port p( this, Port::INPUT, Port::CONTROL, "Radius" ); | |||||
| p.hints.type = Port::Hints::LINEAR; | |||||
| p.hints.ranged = true; | |||||
| p.hints.minimum = 0.0f; | |||||
| p.hints.maximum = max_distance; | |||||
| p.hints.default_value = 1.0f; | |||||
| p.connect_to( new float ); | |||||
| p.control_value( p.hints.default_value ); | |||||
| add_port( p ); | |||||
| } | |||||
| { | |||||
| Port p( this, Port::INPUT, Port::CONTROL, "Highpass (Hz)" ); | |||||
| p.hints.type = Port::Hints::LINEAR; | |||||
| p.hints.ranged = true; | |||||
| p.hints.minimum = 0.0f; | |||||
| p.hints.maximum = 600.0f; | |||||
| p.hints.default_value = 200.0f; | |||||
| p.connect_to( new float ); | |||||
| p.control_value( p.hints.default_value ); | |||||
| add_port( p ); | |||||
| } | |||||
| { | |||||
| Port p( this, Port::INPUT, Port::CONTROL, "Width" ); | |||||
| p.hints.type = Port::Hints::LINEAR; | |||||
| p.hints.ranged = true; | |||||
| p.hints.minimum = -90.0f; | |||||
| p.hints.maximum = 90.0f; | |||||
| p.hints.default_value = 90.0f; | |||||
| p.hints.visible = false; | |||||
| p.connect_to( new float ); | |||||
| p.control_value( p.hints.default_value ); | |||||
| add_port( p ); | |||||
| } | |||||
| log_create(); | |||||
| _panner = new ambisonic_panner(); | |||||
| labelsize(9); | |||||
| color( FL_DARK1 ); | |||||
| copy_label( "Spatializer" ); | |||||
| align(FL_ALIGN_LEFT|FL_ALIGN_TOP|FL_ALIGN_INSIDE); | |||||
| gain_smoothing.sample_rate( sample_rate() ); | |||||
| delay_smoothing.cutoff( 0.5f ); | |||||
| delay_smoothing.sample_rate( sample_rate() ); | |||||
| } | |||||
| Spatializer_Module::~Spatializer_Module ( ) | |||||
| { | |||||
| configure_inputs(0); | |||||
| delete _panner; | |||||
| delete (float*)control_input[0].buffer(); | |||||
| delete (float*)control_input[1].buffer(); | |||||
| delete (float*)control_input[2].buffer(); | |||||
| delete (float*)control_input[3].buffer(); | |||||
| delete (float*)control_input[4].buffer(); | |||||
| } | |||||
| void | |||||
| Spatializer_Module::handle_sample_rate_change ( nframes_t n ) | |||||
| { | |||||
| gain_smoothing.sample_rate( n ); | |||||
| delay_smoothing.sample_rate( n ); | |||||
| for ( unsigned int i = 0; i < audio_input.size(); i++ ) | |||||
| { | |||||
| _lowpass[i]->sample_rate( n ); | |||||
| _highpass[i]->sample_rate( n ); | |||||
| _delay[i]->sample_rate( n ); | |||||
| } | |||||
| } | |||||
| void | |||||
| Spatializer_Module::draw ( void ) | |||||
| { | |||||
| int W = 5; | |||||
| child(0)->size( w() - W, h() ); | |||||
| Module::draw_box(x(),y(),w() - W,h()); | |||||
| Module::draw_label(x() + 4,y(),w() - W,h()); | |||||
| Module *m = this; | |||||
| fl_color( fl_darker( FL_FOREGROUND_COLOR ) ); | |||||
| int spacing, offset; | |||||
| int ni = jack_output.size(); | |||||
| spacing = h() / ni; | |||||
| offset = spacing / 2; | |||||
| for ( int i = ni; i--; ) | |||||
| { | |||||
| int xi = offset + ( spacing * i ); | |||||
| fl_rectf( m->x() + m->w() - W, m->y() + xi, W, 2 ); | |||||
| } | |||||
| } | |||||
| void | |||||
| Spatializer_Module::process ( nframes_t nframes ) | |||||
| { | |||||
| if ( !bypass() ) | |||||
| { | |||||
| float azimuth = control_input[0].control_value(); | |||||
| float elevation = control_input[1].control_value(); | |||||
| float radius = control_input[2].control_value(); | |||||
| float highpass_freq = control_input[3].control_value(); | |||||
| float width = control_input[4].control_value(); | |||||
| float delay_seconds = 0.0f; | |||||
| if ( radius > 1.0f ) | |||||
| delay_seconds = ( radius - 1.0f ) / 340.29f; | |||||
| /* direct sound follows inverse square law */ | |||||
| /* but it's just the inverse as far as SPL goes */ | |||||
| /* let's not go nuts... */ | |||||
| if ( radius < 0.01f ) | |||||
| radius = 0.01f; | |||||
| float gain = 1.0f / radius; | |||||
| float cutoff_frequency = gain * LOWPASS_FREQ; | |||||
| sample_t gainbuf[nframes]; | |||||
| sample_t delaybuf[nframes]; | |||||
| bool use_gainbuf = gain_smoothing.apply( gainbuf, nframes, gain ); | |||||
| bool use_delaybuf = delay_smoothing.apply( delaybuf, nframes, delay_seconds ); | |||||
| for ( unsigned int i = 0; i < audio_input.size(); i++ ) | |||||
| { | |||||
| sample_t *buf = (sample_t*) audio_input[i].buffer(); | |||||
| /* frequency effects */ | |||||
| _highpass[i]->run_highpass( buf, highpass_freq, nframes ); | |||||
| _lowpass[i]->run_lowpass( buf, cutoff_frequency, nframes ); | |||||
| /* send to late reverb */ | |||||
| if ( i == 0 ) | |||||
| buffer_copy( (sample_t*)jack_output[0].buffer(nframes), buf, nframes ); | |||||
| else | |||||
| buffer_mix( (sample_t*)jack_output[0].buffer(nframes), buf, nframes ); | |||||
| /* /\* FIXME: use smoothed value... *\/ */ | |||||
| /* buffer_apply_gain( (sample_t*)jack_output[0].buffer(nframes), nframes, 1.0f / sqrt(D) ); */ | |||||
| if ( use_delaybuf ) | |||||
| _delay[i]->run( buf, delaybuf, 0, nframes ); | |||||
| else | |||||
| _delay[i]->run( buf, 0, delay_seconds, nframes ); | |||||
| } | |||||
| if ( audio_input.size() == 1 ) | |||||
| { | |||||
| _panner->run_mono( (sample_t*)audio_input[0].buffer(), | |||||
| (sample_t*)audio_output[0].buffer(), | |||||
| (sample_t*)audio_output[1].buffer(), | |||||
| (sample_t*)audio_output[2].buffer(), | |||||
| (sample_t*)audio_output[3].buffer(), | |||||
| azimuth, | |||||
| elevation, | |||||
| nframes ); | |||||
| } | |||||
| else | |||||
| { | |||||
| _panner->run_stereo( (sample_t*)audio_input[0].buffer(), | |||||
| (sample_t*)audio_input[1].buffer(), | |||||
| (sample_t*)audio_output[0].buffer(), | |||||
| (sample_t*)audio_output[1].buffer(), | |||||
| (sample_t*)audio_output[2].buffer(), | |||||
| (sample_t*)audio_output[3].buffer(), | |||||
| azimuth, | |||||
| elevation, | |||||
| width, | |||||
| nframes ); | |||||
| } | |||||
| /* send to early reverb */ | |||||
| for ( int i = 4; i--; ) | |||||
| buffer_copy( (sample_t*)jack_output[1 + i].buffer(nframes), | |||||
| (sample_t*)audio_output[0 + i].buffer(), | |||||
| nframes ); | |||||
| /* gain effects */ | |||||
| if ( use_gainbuf ) | |||||
| { | |||||
| for ( int i = 4; i--; ) | |||||
| buffer_apply_gain_buffer( (sample_t*)audio_output[i].buffer(), gainbuf, nframes ); | |||||
| } | |||||
| else | |||||
| { | |||||
| for ( int i = 4; i--; ) | |||||
| buffer_apply_gain( (sample_t*)audio_output[i].buffer(), nframes, gain ); | |||||
| } | |||||
| } | |||||
| } | |||||
| bool | |||||
| Spatializer_Module::configure_inputs ( int n ) | |||||
| { | |||||
| output_connection_handle->show(); | |||||
| output_connection_handle->tooltip( "Late Reverb" ); | |||||
| output_connection2_handle->show(); | |||||
| output_connection2_handle->tooltip( "Early Reverb" ); | |||||
| int on = audio_input.size(); | |||||
| if ( n > on ) | |||||
| { | |||||
| for ( int i = n - on; i--; ) | |||||
| { | |||||
| { filter *o = new filter(); | |||||
| o->sample_rate( sample_rate() ); | |||||
| _lowpass.push_back( o ); | |||||
| } | |||||
| { | |||||
| filter *o = new filter(); | |||||
| o->sample_rate( sample_rate() ); | |||||
| _highpass.push_back( o ); | |||||
| } | |||||
| { | |||||
| delay *o = new delay( max_distance / 340.29f ); | |||||
| o->sample_rate( sample_rate() ); | |||||
| _delay.push_back( o ); | |||||
| } | |||||
| add_port( Port( this, Port::INPUT, Port::AUDIO ) ); | |||||
| } | |||||
| } | |||||
| else if ( n < on ) | |||||
| { | |||||
| for ( int i = on - n; i--; ) | |||||
| { | |||||
| delete _lowpass.back(); | |||||
| _lowpass.pop_back(); | |||||
| delete _highpass.back(); | |||||
| _highpass.pop_back(); | |||||
| delete _delay.back(); | |||||
| _delay.pop_back(); | |||||
| audio_input.pop_back(); | |||||
| } | |||||
| } | |||||
| control_input[4].hints.visible = audio_input.size() == 2; | |||||
| if ( n == 0 ) | |||||
| { | |||||
| remove_jack_outputs(); | |||||
| audio_output.clear(); | |||||
| audio_input.clear(); | |||||
| } | |||||
| else | |||||
| { | |||||
| if ( audio_output.size() != 4 ) | |||||
| { | |||||
| for ( int i = 0; i < 4; i++ ) | |||||
| { | |||||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); | |||||
| } | |||||
| } | |||||
| if ( jack_output.size() != 5 ) | |||||
| { | |||||
| add_jack_output( "late reverb", 0 ); | |||||
| add_jack_output( "early reverb", 0 ); | |||||
| add_jack_output( "early reverb", 1 ); | |||||
| add_jack_output( "early reverb", 2 ); | |||||
| add_jack_output( "early reverb", 3 ); | |||||
| } | |||||
| } | |||||
| _connection_handle_outputs[0][0] = 0; | |||||
| _connection_handle_outputs[0][1] = 1; | |||||
| _connection_handle_outputs[1][0] = 1; | |||||
| _connection_handle_outputs[1][1] = jack_output.size(); | |||||
| return true; | |||||
| } | |||||
| @@ -0,0 +1,62 @@ | |||||
| /*******************************************************************************/ | |||||
| /* 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. */ | |||||
| /*******************************************************************************/ | |||||
| #pragma once | |||||
| #include "JACK_Module.H" | |||||
| #include "dsp.h" | |||||
| #include <vector> | |||||
| class filter; | |||||
| class delay; | |||||
| class ambisonic_panner; | |||||
| class Spatializer_Module : public JACK_Module | |||||
| { | |||||
| Value_Smoothing_Filter gain_smoothing; | |||||
| Value_Smoothing_Filter delay_smoothing; | |||||
| std::vector<filter*> _lowpass; | |||||
| std::vector<filter*> _highpass; | |||||
| std::vector<delay*> _delay; | |||||
| ambisonic_panner *_panner; | |||||
| public: | |||||
| virtual const char *name ( void ) const { return "Spatializer"; } | |||||
| int can_support_inputs ( int n ) { return n > 0 && n < 3 ? 4 : -1; } | |||||
| virtual bool configure_inputs ( int n ); | |||||
| Spatializer_Module ( ); | |||||
| virtual ~Spatializer_Module ( ); | |||||
| LOG_CREATE_FUNC( Spatializer_Module ); | |||||
| virtual void handle_sample_rate_change ( nframes_t n ); | |||||
| virtual void draw ( void ); | |||||
| protected: | |||||
| virtual void process ( nframes_t nframes ); | |||||
| }; | |||||
| @@ -43,6 +43,7 @@ | |||||
| /* for registration */ | /* for registration */ | ||||
| #include "Module.H" | #include "Module.H" | ||||
| #include "Gain_Module.H" | #include "Gain_Module.H" | ||||
| #include "Spatializer_Module.H" | |||||
| #include "Plugin_Module.H" | #include "Plugin_Module.H" | ||||
| #include "JACK_Module.H" | #include "JACK_Module.H" | ||||
| #include "Meter_Module.H" | #include "Meter_Module.H" | ||||
| @@ -151,6 +152,7 @@ main ( int argc, char **argv ) | |||||
| LOG_REGISTER_CREATE( Chain ); | LOG_REGISTER_CREATE( Chain ); | ||||
| LOG_REGISTER_CREATE( Plugin_Module ); | LOG_REGISTER_CREATE( Plugin_Module ); | ||||
| LOG_REGISTER_CREATE( Gain_Module ); | LOG_REGISTER_CREATE( Gain_Module ); | ||||
| LOG_REGISTER_CREATE( Spatializer_Module ); | |||||
| LOG_REGISTER_CREATE( Meter_Module ); | LOG_REGISTER_CREATE( Meter_Module ); | ||||
| LOG_REGISTER_CREATE( JACK_Module ); | LOG_REGISTER_CREATE( JACK_Module ); | ||||
| LOG_REGISTER_CREATE( Mono_Pan_Module ); | LOG_REGISTER_CREATE( Mono_Pan_Module ); | ||||
| @@ -48,6 +48,7 @@ src/Controller_Module.C | |||||
| src/DPM.C | src/DPM.C | ||||
| src/Engine/Engine.C | src/Engine/Engine.C | ||||
| src/Gain_Module.C | src/Gain_Module.C | ||||
| src/Spatializer_Module.C | |||||
| src/JACK_Module.C | src/JACK_Module.C | ||||
| src/AUX_Module.C | src/AUX_Module.C | ||||
| src/LADSPAInfo.C | src/LADSPAInfo.C | ||||
| @@ -93,8 +94,11 @@ src/Spatialization_Console.C | |||||
| cwd=start_dir, relative_trick=True) | cwd=start_dir, relative_trick=True) | ||||
| bld.install_as('${DATADIR}/pixmaps/' + APPNAME + '/icon-256x256.png', 'icons/hicolor/256x256/apps/' + APPNAME + '.png') | bld.install_as('${DATADIR}/pixmaps/' + APPNAME + '/icon-256x256.png', 'icons/hicolor/256x256/apps/' + APPNAME + '.png') | ||||
| bld.install_as('${DATADIR}/pixmaps/' + APPNAME + '/panner-512x125.png', 'pixmaps/panner-512x512.png') | |||||
| bld.install_as('${DATADIR}/pixmaps/' + APPNAME + '/panner-92x125.png', 'pixmaps/panner-92x92.png') | |||||
| start_dir = bld.path.find_dir( 'pixmaps' ) | |||||
| bld.install_files('${DATADIR}/pixmaps/' + APPNAME + '/', start_dir.ant_glob('*.png'), | |||||
| cwd=start_dir, relative_trick=True) | |||||
| bld.install_files( '/'.join( [ '${DATADIR}/doc', APPNAME ] ), bld.path.ant_glob( 'doc/*.html doc/*.png' ) ) | bld.install_files( '/'.join( [ '${DATADIR}/doc', APPNAME ] ), bld.path.ant_glob( 'doc/*.html doc/*.png' ) ) | ||||
| @@ -58,7 +58,7 @@ namespace JACK | |||||
| _client = client; | _client = client; | ||||
| _port = port; | _port = port; | ||||
| _name = strdup( jack_port_name( port ) ); | _name = strdup( jack_port_name( port ) ); | ||||
| _direction = jack_port_flags( _port ) == JackPortIsOutput ? Output : Input; | |||||
| _direction = ( jack_port_flags( _port ) & JackPortIsOutput ) ? Output : Input; | |||||
| const char *type = jack_port_type( _port ); | const char *type = jack_port_type( _port ); | ||||
| _type = Audio; | _type = Audio; | ||||
| @@ -169,10 +169,19 @@ namespace JACK | |||||
| bool | bool | ||||
| Port::activate ( void ) | Port::activate ( void ) | ||||
| { | { | ||||
| int flags = 0; | |||||
| if ( _direction == Output ) | |||||
| flags |= JackPortIsOutput; | |||||
| else | |||||
| flags |= JackPortIsInput; | |||||
| if ( _terminal ) | |||||
| flags |= JackPortIsTerminal; | |||||
| _port = jack_port_register( _client->jack_client(), _name, | _port = jack_port_register( _client->jack_client(), _name, | ||||
| _type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, | _type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, | ||||
| ( _direction == Output ? JackPortIsOutput : JackPortIsInput ) | | |||||
| ( _terminal ? JackPortIsTerminal : 0 ), | |||||
| flags, | |||||
| 0 ); | 0 ); | ||||
| if ( ! _port ) | if ( ! _port ) | ||||
| @@ -134,10 +134,10 @@ Value_Smoothing_Filter::sample_rate ( nframes_t n ) | |||||
| const float FS = n; | const float FS = n; | ||||
| const float T = 0.05f; | const float T = 0.05f; | ||||
| w = 10.0f / (FS * T); | |||||
| w = _cutoff / (FS * T); | |||||
| } | } | ||||
| void | |||||
| bool | |||||
| Value_Smoothing_Filter::apply( sample_t *dst, nframes_t nframes, float gt ) | Value_Smoothing_Filter::apply( sample_t *dst, nframes_t nframes, float gt ) | ||||
| { | { | ||||
| const float a = 0.07f; | const float a = 0.07f; | ||||
| @@ -148,6 +148,9 @@ Value_Smoothing_Filter::apply( sample_t *dst, nframes_t nframes, float gt ) | |||||
| float g1 = this->g1; | float g1 = this->g1; | ||||
| float g2 = this->g2; | float g2 = this->g2; | ||||
| if ( target_reached(gt) ) | |||||
| return false; | |||||
| for (nframes_t i = 0; i < nframes; i++) | for (nframes_t i = 0; i < nframes; i++) | ||||
| { | { | ||||
| g1 += w * (gm - g1 - a * g2); | g1 += w * (gm - g1 - a * g2); | ||||
| @@ -160,4 +163,6 @@ Value_Smoothing_Filter::apply( sample_t *dst, nframes_t nframes, float gt ) | |||||
| this->g1 = g1; | this->g1 = g1; | ||||
| this->g2 = g2; | this->g2 = g2; | ||||
| return true; | |||||
| } | } | ||||
| @@ -38,25 +38,38 @@ void buffer_copy_and_apply_gain ( sample_t *dst, const sample_t *src, nframes_t | |||||
| class Value_Smoothing_Filter | class Value_Smoothing_Filter | ||||
| { | { | ||||
| float w, g1, g2; | float w, g1, g2; | ||||
| float _cutoff; | |||||
| public: | public: | ||||
| Value_Smoothing_Filter ( ) | Value_Smoothing_Filter ( ) | ||||
| { | { | ||||
| g1 = g2 = 0; | g1 = g2 = 0; | ||||
| _cutoff = 10.0f; | |||||
| } | } | ||||
| void cutoff ( float v ) { _cutoff = v; } | |||||
| void sample_rate ( nframes_t v ); | void sample_rate ( nframes_t v ); | ||||
| inline bool target_reached ( float gt ) const { return gt == g2; } | inline bool target_reached ( float gt ) const { return gt == g2; } | ||||
| void apply ( sample_t *dst, nframes_t nframes, float target ); | |||||
| bool apply ( sample_t *dst, nframes_t nframes, float target ); | |||||
| }; | }; | ||||
| static inline float interpolate_cubic ( const float fr, const float inm1, const float in, const float inp1, const float inp2) | |||||
| { | |||||
| return in + 0.5f * fr * (inp1 - inm1 + | |||||
| fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 + | |||||
| fr * (3.0f * (in - inp1) - inm1 + inp2))); | |||||
| } | |||||
| // from SWH plugins. | // from SWH plugins. | ||||
| // Convert a value in dB's to a coefficent | // Convert a value in dB's to a coefficent | ||||
| #define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) | #define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) | ||||
| #define CO_DB(v) (20.0f * log10f(v)) | #define CO_DB(v) (20.0f * log10f(v)) | ||||
| #define DEG2RAD 0.01745329251f | |||||
| #define ONEOVERSQRT2 0.70710678118f | |||||