diff --git a/lib/ntk b/lib/ntk index 9884cca..5719b00 160000 --- a/lib/ntk +++ b/lib/ntk @@ -1 +1 @@ -Subproject commit 9884cca97d2dc7a34e730b942465b0bb808c6ccb +Subproject commit 5719b0044d9f267de5391fab006370cc7f4e70bd diff --git a/mixer/src/Chain.C b/mixer/src/Chain.C index b899d61..d0b173a 100644 --- a/mixer/src/Chain.C +++ b/mixer/src/Chain.C @@ -383,7 +383,7 @@ Chain::configure_ports ( void ) for ( unsigned int i = 0; i < req_buffers; ++i ) { Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO ); - p.connect_to( buffer_alloc( client()->nframes() ) ); + p.set_buffer( buffer_alloc( client()->nframes() ) ); buffer_fill_with_silence( (sample_t*)p.buffer(), client()->nframes() ); scratch_port.push_back( p ); } @@ -749,11 +749,11 @@ Chain::build_process_queue ( void ) for ( unsigned int j = 0; j < m->audio_input.size(); ++j ) { - m->audio_input[j].connect_to( &scratch_port[j] ); + m->audio_input[j].set_buffer( scratch_port[j].buffer() ); } for ( unsigned int j = 0; j < m->audio_output.size(); ++j ) { - m->audio_output[j].connect_to( &scratch_port[j] ); + m->audio_output[j].set_buffer( scratch_port[j].buffer() ); } m->handle_port_connection_change(); diff --git a/mixer/src/JACK_Module.C b/mixer/src/JACK_Module.C index 756fd67..714bee6 100644 --- a/mixer/src/JACK_Module.C +++ b/mixer/src/JACK_Module.C @@ -51,6 +51,8 @@ extern char *instance_name; static JACK_Module *receptive_to_drop = NULL; +const int MAX_PORTS = 16; + JACK_Module::JACK_Module ( bool log ) : Module ( 25, 25, name() ) { @@ -70,8 +72,8 @@ JACK_Module::JACK_Module ( bool log ) { Port p( this, Port::INPUT, Port::CONTROL, "Inputs" ); p.hints.type = Port::Hints::INTEGER; - p.hints.minimum = 0; - p.hints.maximum = 16; + p.hints.minimum = 1; + p.hints.maximum = MAX_PORTS; p.hints.ranged = true; p.hints.visible = false; @@ -84,8 +86,8 @@ JACK_Module::JACK_Module ( bool log ) { Port p( this, Port::INPUT, Port::CONTROL, "Outputs" ); p.hints.type = Port::Hints::INTEGER; - p.hints.minimum = 0; - p.hints.maximum = 16; + p.hints.minimum = 1; + p.hints.maximum = MAX_PORTS; p.hints.ranged = true; p.hints.visible = false; @@ -226,8 +228,7 @@ get_connections_for_ports ( std::vector ports ) free( strip_name ); strip_name = s; } - else - if ( 2 == sscanf( *c, "Non-Mixer.%a[^:(] (%a[^:)]):", &client_id, &strip_name ) ) + else if ( 2 == sscanf( *c, "Non-Mixer.%a[^:(] (%a[^:)]):", &client_id, &strip_name ) ) { free( client_id ); char *s = NULL; @@ -235,8 +236,7 @@ get_connections_for_ports ( std::vector ports ) free( strip_name ); strip_name = s; } - else - if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) ) + else if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) ) { free( client_id ); char *s = NULL; @@ -244,8 +244,7 @@ get_connections_for_ports ( std::vector ports ) free( strip_name ); strip_name = s; } - else - if ( 2 == sscanf( *c, "Non-DAW.%a[^:/]:%a[^/]/", &client_id, &strip_name ) ) + else if ( 2 == sscanf( *c, "Non-DAW.%a[^:/]:%a[^/]/", &client_id, &strip_name ) ) { free( client_id ); char *s = NULL; @@ -379,11 +378,11 @@ JACK_Module::configure_inputs ( int n ) { if ( n > 0 ) { - if ( is_default() ) - control_input[0].hints.minimum = 1; - output_connection_handle->show(); } + + if ( n < 1 || n > MAX_PORTS ) + return false; int on = audio_input.size(); @@ -396,6 +395,8 @@ JACK_Module::configure_inputs ( int n ) add_port( Port( this, Port::INPUT, Port::AUDIO ) ); } } + + mixer->maybe_auto_connect_output(&aux_audio_output.back()); } else { @@ -403,9 +404,11 @@ JACK_Module::configure_inputs ( int n ) { audio_input.back().disconnect(); audio_input.pop_back(); + aux_audio_output.back().disconnect(); aux_audio_output.back().jack_port()->shutdown(); delete aux_audio_output.back().jack_port(); aux_audio_output.pop_back(); + } } @@ -422,7 +425,10 @@ bool JACK_Module::configure_outputs ( int n ) { int on = audio_output.size(); - + + if ( n > MAX_PORTS ) + return false; + if ( n > 0 ) { input_connection_handle->show(); @@ -437,6 +443,8 @@ JACK_Module::configure_outputs ( int n ) add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); } } + + mixer->auto_connect(); } else { @@ -444,6 +452,7 @@ JACK_Module::configure_outputs ( int n ) { audio_output.back().disconnect(); audio_output.pop_back(); + aux_audio_input.back().disconnect(); aux_audio_input.back().jack_port()->shutdown(); delete aux_audio_input.back().jack_port(); aux_audio_input.pop_back(); @@ -458,9 +467,11 @@ JACK_Module::configure_outputs ( int n ) dec_button->show(); inc_button->show(); } + return true; } + bool JACK_Module::initialize ( void ) { @@ -703,7 +714,7 @@ JACK_Module::process ( nframes_t nframes ) buffer_copy( (sample_t*)aux_audio_output[i].jack_port()->buffer(nframes), (sample_t*)audio_input[i].buffer(), nframes ); - } + } } diff --git a/mixer/src/Mixer.C b/mixer/src/Mixer.C index 9ec7adc..3c03bee 100644 --- a/mixer/src/Mixer.C +++ b/mixer/src/Mixer.C @@ -1148,6 +1148,12 @@ Mixer::get_auto_connect_targets ( void ) void Mixer::auto_connect ( void ) { + if ( Project::is_opening() ) + /* it's more efficient to do this once at the end rather than as we go. */ + return; + + DMESSAGE("Full auto-connect cycle" ); + /* give strips with group affinity the first shot */ for ( int i = 0; i < mixer_strips->children(); i++ ) { @@ -1170,6 +1176,12 @@ Mixer::auto_connect ( void ) void Mixer::maybe_auto_connect_output ( Module::Port *p ) { + if ( Project::is_opening() ) + /* it's more efficient to do this once at the end rather than as we go. */ + return; + +// DMESSAGE( "Single output auto connect cycle" ); + /* give strips with group affinity the first shot */ for ( int i = 0; i < mixer_strips->children(); i++ ) { diff --git a/mixer/src/Mixer_Strip.C b/mixer/src/Mixer_Strip.C index 9ab687d..88dffa0 100644 --- a/mixer/src/Mixer_Strip.C +++ b/mixer/src/Mixer_Strip.C @@ -996,7 +996,6 @@ Mixer_Strip::has_group_affinity ( void ) const return _auto_input && strncmp( _auto_input, "*/", 2 ); } - bool Mixer_Strip::maybe_auto_connect_output ( Module::Port *p ) { @@ -1009,25 +1008,16 @@ Mixer_Strip::maybe_auto_connect_output ( Module::Port *p ) 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(); - } + /* break any previous connection between this port and this module */ + p->disconnect_from_strip(this); } 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(); - } - + /* break any prior auto-connection */ + p->disconnect(); + + // FIXME: Find a better way to get the port index. const char* jack_name = p->jack_port()->jack_name(); /* get port number */ @@ -1036,14 +1026,21 @@ Mixer_Strip::maybe_auto_connect_output ( Module::Port *p ) /* FIXME: safe assumption? */ JACK_Module *m = (JACK_Module*)chain()->module(0); + + if ( !m ) + { + /* no jack module in the chian... may be in the process of adding the JACK module to the chain... i.e in log replay when loading a project. */ + return false; + } - if ( n < m->aux_audio_input.size() ) + 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 ); +// DMESSAGE( "No port to connect to at this index"); + return false; } + m->aux_audio_input[n].connect_to( p ); + if ( p->module()->is_default() ) { /* only do this for mains */ diff --git a/mixer/src/Module.C b/mixer/src/Module.C index 853ab17..2939412 100644 --- a/mixer/src/Module.C +++ b/mixer/src/Module.C @@ -86,6 +86,18 @@ Module::~Module ( ) audio_input[i].disconnect(); for ( unsigned int i = 0; i < audio_output.size(); ++i ) audio_output[i].disconnect(); + for ( unsigned int i = 0; i < aux_audio_input.size(); ++i ) + { + aux_audio_input[i].disconnect(); + aux_audio_input[i].jack_port()->shutdown(); + delete aux_audio_input[i].jack_port(); + } + for ( unsigned int i = 0; i < aux_audio_output.size(); ++i ) + { + aux_audio_output[i].disconnect(); + aux_audio_output[i].jack_port()->shutdown(); + delete aux_audio_output[i].jack_port(); + } for ( unsigned int i = 0; i < control_input.size(); ++i ) { /* destroy connected Controller_Module */ @@ -129,6 +141,15 @@ Module::~Module ( ) void Module::init ( void ) { + + /* we use pointers to these vector elements for port auto connection stuff and need to prevent reallocation from invalidating them. */ + audio_input.reserve(16); + audio_output.reserve(16); + control_input.reserve(16); + control_output.reserve(16); + aux_audio_input.reserve(16); + aux_audio_output.reserve(16); + // _latency = 0; _is_default = false; _editor = 0; @@ -245,6 +266,23 @@ Module::paste_before ( void ) +void +Module::Port::disconnect_from_strip ( Mixer_Strip *o ) +{ + for ( std::list::iterator i = _connected.begin(); i != _connected.end(); i++ ) + { + Port *p = *i; + + if ( p->module()->chain()->strip() == o ) + { + /* iterator about to be invalidated... */ + i = _connected.erase(i); + + disconnect(p); + } + } +} + const char * Module::Port::osc_number_path ( void ) { diff --git a/mixer/src/Module.H b/mixer/src/Module.H index 4ed9349..8eebc82 100644 --- a/mixer/src/Module.H +++ b/mixer/src/Module.H @@ -31,12 +31,15 @@ #include "Loggable.H" #include "JACK/Port.H" #include "OSC/Endpoint.H" +#include +#include class Chain; class Module_Parameter_Editor; class Fl_Menu_; class Fl_Menu_Button; class Fl_Button; +class Mixer_Strip; class Module : public Fl_Group, public Loggable { @@ -86,6 +89,11 @@ public: class Port { + + /* support multiple connection for audio ports (many to one, one to many, many to many). + control ports only support one to one and one to many. */ + + /* char *type_names[] = { "Audio", "Control" }; */ /* char *direction_names[] = { "Input", "Output" }; */ @@ -139,7 +147,6 @@ public: _type = type; _buf = 0; _nframes = 0; - _connected = 0; _module = module; _scaled_signal = 0; _unscaled_signal = 0; @@ -155,7 +162,6 @@ public: _type = p._type; _buf = p._buf; _nframes = p._nframes; - _connected = p._connected; _module = p._module; hints = p.hints; _scaled_signal = p._scaled_signal; @@ -170,9 +176,6 @@ public: if ( _by_number_path ) free( _by_number_path ); _by_number_path = NULL; - - // change_osc_path( NULL ); - // disconnect(); } const char *name ( void ) const { return _name; } @@ -242,44 +245,94 @@ public: return 0.0f; } - bool connected ( void ) const { return _connected; } + bool connected ( void ) const { + if ( _type == Port::AUDIO ) + /* internal audio ports are considered connected by the buffer setting */ + return _buf != 0; + else + /* control and external audio ports belong to a graph */ + return _connected.size() > 0; + } bool connected_osc ( void ) const; Port *connected_port ( void ) const { - return _connected; + ASSERT( _type == Port::CONTROL, "Operation only available for control ports" ); + return _connected.size() == 0 ? NULL : _connected.front(); } void connect_to ( Port *to ) { - _buf = to->_buf; - to->_connected = this; - _connected = to; + if ( _type != Port::AUX_AUDIO ) + { + _buf = to->_buf; + } + + if ( jack_port() && to->jack_port() ) + jack_port()->connect( to->jack_port()->jack_name() ); + + if ( std::find(_connected.begin(),_connected.end(),to) == _connected.end() ) + { + _connected.push_back(to); + to->_connected.push_back(this); + } } + + /* disconnect this port from any ports of modules belonging to strip /o/ */ + void disconnect_from_strip ( Mixer_Strip *o ); + void connect_to ( void *buf ) { + ASSERT( _type == Port::CONTROL, "Operation only available for control ports" ); _buf = buf; update_connected_port_buffer(); } + void set_buffer ( void *buf ) + { + ASSERT( _type != Port::CONTROL, "Operation only available for audio ports" ); + _buf = buf; + } + void send_feedback ( void ); + bool connected_to ( Port *p ) + { + return std::find( _connected.begin(), _connected.end(), p ) != _connected.end(); + } + + /* disconnect from specified port */ + void disconnect ( Port *p ) + { + if ( ! connected_to(p) ) + return; + + if ( _type == Port::CONTROL && p->_module ) + p->_module->handle_control_disconnect( this ); + + if ( jack_port() && p->jack_port() ) + jack_port()->disconnect( p->jack_port()->jack_name() ); + + _connected.remove(p); + p->_connected.remove(this); + } + + /* disconnect from *all* connected ports */ void disconnect ( void ) { - if ( _connected && _connected != (void*)0x01 ) + if ( _connected.size() ) { - _connected->_connected = NULL; + for ( std::list::iterator i = _connected.begin(); i != _connected.end(); i++ ) + { + Port *p = *i; - if ( _connected->_module ) - _connected->_module->handle_control_disconnect( this ); - - _connected = NULL; + /* iterator about to be invalidated... */ + i = _connected.erase(i); + + disconnect(p); + } } - else - _connected = NULL; - - /* FIXME: do something! */ } void jack_port ( JACK::Port *v ) { _jack_port = v; } @@ -289,8 +342,8 @@ public: char *generate_osc_path ( void ); void change_osc_path ( char *path ); - - Port *_connected; + + std::list _connected; Type _type; Direction _direction; const char *_name; diff --git a/mixer/src/Project.C b/mixer/src/Project.C index 2ba92cf..4a061a0 100644 --- a/mixer/src/Project.C +++ b/mixer/src/Project.C @@ -58,6 +58,7 @@ char Project::_name[256]; char Project::_created_on[40]; char Project::_path[512]; bool Project::_is_open = false; +bool Project::_is_opening = false; int Project::_lockfd = 0; @@ -261,6 +262,8 @@ Project::open ( const char *name ) if ( version != PROJECT_VERSION ) return E_VERSION; + _is_opening = true; + if ( ! Loggable::replay( "snapshot" ) ) return E_INVALID; @@ -279,6 +282,7 @@ Project::open ( const char *name ) _is_open = true; + _is_opening = false; // tle->load_timeline_settings(); // timeline->zoom_fit(); diff --git a/mixer/src/Project.H b/mixer/src/Project.H index 7de2efb..42328dc 100644 --- a/mixer/src/Project.H +++ b/mixer/src/Project.H @@ -27,6 +27,7 @@ class Project static int _lockfd; static bool _is_open; + static bool _is_opening; static char _name[256]; static char _path[512]; static char _created_on[40]; @@ -61,4 +62,5 @@ public: static const char *path ( void ) { return _path; } static const char *created_on ( void ) { return _created_on; } + static const bool is_opening ( void ) { return _is_opening; } }; diff --git a/nonlib/JACK/Port.C b/nonlib/JACK/Port.C index d8bc52f..e60e6d5 100644 --- a/nonlib/JACK/Port.C +++ b/nonlib/JACK/Port.C @@ -380,12 +380,20 @@ namespace JACK { const char *name = jack_port_name( _port ); + /* jack complains when you attempt to connect an already connected port... */ + if ( connected_to( to ) ) + return 0; + if ( _direction == Output ) { + DMESSAGE("Connecting jack port %s to %s", name, to ); + return jack_connect( _client->jack_client(), name, to ); } else { + DMESSAGE("Connecting jack port %s to %s", to, name ); + return jack_connect( _client->jack_client(), to, name ); } } @@ -397,11 +405,15 @@ namespace JACK const char *name = jack_port_name( _port ); if ( _direction == Output ) - { + { + DMESSAGE("Disconnecting jack port %s from %s", name, from ); + return jack_disconnect( _client->jack_client(), name, from ); } else { + DMESSAGE("Disconnecting jack port %s from %s", from, name ); + return jack_disconnect( _client->jack_client(), from, name ); } }