Mixer: Implement Spatializer module.

pull/116/head
Jonathan Moore Liles 2013-06-26 23:49:29 -07:00
parent a59dad4204
commit cfd451c4d6
31 changed files with 1487 additions and 251 deletions

@ -1 +1 @@
Subproject commit f084e8d2ccdcf8f5c997618b984c493462532a1c Subproject commit 977a180175b776f700e799112ac138281b29a7a4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -112,11 +112,12 @@ 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]; sample_t gainbuf[nframes];
smoothing.apply( gainbuf, nframes, gt ); bool use_gainbuf = smoothing.apply( gainbuf, nframes, gt );
if ( use_gainbuf )
{
for ( unsigned int i = 0; i < audio_input.size(); ++i ) for ( unsigned int i = 0; i < audio_input.size(); ++i )
{ {

View File

@ -908,8 +908,7 @@ Chain::update_connection_status ( void )
{ {
Module *m = module(i); Module *m = module(i);
if ( !strcmp( m->name(), "JACK" ) || if ( !strcmp( m->basename(), "JACK" ) )
!strcmp( m->name(), "AUX" ))
{ {
((JACK_Module*)m)->update_connection_status(); ((JACK_Module*)m)->update_connection_status();
} }

View File

@ -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;
@ -273,56 +370,24 @@ Controller_Module::connect_spatializer_to ( Module *m )
if ( ! ( azimuth_port && elevation_port ) ) if ( ! ( azimuth_port && elevation_port ) )
return false; return false;
if ( control_output.size() != 3 )
{
control_output.clear(); 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 ) ); 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(); maybe_create_panner();
Panner *o = new Panner( 0,0, 92,92 ); Panner *o = (Panner*)control;
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->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();
} }

View File

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

View File

@ -90,11 +90,12 @@ 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]; sample_t gainbuf[nframes];
smoothing.apply( gainbuf, nframes, gt ); 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--; )
{ {

View File

@ -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;

View File

@ -52,10 +52,15 @@ 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:
void update_connection_status ( void ); void update_connection_status ( void );
@ -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 );

View File

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

View File

@ -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;

View File

@ -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,6 +146,8 @@ 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;
@ -175,6 +179,12 @@ Module_Parameter_Editor::make_controls ( void )
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();

View File

@ -28,6 +28,7 @@ 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
{ {
@ -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:

View File

@ -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();
tw -= PW;
th -= PH;
float px, py; float px, py;
float s = 1.0f;
p->axes( &px, &py ); if ( projection() == POLAR )
{
project_polar( p, &px, &py, &s );
}
else
{
project_ortho( p, &px, &py, &s );
}
*X = tx + ((tw / 2) * px + (tw / 2)); const float htw = float(tw)*0.5f;
*Y = ty + ((th / 2) * py + (th / 2)); const float hth = float(th)*0.5f;
*W = *H = tw * s;
if ( *W < 8 )
*W = 8;
if ( *H < 8 )
*H = 8;
*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 = 0;
{
Fl_Image *i = Fl_Shared_Image::get( PIXMAP_PATH "/non-mixer/panner-92x92.png" );
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 ); 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 ); 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 ) /* if ( _bypassed ) */
{ /* { */
draw_box(); /* draw_box(); */
fl_color( 0 ); /* fl_color( 0 ); */
fl_font( FL_HELVETICA, 12 ); /* fl_font( FL_HELVETICA, 12 ); */
fl_draw( "(bypass)", x(), y(), w(), h(), FL_ALIGN_CENTER ); /* fl_draw( "(bypass)", x(), y(), w(), h(), FL_ALIGN_CENTER ); */
goto done; /* 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,51 +354,73 @@ 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 );
{ {
float po = 5;
const float S = ( 0.5 + ( 1.0f - p->d ) );
float po = 5 * S;
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 ) fl_pop_clip();
if ( projection() == POLAR )
{ {
/* draw echo */
fl_color( c = fl_darker( c ) );
fl_arc( px - po, py - po, pw + ( po * 2 ), ph + ( po * 2 ), 0, 360 ); fl_color( fl_color_average( fl_rgb_color( 127,127,127 ), p->color, 0.50 ) );
fl_begin_loop();
fl_color( c = fl_darker( c ) ); fl_circle( tx + tw/2, ty + th/2, tw/2.0f * ( ( p->radius() / range() )));
fl_end_loop();
fl_arc( px - ( po * 2 ), py - ( po * 2 ), pw + ( po * 4 ), ph + ( po * 4 ), 0, 360 );
} }
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:
fl_line_style( FL_SOLID, 0 ); fl_line_style( FL_SOLID, 0 );
@ -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_button2() ) if ( Fl::event_button1() || Fl::event_button3() )
{
_bypassed = ! _bypassed;
redraw();
return 1;
}
if ( Fl::event_button1() )
drag = event_point(); drag = event_point();
if ( Fl::event_button2() )
{
/* if ( _projection == POLAR ) */
/* _projection = ORTHO; */
/* else */
/* _projection = POLAR; */
}
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 */ /* Point *p = event_point(); */
return 0;
/* 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 X = (float(Fl::event_x() - tx) / tw ) - 0.5f;
float Y = Fl::event_y() - ty; float Y = (float(Fl::event_y() - ty) / th) - 0.5f;
/* if ( _outs < 3 ) */ if ( Fl::event_button1() )
/* drag->angle( (float)(X / (tw / 2)) - 1.0f, 0.0f ); */ {
/* else */ if ( POLAR == projection() )
drag->angle( (float)(X / (tw / 2)) - 1.0f, (float)(Y / (th / 2)) - 1.0f ); 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();

View File

@ -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 */ float x,y,z;
/* distance from center (from 0 to 1) */
float d;
/* angle */
float a;
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 ( ) {
Point ( float D, float A ) : d( D ), a( A ), label(0), visible(1) { } x = 1;
y = 0;
/** translate angle /a/ into x/y coords and place the result in /X/ and /Y/ */ z = 0;
void label = 0;
axes ( float *X, float *Y ) const visible = 1;
{ color = FL_WHITE;
/* rotate */
float A = ( 270 - a ) * ( M_PI / 180 );
*X = -d * cosf( A );
*Y = d * sinf( A );
} }
float azimuth ( void ) const Point ( float D, float A )
{ {
return a > 180.0f ? a - 360.0f : a; radius( D );
azimuth( A );
label = 0;
visible = 1;
color = FL_WHITE;
} }
static inline void spherical_to_cartesian (float a, float e, float &x, float &y, float &z )
float elevation ( void ) const
{ {
return ( 1.0f - d ) * 90.0f; 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 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 ) void azimuth ( float v )
{ {
a = v < 0.0f ? v + 360.0f : v; float r = radius();
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 ); float r = radius();
d = d < 0.0f ? 0.0f : d > 1.0f ? 1.0f : d;
spherical_to_cartesian( azimuth(), v, x,y,z );
x *= r;
y *= r;
z *= r;
} }
/** set point position in X, Y coordinates (0.0 to 1.0) */
void void radius ( float v )
angle ( float X1, float Y1 )
{ {
float r = v;
float X2, Y2; spherical_to_cartesian( azimuth(), elevation(), x,y,z );
Y2 = X2 = 0; x *= r;
y *= r;
float t; z *= r;
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;
} }
/** return the distance between the point and that referenced by /p/ */
float
distance ( const Point &p )
{
/* first, transform point coords */
float x1, y1, x2, y2;
axes( &x1, &y1 );
p.axes( &x2, &y2 );
/* standard distance calculation */
return sqrt( pow( x1 - x2, 2 ) + pow( y1 - y2, 2 ) );
}
}; };
private: private:
/* channel configuration */ float _range;
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 ) : float projection ( void ) const { return _projection_choice->value(); }
Fl_Widget( X, Y, W, H, L ) void projection ( int v ) { _projection_choice->value(v); }
{
_bypassed = false;
_ins = 1; Panner ( int X, int Y, int W, int H, const char *L = 0 );
_outs = 1; float range ( void ) const { return *((int*)_range_choice->mvalue()->user_data()); }
/* void range ( float v ) { _range = v; } */
_points.push_back( Point( 1, 0 ) );
}
void clear_points ( void ) { _points.clear(); } void clear_points ( void ) { _points.clear(); }

View File

@ -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;
@ -47,7 +48,7 @@ Spatialization_Console::Spatialization_Console ( void ) : Fl_Double_Window( 565,
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 );
@ -62,6 +63,11 @@ Spatialization_Console::~Spatialization_Console ( )
// controls_by_port.clear(); // controls_by_port.clear();
} }
void 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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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 );
};

View File

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

View File

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

View File

@ -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 ) | flags,
( _terminal ? JackPortIsTerminal : 0 ),
0 ); 0 );
if ( ! _port ) if ( ! _port )

View File

@ -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;
} }

View File

@ -39,24 +39,37 @@ 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