Mixer: Fix issues with port auto-connection

Closes #188
This commit is contained in:
Jonathan Moore Liles 2016-03-02 21:13:17 -08:00
parent 1ef382fbbe
commit 3946d39221
10 changed files with 191 additions and 62 deletions

@ -1 +1 @@
Subproject commit 9884cca97d2dc7a34e730b942465b0bb808c6ccb Subproject commit 5719b0044d9f267de5391fab006370cc7f4e70bd

View File

@ -383,7 +383,7 @@ Chain::configure_ports ( void )
for ( unsigned int i = 0; i < req_buffers; ++i ) for ( unsigned int i = 0; i < req_buffers; ++i )
{ {
Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO ); 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() ); buffer_fill_with_silence( (sample_t*)p.buffer(), client()->nframes() );
scratch_port.push_back( p ); 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 ) 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 ) 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(); m->handle_port_connection_change();

View File

@ -51,6 +51,8 @@ extern char *instance_name;
static JACK_Module *receptive_to_drop = NULL; static JACK_Module *receptive_to_drop = NULL;
const int MAX_PORTS = 16;
JACK_Module::JACK_Module ( bool log ) JACK_Module::JACK_Module ( bool log )
: Module ( 25, 25, name() ) : Module ( 25, 25, name() )
{ {
@ -70,8 +72,8 @@ JACK_Module::JACK_Module ( bool log )
{ {
Port p( this, Port::INPUT, Port::CONTROL, "Inputs" ); Port p( this, Port::INPUT, Port::CONTROL, "Inputs" );
p.hints.type = Port::Hints::INTEGER; p.hints.type = Port::Hints::INTEGER;
p.hints.minimum = 0; p.hints.minimum = 1;
p.hints.maximum = 16; p.hints.maximum = MAX_PORTS;
p.hints.ranged = true; p.hints.ranged = true;
p.hints.visible = false; p.hints.visible = false;
@ -84,8 +86,8 @@ JACK_Module::JACK_Module ( bool log )
{ {
Port p( this, Port::INPUT, Port::CONTROL, "Outputs" ); Port p( this, Port::INPUT, Port::CONTROL, "Outputs" );
p.hints.type = Port::Hints::INTEGER; p.hints.type = Port::Hints::INTEGER;
p.hints.minimum = 0; p.hints.minimum = 1;
p.hints.maximum = 16; p.hints.maximum = MAX_PORTS;
p.hints.ranged = true; p.hints.ranged = true;
p.hints.visible = false; p.hints.visible = false;
@ -226,8 +228,7 @@ get_connections_for_ports ( std::vector<Module::Port> ports )
free( strip_name ); free( strip_name );
strip_name = s; strip_name = s;
} }
else else if ( 2 == sscanf( *c, "Non-Mixer.%a[^:(] (%a[^:)]):", &client_id, &strip_name ) )
if ( 2 == sscanf( *c, "Non-Mixer.%a[^:(] (%a[^:)]):", &client_id, &strip_name ) )
{ {
free( client_id ); free( client_id );
char *s = NULL; char *s = NULL;
@ -235,8 +236,7 @@ get_connections_for_ports ( std::vector<Module::Port> ports )
free( strip_name ); free( strip_name );
strip_name = s; strip_name = s;
} }
else else if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) )
if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) )
{ {
free( client_id ); free( client_id );
char *s = NULL; char *s = NULL;
@ -244,8 +244,7 @@ get_connections_for_ports ( std::vector<Module::Port> ports )
free( strip_name ); free( strip_name );
strip_name = s; strip_name = s;
} }
else else if ( 2 == sscanf( *c, "Non-DAW.%a[^:/]:%a[^/]/", &client_id, &strip_name ) )
if ( 2 == sscanf( *c, "Non-DAW.%a[^:/]:%a[^/]/", &client_id, &strip_name ) )
{ {
free( client_id ); free( client_id );
char *s = NULL; char *s = NULL;
@ -379,12 +378,12 @@ JACK_Module::configure_inputs ( int n )
{ {
if ( n > 0 ) if ( n > 0 )
{ {
if ( is_default() )
control_input[0].hints.minimum = 1;
output_connection_handle->show(); output_connection_handle->show();
} }
if ( n < 1 || n > MAX_PORTS )
return false;
int on = audio_input.size(); int on = audio_input.size();
if ( n > on ) if ( n > on )
@ -396,6 +395,8 @@ JACK_Module::configure_inputs ( int n )
add_port( Port( this, Port::INPUT, Port::AUDIO ) ); add_port( Port( this, Port::INPUT, Port::AUDIO ) );
} }
} }
mixer->maybe_auto_connect_output(&aux_audio_output.back());
} }
else else
{ {
@ -403,9 +404,11 @@ JACK_Module::configure_inputs ( int n )
{ {
audio_input.back().disconnect(); audio_input.back().disconnect();
audio_input.pop_back(); audio_input.pop_back();
aux_audio_output.back().disconnect();
aux_audio_output.back().jack_port()->shutdown(); aux_audio_output.back().jack_port()->shutdown();
delete aux_audio_output.back().jack_port(); delete aux_audio_output.back().jack_port();
aux_audio_output.pop_back(); aux_audio_output.pop_back();
} }
} }
@ -423,6 +426,9 @@ JACK_Module::configure_outputs ( int n )
{ {
int on = audio_output.size(); int on = audio_output.size();
if ( n > MAX_PORTS )
return false;
if ( n > 0 ) if ( n > 0 )
{ {
input_connection_handle->show(); input_connection_handle->show();
@ -437,6 +443,8 @@ JACK_Module::configure_outputs ( int n )
add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); add_port( Port( this, Port::OUTPUT, Port::AUDIO ) );
} }
} }
mixer->auto_connect();
} }
else else
{ {
@ -444,6 +452,7 @@ JACK_Module::configure_outputs ( int n )
{ {
audio_output.back().disconnect(); audio_output.back().disconnect();
audio_output.pop_back(); audio_output.pop_back();
aux_audio_input.back().disconnect();
aux_audio_input.back().jack_port()->shutdown(); aux_audio_input.back().jack_port()->shutdown();
delete aux_audio_input.back().jack_port(); delete aux_audio_input.back().jack_port();
aux_audio_input.pop_back(); aux_audio_input.pop_back();
@ -458,9 +467,11 @@ JACK_Module::configure_outputs ( int n )
dec_button->show(); dec_button->show();
inc_button->show(); inc_button->show();
} }
return true; return true;
} }
bool bool
JACK_Module::initialize ( void ) JACK_Module::initialize ( void )
{ {

View File

@ -1148,6 +1148,12 @@ Mixer::get_auto_connect_targets ( void )
void void
Mixer::auto_connect ( 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 */ /* give strips with group affinity the first shot */
for ( int i = 0; i < mixer_strips->children(); i++ ) for ( int i = 0; i < mixer_strips->children(); i++ )
{ {
@ -1170,6 +1176,12 @@ Mixer::auto_connect ( void )
void void
Mixer::maybe_auto_connect_output ( Module::Port *p ) 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 */ /* give strips with group affinity the first shot */
for ( int i = 0; i < mixer_strips->children(); i++ ) for ( int i = 0; i < mixer_strips->children(); i++ )
{ {

View File

@ -996,7 +996,6 @@ Mixer_Strip::has_group_affinity ( void ) const
return _auto_input && strncmp( _auto_input, "*/", 2 ); return _auto_input && strncmp( _auto_input, "*/", 2 );
} }
bool bool
Mixer_Strip::maybe_auto_connect_output ( Module::Port *p ) 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 ( ! _auto_input )
{ {
if ( p->connected_port() && p->connected_port()->module()->chain()->strip() == this ) /* break any previous connection between this port and this module */
{ p->disconnect_from_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 ) ) if ( _auto_input && matches_pattern( _auto_input, p ) )
{ {
DMESSAGE( "Auto connecting port" ); /* break any prior auto-connection */
if ( p->connected_port() )
{
/* first break previous auto connection */
p->connected_port()->jack_port()->disconnect( p->jack_port()->jack_name() );
p->disconnect(); p->disconnect();
}
// FIXME: Find a better way to get the port index.
const char* jack_name = p->jack_port()->jack_name(); const char* jack_name = p->jack_port()->jack_name();
/* get port number */ /* get port number */
@ -1037,13 +1027,20 @@ Mixer_Strip::maybe_auto_connect_output ( Module::Port *p )
/* FIXME: safe assumption? */ /* FIXME: safe assumption? */
JACK_Module *m = (JACK_Module*)chain()->module(0); JACK_Module *m = (JACK_Module*)chain()->module(0);
if ( n < m->aux_audio_input.size() ) if ( !m )
{ {
m->aux_audio_input[n].jack_port()->connect( jack_name ); /* 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. */
/* make a note of the connection so we know to disconnected later */ return false;
m->aux_audio_input[n].connect_to( p );
} }
if ( n >= m->aux_audio_input.size() )
{
// DMESSAGE( "No port to connect to at this index");
return false;
}
m->aux_audio_input[n].connect_to( p );
if ( p->module()->is_default() ) if ( p->module()->is_default() )
{ {
/* only do this for mains */ /* only do this for mains */

View File

@ -86,6 +86,18 @@ Module::~Module ( )
audio_input[i].disconnect(); audio_input[i].disconnect();
for ( unsigned int i = 0; i < audio_output.size(); ++i ) for ( unsigned int i = 0; i < audio_output.size(); ++i )
audio_output[i].disconnect(); 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 ) for ( unsigned int i = 0; i < control_input.size(); ++i )
{ {
/* destroy connected Controller_Module */ /* destroy connected Controller_Module */
@ -129,6 +141,15 @@ Module::~Module ( )
void void
Module::init ( 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; // _latency = 0;
_is_default = false; _is_default = false;
_editor = 0; _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 * const char *
Module::Port::osc_number_path ( void ) Module::Port::osc_number_path ( void )
{ {

View File

@ -31,12 +31,15 @@
#include "Loggable.H" #include "Loggable.H"
#include "JACK/Port.H" #include "JACK/Port.H"
#include "OSC/Endpoint.H" #include "OSC/Endpoint.H"
#include <list>
#include <algorithm>
class Chain; class Chain;
class Module_Parameter_Editor; class Module_Parameter_Editor;
class Fl_Menu_; class Fl_Menu_;
class Fl_Menu_Button; class Fl_Menu_Button;
class Fl_Button; class Fl_Button;
class Mixer_Strip;
class Module : public Fl_Group, public Loggable { class Module : public Fl_Group, public Loggable {
@ -86,6 +89,11 @@ public:
class Port 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 *type_names[] = { "Audio", "Control" }; */
/* char *direction_names[] = { "Input", "Output" }; */ /* char *direction_names[] = { "Input", "Output" }; */
@ -139,7 +147,6 @@ public:
_type = type; _type = type;
_buf = 0; _buf = 0;
_nframes = 0; _nframes = 0;
_connected = 0;
_module = module; _module = module;
_scaled_signal = 0; _scaled_signal = 0;
_unscaled_signal = 0; _unscaled_signal = 0;
@ -155,7 +162,6 @@ public:
_type = p._type; _type = p._type;
_buf = p._buf; _buf = p._buf;
_nframes = p._nframes; _nframes = p._nframes;
_connected = p._connected;
_module = p._module; _module = p._module;
hints = p.hints; hints = p.hints;
_scaled_signal = p._scaled_signal; _scaled_signal = p._scaled_signal;
@ -170,9 +176,6 @@ public:
if ( _by_number_path ) if ( _by_number_path )
free( _by_number_path ); free( _by_number_path );
_by_number_path = NULL; _by_number_path = NULL;
// change_osc_path( NULL );
// disconnect();
} }
const char *name ( void ) const { return _name; } const char *name ( void ) const { return _name; }
@ -242,44 +245,94 @@ public:
return 0.0f; 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; bool connected_osc ( void ) const;
Port *connected_port ( 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 ) void connect_to ( Port *to )
{
if ( _type != Port::AUX_AUDIO )
{ {
_buf = to->_buf; _buf = to->_buf;
to->_connected = this;
_connected = to;
} }
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 ) void connect_to ( void *buf )
{ {
ASSERT( _type == Port::CONTROL, "Operation only available for control ports" );
_buf = buf; _buf = buf;
update_connected_port_buffer(); 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 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 ) void disconnect ( void )
{ {
if ( _connected && _connected != (void*)0x01 ) if ( _connected.size() )
{ {
_connected->_connected = NULL; for ( std::list<Port*>::iterator i = _connected.begin(); i != _connected.end(); i++ )
{
Port *p = *i;
if ( _connected->_module ) /* iterator about to be invalidated... */
_connected->_module->handle_control_disconnect( this ); i = _connected.erase(i);
_connected = NULL; disconnect(p);
}
} }
else
_connected = NULL;
/* FIXME: do something! */
} }
void jack_port ( JACK::Port *v ) { _jack_port = v; } void jack_port ( JACK::Port *v ) { _jack_port = v; }
@ -290,7 +343,7 @@ public:
char *generate_osc_path ( void ); char *generate_osc_path ( void );
void change_osc_path ( char *path ); void change_osc_path ( char *path );
Port *_connected; std::list <Port*> _connected;
Type _type; Type _type;
Direction _direction; Direction _direction;
const char *_name; const char *_name;

View File

@ -58,6 +58,7 @@ char Project::_name[256];
char Project::_created_on[40]; char Project::_created_on[40];
char Project::_path[512]; char Project::_path[512];
bool Project::_is_open = false; bool Project::_is_open = false;
bool Project::_is_opening = false;
int Project::_lockfd = 0; int Project::_lockfd = 0;
@ -261,6 +262,8 @@ Project::open ( const char *name )
if ( version != PROJECT_VERSION ) if ( version != PROJECT_VERSION )
return E_VERSION; return E_VERSION;
_is_opening = true;
if ( ! Loggable::replay( "snapshot" ) ) if ( ! Loggable::replay( "snapshot" ) )
return E_INVALID; return E_INVALID;
@ -279,6 +282,7 @@ Project::open ( const char *name )
_is_open = true; _is_open = true;
_is_opening = false;
// tle->load_timeline_settings(); // tle->load_timeline_settings();
// timeline->zoom_fit(); // timeline->zoom_fit();

View File

@ -27,6 +27,7 @@ class Project
static int _lockfd; static int _lockfd;
static bool _is_open; static bool _is_open;
static bool _is_opening;
static char _name[256]; static char _name[256];
static char _path[512]; static char _path[512];
static char _created_on[40]; static char _created_on[40];
@ -61,4 +62,5 @@ public:
static const char *path ( void ) { return _path; } static const char *path ( void ) { return _path; }
static const char *created_on ( void ) { return _created_on; } static const char *created_on ( void ) { return _created_on; }
static const bool is_opening ( void ) { return _is_opening; }
}; };

View File

@ -380,12 +380,20 @@ namespace JACK
{ {
const char *name = jack_port_name( _port ); 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 ) if ( _direction == Output )
{ {
DMESSAGE("Connecting jack port %s to %s", name, to );
return jack_connect( _client->jack_client(), name, to ); return jack_connect( _client->jack_client(), name, to );
} }
else else
{ {
DMESSAGE("Connecting jack port %s to %s", to, name );
return jack_connect( _client->jack_client(), to, name ); return jack_connect( _client->jack_client(), to, name );
} }
} }
@ -398,10 +406,14 @@ namespace JACK
if ( _direction == Output ) if ( _direction == Output )
{ {
DMESSAGE("Disconnecting jack port %s from %s", name, from );
return jack_disconnect( _client->jack_client(), name, from ); return jack_disconnect( _client->jack_client(), name, from );
} }
else else
{ {
DMESSAGE("Disconnecting jack port %s from %s", from, name );
return jack_disconnect( _client->jack_client(), from, name ); return jack_disconnect( _client->jack_client(), from, name );
} }
} }