From fc5c59c61d0fbcc9ab70cec41afaa5103def379d Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Wed, 4 Sep 2013 01:00:29 -0700 Subject: [PATCH] Mixer: Implement port autoconnection system. --- lib/ntk | 2 +- mixer/src/Chain.C | 41 ++++++ mixer/src/Chain.H | 5 + mixer/src/JACK_Module.C | 39 ++++- mixer/src/Mixer.C | 85 +++++++++++ mixer/src/Mixer.H | 5 +- mixer/src/Mixer_Strip.C | 314 +++++++++++++++++++++++++++++++++------- mixer/src/Mixer_Strip.H | 14 ++ mixer/src/Module.C | 35 ++++- mixer/src/Module.H | 3 + 10 files changed, 483 insertions(+), 60 deletions(-) diff --git a/lib/ntk b/lib/ntk index 6455799..d7f9416 160000 --- a/lib/ntk +++ b/lib/ntk @@ -1 +1 @@ -Subproject commit 645579913d0bfbfdcc9cd0107dd55bd121a99990 +Subproject commit d7f94164bc2d44b7ddf39e19e90a1fc172e62c7d diff --git a/mixer/src/Chain.C b/mixer/src/Chain.C index 6d92802..0b5eca5 100644 --- a/mixer/src/Chain.C +++ b/mixer/src/Chain.C @@ -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 &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(); + } +} + + /*****************/ diff --git a/mixer/src/Chain.H b/mixer/src/Chain.H index c478b5d..51e77fe 100644 --- a/mixer/src/Chain.H +++ b/mixer/src/Chain.H @@ -77,6 +77,11 @@ protected: public: + void auto_connect_outputs ( void ); + void auto_disconnect_outputs ( void ); + + void get_output_ports ( std::list &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 ); diff --git a/mixer/src/JACK_Module.C b/mixer/src/JACK_Module.C index b37e931..59561a9 100644 --- a/mixer/src/JACK_Module.C +++ b/mixer/src/JACK_Module.C @@ -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); diff --git a/mixer/src/Mixer.C b/mixer/src/Mixer.C index 16216b5..2cb0b10 100644 --- a/mixer/src/Mixer.C +++ b/mixer/src/Mixer.C @@ -1101,6 +1101,89 @@ Mixer::handle ( int m ) return 0; } +#include + +std::list +Mixer::get_auto_connect_targets ( void ) +{ + std::list sl; + std::list rl; + + for ( int i = mixer_strips->children(); i--; ) + { + ((Mixer_Strip*)mixer_strips->child(i))->get_output_ports(sl); + } + + for ( std::list::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; diff --git a/mixer/src/Mixer.H b/mixer/src/Mixer.H index c6c8bf3..845f22a 100644 --- a/mixer/src/Mixer.H +++ b/mixer/src/Mixer.H @@ -91,7 +91,10 @@ private: public: - + + void auto_connect ( void ); + void maybe_auto_connect_output ( Module::Port *p ); + std::list get_auto_connect_targets ( void ); Group * group_by_name ( const char * name ); char *get_unique_group_name ( const char *name ); diff --git a/mixer/src/Mixer_Strip.C b/mixer/src/Mixer_Strip.C index e98e095..dd003be 100644 --- a/mixer/src/Mixer_Strip.C +++ b/mixer/src/Mixer_Strip.C @@ -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 & @@ -853,30 +1049,38 @@ Mixer_Strip::menu ( void ) const m.label( label ); // int c = output.size(); + + m.clear(); - 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 }, - }; + std::list sl = mixer->get_auto_connect_targets(); - menu_set_callback( menu, &Mixer_Strip::menu_cb, (void*)this ); - 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::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(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 &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 diff --git a/mixer/src/Mixer_Strip.H b/mixer/src/Mixer_Strip.H index fae6b22..df30a1b 100644 --- a/mixer/src/Mixer_Strip.H +++ b/mixer/src/Mixer_Strip.H @@ -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 &ports ); + void update_group_choice ( void ); Controller_Module *spatializer ( void ); diff --git a/mixer/src/Module.C b/mixer/src/Module.C index 602f119..1595fc7 100644 --- a/mixer/src/Module.C +++ b/mixer/src/Module.C @@ -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 */ /************/ diff --git a/mixer/src/Module.H b/mixer/src/Module.H index 96ea4b4..99281c9 100644 --- a/mixer/src/Module.H +++ b/mixer/src/Module.H @@ -493,6 +493,9 @@ protected: public: + void auto_connect_outputs(); + void auto_disconnect_outputs(); + void freeze_ports ( void ); void thaw_ports ( void );