View Issue Details

IDProjectCategoryView StatusLast Update
0001537ardourfeaturespublic2008-11-21 00:04
Reportercth103 Assigned Tocth103  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Summary0001537: Allow section of other routes from the input and output menus on a mixer strip
DescriptionThe Input and Output menus on a mixer strip currently allow you either to set I/O to a session's connection, or to edit the routing using a dialogue box. I think it would be nice if you could select other routes from this menu, to avoid the need to use the dialogue box in some cases.
Additional InformationThe attached patch implements this (and throws in some comments for good measure).
TagsNo tags attached.

Activities

2007-03-07 14:30

 

routes-2 (11,694 bytes)   
Index: gtk2_ardour/mixer_strip.cc
===================================================================
--- gtk2_ardour/mixer_strip.cc	(revision 1560)
+++ gtk2_ardour/mixer_strip.cc	(working copy)
@@ -36,6 +36,7 @@
 #include <ardour/session.h>
 #include <ardour/audioengine.h>
 #include <ardour/route.h>
+#include <ardour/session_route.h>
 #include <ardour/audio_track.h>
 #include <ardour/audio_diskstream.h>
 #include <ardour/panner.h>
@@ -511,6 +512,7 @@
 		citems.push_back (SeparatorElem());
 		
 		_session.foreach_connection (this, &MixerStrip::add_connection_to_output_menu);
+		_session.foreach_route (this, &MixerStrip::add_route_to_output_menu);
 
 		output_menu.popup (1, ev->time);
 		break;
@@ -573,6 +575,7 @@
 		citems.push_back (SeparatorElem());
 		
 		_session.foreach_connection (this, &MixerStrip::add_connection_to_input_menu);
+		_session.foreach_route (this, &MixerStrip::add_route_to_input_menu);
 
 		input_menu.popup (1, ev->time);
 		break;
