Mixer: Implement port autoconnection system.

This commit is contained in:
Jonathan Moore Liles 2013-09-04 01:00:29 -07:00
parent a05384a8c3
commit fc5c59c61d
10 changed files with 483 additions and 60 deletions

@ -1 +1 @@
Subproject commit 645579913d0bfbfdcc9cd0107dd55bd121a99990
Subproject commit d7f94164bc2d44b7ddf39e19e90a1fc172e62c7d

View File

@ -791,6 +791,47 @@ Chain::resize ( int X, int Y, int W, int H )
controls_pack->size( W, controls_pack->h() );
}
void
Chain::get_output_ports ( std::list<std::string> &sl)
{
for ( int i = 0; i < modules(); i++ )
{
Module *m = module(i);
for ( unsigned int j = 0; j < m->aux_audio_output.size(); j++ )
{
char *s;
asprintf( &s, "%s/%s",
strip()->group()->single() ? "*" : strip()->group()->name(),
m->aux_audio_output[j].jack_port()->name() );
sl.push_back( s );
free(s);
}
}
}
void
Chain::auto_connect_outputs ( void )
{
for ( int i = 0; i < modules(); i++ )
{
module(i)->auto_connect_outputs();
}
}
void
Chain::auto_disconnect_outputs ( void )
{
for ( int i = 0; i < modules(); i++ )
{
module(i)->auto_disconnect_outputs();
}
}
/*****************/

View File

@ -77,6 +77,11 @@ protected:
public:
void auto_connect_outputs ( void );
void auto_disconnect_outputs ( void );
void get_output_ports ( std::list<std::string> &sl);
void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect );
void buffer_size ( nframes_t nframes );
int sample_rate_change ( nframes_t nframes );

View File

