| @@ -1 +1 @@ | |||
| Subproject commit 9884cca97d2dc7a34e730b942465b0bb808c6ccb | |||
| Subproject commit 5719b0044d9f267de5391fab006370cc7f4e70bd | |||
| @@ -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(); | |||
| @@ -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<Module::Port> 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<Module::Port> 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<Module::Port> 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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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++ ) | |||
| { | |||
| @@ -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 */ | |||
| @@ -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<Port*>::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 ) | |||
| { | |||
| @@ -31,12 +31,15 @@ | |||
| #include "Loggable.H" | |||
| #include "JACK/Port.H" | |||
| #include "OSC/Endpoint.H" | |||
| #include <list> | |||
| #include <algorithm> | |||
| 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 ); | |||
| void disconnect ( void ) | |||
| bool connected_to ( Port *p ) | |||
| { | |||
| if ( _connected && _connected != (void*)0x01 ) | |||
| { | |||
| _connected->_connected = NULL; | |||
| return std::find( _connected.begin(), _connected.end(), p ) != _connected.end(); | |||
| } | |||
| /* disconnect from specified port */ | |||
| void disconnect ( Port *p ) | |||
| { | |||
| if ( ! connected_to(p) ) | |||
| return; | |||
| if ( _connected->_module ) | |||
| _connected->_module->handle_control_disconnect( this ); | |||
| 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 = NULL; | |||
| _connected.remove(p); | |||
| p->_connected.remove(this); | |||
| } | |||
| /* disconnect from *all* connected ports */ | |||
| void disconnect ( void ) | |||
| { | |||
| if ( _connected.size() ) | |||
| { | |||
| for ( std::list<Port*>::iterator i = _connected.begin(); i != _connected.end(); i++ ) | |||
| { | |||
| Port *p = *i; | |||
| /* 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 <Port*> _connected; | |||
| Type _type; | |||
| Direction _direction; | |||
| const char *_name; | |||
| @@ -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(); | |||
| @@ -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; } | |||
| }; | |||
| @@ -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 ); | |||
| } | |||
| } | |||