@@ -710,9 +713,14 @@
 MixerStrip::update_input_display ()
 {
 	ARDOUR::Connection *c;
+	std::string r;
 
 	if ((c = _route->input_connection()) != 0) {
+		/* connection input */
 		input_label.set_text (c->name());
+	} else if ((r = input_route ()) != "") {
+		/* route input */
+		input_label.set_text (r);
 	} else {
 		switch (_width) {
 		case Wide:
@@ -730,9 +738,14 @@
 MixerStrip::update_output_display ()
 {
 	ARDOUR::Connection *c;
+	std::string r;
 
 	if ((c = _route->output_connection()) != 0) {
+		/* connection output */
 		output_label.set_text (c->name());
+	} else if ((r = output_route ()) != "") {
+		/* route output */
+		output_label.set_text (r);
 	} else {
 		switch (_width) {
 		case Wide:
@@ -1219,3 +1232,135 @@
 		gpm.setup_meters ();
 }
 
+
+void
+MixerStrip::add_route_to_output_menu (Route& r)
+{
+        using namespace Menu_Helpers;
+
+	/* we can use this route as an output if its number of inputs is the
+	   same as our number of outputs */
+
+	if (r.n_inputs() == _route->n_outputs()) {
+
+		MenuList& citems = output_menu.items ();
+		citems.push_back (CheckMenuElem (r.name(), bind (mem_fun(*this, &MixerStrip::route_output_chosen), &r)));
+
+		if (output_route () == r.name()) {
+			/* tick this item if it's active */
+			ignore_toggle = true;
+			dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
+			ignore_toggle = false;
+		}
+	}
+}
+
+
+
+void
+MixerStrip::add_route_to_input_menu (Route& r)
+{
+        using namespace Menu_Helpers;
+
+	/* we can use this route as an input if its number of outputs is the
+	   same as our number of inputs */
+
+	if (r.n_outputs() == _route->n_inputs()) {
+
+		MenuList& citems = input_menu.items ();
+		citems.push_back (CheckMenuElem (r.name(), bind (mem_fun(*this, &MixerStrip::route_input_chosen), &r)));
+
+		if (input_route () == r.name()) {
+			/* tick this item if it's active */
+			ignore_toggle = true;
+			dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
+			ignore_toggle = false;
+		}
+	}
+}
+
+
+
+void
+MixerStrip::route_output_chosen (ARDOUR::Route* r)
+{
+	_route->disconnect_outputs (this);
+	
+	for (uint32_t i = 0; i < _route->n_outputs(); i++) {
+		_route->connect_output (_route->output(i), r->input(i)->name(), this);
+	}
+}
+
+
+void
+MixerStrip::route_input_chosen (ARDOUR::Route* r)
+{
+	_route->disconnect_inputs (this);
+	
+	for (uint32_t i = 0; i < _route->n_inputs(); i++) {
+		_route->connect_input (_route->input(i), r->output(i)->name(), this);
+	}
+}
+
+
+std::string
+MixerStrip::output_route ()
+{
+	boost::shared_ptr<Session::RouteList> r = _session.get_routes ();
+
+	/* run through all the session's routes looking for one that
+	   our route's output might be connected to */
+	for (Session::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+		
+		if ( (*i)->n_inputs() == _route->n_outputs()) {
+
+			/* this route has the right number of inputs */
+
+			uint32_t j;
+			for (j = 0; j < (*i)->n_outputs(); ++j) {
+				if (!_route->output(j)->connected_to((*i)->input(j)->name())) {
+					/* this connection is not made; jump out */
+					break;
+				}
+			}
+
+			if (j == (*i)->n_outputs()) {
+				return (*i)->name ();
+			}
+		}
+	}
+
+	return "";
+}
+
+
+
+std::string
+MixerStrip::input_route ()
+{
+	boost::shared_ptr<Session::RouteList> r = _session.get_routes ();
+
+	/* run through all the session's routes looking for one that
+	   our route's input might be connected to */
+	for (Session::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+		
+		if ( (*i)->n_outputs() == _route->n_inputs()) {
+
+			/* this route has the right number of outputs */
+
+			uint32_t j;
+			for (j = 0; j < (*i)->n_inputs(); ++j) {
+				if (!_route->input(j)->connected_to((*i)->output(j)->name())) {
+					/* this connection is not made; jump out */
+					break;
+				}
+			}
+
+			if (j == (*i)->n_inputs()) {
+				return (*i)->name ();
+			}
+		}
+	}
+
+	return "";
+}
Index: gtk2_ardour/mixer_strip.h
===================================================================
--- gtk2_ardour/mixer_strip.h	(revision 1560)
+++ gtk2_ardour/mixer_strip.h	(working copy)
@@ -165,15 +165,23 @@
 	gint input_press (GdkEventButton *);
 	gint output_press (GdkEventButton *);
 
-	Gtk::Menu  input_menu;
+	Gtk::Menu input_menu;
 	void add_connection_to_input_menu (ARDOUR::Connection *);
+	void add_route_to_input_menu (ARDOUR::Route &);
 
 	Gtk::Menu output_menu;
 	void add_connection_to_output_menu (ARDOUR::Connection *);
+	void add_route_to_output_menu (ARDOUR::Route &);
 	
 	void connection_input_chosen (ARDOUR::Connection *);
 	void connection_output_chosen (ARDOUR::Connection *);
 
+	void route_input_chosen (ARDOUR::Route *);
+	void route_output_chosen (ARDOUR::Route *);
+
+	std::string input_route ();
+	std::string output_route ();
+
 	void edit_input_configuration ();
 	void edit_output_configuration ();
 
Index: libs/ardour/route.cc
===================================================================
--- libs/ardour/route.cc	(revision 1560)
+++ libs/ardour/route.cc	(working copy)
@@ -41,6 +41,7 @@
 #include <ardour/panner.h>
 #include <ardour/dB.h>
 #include <ardour/mix.h>
+#include <ardour/connection.h>
 
 #include "i18n.h"
 
Index: libs/ardour/ardour/audioengine.h
===================================================================
--- libs/ardour/ardour/audioengine.h	(revision 1560)
+++ libs/ardour/ardour/audioengine.h	(working copy)
@@ -222,7 +222,9 @@
 	typedef std::pair<std::string,std::string> PortConnection;
 	typedef std::list<PortConnection> PortConnections;
 
-	PortConnections port_connections;
+	PortConnections port_connections; ///< connections that we've made between JACK ports;
+	///< used for re-establishing connections in reconnect_to_jack ()
+  
 	void   remove_connections_for (Port*);
 
 	std::string get_nth_physical (uint32_t which, int flags);
Index: libs/ardour/ardour/route.h
===================================================================
--- libs/ardour/ardour/route.h	(revision 1560)
+++ libs/ardour/ardour/route.h	(working copy)
@@ -47,6 +47,8 @@
 class Insert;
 class Send;
 class RouteGroup;
+class InputConnection;
+class OutputConnection;
 
 enum mute_type {
     PRE_FADER =    0x1,
Index: libs/ardour/ardour/port.h
===================================================================
--- libs/ardour/ardour/port.h	(revision 1560)
+++ libs/ardour/ardour/port.h	(working copy)
@@ -29,6 +29,14 @@
 
 class AudioEngine;
 
+/// JACK Port
+
+/**
+ *  This class is basically a description of a JACK port, with
+ *  some wrappers around JACK functions.  It is identified
+ *  by a jack_port_t*.  Port objects can only be instantiated
+ *  and connections set up by the AudioEngine.
+ */
 class Port : public sigc::trackable {
    public:
 	virtual ~Port() { 
@@ -37,8 +45,10 @@
 
 	Sample *get_buffer (nframes_t nframes) {
 		if (_flags & JackPortIsOutput) {
+			/* its an output so we can use our cached value */
 			return _buffer;
 		} else {
+			/* its an input so we must ask JACK */
 			return (Sample *) jack_port_get_buffer (_port, nframes);
 		}
 	}
@@ -52,20 +62,34 @@
 		_silent = false;
 	}
 
+	/**
+	 *  \return JACK's name for this port.
+	 */
 	std::string name() { 
 		return _name;
 	}
 
+	/**
+	 *  \return JACK's short name for this port.
+	 */
 	std::string short_name() { 
 		return jack_port_short_name (_port);
 	}
-	
+
+	/**
+	 *  Set the name that JACK uses for this port.
+	 *  \param str New name.
+	 */
 	int set_name (std::string str);
 
 	JackPortFlags flags() const {
 		return _flags;
 	}
 
+	/**
+	 *  \param JACK client.
+	 *  \return true if this port belongs to `client'
+	 */
 	bool is_mine (jack_client_t *client) { 
 		return jack_port_is_mine (client, _port);
 	}
@@ -77,11 +101,20 @@
 	int connected () const {
 		return jack_port_connected (_port);
 	}
-	
+
+	/**
+	 *  \param portname Port name.
+	 *  \return true if this port is connected to `portname'
+	 */
 	bool connected_to (const std::string& portname) const {
 		return jack_port_connected_to (_port, portname.c_str());
 	}
 
+	/**
+	 *  \return NULL-terminated array of port names that this
+	 *  port is connected to, or 0.  The caller should free() the
+	 *  returned pointer.
+	 */
 	const char ** get_connections () const {
 		return jack_port_get_connections (_port);
 	}
Index: libs/ardour/audioengine.cc
===================================================================
--- libs/ardour/audioengine.cc	(revision 1560)
+++ libs/ardour/audioengine.cc	(working copy)
@@ -576,6 +576,14 @@
 	}
 }
 
+
+/**
+ *    Basic call to connect two JACK ports together.
+ *
+ *    \param source name of source JACK port.
+ *    \param destination name of destination JACK port.
+ */
+
 int 
 AudioEngine::connect (const string& source, const string& destination)
 {
@@ -587,14 +595,16 @@
 			return -1;
 		}
 	}
-	
-	string s = make_port_name_non_relative (source);
-	string d = make_port_name_non_relative (destination);
 
+	/* ensure that the port names include client names */
+	const string s = make_port_name_non_relative (source);
+	const string d = make_port_name_non_relative (destination);
+
 	int ret = jack_connect (_jack, s.c_str(), d.c_str());
 
 	if (ret == 0) {
-		pair<string,string> c (s, d);
+		/* connection ok; make a note of it */
+		const pair<string,string> c (s, d);
 		port_connections.push_back (c);
 	} else if (ret == EEXIST) {
 		error << string_compose(_("AudioEngine: connection already exists: %1 (%2) to %3 (%4)"), 
@@ -609,6 +619,13 @@
 	return ret;
 }
 
+/**
+ *    Basic call to disconnect two previously-connected JACK ports.
+ *
+ *    \param source name of source JACK port.
+ *    \param destination name of destination JACK port.
+ */
+
 int 
 AudioEngine::disconnect (const string& source, const string& destination)
 {
@@ -621,15 +638,17 @@
 		}
 	}
 	
-	string s = make_port_name_non_relative (source);
-	string d = make_port_name_non_relative (destination);
+	/* ensure that the port names include client names */
+	const string s = make_port_name_non_relative (source);
+	const string d = make_port_name_non_relative (destination);
 
 	int ret = jack_disconnect (_jack, s.c_str(), d.c_str());
 
 	if (ret == 0) {
-		pair<string,string> c (s, d);
+		/* disconnection ok; make a note of it */
+		const pair<string,string> c (s, d);
 		PortConnections::iterator i;
-		
+
 		if ((i = find (port_connections.begin(), port_connections.end(), c)) != port_connections.end()) {
 			port_connections.erase (i);
 		}
@@ -993,6 +1012,13 @@
 	port_connections.clear ();
 }
 
+/**
+ *    Remove any connections that we have made a note of in port_connections
+ *    for a given port.
+ *
+ *    \param port Port.
+ */
+
 void
 AudioEngine::remove_connections_for (Port* port)
 {
routes-2 (11,694 bytes)   

paul

2007-03-09 13:29

administrator   ~0003545

this one i am not sure about. first of all, it doesn't add the routes to a submenu, which for anyone using a multichannel card and a largish session will make the menu incredibly long.

more importantly, i plan to switch to a matrix-based i/o setup system, like the one in Reaper. i'm not opposed to the idea of this patch, but its a band aid over deeper issues that the matrix editor will fix.

cth103

2007-03-09 13:48

administrator   ~0003550

Fair comment about the menu issues. To be honest the motivation for this patch is as much that I'd like the mixer strip button to say more than just "Output" when its connected to a route. Its a cubase thing ;)

So we could lose the menu alterations and just preserve the code that labels the Output button better.

Anyway if there's a better IO setup system on the way then that's great :)

cth103

2007-11-04 15:08

administrator   ~0004519

This has now been implemented in the re-work of bundles etc.

Issue History

Date Modified Username Field Change
2007-03-07 14:30 cth103 New Issue
2007-03-07 14:30 cth103 File Added: routes-2
2007-03-07 14:30 cth103 Status new => assigned
2007-03-07 14:30 cth103 Assigned To => cth103
2007-03-09 13:29 paul Note Added: 0003545
2007-03-09 13:48 cth103 Note Added: 0003550
2007-11-04 15:08 cth103 Status assigned => resolved
2007-11-04 15:08 cth103 Resolution open => fixed
2007-11-04 15:08 cth103 Note Added: 0004519
2008-11-21 00:04 seablade Status resolved => closed