@ -161,7 +161,7 @@ JACK_Module::JACK_Module ( bool log )
{
Fl_Browser *o = connection_display = new Fl_Browser( 0, 0, w(), h() );
o->has_scrollbar(Fl_Browser_::VERTICAL);
o->textsize( 11 );
o->textsize( 10 );
o->textcolor( FL_LIGHT3 );
o->textfont( FL_COURIER );
o->box( FL_FLAT_BOX );
@ -497,13 +497,38 @@ JACK_Module::handle_control_changed ( Port *p )
int
JACK_Module::handle ( int m )
{
static unsigned long _event_state = 0;
unsigned long evstate = Fl::event_state();
switch ( m )
{
case FL_PUSH:
if ( Fl::event_inside( output_connection_handle ) ||
Fl::event_inside( output_connection2_handle ) ||
Fl::event_inside( input_connection_handle ) )
{
_event_state = evstate;
return 1;
}
return Module::handle(m) || 1;
case FL_RELEASE:
Fl::selection_owner(0);
receptive_to_drop = NULL;
if ( Fl::event_inside( output_connection_handle ) ||
Fl::event_inside( output_connection2_handle ) ||
Fl::event_inside( input_connection_handle ) )
{
if ( _event_state & FL_BUTTON3 )
{
/* was a right click */
// TODO: Pop up connection menu.
}
}
return Module::handle(m) || 1;
case FL_DRAG:
{
@ -516,8 +541,6 @@ JACK_Module::handle ( int m )
if ( Fl::event_inside( output_connection2_handle ) )
connection_handle = 1;
if ( Fl::event_button1() &&
connection_handle >= 0
&& ! Fl::selection_owner() )
@ -555,6 +578,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:
if ( Fl::event_inside( output_connection_handle ) ||
Fl::event_inside( output_connection2_handle ) ||
Fl::event_inside( input_connection_handle ) )
{
fl_cursor( FL_CURSOR_HAND );
}
else
fl_cursor( FL_CURSOR_DEFAULT );
Module::handle(m);
return 1;
case FL_ENTER:
@ -569,6 +601,7 @@ JACK_Module::handle ( int m )
receptive_to_drop = NULL;
redraw();
}
fl_cursor( FL_CURSOR_DEFAULT );
return 1;
case FL_DND_RELEASE:
Fl::selection_owner(0);

View File

@ -1101,6 +1101,89 @@ Mixer::handle ( int m )
return 0;
}
#include <algorithm>
std::list <std::string>
Mixer::get_auto_connect_targets ( void )
{
std::list <std::string> sl;
std::list <std::string> rl;
for ( int i = mixer_strips->children(); i--; )
{
((Mixer_Strip*)mixer_strips->child(i))->get_output_ports(sl);
}
for ( std::list<std::string>::iterator i = sl.begin(); i != sl.end(); i++ )
{
char *s = strdup( i->c_str() );
*rindex( s, '/' ) = 0;
if ( !index( s, '/' ) )
{
char *o;
asprintf( &o, "%s/mains", s );
free(s);
s = o;
}
if ( std::find( rl.begin(), rl.end(), s ) == rl.end() )
{
rl.push_back( s );
}
free(s);
}
return rl;
}
void
Mixer::auto_connect ( void )
{
/* give strips with group affinity the first shot */
for ( int i = 0; i < mixer_strips->children(); i++ )
{
Mixer_Strip *s = ((Mixer_Strip*)mixer_strips->child(i));
if ( s->has_group_affinity() )
s->auto_connect_outputs();
}
/* now do that catch-alls, first one wins! */
for ( int i = 0; i < mixer_strips->children(); i++ )
{
Mixer_Strip *s = ((Mixer_Strip*)mixer_strips->child(i));
if ( ! s->has_group_affinity() )
s->auto_connect_outputs();
}
}
void
Mixer::maybe_auto_connect_output ( Module::Port *p )
{
/* give strips with group affinity the first shot */
for ( int i = 0; i < mixer_strips->children(); i++ )
{
Mixer_Strip *s = ((Mixer_Strip*)mixer_strips->child(i));
if ( s->has_group_affinity() )
if ( s->maybe_auto_connect_output( p ) )
return;
}
/* now do that catch-alls, first one wins! */
for ( int i = 0; i < mixer_strips->children(); i++ )
{
Mixer_Strip *s = ((Mixer_Strip*)mixer_strips->child(i));
if ( ! s->has_group_affinity() )
if ( s->maybe_auto_connect_output( p ) )
return;
}
}
/************/
/* Commands */
/************/
@ -1152,6 +1235,8 @@ Mixer::command_load ( const char *path, const char *display_name )
update_menu();
auto_connect();
mixer->activate();
return true;

View File

@ -92,6 +92,9 @@ private:
public:
void auto_connect ( void );
void maybe_auto_connect_output ( Module::Port *p );
std::list<std::string> get_auto_connect_targets ( void );
Group * group_by_name ( const char * name );
char *get_unique_group_name ( const char *name );

View File

@ -132,8 +132,8 @@ Mixer_Strip::get ( Log_Entry &e ) const
e.add( ":group", _group );
else
e.add( ":group", (Loggable*)0 );
e.add( ":auto_input", _auto_input );
e.add( ":manual_connection", _manual_connection );
}
void
@ -170,6 +170,14 @@ Mixer_Strip::set ( Log_Entry &e )
{
_mute_controller_mode = atoi( v );
}
else if ( ! strcmp( s, ":auto_input" ) )
{
auto_input( v );
}
else if ( ! strcmp( s, ":manual_connection" ) )
{
manual_connection( atoi( v ) );
}
else if ( ! strcmp( s, ":group" ) )
{
int i;
@ -288,8 +296,19 @@ void Mixer_Strip::cb_handle(Fl_Widget* o) {
else
size( 96, h() );
if ( parent() )
parent()->parent()->redraw();
if ( parent() )
parent()->parent()->redraw();
}
else if ( o == output_connection_button )
{
if ( output_connection_button->value() == 1 )
o->label( output_connection_button->mvalue()->label() );
else
o->label( NULL );
manual_connection( output_connection_button->value() );
// _manual_connection = output_connection_button->value();
}
else if ( o == group_choice )
{
@ -401,13 +420,13 @@ Mixer_Strip::set_spatializer_visibility ( void )
{
if ( fader_tab->visible() && spatialization_controller->is_controlling() )
{
spatialization_controller->show();
spatialization_label->show();
spatialization_controller->show();
spatialization_label->show();
}
else
{
spatialization_controller->hide();
spatialization_label->hide();
spatialization_controller->hide();
spatialization_label->hide();
}
}
@ -501,6 +520,8 @@ Mixer_Strip::init ( )
{
selection_color( FL_MAGENTA );
_manual_connection = 0;
_auto_input = 0;
_mute_controller_mode = 0;
_gain_controller_mode = 0;
_chain = 0;
@ -515,14 +536,14 @@ Mixer_Strip::init ( )
set_visible_focus();
{ Fl_Scalepack *o = new Fl_Scalepack( 2, 2, 116, 595 );
o->type( FL_VERTICAL );
o->spacing( 2 );
{ Fl_Scalepack *o = new Fl_Scalepack( 2, 2, 116, 595 );
o->type( FL_VERTICAL );
o->spacing( 2 );
{ Fl_Box *o = color_box = new Fl_Box( 0,0, 25, 10 );
o->box(FL_FLAT_BOX);
o->tooltip( "Drag and drop to move strip" );
}
{ Fl_Box *o = color_box = new Fl_Box( 0,0, 25, 10 );
o->box(FL_FLAT_BOX);
o->tooltip( "Drag and drop to move strip" );
}
{ Fl_Pack *o = new Fl_Pack( 2, 2, 114, 100 );
o->type( Fl_Pack::VERTICAL );
@ -649,20 +670,31 @@ Mixer_Strip::init ( )
/* { Fl_Pack *o = panner_pack = new Fl_Pack( 2, 465, 114, 40 ); */
/* o->spacing( 2 ); */
/* o->type( Fl_Pack::VERTICAL ); */
{ Fl_Box *o = spatialization_label = new Fl_Box( 0, 0, 100, 12 );
o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) );
o->labelsize( 10 );
o->hide();
o->label( "Spatialization" );
}
{ Controller_Module *o = spatialization_controller = new Controller_Module( true );
o->hide();
o->label( 0 );
o->pad( false );
o->size( 92,92 );
}
{ Fl_Box *o = spatialization_label = new Fl_Box( 0, 0, 100, 12 );
o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) );
o->labelsize( 10 );
o->hide();
o->label( "Spatialization" );
}
{ Controller_Module *o = spatialization_controller = new Controller_Module( true );
o->hide();
o->label( 0 );
o->pad( false );
o->size( 92,92 );
}
/* o->end(); */
/* } */
{ Fl_Menu_Button *o = output_connection_button = new Fl_Menu_Button( 0, 0, 10, 18 );
o->labelsize( 9 );
o->add("- auto -");
o->add("- manual -");
o->align( FL_ALIGN_CLIP );
o->labelcolor( FL_YELLOW );
o->callback( cb_handle, this );
o->hide();
}
o->end();
}
@ -718,9 +750,9 @@ Mixer_Strip::update_group_choice ( void )
void
Mixer_Strip::draw ( void )
{
/* don't bother drawing anything else, all we're doing is drawing the focus. */
/* don't bother drawing anything else, all we're doing is drawing the focus. */
// if ( damage() & ~FL_DAMAGE_USER1 )
Fl_Group::draw();
Fl_Group::draw();
if ( Fl::focus() == this )
draw_focus_frame( x(),y(),w(),h(), Fl_Group::selection_color() );
@ -833,6 +865,23 @@ Mixer_Strip::menu_cb ( const Fl_Menu_ *m )
if ( Fl::event_shift() || 1 == fl_choice( "Are you sure you want to remove this strip?\n\n(this action cannot be undone)", "Cancel", "Remove", NULL ) )
command_close();
}
else if ( ! strcmp( picked, "Auto Output/On" ) )
{
manual_connection( false );
}
else if ( ! strcmp( picked, "Auto Output/Off" ) )
{
manual_connection( true );
}
else if ( ! strncmp( picked, "Auto Input/", strlen( "Auto Input/" ) ))
{
const char *s = index( picked, '/' ) + 1;
if ( ! strcmp( s, "Off" ) )
auto_input( NULL );
else
auto_input( s );
}
}
void
@ -841,6 +890,153 @@ Mixer_Strip::menu_cb ( Fl_Widget *w, void *v )
((Mixer_Strip*)v)->menu_cb( (Fl_Menu_*) w );
}
void
Mixer_Strip::auto_input ( const char *s )
{
if ( _auto_input )
free( _auto_input );
_auto_input = NULL;
if ( s )
_auto_input = strdup( s );
mixer->auto_connect();
}
void
Mixer_Strip::manual_connection ( bool b )
{
_manual_connection = b;
output_connection_button->value(b);
if ( chain() )
{
if ( b )
chain()->auto_disconnect_outputs();
else
chain()->auto_connect_outputs();
}
}
static bool matches_pattern ( const char *pattern, Module::Port *p )
{
char group_name[256];
char port_group[256];
if ( 2 == sscanf( pattern, "%[^/]/%[^\n]", group_name, port_group ))
{
if ( strcmp( group_name, "*" ) &&
strcmp( group_name, p->module()->chain()->strip()->group()->name() ))
{
/* group mismatch */
return false;
}
/* group matches... try port group */
if ( ! strcmp( port_group, "mains" ) )
{
if ( index( p->jack_port()->name(), '/' ) )
return false;
else
return true;
}
else
{
const char *pn = p->jack_port()->name();
const char *n = rindex( pn, '/' );
if ( n )
{
// *n = 0;
if ( ! strncmp( port_group, pn, ( n - 1 ) - pn ) )
return true;
else
return false;
}
else
return false;
}
}
return false;
}
#include "JACK_Module.H"
void
Mixer_Strip::auto_connect_outputs ( void )
{
chain()->auto_connect_outputs();
}
bool
Mixer_Strip::has_group_affinity ( void ) const
{
return _auto_input && strncmp( _auto_input, "*/", 2 );
}
bool
Mixer_Strip::maybe_auto_connect_output ( Module::Port *p )
{
if ( p->module()->chain()->strip()->_manual_connection )
return true;
if ( p->module()->chain()->strip() == this )
/* don't auto connect to self! */
return false;
if ( ! _auto_input )
{
if ( p->connected_port() && p->connected_port()->module()->chain()->strip() == this )
{
/* first break previous auto connection */
p->connected_port()->jack_port()->disconnect( p->jack_port()->jack_name() );
p->disconnect();
}
}
if ( _auto_input && matches_pattern( _auto_input, p ) )
{
DMESSAGE( "Auto connecting port" );
if ( p->connected_port() )
{
/* first break previous auto connection */
p->connected_port()->jack_port()->disconnect( p->jack_port()->jack_name() );
p->disconnect();
}
const char* jack_name = p->jack_port()->jack_name();
/* get port number */
const char *s = rindex( jack_name, '-' );
unsigned int n = atoi( s + 1 ) - 1;
/* FIXME: safe assumption? */
JACK_Module *m = (JACK_Module*)chain()->module(0);
if ( n < m->aux_audio_input.size() )
{
m->aux_audio_input[n].jack_port()->connect( jack_name );
/* make a note of the connection so we know to disconnected later */
m->aux_audio_input[n].connect_to( p );
}
if ( p->module()->is_default() )
{
/* only do this for mains */
p->module()->chain()->strip()->output_connection_button->copy_label( name() );
p->module()->chain()->strip()->output_connection_button->labelcolor( FL_FOREGROUND_COLOR );
}
return true;
}
return false;
}
/** build the context menu */
Fl_Menu_Button &
@ -854,29 +1050,37 @@ Mixer_Strip::menu ( void ) const
// int c = output.size();
Fl_Menu_Item menu[] =
{
{ "Width", 0, 0, 0, FL_SUBMENU },
{ "Narrow", 'n', 0, 0, FL_MENU_RADIO | ( ! width_button->value() ? FL_MENU_VALUE : 0 ) },
{ "Wide", 'w', 0, 0, FL_MENU_RADIO | ( width_button->value() ? FL_MENU_VALUE : 0 ) },
{ 0 },
{ "View", 0, 0, 0, FL_SUBMENU },
{ "Fader", 'f', 0, 0, FL_MENU_RADIO | ( 0 == tab_button->value() ? FL_MENU_VALUE : 0 ) },
{ "Signal", 's', 0, 0, FL_MENU_RADIO | ( 1 == tab_button->value() ? FL_MENU_VALUE : 0 ) },
{ 0 },
{ "Move Left", '[', 0, 0 },
{ "Move Right", ']', 0, 0 },
{ "Color", 0, 0, 0 },
{ "Copy", FL_CTRL + 'c', 0, 0 },
{ "Export Strip", 0, 0, 0 },
{ "Rename", FL_CTRL + 'n', 0, 0 },
{ "Remove", FL_Delete, 0, 0 },
{ 0 },
};
m.clear();
menu_set_callback( menu, &Mixer_Strip::menu_cb, (void*)this );
std::list<std::string> sl = mixer->get_auto_connect_targets();
m.copy( menu, (void*)this );
m.add( "Auto Output/On", 0, 0, 0, FL_MENU_RADIO | ( _manual_connection ? 0 : FL_MENU_VALUE ) );
m.add( "Auto Output/Off", 0, 0, 0, FL_MENU_RADIO | ( ! _manual_connection ? 0 : FL_MENU_VALUE ) );
m.add( "Auto Input/Off", 0, 0, 0, FL_MENU_DIVIDER | FL_MENU_RADIO | ( _auto_input ? 0 : FL_MENU_VALUE ) );
for ( std::list<std::string>::iterator i = sl.begin(); i != sl.end(); i++ )
{
char *s;
asprintf( &s, "Auto Input/%s", i->c_str() );
m.add( s, 0,0,0, FL_MENU_RADIO | ( _auto_input && !strcmp( _auto_input, i->c_str() ) ? FL_MENU_VALUE : 0 ));
free(s );
}
m.add( "Width/Narrow", 'n', 0, 0, FL_MENU_RADIO | ( ! width_button->value() ? FL_MENU_VALUE : 0 ));
m.add( "Width/Wide", 'w', 0, 0, FL_MENU_RADIO | ( width_button->value() ? FL_MENU_VALUE : 0 ) );
m.add( "View/Fader", 'f', 0, 0, FL_MENU_RADIO | ( 0 == tab_button->value() ? FL_MENU_VALUE : 0 ) );
m.add( "View/Signal", 's', 0, 0, FL_MENU_RADIO | ( 1 == tab_button->value() ? FL_MENU_VALUE : 0 ) );
m.add( "Move Left", '[', 0, 0 );
m.add( "Move Right", ']', 0, 0 );
m.add( "Color", 0, 0, 0 );
m.add( "Copy", FL_CTRL + 'c', 0, 0 );
m.add( "Export Strip", 0, 0, 0 );
m.add( "Rename", FL_CTRL + 'n', 0, 0 );
m.add( "Remove", FL_Delete, 0, 0 );
menu_set_callback( const_cast<Fl_Menu_Item*>(m.menu()), &Mixer_Strip::menu_cb, (void*)this );
return m;
}
@ -887,6 +1091,12 @@ Mixer_Strip::spatializer ( void )
return spatialization_controller;
}
void
Mixer_Strip::get_output_ports ( std::list<std::string> &ports )
{
_chain->get_output_ports(ports);
}
int
Mixer_Strip::handle ( int m )
{
@ -1003,8 +1213,8 @@ Mixer_Strip::command_move_right ( void )
void
Mixer_Strip::command_close ( void )
{
mixer->remove( this );
Fl::delete_widget( this );
mixer->remove( this );
Fl::delete_widget( this );
}
void

View File

@ -50,6 +50,8 @@ class Fl_Menu_Button;
class Fl_Choice;
class Group;
#include "Module.H"
class Mixer_Strip : public Fl_Group, public Loggable {
public:
@ -85,6 +87,9 @@ public:
private:
char *_auto_input;
void auto_input ( const char *s );
unsigned int _dsp_load_index;
/* used to defer setting the mode of the gain controller until the
@ -92,7 +97,9 @@ private:
module */
int _gain_controller_mode;
int _mute_controller_mode;
bool _manual_connection;
Fl_Menu_Button *output_connection_button;
Fl_Flip_Button *width_button;
Fl_Flip_Button *tab_button;
Fl_Button *close_button;
@ -151,6 +158,13 @@ protected:
public:
void manual_connection ( bool b );
bool has_group_affinity ( void ) const;
void auto_connect_outputs ( void );
bool maybe_auto_connect_output ( Module::Port *p );
void get_output_ports ( std::list<std::string> &ports );
void update_group_choice ( void );
Controller_Module *spatializer ( void );

View File

@ -1110,6 +1110,32 @@ Module::thaw_ports ( void )
aux_audio_output[i].jack_port()->client( chain()->client() );
aux_audio_output[i].jack_port()->trackname( trackname );
aux_audio_output[i].jack_port()->thaw();
mixer->maybe_auto_connect_output( &aux_audio_output[i] );
}
}
void
Module::auto_connect_outputs ( void )
{
for ( unsigned int i = 0; i < aux_audio_output.size(); ++i )
{
mixer->maybe_auto_connect_output( &aux_audio_output[i] );
}
}
void
Module::auto_disconnect_outputs ( void )
{
for ( unsigned int i = 0; i < aux_audio_output.size(); ++i )
{
Module::Port *p = &aux_audio_output[i];
if ( p->connected_port() )
{
p->connected_port()->jack_port()->disconnect( p->jack_port()->jack_name() );
p->disconnect();
}
}
}
@ -1233,7 +1259,12 @@ Module::add_aux_port ( bool input, const char *prefix, int i )
bool
Module::add_aux_audio_output( const char *prefix, int i )
{
return add_aux_port ( false, prefix, i );
bool r = add_aux_port ( false, prefix, i );
if ( r )
mixer->maybe_auto_connect_output( &aux_audio_output.back() );
return r;
}
bool
@ -1243,8 +1274,6 @@ Module::add_aux_audio_input( const char *prefix, int i )
}
/************/
/* Commands */
/************/

View File

@ -493,6 +493,9 @@ protected:
public:
void auto_connect_outputs();
void auto_disconnect_outputs();
void freeze_ports ( void );
void thaw_ports ( void );