Mixer: Implement Spatializer module.
2
lib/ntk
|
@ -1 +1 @@
|
|||
Subproject commit f084e8d2ccdcf8f5c997618b984c493462532a1c
|
||||
Subproject commit 977a180175b776f700e799112ac138281b29a7a4
|
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 611 B |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 8.0 KiB |
|
@ -112,12 +112,13 @@ AUX_Module::process ( nframes_t nframes )
|
|||
{
|
||||
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 ( unsigned int i = 0; i < audio_input.size(); ++i )
|
||||
{
|
||||
if ( audio_input[i].connected() )
|
||||
|
|
|
@ -908,8 +908,7 @@ Chain::update_connection_status ( void )
|
|||
{
|
||||
Module *m = module(i);
|
||||
|
||||
if ( !strcmp( m->name(), "JACK" ) ||
|
||||
!strcmp( m->name(), "AUX" ))
|
||||
if ( !strcmp( m->basename(), "JACK" ) )
|
||||
{
|
||||
((JACK_Module*)m)->update_connection_status();
|
||||
}
|
||||
|
|
|
@ -234,6 +234,101 @@ Controller_Module::mode ( 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
|
||||
controller and connect to the given module's spatialization
|
||||
control inputs. Returns true on success, false if given module
|
||||
|
@ -241,6 +336,8 @@ Controller_Module::mode ( Mode m )
|
|||
bool
|
||||
Controller_Module::connect_spatializer_to ( Module *m )
|
||||
{
|
||||
connect_spatializer_radius_to( m );
|
||||
|
||||
/* these are for detecting related parameter groups which can be
|
||||
better represented by a single control */
|
||||
Port *azimuth_port = NULL;
|
||||
|
@ -269,60 +366,28 @@ Controller_Module::connect_spatializer_to ( Module *m )
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ! ( azimuth_port && elevation_port ) )
|
||||
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[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 )->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 )
|
||||
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[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
|
||||
|
@ -806,6 +876,14 @@ Controller_Module::handle_control_changed ( Port *p )
|
|||
pan->point( 0 )->azimuth( control_output[0].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() )
|
||||
pan->redraw();
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
|
||||
void connect_to ( Port *p );
|
||||
bool connect_spatializer_to ( Module *m );
|
||||
bool connect_spatializer_radius_to ( Module *m );
|
||||
void disconnect ( void );
|
||||
|
||||
void handle_control_changed ( Port *p );
|
||||
|
@ -111,6 +112,7 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
void maybe_create_panner ( void );
|
||||
char *generate_osc_path ( void );
|
||||
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() );
|
||||
|
||||
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--; )
|
||||
{
|
||||
if ( audio_input[i].connected() && audio_output[i].connected() )
|
||||
|
|
|
@ -54,6 +54,12 @@ JACK_Module::JACK_Module ( bool log )
|
|||
{
|
||||
_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 );
|
||||
|
||||
if ( log )
|
||||
|
@ -135,7 +141,13 @@ JACK_Module::JACK_Module ( bool log )
|
|||
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->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();
|
||||
|
@ -335,6 +347,41 @@ JACK_Module::can_support_inputs ( int )
|
|||
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
|
||||
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() )
|
||||
control_input[0].control_value_no_callback( n );
|
||||
|
||||
|
@ -465,7 +515,7 @@ JACK_Module::initialize ( void )
|
|||
void
|
||||
JACK_Module::handle_control_changed ( Port *p )
|
||||
{
|
||||
THREAD_ASSERT( UI );
|
||||
// THREAD_ASSERT( UI );
|
||||
|
||||
if ( 0 == strcmp( p->name(), "Inputs" ) )
|
||||
{
|
||||
|
@ -492,6 +542,8 @@ JACK_Module::handle_control_changed ( Port *p )
|
|||
p->connected_port()->control_value( noutputs() );
|
||||
}
|
||||
}
|
||||
|
||||
Module::handle_control_changed( p );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -519,14 +571,28 @@ JACK_Module::handle ( int m )
|
|||
return Module::handle(m) || 1;
|
||||
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" );
|
||||
|
||||
char *s = (char*)malloc(256);
|
||||
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;
|
||||
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() */
|
||||
case FL_MOVE:
|
||||
return 0;
|
||||
Module::handle(m);
|
||||
return 1;
|
||||
case FL_ENTER:
|
||||
case FL_DND_ENTER:
|
||||
Module::handle(m);
|
||||
return 1;
|
||||
case FL_LEAVE:
|
||||
case FL_DND_LEAVE:
|
||||
Module::handle(m);
|
||||
if ( this == receptive_to_drop )
|
||||
{
|
||||
receptive_to_drop = NULL;
|
||||
|
|
|
@ -52,9 +52,14 @@ protected:
|
|||
Fl_Browser * connection_display;
|
||||
Fl_Box * input_connection_handle;
|
||||
Fl_Box * output_connection_handle;
|
||||
Fl_Box * output_connection2_handle;
|
||||
|
||||
static void cb_button ( Fl_Widget *w, void *v );
|
||||
void cb_button ( Fl_Widget *w );
|
||||
|
||||
protected:
|
||||
|
||||
int _connection_handle_outputs[2][2];
|
||||
|
||||
public:
|
||||
|
||||
|
@ -63,6 +68,7 @@ public:
|
|||
JACK_Module ( bool log = true );
|
||||
virtual ~JACK_Module ( );
|
||||
|
||||
virtual const char *basename ( void ) const { return "JACK"; }
|
||||
virtual const char *name ( void ) const { return "JACK"; }
|
||||
virtual bool initialize ( void );
|
||||
|
||||
|
@ -70,6 +76,8 @@ public:
|
|||
virtual int handle ( int m );
|
||||
|
||||
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_outputs ( int n );
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "Meter_Module.H"
|
||||
#include "Plugin_Module.H"
|
||||
#include "AUX_Module.H"
|
||||
#include "Spatializer_Module.H"
|
||||
|
||||
#include <FL/Fl_Menu_Button.H>
|
||||
#include "FL/test_press.H"
|
||||
|
@ -137,6 +138,7 @@ Module::init ( void )
|
|||
align( FL_ALIGN_CENTER | FL_ALIGN_INSIDE );
|
||||
set_visible_focus();
|
||||
selection_color( FL_RED );
|
||||
labelsize(12);
|
||||
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_font( FL_HELVETICA, 12 );
|
||||
fl_font( FL_HELVETICA, labelsize() );
|
||||
|
||||
int LW = fl_width( lp );
|
||||
|
||||
|
@ -720,8 +722,32 @@ Module::insert_menu_cb ( const Fl_Menu_ *m )
|
|||
|
||||
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" ) )
|
||||
mod = new Gain_Module();
|
||||
/* else if ( !strcmp( picked, "Spatializer" ) ) */
|
||||
/* mod = new Spatializer_Module(); */
|
||||
else if ( !strcmp( picked, "Meter" ) )
|
||||
mod = new Meter_Module();
|
||||
else if ( !strcmp( picked, "Mono Pan" ))
|
||||
|
@ -826,6 +852,8 @@ Module::menu ( void ) const
|
|||
insert_menu->add( "Meter", 0, 0 );
|
||||
insert_menu->add( "Mono Pan", 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->callback( &Module::insert_menu_cb, (void*)this );
|
||||
|
|
|
@ -344,6 +344,7 @@ public:
|
|||
}
|
||||
|
||||
virtual const char *name ( void ) const = 0;
|
||||
virtual const char *basename ( void ) const { return "Module"; }
|
||||
|
||||
std::vector<Port> audio_input;
|
||||
std::vector<Port> audio_output;
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
elevation_port_number = -1;
|
||||
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 bc = FL_BACKGROUND2_COLOR;
|
||||
|
||||
|
@ -174,6 +178,12 @@ Module_Parameter_Editor::make_controls ( void )
|
|||
elevation_port_number = i;
|
||||
elevation_value = p->control_value();
|
||||
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 )
|
||||
|
@ -268,10 +278,12 @@ Module_Parameter_Editor::make_controls ( void )
|
|||
w->align(FL_ALIGN_TOP);
|
||||
w->labelsize( 10 );
|
||||
|
||||
_callback_data.push_back( callback_data( this, i ) );
|
||||
|
||||
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
|
||||
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 );
|
||||
{
|
||||
|
@ -284,7 +296,7 @@ Module_Parameter_Editor::make_controls ( void )
|
|||
|
||||
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 );
|
||||
|
@ -305,7 +317,7 @@ Module_Parameter_Editor::make_controls ( void )
|
|||
|
||||
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->color(FL_GRAY0);
|
||||
o->selection_color(FL_BACKGROUND_COLOR);
|
||||
|
@ -318,10 +330,13 @@ Module_Parameter_Editor::make_controls ( void )
|
|||
|
||||
o->align(FL_ALIGN_TOP);
|
||||
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 )->elevation( elevation_value );
|
||||
o->point( 0 )->radius( radius_value );
|
||||
|
||||
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[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[1], ((Panner*)w)->point( 0 )->elevation() );
|
||||
cd->base_widget->set_value( cd->port_number[2], ((Panner*)w)->point( 0 )->radius() );
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -417,7 +435,8 @@ Module_Parameter_Editor::handle_control_changed ( Module::Port *p )
|
|||
Fl_Widget *w = controls_by_port[i];
|
||||
|
||||
if ( i == azimuth_port_number ||
|
||||
i == elevation_port_number )
|
||||
i == elevation_port_number ||
|
||||
i == radius_port_number )
|
||||
{
|
||||
Panner *_panner = (Panner*)w;
|
||||
|
||||
|
@ -425,6 +444,8 @@ Module_Parameter_Editor::handle_control_changed ( Module::Port *p )
|
|||
_panner->point(0)->azimuth( p->control_value() );
|
||||
else if ( i == elevation_port_number )
|
||||
_panner->point(0)->elevation( p->control_value() );
|
||||
else if ( i == radius_port_number )
|
||||
_panner->point(0)->radius( p->control_value() );
|
||||
|
||||
_panner->redraw();
|
||||
|
||||
|
|
|
@ -28,11 +28,12 @@ class Fl_Menu_Button;
|
|||
class Panner;
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class Module_Parameter_Editor : public Fl_Double_Window
|
||||
{
|
||||
Module *_module;
|
||||
|
||||
|
||||
struct callback_data
|
||||
{
|
||||
Module_Parameter_Editor *base_widget;
|
||||
|
@ -44,6 +45,7 @@ class Module_Parameter_Editor : public Fl_Double_Window
|
|||
this->base_widget = base_widget;
|
||||
this->port_number[0] = port_number;
|
||||
this->port_number[1] = -1;
|
||||
this->port_number[2] = -1;
|
||||
}
|
||||
|
||||
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->port_number[0] = port_number1;
|
||||
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 elevation_port_number;
|
||||
|
||||
int radius_port_number;
|
||||
|
||||
std::list<callback_data> _callback_data;
|
||||
std::vector<Fl_Widget*> controls_by_port;
|
||||
|
||||
public:
|
||||
|
|
|
@ -25,11 +25,49 @@
|
|||
// #include <FL/fl_draw.H>
|
||||
|
||||
#include <FL/Fl_Shared_Image.H>
|
||||
#include <FL/Fl_Tiled_Image.H>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* 2D Panner widget. Supports various multichannel configurations. */
|
||||
|
||||
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 */
|
||||
void
|
||||
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 );
|
||||
|
||||
const float PW = pw();
|
||||
const float PH = ph();
|
||||
|
||||
tw -= PW;
|
||||
th -= PH;
|
||||
|
||||
float px, py;
|
||||
float s = 1.0f;
|
||||
|
||||
if ( projection() == POLAR )
|
||||
{
|
||||
project_polar( p, &px, &py, &s );
|
||||
}
|
||||
else
|
||||
{
|
||||
project_ortho( p, &px, &py, &s );
|
||||
}
|
||||
|
||||
p->axes( &px, &py );
|
||||
const float htw = float(tw)*0.5f;
|
||||
const float hth = float(th)*0.5f;
|
||||
|
||||
*X = tx + ((tw / 2) * px + (tw / 2));
|
||||
*Y = ty + ((th / 2) * py + (th / 2));
|
||||
|
||||
*W = PW;
|
||||
*H = PH;
|
||||
*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;
|
||||
|
||||
}
|
||||
|
||||
Panner::Point *
|
||||
|
@ -85,18 +134,183 @@ Panner::draw_the_box ( int tx, int ty, int tw, int th )
|
|||
{
|
||||
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
|
||||
|
@ -115,21 +329,20 @@ Panner::draw ( void )
|
|||
// draw_box();
|
||||
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; */
|
||||
/* ty += b; */
|
||||
/* tw -= b * 2; */
|
||||
/* th -= b * 2; */
|
||||
|
||||
|
||||
fl_line_style( FL_SOLID, 1 );
|
||||
|
||||
fl_color( FL_WHITE );
|
||||
|
@ -141,50 +354,72 @@ Panner::draw ( void )
|
|||
if ( ! p->visible )
|
||||
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;
|
||||
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 ),
|
||||
py - ( po * 12 ),
|
||||
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_color(c);
|
||||
|
||||
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;
|
||||
|
||||
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 );
|
||||
|
||||
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:
|
||||
|
||||
|
@ -203,7 +438,7 @@ Panner::point( int i )
|
|||
int
|
||||
Panner::handle ( int m )
|
||||
{
|
||||
int r = Fl_Widget::handle( m );
|
||||
int r = Fl_Group::handle( m );
|
||||
|
||||
switch ( m )
|
||||
{
|
||||
|
@ -213,16 +448,16 @@ Panner::handle ( int m )
|
|||
return 1;
|
||||
case FL_PUSH:
|
||||
{
|
||||
if ( Fl::event_button2() )
|
||||
{
|
||||
_bypassed = ! _bypassed;
|
||||
redraw();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( Fl::event_button1() )
|
||||
if ( Fl::event_button1() || Fl::event_button3() )
|
||||
drag = event_point();
|
||||
|
||||
if ( Fl::event_button2() )
|
||||
{
|
||||
/* if ( _projection == POLAR ) */
|
||||
/* _projection = ORTHO; */
|
||||
/* else */
|
||||
/* _projection = POLAR; */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
case FL_RELEASE:
|
||||
|
@ -237,29 +472,42 @@ Panner::handle ( int m )
|
|||
return 0;
|
||||
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:
|
||||
{
|
||||
if ( ! drag )
|
||||
return 0;
|
||||
|
||||
/* else if ( Fl::event_button1() && ( drag = event_point() ) ) */
|
||||
/* return 1; */
|
||||
/* else */
|
||||
|
||||
|
||||
int 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 )
|
||||
do_callback();
|
||||
|
|
|
@ -19,130 +19,113 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <FL/Fl_Widget.H>
|
||||
#include <FL/Fl_Group.H>
|
||||
#include <FL/fl_draw.H>
|
||||
#include <FL/Fl.H>
|
||||
|
||||
#include <FL/Fl_Choice.H>
|
||||
#include <math.h>
|
||||
|
||||
#include <vector>
|
||||
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 );
|
||||
|
||||
public:
|
||||
|
||||
struct Point
|
||||
{
|
||||
/* axes */
|
||||
|
||||
/* distance from center (from 0 to 1) */
|
||||
float d;
|
||||
/* angle */
|
||||
float a;
|
||||
float x,y,z;
|
||||
|
||||
const char *label;
|
||||
void *userdata;
|
||||
Fl_Color color;
|
||||
|
||||
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 )
|
||||
{
|
||||
d = 1.0f - ( v / 90.0f );
|
||||
d = d < 0.0f ? 0.0f : d > 1.0f ? 1.0f : d;
|
||||
}
|
||||
float r = radius();
|
||||
|
||||
/** set point position in X, Y coordinates (0.0 to 1.0) */
|
||||
void
|
||||
angle ( float X1, float Y1 )
|
||||
spherical_to_cartesian( azimuth(), v, x,y,z );
|
||||
x *= r;
|
||||
y *= r;
|
||||
z *= r;
|
||||
}
|
||||
|
||||
|
||||
void radius ( float v )
|
||||
{
|
||||
float r = v;
|
||||
|
||||
float X2, Y2;
|
||||
spherical_to_cartesian( azimuth(), elevation(), x,y,z );
|
||||
|
||||
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;
|
||||
x *= r;
|
||||
y *= r;
|
||||
z *= r;
|
||||
}
|
||||
|
||||
/** 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:
|
||||
|
||||
float _range;
|
||||
|
||||
/* channel configuration */
|
||||
int _ins,
|
||||
_outs;
|
||||
|
||||
bool _bypassed;
|
||||
|
||||
|
||||
vector <Point> _points;
|
||||
|
||||
static int pw ( void ) { return 16; }
|
||||
static int ph ( void ) { return 16; }
|
||||
|
||||
static int _configs[][12];
|
||||
|
||||
void bbox ( int &X, int &Y, int &W, int &H ) const
|
||||
|
@ -169,6 +152,14 @@ private:
|
|||
|
||||
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:
|
||||
|
||||
virtual void draw ( void );
|
||||
|
@ -176,18 +167,15 @@ protected:
|
|||
|
||||
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;
|
||||
float projection ( void ) const { return _projection_choice->value(); }
|
||||
void projection ( int v ) { _projection_choice->value(v); }
|
||||
|
||||
_ins = 1;
|
||||
Panner ( int X, int Y, int W, int H, const char *L = 0 );
|
||||
|
||||
_outs = 1;
|
||||
|
||||
_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(); }
|
||||
|
||||
|
|
|
@ -35,10 +35,11 @@
|
|||
#include "Mixer.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;
|
||||
_min_width = 100;
|
||||
|
@ -46,8 +47,8 @@ Spatialization_Console::Spatialization_Console ( void ) : Fl_Double_Window( 565,
|
|||
label( "Spatialization Console" );
|
||||
|
||||
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->when( FL_WHEN_CHANGED );
|
||||
|
@ -62,6 +63,11 @@ Spatialization_Console::~Spatialization_Console ( )
|
|||
// controls_by_port.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
|
@ -87,6 +93,7 @@ Spatialization_Console::make_controls ( void )
|
|||
|
||||
p.azimuth( o->spatializer()->control_output[0].control_value() );
|
||||
p.elevation( o->spatializer()->control_output[1].control_value() );
|
||||
p.radius( o->spatializer()->control_output[2].control_value() );
|
||||
}
|
||||
else
|
||||
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[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 */
|
||||
|
@ -128,6 +136,7 @@ Spatialization_Console::handle_control_changed ( Controller_Module *m )
|
|||
{
|
||||
p->azimuth( m->control_output[0].control_value() );
|
||||
p->elevation( m->control_output[1].control_value() );
|
||||
p->radius( m->control_output[2].control_value() );
|
||||
|
||||
if ( panner->visible_r() )
|
||||
panner->redraw();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <FL/Fl_Double_Window.H>
|
||||
#include <Module.H>
|
||||
|
||||
|
||||
class Fl_Pack;
|
||||
class Fl_Flowpack;
|
||||
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 */
|
||||
#include "Module.H"
|
||||
#include "Gain_Module.H"
|
||||
#include "Spatializer_Module.H"
|
||||
#include "Plugin_Module.H"
|
||||
#include "JACK_Module.H"
|
||||
#include "Meter_Module.H"
|
||||
|
@ -151,6 +152,7 @@ main ( int argc, char **argv )
|
|||
LOG_REGISTER_CREATE( Chain );
|
||||
LOG_REGISTER_CREATE( Plugin_Module );
|
||||
LOG_REGISTER_CREATE( Gain_Module );
|
||||
LOG_REGISTER_CREATE( Spatializer_Module );
|
||||
LOG_REGISTER_CREATE( Meter_Module );
|
||||
LOG_REGISTER_CREATE( JACK_Module );
|
||||
LOG_REGISTER_CREATE( Mono_Pan_Module );
|
||||
|
|
|
@ -48,6 +48,7 @@ src/Controller_Module.C
|
|||
src/DPM.C
|
||||
src/Engine/Engine.C
|
||||
src/Gain_Module.C
|
||||
src/Spatializer_Module.C
|
||||
src/JACK_Module.C
|
||||
src/AUX_Module.C
|
||||
src/LADSPAInfo.C
|
||||
|
@ -93,8 +94,11 @@ src/Spatialization_Console.C
|
|||
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 + '/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' ) )
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace JACK
|
|||
_client = client;
|
||||
_port = 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 );
|
||||
|
||||
_type = Audio;
|
||||
|
@ -169,10 +169,19 @@ namespace JACK
|
|||
bool
|
||||
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,
|
||||
_type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE,
|
||||
( _direction == Output ? JackPortIsOutput : JackPortIsInput ) |
|
||||
( _terminal ? JackPortIsTerminal : 0 ),
|
||||
flags,
|
||||
0 );
|
||||
|
||||
if ( ! _port )
|
||||
|
|
|
@ -134,10 +134,10 @@ Value_Smoothing_Filter::sample_rate ( nframes_t n )
|
|||
const float FS = n;
|
||||
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 )
|
||||
{
|
||||
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 g2 = this->g2;
|
||||
|
||||
if ( target_reached(gt) )
|
||||
return false;
|
||||
|
||||
for (nframes_t i = 0; i < nframes; i++)
|
||||
{
|
||||
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->g2 = g2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
17
nonlib/dsp.h
|
@ -38,25 +38,38 @@ void buffer_copy_and_apply_gain ( sample_t *dst, const sample_t *src, nframes_t
|
|||
class Value_Smoothing_Filter
|
||||
{
|
||||
float w, g1, g2;
|
||||
|
||||
|
||||
float _cutoff;
|
||||
|
||||
public:
|
||||
|
||||
Value_Smoothing_Filter ( )
|
||||
{
|
||||
g1 = g2 = 0;
|
||||
_cutoff = 10.0f;
|
||||
}
|
||||
|
||||
void cutoff ( float v ) { _cutoff = v; }
|
||||
|
||||
void sample_rate ( nframes_t v );
|
||||
|
||||
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.
|
||||
// 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 CO_DB(v) (20.0f * log10f(v))
|
||||
|
||||
#define DEG2RAD 0.01745329251f
|
||||
#define ONEOVERSQRT2 0.70710678118f
|
||||
|
|